From f14f88042495e885973a65efddeff38a9343cb4c Mon Sep 17 00:00:00 2001 From: Anatoly Piskunov Date: Sun, 17 May 2026 13:18:43 +0400 Subject: [PATCH 01/29] =?UTF-8?q?refactor:=20rename=20witness=E2=86=92vali?= =?UTF-8?q?dator=20across=20protocol,=20chain,=20API,=20and=20wallet=20lay?= =?UTF-8?q?ers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the core "witness" concept to "validator" throughout the C++ node without breaking binary block_log compatibility (binary format is positional, not name-based). Operations (protocol layer): - witness_update_operation → validator_update_operation - account_witness_vote_operation → account_validator_vote_operation - account_witness_proxy_operation → account_validator_proxy_operation - shutdown_witness_operation → shutdown_validator_operation - witness_reward_operation → validator_reward_operation JSON backward compatibility: resolve_operation_name() alias table in operation_util_impl.cpp maps old operation names to new ones, so existing JSON transactions and block explorers continue to work. Chain objects and DB methods: - witness_object → validator_object - witness_schedule_object → validator_schedule_object - witness_index / validator_index, get_validator_schedule_object(), adjust_validator_vote(), clear_validator_votes(), etc. Chain properties fields: - inflation_witness_percent → inflation_validator_percent - witness_miss_penalty_percent → validator_miss_penalty_percent - witness_miss_penalty_duration → validator_miss_penalty_duration - witness_declaration_fee → validator_declaration_fee (updated in chain_operations.hpp, global_property_object.hpp, chain_api_properties.hpp, database.cpp, chain_properties_evaluators.cpp) API and wallet: - witness_api_object → validator_api_object - remote_witness_api methods renamed to get_validators / get_validator_* - wallet.cpp command implementations and wallet.hpp declarations updated Snapshot backward compatibility: - patch_chain_props_variant() helper translates old JSON field names before fc::from_variant, so pre-rename snapshots import correctly - import_witness_schedule() handles current_shuffled_witnesses → current_shuffled_validators and num_scheduled_witnesses → num_scheduled_validators key aliases Note: block_header::witness and signed_block_header::witness_signature are NOT renamed in this commit (Task 8a, separate PR). Physical file renames (witness_objects.hpp → validator_objects.hpp, plugins/witness_api/ → plugins/validator_api/) are also deferred to a follow-up commit. --- ...itness-to-validator-migration-reference.md | 447 ++++++++++++++++++ ...tness-to-Validator_Full_Rename_6e522add.md | 148 ++++++ libraries/api/chain_api_properties.cpp | 8 +- .../graphene/api/chain_api_properties.hpp | 14 +- .../graphene/api/witness_api_object.hpp | 10 +- libraries/api/witness_api_object.cpp | 2 +- libraries/chain/chain_evaluator.cpp | 76 +-- .../chain/chain_properties_evaluators.cpp | 32 +- libraries/chain/committee_evaluator.cpp | 2 +- libraries/chain/database.cpp | 422 ++++++++--------- .../graphene/chain/chain_evaluator.hpp | 6 +- .../graphene/chain/chain_object_types.hpp | 16 +- .../chain/include/graphene/chain/database.hpp | 26 +- .../graphene/chain/global_property_object.hpp | 4 +- .../graphene/chain/witness_objects.hpp | 96 ++-- libraries/chain/invite_evaluator.cpp | 2 +- .../chain/paid_subscription_evaluator.cpp | 2 +- libraries/protocol/chain_operations.cpp | 8 +- .../graphene/protocol/chain_operations.hpp | 44 +- .../protocol/chain_virtual_operations.hpp | 20 +- .../graphene/protocol/operation_util_impl.hpp | 6 +- .../include/graphene/protocol/operations.hpp | 10 +- libraries/protocol/operation_util_impl.cpp | 14 + .../graphene/wallet/remote_node_api.hpp | 28 +- .../wallet/include/graphene/wallet/wallet.hpp | 24 +- libraries/wallet/wallet.cpp | 30 +- plugins/account_history/plugin.cpp | 14 +- plugins/snapshot/plugin.cpp | 40 +- .../graphene/plugins/witness_api/plugin.hpp | 22 +- plugins/witness_api/plugin.cpp | 36 +- plugins/witness_guard/witness_guard.cpp | 4 +- 31 files changed, 1128 insertions(+), 485 deletions(-) create mode 100644 .qoder/docs/witness-to-validator-migration-reference.md create mode 100644 .qoder/plans/Witness-to-Validator_Full_Rename_6e522add.md diff --git a/.qoder/docs/witness-to-validator-migration-reference.md b/.qoder/docs/witness-to-validator-migration-reference.md new file mode 100644 index 0000000000..6e7266cf02 --- /dev/null +++ b/.qoder/docs/witness-to-validator-migration-reference.md @@ -0,0 +1,447 @@ +# VIZ Blockchain — Witness → Validator Migration Reference + +## Quick Summary + +The VIZ blockchain is renaming "witness" terminology to "validator" across the entire stack. This document is a reference for JS/PHP library developers to support both old and new names during migration. + +**Key principle: binary wire format uses integer type IDs, not string names. Submitting transactions by integer ID never breaks. Only JSON string names change.** + +--- + +## 0. What Libraries Must Change Themselves vs What They Just Relay + +This distinction is the most important rule for library developers. + +### Must change (library constructs these from scratch): + +| What | Where in library code | +|------|----------------------| +| Operation name→ID mapping table | Serialization/deserialization layer | +| Field names inside type 7 and type 42 operations | Operation builders, schema definitions | +| Chain properties field names (`inflation_witness_percent`, etc.) | `chain_properties_update` builder | +| TypeScript interfaces / PHP classes for operation structs | Type definitions | +| TypeScript interfaces / PHP classes for `validator_schedule_object` | Type definitions | + +### Do NOT need to change (library receives from node and relays): + +| What | Reason | +|------|--------| +| Block header fields (`witness`, `witness_signature`) | Library reads from node, forwards to caller. Field name in response changes automatically after node upgrade — library just reflects whatever the node returns. No hardcoded logic. | +| Raw API response objects (beyond type definitions) | JSON deserialization is dynamic — field access works regardless of name if library doesn't validate field names | +| Historical transaction data from node | Same — the node returns it, library relays it | + +**Practical rule:** if your library has a hardcoded string `"witness"` as a field name when *building* a JSON object to *send* to a node — that string must be updated. If the string `"witness"` only appears in a *comment*, a *display label*, or a *type definition that only affects IDE autocomplete* — it can wait. + +--- + +## Current Status (What Has Been Done vs What Is Planned) + +| Layer | Status | Visible to JS/PHP? | +|-------|--------|---------------------| +| Internal C++ methods (e.g., `is_validator_scheduled_soon`) | Done | No | +| Internal enums (`block_validation_condition`) | Done | No | +| Protocol operation struct names | Not yet | Yes — JSON name in transactions | +| API method names (`get_active_witnesses`, etc.) | Not yet | Yes — JSON-RPC calls | +| Chain object types (`witness_object`, etc.) | Not yet | Yes — API response type names | +| Plugin names (`witness`, `witness_api`, `witness_guard`) | Not yet | Yes — config files | +| CLI wallet commands | Not yet | Yes — if using CLI wallet | + +--- + +## 1. Protocol Operations (JSON-RPC Transaction Submission) + +These are the operations users submit in transactions. The **integer type ID never changes** — only the JSON string name changes. + +### Operations to Rename + +| Type ID | Current JSON Name (old) | New JSON Name | Virtual? | Fields (unchanged) | +|---------|------------------------|---------------|----------|---------------------| +| `6` | `witness_update` | `validator_update` | no | `owner`, `url`, `block_signing_key` | +| `7` | `account_witness_vote` | `account_validator_vote` | no | `account`, **`witness` → `validator`**, `approve` | +| `8` | `account_witness_proxy` | `account_validator_proxy` | no | `account`, `proxy` | +| `30` | `shutdown_witness` | `shutdown_validator` | **yes** | `owner` | +| `42` | `witness_reward` | `validator_reward` | **yes** | **`witness` → `validator`**, `shares` | + +> **Field renames inside operations:** In type 7 (`account_validator_vote`) the field `witness` (the target account name) is renamed to `validator`. In type 42 (`validator_reward`) the field `witness` is renamed to `validator`. The node accepts both old and new field names in incoming JSON, but responses use new names only. + +### What JS/PHP Developers Must Handle + +**Sending transactions (2 safe approaches):** + +```js +// Approach A: Use integer type ID (always safe, never breaks) +const op = [6, { + owner: 'alice', + url: 'https://alice.example.com', + block_signing_key: 'VIZ5hq...', +}]; + +// Approach B: Use string name (need to support both old and new) +const op = ['validator_update', { // new name + owner: 'alice', + url: 'https://alice.example.com', + block_signing_key: 'VIZ5hq...', +}]; +``` + +**Sending type 7 (vote) with updated field name:** + +```js +// Old (still accepted by node, but deprecated): +const op = [7, { account: 'alice', witness: 'bob', approve: true }]; + +// New (correct): +const op = [7, { account: 'alice', validator: 'bob', approve: true }]; +``` + +**Receiving transactions (operation history, block parsing):** + +```js +// Old server response: +["witness_update", { "owner": "alice", ... }] + +// New server response: +["validator_update", { "owner": "alice", ... }] + +// Your code must accept BOTH names for the same operation +``` + +**Server-side fallback:** The C++ node will accept both old and new JSON names in incoming transactions. But responses will use **new names only**. + +### Implementation Pattern for JS/PHP + +```js +// Operation name mapping (accept both, send new) +const OP_NAME_MAP = { + 'witness_update': 'validator_update', + 'account_witness_vote': 'account_validator_vote', + 'account_witness_proxy': 'account_validator_proxy', + 'shutdown_witness': 'shutdown_validator', + 'witness_reward': 'validator_reward', +}; + +// Reverse map (for receiving — normalize old names to new) +const OP_ALIAS_MAP = { + 'witness_update': 'validator_update', + 'account_witness_vote': 'account_validator_vote', + 'account_witness_proxy': 'account_validator_proxy', + 'shutdown_witness': 'shutdown_validator', + 'witness_reward': 'validator_reward', +}; + +// Type ID constants (never change) +const OP_TYPE_ID = { + validator_update: 6, + account_validator_vote: 7, + account_validator_proxy: 8, + shutdown_validator: 30, + validator_reward: 42, +}; +``` + +```php +// PHP equivalent +const OP_NAME_MAP = [ + 'witness_update' => 'validator_update', + 'account_witness_vote' => 'account_validator_vote', + 'account_witness_proxy' => 'account_validator_proxy', + 'shutdown_witness' => 'shutdown_validator', + 'witness_reward' => 'validator_reward', +]; + +const OP_TYPE_ID = [ + 'validator_update' => 6, + 'account_validator_vote' => 7, + 'account_validator_proxy' => 8, + 'shutdown_validator' => 30, + 'validator_reward' => 42, +]; +``` + +--- + +## 2. API Methods (JSON-RPC Calls) + +### API Namespace + +The JSON-RPC namespace (`"api"` field) is currently **`"witness_api"`** and will be renamed to **`"validator_api"`** when the plugin directory is renamed. Until then, all calls — including new `get_validator_*` methods — must use `"witness_api"`: + +```json +{ "api": "witness_api", "method": "get_active_validators", "params": [] } +``` + +There is no `"validator_api"` alias at this time. The namespace change will require a fallback strategy identical to the method-name fallback (try `validator_api`, fall back to `witness_api`). + +### Methods to Rename + +| Current Name (old) | New Name | Returns (unchanged) | +|-------------------|----------|---------------------| +| `get_active_witnesses` | `get_active_validators` | `vector` | +| `get_witness_schedule` | `get_validator_schedule` | `witness_schedule_object` → `validator_schedule_object` | +| `get_witnesses` | `get_validators` | `vector>` → `validator_api_object` | +| `get_witness_by_account` | `get_validator_by_account` | `optional` → `validator_api_object` | +| `get_witnesses_by_vote` | `get_validators_by_vote` | `vector` → `validator_api_object` | +| `get_witnesses_by_counted_vote` | `get_validators_by_counted_vote` | `vector` → `validator_api_object` | +| `get_witness_count` | `get_validator_count` | `uint64_t` | +| `lookup_witness_accounts` | `lookup_validator_accounts` | `set` | + +### What JS/PHP Developers Must Handle + +**Server-side fallback:** Old method names will remain as deprecated aliases for one release cycle. Calling `get_active_witnesses` will still work but will log a deprecation warning on the server. + +### Implementation Pattern for JS/PHP + +```js +// Dual-support API wrapper +class VizApi { + async getActiveValidators() { + try { + return await this.call('get_active_validators'); + } catch (e) { + // Fallback to old name for older nodes + return await this.call('get_active_witnesses'); + } + } + + async getValidatorByAccount(account) { + try { + return await this.call('get_validator_by_account', [account]); + } catch (e) { + return await this.call('get_witness_by_account', [account]); + } + } + + // ... same pattern for all renamed methods +} +``` + +--- + +## 3. API Response Objects + +### Object Types to Rename + +| Current Name (old) | New Name | Key Fields (unchanged) | +|-------------------|----------|------------------------| +| `witness_object` | `validator_object` | `id`, `owner`, `url`, `signing_key`, `votes`, `schedule` | +| `witness_schedule_object` | `validator_schedule_object` | `current_shuffled_validators[]`, `num_scheduled` | +| `witness_api_object` | `validator_api_object` | All fields same as `witness_object` + computed fields | + +### Field Renames in Response Objects + +| Object | Current Field Name (old) | New Field Name | +|--------|-------------------------|----------------| +| `validator_schedule_object` | `current_shuffled_witnesses` | `current_shuffled_validators` | +| `validator_schedule_object` | `num_scheduled_witnesses` | `num_scheduled_validators` | + +### What JS/PHP Developers Must Handle + +```js +// Old response: +{ + "current_shuffled_witnesses": ["alice", "bob", ...], + "num_scheduled_witnesses": 21 +} + +// New response: +{ + "current_shuffled_validators": ["alice", "bob", ...], + "num_scheduled_validators": 21 +} + +// Safe accessor pattern +function getShuffledValidators(schedule) { + return schedule.current_shuffled_validators + || schedule.current_shuffled_witnesses; // fallback for old nodes +} +``` + +--- + +## 4. Operation Field Names (Changing and Unchanged) + +### Fields Being Renamed + +| Field (old) | Field (new) | Operation | Note | +|-------------|-------------|-----------|------| +| `witness` | `validator` | `account_validator_vote` (type 7) | Target account name | +| `witness` | `validator` | `validator_reward` (type 42) | Virtual op — library receives, not constructs | + +The node accepts both old and new field names in incoming JSON (backward compat). Responses always use new names. + +### Fields That Stay Unchanged + +| Field | Operation | Why It Stays | +|-------|-----------|-------------| +| `owner` | `validator_update` (type 6) | Describes the account, not the role | +| `url` | `validator_update` (type 6) | URL is a URL | +| `block_signing_key` | `validator_update` (type 6) | Describes the cryptographic key purpose | +| `account` | `account_validator_vote` (type 7) | Describes the voting account | +| `proxy` | `account_validator_proxy` (type 8) | Describes the proxy account | +| `approve` | `account_validator_vote` (type 7) | Boolean flag | +| `shares` | `validator_reward` (type 42) | Vesting shares amount | + +--- + +## 5. Chain Properties Field Renames + +**This section is critical for library developers.** The `chain_properties_update_operation` and `versioned_chain_properties_update_operation` carry governance parameters with `witness` in their names. These field names change in JSON. Libraries that construct these operations must update field names. + +**Binary format is safe** — field order is preserved in binary serialization, names are not written. Only JSON field names change. + +### Fields Being Renamed + +| Old Field Name | New Field Name | In Struct | +|----------------|----------------|-----------| +| `inflation_witness_percent` | `inflation_validator_percent` | `chain_properties_hf4` | +| `witness_miss_penalty_percent` | `validator_miss_penalty_percent` | `chain_properties_hf6` | +| `witness_miss_penalty_duration` | `validator_miss_penalty_duration` | `chain_properties_hf6` | +| `witness_declaration_fee` | `validator_declaration_fee` | `chain_properties_hf9` | + +### What JS/PHP Developers Must Handle + +```js +// Old (still accepted by node with compat layer): +const props = { + inflation_witness_percent: 1500, + witness_miss_penalty_percent: 100, + witness_miss_penalty_duration: 86400, + witness_declaration_fee: { amount: '10000', asset: 'VIZ' }, +}; + +// New (correct): +const props = { + inflation_validator_percent: 1500, + validator_miss_penalty_percent: 100, + validator_miss_penalty_duration: 86400, + validator_declaration_fee: { amount: '10000', asset: 'VIZ' }, +}; +``` + +```php +// PHP equivalent +$props = [ + 'inflation_validator_percent' => 1500, + 'validator_miss_penalty_percent' => 100, + 'validator_miss_penalty_duration' => 86400, + 'validator_declaration_fee' => ['amount' => '10000', 'asset' => 'VIZ'], +]; +``` + +--- + +## 6. Config Keys (For Node Operators) + +Not directly relevant to JS/PHP libraries, but included for completeness: + +| Current Config Key (old) | New Config Key | +|--------------------------|----------------| +| `plugin = witness` | `plugin = validator` | +| `plugin = witness_api` | `plugin = validator_api` | +| `plugin = witness_guard` | `plugin = validator_guard` | +| `--witness = "name"` | `--validator = "name"` | +| `witness-guard-enabled` | `validator-guard-enabled` | +| `witness-guard-disable` | `validator-guard-disable` | +| `witness-guard-interval` | `validator-guard-interval` | +| `witness-guard-witness` | `validator-guard-validator` | + +--- + +## 7. CLI Wallet Commands (If Using CLI Wallet) + +| Current Command (old) | New Command | Notes | +|----------------------|-------------|-------| +| `list_witnesses()` | `list_validators()` | Read-only | +| `get_witness()` | `get_validator()` | Read-only | +| `get_active_witnesses()` | `get_active_validators()` | Read-only | +| `update_witness()` | `update_validator()` | Sends type 6 | +| `vote_for_witness()` | `vote_for_validator()` | Sends type 7 | +| `set_voting_proxy()` | `set_voting_proxy()` | Command name stays, sends type 8 | + +--- + +## 8. What NEVER Changes + +| Item | Why | +|------|-----| +| Integer type IDs (6, 7, 8, 30, 42) | Binary wire format uses integer indices | +| Binary serialization of operations | Struct field order is preserved; field names are not written to binary | +| Field names `block_signing_key`, `url`, `approve`, `proxy` | Describe data, not the role | +| Null key for deactivation: `VIZ1111111111111111111111111111111114T1Anm` | Same null key format | +| Signing authority level (`active`) | Operations still require active authority | +| Block interval, slot scheduling, consensus rules | Unchanged | + +> **Note on block header fields:** `block.witness` and `block.witness_signature` are renamed to `block.validator` and `block.validator_signature` in JSON output. This is a **simple rename with no hardfork required** — binary format does not serialize field names, only values by position. Libraries that just relay block data will reflect new names automatically after node upgrade. No binary migration or version negotiation is needed. + +--- + +## 9. Migration Strategy for Library Developers + +### Phase A — Prepare (Before Node Upgrade) + +1. Add dual-name support for operation type identification: + - Accept both `witness_update` and `validator_update` as name for type ID 6 + - Accept both `account_witness_vote` and `account_validator_vote` as name for type ID 7 + - Same for types 8, 30, 42 +2. Update field names in operation builders: + - Type 7: accept both `witness` and `validator` for the target account field; send `validator` + - Chain properties: add new field names; keep old for backward compat with old nodes +3. Add dual-name support for API methods: + - Try new method name first, fall back to old name +4. Add dual field access for response objects: + - Check `current_shuffled_validators` first, fall back to `current_shuffled_witnesses` +5. **Send transactions using integer type IDs** for maximum compatibility + +### Phase B — After Node Upgrade + +1. Default to new names for sending +2. Keep old name acceptance for receiving (history may contain old-format blocks) +3. Release library update with both names supported + +### Phase C — Cleanup (After All Nodes Upgraded) + +1. Remove old name fallbacks +2. Use only new names throughout + +--- + +## 10. Complete Quick-Reference Table + +### Operations + +| Type ID | Old JSON Name | New JSON Name | Field changes | +|---------|--------------|---------------|---------------| +| 6 | `witness_update` | `validator_update` | none | +| 7 | `account_witness_vote` | `account_validator_vote` | `witness` → `validator` | +| 8 | `account_witness_proxy` | `account_validator_proxy` | none | +| 30 | `shutdown_witness` | `shutdown_validator` | none | +| 42 | `witness_reward` | `validator_reward` | `witness` → `validator` | + +### API Methods + +| Old Name | New Name | +|----------|----------| +| `get_active_witnesses` | `get_active_validators` | +| `get_witness_schedule` | `get_validator_schedule` | +| `get_witnesses` | `get_validators` | +| `get_witness_by_account` | `get_validator_by_account` | +| `get_witnesses_by_vote` | `get_validators_by_vote` | +| `get_witnesses_by_counted_vote` | `get_validators_by_counted_vote` | +| `get_witness_count` | `get_validator_count` | +| `lookup_witness_accounts` | `lookup_validator_accounts` | + +### Response Fields + +| Old Field | New Field | In Object | +|-----------|-----------|-----------| +| `current_shuffled_witnesses` | `current_shuffled_validators` | schedule object | +| `num_scheduled_witnesses` | `num_scheduled_validators` | schedule object | + +### Chain Properties Fields + +| Old Field | New Field | In Struct | +|-----------|-----------|-----------| +| `inflation_witness_percent` | `inflation_validator_percent` | `chain_properties_hf4` | +| `witness_miss_penalty_percent` | `validator_miss_penalty_percent` | `chain_properties_hf6` | +| `witness_miss_penalty_duration` | `validator_miss_penalty_duration` | `chain_properties_hf6` | +| `witness_declaration_fee` | `validator_declaration_fee` | `chain_properties_hf9` | diff --git a/.qoder/plans/Witness-to-Validator_Full_Rename_6e522add.md b/.qoder/plans/Witness-to-Validator_Full_Rename_6e522add.md new file mode 100644 index 0000000000..ae25feb8ac --- /dev/null +++ b/.qoder/plans/Witness-to-Validator_Full_Rename_6e522add.md @@ -0,0 +1,148 @@ +# Witness-to-Validator Full Rename Plan + +Phase 1 (internal C++ methods/enums) is already done. This plan covers everything remaining. + +## Scope: 5 Protocol Operations + +| Type ID | Old Struct | New Struct | +|---------|-----------|------------| +| 6 | `witness_update_operation` | `validator_update_operation` | +| 7 | `account_witness_vote_operation` | `account_validator_vote_operation` | +| 8 | `account_witness_proxy_operation` | `account_validator_proxy_operation` | +| 30 | `shutdown_witness_operation` | `shutdown_validator_operation` | +| 42 | `witness_reward_operation` | `validator_reward_operation` | + +## Task 1: Protocol operation struct renames + +**Files:** +- `libraries/protocol/include/graphene/protocol/chain_operations.hpp` — rename 3 structs + 3 FC_REFLECT macros +- `libraries/protocol/include/graphene/protocol/chain_virtual_operations.hpp` — rename 2 structs + 2 FC_REFLECT macros + constructors +- `libraries/protocol/include/graphene/protocol/operations.hpp` — update 5 names in static_variant list (order unchanged!) +- `libraries/protocol/chain_operations.cpp` — rename 3 validate() method definitions + +Rename `witness` field to `validator` in `account_validator_vote_operation` (was `account_witness_vote_operation`). Also rename `witness` field to `validator` in `validator_reward_operation` (was `witness_reward_operation`). + +## Task 2: JSON backward-compatibility alias table + +**File:** `libraries/protocol/operation_util_impl.cpp` + +Add a `resolve_operation_name()` function that maps old JSON names to new names: +- `witness_update` → `validator_update` +- `account_witness_vote` → `account_validator_vote` +- `account_witness_proxy` → `account_validator_proxy` +- `shutdown_witness` → `shutdown_validator` +- `witness_reward` → `validator_reward` + +This must be hooked into the `from_variant` path so nodes accept both old and new JSON names from clients. + +## Task 3: Evaluator renames + +**Files:** +- `libraries/chain/include/graphene/chain/chain_evaluator.hpp` — `DEFINE_EVALUATOR(witness_update)` → `DEFINE_EVALUATOR(validator_update)`, same for vote and proxy +- `libraries/chain/chain_evaluator.cpp` — 3 method signatures +- `libraries/chain/chain_properties_evaluators.cpp` — `witness_update_evaluator::do_apply` +- `libraries/chain/database.cpp` — 3 `register_evaluator` calls + all `push_virtual_operation(witness_reward_operation(...))` → `validator_reward_operation(...)` and `push_virtual_operation(shutdown_witness_operation(...))` → `shutdown_validator_operation(...)` + +## Task 4: Chain object renames + +**File:** `libraries/chain/include/graphene/chain/witness_objects.hpp` +- `witness_object` → `validator_object` +- `witness_schedule_object` → `validator_schedule_object` +- `witness_schedule_type` → `validator_schedule_type` +- `current_shuffled_witnesses` → `current_shuffled_validators` +- `witness_index` → `validator_index` +- `witness_id_type` → `validator_id_type` +- `witness_object_type` → `validator_object_type` +- All FC_REFLECT macros + +Also rename the file itself: `witness_objects.hpp` → `validator_objects.hpp` + +**File:** `libraries/chain/include/graphene/chain/chain_objects.hpp` +- `block_post_validation_object` → `validator_confirmation_object` + +**File:** `libraries/chain/include/graphene/chain/chain_object_types.hpp` +- `witness_object_type` → `validator_object_type` +- `witness_schedule_object_type` → `validator_schedule_object_type` + +**Every file that includes `witness_objects.hpp`** must be updated to include `validator_objects.hpp` and use new type names. + +## Task 5: Database layer references + +**Files:** +- `libraries/chain/database.cpp` — all references to witness_object, witness_schedule_object, get_witness_schedule_object(), etc. +- `libraries/chain/database.hpp` — method signatures like `get_witness_schedule_object()` +- `libraries/chain/include/graphene/chain/database.hpp` — same + +Key method renames: +- `get_witness_schedule_object()` → `get_validator_schedule_object()` +- `get_scheduled_witness()` → `get_scheduled_validator()` +- `get_slot_time()`, `get_slot_at_time()` — keep (no "witness") +- `adjust_witness_votes()` → `adjust_validator_votes()` +- `adjust_proxied_witness_votes()` → `adjust_proxied_validator_votes()` + +## Task 6: API object + endpoint renames + +**File:** `libraries/api/include/graphene/api/witness_api_object.hpp` → rename to `validator_api_object.hpp` +- `witness_api_object` → `validator_api_object` + +**File:** `libraries/api/witness_api_object.cpp` — same rename + all field references + +**File:** `plugins/witness_api/plugin.cpp` — rename all 8 API endpoints + add deprecated aliases: +- `get_active_witnesses` → `get_active_validators` (+ keep old as alias) +- `get_witness_schedule` → `get_validator_schedule` (+ keep old as alias) +- `get_witnesses` → `get_validators` (+ keep old as alias) +- `get_witness_by_account` → `get_validator_by_account` (+ keep old as alias) +- `get_witnesses_by_vote` → `get_validators_by_vote` (+ keep old as alias) +- `get_witnesses_by_counted_vote` → `get_validators_by_counted_vote` (+ keep old as alias) +- `get_witness_count` → `get_validator_count` (+ keep old as alias) +- `lookup_witness_accounts` → `lookup_validator_accounts` (+ keep old as alias) + +**File:** `plugins/witness_api/include/graphene/plugins/witness_api/plugin.hpp` — update DEFINE_API_ARGS macros + +## Task 7: Wallet renames + +**Files:** +- `libraries/wallet/wallet.cpp` — rename commands, keep old as deprecated aliases +- `libraries/wallet/include/graphene/wallet/wallet.hpp` — method declarations +- `libraries/wallet/include/graphene/wallet/remote_node_api.hpp` — remote API method names + +## Task 8: Plugin directory + config renames + +This involves actual directory renaming which is outside the scope of code editing tools. Manual steps: +- `plugins/witness/` → `plugins/validator/` +- `plugins/witness_api/` → `plugins/validator_api/` +- `plugins/witness_guard/` → `plugins/validator_guard/` +- Update all CMakeLists.txt references +- Update `share/vizd/config/config_witness.ini` → `config_validator.ini` +- Update `share/vizd/config/config.ini` plugin names + +## Task 9: Update documentation + +- `.qoder/docs/witness-to-validator-migration-reference.md` — update status table to "Done" +- `.qoder/docs/op-witness.md` → rename to `op-validator.md` with new operation names +- `.qoder/docs/witness-plugin.md` → rename/rewrite +- `.qoder/docs/data-types.md` — update operation type table + +## Task 10: Build verification + +Compile the entire project to verify no broken references. + +## Execution Order + +Tasks 1-5 must be done together (they're interdependent — renaming a struct breaks all references). Recommended approach: +1. Task 1 (protocol structs) + Task 2 (alias table) together +2. Task 3 (evaluators) — immediately after, since they reference the structs +3. Task 4 (chain objects) — next, largest scope +4. Task 5 (database layer) — after chain objects +5. Task 6 (API) — after database +6. Task 7 (wallet) — after API +7. Task 8 (plugins/config) — last, requires directory renames +8. Task 9 (docs) +9. Task 10 (build verify) + +## Risk Assessment + +- **Binary wire format:** Zero risk — integer type IDs are unchanged +- **JSON compatibility:** The alias table ensures old clients can still submit transactions with old names +- **Shared memory on-disk format:** `witness_object` is serialized to shared memory by type ID, not name — but FC_REFLECT field names ARE serialized in some contexts. Need to verify if renaming FC_REFLECT fields breaks the shared memory format. If it does, a migration layer is needed. +- **Snapshot format:** Snapshots serialize objects as JSON with field names — renaming fields breaks snapshot compatibility with old snapshots diff --git a/libraries/api/chain_api_properties.cpp b/libraries/api/chain_api_properties.cpp index f763a90cb7..aa98d63265 100644 --- a/libraries/api/chain_api_properties.cpp +++ b/libraries/api/chain_api_properties.cpp @@ -19,14 +19,14 @@ namespace graphene { namespace api { committee_request_approve_min_percent(src.committee_request_approve_min_percent) { if (db.has_hardfork(CHAIN_HARDFORK_4)) { - inflation_witness_percent=src.inflation_witness_percent; + inflation_validator_percent=src.inflation_validator_percent; inflation_ratio_committee_vs_reward_fund=src.inflation_ratio_committee_vs_reward_fund; inflation_recalc_period=src.inflation_recalc_period; } if (db.has_hardfork(CHAIN_HARDFORK_6)) { data_operations_cost_additional_bandwidth=src.data_operations_cost_additional_bandwidth; - witness_miss_penalty_percent=src.witness_miss_penalty_percent; - witness_miss_penalty_duration=src.witness_miss_penalty_duration; + validator_miss_penalty_percent=src.validator_miss_penalty_percent; + validator_miss_penalty_duration=src.validator_miss_penalty_duration; } if (db.has_hardfork(CHAIN_HARDFORK_9)) { create_invite_min_balance=src.create_invite_min_balance; @@ -34,7 +34,7 @@ namespace graphene { namespace api { create_paid_subscription_fee=src.create_paid_subscription_fee; account_on_sale_fee=src.account_on_sale_fee; subaccount_on_sale_fee=src.subaccount_on_sale_fee; - witness_declaration_fee=src.witness_declaration_fee; + validator_declaration_fee=src.validator_declaration_fee; withdraw_intervals=src.withdraw_intervals; } if (db.has_hardfork(CHAIN_HARDFORK_13)) { diff --git a/libraries/api/include/graphene/api/chain_api_properties.hpp b/libraries/api/include/graphene/api/chain_api_properties.hpp index 47c2100dd0..7efe4286ac 100644 --- a/libraries/api/include/graphene/api/chain_api_properties.hpp +++ b/libraries/api/include/graphene/api/chain_api_properties.hpp @@ -26,20 +26,20 @@ namespace graphene { namespace api { uint32_t vote_accounting_min_rshares; int16_t committee_request_approve_min_percent; - fc::optional inflation_witness_percent; + fc::optional inflation_validator_percent; fc::optional inflation_ratio_committee_vs_reward_fund; fc::optional inflation_recalc_period; fc::optional data_operations_cost_additional_bandwidth; - fc::optional witness_miss_penalty_percent; - fc::optional witness_miss_penalty_duration; + fc::optional validator_miss_penalty_percent; + fc::optional validator_miss_penalty_duration; fc::optional create_invite_min_balance; fc::optional committee_create_request_fee; fc::optional create_paid_subscription_fee; fc::optional account_on_sale_fee; fc::optional subaccount_on_sale_fee; - fc::optional witness_declaration_fee; + fc::optional validator_declaration_fee; fc::optional withdraw_intervals; fc::optional distribution_epoch_length; }; @@ -52,8 +52,8 @@ FC_REFLECT( (create_account_delegation_ratio)(create_account_delegation_time)(min_delegation) (min_curation_percent)(max_curation_percent)(bandwidth_reserve_percent)(bandwidth_reserve_below) (flag_energy_additional_cost)(vote_accounting_min_rshares)(committee_request_approve_min_percent) - (inflation_witness_percent)(inflation_ratio_committee_vs_reward_fund)(inflation_recalc_period) - (data_operations_cost_additional_bandwidth)(witness_miss_penalty_percent)(witness_miss_penalty_duration) + (inflation_validator_percent)(inflation_ratio_committee_vs_reward_fund)(inflation_recalc_period) + (data_operations_cost_additional_bandwidth)(validator_miss_penalty_percent)(validator_miss_penalty_duration) (create_invite_min_balance)(committee_create_request_fee)(create_paid_subscription_fee) - (account_on_sale_fee)(subaccount_on_sale_fee)(witness_declaration_fee)(withdraw_intervals) + (account_on_sale_fee)(subaccount_on_sale_fee)(validator_declaration_fee)(withdraw_intervals) (distribution_epoch_length)) \ No newline at end of file diff --git a/libraries/api/include/graphene/api/witness_api_object.hpp b/libraries/api/include/graphene/api/witness_api_object.hpp index a1895333b7..990cb64fdc 100644 --- a/libraries/api/include/graphene/api/witness_api_object.hpp +++ b/libraries/api/include/graphene/api/witness_api_object.hpp @@ -7,12 +7,12 @@ namespace graphene { namespace api { using namespace graphene::chain; - struct witness_api_object { - witness_api_object(const witness_object &w, const database& db); + struct validator_api_object { + validator_api_object(const validator_object &w, const database& db); - witness_api_object() = default; + validator_api_object() = default; - witness_object::id_type id; + validator_object::id_type id; account_name_type owner; time_point_sec created; std::string url; @@ -40,7 +40,7 @@ namespace graphene { namespace api { FC_REFLECT( - (graphene::api::witness_api_object), + (graphene::api::validator_api_object), (id)(owner)(created)(url)(votes)(penalty_percent)(counted_votes)(virtual_last_update)(virtual_position)(virtual_scheduled_time) (total_missed)(last_aslot)(last_confirmed_block_num)(signing_key)(props) (last_work)(running_version)(hardfork_version_vote)(hardfork_time_vote) diff --git a/libraries/api/witness_api_object.cpp b/libraries/api/witness_api_object.cpp index 72c1878c4d..41cdd4a4a3 100644 --- a/libraries/api/witness_api_object.cpp +++ b/libraries/api/witness_api_object.cpp @@ -1,7 +1,7 @@ #include namespace graphene { namespace api { - witness_api_object::witness_api_object(const witness_object &w, const database& db) + validator_api_object::validator_api_object(const witness_object &w, const database& db) : id(w.id), owner(w.owner), created(w.created), url(to_string(w.url)), total_missed(w.total_missed), last_aslot(w.last_aslot), last_confirmed_block_num(w.last_confirmed_block_num), diff --git a/libraries/chain/chain_evaluator.cpp b/libraries/chain/chain_evaluator.cpp index c5fcbb56e1..66c9148b9d 100644 --- a/libraries/chain/chain_evaluator.cpp +++ b/libraries/chain/chain_evaluator.cpp @@ -79,7 +79,7 @@ namespace graphene { namespace chain { } const auto& v_share_price = _db.get_dynamic_global_properties().get_vesting_share_price(); - const auto& median_props = _db.get_witness_schedule_object().median_props; + const auto& median_props = _db.get_validator_schedule_object().median_props; auto target_delegation = median_props.create_account_delegation_ratio * median_props.account_creation_fee * v_share_price; @@ -306,7 +306,7 @@ namespace graphene { namespace chain { FC_ASSERT( _db.has_hardfork(CHAIN_HARDFORK_4), "award_evaluator not enabled until HF 4" ); try { database &_db = db(); - const auto& median_props = _db.get_witness_schedule_object().median_props; + const auto& median_props = _db.get_validator_schedule_object().median_props; const auto &initiator = _db.get_account(o.initiator); //if(_db.has_hardfork(CHAIN_HARDFORK_9))//can be deleted after fix in CHAIN_HARDFORK_11 // FC_ASSERT(!initiator.valid, "Account flagged as invalid"); @@ -399,7 +399,7 @@ namespace graphene { namespace chain { try { database &_db = db(); const dynamic_global_property_object &dgpo = _db.get_dynamic_global_properties(); - const auto& median_props = _db.get_witness_schedule_object().median_props; + const auto& median_props = _db.get_validator_schedule_object().median_props; const auto &initiator = _db.get_account(o.initiator); //if(_db.has_hardfork(CHAIN_HARDFORK_9))//can be deleted after fix in CHAIN_HARDFORK_11 // FC_ASSERT(!initiator.valid, "Account flagged as invalid"); @@ -870,7 +870,7 @@ namespace graphene { namespace chain { //VIZ support anonymous account creation if(CHAIN_ANONYMOUS_ACCOUNT==to_account.name){ - const auto& median_props = _db.get_witness_schedule_object().median_props; + const auto& median_props = _db.get_validator_schedule_object().median_props; FC_ASSERT(o.amount >= median_props.account_creation_fee, "Inssufficient amount ${f} required, ${p} provided.", ("f", median_props.account_creation_fee)("p", o.amount)); @@ -968,7 +968,7 @@ namespace graphene { namespace chain { void withdraw_vesting_evaluator::do_apply(const withdraw_vesting_operation &o) { database &_db = db(); - const auto& median_props = _db.get_witness_schedule_object().median_props; + const auto& median_props = _db.get_validator_schedule_object().median_props; const auto &account = _db.get_account(o.account); //if(_db.has_hardfork(CHAIN_HARDFORK_9))//can be deleted after fix in CHAIN_HARDFORK_11 @@ -1076,7 +1076,7 @@ namespace graphene { namespace chain { FC_CAPTURE_AND_RETHROW() } - void account_witness_proxy_evaluator::do_apply(const account_witness_proxy_operation &o) { + void account_validator_proxy_evaluator::do_apply(const account_validator_proxy_operation &o) { database &_db = db(); const auto &account = _db.get_account(o.account); //if(_db.has_hardfork(CHAIN_HARDFORK_9))//can be deleted after fix in CHAIN_HARDFORK_11 @@ -1089,7 +1089,7 @@ namespace graphene { namespace chain { for (int i = 0; i < CHAIN_MAX_PROXY_RECURSION_DEPTH; ++i) { delta[i + 1] = -account.proxied_vsf_votes[i]; } - _db.adjust_proxied_witness_votes(account, delta); + _db.adjust_proxied_validator_votes(account, delta); if (o.proxy.size()) { const auto &new_proxy = _db.get_account(o.proxy); @@ -1108,7 +1108,7 @@ namespace graphene { namespace chain { } /// clear all individual vote records - _db.clear_witness_votes(account); + _db.clear_validator_votes(account); _db.modify(account, [&](account_object &a) { a.proxy = o.proxy; @@ -1118,7 +1118,7 @@ namespace graphene { namespace chain { for (int i = 0; i <= CHAIN_MAX_PROXY_RECURSION_DEPTH; ++i) { delta[i] = -delta[i]; } - _db.adjust_proxied_witness_votes(account, delta); + _db.adjust_proxied_validator_votes(account, delta); } else { /// we are clearing the proxy which means we simply update the account _db.modify(account, [&](account_object &a) { a.proxy = o.proxy; @@ -1126,7 +1126,7 @@ namespace graphene { namespace chain { } } - void account_witness_vote_evaluator::do_apply(const account_witness_vote_operation &o) { + void account_validator_vote_evaluator::do_apply(const account_validator_vote_operation &o) { database &_db = db(); const auto &voter = _db.get_account(o.account); //if(_db.has_hardfork(CHAIN_HARDFORK_9))//can be deleted after fix in CHAIN_HARDFORK_11 @@ -1140,7 +1140,7 @@ namespace graphene { namespace chain { for (int i = 0; i < CHAIN_MAX_PROXY_RECURSION_DEPTH; ++i) { delta[i + 1] = -voter.proxied_vsf_votes[i]; } - _db.adjust_proxied_witness_votes(voter, delta); + _db.adjust_proxied_validator_votes(voter, delta); _db.modify(voter, [&](account_object &a) { a.proxy = CHAIN_PROXY_TO_SELF_ACCOUNT;//blank proxy }); @@ -1151,7 +1151,7 @@ namespace graphene { namespace chain { 0, "A proxy is currently set, please clear the proxy before voting for a witness."); } - const auto &witness = _db.get_witness(o.witness); + const auto &witness = _db.get_witness(o.validator); const auto &by_account_witness_idx = _db.get_index().indices().get(); auto itr = by_account_witness_idx.find(boost::make_tuple(voter.id, witness.id)); @@ -1170,9 +1170,9 @@ namespace graphene { namespace chain { if(_db.has_hardfork(CHAIN_HARDFORK_5)){ const auto &vidx = _db.get_index().indices().get(); - auto vitr = vidx.lower_bound(boost::make_tuple(voter.id, witness_id_type())); + auto vitr = vidx.lower_bound(boost::make_tuple(voter.id, validator_id_type())); while (vitr != vidx.end() && vitr->account == voter.id) { - _db.adjust_witness_vote(_db.get(vitr->witness), -voter.witnesses_vote_weight); + _db.adjust_validator_vote(_db.get(vitr->witness), -voter.witnesses_vote_weight); ++vitr; } @@ -1191,17 +1191,17 @@ namespace graphene { namespace chain { }); const auto &vidx2 = _db.get_index().indices().get(); - auto vitr2 = vidx2.lower_bound(boost::make_tuple(voter.id, witness_id_type())); + auto vitr2 = vidx2.lower_bound(boost::make_tuple(voter.id, validator_id_type())); while (vitr2 != vidx2.end() && vitr2->account == voter.id) { - _db.adjust_witness_vote(_db.get(vitr2->witness), voter.witnesses_vote_weight); + _db.adjust_validator_vote(_db.get(vitr2->witness), voter.witnesses_vote_weight); ++vitr2; } } else if(_db.has_hardfork(CHAIN_HARDFORK_4)){ const auto &vidx = _db.get_index().indices().get(); - auto vitr = vidx.lower_bound(boost::make_tuple(voter.id, witness_id_type())); + auto vitr = vidx.lower_bound(boost::make_tuple(voter.id, validator_id_type())); while (vitr != vidx.end() && vitr->account == voter.id) { - _db.adjust_witness_vote(_db.get(vitr->witness), -voter.witness_vote_fair_weight_prehf5()); + _db.adjust_validator_vote(_db.get(vitr->witness), -voter.witness_vote_fair_weight_prehf5()); ++vitr; } @@ -1216,9 +1216,9 @@ namespace graphene { namespace chain { }); const auto &vidx2 = _db.get_index().indices().get(); - auto vitr2 = vidx2.lower_bound(boost::make_tuple(voter.id, witness_id_type())); + auto vitr2 = vidx2.lower_bound(boost::make_tuple(voter.id, validator_id_type())); while (vitr2 != vidx2.end() && vitr2->account == voter.id) { - _db.adjust_witness_vote(_db.get(vitr2->witness), voter.witness_vote_fair_weight_prehf5()); + _db.adjust_validator_vote(_db.get(vitr2->witness), voter.witness_vote_fair_weight_prehf5()); ++vitr2; } } @@ -1228,7 +1228,7 @@ namespace graphene { namespace chain { v.account = voter.id; v.vote_created_block = _db.head_block_num(); }); - _db.adjust_witness_vote(witness, voter.witness_vote_weight()); + _db.adjust_validator_vote(witness, voter.witness_vote_weight()); _db.modify(voter, [&](account_object &a) { a.witnesses_voted_for++; }); @@ -1238,9 +1238,9 @@ namespace graphene { namespace chain { if(_db.has_hardfork(CHAIN_HARDFORK_5)){ const auto &vidx = _db.get_index().indices().get(); - auto vitr = vidx.lower_bound(boost::make_tuple(voter.id, witness_id_type())); + auto vitr = vidx.lower_bound(boost::make_tuple(voter.id, validator_id_type())); while (vitr != vidx.end() && vitr->account == voter.id) { - _db.adjust_witness_vote(_db.get(vitr->witness), -voter.witnesses_vote_weight); + _db.adjust_validator_vote(_db.get(vitr->witness), -voter.witnesses_vote_weight); ++vitr; } @@ -1255,17 +1255,17 @@ namespace graphene { namespace chain { }); const auto &vidx2 = _db.get_index().indices().get(); - auto vitr2 = vidx2.lower_bound(boost::make_tuple(voter.id, witness_id_type())); + auto vitr2 = vidx2.lower_bound(boost::make_tuple(voter.id, validator_id_type())); while (vitr2 != vidx2.end() && vitr2->account == voter.id) { - _db.adjust_witness_vote(_db.get(vitr2->witness), voter.witnesses_vote_weight); + _db.adjust_validator_vote(_db.get(vitr2->witness), voter.witnesses_vote_weight); ++vitr2; } } else if(_db.has_hardfork(CHAIN_HARDFORK_4)){ const auto &vidx = _db.get_index().indices().get(); - auto vitr = vidx.lower_bound(boost::make_tuple(voter.id, witness_id_type())); + auto vitr = vidx.lower_bound(boost::make_tuple(voter.id, validator_id_type())); while (vitr != vidx.end() && vitr->account == voter.id) { - _db.adjust_witness_vote(_db.get(vitr->witness), -voter.witness_vote_fair_weight_prehf5()); + _db.adjust_validator_vote(_db.get(vitr->witness), -voter.witness_vote_fair_weight_prehf5()); ++vitr; } @@ -1276,14 +1276,14 @@ namespace graphene { namespace chain { }); const auto &vidx2 = _db.get_index().indices().get(); - auto vitr2 = vidx2.lower_bound(boost::make_tuple(voter.id, witness_id_type())); + auto vitr2 = vidx2.lower_bound(boost::make_tuple(voter.id, validator_id_type())); while (vitr2 != vidx2.end() && vitr2->account == voter.id) { - _db.adjust_witness_vote(_db.get(vitr2->witness), voter.witness_vote_fair_weight_prehf5()); + _db.adjust_validator_vote(_db.get(vitr2->witness), voter.witness_vote_fair_weight_prehf5()); ++vitr2; } } else{ - _db.adjust_witness_vote(witness, -voter.witness_vote_weight()); + _db.adjust_validator_vote(witness, -voter.witness_vote_weight()); _db.modify(voter, [&](account_object &a) { a.witnesses_voted_for--; }); @@ -1298,7 +1298,7 @@ namespace graphene { namespace chain { try { const auto &content = _db.get_content(o.author, o.permlink); const auto &voter = _db.get_account(o.voter); - const auto& median_props = _db.get_witness_schedule_object().median_props; + const auto& median_props = _db.get_validator_schedule_object().median_props; const auto &content_vote_idx = _db.get_index().indices().get(); auto itr = content_vote_idx.find(std::make_tuple(content.id, voter.id)); @@ -1566,7 +1566,7 @@ namespace graphene { namespace chain { o.recovery_account, "Cannot recover an account that does not have you as there recovery partner."); else // Empty string recovery account defaults to top witness FC_ASSERT( - _db.get_index().indices().get().begin()->owner == + _db.get_index().indices().get().begin()->owner == o.recovery_account, "Top witness must recover an account with no recovery partner."); const auto &recovery_request_idx = _db.get_index().indices().get(); @@ -1704,7 +1704,7 @@ namespace graphene { namespace chain { FC_ASSERT(op.vesting_shares.amount != 0,"Delegation did not exist for initiate revoke."); } - const auto& median_props = _db.get_witness_schedule_object().median_props; + const auto& median_props = _db.get_validator_schedule_object().median_props; const auto v_share_price = _db.get_dynamic_global_properties().get_vesting_share_price(); auto min_delegation = median_props.min_delegation * v_share_price; @@ -1789,7 +1789,7 @@ namespace graphene { namespace chain { // FC_ASSERT(!account_seller.valid, "Account flagged as invalid") if(_db.has_hardfork(CHAIN_HARDFORK_11)){//new auction mechanics while account have time to sale start if(""==account.account_seller){//pay fee if seller is empty - const auto& median_props = _db.get_witness_schedule_object().median_props; + const auto& median_props = _db.get_validator_schedule_object().median_props; const dynamic_global_property_object &dgp = _db.get_dynamic_global_properties(); FC_ASSERT(account.balance >= @@ -1875,7 +1875,7 @@ namespace graphene { namespace chain { else{//old behaviour if(_db.has_hardfork(CHAIN_HARDFORK_9)){ if(""==account.account_seller){ - const auto& median_props = _db.get_witness_schedule_object().median_props; + const auto& median_props = _db.get_validator_schedule_object().median_props; const dynamic_global_property_object &dgp = _db.get_dynamic_global_properties(); FC_ASSERT(account.balance >= @@ -1911,7 +1911,7 @@ namespace graphene { namespace chain { _db.get_account(op.target_buyer); // validate existence if(""==account.account_seller){//pay fee if seller is empty - const auto& median_props = _db.get_witness_schedule_object().median_props; + const auto& median_props = _db.get_validator_schedule_object().median_props; const dynamic_global_property_object &dgp = _db.get_dynamic_global_properties(); FC_ASSERT(account.balance >= @@ -2068,7 +2068,7 @@ namespace graphene { namespace chain { // FC_ASSERT(!subaccount_seller.valid, "Account flagged as invalid") if(_db.has_hardfork(CHAIN_HARDFORK_9)){ if(""==account.subaccount_seller){ - const auto& median_props = _db.get_witness_schedule_object().median_props; + const auto& median_props = _db.get_validator_schedule_object().median_props; const dynamic_global_property_object &dgp = _db.get_dynamic_global_properties(); FC_ASSERT(account.balance >= @@ -2091,7 +2091,7 @@ namespace graphene { namespace chain { const auto& buyer = _db.get_account(op.buyer); //if(_db.has_hardfork(CHAIN_HARDFORK_9))//can be deleted after fix in CHAIN_HARDFORK_11 // FC_ASSERT(!buyer.valid, "Account flagged as invalid"); - const auto& median_props = _db.get_witness_schedule_object().median_props; + const auto& median_props = _db.get_validator_schedule_object().median_props; FC_ASSERT(op.account_authorities_key != public_key_type(), "Account authorities key cannot be blank."); diff --git a/libraries/chain/chain_properties_evaluators.cpp b/libraries/chain/chain_properties_evaluators.cpp index 4645370151..701f71e7be 100644 --- a/libraries/chain/chain_properties_evaluators.cpp +++ b/libraries/chain/chain_properties_evaluators.cpp @@ -4,30 +4,30 @@ namespace graphene { namespace chain { - void witness_update_evaluator::do_apply(const witness_update_operation& o) { + void validator_update_evaluator::do_apply(const validator_update_operation& o) { const auto& owner = _db.get_account(o.owner); // verify owner exists - const auto &idx = _db.get_index().indices().get(); + const auto &idx = _db.get_index().indices().get(); auto itr = idx.find(o.owner); if (itr != idx.end()) { - _db.modify(*itr, [&](witness_object& w) { + _db.modify(*itr, [&](validator_object& w) { from_string(w.url, o.url); w.signing_key = o.block_signing_key; }); } else { if(_db.has_hardfork(CHAIN_HARDFORK_9)){ - const auto& median_props = _db.get_witness_schedule_object().median_props; + const auto& median_props = _db.get_validator_schedule_object().median_props; const dynamic_global_property_object &dgp = _db.get_dynamic_global_properties(); FC_ASSERT(owner.balance >= - median_props.witness_declaration_fee, "Account does not have sufficient funds to declare himself as witness: required ${a}.",("a",median_props.witness_declaration_fee)); + median_props.validator_declaration_fee, "Account does not have sufficient funds to declare himself as witness: required ${a}.",("a",median_props.validator_declaration_fee)); - _db.adjust_balance(owner, -median_props.witness_declaration_fee); + _db.adjust_balance(owner, -median_props.validator_declaration_fee); _db.modify(dgp, [&](dynamic_global_property_object &dgp) { - dgp.committee_fund += median_props.witness_declaration_fee; + dgp.committee_fund += median_props.validator_declaration_fee; }); } - _db.create([&](witness_object& w) { + _db.create([&](validator_object& w) { w.owner = o.owner; from_string(w.url, o.url); w.signing_key = o.block_signing_key; @@ -39,14 +39,14 @@ namespace graphene { namespace chain { void chain_properties_update_evaluator::do_apply(const chain_properties_update_operation& o) { _db.get_account(o.owner); // verify owner exists - const auto &idx = _db.get_index().indices().get(); + const auto &idx = _db.get_index().indices().get(); auto itr = idx.find(o.owner); if (itr != idx.end()) { - _db.modify(*itr, [&](witness_object& w) { + _db.modify(*itr, [&](validator_object& w) { w.props = o.props; }); } else { - _db.create([&](witness_object& w) { + _db.create([&](validator_object& w) { w.owner = o.owner; w.created = _db.head_block_time(); w.props = o.props; @@ -94,14 +94,14 @@ namespace graphene { namespace chain { FC_ASSERT( _db.has_hardfork(CHAIN_HARDFORK_4), "versioned_chain_properties_update_evaluator not enabled until HF 4" ); _db.get_account(o.owner); // verify owner exists - const auto &idx = _db.get_index().indices().get(); + const auto &idx = _db.get_index().indices().get(); auto itr = idx.find(o.owner); if (itr != idx.end()) { - _db.modify(*itr, [&](witness_object& w) { + _db.modify(*itr, [&](validator_object& w) { o.props.visit(chain_properties_update(_db, w.props)); }); } else { - _db.create([&](witness_object& w) { + _db.create([&](validator_object& w) { w.owner = o.owner; w.created = _db.head_block_time(); o.props.visit(chain_properties_update(_db, w.props)); @@ -113,11 +113,11 @@ namespace graphene { namespace chain { ASSERT_REQ_HF(CHAIN_HARDFORK_13, "set_reward_sharing_operation"); _db.get_account(o.owner); // verify account exists - const auto& idx = _db.get_index().indices().get(); + const auto& idx = _db.get_index().indices().get(); auto itr = idx.find(o.owner); FC_ASSERT(itr != idx.end(), "Account ${a} is not a registered validator", ("a", o.owner)); - _db.modify(*itr, [&](witness_object& w) { + _db.modify(*itr, [&](validator_object& w) { w.sharing_rate = o.sharing_rate; }); } diff --git a/libraries/chain/committee_evaluator.cpp b/libraries/chain/committee_evaluator.cpp index b5f7e79bd5..0fd7b3987d 100644 --- a/libraries/chain/committee_evaluator.cpp +++ b/libraries/chain/committee_evaluator.cpp @@ -6,7 +6,7 @@ namespace graphene { namespace chain { void committee_worker_create_request_evaluator::do_apply(const committee_worker_create_request_operation& o) { - const auto& median_props = _db.get_witness_schedule_object().median_props; + const auto& median_props = _db.get_validator_schedule_object().median_props; const auto &creator = _db.get_account(o.creator); _db.get_account(o.worker); diff --git a/libraries/chain/database.cpp b/libraries/chain/database.cpp index e162e5c09e..1f7b88b2df 100644 --- a/libraries/chain/database.cpp +++ b/libraries/chain/database.cpp @@ -437,11 +437,11 @@ namespace graphene { namespace chain { // cannot fix this. Detect and repair it here on startup. if (head_block_num() > 0) { const dynamic_global_property_object &startup_dgp = get_dynamic_global_properties(); - const witness_schedule_object &startup_wso = get_witness_schedule_object(); + const validator_schedule_object &startup_wso = get_validator_schedule_object(); bool schedule_broken = false; - for (int i = 0; i < startup_wso.num_scheduled_witnesses; i += CHAIN_BLOCK_WITNESS_REPEAT) { - if (startup_wso.current_shuffled_witnesses[i] == account_name_type()) { + for (int i = 0; i < startup_wso.num_scheduled_validators; i += CHAIN_BLOCK_WITNESS_REPEAT) { + if (startup_wso.current_shuffled_validators[i] == account_name_type()) { schedule_broken = true; break; } @@ -465,12 +465,12 @@ namespace graphene { namespace chain { } // Fill all schedule slots with committee - modify(startup_wso, [&](witness_schedule_object &_wso) { + modify(startup_wso, [&](validator_schedule_object &_wso) { for (int i = 0; i < CHAIN_MAX_WITNESSES * CHAIN_BLOCK_WITNESS_REPEAT; i++) { - _wso.current_shuffled_witnesses[i] = CHAIN_EMERGENCY_WITNESS_ACCOUNT; + _wso.current_shuffled_validators[i] = CHAIN_EMERGENCY_WITNESS_ACCOUNT; } - _wso.num_scheduled_witnesses = CHAIN_MAX_WITNESSES * CHAIN_BLOCK_WITNESS_REPEAT; - _wso.next_shuffle_block_num = head_block_num() + _wso.num_scheduled_witnesses; + _wso.num_scheduled_validators = CHAIN_MAX_WITNESSES * CHAIN_BLOCK_WITNESS_REPEAT; + _wso.next_shuffle_block_num = head_block_num() + _wso.num_scheduled_validators; }); wlog("EMERGENCY SCHEDULE RECOVERY: schedule repaired, all ${n} slots set to committee", @@ -817,7 +817,7 @@ namespace graphene { namespace chain { // Use the resize barrier to pause ALL database operations. // This is stronger than with_strong_write_lock: it also blocks - // lockless reads (e.g. get_slot_at_time, get_scheduled_witness, + // lockless reads (e.g. get_slot_at_time, get_scheduled_validator, // find_account in _generate_block and witness plugin) that do not // acquire any chainbase lock. After begin_resize_barrier() // returns, no thread holds any reference into shared memory. @@ -1151,14 +1151,14 @@ namespace graphene { namespace chain { return CHAIN_ID; } - const witness_object &database::get_witness(const account_name_type &name) const { + const validator_object &database::get_witness(const account_name_type &name) const { try { - return get(name); + return get(name); } FC_CAPTURE_AND_RETHROW((name)) } - const witness_object *database::find_witness(const account_name_type &name) const { - return find(name); + const validator_object *database::find_witness(const account_name_type &name) const { + return find(name); } const account_object &database::get_account(const account_name_type &name) const { @@ -1218,9 +1218,9 @@ namespace graphene { namespace chain { } FC_CAPTURE_AND_RETHROW() } - const witness_schedule_object &database::get_witness_schedule_object() const { + const validator_schedule_object &database::get_validator_schedule_object() const { try { - return get(); + return get(); } FC_CAPTURE_AND_RETHROW() } @@ -1237,7 +1237,7 @@ namespace graphene { namespace chain { bool database::update_account_bandwidth(const account_object &a, uint32_t trx_size) { const auto &props = get_dynamic_global_properties(); bool has_bandwidth = true; - const witness_schedule_object &consensus = get_witness_schedule_object(); + const validator_schedule_object &consensus = get_validator_schedule_object(); if (props.total_vesting_shares.amount > 0) { share_type new_bandwidth; @@ -2178,7 +2178,7 @@ namespace graphene { namespace chain { uint32_t slot_num = get_slot_at_time(when); FC_ASSERT(slot_num > 0); - string scheduled_witness = get_scheduled_witness(slot_num); + string scheduled_witness = get_scheduled_validator(slot_num); FC_ASSERT(scheduled_witness == witness_owner); const auto &witness_obj = get_witness(witness_owner); @@ -2465,12 +2465,12 @@ namespace graphene { namespace chain { CHAIN_TRY_NOTIFY(on_applied_transaction, tx) } - account_name_type database::get_scheduled_witness(uint32_t slot_num) const { + account_name_type database::get_scheduled_validator(uint32_t slot_num) const { const dynamic_global_property_object &dpo = get_dynamic_global_properties(); - const witness_schedule_object &wso = get_witness_schedule_object(); + const validator_schedule_object &wso = get_validator_schedule_object(); uint64_t current_aslot = dpo.current_aslot + slot_num; - return wso.current_shuffled_witnesses[current_aslot % - wso.num_scheduled_witnesses]; + return wso.current_shuffled_validators[current_aslot % + wso.num_scheduled_validators]; } fc::time_point_sec database::get_slot_time(uint32_t slot_num) const { @@ -2554,7 +2554,7 @@ namespace graphene { namespace chain { props.total_vesting_shares += new_vesting; }); - adjust_proxied_witness_votes(to_account, new_vesting.amount); + adjust_proxied_validator_votes(to_account, new_vesting.amount); return new_vesting; } @@ -2565,7 +2565,7 @@ namespace graphene { namespace chain { if ((head_block_num() % CHAIN_BLOCKS_PER_HOUR ) != 0) return; uint32_t bandwidth_reserve_candidates = 1; const auto &gprops = get_dynamic_global_properties(); - const witness_schedule_object &consensus = get_witness_schedule_object(); + const validator_schedule_object &consensus = get_validator_schedule_object(); const auto &widx = get_index().indices().get(); for (auto itr = widx.begin(); itr != widx.end(); ++itr) { @@ -2648,7 +2648,7 @@ namespace graphene { namespace chain { void database::committee_processing() { const auto &props = get_dynamic_global_properties(); - const witness_schedule_object &consensus = get_witness_schedule_object(); + const validator_schedule_object &consensus = get_validator_schedule_object(); const auto &idx0 = get_index().indices().get(); auto itr0 = idx0.lower_bound(0); while (itr0 != idx0.end() && @@ -2775,7 +2775,7 @@ namespace graphene { namespace chain { ++itr; if(current.expires <= head_block_time()){ const auto &witness_obj = get_witness(current.witness); - modify(witness_obj, [&](witness_object &w) { + modify(witness_obj, [&](validator_object &w) { w.penalty_percent-=current.penalty_percent; w.counted_votes=(fc::uint128_t(w.votes) - (fc::uint128_t(w.votes) * std::min(w.penalty_percent,uint32_t(CHAIN_100_PERCENT)) / CHAIN_100_PERCENT )).to_uint64(); }); @@ -2791,10 +2791,10 @@ namespace graphene { namespace chain { support_witnesses.reserve(CHAIN_MAX_WITNESSES); /// Add the highest voted witnesses - flat_set selected_voted; + flat_set selected_voted; selected_voted.reserve(CHAIN_MAX_TOP_WITNESSES); - const auto &widx = get_index().indices().get(); + const auto &widx = get_index().indices().get(); for (auto itr = widx.begin(); itr != widx.end() && selected_voted.size() < CHAIN_MAX_TOP_WITNESSES; @@ -2810,13 +2810,13 @@ namespace graphene { namespace chain { } selected_voted.insert(itr->id); active_witnesses.push_back(itr->owner); - modify(*itr, [&](witness_object &wo) { wo.schedule = witness_object::top; }); + modify(*itr, [&](validator_object &wo) { wo.schedule = validator_object::top; }); } /// Add the running witnesses in the lead - const witness_schedule_object &wso = get_witness_schedule_object(); + const validator_schedule_object &wso = get_validator_schedule_object(); fc::uint128_t new_virtual_time = wso.current_virtual_time; - const auto &schedule_idx = get_index().indices().get(); + const auto &schedule_idx = get_index().indices().get(); auto sitr = schedule_idx.begin(); vector processed_witnesses; for (auto witness_count = selected_voted.size(); @@ -2838,7 +2838,7 @@ namespace graphene { namespace chain { if (selected_voted.find(sitr->id) == selected_voted.end()) { support_witnesses.push_back(sitr->owner); - modify(*sitr, [&](witness_object &wo) { wo.schedule = witness_object::support; }); + modify(*sitr, [&](validator_object &wo) { wo.schedule = validator_object::support; }); ++witness_count; } } @@ -2854,7 +2854,7 @@ namespace graphene { namespace chain { reset_virtual_time = true; /// overflow break; } - modify(*(*itr), [&](witness_object &wo) { + modify(*(*itr), [&](validator_object &wo) { wo.virtual_position = fc::uint128_t(); wo.virtual_last_update = new_virtual_time; wo.virtual_scheduled_time = new_virtual_scheduled_time; @@ -2875,8 +2875,8 @@ namespace graphene { namespace chain { const dynamic_global_property_object &_dgp = get_dynamic_global_properties(); - for (uint32_t i = 0; i < wso.num_scheduled_witnesses; i+=CHAIN_BLOCK_WITNESS_REPEAT) { - auto wname = wso.current_shuffled_witnesses[i]; + for (uint32_t i = 0; i < wso.num_scheduled_validators; i+=CHAIN_BLOCK_WITNESS_REPEAT) { + auto wname = wso.current_shuffled_validators[i]; // During emergency mode, exclude committee witness from hardfork vote tally. // Committee occupies multiple schedule slots but is a single entity with @@ -2952,7 +2952,7 @@ namespace graphene { namespace chain { if (_debug_block_production) ilog("DEBUG_CRASH: schedule normal build: active=${a} support=${s}", ("a", active_witnesses.size())("s", support_witnesses.size())); - modify(wso, [&](witness_schedule_object &_wso) { + modify(wso, [&](validator_schedule_object &_wso) { // active witnesses has exactly CHAIN_MAX_WITNESSES elements, asserted above size_t j = 0; size_t support_witnesses_count = support_witnesses.size(); @@ -2963,14 +2963,14 @@ namespace graphene { namespace chain { if(active_witnesses_count > 0){ --active_witnesses_count; for(int repeat=0; repeat < CHAIN_BLOCK_WITNESS_REPEAT; ++repeat){ - _wso.current_shuffled_witnesses[j] = active_witnesses[i]; + _wso.current_shuffled_validators[j] = active_witnesses[i]; ++j; } } if(support_witnesses_count > 0){ --support_witnesses_count; for(int repeat=0; repeat < CHAIN_BLOCK_WITNESS_REPEAT; ++repeat){ - _wso.current_shuffled_witnesses[j] = support_witnesses[i]; + _wso.current_shuffled_validators[j] = support_witnesses[i]; ++j; } } @@ -2978,16 +2978,16 @@ namespace graphene { namespace chain { for (size_t i = sum_witnesses_count * CHAIN_BLOCK_WITNESS_REPEAT; i < ( CHAIN_MAX_WITNESSES * CHAIN_BLOCK_WITNESS_REPEAT ); i++) { - _wso.current_shuffled_witnesses[i] = account_name_type(); + _wso.current_shuffled_validators[i] = account_name_type(); } - _wso.num_scheduled_witnesses = std::max(sum_witnesses_count * CHAIN_BLOCK_WITNESS_REPEAT , 1); + _wso.num_scheduled_validators = std::max(sum_witnesses_count * CHAIN_BLOCK_WITNESS_REPEAT , 1); /* // VIZ remove randomization /// shuffle current shuffled witnesses auto now_hi = uint64_t(head_block_time().sec_since_epoch()) << 32; - for (uint32_t i = 0; i < _wso.num_scheduled_witnesses; ++i) { + for (uint32_t i = 0; i < _wso.num_scheduled_validators; ++i) { /// High performance random generator /// http://xorshift.di.unimi.it/ uint64_t k = now_hi + uint64_t(i) * 2685821657736338717ULL; @@ -2996,21 +2996,21 @@ namespace graphene { namespace chain { k ^= (k >> 27); k *= 2685821657736338717ULL; - uint32_t jmax = _wso.num_scheduled_witnesses - i; + uint32_t jmax = _wso.num_scheduled_validators - i; uint32_t j = i + k % jmax; - std::swap(_wso.current_shuffled_witnesses[i], - _wso.current_shuffled_witnesses[j]); + std::swap(_wso.current_shuffled_validators[i], + _wso.current_shuffled_validators[j]); } */ _wso.current_virtual_time = new_virtual_time; _wso.next_shuffle_block_num = - head_block_num() + _wso.num_scheduled_witnesses; + head_block_num() + _wso.num_scheduled_validators; _wso.majority_version = majority_version; }); if (_debug_block_production) ilog("DEBUG_CRASH: schedule normal build done, num_scheduled=${n}", - ("n", wso.num_scheduled_witnesses)); + ("n", wso.num_scheduled_validators)); // === HARDFORK 12: EMERGENCY HYBRID SCHEDULE === // Must run BEFORE update_median_witness_props() because the normal @@ -3020,11 +3020,11 @@ namespace graphene { namespace chain { if (has_hardfork(CHAIN_HARDFORK_12) && emergency_dgp.emergency_consensus_active) { if (_debug_block_production) ilog("DEBUG_CRASH: hybrid override ENTER"); - const witness_schedule_object &emergency_wso = get_witness_schedule_object(); + const validator_schedule_object &emergency_wso = get_validator_schedule_object(); uint32_t real_witness_slots = 0; uint32_t committee_slots = 0; - modify(emergency_wso, [&](witness_schedule_object &_wso) { + modify(emergency_wso, [&](validator_schedule_object &_wso) { // First pass: replace unavailable/empty slots with committee // Iterate the FULL schedule (CHAIN_MAX_WITNESSES), not just @@ -3034,14 +3034,14 @@ namespace graphene { namespace chain { for (int i = 0; i < CHAIN_MAX_WITNESSES; i += CHAIN_BLOCK_WITNESS_REPEAT) { // Read from slot i, but only if within current num_scheduled_witnesses - const auto &wname = (i < _wso.num_scheduled_witnesses) - ? _wso.current_shuffled_witnesses[i] + const auto &wname = (i < _wso.num_scheduled_validators) + ? _wso.current_shuffled_validators[i] : account_name_type(); if (wname == account_name_type()) { // Empty slot -> assign committee for (int j = 0; j < CHAIN_BLOCK_WITNESS_REPEAT; ++j) { - _wso.current_shuffled_witnesses[i+j] = + _wso.current_shuffled_validators[i+j] = CHAIN_EMERGENCY_WITNESS_ACCOUNT; } committee_slots++; @@ -3054,7 +3054,7 @@ namespace graphene { namespace chain { if (!witness_available) { for (int j = 0; j < CHAIN_BLOCK_WITNESS_REPEAT; ++j) { - _wso.current_shuffled_witnesses[i+j] = + _wso.current_shuffled_validators[i+j] = CHAIN_EMERGENCY_WITNESS_ACCOUNT; } committee_slots++; @@ -3065,9 +3065,9 @@ namespace graphene { namespace chain { // Expand num_scheduled_witnesses to CHAIN_MAX_WITNESSES so that // committee slots are included in the production cycle. - _wso.num_scheduled_witnesses = CHAIN_MAX_WITNESSES * CHAIN_BLOCK_WITNESS_REPEAT; + _wso.num_scheduled_validators = CHAIN_MAX_WITNESSES * CHAIN_BLOCK_WITNESS_REPEAT; _wso.next_shuffle_block_num = - head_block_num() + _wso.num_scheduled_witnesses; + head_block_num() + _wso.num_scheduled_validators; dlog(DB_LOG_YELLOW "Emergency hybrid schedule: ${r} real witness slots, " "${c} committee slots" DB_LOG_RESET, @@ -3076,7 +3076,7 @@ namespace graphene { namespace chain { }); if (_debug_block_production) ilog("DEBUG_CRASH: hybrid override done, real=${r} committee=${c} num_scheduled=${n}", - ("r", real_witness_slots)("c", committee_slots)("n", emergency_wso.num_scheduled_witnesses)); + ("r", real_witness_slots)("c", committee_slots)("n", emergency_wso.num_scheduled_validators)); // Sync committee witness props/hardfork-vote with the latest median // and current hardfork state. This runs every schedule update so that @@ -3084,11 +3084,11 @@ namespace graphene { namespace chain { // committee's props stay in sync and don't skew the next median // computation. Also keeps hardfork vote aligned with the currently // applied on-chain version. - auto committee_wit_itr = get_index().indices().get().find(CHAIN_EMERGENCY_WITNESS_ACCOUNT); - if (committee_wit_itr != get_index().indices().get().end()) { + auto committee_wit_itr = get_index().indices().get().find(CHAIN_EMERGENCY_WITNESS_ACCOUNT); + if (committee_wit_itr != get_index().indices().get().end()) { const auto &latest_hfp = get_hardfork_property_object(); - const auto &latest_wso = get_witness_schedule_object(); - modify(*committee_wit_itr, [&](witness_object &w) { + const auto &latest_wso = get_validator_schedule_object(); + modify(*committee_wit_itr, [&](validator_object &w) { w.props = latest_wso.median_props; w.running_version = CHAIN_VERSION; w.hardfork_version_vote = latest_hfp.current_hardfork_version; @@ -3121,14 +3121,14 @@ namespace graphene { namespace chain { } void database::update_median_witness_props() { - const witness_schedule_object &wso = get_witness_schedule_object(); + const validator_schedule_object &wso = get_validator_schedule_object(); const dynamic_global_property_object &median_dgp = get_dynamic_global_properties(); /// fetch all witness objects (excluding committee during emergency) - vector active; - active.reserve(wso.num_scheduled_witnesses); - for (int i = 0; i < wso.num_scheduled_witnesses; i+=CHAIN_BLOCK_WITNESS_REPEAT) { - const auto &wname = wso.current_shuffled_witnesses[i]; + vector active; + active.reserve(wso.num_scheduled_validators); + for (int i = 0; i < wso.num_scheduled_validators; i+=CHAIN_BLOCK_WITNESS_REPEAT) { + const auto &wname = wso.current_shuffled_validators[i]; // During emergency mode, exclude committee witness from median computation. // Committee has default chain_properties (zero fees, zero sizes) and // occupies multiple schedule slots, which would skew the median. @@ -3173,14 +3173,14 @@ namespace graphene { namespace chain { calc_median(&chain_properties_init::vote_accounting_min_rshares); calc_median(&chain_properties_init::committee_request_approve_min_percent); if(has_hardfork(CHAIN_HARDFORK_4)){ - calc_median(&chain_properties_hf4::inflation_witness_percent); + calc_median(&chain_properties_hf4::inflation_validator_percent); calc_median(&chain_properties_hf4::inflation_ratio_committee_vs_reward_fund); calc_median(&chain_properties_hf4::inflation_recalc_period); } if(has_hardfork(CHAIN_HARDFORK_6)){ calc_median(&chain_properties_hf6::data_operations_cost_additional_bandwidth); - calc_median(&chain_properties_hf6::witness_miss_penalty_percent); - calc_median(&chain_properties_hf6::witness_miss_penalty_duration); + calc_median(&chain_properties_hf6::validator_miss_penalty_percent); + calc_median(&chain_properties_hf6::validator_miss_penalty_duration); } if(has_hardfork(CHAIN_HARDFORK_9)){ calc_median(&chain_properties_hf9::create_invite_min_balance); @@ -3188,14 +3188,14 @@ namespace graphene { namespace chain { calc_median(&chain_properties_hf9::create_paid_subscription_fee); calc_median(&chain_properties_hf9::account_on_sale_fee); calc_median(&chain_properties_hf9::subaccount_on_sale_fee); - calc_median(&chain_properties_hf9::witness_declaration_fee); + calc_median(&chain_properties_hf9::validator_declaration_fee); calc_median(&chain_properties_hf9::withdraw_intervals); } if(has_hardfork(CHAIN_HARDFORK_13)){ calc_median(&chain_properties_hf13::distribution_epoch_length); } - modify(wso, [&](witness_schedule_object &_wso) { + modify(wso, [&](validator_schedule_object &_wso) { _wso.median_props = median_props; }); @@ -3204,7 +3204,7 @@ namespace graphene { namespace chain { }); } - void database::adjust_proxied_witness_votes(const account_object &a, + void database::adjust_proxied_validator_votes(const account_object &a, const std::array &delta, int depth) { @@ -3223,18 +3223,18 @@ namespace graphene { namespace chain { } }); - adjust_proxied_witness_votes(proxy, delta, depth + 1); + adjust_proxied_validator_votes(proxy, delta, depth + 1); } else { share_type total_delta = 0; for (int i = CHAIN_MAX_PROXY_RECURSION_DEPTH - depth; i >= 0; --i) { total_delta += delta[i]; } - adjust_witness_votes(a, total_delta); + adjust_validator_votes(a, total_delta); } } - void database::adjust_proxied_witness_votes(const account_object &a, share_type delta, int depth) { + void database::adjust_proxied_validator_votes(const account_object &a, share_type delta, int depth) { if (a.proxy != CHAIN_PROXY_TO_SELF_ACCOUNT) { /// nested proxies are not supported, vote will not propagate if (depth >= CHAIN_MAX_PROXY_RECURSION_DEPTH) { @@ -3247,13 +3247,13 @@ namespace graphene { namespace chain { a.proxied_vsf_votes[depth] += delta; }); - adjust_proxied_witness_votes(proxy, delta, depth + 1); + adjust_proxied_validator_votes(proxy, delta, depth + 1); } else { - adjust_witness_votes(a, delta); + adjust_validator_votes(a, delta); } } - void database::adjust_witness_votes(const account_object &a, share_type delta) { + void database::adjust_validator_votes(const account_object &a, share_type delta) { if(has_hardfork(CHAIN_HARDFORK_5)){//need to clear witness vote weight if(a.witnesses_voted_for > 0){ share_type fair_delta = delta / a.witnesses_voted_for; @@ -3262,26 +3262,26 @@ namespace graphene { namespace chain { }); const auto &vidx = get_index().indices().get(); - auto itr = vidx.lower_bound(boost::make_tuple(a.id, witness_id_type())); + auto itr = vidx.lower_bound(boost::make_tuple(a.id, validator_id_type())); while (itr != vidx.end() && itr->account == a.id) { - adjust_witness_vote(get(itr->witness), fair_delta); + adjust_validator_vote(get(itr->witness), fair_delta); ++itr; } } } else{ const auto &vidx = get_index().indices().get(); - auto itr = vidx.lower_bound(boost::make_tuple(a.id, witness_id_type())); + auto itr = vidx.lower_bound(boost::make_tuple(a.id, validator_id_type())); while (itr != vidx.end() && itr->account == a.id) { - adjust_witness_vote(get(itr->witness), delta); + adjust_validator_vote(get(itr->witness), delta); ++itr; } } } - void database::adjust_witness_vote(const witness_object &witness, share_type delta) { - const witness_schedule_object &wso = get_witness_schedule_object(); - modify(witness, [&](witness_object &w) { + void database::adjust_validator_vote(const validator_object &witness, share_type delta) { + const validator_schedule_object &wso = get_validator_schedule_object(); + modify(witness, [&](validator_object &w) { auto delta_pos = w.counted_votes.value * (wso.current_virtual_time - w.virtual_last_update); w.virtual_position += delta_pos; @@ -3316,9 +3316,9 @@ namespace graphene { namespace chain { }); } - void database::clear_witness_votes(const account_object &a) { + void database::clear_validator_votes(const account_object &a) { const auto &vidx = get_index().indices().get(); - auto itr = vidx.lower_bound(boost::make_tuple(a.id, witness_id_type())); + auto itr = vidx.lower_bound(boost::make_tuple(a.id, validator_id_type())); while (itr != vidx.end() && itr->account == a.id) { const auto ¤t = *itr; ++itr; @@ -3518,7 +3518,7 @@ namespace graphene { namespace chain { a.vesting_shares.amount += to_deposit; }); - adjust_proxied_witness_votes(to_account, to_deposit); + adjust_proxied_validator_votes(to_account, to_deposit); push_virtual_operation(fill_vesting_withdraw_operation(from_account.name, to_account.name, asset(to_deposit, SHARES_SYMBOL), asset(to_deposit, SHARES_SYMBOL))); } @@ -3586,7 +3586,7 @@ namespace graphene { namespace chain { }); if (to_withdraw > 0) { - adjust_proxied_witness_votes(from_account, -to_withdraw); + adjust_proxied_validator_votes(from_account, -to_withdraw); push_virtual_operation(fill_vesting_withdraw_operation(from_account.name, from_account.name, asset(to_withdraw, SHARES_SYMBOL), converted_tokens)); } } @@ -3617,7 +3617,7 @@ namespace graphene { namespace chain { asset total_payout; if (reward_tokens > 0) { - const witness_schedule_object &props = get_witness_schedule_object(); + const validator_schedule_object &props = get_validator_schedule_object(); auto consensus_curation_percent=std::min(content.curation_percent,props.median_props.max_curation_percent); consensus_curation_percent=std::max(consensus_curation_percent,props.median_props.min_curation_percent); share_type curation_tokens = ((reward_tokens * @@ -3754,11 +3754,11 @@ namespace graphene { namespace chain { void database::process_inflation_recalc(){ const auto &props = get_dynamic_global_properties(); - const witness_schedule_object &consensus = get_witness_schedule_object(); + const validator_schedule_object &consensus = get_validator_schedule_object(); if(props.inflation_calc_block_num + consensus.median_props.inflation_recalc_period < props.head_block_number){ modify( props, [&]( dynamic_global_property_object& p ){ p.inflation_calc_block_num = props.head_block_number; - p.inflation_witness_percent = consensus.median_props.inflation_witness_percent; + p.inflation_validator_percent = consensus.median_props.inflation_validator_percent; p.inflation_ratio = consensus.median_props.inflation_ratio_committee_vs_reward_fund; }); } @@ -3768,7 +3768,7 @@ namespace graphene { namespace chain { const auto &props = get_dynamic_global_properties(); if(has_hardfork(CHAIN_HARDFORK_11)){//new emission model (fixed amount of digital asset per block) share_type digital_asset_per_block = int64_t( CHAIN_DIGITAL_ASSET_ISSUED_PER_BLOCK ); - auto witness_reward = ( digital_asset_per_block * props.inflation_witness_percent ) / CHAIN_100_PERCENT; + auto witness_reward = ( digital_asset_per_block * props.inflation_validator_percent ) / CHAIN_100_PERCENT; auto ratio_reward = digital_asset_per_block - witness_reward; auto committee_part = ( ratio_reward * props.inflation_ratio ) / CHAIN_100_PERCENT; auto reward_fund_part = ratio_reward - committee_part; @@ -3806,17 +3806,17 @@ namespace graphene { namespace chain { if (validator_token > 0) { auto validator_vshares = create_vesting(*witness_account, asset(validator_token, TOKEN_SYMBOL)); - push_virtual_operation(witness_reward_operation(cwit.owner, validator_vshares)); + push_virtual_operation(validator_reward_operation(cwit.owner, validator_vshares)); } if (stakeholder_token > 0) { - modify(cwit, [&](witness_object& w) { + modify(cwit, [&](validator_object& w) { w.pending_stakeholder_reward += stakeholder_token; }); } } else { auto witness_reward_shares = create_vesting(*witness_account, asset(witness_reward, TOKEN_SYMBOL)); - push_virtual_operation(witness_reward_operation(cwit.owner, witness_reward_shares)); + push_virtual_operation(validator_reward_operation(cwit.owner, witness_reward_shares)); } } else{ @@ -3836,7 +3836,7 @@ namespace graphene { namespace chain { share_type inflation_per_block = inflation_per_year / int64_t( CHAIN_BLOCKS_PER_YEAR ); if(has_hardfork(CHAIN_HARDFORK_4)){//consensus inflation model - auto witness_reward = ( inflation_per_block * props.inflation_witness_percent ) / CHAIN_100_PERCENT; + auto witness_reward = ( inflation_per_block * props.inflation_validator_percent ) / CHAIN_100_PERCENT; auto inflation_ratio_reward = inflation_per_block - witness_reward; auto committee_reward = ( inflation_ratio_reward * props.inflation_ratio ) / CHAIN_100_PERCENT; auto content_reward = inflation_ratio_reward - committee_reward; @@ -3865,7 +3865,7 @@ namespace graphene { namespace chain { ("w", cwit.owner)); } auto witness_reward_shares = create_vesting(*witness_account, asset(witness_reward, TOKEN_SYMBOL)); - push_virtual_operation(witness_reward_operation(cwit.owner,witness_reward_shares)); + push_virtual_operation(validator_reward_operation(cwit.owner,witness_reward_shares)); } else{ /*ilog( "Inflation status: props.head_block_number=${h1}, inflation_per_year=${h2}, new_supply=${h3}, inflation_per_block=${h4}", @@ -3893,13 +3893,13 @@ namespace graphene { namespace chain { }); auto witness_reward_shares = create_vesting(get_account(cwit.owner), asset(witness_reward, TOKEN_SYMBOL)); - push_virtual_operation(witness_reward_operation(cwit.owner,witness_reward_shares)); + push_virtual_operation(validator_reward_operation(cwit.owner,witness_reward_shares)); } } } void database::process_validator_epoch_distribution() { - const auto& wso = get_witness_schedule_object(); + const auto& wso = get_validator_schedule_object(); uint32_t epoch_length = wso.median_props.distribution_epoch_length; // Only run at epoch boundaries; guard against zero (shouldn't happen). @@ -3911,7 +3911,7 @@ namespace graphene { namespace chain { uint32_t epoch_start_block = head_block_num() - epoch_length + 1; const auto& vote_idx = get_index().indices().get(); - const auto& widx = get_index().indices().get(); + const auto& widx = get_index().indices().get(); for (const auto& wit : widx) { if (wit.pending_stakeholder_reward == 0) continue; @@ -3956,8 +3956,8 @@ namespace graphene { namespace chain { if (total_weighted == 0) { // No stakeholders — entire pool returns to validator. auto dust_shares = create_vesting(*witness_account, asset(total_token, TOKEN_SYMBOL)); - push_virtual_operation(witness_reward_operation(wit.owner, dust_shares)); - modify(wit, [&](witness_object& w) { w.pending_stakeholder_reward = 0; }); + push_virtual_operation(validator_reward_operation(wit.owner, dust_shares)); + modify(wit, [&](validator_object& w) { w.pending_stakeholder_reward = 0; }); continue; } @@ -3980,10 +3980,10 @@ namespace graphene { namespace chain { share_type dust_token = total_token - total_distributed_token; if (dust_token > 0) { auto dust_shares = create_vesting(*witness_account, asset(dust_token, TOKEN_SYMBOL)); - push_virtual_operation(witness_reward_operation(wit.owner, dust_shares)); + push_virtual_operation(validator_reward_operation(wit.owner, dust_shares)); } - modify(wit, [&](witness_object& w) { w.pending_stakeholder_reward = 0; }); + modify(wit, [&](validator_object& w) { w.pending_stakeholder_reward = 0; }); } } @@ -4466,9 +4466,9 @@ namespace graphene { namespace chain { _my->_evaluator_registry.register_evaluator(); _my->_evaluator_registry.register_evaluator(); _my->_evaluator_registry.register_evaluator(); - _my->_evaluator_registry.register_evaluator(); - _my->_evaluator_registry.register_evaluator(); - _my->_evaluator_registry.register_evaluator(); + _my->_evaluator_registry.register_evaluator(); + _my->_evaluator_registry.register_evaluator(); + _my->_evaluator_registry.register_evaluator(); _my->_evaluator_registry.register_evaluator(); _my->_evaluator_registry.register_evaluator(); _my->_evaluator_registry.register_evaluator(); @@ -4519,10 +4519,10 @@ namespace graphene { namespace chain { add_core_index(*this); add_core_index(*this); add_core_index(*this); - add_core_index(*this); + add_core_index(*this); add_core_index(*this); add_core_index(*this); - add_core_index(*this); + add_core_index(*this); add_core_index(*this); add_core_index(*this); add_core_index(*this); @@ -4651,10 +4651,10 @@ namespace graphene { namespace chain { auth.active.weight_threshold = 1; auth.regular.weight_threshold = 1; }); - create([&](witness_object &w) { + create([&](validator_object &w) { w.owner = CHAIN_COMMITTEE_ACCOUNT; w.signing_key = committee_public_key; - w.schedule = witness_object::top; + w.schedule = validator_object::top; }); create([&](account_object &a) { @@ -4718,10 +4718,10 @@ namespace graphene { namespace chain { auth.active = auth.master; auth.regular = auth.active; }); - create([&](witness_object &w) { + create([&](validator_object &w) { w.owner = name; w.signing_key = initiator_public_key; - w.schedule = witness_object::top; + w.schedule = validator_object::top; }); } } @@ -4757,8 +4757,8 @@ namespace graphene { namespace chain { }); // Create witness scheduler - create([&](witness_schedule_object &wso) { - wso.current_shuffled_witnesses[0] = CHAIN_COMMITTEE_ACCOUNT; + create([&](validator_schedule_object &wso) { + wso.current_shuffled_validators[0] = CHAIN_COMMITTEE_ACCOUNT; }); if(CHAIN_STARTUP_HARDFORKS>0){ @@ -4771,14 +4771,14 @@ namespace graphene { namespace chain { FC_ASSERT( hardfork_state.current_hardfork_version == _hardfork_versions[n], "Unexpected genesis hardfork state" ); - const auto& witness_idx = get_index().indices().get(); - vector wit_ids_to_update; + const auto& witness_idx = get_index().indices().get(); + vector wit_ids_to_update; for( auto it=witness_idx.begin(); it!=witness_idx.end(); ++it ) wit_ids_to_update.push_back(it->id); - for( witness_id_type wit_id : wit_ids_to_update ) + for( validator_id_type wit_id : wit_ids_to_update ) { - modify( get( wit_id ), [&]( witness_object& wit ) + modify( get( wit_id ), [&]( validator_object& wit ) { wit.running_version = _hardfork_versions[n]; wit.hardfork_version_vote = _hardfork_versions[n]; @@ -4787,14 +4787,14 @@ namespace graphene { namespace chain { } } else{ - const auto& witness_idx = get_index().indices().get(); - vector wit_ids_to_update; + const auto& witness_idx = get_index().indices().get(); + vector wit_ids_to_update; for( auto it=witness_idx.begin(); it!=witness_idx.end(); ++it ) wit_ids_to_update.push_back(it->id); - for( witness_id_type wit_id : wit_ids_to_update ) + for( validator_id_type wit_id : wit_ids_to_update ) { - modify( get( wit_id ), [&]( witness_object& wit ) + modify( get( wit_id ), [&]( validator_object& wit ) { wit.running_version = _hardfork_versions[0]; wit.hardfork_version_vote = _hardfork_versions[0]; @@ -5072,7 +5072,7 @@ namespace graphene { namespace chain { _validate_block(next_block, skip); - const witness_object &signing_witness = validate_block_header(skip, next_block); + const validator_object &signing_witness = validate_block_header(skip, next_block); _current_block_num = next_block_num; _current_trx_in_block = 0; @@ -5202,7 +5202,7 @@ namespace graphene { namespace chain { if (reported_version != signing_witness.running_version) { - modify(signing_witness, [&](witness_object &wo) { + modify(signing_witness, [&](validator_object &wo) { wo.running_version = reported_version; }); } @@ -5217,7 +5217,7 @@ namespace graphene { namespace chain { if (hfv.hf_version != signing_witness.hardfork_version_vote || hfv.hf_time != signing_witness.hardfork_time_vote) { - modify(signing_witness, [&](witness_object &wo) { + modify(signing_witness, [&](validator_object &wo) { wo.hardfork_version_vote = hfv.hf_version; wo.hardfork_time_vote = hfv.hf_time; }); @@ -5258,7 +5258,7 @@ namespace graphene { namespace chain { auto trx_size = fc::raw::pack_size(trx); - const witness_schedule_object &consensus = get_witness_schedule_object(); + const validator_schedule_object &consensus = get_validator_schedule_object(); for (const auto& auth : required) { const auto& acnt = get_account(auth); @@ -5305,13 +5305,13 @@ namespace graphene { namespace chain { notify_post_apply_operation(note); } - const witness_object &database::validate_block_header(uint32_t skip, const signed_block &next_block) const { + const validator_object &database::validate_block_header(uint32_t skip, const signed_block &next_block) const { try { FC_ASSERT(head_block_id() == next_block.previous, "", ("head_block_id", head_block_id())("next.prev", next_block.previous)); FC_ASSERT(head_block_time() < next_block.timestamp, "", ("head_block_time", head_block_time())("next", next_block.timestamp)("blocknum", next_block.block_num())); - const witness_object &witness = get_witness(next_block.witness); + const validator_object &witness = get_witness(next_block.witness); if (!(skip & skip_witness_signature)) { FC_ASSERT(witness.signing_key != public_key_type(), @@ -5325,7 +5325,7 @@ namespace graphene { namespace chain { uint32_t slot_num = get_slot_at_time(next_block.timestamp); FC_ASSERT(slot_num > 0); - string scheduled_witness = get_scheduled_witness(slot_num); + string scheduled_witness = get_scheduled_validator(slot_num); // During emergency consensus, the witness schedule can diverge // between competing forks (different blocks → different shuffle). @@ -5347,10 +5347,10 @@ namespace graphene { namespace chain { // This prevents arbitrary key holders from producing // blocks in random slots while allowing competing // forks with different shuffles to interoperate. - const witness_schedule_object &wso = get_witness_schedule_object(); + const validator_schedule_object &wso = get_validator_schedule_object(); bool in_schedule = false; - for (int i = 0; i < wso.num_scheduled_witnesses; i += CHAIN_BLOCK_WITNESS_REPEAT) { - if (wso.current_shuffled_witnesses[i] == witness.owner) { + for (int i = 0; i < wso.num_scheduled_validators; i += CHAIN_BLOCK_WITNESS_REPEAT) { + if (wso.current_shuffled_validators[i] == witness.owner) { in_schedule = true; break; } @@ -5388,7 +5388,7 @@ namespace graphene { namespace chain { auto block_size = fc::raw::pack_size(b); const dynamic_global_property_object &_dgp = get_dynamic_global_properties(); - const witness_schedule_object &consensus = get_witness_schedule_object(); + const validator_schedule_object &consensus = get_validator_schedule_object(); uint32_t missed_blocks = 0; if (head_block_time() != fc::time_point_sec()) { @@ -5396,7 +5396,7 @@ namespace graphene { namespace chain { assert(missed_blocks != 0); missed_blocks--; for (uint32_t i = 0; i < missed_blocks; ++i) { - const auto &witness_missed = get_witness(get_scheduled_witness(i + 1)); + const auto &witness_missed = get_witness(get_scheduled_validator(i + 1)); // During emergency mode, skip penalties for offline witnesses. // The hybrid schedule assigns committee to fill their slots, but @@ -5418,7 +5418,7 @@ namespace graphene { namespace chain { ("next", b.witness)); } - modify(witness_missed, [&](witness_object &w) { + modify(witness_missed, [&](validator_object &w) { // Only reset current_run for witnesses that actually missed their slot. // In emergency hybrid mode, the committee witness occupies multiple // schedule slots and produces the current block. If we reset current_run @@ -5444,7 +5444,7 @@ namespace graphene { namespace chain { ("w", w.owner)("missed", head_block_num() - w.last_confirmed_block_num) ("lc", w.last_confirmed_block_num)("t", CHAIN_EMERGENCY_MAX_WITNESS_MISSED_BLOCKS)); w.signing_key = public_key_type(); - push_virtual_operation(shutdown_witness_operation(w.owner)); + push_virtual_operation(shutdown_validator_operation(w.owner)); } } else if(witness_missed.owner != b.witness){ // total_missed does not increment when witness_missed.owner == b.witness @@ -5455,13 +5455,13 @@ namespace graphene { namespace chain { w.total_missed++; if(has_hardfork(CHAIN_HARDFORK_6)){// Consensus counted votes penalty to witness for block missing - w.penalty_percent+=consensus.median_props.witness_miss_penalty_percent; + w.penalty_percent+=consensus.median_props.validator_miss_penalty_percent; w.counted_votes=(fc::uint128_t(w.votes) - (fc::uint128_t(w.votes) * std::min(w.penalty_percent,uint32_t(CHAIN_100_PERCENT)) / CHAIN_100_PERCENT )).to_uint64(); create([&](witness_penalty_expire_object& wpe) { wpe.witness = witness_missed.owner; - wpe.penalty_percent = consensus.median_props.witness_miss_penalty_percent; - wpe.expires = head_block_time() + fc::seconds(consensus.median_props.witness_miss_penalty_duration); + wpe.penalty_percent = consensus.median_props.validator_miss_penalty_percent; + wpe.expires = head_block_time() + fc::seconds(consensus.median_props.validator_miss_penalty_duration); }); } @@ -5473,7 +5473,7 @@ namespace graphene { namespace chain { ("w", w.owner)("missed", head_block_num() - w.last_confirmed_block_num) ("lc", w.last_confirmed_block_num)("k", w.signing_key)); w.signing_key = public_key_type(); - push_virtual_operation(shutdown_witness_operation(w.owner)); + push_virtual_operation(shutdown_validator_operation(w.owner)); } } }); @@ -5602,15 +5602,15 @@ namespace graphene { namespace chain { }); // Change 5: Ensure emergency witness object exists with correct key - const auto &witness_by_name = get_index().indices().get(); + const auto &witness_by_name = get_index().indices().get(); auto wit_itr = witness_by_name.find(CHAIN_EMERGENCY_WITNESS_ACCOUNT); if (wit_itr == witness_by_name.end()) { - create([&](witness_object &w) { + create([&](validator_object &w) { w.owner = CHAIN_EMERGENCY_WITNESS_ACCOUNT; w.signing_key = CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY; w.created = head_block_time(); - w.schedule = witness_object::top; + w.schedule = validator_object::top; // Set running version to match the binary w.running_version = CHAIN_VERSION; // Vote for the CURRENTLY APPLIED hardfork version, not @@ -5625,31 +5625,31 @@ namespace graphene { namespace chain { // counted in update_median_witness_props(). With median // props, N committee entries are invisible to the median // computation (they just reinforce the existing median). - w.props = get_witness_schedule_object().median_props; + w.props = get_validator_schedule_object().median_props; }); } else { - modify(*wit_itr, [&](witness_object &w) { + modify(*wit_itr, [&](validator_object &w) { w.signing_key = CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY; - w.schedule = witness_object::top; + w.schedule = validator_object::top; // Update version fields on re-activation too w.running_version = CHAIN_VERSION; const auto &hfp = get_hardfork_property_object(); w.hardfork_version_vote = hfp.current_hardfork_version; w.hardfork_time_vote = hfp.processed_hardforks[hfp.last_hardfork]; // Sync chain properties with current median - w.props = get_witness_schedule_object().median_props; + w.props = get_validator_schedule_object().median_props; }); } // Change 7: Disable all real witnesses and reset penalties. // On emergency start, zero signing_key so ONLY committee produces. // Operators must manually re-enable witnesses via update_witness tx. - const auto &witness_idx = get_index().indices().get(); + const auto &witness_idx = get_index().indices().get(); uint32_t blanked_count = 0; for (auto witr = witness_idx.begin(); witr != witness_idx.end(); ++witr) { if (witr->owner == CHAIN_EMERGENCY_WITNESS_ACCOUNT) continue; if (witr->signing_key != public_key_type()) blanked_count++; - modify(*witr, [&](witness_object &w) { + modify(*witr, [&](validator_object &w) { w.signing_key = public_key_type(); w.penalty_percent = 0; w.counted_votes = w.votes; @@ -5675,12 +5675,12 @@ namespace graphene { namespace chain { // next_shuffle_block_num is still N blocks away, the node // would run an all-committee schedule until then, rejecting // blocks from real witnesses during that window. - const witness_schedule_object &wso = get_witness_schedule_object(); - modify(wso, [&](witness_schedule_object &_wso) { - for (int i = 0; i < _wso.num_scheduled_witnesses; i++) { - _wso.current_shuffled_witnesses[i] = CHAIN_EMERGENCY_WITNESS_ACCOUNT; + const validator_schedule_object &wso = get_validator_schedule_object(); + modify(wso, [&](validator_schedule_object &_wso) { + for (int i = 0; i < _wso.num_scheduled_validators; i++) { + _wso.current_shuffled_validators[i] = CHAIN_EMERGENCY_WITNESS_ACCOUNT; } - _wso.next_shuffle_block_num = head_block_num() + _wso.num_scheduled_witnesses; + _wso.next_shuffle_block_num = head_block_num() + _wso.num_scheduled_validators; }); // Notify fork_db about emergency mode @@ -5711,7 +5711,7 @@ namespace graphene { namespace chain { const auto& validation_list = get_index().indices().get(); if(!validation_list.empty()){ const dynamic_global_property_object &dpo = get_dynamic_global_properties(); - const witness_schedule_object &wso = get_witness_schedule_object(); + const validator_schedule_object &wso = get_validator_schedule_object(); //ilog("! check_block_post_validation_chain = ${n}", ("n", validation_list.size())); auto itr = validation_list.begin(); while(itr != validation_list.end()) @@ -5726,7 +5726,7 @@ namespace graphene { namespace chain { count++; } } - if(count >= (size_t(wso.num_scheduled_witnesses) * CHAIN_IRREVERSIBLE_THRESHOLD / CHAIN_100_PERCENT)){ + if(count >= (size_t(wso.num_scheduled_validators) * CHAIN_IRREVERSIBLE_THRESHOLD / CHAIN_100_PERCENT)){ modify(dpo, [&](dynamic_global_property_object &_dpo) { _dpo.last_irreversible_block_num = current.block_num; _dpo.last_irreversible_block_id = block_id_type(); @@ -5891,10 +5891,10 @@ namespace graphene { namespace chain { // Only scheduled witnesses can contribute to LIB advancement; accepting // validations from non-scheduled witnesses is pointless and could be // exploited for spam. - const witness_schedule_object &wso = get_witness_schedule_object(); + const validator_schedule_object &wso = get_validator_schedule_object(); bool is_scheduled = false; - for (int i = 0; i < wso.num_scheduled_witnesses; i += CHAIN_BLOCK_WITNESS_REPEAT) { - if (wso.current_shuffled_witnesses[i] == witness_account) { + for (int i = 0; i < wso.num_scheduled_validators; i += CHAIN_BLOCK_WITNESS_REPEAT) { + if (wso.current_shuffled_validators[i] == witness_account) { is_scheduled = true; break; } @@ -5936,8 +5936,8 @@ namespace graphene { namespace chain { if(find){ const dynamic_global_property_object &dpo = get_dynamic_global_properties(); if((1 + dpo.last_irreversible_block_num) == find_block_num){ - const witness_schedule_object &wso = get_witness_schedule_object(); - if(count >= (size_t(wso.num_scheduled_witnesses) * CHAIN_IRREVERSIBLE_THRESHOLD / CHAIN_100_PERCENT)){ + const validator_schedule_object &wso = get_validator_schedule_object(); + if(count >= (size_t(wso.num_scheduled_validators) * CHAIN_IRREVERSIBLE_THRESHOLD / CHAIN_100_PERCENT)){ modify(dpo, [&](dynamic_global_property_object &_dpo) { _dpo.last_irreversible_block_num = find_block_num; _dpo.last_irreversible_block_id = block_id_type(); @@ -6145,14 +6145,14 @@ namespace graphene { namespace chain { create([&](block_post_validation_object& o) { o.block_num = block_num; o.block_id = block_id_type(block_id); - const witness_schedule_object &wso = get_witness_schedule_object(); - size_t witness_index=0; + const validator_schedule_object &wso = get_validator_schedule_object(); + size_t validator_index=0; size_t i = 0; - for (; i < wso.num_scheduled_witnesses; i+=CHAIN_BLOCK_WITNESS_REPEAT) { - if(witness_account != wso.current_shuffled_witnesses[i]){ - o.current_shuffled_witnesses[witness_index] = account_name_type(wso.current_shuffled_witnesses[i]); - o.current_shuffled_witnesses_validations[witness_index] = false; - witness_index++; + for (; i < wso.num_scheduled_validators; i+=CHAIN_BLOCK_WITNESS_REPEAT) { + if(witness_account != wso.current_shuffled_validators[i]){ + o.current_shuffled_witnesses[validator_index] = account_name_type(wso.current_shuffled_validators[i]); + o.current_shuffled_witnesses_validations[validator_index] = false; + validator_index++; } } for (; i < CHAIN_MAX_WITNESSES; i+=CHAIN_BLOCK_WITNESS_REPEAT) { @@ -6162,13 +6162,13 @@ namespace graphene { namespace chain { }); } - void database::update_signing_witness(const witness_object &signing_witness, const signed_block &new_block) { + void database::update_signing_witness(const validator_object &signing_witness, const signed_block &new_block) { try { const dynamic_global_property_object &dpo = get_dynamic_global_properties(); uint64_t new_block_aslot = dpo.current_aslot + get_slot_at_time(new_block.timestamp); - modify(signing_witness, [&](witness_object &_wit) { + modify(signing_witness, [&](validator_object &_wit) { _wit.last_aslot = new_block_aslot; _wit.last_confirmed_block_num = new_block.block_num(); if( _wit.current_run >= CHAIN_IRREVERSIBLE_SUPPORT_MIN_RUN ){ @@ -6182,7 +6182,7 @@ namespace graphene { namespace chain { void database::update_last_irreversible_block(uint32_t skip) { try { const dynamic_global_property_object &dpo = get_dynamic_global_properties(); - const witness_schedule_object &wso = get_witness_schedule_object(); + const validator_schedule_object &wso = get_validator_schedule_object(); // === HARDFORK 12: EMERGENCY LIB === // During emergency mode, LIB advances normally using all witnesses @@ -6195,10 +6195,10 @@ namespace graphene { namespace chain { // normal LIB computation continues seamlessly. // === END HARDFORK 12 EMERGENCY LIB === - vector wit_objs; - wit_objs.reserve(wso.num_scheduled_witnesses); - for (int i = 0; i < wso.num_scheduled_witnesses; i+=CHAIN_BLOCK_WITNESS_REPEAT) { - wit_objs.push_back(&get_witness(wso.current_shuffled_witnesses[i])); + vector wit_objs; + wit_objs.reserve(wso.num_scheduled_validators); + for (int i = 0; i < wso.num_scheduled_validators; i+=CHAIN_BLOCK_WITNESS_REPEAT) { + wit_objs.push_back(&get_witness(wso.current_shuffled_validators[i])); } static_assert(CHAIN_IRREVERSIBLE_THRESHOLD > @@ -6214,7 +6214,7 @@ namespace graphene { namespace chain { std::nth_element(wit_objs.begin(), wit_objs.begin() + offset, wit_objs.end(), - [](const witness_object *a, const witness_object *b) { + [](const validator_object *a, const validator_object *b) { return a->last_supported_block_num < b->last_supported_block_num; }); @@ -6553,14 +6553,14 @@ namespace graphene { namespace chain { } void database::reset_virtual_schedule_time() { - const witness_schedule_object &wso = get_witness_schedule_object(); - modify(wso, [&](witness_schedule_object &o) { + const validator_schedule_object &wso = get_validator_schedule_object(); + modify(wso, [&](validator_schedule_object &o) { o.current_virtual_time = fc::uint128_t(); // reset it 0 }); - const auto &idx = get_index().indices(); + const auto &idx = get_index().indices(); for (const auto &witness : idx) { - modify(witness, [&](witness_object &wobj) { + modify(witness, [&](validator_object &wobj) { wobj.virtual_position = fc::uint128_t(); wobj.virtual_last_update = wso.current_virtual_time; wobj.virtual_scheduled_time = VIRTUAL_SCHEDULE_LAP_LENGTH2 / @@ -6716,8 +6716,8 @@ namespace graphene { namespace chain { const auto &voter = get(witr->account); share_type old_weight=voter.witness_vote_weight(); share_type new_weight=voter.witness_vote_fair_weight_prehf5(); - adjust_witness_vote(get(witr->witness), -old_weight); - adjust_witness_vote(get(witr->witness), new_weight); + adjust_validator_vote(get(witr->witness), -old_weight); + adjust_validator_vote(get(witr->witness), new_weight); } break; @@ -6725,11 +6725,11 @@ namespace graphene { namespace chain { case CHAIN_HARDFORK_5: { //clear votes for each witness - const auto &widx = get_index().indices().get(); + const auto &widx = get_index().indices().get(); for (auto itr = widx.begin(); itr != widx.end(); ++itr) { - modify(*itr, [&](witness_object &w) { + modify(*itr, [&](validator_object &w) { elog("HF5 witness ${a} was votes: ${n}", ("a", w.owner)("n", w.votes)); w.votes = 0; w.counted_votes = 0; @@ -6747,7 +6747,7 @@ namespace graphene { namespace chain { }); elog("HF5 witness ${a} calc votes: ${n}", ("a", witness.owner)("n", fair_weight)); - adjust_witness_vote(get(witr->witness), fair_weight); + adjust_validator_vote(get(witr->witness), fair_weight); } break; } @@ -6770,7 +6770,7 @@ namespace graphene { namespace chain { for (int i = 0; i < CHAIN_MAX_PROXY_RECURSION_DEPTH; ++i) { delta[i + 1] = -(itr->proxied_vsf_votes[i]); } - adjust_proxied_witness_votes(get_account(itr->name), delta); + adjust_proxied_validator_votes(get_account(itr->name), delta); } auto old_shares=itr->vesting_shares; @@ -6801,7 +6801,7 @@ namespace graphene { namespace chain { for (int i = 0; i < CHAIN_MAX_PROXY_RECURSION_DEPTH; ++i) { delta[i + 1] = itr->proxied_vsf_votes[i]; } - adjust_proxied_witness_votes(get_account(itr->name), delta); + adjust_proxied_validator_votes(get_account(itr->name), delta); } } @@ -6877,11 +6877,11 @@ namespace graphene { namespace chain { } //clear votes for each witness - const auto &widx = get_index().indices().get(); + const auto &widx = get_index().indices().get(); for (auto itr = widx.begin(); itr != widx.end(); ++itr) { - modify(*itr, [&](witness_object &w) { + modify(*itr, [&](validator_object &w) { elog("HF6 witness ${a} has votes: ${n}", ("a", w.owner)("n", w.votes)); w.votes = 0; w.counted_votes = 0; @@ -6899,7 +6899,7 @@ namespace graphene { namespace chain { }); elog("HF6 witness ${a} recalc votes from ${a}: ${n}", ("a", witness.owner)("n", fair_weight)); - adjust_witness_vote(get(witr->witness), fair_weight); + adjust_validator_vote(get(witr->witness), fair_weight); } break; } @@ -6941,7 +6941,7 @@ namespace graphene { namespace chain { for (int i = 0; i < CHAIN_MAX_PROXY_RECURSION_DEPTH; ++i) { delta[i + 1] = -(current.proxied_vsf_votes[i]); } - adjust_proxied_witness_votes(get_account(current.name), delta); + adjust_proxied_validator_votes(get_account(current.name), delta); } //move shares and balance to committee elog("- add to committee funds: ${a} SHARES, ${b} TOKEN", ("a", current.vesting_shares)("b", current.balance)); @@ -7047,19 +7047,19 @@ namespace graphene { namespace chain { //decrease witnesses_vote_weight from all votes by invalid account const auto &vidx = get_index().indices().get(); - auto vitr = vidx.lower_bound(boost::make_tuple(current.id, witness_id_type())); + auto vitr = vidx.lower_bound(boost::make_tuple(current.id, validator_id_type())); while (vitr != vidx.end() && vitr->account == current.id) { - adjust_witness_vote(get(vitr->witness),-current.witnesses_vote_weight); + adjust_validator_vote(get(vitr->witness),-current.witnesses_vote_weight); ++vitr; } //remove from witness_vote_index by_account_witness (remove all votes from invalid account) const auto &d10idx = get_index().indices().get(); - auto delete_itr10 = d10idx.lower_bound(boost::make_tuple(current.id, witness_id_type())); + auto delete_itr10 = d10idx.lower_bound(boost::make_tuple(current.id, validator_id_type())); while(delete_itr10 != d10idx.end() && delete_itr10->account == current.id) { const auto &delete_current = *delete_itr10; - adjust_witness_vote(get(delete_itr10->witness),-current.witnesses_vote_weight); + adjust_validator_vote(get(delete_itr10->witness),-current.witnesses_vote_weight); modify(current, [&](account_object &a) { a.witnesses_voted_for--; }); @@ -7074,7 +7074,7 @@ namespace graphene { namespace chain { }); //look witness object from invalid account - const auto &invalid_witness = find(current.name); + const auto &invalid_witness = find(current.name); if(invalid_witness != nullptr){//found witness //remove invalid witness account from penalty index const auto &d8idx = get_index().indices().get(); @@ -7086,11 +7086,11 @@ namespace graphene { namespace chain { remove(delete_current); } //remove invalid witness account from schedule - const witness_schedule_object &wso = get_witness_schedule_object(); - modify(wso, [&](witness_schedule_object &_wso) { - for (int i = 0; i < _wso.num_scheduled_witnesses; i+=CHAIN_BLOCK_WITNESS_REPEAT) { - if(_wso.current_shuffled_witnesses[i] == invalid_witness->owner){ - _wso.current_shuffled_witnesses[i] = account_name_type(); + const validator_schedule_object &wso = get_validator_schedule_object(); + modify(wso, [&](validator_schedule_object &_wso) { + for (int i = 0; i < _wso.num_scheduled_validators; i+=CHAIN_BLOCK_WITNESS_REPEAT) { + if(_wso.current_shuffled_validators[i] == invalid_witness->owner){ + _wso.current_shuffled_validators[i] = account_name_type(); } } }); @@ -7100,9 +7100,9 @@ namespace graphene { namespace chain { while (vitr != vidx.end() && vitr->witness == invalid_witness->id) { const auto &voter_account = get(vitr->account); const auto &vidx2 = get_index().indices().get(); - auto vitr2 = vidx2.lower_bound(boost::make_tuple(voter_account.id, witness_id_type())); + auto vitr2 = vidx2.lower_bound(boost::make_tuple(voter_account.id, validator_id_type())); while (vitr2 != vidx2.end() && vitr2->account == voter_account.id) { - adjust_witness_vote(get(vitr2->witness), -voter_account.witnesses_vote_weight); + adjust_validator_vote(get(vitr2->witness), -voter_account.witnesses_vote_weight); ++vitr2; } @@ -7118,16 +7118,16 @@ namespace graphene { namespace chain { }); const auto &vidx3 = get_index().indices().get(); - auto vitr3 = vidx3.lower_bound(boost::make_tuple(voter_account.id, witness_id_type())); + auto vitr3 = vidx3.lower_bound(boost::make_tuple(voter_account.id, validator_id_type())); while (vitr3 != vidx3.end() && vitr3->account == voter_account.id) { - adjust_witness_vote(get(vitr3->witness), voter_account.witnesses_vote_weight); + adjust_validator_vote(get(vitr3->witness), voter_account.witnesses_vote_weight); ++vitr3; } ++vitr; } - //remove from witness_index invalid witness account - const auto &d9idx = get_index().indices().get(); + //remove from validator_index invalid witness account + const auto &d9idx = get_index().indices().get(); auto delete_itr9 = d9idx.lower_bound(current.name); while(delete_itr9 != d9idx.end() && delete_itr9->owner == current.name) { @@ -7343,7 +7343,7 @@ namespace graphene { namespace chain { case CHAIN_HARDFORK_9: { //remove witnesses without signed block - const auto &idx = get_index().indices().get(); + const auto &idx = get_index().indices().get(); auto itr = idx.begin(); while(itr != idx.end()){ const auto ¤t = *itr; @@ -7365,9 +7365,9 @@ namespace graphene { namespace chain { while (vitr != vidx.end() && vitr->witness == current.id) { const auto &voter_account = get(vitr->account); const auto &vidx2 = get_index().indices().get(); - auto vitr2 = vidx2.lower_bound(boost::make_tuple(voter_account.id, witness_id_type())); + auto vitr2 = vidx2.lower_bound(boost::make_tuple(voter_account.id, validator_id_type())); while (vitr2 != vidx2.end() && vitr2->account == voter_account.id) { - adjust_witness_vote(get(vitr2->witness), -voter_account.witnesses_vote_weight); + adjust_validator_vote(get(vitr2->witness), -voter_account.witnesses_vote_weight); ++vitr2; } @@ -7384,9 +7384,9 @@ namespace graphene { namespace chain { }); const auto &vidx3 = get_index().indices().get(); - auto vitr3 = vidx3.lower_bound(boost::make_tuple(voter_account.id, witness_id_type())); + auto vitr3 = vidx3.lower_bound(boost::make_tuple(voter_account.id, validator_id_type())); while (vitr3 != vidx3.end() && vitr3->account == voter_account.id) { - adjust_witness_vote(get(vitr3->witness), voter_account.witnesses_vote_weight); + adjust_validator_vote(get(vitr3->witness), voter_account.witnesses_vote_weight); ++vitr3; } ++vitr; @@ -7453,7 +7453,7 @@ namespace graphene { namespace chain { break; case CHAIN_HARDFORK_13: // Validator reward sharing: new fields sharing_rate and - // pending_stakeholder_reward on witness_object default to 0 (replay + // pending_stakeholder_reward on validator_object default to 0 (replay // initialises them), no extra migration needed. break; default: diff --git a/libraries/chain/include/graphene/chain/chain_evaluator.hpp b/libraries/chain/include/graphene/chain/chain_evaluator.hpp index dbfbf6d468..7342ec81c5 100644 --- a/libraries/chain/include/graphene/chain/chain_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/chain_evaluator.hpp @@ -16,9 +16,9 @@ namespace graphene { namespace chain { DEFINE_EVALUATOR(account_metadata) DEFINE_EVALUATOR(transfer) DEFINE_EVALUATOR(transfer_to_vesting) - DEFINE_EVALUATOR(witness_update) - DEFINE_EVALUATOR(account_witness_vote) - DEFINE_EVALUATOR(account_witness_proxy) + DEFINE_EVALUATOR(validator_update) + DEFINE_EVALUATOR(account_validator_vote) + DEFINE_EVALUATOR(account_validator_proxy) DEFINE_EVALUATOR(withdraw_vesting) DEFINE_EVALUATOR(set_withdraw_vesting_route) DEFINE_EVALUATOR(content) diff --git a/libraries/chain/include/graphene/chain/chain_object_types.hpp b/libraries/chain/include/graphene/chain/chain_object_types.hpp index 985878e65e..4823cec9f9 100644 --- a/libraries/chain/include/graphene/chain/chain_object_types.hpp +++ b/libraries/chain/include/graphene/chain/chain_object_types.hpp @@ -45,10 +45,10 @@ namespace graphene { namespace chain { dynamic_global_property_object_type, account_object_type, account_authority_object_type, - witness_object_type, + validator_object_type, transaction_object_type, block_summary_object_type, - witness_schedule_object_type, + validator_schedule_object_type, content_object_type, content_type_object_type, content_vote_object_type, @@ -79,10 +79,10 @@ namespace graphene { namespace chain { class dynamic_global_property_object; class account_object; class account_authority_object; - class witness_object; + class validator_object; class transaction_object; class block_summary_object; - class witness_schedule_object; + class validator_schedule_object; class proposal_object; class required_approval_object; class content_object; @@ -113,10 +113,10 @@ namespace graphene { namespace chain { typedef object_id dynamic_global_property_id_type; typedef object_id account_id_type; typedef object_id account_authority_id_type; - typedef object_id witness_id_type; + typedef object_id validator_id_type; typedef object_id transaction_object_id_type; typedef object_id block_summary_id_type; - typedef object_id witness_schedule_id_type; + typedef object_id validator_schedule_id_type; typedef object_id content_id_type; typedef object_id content_type_id_type; typedef object_id content_vote_id_type; @@ -210,10 +210,10 @@ FC_REFLECT_ENUM(graphene::chain::object_type, (dynamic_global_property_object_type) (account_object_type) (account_authority_object_type) - (witness_object_type) + (validator_object_type) (transaction_object_type) (block_summary_object_type) - (witness_schedule_object_type) + (validator_schedule_object_type) (content_object_type) (content_type_object_type) (content_vote_object_type) diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 0274325380..bb8393a216 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -221,9 +221,9 @@ namespace graphene { namespace chain { chain_id_type get_chain_id() const; - const witness_object &get_witness(const account_name_type &name) const; + const validator_object &get_witness(const account_name_type &name) const; - const witness_object *find_witness(const account_name_type &name) const; + const validator_object *find_witness(const account_name_type &name) const; const account_object &get_account(const account_name_type &name) const; @@ -251,7 +251,7 @@ namespace graphene { namespace chain { const dynamic_global_property_object &get_dynamic_global_properties() const; - const witness_schedule_object &get_witness_schedule_object() const; + const validator_schedule_object &get_validator_schedule_object() const; const hardfork_property_object &get_hardfork_property_object() const; @@ -397,7 +397,7 @@ namespace graphene { namespace chain { * * Passing slot_num == 0 returns CHAIN_NULL_WITNESS */ - account_name_type get_scheduled_witness(uint32_t slot_num) const; + account_name_type get_scheduled_validator(uint32_t slot_num) const; /** * Get the time at which the given slot occurs. @@ -444,25 +444,25 @@ namespace graphene { namespace chain { } /** this updates the votes for witnesses as a result of account voting proxy changing */ - void adjust_proxied_witness_votes(const account_object &a, + void adjust_proxied_validator_votes(const account_object &a, const std::array &delta, int depth = 0); /** this updates the votes for all witnesses as a result of account SHARES changing */ - void adjust_proxied_witness_votes(const account_object &a, share_type delta, int depth = 0); + void adjust_proxied_validator_votes(const account_object &a, share_type delta, int depth = 0); - /** this is called by `adjust_proxied_witness_votes` when account proxy to self */ - void adjust_witness_votes(const account_object &a, share_type delta); + /** this is called by `adjust_proxied_validator_votes` when account proxy to self */ + void adjust_validator_votes(const account_object &a, share_type delta); /** this updates the vote of a single witness as a result of a vote being added or removed*/ - void adjust_witness_vote(const witness_object &obj, share_type delta); + void adjust_validator_vote(const validator_object &obj, share_type delta); /** clears all vote records for a particular account but does not update the * witness vote totals. Vote totals should be updated first via a call to - * adjust_proxied_witness_votes( a, -a.witness_vote_weight() ) + * adjust_proxied_validator_votes( a, -a.witness_vote_weight() ) */ - void clear_witness_votes(const account_object &a); + void clear_validator_votes(const account_object &a); void process_vesting_withdrawals(); @@ -607,7 +607,7 @@ namespace graphene { namespace chain { ///Steps involved in applying a new block ///@{ - const witness_object &validate_block_header(uint32_t skip, const signed_block &next_block) const; + const validator_object &validate_block_header(uint32_t skip, const signed_block &next_block) const; void create_block_summary(const signed_block &next_block); @@ -621,7 +621,7 @@ namespace graphene { namespace chain { void update_global_dynamic_data(const signed_block &b, uint32_t skip); - void update_signing_witness(const witness_object &signing_witness, const signed_block &new_block); + void update_signing_witness(const validator_object &signing_witness, const signed_block &new_block); void update_last_irreversible_block(uint32_t skip); diff --git a/libraries/chain/include/graphene/chain/global_property_object.hpp b/libraries/chain/include/graphene/chain/global_property_object.hpp index e23fddd433..69d040617e 100644 --- a/libraries/chain/include/graphene/chain/global_property_object.hpp +++ b/libraries/chain/include/graphene/chain/global_property_object.hpp @@ -49,7 +49,7 @@ namespace graphene { fc::uint128_t total_reward_shares; uint32_t inflation_calc_block_num = 0; - int16_t inflation_witness_percent = 0; + int16_t inflation_validator_percent = 0; int16_t inflation_ratio = 0; price get_vesting_share_price() const { @@ -186,7 +186,7 @@ FC_REFLECT((graphene::chain::dynamic_global_property_object), (vote_regeneration_per_day) (bandwidth_reserve_candidates) (inflation_calc_block_num) - (inflation_witness_percent) + (inflation_validator_percent) (inflation_ratio) (emergency_consensus_active) (emergency_consensus_start_block) diff --git a/libraries/chain/include/graphene/chain/witness_objects.hpp b/libraries/chain/include/graphene/chain/witness_objects.hpp index a358b4b87a..630c0a5284 100644 --- a/libraries/chain/include/graphene/chain/witness_objects.hpp +++ b/libraries/chain/include/graphene/chain/witness_objects.hpp @@ -24,19 +24,19 @@ namespace graphene { namespace chain { * at least 2 weeks old are able to participate in block * production. */ - class witness_object - : public object { - witness_object() = delete; + class validator_object + : public object { + validator_object() = delete; public: - enum witness_schedule_type { + enum validator_schedule_type { top, support, none }; template - witness_object(Constructor &&c, allocator a) + validator_object(Constructor &&c, allocator a) :url(a) { c(*this); } @@ -70,7 +70,7 @@ namespace graphene { namespace chain { share_type votes; uint32_t penalty_percent = 0; share_type counted_votes; - witness_schedule_type schedule = none; /// How the witness was scheduled the last time it was scheduled + validator_schedule_type schedule = none; /// How the witness was scheduled the last time it was scheduled /** * These fields are used for the witness scheduling algorithm which uses @@ -109,23 +109,23 @@ namespace graphene { namespace chain { /// Stored in TOKEN atomic units; converted to SHARES via create_vesting() at epoch end. /// witness_reward_operation for the validator carries only the validator's own share. share_type pending_stakeholder_reward = 0; - class witness_schedule_object - : public object { + class validator_schedule_object + : public object { public: template - witness_schedule_object(Constructor &&c, allocator a) { + validator_schedule_object(Constructor &&c, allocator a) { c(*this); } - witness_schedule_object() { + validator_schedule_object() { } id_type id; fc::uint128_t current_virtual_time; uint32_t next_shuffle_block_num = 1; - fc::array current_shuffled_witnesses; - uint8_t num_scheduled_witnesses = 1; + fc::array current_shuffled_validators; + uint8_t num_scheduled_validators = 1; chain_properties median_props; version majority_version; }; @@ -153,30 +153,30 @@ namespace graphene { namespace chain { id_type id; - witness_id_type witness; + validator_id_type witness; account_id_type account; /// Block number when this vote was cast. Used by HF13 time-weighted epoch distribution. /// Zero for votes cast before HF13 (treated as epoch start = full weight). uint32_t vote_created_block = 0; }; - class witness_schedule_object - : public object { + class validator_schedule_object + : public object { public: template - witness_schedule_object(Constructor &&c, allocator a) { + validator_schedule_object(Constructor &&c, allocator a) { c(*this); } - witness_schedule_object() { + validator_schedule_object() { } id_type id; fc::uint128_t current_virtual_time; uint32_t next_shuffle_block_num = 1; - fc::array current_shuffled_witnesses; - uint8_t num_scheduled_witnesses = 1; + fc::array current_shuffled_validators; + uint8_t num_scheduled_validators = 1; chain_properties median_props; version majority_version; }; @@ -191,43 +191,43 @@ namespace graphene { namespace chain { /** * @ingroup object_index */ - using witness_index = multi_index_container < - witness_object, + using validator_index = multi_index_container < + validator_object, indexed_by< ordered_unique< tag, - member>, + member>, ordered_non_unique< tag, - member>, + member>, ordered_unique< tag, - member>, + member>, ordered_unique< tag, composite_key< - witness_object, - member, - member>, + validator_object, + member, + member>, composite_key_compare< std::greater, graphene::protocol::string_less>>, ordered_unique< tag, composite_key< - witness_object, - member, - member>, + validator_object, + member, + member>, composite_key_compare< std::greater, graphene::protocol::string_less>>, ordered_unique< tag, composite_key< - witness_object, - member, - member>>>, - allocator>; + validator_object, + member, + member>>>, + allocator>; struct by_account_witness; struct by_witness_account; @@ -243,28 +243,28 @@ namespace graphene { namespace chain { composite_key< witness_vote_object, member, - member>, + member>, composite_key_compare< std::less, - std::less>>, + std::less>>, ordered_unique< tag, composite_key< witness_vote_object, - member, + member, member>, composite_key_compare< - std::less, + std::less, std::less>>>, allocator>; - using witness_schedule_index = multi_index_container < - witness_schedule_object, + using validator_schedule_index = multi_index_container < + validator_schedule_object, indexed_by< ordered_unique< tag, - member>>, - allocator >; + member>>, + allocator >; class witness_penalty_expire_object : public object { @@ -303,24 +303,24 @@ namespace graphene { namespace chain { } } -FC_REFLECT_ENUM(graphene::chain::witness_object::witness_schedule_type, (top)(support)(none)) +FC_REFLECT_ENUM(graphene::chain::validator_object::validator_schedule_type, (top)(support)(none)) FC_REFLECT( - (graphene::chain::witness_object), + (graphene::chain::validator_object), (id)(owner)(created)(url)(votes)(penalty_percent)(counted_votes)(schedule)(virtual_last_update)(virtual_position)(virtual_scheduled_time)(total_missed) (last_aslot)(last_confirmed_block_num)(current_run)(last_supported_block_num)(signing_key)(props) (last_work)(running_version)(hardfork_version_vote)(hardfork_time_vote) (sharing_rate)(pending_stakeholder_reward)) -CHAINBASE_SET_INDEX_TYPE(graphene::chain::witness_object, graphene::chain::witness_index) +CHAINBASE_SET_INDEX_TYPE(graphene::chain::validator_object, graphene::chain::validator_index) FC_REFLECT((graphene::chain::witness_vote_object), (id)(witness)(account)(vote_created_block)) CHAINBASE_SET_INDEX_TYPE(graphene::chain::witness_vote_object, graphene::chain::witness_vote_index) -FC_REFLECT((graphene::chain::witness_schedule_object), - (id)(current_virtual_time)(next_shuffle_block_num)(current_shuffled_witnesses)(num_scheduled_witnesses) +FC_REFLECT((graphene::chain::validator_schedule_object), + (id)(current_virtual_time)(next_shuffle_block_num)(current_shuffled_validators)(num_scheduled_validators) (median_props)(majority_version) ) -CHAINBASE_SET_INDEX_TYPE(graphene::chain::witness_schedule_object, graphene::chain::witness_schedule_index) +CHAINBASE_SET_INDEX_TYPE(graphene::chain::validator_schedule_object, graphene::chain::validator_schedule_index) FC_REFLECT((graphene::chain::witness_penalty_expire_object),(id)(witness)(penalty_percent)(expires)) CHAINBASE_SET_INDEX_TYPE(graphene::chain::witness_penalty_expire_object, graphene::chain::witness_penalty_expire_index) \ No newline at end of file diff --git a/libraries/chain/invite_evaluator.cpp b/libraries/chain/invite_evaluator.cpp index 3ff0726cab..5c751c5a4c 100644 --- a/libraries/chain/invite_evaluator.cpp +++ b/libraries/chain/invite_evaluator.cpp @@ -11,7 +11,7 @@ namespace graphene { namespace chain { const auto& creator = _db.get_account(o.creator); //if(_db.has_hardfork(CHAIN_HARDFORK_9))//can be deleted after fix in CHAIN_HARDFORK_11 // FC_ASSERT(!creator.valid, "Account flagged as invalid"); - const auto& median_props = _db.get_witness_schedule_object().median_props; + const auto& median_props = _db.get_validator_schedule_object().median_props; FC_ASSERT(creator.balance >= o.balance, "Insufficient balance to create invite.", ("creator.balance", creator.balance)("invite.balance", o.balance)); diff --git a/libraries/chain/paid_subscription_evaluator.cpp b/libraries/chain/paid_subscription_evaluator.cpp index ac74742c18..5e00bbea54 100644 --- a/libraries/chain/paid_subscription_evaluator.cpp +++ b/libraries/chain/paid_subscription_evaluator.cpp @@ -26,7 +26,7 @@ namespace graphene { namespace chain { } else{ if(_db.has_hardfork(CHAIN_HARDFORK_9)){ - const auto& median_props = _db.get_witness_schedule_object().median_props; + const auto& median_props = _db.get_validator_schedule_object().median_props; const dynamic_global_property_object &dgp = _db.get_dynamic_global_properties(); FC_ASSERT(account.balance >= diff --git a/libraries/protocol/chain_operations.cpp b/libraries/protocol/chain_operations.cpp index d1a2001560..4023219641 100644 --- a/libraries/protocol/chain_operations.cpp +++ b/libraries/protocol/chain_operations.cpp @@ -180,7 +180,7 @@ namespace graphene { namespace protocol { CHAIN_100_PERCENT, "Percent must be valid percent"); } - void witness_update_operation::validate() const { + void validator_update_operation::validate() const { validate_account_name(owner); FC_ASSERT(url.size() > 0, "URL size must be greater than 0"); FC_ASSERT(url.size() < CHAIN_MAX_WITNESS_URL_LENGTH, "URL size must be lesser than CHAIN_MAX_WITNESS_URL_LENGTH"); @@ -206,12 +206,12 @@ namespace graphene { namespace protocol { props.visit(chain_properties_validator()); } - void account_witness_vote_operation::validate() const { + void account_validator_vote_operation::validate() const { validate_account_name(account); - validate_account_name(witness); + validate_account_name(validator); } - void account_witness_proxy_operation::validate() const { + void account_validator_proxy_operation::validate() const { validate_account_name(account); if (proxy.size()) { validate_account_name(proxy); diff --git a/libraries/protocol/include/graphene/protocol/chain_operations.hpp b/libraries/protocol/include/graphene/protocol/chain_operations.hpp index d7c548ece7..7d66449c1e 100644 --- a/libraries/protocol/include/graphene/protocol/chain_operations.hpp +++ b/libraries/protocol/include/graphene/protocol/chain_operations.hpp @@ -446,7 +446,7 @@ namespace graphene { namespace protocol { /** * Consensus - Witness reward percent from block inflation */ - int16_t inflation_witness_percent = CHAIN_CONSENSUS_INFLATION_WITNESS_PERCENT; + int16_t inflation_validator_percent = CHAIN_CONSENSUS_INFLATION_WITNESS_PERCENT; /** * Consensus - Inflation ratio between committee and reward fund @@ -460,8 +460,8 @@ namespace graphene { namespace protocol { void validate() const { chain_properties_init::validate(); - FC_ASSERT(inflation_witness_percent >= 0); - FC_ASSERT(inflation_witness_percent <= CHAIN_100_PERCENT); + FC_ASSERT(inflation_validator_percent >= 0); + FC_ASSERT(inflation_validator_percent <= CHAIN_100_PERCENT); FC_ASSERT(inflation_ratio_committee_vs_reward_fund >= 0); FC_ASSERT(inflation_ratio_committee_vs_reward_fund <= CHAIN_100_PERCENT); FC_ASSERT(inflation_recalc_period >= 0); @@ -485,20 +485,20 @@ namespace graphene { namespace protocol { /** * Consensus - Witness who missed the block will receive a penality of a percentage of the votes */ - int16_t witness_miss_penalty_percent = CONSENSUS_WITNESS_MISS_PENALTY_PERCENT; + int16_t validator_miss_penalty_percent = CONSENSUS_WITNESS_MISS_PENALTY_PERCENT; /** * Consensus - Witness who missed the block will receive a penality with duration (in seconds) */ - uint32_t witness_miss_penalty_duration = CONSENSUS_WITNESS_MISS_PENALTY_DURATION; + uint32_t validator_miss_penalty_duration = CONSENSUS_WITNESS_MISS_PENALTY_DURATION; void validate() const { chain_properties_hf4::validate(); FC_ASSERT(data_operations_cost_additional_bandwidth >= 0); - FC_ASSERT(witness_miss_penalty_percent >= 0); - FC_ASSERT(witness_miss_penalty_percent <= CHAIN_100_PERCENT); - FC_ASSERT(witness_miss_penalty_duration >= 0); - FC_ASSERT(witness_miss_penalty_duration <= (CHAIN_BLOCKS_PER_YEAR * CHAIN_BLOCK_INTERVAL)); + FC_ASSERT(validator_miss_penalty_percent >= 0); + FC_ASSERT(validator_miss_penalty_percent <= CHAIN_100_PERCENT); + FC_ASSERT(validator_miss_penalty_duration >= 0); + FC_ASSERT(validator_miss_penalty_duration <= (CHAIN_BLOCKS_PER_YEAR * CHAIN_BLOCK_INTERVAL)); } chain_properties_hf6& operator=(const chain_properties_init& src) { @@ -543,7 +543,7 @@ namespace graphene { namespace protocol { /** * Consensus - Fee to the network committee for declare account as witness */ - asset witness_declaration_fee = asset(CONSENSUS_WITNESS_DECLARATION_FEE, TOKEN_SYMBOL); + asset validator_declaration_fee = asset(CONSENSUS_WITNESS_DECLARATION_FEE, TOKEN_SYMBOL); /** * Consensus - withdraw intervals (duration defined as CHAIN_VESTING_WITHDRAW_INTERVAL_SECONDS equal 1 day) @@ -563,8 +563,8 @@ namespace graphene { namespace protocol { FC_ASSERT(account_on_sale_fee.symbol == TOKEN_SYMBOL); FC_ASSERT(subaccount_on_sale_fee.amount > 0); FC_ASSERT(subaccount_on_sale_fee.symbol == TOKEN_SYMBOL); - FC_ASSERT(witness_declaration_fee.amount > 0); - FC_ASSERT(witness_declaration_fee.symbol == TOKEN_SYMBOL); + FC_ASSERT(validator_declaration_fee.amount > 0); + FC_ASSERT(validator_declaration_fee.symbol == TOKEN_SYMBOL); FC_ASSERT(withdraw_intervals > 0); } @@ -706,7 +706,7 @@ namespace graphene { namespace protocol { * contention. The network will pick the top 21 witnesses for * producing blocks. */ - struct witness_update_operation : public base_operation { + struct validator_update_operation : public base_operation { account_name_type owner; string url; public_key_type block_signing_key; @@ -752,9 +752,9 @@ namespace graphene { namespace protocol { * * If a proxy is specified then all existing votes are removed. */ - struct account_witness_vote_operation : public base_operation { + struct account_validator_vote_operation : public base_operation { account_name_type account; - account_name_type witness; + account_name_type validator; bool approve = true; void validate() const; @@ -765,7 +765,7 @@ namespace graphene { namespace protocol { }; - struct account_witness_proxy_operation : public base_operation { + struct account_validator_proxy_operation : public base_operation { account_name_type account; account_name_type proxy; @@ -1200,13 +1200,13 @@ FC_REFLECT( (committee_request_approve_min_percent)) FC_REFLECT_DERIVED( (graphene::protocol::chain_properties_hf4),((graphene::protocol::chain_properties_init)), - (inflation_witness_percent)(inflation_ratio_committee_vs_reward_fund)(inflation_recalc_period)) + (inflation_validator_percent)(inflation_ratio_committee_vs_reward_fund)(inflation_recalc_period)) FC_REFLECT_DERIVED( (graphene::protocol::chain_properties_hf6),((graphene::protocol::chain_properties_hf4)), - (data_operations_cost_additional_bandwidth)(witness_miss_penalty_percent)(witness_miss_penalty_duration)) + (data_operations_cost_additional_bandwidth)(validator_miss_penalty_percent)(validator_miss_penalty_duration)) FC_REFLECT_DERIVED( (graphene::protocol::chain_properties_hf9),((graphene::protocol::chain_properties_hf6)), - (create_invite_min_balance)(committee_create_request_fee)(create_paid_subscription_fee)(account_on_sale_fee)(subaccount_on_sale_fee)(witness_declaration_fee)(withdraw_intervals)) + (create_invite_min_balance)(committee_create_request_fee)(create_paid_subscription_fee)(account_on_sale_fee)(subaccount_on_sale_fee)(validator_declaration_fee)(withdraw_intervals)) FC_REFLECT_DERIVED( (graphene::protocol::chain_properties_hf13),((graphene::protocol::chain_properties_hf9)), (distribution_epoch_length)) @@ -1230,9 +1230,9 @@ FC_REFLECT((graphene::protocol::transfer_operation), (from)(to)(amount)(memo)) FC_REFLECT((graphene::protocol::transfer_to_vesting_operation), (from)(to)(amount)) FC_REFLECT((graphene::protocol::withdraw_vesting_operation), (account)(vesting_shares)) FC_REFLECT((graphene::protocol::set_withdraw_vesting_route_operation), (from_account)(to_account)(percent)(auto_vest)) -FC_REFLECT((graphene::protocol::witness_update_operation), (owner)(url)(block_signing_key)) -FC_REFLECT((graphene::protocol::account_witness_vote_operation), (account)(witness)(approve)) -FC_REFLECT((graphene::protocol::account_witness_proxy_operation), (account)(proxy)) +FC_REFLECT((graphene::protocol::validator_update_operation), (owner)(url)(block_signing_key)) +FC_REFLECT((graphene::protocol::account_validator_vote_operation), (account)(validator)(approve)) +FC_REFLECT((graphene::protocol::account_validator_proxy_operation), (account)(proxy)) FC_REFLECT((graphene::protocol::content_operation), (parent_author)(parent_permlink)(author)(permlink)(title)(body)(curation_percent)(json_metadata)(extensions)) FC_REFLECT((graphene::protocol::vote_operation), (voter)(author)(permlink)(weight)) FC_REFLECT((graphene::protocol::custom_operation), (required_active_auths)(required_regular_auths)(id)(json)) diff --git a/libraries/protocol/include/graphene/protocol/chain_virtual_operations.hpp b/libraries/protocol/include/graphene/protocol/chain_virtual_operations.hpp index 777f32c6d1..8d69ce63ee 100644 --- a/libraries/protocol/include/graphene/protocol/chain_virtual_operations.hpp +++ b/libraries/protocol/include/graphene/protocol/chain_virtual_operations.hpp @@ -70,11 +70,11 @@ namespace graphene { namespace protocol { }; - struct shutdown_witness_operation : public virtual_operation { - shutdown_witness_operation() { + struct shutdown_validator_operation : public virtual_operation { + shutdown_validator_operation() { } - shutdown_witness_operation(const string &o) : owner(o) { + shutdown_validator_operation(const string &o) : owner(o) { } account_name_type owner; @@ -174,15 +174,15 @@ namespace graphene { namespace protocol { asset tokens; }; - struct witness_reward_operation : public virtual_operation { - witness_reward_operation() { + struct validator_reward_operation : public virtual_operation { + validator_reward_operation() { } - witness_reward_operation(const account_name_type& w, const asset& s) - : witness(w), shares(s) { + validator_reward_operation(const account_name_type& w, const asset& s) + : validator(w), shares(s) { } - account_name_type witness; + account_name_type validator; asset shares; }; @@ -324,7 +324,7 @@ FC_REFLECT((graphene::protocol::author_reward_operation), (author)(permlink)(tok FC_REFLECT((graphene::protocol::curation_reward_operation), (curator)(reward)(content_author)(content_permlink)) FC_REFLECT((graphene::protocol::content_reward_operation), (author)(permlink)(payout)) FC_REFLECT((graphene::protocol::fill_vesting_withdraw_operation), (from_account)(to_account)(withdrawn)(deposited)) -FC_REFLECT((graphene::protocol::shutdown_witness_operation), (owner)) +FC_REFLECT((graphene::protocol::shutdown_validator_operation), (owner)) FC_REFLECT((graphene::protocol::hardfork_operation), (hardfork_id)) FC_REFLECT((graphene::protocol::content_payout_update_operation), (author)(permlink)) FC_REFLECT((graphene::protocol::content_benefactor_reward_operation), (benefactor)(author)(permlink)(reward)) @@ -333,7 +333,7 @@ FC_REFLECT((graphene::protocol::committee_cancel_request_operation), (request_id FC_REFLECT((graphene::protocol::committee_approve_request_operation), (request_id)) FC_REFLECT((graphene::protocol::committee_payout_request_operation), (request_id)) FC_REFLECT((graphene::protocol::committee_pay_request_operation), (worker)(request_id)(tokens)) -FC_REFLECT((graphene::protocol::witness_reward_operation), (witness)(shares)) +FC_REFLECT((graphene::protocol::validator_reward_operation), (validator)(shares)) FC_REFLECT((graphene::protocol::receive_award_operation), (initiator)(receiver)(custom_sequence)(memo)(shares)) FC_REFLECT((graphene::protocol::benefactor_award_operation), (initiator)(benefactor)(receiver)(custom_sequence)(memo)(shares)) FC_REFLECT((graphene::protocol::paid_subscription_action_operation), (subscriber)(account)(level)(amount)(period)(summary_amount)) diff --git a/libraries/protocol/include/graphene/protocol/operation_util_impl.hpp b/libraries/protocol/include/graphene/protocol/operation_util_impl.hpp index ec26f10994..280de9ff5c 100644 --- a/libraries/protocol/include/graphene/protocol/operation_util_impl.hpp +++ b/libraries/protocol/include/graphene/protocol/operation_util_impl.hpp @@ -9,6 +9,7 @@ namespace fc { using namespace graphene::protocol; std::string name_from_type(const std::string &type_name); + std::string resolve_operation_name(const std::string &name); struct from_operation { variant &var; @@ -116,9 +117,10 @@ void from_variant( const fc::variant& var, OperationType& vo ) \ vo.set_which( ar[0].as_uint64() ); \ else \ { \ - auto itr = to_tag.find(ar[0].as_string()); \ + auto resolved = resolve_operation_name(ar[0].as_string()); \ + auto itr = to_tag.find(resolved); \ FC_ASSERT( itr != to_tag.end(), "Invalid operation name: ${n}", ("n", ar[0]) ); \ - vo.set_which( to_tag[ar[0].as_string()] ); \ + vo.set_which( to_tag[resolved] ); \ } \ vo.visit( fc::to_static_variant( ar[1] ) ); \ } \ diff --git a/libraries/protocol/include/graphene/protocol/operations.hpp b/libraries/protocol/include/graphene/protocol/operations.hpp index c2bcd28e38..29c97c0819 100644 --- a/libraries/protocol/include/graphene/protocol/operations.hpp +++ b/libraries/protocol/include/graphene/protocol/operations.hpp @@ -20,9 +20,9 @@ namespace graphene { namespace protocol { account_update_operation, - witness_update_operation, - account_witness_vote_operation, - account_witness_proxy_operation, + validator_update_operation, + account_validator_vote_operation, + account_validator_proxy_operation, delete_content_operation,//deprecated custom_operation, @@ -47,7 +47,7 @@ namespace graphene { namespace protocol { curation_reward_operation, content_reward_operation, fill_vesting_withdraw_operation, - shutdown_witness_operation, + shutdown_validator_operation, hardfork_operation, content_payout_update_operation, content_benefactor_reward_operation, @@ -63,7 +63,7 @@ namespace graphene { namespace protocol { committee_payout_request_operation, committee_pay_request_operation, - witness_reward_operation, + validator_reward_operation, // VIZ Invite operations: create_invite_operation, diff --git a/libraries/protocol/operation_util_impl.cpp b/libraries/protocol/operation_util_impl.cpp index 035faf80ce..21f9396415 100644 --- a/libraries/protocol/operation_util_impl.cpp +++ b/libraries/protocol/operation_util_impl.cpp @@ -1,4 +1,5 @@ #include +#include namespace fc { @@ -8,4 +9,17 @@ namespace fc { return type_name.substr(start, end - start); } + std::string resolve_operation_name(const std::string &name) { + static const std::unordered_map aliases = { + {"witness_update", "validator_update"}, + {"account_witness_vote", "account_validator_vote"}, + {"account_witness_proxy", "account_validator_proxy"}, + {"shutdown_witness", "shutdown_validator"}, + {"witness_reward", "validator_reward"}, + }; + auto it = aliases.find(name); + if (it != aliases.end()) return it->second; + return name; + } + } // fc diff --git a/libraries/wallet/include/graphene/wallet/remote_node_api.hpp b/libraries/wallet/include/graphene/wallet/remote_node_api.hpp index 1079205c7e..905967fa33 100644 --- a/libraries/wallet/include/graphene/wallet/remote_node_api.hpp +++ b/libraries/wallet/include/graphene/wallet/remote_node_api.hpp @@ -94,13 +94,13 @@ struct remote_network_broadcast_api { * Class is used by wallet to send formatted API calls to witness_api plugin on remote node. */ struct remote_witness_api { - vector< account_name_type > get_active_witnesses(); - graphene::chain::witness_schedule_object get_witness_schedule(); - vector< optional< witness_api::witness_api_object > > get_witnesses( vector< witness_id_type > ); - vector< witness_api::witness_api_object > get_witnesses_by_vote( account_name_type, uint32_t ); - optional< witness_api::witness_api_object > get_witness_by_account( account_name_type ); - vector< account_name_type > lookup_witness_accounts( string, uint32_t ); - uint64_t get_witness_count(); + vector< account_name_type > get_active_validators(); + graphene::chain::validator_schedule_object get_validator_schedule(); + vector< optional< witness_api::validator_api_object > > get_validators( vector< validator_id_type > ); + vector< witness_api::validator_api_object > get_validators_by_vote( account_name_type, uint32_t ); + optional< witness_api::validator_api_object > get_validator_by_account( account_name_type ); + vector< account_name_type > lookup_validator_accounts( string, uint32_t ); + uint64_t get_validator_count(); }; } } @@ -168,11 +168,11 @@ FC_API( graphene::wallet::remote_account_by_key, * Declaration of remote API formatter to witness_api plugin on remote node */ FC_API( graphene::wallet::remote_witness_api, - (get_active_witnesses) - (get_witness_schedule) - (get_witnesses) - (get_witnesses_by_vote) - (get_witness_count) - (get_witness_by_account) - (lookup_witness_accounts) + (get_active_validators) + (get_validator_schedule) + (get_validators) + (get_validators_by_vote) + (get_validator_count) + (get_validator_by_account) + (lookup_validator_accounts) ) \ No newline at end of file diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index fd8c410349..e06302f84d 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -618,13 +618,13 @@ namespace graphene { namespace wallet { * @param limit the maximum number of witnesss to return (max: 1000) * @returns a list of witnesss mapping witness names to witness ids */ - vector< account_name_type > list_witnesses(const string& lowerbound, uint32_t limit); + vector< account_name_type > list_validators(const string& lowerbound, uint32_t limit); /** Returns information about the given witness. * @param owner_account the name or id of the witness account owner, or the id of the witness * @returns the information about the witness stored in the block chain */ - optional< witness_api::witness_api_object > get_witness(string owner_account); + optional< witness_api::validator_api_object > get_validator(string owner_account); /** * Update a witness object owned by the given account. @@ -634,8 +634,8 @@ namespace graphene { namespace wallet { * @param block_signing_key The new block signing public key. The empty string disables block production. * @param broadcast true if you wish to broadcast the transaction. */ - annotated_signed_transaction update_witness( - string witness_name, + annotated_signed_transaction update_validator( + string validator_name, string url, public_key_type block_signing_key, bool broadcast = false @@ -710,10 +710,10 @@ namespace graphene { namespace wallet { * @param approve true if the account is voting for the account to be able to be a block produce * @param broadcast true if you wish to broadcast the transaction */ - annotated_signed_transaction vote_for_witness(string account_to_vote_with, - string witness_to_vote_for, - bool approve = true, - bool broadcast = false); + annotated_signed_transaction vote_for_validator(string account_to_vote_with, + string validator_to_vote_for, + bool approve = true, + bool broadcast = false); /** * Transfer funds from one account to another. @@ -1449,8 +1449,8 @@ FC_API( graphene::wallet::wallet_api, (database_info) (list_my_accounts) (list_accounts) - (list_witnesses) - (get_witness) + (list_validators) + (get_validator) (get_account) (get_block) (get_ops_in_block) @@ -1467,12 +1467,12 @@ FC_API( graphene::wallet::wallet_api, (update_account_meta) (update_account_memo_key) (delegate_vesting_shares) - (update_witness) + (update_validator) (update_chain_properties) (versioned_update_chain_properties) (set_reward_sharing) (set_voting_proxy) - (vote_for_witness) + (vote_for_validator) //(follow) (transfer) (escrow_transfer) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index c4600b79a1..9d343580cb 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -274,7 +274,7 @@ namespace graphene { namespace wallet { auto median_props = _remote_database_api->get_chain_properties(); fc::mutable_variant_object result(fc::variant(dynamic_props).get_object()); result["witness_majority_version"] = - std::string(_remote_witness_api->get_witness_schedule().majority_version); + std::string(_remote_witness_api->get_validator_schedule().majority_version); result["hardfork_version"] = std::string(_remote_database_api->get_hardfork_version()); result["head_block_num"] = dynamic_props.head_block_number; @@ -646,7 +646,7 @@ namespace graphene { namespace wallet { signed_transaction set_voting_proxy(string account_to_modify, string proxy, bool broadcast /* = false */) { try { - account_witness_proxy_operation op; + account_validator_proxy_operation op; op.account = account_to_modify; op.proxy = proxy; @@ -657,8 +657,8 @@ namespace graphene { namespace wallet { return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (account_to_modify)(proxy)(broadcast) ) } - optional< witness_api::witness_api_object > get_witness( string owner_account ) { - return _remote_witness_api->get_witness_by_account( owner_account ); + optional< witness_api::validator_api_object > get_witness( string owner_account ) { + return _remote_witness_api->get_validator_by_account( owner_account ); } void set_transaction_expiration( uint32_t tx_expiration_seconds ) { @@ -951,8 +951,8 @@ namespace graphene { namespace wallet { return my->_remote_database_api->lookup_accounts( lowerbound, limit ); } - vector< account_name_type > wallet_api::get_active_witnesses()const { - return my->_remote_witness_api->get_active_witnesses(); + vector< account_name_type > wallet_api::get_active_validators()const { + return my->_remote_witness_api->get_active_validators(); } brain_key_info wallet_api::suggest_brain_key()const { @@ -1042,12 +1042,12 @@ fc::ecc::private_key wallet_api::derive_private_key(const std::string& prefix_st } */ - vector< account_name_type > wallet_api::list_witnesses(const string& lowerbound, uint32_t limit) + vector< account_name_type > wallet_api::list_validators(const string& lowerbound, uint32_t limit) { - return my->_remote_witness_api->lookup_witness_accounts( lowerbound, limit ); + return my->_remote_witness_api->lookup_validator_accounts( lowerbound, limit ); } - optional< witness_api::witness_api_object > wallet_api::get_witness(string owner_account) + optional< witness_api::validator_api_object > wallet_api::get_validator(string owner_account) { return my->get_witness(owner_account); } @@ -1649,7 +1649,7 @@ fc::ecc::private_key wallet_api::derive_private_key(const std::string& prefix_st * will be controlable by this wallet. */ - annotated_signed_transaction wallet_api::update_witness( + annotated_signed_transaction wallet_api::update_validator( string witness_account_name, string url, public_key_type block_signing_key, @@ -1658,10 +1658,10 @@ fc::ecc::private_key wallet_api::derive_private_key(const std::string& prefix_st FC_ASSERT(!is_locked()); signed_transaction tx; - witness_update_operation op; + validator_update_operation op; if (url.empty()) { - auto wit = my->_remote_witness_api->get_witness_by_account(witness_account_name); + auto wit = my->_remote_witness_api->get_validator_by_account(witness_account_name); if (wit.valid()) { FC_ASSERT(wit->owner == witness_account_name); url = wit->url; @@ -1732,12 +1732,12 @@ fc::ecc::private_key wallet_api::derive_private_key(const std::string& prefix_st return my->sign_transaction(tx, broadcast); } - annotated_signed_transaction wallet_api::vote_for_witness(string voting_account, string witness_to_vote_for, bool approve, bool broadcast ) + annotated_signed_transaction wallet_api::vote_for_validator(string voting_account, string witness_to_vote_for, bool approve, bool broadcast ) { try { FC_ASSERT( !is_locked() ); - account_witness_vote_operation op; + account_validator_vote_operation op; op.account = voting_account; - op.witness = witness_to_vote_for; + op.validator = witness_to_vote_for; op.approve = approve; signed_transaction tx; diff --git a/plugins/account_history/plugin.cpp b/plugins/account_history/plugin.cpp index 08cc3cbe3c..a6108530a5 100644 --- a/plugins/account_history/plugin.cpp +++ b/plugins/account_history/plugin.cpp @@ -331,16 +331,16 @@ if( options.count(name) ) { \ impacted.insert(op.account); } - void operator()(const witness_update_operation& op) { + void operator()(const validator_update_operation& op) { impacted.insert(op.owner); } - void operator()(const account_witness_vote_operation& op) { + void operator()(const account_validator_vote_operation& op) { impacted.insert(op.account); - impacted.insert(op.witness); + impacted.insert(op.validator); } - void operator()(const account_witness_proxy_operation& op) { + void operator()(const account_validator_proxy_operation& op) { impacted.insert(op.account); impacted.insert(op.proxy); } @@ -350,7 +350,7 @@ if( options.count(name) ) { \ impacted.insert(op.to_account); } - void operator()(const shutdown_witness_operation& op) { + void operator()(const shutdown_validator_operation& op) { impacted.insert(op.owner); } @@ -425,8 +425,8 @@ if( options.count(name) ) { \ impacted.insert(op.worker); } - void operator()(const witness_reward_operation& op) { - impacted.insert(op.witness); + void operator()(const validator_reward_operation& op) { + impacted.insert(op.validator); } void operator()(const set_paid_subscription_operation& op) { diff --git a/plugins/snapshot/plugin.cpp b/plugins/snapshot/plugin.cpp index f1b97ea63b..bd7f0ea214 100644 --- a/plugins/snapshot/plugin.cpp +++ b/plugins/snapshot/plugin.cpp @@ -97,6 +97,24 @@ inline void set_shared_authority(shared_authority& dst, const fc::variant& v) { } } +/// Rename old witness→validator field names in a chain_properties variant for +/// backward compatibility with snapshots created before the terminology rename. +inline fc::variant patch_chain_props_variant(fc::variant v) { + if (!v.is_object()) return v; + fc::mutable_variant_object mvo(v.get_object()); + static const std::pair aliases[] = { + {"inflation_witness_percent", "inflation_validator_percent"}, + {"witness_miss_penalty_percent", "validator_miss_penalty_percent"}, + {"witness_miss_penalty_duration","validator_miss_penalty_duration"}, + {"witness_declaration_fee", "validator_declaration_fee"}, + }; + for (auto& kv : aliases) { + if (mvo.contains(kv.first) && !mvo.contains(kv.second)) + mvo.set(kv.second, mvo[kv.first]); + } + return fc::variant(std::move(mvo)); +} + /// Generic import: convert object to variant, then apply fields via from_variant /// This works for objects without shared_string/buffer_type members. template @@ -166,7 +184,12 @@ inline uint32_t import_dynamic_global_properties( obj.vote_regeneration_per_day = v["vote_regeneration_per_day"].as_uint64(); obj.bandwidth_reserve_candidates = v["bandwidth_reserve_candidates"].as_uint64(); obj.inflation_calc_block_num = v["inflation_calc_block_num"].as_uint64(); - obj.inflation_witness_percent = static_cast(v["inflation_witness_percent"].as_int64()); + // Accept both old and new key names for backward compat with pre-rename snapshots + obj.inflation_validator_percent = static_cast( + v.get_object().contains("inflation_validator_percent") + ? v["inflation_validator_percent"].as_int64() + : v["inflation_witness_percent"].as_int64() + ); obj.inflation_ratio = static_cast(v["inflation_ratio"].as_int64()); // HF12: forward-compatible handling of emergency consensus fields @@ -244,7 +267,7 @@ inline uint32_t import_witnesses( obj.current_run = v["current_run"].as_uint64(); obj.last_supported_block_num = v["last_supported_block_num"].as_uint64(); obj.signing_key = v["signing_key"].as(); - obj.props = v["props"].as(); + obj.props = detail::patch_chain_props_variant(v["props"]).as(); obj.votes = v["votes"].as(); obj.penalty_percent = v["penalty_percent"].as_uint64(); obj.counted_votes = v["counted_votes"].as(); @@ -386,11 +409,20 @@ inline uint32_t import_witness_schedule( const fc::variants& arr ) { FC_ASSERT(arr.size() == 1, "Expected exactly 1 witness_schedule_object"); - const auto& v = arr[0]; + const auto& v_raw = arr[0]; + + // Patch old field names for backward compat with pre-rename snapshots + fc::mutable_variant_object v(v_raw.get_object()); + if (v.contains("current_shuffled_witnesses") && !v.contains("current_shuffled_validators")) + v.set("current_shuffled_validators", v["current_shuffled_witnesses"]); + if (v.contains("num_scheduled_witnesses") && !v.contains("num_scheduled_validators")) + v.set("num_scheduled_validators", v["num_scheduled_witnesses"]); + if (v.contains("median_props")) + v.set("median_props", detail::patch_chain_props_variant(v["median_props"])); const auto& wso = db.get(); db.modify(wso, [&](witness_schedule_object& obj) { - fc::from_variant(v, obj); + fc::from_variant(fc::variant(v), obj); }); return 1; } diff --git a/plugins/witness_api/include/graphene/plugins/witness_api/plugin.hpp b/plugins/witness_api/include/graphene/plugins/witness_api/plugin.hpp index 16da9e5ccf..496919b287 100644 --- a/plugins/witness_api/include/graphene/plugins/witness_api/plugin.hpp +++ b/plugins/witness_api/include/graphene/plugins/witness_api/plugin.hpp @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include @@ -44,21 +44,21 @@ using plugins::json_rpc::msg_pack; // Legacy names (kept for backward compatibility) DEFINE_API_ARGS(get_active_witnesses, msg_pack, std::vector) -DEFINE_API_ARGS(get_witness_schedule, msg_pack, graphene::chain::witness_schedule_object) -DEFINE_API_ARGS(get_witnesses, msg_pack, std::vector >) -DEFINE_API_ARGS(get_witness_by_account, msg_pack, optional) -DEFINE_API_ARGS(get_witnesses_by_vote, msg_pack, std::vector) -DEFINE_API_ARGS(get_witnesses_by_counted_vote, msg_pack, std::vector) +DEFINE_API_ARGS(get_witness_schedule, msg_pack, graphene::chain::validator_schedule_object) +DEFINE_API_ARGS(get_witnesses, msg_pack, std::vector >) +DEFINE_API_ARGS(get_witness_by_account, msg_pack, optional) +DEFINE_API_ARGS(get_witnesses_by_vote, msg_pack, std::vector) +DEFINE_API_ARGS(get_witnesses_by_counted_vote, msg_pack, std::vector) DEFINE_API_ARGS(get_witness_count, msg_pack, uint64_t) DEFINE_API_ARGS(lookup_witness_accounts, msg_pack, std::set) // Preferred names DEFINE_API_ARGS(get_active_validators, msg_pack, std::vector) -DEFINE_API_ARGS(get_validator_schedule, msg_pack, graphene::chain::witness_schedule_object) -DEFINE_API_ARGS(get_validators, msg_pack, std::vector >) -DEFINE_API_ARGS(get_validator_by_account, msg_pack, optional) -DEFINE_API_ARGS(get_validators_by_vote, msg_pack, std::vector) -DEFINE_API_ARGS(get_validators_by_counted_vote, msg_pack, std::vector) +DEFINE_API_ARGS(get_validator_schedule, msg_pack, graphene::chain::validator_schedule_object) +DEFINE_API_ARGS(get_validators, msg_pack, std::vector >) +DEFINE_API_ARGS(get_validator_by_account, msg_pack, optional) +DEFINE_API_ARGS(get_validators_by_vote, msg_pack, std::vector) +DEFINE_API_ARGS(get_validators_by_counted_vote, msg_pack, std::vector) DEFINE_API_ARGS(get_validator_count, msg_pack, uint64_t) DEFINE_API_ARGS(lookup_validator_accounts, msg_pack, std::set) diff --git a/plugins/witness_api/plugin.cpp b/plugins/witness_api/plugin.cpp index 61253680d2..5378109a1b 100644 --- a/plugins/witness_api/plugin.cpp +++ b/plugins/witness_api/plugin.cpp @@ -17,10 +17,10 @@ struct plugin::witness_plugin_impl { ~witness_plugin_impl() = default; - std::vector> get_witnesses(const std::vector &witness_ids) const; - fc::optional get_witness_by_account(std::string account_name) const; - std::vector get_witnesses_by_vote(std::string from, uint32_t limit) const; - std::vector get_witnesses_by_counted_vote(std::string from, uint32_t limit) const; + std::vector> get_witnesses(const std::vector &witness_ids) const; + fc::optional get_witness_by_account(std::string account_name) const; + std::vector get_witnesses_by_vote(std::string from, uint32_t limit) const; + std::vector get_witnesses_by_counted_vote(std::string from, uint32_t limit) const; uint64_t get_witness_count() const; std::set lookup_witness_accounts(const std::string &lower_bound_name, uint32_t limit) const; @@ -29,7 +29,7 @@ struct plugin::witness_plugin_impl { DEFINE_API(plugin, get_active_witnesses) { return my->database.with_weak_read_lock([&]() { - const auto &wso = my->database.get_witness_schedule_object(); + const auto &wso = my->database.get_validator_schedule_object(); size_t n = wso.current_shuffled_witnesses.size(); vector result; result.reserve(n); @@ -44,20 +44,20 @@ DEFINE_API(plugin, get_active_witnesses) { DEFINE_API(plugin, get_witness_schedule) { return my->database.with_weak_read_lock([&]() { - return my->database.get(witness_schedule_object::id_type()); + return my->database.get(validator_schedule_object::id_type()); }); } -std::vector> plugin::witness_plugin_impl::get_witnesses( - const std::vector &witness_ids +std::vector> plugin::witness_plugin_impl::get_witnesses( + const std::vector &witness_ids ) const { - std::vector> result; + std::vector> result; result.reserve(witness_ids.size()); std::transform( witness_ids.begin(), witness_ids.end(), std::back_inserter(result), - [&](witness_object::id_type id) -> optional { + [&](validator_object::id_type id) -> optional { if (auto o = database.find(id)) { - return witness_api_object(*o, database); + return validator_api_object(*o, database); } return {}; }); @@ -66,7 +66,7 @@ std::vector> plugin::witness_plugin_impl::get_witne DEFINE_API(plugin, get_witnesses) { CHECK_ARG_SIZE(1) - auto witness_ids = args.args->at(0).as >(); + auto witness_ids = args.args->at(0).as >(); return my->database.with_weak_read_lock([&]() { return my->get_witnesses(witness_ids); }); @@ -81,11 +81,11 @@ DEFINE_API(plugin, get_witness_by_account) { } -fc::optional plugin::witness_plugin_impl::get_witness_by_account(std::string account_name) const { +fc::optional plugin::witness_plugin_impl::get_witness_by_account(std::string account_name) const { const auto& idx = database.get_index().indices().get(); auto itr = idx.find(account_name); if (itr != idx.end()) { - return witness_api_object(*itr, database); + return validator_api_object(*itr, database); } return {}; } @@ -99,12 +99,12 @@ DEFINE_API(plugin, get_witnesses_by_vote) { }); } -std::vector plugin::witness_plugin_impl::get_witnesses_by_vote( +std::vector plugin::witness_plugin_impl::get_witnesses_by_vote( std::string from, uint32_t limit ) const { FC_ASSERT(limit <= 100); - std::vector result; + std::vector result; result.reserve(limit); const auto &name_idx = database.get_index().indices().get(); @@ -133,12 +133,12 @@ DEFINE_API(plugin, get_witnesses_by_counted_vote) { }); } -std::vector plugin::witness_plugin_impl::get_witnesses_by_counted_vote( +std::vector plugin::witness_plugin_impl::get_witnesses_by_counted_vote( std::string from, uint32_t limit ) const { FC_ASSERT(limit <= 100); - std::vector result; + std::vector result; result.reserve(limit); const auto &name_idx = database.get_index().indices().get(); diff --git a/plugins/witness_guard/witness_guard.cpp b/plugins/witness_guard/witness_guard.cpp index 70949deaa2..827a1859ad 100644 --- a/plugins/witness_guard/witness_guard.cpp +++ b/plugins/witness_guard/witness_guard.cpp @@ -204,7 +204,7 @@ void witness_guard_plugin::impl::send_witness_update( const auto& active_priv = config.active_key; // Build the witness_update operation with the correct signing key - graphene::protocol::witness_update_operation op; + graphene::protocol::validator_update_operation op; op.owner = witness_name; op.url = std::string(obj.url.begin(), obj.url.end()); op.block_signing_key = signing_pub; @@ -260,7 +260,7 @@ void witness_guard_plugin::impl::send_witness_disable( static const graphene::protocol::public_key_type null_key; // Build the witness_update operation with null signing key to disable - graphene::protocol::witness_update_operation op; + graphene::protocol::validator_update_operation op; op.owner = witness_name; op.url = std::string(obj.url.begin(), obj.url.end()); op.block_signing_key = null_key; From 76b8d4762f251a044f48c7830331e15276868878 Mon Sep 17 00:00:00 2001 From: Anatoly Piskunov Date: Sun, 17 May 2026 13:35:33 +0400 Subject: [PATCH 02/29] renamed missed witness fields --- libraries/api/witness_api_object.cpp | 2 +- .../custom_protocol_api.cpp | 2 +- plugins/database_api/api.cpp | 4 +- plugins/snapshot/plugin.cpp | 22 +++---- plugins/witness/witness.cpp | 64 +++++++++---------- plugins/witness_api/plugin.cpp | 14 ++-- plugins/witness_guard/witness_guard.cpp | 8 +-- 7 files changed, 58 insertions(+), 58 deletions(-) diff --git a/libraries/api/witness_api_object.cpp b/libraries/api/witness_api_object.cpp index 41cdd4a4a3..bb441e5222 100644 --- a/libraries/api/witness_api_object.cpp +++ b/libraries/api/witness_api_object.cpp @@ -1,7 +1,7 @@ #include namespace graphene { namespace api { - validator_api_object::validator_api_object(const witness_object &w, const database& db) + validator_api_object::validator_api_object(const validator_object &w, const database& db) : id(w.id), owner(w.owner), created(w.created), url(to_string(w.url)), total_missed(w.total_missed), last_aslot(w.last_aslot), last_confirmed_block_num(w.last_confirmed_block_num), diff --git a/plugins/custom_protocol_api/custom_protocol_api.cpp b/plugins/custom_protocol_api/custom_protocol_api.cpp index c4906ddaa9..0577c4e9a0 100644 --- a/plugins/custom_protocol_api/custom_protocol_api.cpp +++ b/plugins/custom_protocol_api/custom_protocol_api.cpp @@ -124,7 +124,7 @@ namespace graphene { namespace plugins { namespace custom_protocol_api { if (itr != idx.end()) { result=account_api_object(*itr, db); - auto vitr = vidx.lower_bound(boost::make_tuple(itr->id, witness_id_type())); + auto vitr = vidx.lower_bound(boost::make_tuple(itr->id, validator_id_type())); while (vitr != vidx.end() && vitr->account == itr->id) { result.witness_votes.insert(db.get(vitr->witness).owner); ++vitr; diff --git a/plugins/database_api/api.cpp b/plugins/database_api/api.cpp index 3196f10aa8..d3620348bc 100755 --- a/plugins/database_api/api.cpp +++ b/plugins/database_api/api.cpp @@ -337,7 +337,7 @@ DEFINE_API(plugin, get_dynamic_global_properties) { DEFINE_API(plugin, get_chain_properties) { return my->database().with_weak_read_lock([&]() { - return chain_api_properties(my->database().get_witness_schedule_object().median_props, my->database()); + return chain_api_properties(my->database().get_validator_schedule_object().median_props, my->database()); }); } @@ -383,7 +383,7 @@ std::vector plugin::api_impl::get_accounts(std::vectorid, witness_id_type())); + auto vitr = vidx.lower_bound(boost::make_tuple(itr->id, validator_id_type())); while (vitr != vidx.end() && vitr->account == itr->id) { results.back().witness_votes.insert(_db.get(vitr->witness).owner); ++vitr; diff --git a/plugins/snapshot/plugin.cpp b/plugins/snapshot/plugin.cpp index bd7f0ea214..ef4ef277d7 100644 --- a/plugins/snapshot/plugin.cpp +++ b/plugins/snapshot/plugin.cpp @@ -254,10 +254,10 @@ inline uint32_t import_witnesses( uint32_t count = 0; for (const auto& v : arr) { auto id_val = v["id"].as_int64(); - auto& mutable_idx = db.get_mutable_index(); - mutable_idx.set_next_id(witness_id_type(id_val)); + auto& mutable_idx = db.get_mutable_index(); + mutable_idx.set_next_id(validator_id_type(id_val)); - db.create([&](witness_object& obj) { + db.create([&](validator_object& obj) { obj.owner = v["owner"].as(); obj.created = v["created"].as(); detail::set_shared_string(obj.url, v["url"]); @@ -271,7 +271,7 @@ inline uint32_t import_witnesses( obj.votes = v["votes"].as(); obj.penalty_percent = v["penalty_percent"].as_uint64(); obj.counted_votes = v["counted_votes"].as(); - obj.schedule = v["schedule"].as(); + obj.schedule = v["schedule"].as(); obj.virtual_last_update = v["virtual_last_update"].as(); obj.virtual_position = v["virtual_position"].as(); obj.virtual_scheduled_time = v["virtual_scheduled_time"].as(); @@ -393,7 +393,7 @@ inline uint32_t import_witness_votes( mutable_idx.set_next_id(witness_vote_id_type(id_val)); db.create([&](witness_vote_object& obj) { - obj.witness = v["witness"].as(); + obj.witness = v["witness"].as(); obj.account = v["account"].as(); // HF13: flash-voter protection (default 0 for pre-HF13 snapshots) if (v.get_object().contains("vote_created_block")) @@ -408,7 +408,7 @@ inline uint32_t import_witness_schedule( graphene::chain::database& db, const fc::variants& arr ) { - FC_ASSERT(arr.size() == 1, "Expected exactly 1 witness_schedule_object"); + FC_ASSERT(arr.size() == 1, "Expected exactly 1 validator_schedule_object"); const auto& v_raw = arr[0]; // Patch old field names for backward compat with pre-rename snapshots @@ -420,8 +420,8 @@ inline uint32_t import_witness_schedule( if (v.contains("median_props")) v.set("median_props", detail::patch_chain_props_variant(v["median_props"])); - const auto& wso = db.get(); - db.modify(wso, [&](witness_schedule_object& obj) { + const auto& wso = db.get(); + db.modify(wso, [&](validator_schedule_object& obj) { fc::from_variant(fc::variant(v), obj); }); return 1; @@ -924,11 +924,11 @@ fc::mutable_variant_object snapshot_plugin::plugin_impl::serialize_state() { // CRITICAL objects EXPORT_INDEX(dynamic_global_property_index, dynamic_global_property_object, "dynamic_global_property") - EXPORT_INDEX(witness_schedule_index, witness_schedule_object, "witness_schedule") + EXPORT_INDEX(validator_schedule_index, validator_schedule_object, "witness_schedule") EXPORT_INDEX(hardfork_property_index, hardfork_property_object, "hardfork_property") EXPORT_INDEX(account_index, account_object, "account") EXPORT_INDEX(account_authority_index, account_authority_object, "account_authority") - EXPORT_INDEX(witness_index, witness_object, "witness") + EXPORT_INDEX(validator_index, validator_object, "witness") EXPORT_INDEX(witness_vote_index, witness_vote_object, "witness_vote") EXPORT_INDEX(block_summary_index, block_summary_object, "block_summary") EXPORT_INDEX(content_index, content_object, "content") @@ -1394,7 +1394,7 @@ void snapshot_plugin::plugin_impl::load_snapshot(const fc::path& input_path) { const auto& auth_idx = db.get_index().indices(); while (!auth_idx.empty()) { db.remove(*auth_idx.begin()); } - const auto& wit_idx = db.get_index().indices(); + const auto& wit_idx = db.get_index().indices(); while (!wit_idx.empty()) { db.remove(*wit_idx.begin()); } const auto& meta_idx = db.get_index().indices(); diff --git a/plugins/witness/witness.cpp b/plugins/witness/witness.cpp index b3efd864ee..ec1fc6bad8 100644 --- a/plugins/witness/witness.cpp +++ b/plugins/witness/witness.cpp @@ -430,7 +430,7 @@ namespace graphene { continue; } - const auto& witness_by_name = db.get_index().indices().get(); + const auto& witness_by_name = db.get_index().indices().get(); auto itr = witness_by_name.find(scheduled_witness); if (itr == witness_by_name.end()) { continue; @@ -476,7 +476,7 @@ namespace graphene { continue; } - const auto& witness_by_name = db.get_index().indices().get(); + const auto& witness_by_name = db.get_index().indices().get(); auto itr = witness_by_name.find(scheduled_witness); if (itr == witness_by_name.end()) { continue; @@ -517,9 +517,9 @@ namespace graphene { // Condition 2: the committee account is in the current witness schedule. auto& db = pimpl->database(); return db.with_weak_read_lock([&]() -> bool { - const witness_schedule_object& wso = db.get_witness_schedule_object(); - for (int i = 0; i < wso.num_scheduled_witnesses; i += CHAIN_BLOCK_WITNESS_REPEAT) { - if (wso.current_shuffled_witnesses[i] == CHAIN_EMERGENCY_WITNESS_ACCOUNT) { + const validator_schedule_object& wso = db.get_validator_schedule_object(); + for (int i = 0; i < wso.num_scheduled_validators; i += CHAIN_BLOCK_WITNESS_REPEAT) { + if (wso.current_shuffled_validators[i] == CHAIN_EMERGENCY_WITNESS_ACCOUNT) { return true; } } @@ -589,14 +589,14 @@ namespace graphene { if (database()._dlt_mode && !_witnesses.empty() && prev_num > 0 && block_num == prev_num + 1 && dgp_hijack.emergency_consensus_active) { - const auto& wso_sj = database().get_witness_schedule_object(); - uint32_t nsw_sj = wso_sj.num_scheduled_witnesses; + const auto& wso_sj = database().get_validator_schedule_object(); + uint32_t nsw_sj = wso_sj.num_scheduled_validators; if (nsw_sj > 0) { // apply_block increments current_aslot to the applied block's slot // before this callback fires, so current_aslot IS the applied slot. uint64_t slot_idx = dgp_hijack.current_aslot % nsw_sj; const std::string& expected_witness = - wso_sj.current_shuffled_witnesses[slot_idx]; + wso_sj.current_shuffled_validators[slot_idx]; bool was_our_slot = _witnesses.count(expected_witness) > 0; // True hijack only if the actual producer is NOT also one of our witnesses. bool producer_is_ours = _witnesses.count(block.witness) > 0; @@ -646,9 +646,9 @@ namespace graphene { // So: old_aslot = current_aslot - missed_count - 1 // Missed slot i (0-based): abs_slot = current_aslot - missed_count + i const auto &dgp = database().get_dynamic_global_properties(); - const auto &wso = database().get_witness_schedule_object(); + const auto &wso = database().get_validator_schedule_object(); uint64_t cur_aslot = dgp.current_aslot; - uint32_t num_witnesses = wso.num_scheduled_witnesses; + uint32_t num_witnesses = wso.num_scheduled_validators; if (num_witnesses == 0) return; // Check each missed slot to see if our witness was scheduled @@ -656,7 +656,7 @@ namespace graphene { std::string missed_witnesses_list; for (uint32_t i = 0; i < missed_count && i < 100; ++i) { uint64_t abs_slot = cur_aslot - missed_count + i; - const std::string &wname = wso.current_shuffled_witnesses[abs_slot % num_witnesses]; + const std::string &wname = wso.current_shuffled_validators[abs_slot % num_witnesses]; if (!missed_witnesses_list.empty()) missed_witnesses_list += ","; missed_witnesses_list += wname; if (_witnesses.count(wname) > 0) { @@ -690,7 +690,7 @@ namespace graphene { // Check on-chain signing key status for our witnesses std::string key_status; - const auto &wit_idx = database().get_index() + const auto &wit_idx = database().get_index() .indices().get(); for (const auto &wname : _witnesses) { auto witr = wit_idx.find(wname); @@ -976,11 +976,11 @@ namespace graphene { try { catching_up120 = p2p().is_catching_up_after_pause(); } catch (...) {} bool dlt_syncing120 = false; try { dlt_syncing120 = chain().is_syncing(); } catch (...) {} - const auto &wso120 = database().get_witness_schedule_object(); + const auto &wso120 = database().get_validator_schedule_object(); std::string shuffled_top3; - for (int i = 0; i < std::min(3, wso120.num_scheduled_witnesses); i++) { + for (int i = 0; i < std::min(3, wso120.num_scheduled_validators); i++) { if (!shuffled_top3.empty()) shuffled_top3 += ","; - shuffled_top3 += wso120.current_shuffled_witnesses[i]; + shuffled_top3 += wso120.current_shuffled_validators[i]; } std::string _next_w120 = database().get_scheduled_witness(1); bool _ours120 = _witnesses.count(_next_w120) > 0; @@ -1128,14 +1128,14 @@ namespace graphene { } // Scan full shuffled schedule for our witnesses - const auto &wso_wd = db_wd.get_witness_schedule_object(); - for (int i = 0; i < wso_wd.num_scheduled_witnesses; i++) { - if (_witnesses.count(wso_wd.current_shuffled_witnesses[i]) > 0) + const auto &wso_wd = db_wd.get_validator_schedule_object(); + for (int i = 0; i < wso_wd.num_scheduled_validators; i++) { + if (_witnesses.count(wso_wd.current_shuffled_validators[i]) > 0) our_slots_in_schedule++; } // Check on-chain signing keys for our witnesses - const auto &wit_idx = db_wd.get_index().indices().get(); + const auto &wit_idx = db_wd.get_index().indices().get(); for (const auto& w_name : _witnesses) { auto w_itr = wit_idx.find(w_name); if (w_itr != wit_idx.end() && @@ -1417,11 +1417,11 @@ namespace graphene { // that is not in the current schedule cannot contribute to LIB // advancement and broadcasting their post-validation is wasted // bandwidth and CPU. - const witness_schedule_object &wso = db.get_witness_schedule_object(); + const validator_schedule_object &wso = db.get_validator_schedule_object(); std::set scheduled_witnesses_set; - for (int i = 0; i < wso.num_scheduled_witnesses; i += CHAIN_BLOCK_WITNESS_REPEAT) { - if (wso.current_shuffled_witnesses[i] != account_name_type()) { - scheduled_witnesses_set.insert(wso.current_shuffled_witnesses[i]); + for (int i = 0; i < wso.num_scheduled_validators; i += CHAIN_BLOCK_WITNESS_REPEAT) { + if (wso.current_shuffled_validators[i] != account_name_type()) { + scheduled_witnesses_set.insert(wso.current_shuffled_validators[i]); } } @@ -1439,7 +1439,7 @@ namespace graphene { auto block_post_validations = db.get_block_post_validations(witness_account); if (db._debug_block_production) ilog("DEBUG_CRASH: got ${n} post_validations for ${w}", ("n", block_post_validations.size())("w", witness_account)); if (block_post_validations.size() > 0) { - const auto &witness_by_name = db.get_index().indices().get(); + const auto &witness_by_name = db.get_index().indices().get(); auto w_itr = witness_by_name.find(witness_account); if (w_itr == witness_by_name.end()) { wlog("Witness ${w} not found in witness index, skipping block post validation", ("w", witness_account)); @@ -1571,9 +1571,9 @@ namespace graphene { // emergency-private-key was configured — see plugin_initialize). bool we_are_master = false; if (_witnesses.find(CHAIN_EMERGENCY_WITNESS_ACCOUNT) != _witnesses.end()) { - const witness_schedule_object &wso = db.get_witness_schedule_object(); - for (int i = 0; i < wso.num_scheduled_witnesses; i += CHAIN_BLOCK_WITNESS_REPEAT) { - if (wso.current_shuffled_witnesses[i] == CHAIN_EMERGENCY_WITNESS_ACCOUNT) { + const validator_schedule_object &wso = db.get_validator_schedule_object(); + for (int i = 0; i < wso.num_scheduled_validators; i += CHAIN_BLOCK_WITNESS_REPEAT) { + if (wso.current_shuffled_validators[i] == CHAIN_EMERGENCY_WITNESS_ACCOUNT) { we_are_master = true; break; } @@ -1675,7 +1675,7 @@ namespace graphene { ("now", now_fine) ("ns", db.get_slot_time(1)) ("a", _dgp2.current_aslot) - ("ns2", db.get_witness_schedule_object().num_scheduled_witnesses)); + ("ns2", db.get_validator_schedule_object().num_scheduled_validators)); } } } @@ -1727,14 +1727,14 @@ namespace graphene { auto _now3 = fc::time_point::now(); if ((_now3 - _last_nmt_log).count() > 3000000) { // log every 3s max (once per slot) _last_nmt_log = _now3; - const auto &_wso3 = db.get_witness_schedule_object(); + const auto &_wso3 = db.get_validator_schedule_object(); dlog("EMRG-DIAG not_my_turn: slot=${s} scheduled=${sw} head=#${h} aslot=${a} num_sched=${ns} aslot_mod=${am}", ("s", slot) ("sw", scheduled_witness) ("h", _dgp3.head_block_number) ("a", _dgp3.current_aslot) - ("ns", _wso3.num_scheduled_witnesses) - ("am", _dgp3.current_aslot % _wso3.num_scheduled_witnesses)); + ("ns", _wso3.num_scheduled_validators) + ("am", _dgp3.current_aslot % _wso3.num_scheduled_validators)); } } } @@ -1742,7 +1742,7 @@ namespace graphene { } if (db._debug_block_production) ilog("DEBUG_CRASH: looking up witness in index"); - const auto &witness_by_name = db.get_index().indices().get(); + const auto &witness_by_name = db.get_index().indices().get(); auto itr = witness_by_name.find(scheduled_witness); if (db._debug_block_production) ilog("DEBUG_CRASH: witness found=${f}", ("f", itr != witness_by_name.end())); diff --git a/plugins/witness_api/plugin.cpp b/plugins/witness_api/plugin.cpp index 5378109a1b..a7a5219917 100644 --- a/plugins/witness_api/plugin.cpp +++ b/plugins/witness_api/plugin.cpp @@ -82,7 +82,7 @@ DEFINE_API(plugin, get_witness_by_account) { fc::optional plugin::witness_plugin_impl::get_witness_by_account(std::string account_name) const { - const auto& idx = database.get_index().indices().get(); + const auto& idx = database.get_index().indices().get(); auto itr = idx.find(account_name); if (itr != idx.end()) { return validator_api_object(*itr, database); @@ -107,8 +107,8 @@ std::vector plugin::witness_plugin_impl::get_witnesses_by_ std::vector result; result.reserve(limit); - const auto &name_idx = database.get_index().indices().get(); - const auto &vote_idx = database.get_index().indices().get(); + const auto &name_idx = database.get_index().indices().get(); + const auto &vote_idx = database.get_index().indices().get(); auto itr = vote_idx.begin(); if (from.size()) { @@ -141,8 +141,8 @@ std::vector plugin::witness_plugin_impl::get_witnesses_by_ std::vector result; result.reserve(limit); - const auto &name_idx = database.get_index().indices().get(); - const auto &vote_idx = database.get_index().indices().get(); + const auto &name_idx = database.get_index().indices().get(); + const auto &vote_idx = database.get_index().indices().get(); auto itr = vote_idx.begin(); if (from.size()) { @@ -165,7 +165,7 @@ DEFINE_API(plugin, get_witness_count) { } uint64_t plugin::witness_plugin_impl::get_witness_count() const { - return database.get_index().indices().size(); + return database.get_index().indices().size(); } DEFINE_API(plugin, lookup_witness_accounts) { @@ -182,7 +182,7 @@ std::set plugin::witness_plugin_impl::lookup_witness_accounts uint32_t limit ) const { FC_ASSERT(limit <= 1000); - const auto &witnesses_by_id = database.get_index().indices().get(); + const auto &witnesses_by_id = database.get_index().indices().get(); // get all the names and look them all up, sort them, then figure out what // records to return. This could be optimized, but we expect the diff --git a/plugins/witness_guard/witness_guard.cpp b/plugins/witness_guard/witness_guard.cpp index 827a1859ad..ef3a180ec3 100644 --- a/plugins/witness_guard/witness_guard.cpp +++ b/plugins/witness_guard/witness_guard.cpp @@ -67,10 +67,10 @@ struct witness_guard_plugin::impl { bool check_and_restore_internal(); void send_witness_update(const std::string& witness_name, - const graphene::chain::witness_object& obj, + const graphene::chain::validator_object& obj, const witness_info& config); void send_witness_disable(const std::string& witness_name, - const graphene::chain::witness_object& obj, + const graphene::chain::validator_object& obj, const witness_info& config); graphene::plugins::chain::plugin& chain_; @@ -196,7 +196,7 @@ bool witness_guard_plugin::impl::check_and_restore_internal() { void witness_guard_plugin::impl::send_witness_update( const std::string& witness_name, - const graphene::chain::witness_object& obj, + const graphene::chain::validator_object& obj, const witness_info& config) { try { @@ -251,7 +251,7 @@ void witness_guard_plugin::impl::send_witness_update( void witness_guard_plugin::impl::send_witness_disable( const std::string& witness_name, - const graphene::chain::witness_object& obj, + const graphene::chain::validator_object& obj, const witness_info& config) { try { From d3aa9efd0cdd4d516cf9a280f5866a415bbf07cd Mon Sep 17 00:00:00 2001 From: Anatoly Piskunov Date: Sun, 17 May 2026 13:45:18 +0400 Subject: [PATCH 03/29] add missed rename vars --- plugins/witness_guard/witness_guard.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/witness_guard/witness_guard.cpp b/plugins/witness_guard/witness_guard.cpp index ef3a180ec3..dc1e28422d 100644 --- a/plugins/witness_guard/witness_guard.cpp +++ b/plugins/witness_guard/witness_guard.cpp @@ -141,7 +141,7 @@ bool witness_guard_plugin::impl::check_and_restore_internal() { } const auto& idx = database - .get_index() + .get_index() .indices() .get(); @@ -512,7 +512,7 @@ void witness_guard_plugin::plugin_startup() { // Look up the witness object and send disable transaction const auto& idx = pimpl->db() - .get_index() + .get_index() .indices() .get(); auto itr = idx.find(producer); @@ -558,7 +558,7 @@ void witness_guard_plugin::plugin_startup() { bool scheduled_soon = false; if (pimpl->_initial_check_done) { for (uint32_t i = 1; i <= 3; ++i) { - if (pimpl->_witness_configs.count(pimpl->db().get_scheduled_witness(i))) { + if (pimpl->_witness_configs.count(pimpl->db().get_scheduled_validator(i))) { scheduled_soon = true; break; } From ad4401df68b062f1163ff455d2abc1631cb565f8 Mon Sep 17 00:00:00 2001 From: Anatoly Piskunov Date: Sun, 17 May 2026 14:01:04 +0400 Subject: [PATCH 04/29] fix rename --- .../include/graphene/plugins/database_api/forward.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/database_api/include/graphene/plugins/database_api/forward.hpp b/plugins/database_api/include/graphene/plugins/database_api/forward.hpp index b79c97c82d..384d2e7f8a 100644 --- a/plugins/database_api/include/graphene/plugins/database_api/forward.hpp +++ b/plugins/database_api/include/graphene/plugins/database_api/forward.hpp @@ -12,7 +12,7 @@ namespace graphene { namespace plugins { namespace database_api { typedef graphene::chain::escrow_object escrow_api_object; typedef graphene::chain::withdraw_vesting_route_object withdraw_vesting_route_api_object; typedef graphene::chain::witness_vote_object witness_vote_api_object; - typedef graphene::chain::witness_schedule_object witness_schedule_api_object; + typedef graphene::chain::validator_schedule_object witness_schedule_api_object; using vesting_delegation_api_object = graphene::chain::vesting_delegation_object; using vesting_delegation_expiration_api_object = graphene::chain::vesting_delegation_expiration_object; From df04493991bec7b6b736b35e6c7f3c26d3914447 Mon Sep 17 00:00:00 2001 From: Anatoly Piskunov Date: Sun, 17 May 2026 14:14:51 +0400 Subject: [PATCH 05/29] fix renames --- plugins/witness/witness.cpp | 26 +++++++++---------- .../graphene/plugins/witness_api/plugin.hpp | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/plugins/witness/witness.cpp b/plugins/witness/witness.cpp index ec1fc6bad8..733ba71224 100644 --- a/plugins/witness/witness.cpp +++ b/plugins/witness/witness.cpp @@ -425,7 +425,7 @@ namespace graphene { // Check 5 upcoming slots (~15 seconds) to cover snapshot creation time (~10s) + safety margin for (uint32_t s = slot; s <= slot + 4; ++s) { - string scheduled_witness = db.get_scheduled_witness(s); + string scheduled_witness = db.get_scheduled_validator(s); if (pimpl->_witnesses.find(scheduled_witness) == pimpl->_witnesses.end()) { continue; } @@ -471,7 +471,7 @@ namespace graphene { } for (uint32_t s = slot; s <= slot + 4; ++s) { - string scheduled_witness = db.get_scheduled_witness(s); + string scheduled_witness = db.get_scheduled_validator(s); if (pimpl->_witnesses.find(scheduled_witness) == pimpl->_witnesses.end()) { continue; } @@ -686,7 +686,7 @@ namespace graphene { } fc::time_point_sec next_slot = database().get_slot_time(1); - std::string next_scheduled = database().get_scheduled_witness(1); + std::string next_scheduled = database().get_scheduled_validator(1); // Check on-chain signing key status for our witnesses std::string key_status; @@ -909,7 +909,7 @@ namespace graphene { int64_t _drift_us = 0; try { _drift_us = graphene::time::ntp_error().count(); } catch (...) {} int64_t _gap_ms = (_nst_init - _now_init).count() / 1000; - std::string _next_w3 = database().get_scheduled_witness(1); + std::string _next_w3 = database().get_scheduled_validator(1); bool _ours3 = _witnesses.count(_next_w3) > 0; wlog("SLOT=0 STREAK: ${n} consecutive not_time_yet. " "now=${now} head_block_time=${hbt} (drift=${d}us) " @@ -927,7 +927,7 @@ namespace graphene { int64_t _drift10 = 0; try { _drift10 = graphene::time::ntp_error().count(); } catch (...) {} int64_t _gap_ms = (_nst10 - _now10).count() / 1000; - std::string _next_w10 = database().get_scheduled_witness(1); + std::string _next_w10 = database().get_scheduled_validator(1); bool _ours10 = _witnesses.count(_next_w10) > 0; wlog("slot=0 streak: ${n} consecutive not_time_yet (${s}s elapsed). " "now=${now}, head_block_time=${hbt}, next_slot_time=${nst}. " @@ -951,7 +951,7 @@ namespace graphene { try { catching_up = p2p().is_catching_up_after_pause(); } catch (...) {} bool dlt_syncing = false; try { dlt_syncing = chain().is_syncing(); } catch (...) {} - std::string _next_w60 = database().get_scheduled_witness(1); + std::string _next_w60 = database().get_scheduled_validator(1); bool _ours60 = _witnesses.count(_next_w60) > 0; elog("SLOT=0 PROLONGED STALL: ${n} consecutive not_time_yet (${s}s). " "head_block_time=${hbt} is ${f}ms AHEAD of now=${now}! " @@ -982,7 +982,7 @@ namespace graphene { if (!shuffled_top3.empty()) shuffled_top3 += ","; shuffled_top3 += wso120.current_shuffled_validators[i]; } - std::string _next_w120 = database().get_scheduled_witness(1); + std::string _next_w120 = database().get_scheduled_validator(1); bool _ours120 = _witnesses.count(_next_w120) > 0; elog("CRITICAL: slot=0 stall for ${s}s! head_block_time=${hbt} is ${f}ms in the future " "relative to NTP time (now=${now}). next_slot_time=${nst} next_witness=${nw} is_ours=${o}, NTP drift=${d}us. " @@ -1120,11 +1120,11 @@ namespace graphene { fc::time_point_sec now_sec = graphene::time::now() + fc::microseconds(250000); uint32_t cur_slot = db_wd.get_slot_at_time(now_sec); if (cur_slot > 0) { - scheduled_now = db_wd.get_scheduled_witness(cur_slot); + scheduled_now = db_wd.get_scheduled_validator(cur_slot); we_are_scheduled = _witnesses.count(scheduled_now) > 0; } else { // Between slots: show who gets the NEXT slot - scheduled_now = "between_slots/" + db_wd.get_scheduled_witness(1); + scheduled_now = "between_slots/" + db_wd.get_scheduled_validator(1); } // Scan full shuffled schedule for our witnesses @@ -1640,7 +1640,7 @@ namespace graphene { int64_t _guard_ms = (fc::time_point::now() - _guard_enter).count() / 1000; if (_guard_ms > 100) { uint32_t _slot_before = db.get_slot_at_time(now_fine + fc::microseconds(250000) - fc::microseconds(_guard_ms * 1000)); - std::string _wit_before = _slot_before > 0 ? db.get_scheduled_witness(_slot_before) : "none"; + std::string _wit_before = _slot_before > 0 ? db.get_scheduled_validator(_slot_before) : "none"; bool _our_slot_lost = _slot_before > 0 && _witnesses.count(_wit_before) > 0; if (_our_slot_lost) { elog("WITNESS-SLOT-LOST: op_guard stall ${d}ms crossed slot boundary! " @@ -1711,15 +1711,15 @@ namespace graphene { // assert(now > db.head_block_time()); - if (db._debug_block_production) ilog("DEBUG_CRASH: get_scheduled_witness(${s})", ("s", slot)); - string scheduled_witness = db.get_scheduled_witness(slot); + if (db._debug_block_production) ilog("DEBUG_CRASH: get_scheduled_validator(${s})", ("s", slot)); + string scheduled_witness = db.get_scheduled_validator(slot); if (db._debug_block_production) ilog("DEBUG_CRASH: scheduled_witness=${w}", ("w", scheduled_witness)); // we must control the witness scheduled to produce the next block. if (_witnesses.find(scheduled_witness) == _witnesses.end()) { capture("scheduled_witness", scheduled_witness); _last_scheduled_witness = scheduled_witness; // track for diagnostic // Emergency master diagnostic: log when committee is configured but - // get_scheduled_witness returned a different name — reveals schedule misalignment + // get_scheduled_validator returned a different name — reveals schedule misalignment if (_witnesses.find(CHAIN_EMERGENCY_WITNESS_ACCOUNT) != _witnesses.end()) { const auto &_dgp3 = db.get_dynamic_global_properties(); if (_dgp3.emergency_consensus_active) { diff --git a/plugins/witness_api/include/graphene/plugins/witness_api/plugin.hpp b/plugins/witness_api/include/graphene/plugins/witness_api/plugin.hpp index 496919b287..43436a9f3d 100644 --- a/plugins/witness_api/include/graphene/plugins/witness_api/plugin.hpp +++ b/plugins/witness_api/include/graphene/plugins/witness_api/plugin.hpp @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include From 06ddbfc7e682b7d5a11795cdbf0f462dec37d635 Mon Sep 17 00:00:00 2001 From: Anatoly Piskunov Date: Sun, 17 May 2026 14:17:01 +0400 Subject: [PATCH 06/29] fix renames --- plugins/witness_api/plugin.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/witness_api/plugin.cpp b/plugins/witness_api/plugin.cpp index a7a5219917..a90142de6b 100644 --- a/plugins/witness_api/plugin.cpp +++ b/plugins/witness_api/plugin.cpp @@ -30,12 +30,12 @@ struct plugin::witness_plugin_impl { DEFINE_API(plugin, get_active_witnesses) { return my->database.with_weak_read_lock([&]() { const auto &wso = my->database.get_validator_schedule_object(); - size_t n = wso.current_shuffled_witnesses.size(); + size_t n = wso.current_shuffled_validators.size(); vector result; result.reserve(n); for (size_t i = 0; i < n; i++) { - if (wso.current_shuffled_witnesses[i] != "") { - result.push_back(wso.current_shuffled_witnesses[i]); + if (wso.current_shuffled_validators[i] != "") { + result.push_back(wso.current_shuffled_validators[i]); } } return result; From cc3d06f026306d99a42469a10911a8edee3fb305 Mon Sep 17 00:00:00 2001 From: Anatoly Piskunov Date: Sun, 17 May 2026 14:32:47 +0400 Subject: [PATCH 07/29] fix(snapshot): use find() instead of contains() on mutable_variant_object MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fc::mutable_variant_object does not have a contains() method — only fc::variant_object (read-only) does. Replace all .contains() calls with .find() != .end() in patch_chain_props_variant() and import_witness_schedule(). --- plugins/snapshot/plugin.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/snapshot/plugin.cpp b/plugins/snapshot/plugin.cpp index ef4ef277d7..d94a647df9 100644 --- a/plugins/snapshot/plugin.cpp +++ b/plugins/snapshot/plugin.cpp @@ -109,7 +109,7 @@ inline fc::variant patch_chain_props_variant(fc::variant v) { {"witness_declaration_fee", "validator_declaration_fee"}, }; for (auto& kv : aliases) { - if (mvo.contains(kv.first) && !mvo.contains(kv.second)) + if (mvo.find(kv.first) != mvo.end() && mvo.find(kv.second) == mvo.end()) mvo.set(kv.second, mvo[kv.first]); } return fc::variant(std::move(mvo)); @@ -413,11 +413,11 @@ inline uint32_t import_witness_schedule( // Patch old field names for backward compat with pre-rename snapshots fc::mutable_variant_object v(v_raw.get_object()); - if (v.contains("current_shuffled_witnesses") && !v.contains("current_shuffled_validators")) + if (v.find("current_shuffled_witnesses") != v.end() && v.find("current_shuffled_validators") == v.end()) v.set("current_shuffled_validators", v["current_shuffled_witnesses"]); - if (v.contains("num_scheduled_witnesses") && !v.contains("num_scheduled_validators")) + if (v.find("num_scheduled_witnesses") != v.end() && v.find("num_scheduled_validators") == v.end()) v.set("num_scheduled_validators", v["num_scheduled_witnesses"]); - if (v.contains("median_props")) + if (v.find("median_props") != v.end()) v.set("median_props", detail::patch_chain_props_variant(v["median_props"])); const auto& wso = db.get(); From 30a7a8997579693df601f0c9e1a6d9e323e89b54 Mon Sep 17 00:00:00 2001 From: Anatoly Piskunov Date: Sun, 17 May 2026 14:58:27 +0400 Subject: [PATCH 08/29] fix naming --- libraries/wallet/include/graphene/wallet/wallet.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index e06302f84d..df7c17ef76 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -257,7 +257,7 @@ namespace graphene { namespace wallet { /** * Returns the list of witnesses producing blocks in the current round (21 Blocks) */ - vector< account_name_type > get_active_witnesses()const; + vector< account_name_type > get_active_validators()const; /** * Returns vesting withdraw routes for an account. @@ -1541,7 +1541,7 @@ FC_API( graphene::wallet::wallet_api, (serialize_transaction) (sign_transaction) - (get_active_witnesses) + (get_active_validators) (get_transaction) ) From 2ee5bc9a60c718feff25c25d5bf13cdf0a29d101 Mon Sep 17 00:00:00 2001 From: Anatoly Piskunov Date: Sun, 17 May 2026 16:19:21 +0400 Subject: [PATCH 09/29] more renames --- libraries/chain/database.cpp | 64 +++++++++---------- .../chain/include/graphene/chain/database.hpp | 2 +- .../graphene/chain/global_property_object.hpp | 4 +- libraries/network/dlt_p2p_node.cpp | 14 ++-- .../include/graphene/network/dlt_p2p_node.hpp | 2 +- libraries/protocol/block.cpp | 4 +- .../graphene/protocol/block_header.hpp | 8 +-- plugins/chain/plugin.cpp | 2 +- .../graphene/plugins/p2p/p2p_plugin.hpp | 4 +- plugins/p2p/p2p_plugin.cpp | 12 ++-- plugins/snapshot/plugin.cpp | 6 +- plugins/witness/witness.cpp | 16 ++--- plugins/witness_guard/witness_guard.cpp | 4 +- 13 files changed, 72 insertions(+), 70 deletions(-) diff --git a/libraries/chain/database.cpp b/libraries/chain/database.cpp index 1f7b88b2df..cb318f0895 100644 --- a/libraries/chain/database.cpp +++ b/libraries/chain/database.cpp @@ -549,7 +549,7 @@ namespace graphene { namespace chain { uint64_t skip_flags = skip_block_size_check | - skip_witness_signature | + skip_validator_signature | skip_transaction_signatures | skip_transaction_dupe_check | skip_tapos_check | @@ -669,7 +669,7 @@ namespace graphene { namespace chain { uint64_t skip_flags = skip_block_size_check | - skip_witness_signature | + skip_validator_signature | skip_transaction_signatures | skip_transaction_dupe_check | skip_tapos_check | @@ -1435,7 +1435,7 @@ namespace graphene { namespace chain { vector> witness_time_pairs; vector previous_ids; for (const auto &b : blocks) { - witness_time_pairs.push_back(std::make_pair(b->data.witness, b->data.timestamp)); + witness_time_pairs.push_back(std::make_pair(b->data.validator, b->data.timestamp)); previous_ids.push_back(b->data.previous); } @@ -1498,7 +1498,7 @@ namespace graphene { namespace chain { share_type total_weight = 0; bool has_emergency = false; for (const auto& item : branch) { - const auto& wit_name = item->data.witness; + const auto& wit_name = item->data.validator; if (wit_name == CHAIN_EMERGENCY_WITNESS_ACCOUNT) { has_emergency = true; continue; @@ -2203,7 +2203,7 @@ namespace graphene { namespace chain { ("w", witness_owner)); } - if (!(skip & skip_witness_signature)) + if (!(skip & skip_validator_signature)) FC_ASSERT(witness_obj.signing_key == block_signing_private_key.get_public_key()); } // op_guard released here @@ -2287,7 +2287,7 @@ namespace graphene { namespace chain { pending_block.previous = head_block_id(); pending_block.timestamp = when; pending_block.transaction_merkle_root = pending_block.calculate_merkle_root(); - pending_block.witness = witness_owner; + pending_block.validator = witness_owner; const auto &witness = get_witness(witness_owner); @@ -2332,7 +2332,7 @@ namespace graphene { namespace chain { } } - if (!(skip & skip_witness_signature)) { + if (!(skip & skip_validator_signature)) { pending_block.sign(block_signing_private_key); } @@ -3780,7 +3780,7 @@ namespace graphene { namespace chain { p.current_supply += asset( digital_asset_per_block, TOKEN_SYMBOL ); }); - const auto& cwit = get_witness( props.current_witness ); + const auto& cwit = get_witness( props.current_validator ); const auto* witness_account = find_account(cwit.owner); if (!witness_account) { auto& acc_idx = get_index().indices().get(); @@ -3849,7 +3849,7 @@ namespace graphene { namespace chain { p.current_supply += asset( inflation_per_block, TOKEN_SYMBOL ); }); - const auto& cwit = get_witness( props.current_witness ); + const auto& cwit = get_witness( props.current_validator ); const auto* witness_account = find_account(cwit.owner); if (!witness_account) { auto& acc_idx = get_index().indices().get(); @@ -3876,7 +3876,7 @@ namespace graphene { namespace chain { auto committee_reward = ( inflation_per_block * CHAIN_COMMITTEE_FUND_PERCENT ) / CHAIN_100_PERCENT; auto witness_reward = inflation_per_block - content_reward - vesting_reward - committee_reward; /// Remaining 10% to witness pay - const auto& cwit = get_witness( props.current_witness ); + const auto& cwit = get_witness( props.current_validator ); inflation_per_block = content_reward + vesting_reward + committee_reward + witness_reward; /* @@ -4804,7 +4804,7 @@ namespace graphene { namespace chain { } create([&](dynamic_global_property_object &p) { - p.current_witness = CHAIN_COMMITTEE_ACCOUNT; + p.current_validator = CHAIN_COMMITTEE_ACCOUNT; p.recent_slots_filled = fc::uint128_t::max_value(); p.participation_count = 128; p.committee_fund = asset(0, TOKEN_SYMBOL); @@ -5019,7 +5019,7 @@ namespace graphene { namespace chain { itr->second, "Block did not match checkpoint", ("checkpoint", *itr)("block_id", next_block.id())); if (_checkpoints.rbegin()->first >= block_num) { - skip = skip_witness_signature + skip = skip_validator_signature | skip_transaction_signatures | skip_transaction_dupe_check | skip_fork_db @@ -5079,9 +5079,9 @@ namespace graphene { namespace chain { _current_virtual_op = 0; /// modify current witness so transaction evaluators can know who included the transaction, - /// this is mostly for POW operations which must pay the current_witness + /// this is mostly for POW operations which must pay the current_validator modify(gprops, [&](dynamic_global_property_object &dgp) { - dgp.current_witness = next_block.witness; + dgp.current_validator = next_block.validator; }); if( BOOST_UNLIKELY( next_block_num == 1 ) )//change genesis @@ -5098,10 +5098,10 @@ namespace graphene { namespace chain { /// parse witness version reporting process_header_extensions(next_block); - const auto &witness = get_witness(next_block.witness); + const auto &witness = get_witness(next_block.validator); FC_ASSERT(witness.running_version >= hardfork_state.current_hardfork_version, "Block produced by witness that is not running current hardfork", - ("witness", witness)("next_block.witness", next_block.witness)("hardfork_state", hardfork_state) + ("witness", witness)("next_block.validator", next_block.validator)("hardfork_state", hardfork_state) ); for (const auto &trx : next_block.transactions) { @@ -5165,7 +5165,7 @@ namespace graphene { namespace chain { process_hardforks(); check_block_post_validation_chain(); - create_block_post_validation(next_block_num,next_block_id,next_block.witness); + create_block_post_validation(next_block_num,next_block_id,next_block.validator); // notify observers that the block has been applied if (_debug_block_production) ilog("DEBUG_CRASH: notify_applied_block start"); @@ -5197,8 +5197,8 @@ namespace graphene { namespace chain { case 1: // version { auto reported_version = itr->get(); - const auto &signing_witness = get_witness(next_block.witness); - //idump( (next_block.witness)(signing_witness.running_version)(reported_version) ); + const auto &signing_witness = get_witness(next_block.validator); + //idump( (next_block.validator)(signing_witness.running_version)(reported_version) ); if (reported_version != signing_witness.running_version) { @@ -5211,8 +5211,8 @@ namespace graphene { namespace chain { case 2: // hardfork_version vote { auto hfv = itr->get(); - const auto &signing_witness = get_witness(next_block.witness); - //idump( (next_block.witness)(signing_witness.running_version)(hfv) ); + const auto &signing_witness = get_witness(next_block.validator); + //idump( (next_block.validator)(signing_witness.running_version)(hfv) ); if (hfv.hf_version != signing_witness.hardfork_version_vote || @@ -5311,13 +5311,13 @@ namespace graphene { namespace chain { next_block.previous, "", ("head_block_id", head_block_id())("next.prev", next_block.previous)); FC_ASSERT(head_block_time() < next_block.timestamp, "", ("head_block_time", head_block_time())("next", next_block.timestamp)("blocknum", next_block.block_num())); - const validator_object &witness = get_witness(next_block.witness); + const validator_object &witness = get_witness(next_block.validator); - if (!(skip & skip_witness_signature)) { + if (!(skip & skip_validator_signature)) { FC_ASSERT(witness.signing_key != public_key_type(), "Witness '${w}' has null signing key — cannot validate block #${n}. " "The witness disabled their key or the node is on a different fork.", - ("w", next_block.witness)("n", next_block.block_num())); + ("w", next_block.validator)("n", next_block.block_num())); FC_ASSERT(next_block.validate_signee(witness.signing_key)); } @@ -5360,13 +5360,13 @@ namespace graphene { namespace chain { ("w", witness.owner)); dlog("Emergency mode: accepting block from ${bw} at slot scheduled for ${sw} " "(slot_num=${slot}, block=#${num})", - ("bw", next_block.witness)("sw", scheduled_witness) + ("bw", next_block.validator)("sw", scheduled_witness) ("slot", slot_num)("num", next_block.block_num())); } } else { FC_ASSERT(witness.owner == scheduled_witness, "Witness produced block at wrong time", - ("block witness", next_block.witness)("scheduled", scheduled_witness)("slot_num", slot_num)); + ("block witness", next_block.validator)("scheduled", scheduled_witness)("slot_num", slot_num)); } } @@ -5407,15 +5407,15 @@ namespace graphene { namespace chain { bool is_emergency_offline_witness = has_hardfork(CHAIN_HARDFORK_12) && _dgp.emergency_consensus_active && - witness_missed.owner != b.witness && + witness_missed.owner != b.validator && witness_missed.owner != CHAIN_EMERGENCY_WITNESS_ACCOUNT; - if (!is_emergency_offline_witness && witness_missed.owner != b.witness) { + if (!is_emergency_offline_witness && witness_missed.owner != b.validator) { ilog("\033[91mMissed block: witness ${w} did not produce block #${n} at ${t} (next: ${next})\033[0m", ("w", witness_missed.owner) ("n", head_block_num() + i + 1) ("t", get_slot_time(i + 1)) - ("next", b.witness)); + ("next", b.validator)); } modify(witness_missed, [&](validator_object &w) { @@ -5426,7 +5426,7 @@ namespace graphene { namespace chain { // because committee can't fill all slots simultaneously), current_run // never reaches CHAIN_IRREVERSIBLE_SUPPORT_MIN_RUN, so // last_supported_block_num never advances and LIB stalls. - if (w.owner != b.witness) { + if (w.owner != b.validator) { w.current_run = 0; } if(is_emergency_offline_witness) { @@ -5446,8 +5446,8 @@ namespace graphene { namespace chain { w.signing_key = public_key_type(); push_virtual_operation(shutdown_validator_operation(w.owner)); } - } else if(witness_missed.owner != b.witness){ - // total_missed does not increment when witness_missed.owner == b.witness + } else if(witness_missed.owner != b.validator){ + // total_missed does not increment when witness_missed.owner == b.validator // because a low total_missed is a "prestige" item and a witness that // restarts a dead network is "rewarded" by not having total_missed // increase for any blocks they missed in the gap. diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index bb8393a216..7e1584decc 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -99,7 +99,7 @@ namespace graphene { namespace chain { enum validation_steps { skip_nothing = 0, - skip_witness_signature = 1 << 0, ///< used while reindexing + skip_validator_signature = 1 << 0, ///< used while reindexing skip_transaction_signatures = 1 << 1, ///< used by non-witness nodes skip_transaction_dupe_check = 1 << 2, ///< used while reindexing skip_fork_db = 1 << 3, ///< used while reindexing diff --git a/libraries/chain/include/graphene/chain/global_property_object.hpp b/libraries/chain/include/graphene/chain/global_property_object.hpp index 69d040617e..7ca627570b 100644 --- a/libraries/chain/include/graphene/chain/global_property_object.hpp +++ b/libraries/chain/include/graphene/chain/global_property_object.hpp @@ -38,7 +38,7 @@ namespace graphene { block_id_type head_block_id; time_point_sec genesis_time; time_point_sec time; - account_name_type current_witness; + account_name_type current_validator; asset committee_fund = asset(0, TOKEN_SYMBOL); uint32_t committee_requests = 0; @@ -164,7 +164,7 @@ FC_REFLECT((graphene::chain::dynamic_global_property_object), (head_block_id) (genesis_time) (time) - (current_witness) + (current_validator) (committee_fund) (committee_requests) (current_supply) diff --git a/libraries/network/dlt_p2p_node.cpp b/libraries/network/dlt_p2p_node.cpp index 344ab18e1c..80809153f2 100644 --- a/libraries/network/dlt_p2p_node.cpp +++ b/libraries/network/dlt_p2p_node.cpp @@ -1340,7 +1340,7 @@ void dlt_p2p_node::on_dlt_block_range_reply(peer_id peer, const dlt_block_range_ ilog(DLT_LOG_BWHITE "Got block #${n} with ${tx} transaction(s) by witness ${w} [${ep}]" DLT_LOG_RESET, ("n", block.block_num())("tx", block.transactions.size()) - ("w", block.witness)("ep", state.endpoint)); + ("w", block.validator)("ep", state.endpoint)); on_block_applied(block, /*caused_fork_switch=*/false); @@ -1567,7 +1567,7 @@ void dlt_p2p_node::on_dlt_block_reply(peer_id peer, const dlt_block_reply_messag if (result == dlt_block_accept_result::ACCEPTED) { ilog(DLT_LOG_BWHITE "Got block #${n} with ${tx} transaction(s) by witness ${w} [${ep}]" DLT_LOG_RESET, ("n", reply.block.block_num())("tx", reply.block.transactions.size()) - ("w", reply.block.witness)("ep", state.endpoint)); + ("w", reply.block.validator)("ep", state.endpoint)); _last_network_block_time = fc::time_point::now(); _last_block_received_time = fc::time_point::now(); @@ -1590,7 +1590,7 @@ void dlt_p2p_node::on_dlt_block_reply(peer_id peer, const dlt_block_reply_messag // Retransmit to our-fork peers dlog(DLT_LOG_DGRAY "Retransmitting block #${n} by ${w} to fork peers (excluding ${ep})" DLT_LOG_RESET, - ("n", reply.block.block_num())("w", reply.block.witness)("ep", state.endpoint)); + ("n", reply.block.block_num())("w", reply.block.validator)("ep", state.endpoint)); send_to_all_our_fork_peers(message(dlt_block_reply_message(reply)), peer, reply.block.id()); // P26 fix: Check if we've caught up to all peers via single-block replies @@ -1603,7 +1603,7 @@ void dlt_p2p_node::on_dlt_block_reply(peer_id peer, const dlt_block_reply_messag // Do NOT call on_block_applied (which would corrupt mempool), // do NOT retransmit (block is not on our main chain). dlog(DLT_LOG_ORANGE "Stored block #${n} by witness ${w} in fork_db (not applied, head=${h}) from ${ep}" DLT_LOG_RESET, - ("n", block_num)("w", reply.block.witness)("h", _delegate->get_head_block_num())("ep", state.endpoint)); + ("n", block_num)("w", reply.block.validator)("h", _delegate->get_head_block_num())("ep", state.endpoint)); } // Update peer's expected_next_block regardless of outcome so the @@ -1913,7 +1913,7 @@ void dlt_p2p_node::on_dlt_gap_fill_reply(peer_id peer, const dlt_gap_fill_reply& ilog(DLT_LOG_BWHITE "Got block #${n} with ${tx} transaction(s) by witness ${w} [${ep}] (gap fill)" DLT_LOG_RESET, ("n", block.block_num())("tx", block.transactions.size()) - ("w", block.witness)("ep", it->second.endpoint)); + ("w", block.validator)("ep", it->second.endpoint)); on_block_applied(block, /*caused_fork_switch=*/false); @@ -2147,7 +2147,7 @@ void dlt_p2p_node::broadcast_block(const signed_block& block) { void dlt_p2p_node::broadcast_block_post_validation( const block_id_type& block_id, const std::string& witness_account, - const signature_type& witness_signature) { + const signature_type& validator_signature) { // For now, send as fork_status message with block_id dlt_fork_status_message msg; msg.fork_status = _fork_status; @@ -2309,7 +2309,7 @@ void dlt_p2p_node::drain_paused_block_queue() { _last_network_block_time = fc::time_point::now(); ilog(DLT_LOG_BWHITE "Got queued block #${n} with ${tx} transaction(s) by witness ${w}" DLT_LOG_RESET, - ("n", block.block_num())("tx", block.transactions.size())("w", block.witness)); + ("n", block.block_num())("tx", block.transactions.size())("w", block.validator)); on_block_applied(block, /*caused_fork_switch=*/false); } diff --git a/libraries/network/include/graphene/network/dlt_p2p_node.hpp b/libraries/network/include/graphene/network/dlt_p2p_node.hpp index 21917678cf..c3f451f253 100644 --- a/libraries/network/include/graphene/network/dlt_p2p_node.hpp +++ b/libraries/network/include/graphene/network/dlt_p2p_node.hpp @@ -130,7 +130,7 @@ class dlt_p2p_node { void broadcast_block_post_validation( const block_id_type& block_id, const std::string& witness_account, - const signature_type& witness_signature); + const signature_type& validator_signature); void broadcast_transaction(const signed_transaction& trx); void broadcast_chain_status(); diff --git a/libraries/protocol/block.cpp b/libraries/protocol/block.cpp index ea33a59d7b..b5e5d31174 100644 --- a/libraries/protocol/block.cpp +++ b/libraries/protocol/block.cpp @@ -21,11 +21,11 @@ namespace graphene { } fc::ecc::public_key signed_block_header::signee() const { - return fc::ecc::public_key(witness_signature, digest(), true/*enforce canonical*/ ); + return fc::ecc::public_key(validator_signature, digest(), true/*enforce canonical*/ ); } void signed_block_header::sign(const fc::ecc::private_key &signer) { - witness_signature = signer.sign_compact(digest()); + validator_signature = signer.sign_compact(digest()); } bool signed_block_header::validate_signee(const fc::ecc::public_key &expected_signee) const { diff --git a/libraries/protocol/include/graphene/protocol/block_header.hpp b/libraries/protocol/include/graphene/protocol/block_header.hpp index 695ca095b8..0dc3e1d3a8 100644 --- a/libraries/protocol/include/graphene/protocol/block_header.hpp +++ b/libraries/protocol/include/graphene/protocol/block_header.hpp @@ -15,7 +15,7 @@ namespace graphene { } fc::time_point_sec timestamp; - string witness; + string validator; checksum_type transaction_merkle_root; block_header_extensions_type extensions; @@ -31,12 +31,12 @@ namespace graphene { bool validate_signee(const fc::ecc::public_key &expected_signee) const; - signature_type witness_signature; + signature_type validator_signature; }; } } // graphene::protocol -FC_REFLECT((graphene::protocol::block_header), (previous)(timestamp)(witness)(transaction_merkle_root)(extensions)) -FC_REFLECT_DERIVED((graphene::protocol::signed_block_header), ((graphene::protocol::block_header)), (witness_signature)) +FC_REFLECT((graphene::protocol::block_header), (previous)(timestamp)(validator)(transaction_merkle_root)(extensions)) +FC_REFLECT_DERIVED((graphene::protocol::signed_block_header), ((graphene::protocol::block_header)), (validator_signature)) diff --git a/plugins/chain/plugin.cpp b/plugins/chain/plugin.cpp index effd076d65..1d814ca4de 100644 --- a/plugins/chain/plugin.cpp +++ b/plugins/chain/plugin.cpp @@ -149,7 +149,7 @@ namespace chain { if (block.block_num() % 500 == 0) { ilog("\033[93mSyncing Blockchain --- Got block: #${n} time: ${t} producer: ${p}\033[0m", - ("t", block.timestamp)("n", block.block_num())("p", block.witness)); + ("t", block.timestamp)("n", block.block_num())("p", block.validator)); } } else { if (sync_start_logged) { diff --git a/plugins/p2p/include/graphene/plugins/p2p/p2p_plugin.hpp b/plugins/p2p/include/graphene/plugins/p2p/p2p_plugin.hpp index 2f98a590e2..4099b58c2e 100644 --- a/plugins/p2p/include/graphene/plugins/p2p/p2p_plugin.hpp +++ b/plugins/p2p/include/graphene/plugins/p2p/p2p_plugin.hpp @@ -41,7 +41,7 @@ namespace graphene { void broadcast_block_post_validation(const graphene::protocol::block_id_type block_id, const std::string &witness_account, - const graphene::protocol::signature_type &witness_signature); + const graphene::protocol::signature_type &validator_signature); // Fire-and-forget variant: posts the broadcast to the P2P // thread without blocking the caller. Safe to call from @@ -49,7 +49,7 @@ namespace graphene { // for slow peer I/O to complete. void post_broadcast_block_post_validation(const graphene::protocol::block_id_type block_id, const std::string &witness_account, - const graphene::protocol::signature_type &witness_signature); + const graphene::protocol::signature_type &validator_signature); void broadcast_transaction(const graphene::protocol::signed_transaction &tx); diff --git a/plugins/p2p/p2p_plugin.cpp b/plugins/p2p/p2p_plugin.cpp index 637699101b..5693f94d9f 100644 --- a/plugins/p2p/p2p_plugin.cpp +++ b/plugins/p2p/p2p_plugin.cpp @@ -639,22 +639,22 @@ void p2p_plugin::broadcast_block(const graphene::protocol::signed_block& block) void p2p_plugin::broadcast_block_post_validation( const graphene::protocol::block_id_type block_id, const std::string& witness_account, - const graphene::protocol::signature_type& witness_signature) { - my->p2p_thread.async([this, block_id, witness_account, witness_signature]() { - my->node->broadcast_block_post_validation(block_id, witness_account, witness_signature); + const graphene::protocol::signature_type& validator_signature) { + my->p2p_thread.async([this, block_id, witness_account, validator_signature]() { + my->node->broadcast_block_post_validation(block_id, witness_account, validator_signature); }).wait(); } void p2p_plugin::post_broadcast_block_post_validation( const graphene::protocol::block_id_type block_id, const std::string& witness_account, - const graphene::protocol::signature_type& witness_signature) { + const graphene::protocol::signature_type& validator_signature) { // Fire-and-forget: queue the broadcast on the P2P thread without // blocking the caller. The production timer thread must never // wait for slow peer socket I/O — a blocked wait would cause the // production loop to miss its slot window. - my->p2p_thread.async([this, block_id, witness_account, witness_signature]() { - my->node->broadcast_block_post_validation(block_id, witness_account, witness_signature); + my->p2p_thread.async([this, block_id, witness_account, validator_signature]() { + my->node->broadcast_block_post_validation(block_id, witness_account, validator_signature); }); } diff --git a/plugins/snapshot/plugin.cpp b/plugins/snapshot/plugin.cpp index d94a647df9..d6af101595 100644 --- a/plugins/snapshot/plugin.cpp +++ b/plugins/snapshot/plugin.cpp @@ -162,7 +162,9 @@ inline uint32_t import_dynamic_global_properties( obj.head_block_id = v["head_block_id"].as(); obj.genesis_time = v["genesis_time"].as(); obj.time = v["time"].as(); - obj.current_witness = v["current_witness"].as(); + obj.current_validator = v.get_object().contains("current_validator") + ? v["current_validator"].as() + : v["current_witness"].as(); obj.committee_fund = v["committee_fund"].as(); obj.committee_requests = v["committee_requests"].as_uint64(); obj.current_supply = v["current_supply"].as(); @@ -1724,7 +1726,7 @@ void snapshot_plugin::plugin_impl::on_applied_block(const graphene::protocol::si if (snapshot_in_progress.load(std::memory_order_relaxed)) { dlog(SNAP_LOG_YELLOW "New block #${n} (witness ${w}) received while snapshot in progress \u2014 " "block will be processed after snapshot completes" SNAP_LOG_RESET, - ("n", b.block_num())("w", b.witness)); + ("n", b.block_num())("w", b.validator)); } // Update last block received time for stalled sync detection diff --git a/plugins/witness/witness.cpp b/plugins/witness/witness.cpp index 733ba71224..61e3c8241a 100644 --- a/plugins/witness/witness.cpp +++ b/plugins/witness/witness.cpp @@ -599,9 +599,9 @@ namespace graphene { wso_sj.current_shuffled_validators[slot_idx]; bool was_our_slot = _witnesses.count(expected_witness) > 0; // True hijack only if the actual producer is NOT also one of our witnesses. - bool producer_is_ours = _witnesses.count(block.witness) > 0; + bool producer_is_ours = _witnesses.count(block.validator) > 0; - if (was_our_slot && !producer_is_ours && block.witness != expected_witness) { + if (was_our_slot && !producer_is_ours && block.validator != expected_witness) { // External witness (committee / emergency) produced at our slot. _slot_hijack_count++; _slot_hijack_height = static_cast(block_num); @@ -614,18 +614,18 @@ namespace graphene { elog("SLOT-HIJACK: block #${bn} by '${wit}' but slot was assigned " "to our witness '${exp}' (hijack #${cnt}). " "head=#${head} aslot=${aslot} num_sched=${nsched}", - ("bn", block_num)("wit", block.witness)("exp", expected_witness) + ("bn", block_num)("wit", block.validator)("exp", expected_witness) ("cnt", _slot_hijack_count) ("head", dgp_hijack.head_block_number) ("aslot", (uint64_t)dgp_hijack.current_aslot) ("nsched", nsw_sj)); } - } else if (was_our_slot && (block.witness == expected_witness || producer_is_ours)) { + } else if (was_our_slot && (block.validator == expected_witness || producer_is_ours)) { // Our witness (expected or another of ours) produced — reset hijack counter. if (_slot_hijack_count > 0) { wlog("SLOT-HIJACK-RESOLVED: our witness '${wit}' produced " "block #${bn} after ${cnt} hijacked slot(s).", - ("wit", block.witness)("bn", block_num)("cnt", _slot_hijack_count)); + ("wit", block.validator)("bn", block_num)("cnt", _slot_hijack_count)); } _slot_hijack_count = 0; } @@ -1506,7 +1506,7 @@ namespace graphene { auto current = fork_head; while (current && blocks_checked < CHAIN_MAX_WITNESSES) { - if (_witnesses.find(current->data.witness) == _witnesses.end()) { + if (_witnesses.find(current->data.validator) == _witnesses.end()) { all_ours = false; break; } @@ -1591,7 +1591,7 @@ namespace graphene { auto current = fork_head; while (current && blocks_checked < dlt_minority_threshold) { - if (_witnesses.find(current->data.witness) == _witnesses.end()) { + if (_witnesses.find(current->data.validator) == _witnesses.end()) { all_ours = false; break; } @@ -1859,7 +1859,7 @@ namespace graphene { // Normal mode: only count blocks from different witnesses // on a different parent as competing for (const auto &eb : existing_blocks) { - if (eb->data.witness != scheduled_witness && + if (eb->data.validator != scheduled_witness && eb->data.previous != db.head_block_id()) { has_competing_block = true; competing_block = eb; diff --git a/plugins/witness_guard/witness_guard.cpp b/plugins/witness_guard/witness_guard.cpp index dc1e28422d..a96c225d97 100644 --- a/plugins/witness_guard/witness_guard.cpp +++ b/plugins/witness_guard/witness_guard.cpp @@ -493,8 +493,8 @@ void witness_guard_plugin::plugin_startup() { // 0. Consecutive-block auto-disable check: // If one of our witnesses produces N consecutive blocks, disable it. - if (pimpl->_disable_threshold > 0 && pimpl->_witness_configs.count(b.witness)) { - const std::string& producer = b.witness; + if (pimpl->_disable_threshold > 0 && pimpl->_witness_configs.count(b.validator)) { + const std::string& producer = b.validator; // Reset counters for all OTHER witnesses — only the current producer's streak continues for (auto& entry : pimpl->_consecutive_blocks) { if (entry.first != producer) entry.second = 0; From 987a78ac9946c0b5c8d5b1d4f6c21558bcc28287 Mon Sep 17 00:00:00 2001 From: Anatoly Piskunov Date: Sun, 17 May 2026 16:32:05 +0400 Subject: [PATCH 10/29] update docs --- ...itness-to-validator-migration-reference.md | 43 ++++-- .qoder/docs/witness-to-validator-rename.md | 145 +++++++++++++----- 2 files changed, 135 insertions(+), 53 deletions(-) diff --git a/.qoder/docs/witness-to-validator-migration-reference.md b/.qoder/docs/witness-to-validator-migration-reference.md index 6e7266cf02..3b63f45264 100644 --- a/.qoder/docs/witness-to-validator-migration-reference.md +++ b/.qoder/docs/witness-to-validator-migration-reference.md @@ -26,7 +26,7 @@ This distinction is the most important rule for library developers. | What | Reason | |------|--------| -| Block header fields (`witness`, `witness_signature`) | Library reads from node, forwards to caller. Field name in response changes automatically after node upgrade — library just reflects whatever the node returns. No hardcoded logic. | +| Block header fields | **Done.** Node now returns `validator` / `validator_signature`. Libraries that relay raw block objects with no field-specific code need no changes. Code explicitly accessing `.witness` / `.witness_signature` on a block header must be updated to `.validator` / `.validator_signature`. | | Raw API response objects (beyond type definitions) | JSON deserialization is dynamic — field access works regardless of name if library doesn't validate field names | | Historical transaction data from node | Same — the node returns it, library relays it | @@ -38,13 +38,21 @@ This distinction is the most important rule for library developers. | Layer | Status | Visible to JS/PHP? | |-------|--------|---------------------| -| Internal C++ methods (e.g., `is_validator_scheduled_soon`) | Done | No | -| Internal enums (`block_validation_condition`) | Done | No | -| Protocol operation struct names | Not yet | Yes — JSON name in transactions | -| API method names (`get_active_witnesses`, etc.) | Not yet | Yes — JSON-RPC calls | -| Chain object types (`witness_object`, etc.) | Not yet | Yes — API response type names | -| Plugin names (`witness`, `witness_api`, `witness_guard`) | Not yet | Yes — config files | -| CLI wallet commands | Not yet | Yes — if using CLI wallet | +| Internal C++ methods (e.g., `is_validator_scheduled_soon`) | **Done** | No | +| Internal enums (`block_validation_condition`) | **Done** | No | +| Internal skip flags (`skip_validator_signature`) | **Done** | No | +| Block header fields (`validator`, `validator_signature`) | **Done** | Yes — block responses | +| Dynamic global property (`current_validator`) | **Done** | Yes — `get_dynamic_global_properties` | +| Protocol operation struct names and JSON names | **Done** | Yes — JSON name in transactions | +| Operation field names inside types 7 and 42 | **Done** | Yes — field names in operation body | +| Chain properties field names | **Done** | Yes — JSON field names in governance ops | +| API method names (`get_active_validators`, etc.) | **Done** | Yes — JSON-RPC calls | +| Chain object types (`validator_object`, etc.) | **Done** | Yes — API response type names | +| CLI wallet commands (`get_active_validators`, etc.) | **Done** | Yes — if using CLI wallet | +| Physical file renames (`.hpp`/`.cpp`) | Pending — future PR | No — internal build only | +| Plugin directory and CMake target renames | Pending — future PR | Yes — config `plugin =` keys | +| Config key renames (`--validator`, etc.) | Pending — future PR | Yes — node operator config | +| API namespace (`witness_api` → `validator_api`) | Pending — future PR | Yes — JSON-RPC `"api"` field | --- @@ -371,7 +379,7 @@ Not directly relevant to JS/PHP libraries, but included for completeness: | Signing authority level (`active`) | Operations still require active authority | | Block interval, slot scheduling, consensus rules | Unchanged | -> **Note on block header fields:** `block.witness` and `block.witness_signature` are renamed to `block.validator` and `block.validator_signature` in JSON output. This is a **simple rename with no hardfork required** — binary format does not serialize field names, only values by position. Libraries that just relay block data will reflect new names automatically after node upgrade. No binary migration or version negotiation is needed. +> **Block header fields** are now `validator` and `validator_signature` in all node responses. Binary wire format is unchanged — field names are not serialized, only values by position. Libraries relaying raw block objects reflect new names automatically; no version negotiation needed. --- @@ -434,8 +442,21 @@ Not directly relevant to JS/PHP libraries, but included for completeness: | Old Field | New Field | In Object | |-----------|-----------|-----------| -| `current_shuffled_witnesses` | `current_shuffled_validators` | schedule object | -| `num_scheduled_witnesses` | `num_scheduled_validators` | schedule object | +| `current_shuffled_witnesses` | `current_shuffled_validators` | `validator_schedule_object` | +| `num_scheduled_witnesses` | `num_scheduled_validators` | `validator_schedule_object` | + +### Block Header Fields + +| Old Field | New Field | +|-----------|-----------| +| `witness` | `validator` | +| `witness_signature` | `validator_signature` | + +### Dynamic Global Property Fields + +| Old Field | New Field | +|-----------|-----------| +| `current_witness` | `current_validator` | ### Chain Properties Fields diff --git a/.qoder/docs/witness-to-validator-rename.md b/.qoder/docs/witness-to-validator-rename.md index 7651118bca..6c75a19363 100644 --- a/.qoder/docs/witness-to-validator-rename.md +++ b/.qoder/docs/witness-to-validator-rename.md @@ -32,6 +32,50 @@ The same pattern holds across other major PoS ecosystems: --- +--- + +## 2b. Current Implementation Status (2026-05-17) + +### Done + +| Item | Notes | +|------|-------| +| Protocol operations (types 6, 7, 8, 30, 42) | Renamed; old-name alias table in `operation_util_impl.cpp` | +| Operation field names (`witness` → `validator` in types 7 and 42) | Node accepts both old and new on input | +| Chain properties fields (`inflation_validator_percent`, etc.) | Old names accepted in snapshot import | +| Chain objects (`validator_object`, `validator_schedule_object`) | C++ types renamed; files not renamed yet | +| Schedule fields (`current_shuffled_validators`, `num_scheduled_validators`) | Done | +| API object (`validator_api_object`) | Done | +| API methods (`get_active_validators`, `get_validator_by_account`, etc.) | Done | +| CLI wallet commands (`get_active_validators`, `vote_for_validator`, etc.) | Done | +| Block header fields (`validator`, `validator_signature`) | Done | +| Dynamic global property (`current_validator`) | Done | +| Internal skip flag (`skip_validator_signature`) | Done | +| P2P function signatures (`validator_signature` parameter) | Done | +| Internal plugin methods (`block_validation_loop`, `maybe_validate_block`, etc.) | Done | +| Internal enum (`block_validation_condition`) | Done | +| Snapshot backward compat | Old field names accepted on import; new names on export | +| Operation name alias table | `resolve_operation_name()` in `operation_util_impl.cpp` | + +### Deferred to Future PR + +| Item | Reason | +|------|--------| +| Physical file renames (`witness_objects.hpp` → `validator_objects.hpp`, etc.) | ~40 include sites; batched separately | +| Plugin directory renames (`plugins/witness/` → `plugins/validator/`, etc.) | Tied to file renames | +| CMake target renames | Tied to directory renames | +| Config key renames (`--validator`, `validator-guard-*`) | Tied to plugin renames | +| API namespace rename (`witness_api` → `validator_api`) | Tied to plugin rename | +| `block_post_validation_object` → `validator_confirmation_object` | Deferred with file renames | + +### Explicitly Kept (not renamed) + +- `witness_vote_object` — internal vote-tracking object; not exposed by name in protocol +- `witness_penalty_expire_object` — internal object; not exposed in protocol +- `witness_penalty_expire_object::witness` field — internal back-reference, not a block header field + +--- + ## 3. How Operations Are Serialized (Critical for Compatibility) Understanding the wire format determines what is and is not a breaking change. @@ -253,29 +297,32 @@ Even with server-side fallback, clients will receive **responses** with new name ## 8. Implementation Phases -### Phase 1 — Internal rename (zero breaking changes) - -1. Rename `block_production_condition` namespace, enum, and `exception_producing_block` in `witness.hpp` + `witness.cpp`. -2. Rename internal method names: `block_production_loop`, `maybe_produce_block`, `is_witness_scheduled_soon`. -3. Rename `block_post_validation_object` → `validator_confirmation_object` (internal chain object — not exposed by name in the protocol). -4. Rename `current_shuffled_witnesses[]` field. -5. Build and verify — all existing behavior unchanged. -6. Update all `.qoder/` documentation files. - -### Phase 2 — API and config rename (with fallbacks) - -1. Add operation name alias table in `operation_util_impl.cpp` — old JSON names → new names. -2. Rename `witness_update_operation` → `validator_update_operation` and the other four operations (Section 5.1). Binary type IDs preserved. -3. Add deprecated endpoint aliases in `witness_api_plugin` for all `get_witness_*` methods. -4. Rename CLI wallet commands; keep old names as deprecated aliases. -5. Rename config keys; add startup warnings for old keys. -6. Rename plugin directories and CMake targets. -7. Rename `witness_object`, `witness_schedule_object`, `witness_api_object`. -8. Update `config_witness.ini` template to new key names. +### Phase 1 — Internal rename (zero breaking changes) ✅ Done + +1. ✅ Rename `block_production_condition` namespace, enum, and `exception_producing_block` in `witness.hpp` + `witness.cpp`. +2. ✅ Rename internal method names: `block_production_loop`, `maybe_produce_block`, `is_witness_scheduled_soon`. +3. ⏳ Rename `block_post_validation_object` → `validator_confirmation_object` — deferred with physical file renames. +4. ✅ Rename `current_shuffled_witnesses[]` field. +5. ✅ Build verified. +6. ✅ `.qoder/` documentation updated. + +### Phase 2 — API and config rename (with fallbacks) — Mostly Done + +1. ✅ Add operation name alias table in `operation_util_impl.cpp` — old JSON names → new names. +2. ✅ Rename `witness_update_operation` → `validator_update_operation` and the other four operations (Section 5.1). Binary type IDs preserved. +3. ✅ Add deprecated endpoint aliases in `witness_api_plugin` for all `get_witness_*` methods. +4. ✅ Rename CLI wallet commands; keep old names as deprecated aliases. +5. ⏳ Rename config keys; add startup warnings for old keys — deferred with plugin directory renames. +6. ⏳ Rename plugin directories and CMake targets — future PR. +7. ✅ Rename `witness_object`, `witness_schedule_object`, `witness_api_object` (C++ types; files not renamed yet). +8. ⏳ Update `config_witness.ini` template to new key names — deferred. +9. ✅ Rename block header fields: `validator`, `validator_signature`. +10. ✅ Rename dynamic global property field: `current_validator`. +11. ✅ Rename skip flag: `skip_validator_signature`. ### Phase 3 — External library updates -1. Update JS client library: operation name constants, API method names, response parsing. +1. Update JS client library: operation name constants, API method names, response parsing, block header field names. 2. Update PHP client library: same scope. 3. After both libraries are released, schedule removal of the server-side fallback aliases. @@ -283,28 +330,42 @@ Even with server-side fallback, clients will receive **responses** with new name ## 9. Files Affected (Source Code) -| File | Phase | Scope | -|------|-------|-------| -| `plugins/witness/include/graphene/plugins/witness/witness.hpp` | 1 | Enum namespace, method declarations | -| `plugins/witness/witness.cpp` | 1 | All enum references, method definitions | -| `plugins/witness_guard/witness_guard.cpp` | 2 | Config keys, class names | -| `plugins/witness_guard/include/.../witness_guard.hpp` | 2 | Class names, config declarations | -| `plugins/witness_api/plugin.cpp` | 2 | API method names + deprecated aliases | -| `plugins/witness_api/include/.../plugin.hpp` | 2 | API method declarations | -| `libraries/chain/include/graphene/chain/witness_objects.hpp` | 2 | Object type names, field names | -| `libraries/chain/include/graphene/chain/chain_objects.hpp` | 1 | `block_post_validation_object` rename | -| `libraries/chain/database.cpp` | 1+2 | All object references | -| `libraries/chain/database.hpp` | 1+2 | Method signatures | -| `libraries/protocol/include/graphene/protocol/chain_operations.hpp` | 2 | Operation struct names | -| `libraries/protocol/include/graphene/protocol/chain_virtual_operations.hpp` | 2 | Virtual operation struct names | -| `libraries/protocol/include/graphene/protocol/operations.hpp` | 2 | static_variant list — struct names only, order unchanged | -| `libraries/protocol/operation_util_impl.cpp` | 2 | Add alias table for old JSON names | -| `libraries/api/include/graphene/api/witness_api_object.hpp` | 2 | Rename `witness_api_object` | -| `libraries/wallet/wallet.cpp` | 2 | CLI wallet command implementations | -| `libraries/wallet/include/graphene/wallet/wallet.hpp` | 2 | CLI wallet method declarations | -| `libraries/wallet/include/graphene/wallet/remote_node_api.hpp` | 2 | Remote API method names | -| `share/vizd/config/config_witness.ini` | 2 | Config key names | -| `share/vizd/config/config.ini` | 2 | Plugin names | +| File | Status | Scope | +|------|--------|-------| +| `plugins/witness/include/graphene/plugins/witness/witness.hpp` | ✅ Done | Enum namespace, method declarations | +| `plugins/witness/witness.cpp` | ✅ Done | All enum references, method definitions, block field accesses | +| `plugins/witness_guard/witness_guard.cpp` | ✅ Done | Object types, block field accesses | +| `plugins/witness_guard/include/.../witness_guard.hpp` | ✅ Done | Class names, config declarations | +| `plugins/witness_api/plugin.cpp` | ✅ Done | API method names + deprecated aliases | +| `plugins/witness_api/include/.../plugin.hpp` | ✅ Done | API method declarations | +| `plugins/p2p/p2p_plugin.cpp` | ✅ Done | `validator_signature` parameter | +| `plugins/p2p/include/.../p2p_plugin.hpp` | ✅ Done | `validator_signature` parameter | +| `plugins/chain/plugin.cpp` | ✅ Done | Block field access | +| `plugins/snapshot/plugin.cpp` | ✅ Done | Object types, field accesses, backward compat import | +| `plugins/database_api/api.cpp` | ✅ Done | Object type references | +| `plugins/account_history/plugin.cpp` | ✅ Done | Operation visitor method names | +| `libraries/chain/include/graphene/chain/witness_objects.hpp` | ✅ Done | Object type names, field names (file not renamed yet) | +| `libraries/chain/include/graphene/chain/global_property_object.hpp` | ✅ Done | `current_validator` field + FC_REFLECT | +| `libraries/chain/include/graphene/chain/chain_objects.hpp` | ⏳ Deferred | `block_post_validation_object` rename | +| `libraries/chain/database.cpp` | ✅ Done | All object and block field references | +| `libraries/chain/database.hpp` | ✅ Done | `skip_validator_signature` flag | +| `libraries/protocol/include/graphene/protocol/block_header.hpp` | ✅ Done | `validator`, `validator_signature` fields | +| `libraries/protocol/block.cpp` | ✅ Done | `validator_signature` references | +| `libraries/protocol/include/graphene/protocol/chain_operations.hpp` | ✅ Done | Operation struct names, field names | +| `libraries/protocol/include/graphene/protocol/chain_virtual_operations.hpp` | ✅ Done | Virtual operation struct names | +| `libraries/protocol/include/graphene/protocol/operations.hpp` | ✅ Done | static_variant list — struct names only, order unchanged | +| `libraries/protocol/operation_util_impl.cpp` | ✅ Done | Alias table for old JSON names | +| `libraries/api/include/graphene/api/witness_api_object.hpp` | ✅ Done | `validator_api_object` type | +| `libraries/api/witness_api_object.cpp` | ✅ Done | Constructor, field assignments | +| `libraries/api/include/graphene/api/chain_api_properties.hpp` | ✅ Done | Chain properties field names | +| `libraries/api/chain_api_properties.cpp` | ✅ Done | Field assignments | +| `libraries/network/dlt_p2p_node.cpp` | ✅ Done | Block field accesses, `validator_signature` parameter | +| `libraries/network/include/graphene/network/dlt_p2p_node.hpp` | ✅ Done | `validator_signature` parameter | +| `libraries/wallet/wallet.cpp` | ✅ Done | CLI wallet command implementations | +| `libraries/wallet/include/graphene/wallet/wallet.hpp` | ✅ Done | CLI wallet method declarations | +| `libraries/wallet/include/graphene/wallet/remote_node_api.hpp` | ✅ Done | Remote API method names | +| `share/vizd/config/config_witness.ini` | ⏳ Deferred | Config key names | +| `share/vizd/config/config.ini` | ⏳ Deferred | Plugin names | --- From fa74a859b2d2ec34520be7600a21bb0dc37c333f Mon Sep 17 00:00:00 2001 From: Anatoly Piskunov Date: Sun, 17 May 2026 16:58:20 +0400 Subject: [PATCH 11/29] =?UTF-8?q?rename(file):=20witness=20=E2=86=92=20val?= =?UTF-8?q?idator=20physical=20renames?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename all remaining witness-named files, directories, namespaces, plugin names and chain objects to validator equivalents. File renames (git mv): - libraries/chain/include/graphene/chain/witness_objects.hpp → validator_objects.hpp - libraries/api/include/graphene/api/witness_api_object.{hpp,cpp} → validator_api_object.{hpp,cpp} - plugins/witness/ → plugins/validator/ witness.{hpp,cpp} → validator.{hpp,cpp} - plugins/witness_api/ → plugins/validator_api/ - plugins/witness_guard/ → plugins/validator_guard/ witness_guard.{hpp,cpp} → validator_guard.{hpp,cpp} C++ namespace/class renames: - namespace witness_plugin → validator_plugin - class witness_plugin → validator_plugin - namespace witness_api → validator_api - namespace witness_guard → validator_guard - class witness_guard_plugin → validator_guard_plugin - plugin_name: "witness" → "validator", "witness_api" → "validator_api", "witness_guard" → "validator_guard" - struct remote_witness_api → remote_validator_api (wallet) Chain object renames: - block_post_validation_object → validator_confirmation_object - block_post_validation_index → validator_confirmation_index - get_block_post_validations() → get_validator_confirmations() Snapshot JSON key "block_post_validation" kept for backward compat. CMake targets: - graphene_witness → graphene_validator - graphene_witness_api → graphene_validator_api - graphene_witness_guard → graphene_validator_guard Config files (all config*.ini): - plugin = witness → plugin = validator - plugin = witness_api → plugin = validator_api - plugin = witness_guard → plugin = validator_guard --witness kept as deprecated backward-compat alias for --validator. All include paths, FC_REFLECT macros, CMakeLists.txt updated accordingly. --- ...itness-to-validator-migration-reference.md | 25 +++-- .qoder/docs/witness-to-validator-rename.md | 40 ++++---- libraries/api/CMakeLists.txt | 4 +- .../graphene/api/chain_api_properties.hpp | 2 +- ...pi_object.hpp => validator_api_object.hpp} | 2 +- ...pi_object.cpp => validator_api_object.cpp} | 2 +- libraries/chain/CMakeLists.txt | 4 +- libraries/chain/database.cpp | 26 +++--- .../include/graphene/chain/account_object.hpp | 2 +- .../graphene/chain/chain_object_types.hpp | 8 +- .../include/graphene/chain/chain_objects.hpp | 20 ++-- .../graphene/chain/committee_objects.hpp | 2 +- .../include/graphene/chain/content_object.hpp | 2 +- .../chain/include/graphene/chain/database.hpp | 2 +- .../include/graphene/chain/invite_objects.hpp | 2 +- .../chain/paid_subscription_objects.hpp | 2 +- ...ness_objects.hpp => validator_objects.hpp} | 0 libraries/wallet/CMakeLists.txt | 2 +- .../graphene/wallet/remote_node_api.hpp | 14 +-- .../wallet/include/graphene/wallet/wallet.hpp | 2 +- libraries/wallet/wallet.cpp | 18 ++-- plugins/p2p/CMakeLists.txt | 2 +- plugins/p2p/p2p_plugin.cpp | 6 +- plugins/snapshot/CMakeLists.txt | 2 +- .../plugins/snapshot/snapshot_serializer.hpp | 2 +- plugins/snapshot/plugin.cpp | 18 ++-- plugins/{witness => validator}/CMakeLists.txt | 7 +- .../graphene/plugins/validator/validator.hpp} | 14 +-- .../witness.cpp => validator/validator.cpp} | 54 +++++------ .../CMakeLists.txt | 8 +- .../api_objects/feed_history_api_object.hpp | 2 +- .../api_objects/validator_api_object.hpp} | 8 +- .../plugins/validator_api}/plugin.hpp | 14 +-- .../{witness_api => validator_api}/plugin.cpp | 34 +++---- .../CMakeLists.txt | 8 +- .../validator_guard/validator_guard.hpp} | 14 +-- .../validator_guard.cpp} | 92 +++++++++---------- programs/cli_wallet/CMakeLists.txt | 2 +- programs/vizd/CMakeLists.txt | 6 +- programs/vizd/main.cpp | 12 +-- share/vizd/config/config.ini | 6 +- share/vizd/config/config_debug.ini | 4 +- share/vizd/config/config_debug_mongo.ini | 4 +- share/vizd/config/config_mongo.ini | 4 +- share/vizd/config/config_stock_exchange.ini | 2 +- share/vizd/config/config_testnet.ini | 4 +- share/vizd/config/config_witness.ini | 10 +- 47 files changed, 268 insertions(+), 252 deletions(-) rename libraries/api/include/graphene/api/{witness_api_object.hpp => validator_api_object.hpp} (97%) rename libraries/api/{witness_api_object.cpp => validator_api_object.cpp} (95%) rename libraries/chain/include/graphene/chain/{witness_objects.hpp => validator_objects.hpp} (100%) rename plugins/{witness => validator}/CMakeLists.txt (91%) rename plugins/{witness/include/graphene/plugins/witness/witness.hpp => validator/include/graphene/plugins/validator/validator.hpp} (90%) rename plugins/{witness/witness.cpp => validator/validator.cpp} (98%) rename plugins/{witness_api => validator_api}/CMakeLists.txt (84%) rename plugins/{witness_api/include/graphene/plugins/witness_api => validator_api/include/graphene/plugins/validator_api}/api_objects/feed_history_api_object.hpp (71%) rename plugins/{witness_api/include/graphene/plugins/witness_api/api_objects/witness_api_object.hpp => validator_api/include/graphene/plugins/validator_api/api_objects/validator_api_object.hpp} (91%) rename plugins/{witness_api/include/graphene/plugins/witness_api => validator_api/include/graphene/plugins/validator_api}/plugin.hpp (91%) rename plugins/{witness_api => validator_api}/plugin.cpp (86%) rename plugins/{witness_guard => validator_guard}/CMakeLists.txt (88%) rename plugins/{witness_guard/include/graphene/plugins/witness_guard/witness_guard.hpp => validator_guard/include/graphene/plugins/validator_guard/validator_guard.hpp} (76%) rename plugins/{witness_guard/witness_guard.cpp => validator_guard/validator_guard.cpp} (87%) diff --git a/.qoder/docs/witness-to-validator-migration-reference.md b/.qoder/docs/witness-to-validator-migration-reference.md index 3b63f45264..645e8a9984 100644 --- a/.qoder/docs/witness-to-validator-migration-reference.md +++ b/.qoder/docs/witness-to-validator-migration-reference.md @@ -49,10 +49,10 @@ This distinction is the most important rule for library developers. | API method names (`get_active_validators`, etc.) | **Done** | Yes — JSON-RPC calls | | Chain object types (`validator_object`, etc.) | **Done** | Yes — API response type names | | CLI wallet commands (`get_active_validators`, etc.) | **Done** | Yes — if using CLI wallet | -| Physical file renames (`.hpp`/`.cpp`) | Pending — future PR | No — internal build only | -| Plugin directory and CMake target renames | Pending — future PR | Yes — config `plugin =` keys | -| Config key renames (`--validator`, etc.) | Pending — future PR | Yes — node operator config | -| API namespace (`witness_api` → `validator_api`) | Pending — future PR | Yes — JSON-RPC `"api"` field | +| Physical file renames (`.hpp`/`.cpp`, directories) | **Done** | No — internal build only | +| Plugin directory and CMake target renames | **Done** | No — internal build | +| Config key renames (`plugin = validator`, etc.) | **Done** | Yes — node operators must update `config.ini` | +| API namespace (`validator_api`) | **Done** | Yes — JSON-RPC `"api"` field (see Section 2) | --- @@ -172,13 +172,24 @@ const OP_TYPE_ID = [ ### API Namespace -The JSON-RPC namespace (`"api"` field) is currently **`"witness_api"`** and will be renamed to **`"validator_api"`** when the plugin directory is renamed. Until then, all calls — including new `get_validator_*` methods — must use `"witness_api"`: +**Done.** The JSON-RPC namespace is now **`"validator_api"`**. Old clients still using `"witness_api"` will fail — they must update: ```json -{ "api": "witness_api", "method": "get_active_validators", "params": [] } +{ "api": "validator_api", "method": "get_active_validators", "params": [] } ``` -There is no `"validator_api"` alias at this time. The namespace change will require a fallback strategy identical to the method-name fallback (try `validator_api`, fall back to `witness_api`). +Implementation pattern for dual support during library migration: + +```js +async function callApi(method, params) { + try { + return await rpc({ api: 'validator_api', method, params }); + } catch (e) { + // Fallback for old nodes not yet upgraded + return await rpc({ api: 'witness_api', method, params }); + } +} +``` ### Methods to Rename diff --git a/.qoder/docs/witness-to-validator-rename.md b/.qoder/docs/witness-to-validator-rename.md index 6c75a19363..fa853ed92c 100644 --- a/.qoder/docs/witness-to-validator-rename.md +++ b/.qoder/docs/witness-to-validator-rename.md @@ -59,14 +59,7 @@ The same pattern holds across other major PoS ecosystems: ### Deferred to Future PR -| Item | Reason | -|------|--------| -| Physical file renames (`witness_objects.hpp` → `validator_objects.hpp`, etc.) | ~40 include sites; batched separately | -| Plugin directory renames (`plugins/witness/` → `plugins/validator/`, etc.) | Tied to file renames | -| CMake target renames | Tied to directory renames | -| Config key renames (`--validator`, `validator-guard-*`) | Tied to plugin renames | -| API namespace rename (`witness_api` → `validator_api`) | Tied to plugin rename | -| `block_post_validation_object` → `validator_confirmation_object` | Deferred with file renames | +Nothing remaining. All renames complete. ### Explicitly Kept (not renamed) @@ -306,19 +299,20 @@ Even with server-side fallback, clients will receive **responses** with new name 5. ✅ Build verified. 6. ✅ `.qoder/` documentation updated. -### Phase 2 — API and config rename (with fallbacks) — Mostly Done +### Phase 2 — API and config rename (with fallbacks) ✅ Done 1. ✅ Add operation name alias table in `operation_util_impl.cpp` — old JSON names → new names. 2. ✅ Rename `witness_update_operation` → `validator_update_operation` and the other four operations (Section 5.1). Binary type IDs preserved. -3. ✅ Add deprecated endpoint aliases in `witness_api_plugin` for all `get_witness_*` methods. +3. ✅ Add deprecated endpoint aliases in `validator_api` plugin for all `get_witness_*` methods. 4. ✅ Rename CLI wallet commands; keep old names as deprecated aliases. -5. ⏳ Rename config keys; add startup warnings for old keys — deferred with plugin directory renames. -6. ⏳ Rename plugin directories and CMake targets — future PR. -7. ✅ Rename `witness_object`, `witness_schedule_object`, `witness_api_object` (C++ types; files not renamed yet). -8. ⏳ Update `config_witness.ini` template to new key names — deferred. +5. ✅ Config keys updated (`plugin = validator`, `plugin = validator_api`, `plugin = validator_guard`). `--witness` kept as deprecated alias for `--validator` in config.ini backward compat. +6. ✅ Plugin directories renamed: `plugins/validator/`, `plugins/validator_api/`, `plugins/validator_guard/`. CMake targets updated. +7. ✅ Rename `witness_object`, `witness_schedule_object`, `witness_api_object` (C++ types and files). +8. ✅ `config_witness.ini` and all other `config*.ini` updated to new plugin names. 9. ✅ Rename block header fields: `validator`, `validator_signature`. 10. ✅ Rename dynamic global property field: `current_validator`. 11. ✅ Rename skip flag: `skip_validator_signature`. +12. ✅ Plugin namespaces: `validator_plugin`, `validator_api`, `validator_guard`. `plugin_name` strings updated. ### Phase 3 — External library updates @@ -346,7 +340,7 @@ Even with server-side fallback, clients will receive **responses** with new name | `plugins/account_history/plugin.cpp` | ✅ Done | Operation visitor method names | | `libraries/chain/include/graphene/chain/witness_objects.hpp` | ✅ Done | Object type names, field names (file not renamed yet) | | `libraries/chain/include/graphene/chain/global_property_object.hpp` | ✅ Done | `current_validator` field + FC_REFLECT | -| `libraries/chain/include/graphene/chain/chain_objects.hpp` | ⏳ Deferred | `block_post_validation_object` rename | +| `libraries/chain/include/graphene/chain/chain_objects.hpp` | ✅ Done | `validator_confirmation_object`, `validator_confirmation_index` | | `libraries/chain/database.cpp` | ✅ Done | All object and block field references | | `libraries/chain/database.hpp` | ✅ Done | `skip_validator_signature` flag | | `libraries/protocol/include/graphene/protocol/block_header.hpp` | ✅ Done | `validator`, `validator_signature` fields | @@ -361,11 +355,23 @@ Even with server-side fallback, clients will receive **responses** with new name | `libraries/api/chain_api_properties.cpp` | ✅ Done | Field assignments | | `libraries/network/dlt_p2p_node.cpp` | ✅ Done | Block field accesses, `validator_signature` parameter | | `libraries/network/include/graphene/network/dlt_p2p_node.hpp` | ✅ Done | `validator_signature` parameter | +| `plugins/validator/CMakeLists.txt` | ✅ Done | Target `graphene_validator`, new source/header paths | +| `plugins/validator_api/CMakeLists.txt` | ✅ Done | Target `graphene_validator_api` | +| `plugins/validator_guard/CMakeLists.txt` | ✅ Done | Target `graphene_validator_guard` | +| `plugins/p2p/CMakeLists.txt` | ✅ Done | Include path `../validator/include` | +| `plugins/snapshot/CMakeLists.txt` | ✅ Done | Link `graphene_validator` | +| `programs/vizd/CMakeLists.txt` | ✅ Done | Links `graphene::validator`, `graphene::validator_api`, `graphene::validator_guard` | +| `programs/cli_wallet/CMakeLists.txt` | ✅ Done | Link `graphene::validator_api` | | `libraries/wallet/wallet.cpp` | ✅ Done | CLI wallet command implementations | | `libraries/wallet/include/graphene/wallet/wallet.hpp` | ✅ Done | CLI wallet method declarations | | `libraries/wallet/include/graphene/wallet/remote_node_api.hpp` | ✅ Done | Remote API method names | -| `share/vizd/config/config_witness.ini` | ⏳ Deferred | Config key names | -| `share/vizd/config/config.ini` | ⏳ Deferred | Plugin names | +| `share/vizd/config/config_witness.ini` | ✅ Done | Plugin names → `validator`, `validator_api`, `validator_guard` | +| `share/vizd/config/config.ini` | ✅ Done | Plugin names | +| `share/vizd/config/config_debug.ini` | ✅ Done | Plugin names | +| `share/vizd/config/config_debug_mongo.ini` | ✅ Done | Plugin names | +| `share/vizd/config/config_mongo.ini` | ✅ Done | Plugin names | +| `share/vizd/config/config_stock_exchange.ini` | ✅ Done | Plugin names | +| `share/vizd/config/config_testnet.ini` | ✅ Done | Plugin names | --- diff --git a/libraries/api/CMakeLists.txt b/libraries/api/CMakeLists.txt index 07d49dd4c2..5c62ef3c91 100644 --- a/libraries/api/CMakeLists.txt +++ b/libraries/api/CMakeLists.txt @@ -4,7 +4,7 @@ list(APPEND CURRENT_TARGET_HEADERS include/graphene/api/account_api_object.hpp include/graphene/api/content_api_object.hpp include/graphene/api/chain_api_properties.hpp - include/graphene/api/witness_api_object.hpp + include/graphene/api/validator_api_object.hpp include/graphene/api/discussion.hpp include/graphene/api/vote_state.hpp include/graphene/api/account_vote.hpp @@ -19,7 +19,7 @@ list(APPEND CURRENT_TARGET_SOURCES discussion_helper.cpp content_api_object.cpp chain_api_properties.cpp - witness_api_object.cpp + validator_api_object.cpp committee_api_object.cpp invite_api_object.cpp paid_subscription_api_object.cpp diff --git a/libraries/api/include/graphene/api/chain_api_properties.hpp b/libraries/api/include/graphene/api/chain_api_properties.hpp index 7efe4286ac..a7d3acc924 100644 --- a/libraries/api/include/graphene/api/chain_api_properties.hpp +++ b/libraries/api/include/graphene/api/chain_api_properties.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include namespace graphene { namespace api { diff --git a/libraries/api/include/graphene/api/witness_api_object.hpp b/libraries/api/include/graphene/api/validator_api_object.hpp similarity index 97% rename from libraries/api/include/graphene/api/witness_api_object.hpp rename to libraries/api/include/graphene/api/validator_api_object.hpp index 990cb64fdc..c0cfeab75e 100644 --- a/libraries/api/include/graphene/api/witness_api_object.hpp +++ b/libraries/api/include/graphene/api/validator_api_object.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include diff --git a/libraries/api/witness_api_object.cpp b/libraries/api/validator_api_object.cpp similarity index 95% rename from libraries/api/witness_api_object.cpp rename to libraries/api/validator_api_object.cpp index bb441e5222..814148e31b 100644 --- a/libraries/api/witness_api_object.cpp +++ b/libraries/api/validator_api_object.cpp @@ -1,4 +1,4 @@ -#include +#include namespace graphene { namespace api { validator_api_object::validator_api_object(const validator_object &w, const database& db) diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 6b2d60fdec..46c541ea44 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -62,7 +62,7 @@ if(BUILD_SHARED_LIBRARIES) include/graphene/chain/chain_object_types.hpp include/graphene/chain/chain_objects.hpp include/graphene/chain/transaction_object.hpp - include/graphene/chain/witness_objects.hpp + include/graphene/chain/validator_objects.hpp include/graphene/chain/committee_objects.hpp include/graphene/chain/paid_subscription_objects.hpp @@ -117,7 +117,7 @@ else() include/graphene/chain/chain_object_types.hpp include/graphene/chain/chain_objects.hpp include/graphene/chain/transaction_object.hpp - include/graphene/chain/witness_objects.hpp + include/graphene/chain/validator_objects.hpp include/graphene/chain/committee_objects.hpp include/graphene/chain/invite_objects.hpp include/graphene/chain/paid_subscription_objects.hpp diff --git a/libraries/chain/database.cpp b/libraries/chain/database.cpp index cb318f0895..b0c0597ad4 100644 --- a/libraries/chain/database.cpp +++ b/libraries/chain/database.cpp @@ -4546,7 +4546,7 @@ namespace graphene { namespace chain { add_core_index(*this); add_core_index(*this); add_core_index(*this); - add_core_index(*this); + add_core_index(*this); _plugin_index_signal(); } @@ -5708,7 +5708,7 @@ namespace graphene { namespace chain { return; } - const auto& validation_list = get_index().indices().get(); + const auto& validation_list = get_index().indices().get(); if(!validation_list.empty()){ const dynamic_global_property_object &dpo = get_dynamic_global_properties(); const validator_schedule_object &wso = get_validator_schedule_object(); @@ -5903,7 +5903,7 @@ namespace graphene { namespace chain { return; } - const auto& validation_list = get_index().indices().get(); + const auto& validation_list = get_index().indices().get(); if(!validation_list.empty()){ auto itr = validation_list.begin(); bool find = false; @@ -5915,7 +5915,7 @@ namespace graphene { namespace chain { const auto& current = *itr; if(current.block_id == block_id){ find_block_num = current.block_num; - modify(current, [&](block_post_validation_object &o) { + modify(current, [&](validator_confirmation_object &o) { //remove witness from shuffled witnesses for (size_t j = 0; j< CHAIN_MAX_WITNESSES; j++) { if(o.current_shuffled_witnesses[j] == witness_account){ @@ -6081,10 +6081,10 @@ namespace graphene { namespace chain { } //get block post validation objects for witness - //return array of block_post_validation_object event if it is empty - std::array database::get_block_post_validations(const account_name_type &witness_account){ - std::array result; - const auto& validation_list = get_index().indices().get(); + //return array of validator_confirmation_object event if it is empty + std::array database::get_validator_confirmations(const account_name_type &witness_account){ + std::array result; + const auto& validation_list = get_index().indices().get(); auto itr = validation_list.begin(); size_t i = 0; while(itr != validation_list.end() && i < CHAIN_MAX_BLOCK_POST_VALIDATION_COUNT) @@ -6097,7 +6097,7 @@ namespace graphene { namespace chain { for (size_t j = 0; j < CHAIN_MAX_WITNESSES && !matched; j++) { if(current.current_shuffled_witnesses[j] == witness_account){ if(current.current_shuffled_witnesses_validations[j] == false){//need validate - result[i] = block_post_validation_object(current); + result[i] = validator_confirmation_object(current); ++i; matched = true; } @@ -6106,7 +6106,7 @@ namespace graphene { namespace chain { } //fill result with empty objects to return array with fixed size for(; i < CHAIN_MAX_BLOCK_POST_VALIDATION_COUNT; i++){ - result[i] = block_post_validation_object(); + result[i] = validator_confirmation_object(); } return result; } @@ -6117,7 +6117,7 @@ namespace graphene { namespace chain { void database::create_block_post_validation(uint32_t block_num, block_id_type block_id, const account_name_type& witness_account){ const dynamic_global_property_object &dpo = get_dynamic_global_properties(); //remove blocks if they height is less than last irreversible block - const auto& validation_list = get_index().indices().get(); + const auto& validation_list = get_index().indices().get(); auto itr1 = validation_list.begin(); while(itr1 != validation_list.end()) { @@ -6128,7 +6128,7 @@ namespace graphene { namespace chain { } } //remove old blocks from post validation list if it is full - const auto& validation_list2 = get_index().indices().get(); + const auto& validation_list2 = get_index().indices().get(); size_t max_block_post_validation_size = 0; auto first=validation_list2.begin(); for (auto itr = validation_list2.begin(); @@ -6142,7 +6142,7 @@ namespace graphene { namespace chain { } } //create new block in post validation list - create([&](block_post_validation_object& o) { + create([&](validator_confirmation_object& o) { o.block_num = block_num; o.block_id = block_id_type(block_id); const validator_schedule_object &wso = get_validator_schedule_object(); diff --git a/libraries/chain/include/graphene/chain/account_object.hpp b/libraries/chain/include/graphene/chain/account_object.hpp index a8acb194e2..dcdf2cef16 100644 --- a/libraries/chain/include/graphene/chain/account_object.hpp +++ b/libraries/chain/include/graphene/chain/account_object.hpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include #include diff --git a/libraries/chain/include/graphene/chain/chain_object_types.hpp b/libraries/chain/include/graphene/chain/chain_object_types.hpp index 4823cec9f9..a4393c879b 100644 --- a/libraries/chain/include/graphene/chain/chain_object_types.hpp +++ b/libraries/chain/include/graphene/chain/chain_object_types.hpp @@ -73,7 +73,7 @@ namespace graphene { namespace chain { paid_subscription_object_type, paid_subscribe_object_type, witness_penalty_expire_object_type, - block_post_validation_object_type + validator_confirmation_object_type }; class dynamic_global_property_object; @@ -108,7 +108,7 @@ namespace graphene { namespace chain { class paid_subscription_object; class paid_subscribe_object; class witness_penalty_expire_object; - class block_post_validation_object; + class validator_confirmation_object; typedef object_id dynamic_global_property_id_type; typedef object_id account_id_type; @@ -141,7 +141,7 @@ namespace graphene { namespace chain { typedef object_id paid_subscription_object_id_type; typedef object_id paid_subscribe_object_id_type; typedef object_id witness_penalty_expire_object_id_type; - typedef object_id block_post_validation_object_id_type; + typedef object_id validator_confirmation_object_id_type; } } //graphene::chain @@ -238,7 +238,7 @@ FC_REFLECT_ENUM(graphene::chain::object_type, (paid_subscription_object_type) (paid_subscribe_object_type) (witness_penalty_expire_object_type) - (block_post_validation_object_type) + (validator_confirmation_object_type) ) FC_REFLECT_TYPENAME((graphene::chain::shared_string)) diff --git a/libraries/chain/include/graphene/chain/chain_objects.hpp b/libraries/chain/include/graphene/chain/chain_objects.hpp index c62cf72e21..4801507942 100755 --- a/libraries/chain/include/graphene/chain/chain_objects.hpp +++ b/libraries/chain/include/graphene/chain/chain_objects.hpp @@ -171,15 +171,15 @@ namespace graphene { > award_shares_expire_index; - class block_post_validation_object - : public object { + class validator_confirmation_object + : public object { public: template - block_post_validation_object(Constructor &&c, allocator a) { + validator_confirmation_object(Constructor &&c, allocator a) { c(*this); } - block_post_validation_object() { + validator_confirmation_object() { } id_type id; @@ -190,15 +190,15 @@ namespace graphene { fc::array current_shuffled_witnesses_validations; }; typedef multi_index_container < - block_post_validation_object, + validator_confirmation_object, indexed_by< ordered_unique, - member + member > >, - allocator + allocator > - block_post_validation_index; + validator_confirmation_index; } } // graphene::chain @@ -220,6 +220,6 @@ FC_REFLECT((graphene::chain::award_shares_expire_object), (id)(expires)(rshares)) CHAINBASE_SET_INDEX_TYPE(graphene::chain::award_shares_expire_object, graphene::chain::award_shares_expire_index) -FC_REFLECT((graphene::chain::block_post_validation_object), +FC_REFLECT((graphene::chain::validator_confirmation_object), (id)(block_num)(block_id)(current_shuffled_witnesses)(current_shuffled_witnesses_validations)) -CHAINBASE_SET_INDEX_TYPE(graphene::chain::block_post_validation_object, graphene::chain::block_post_validation_index) +CHAINBASE_SET_INDEX_TYPE(graphene::chain::validator_confirmation_object, graphene::chain::validator_confirmation_index) diff --git a/libraries/chain/include/graphene/chain/committee_objects.hpp b/libraries/chain/include/graphene/chain/committee_objects.hpp index 683a2d6f14..1b76f79218 100644 --- a/libraries/chain/include/graphene/chain/committee_objects.hpp +++ b/libraries/chain/include/graphene/chain/committee_objects.hpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include #include diff --git a/libraries/chain/include/graphene/chain/content_object.hpp b/libraries/chain/include/graphene/chain/content_object.hpp index 7687964a11..7c87232f30 100644 --- a/libraries/chain/include/graphene/chain/content_object.hpp +++ b/libraries/chain/include/graphene/chain/content_object.hpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 7e1584decc..b8d5549b8e 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -564,7 +564,7 @@ namespace graphene { namespace chain { void create_block_post_validation(uint32_t block_num, block_id_type block_id, const account_name_type &witness_account); - std::array get_block_post_validations(const account_name_type &witness_account); + std::array get_validator_confirmations(const account_name_type &witness_account); void apply_block_post_validation(block_id_type block_id, const account_name_type &witness_account); diff --git a/libraries/chain/include/graphene/chain/invite_objects.hpp b/libraries/chain/include/graphene/chain/invite_objects.hpp index 1c2817edba..848ceabf46 100644 --- a/libraries/chain/include/graphene/chain/invite_objects.hpp +++ b/libraries/chain/include/graphene/chain/invite_objects.hpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include diff --git a/libraries/chain/include/graphene/chain/paid_subscription_objects.hpp b/libraries/chain/include/graphene/chain/paid_subscription_objects.hpp index e8b9bdf8b0..bef2354657 100644 --- a/libraries/chain/include/graphene/chain/paid_subscription_objects.hpp +++ b/libraries/chain/include/graphene/chain/paid_subscription_objects.hpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include #include diff --git a/libraries/chain/include/graphene/chain/witness_objects.hpp b/libraries/chain/include/graphene/chain/validator_objects.hpp similarity index 100% rename from libraries/chain/include/graphene/chain/witness_objects.hpp rename to libraries/chain/include/graphene/chain/validator_objects.hpp diff --git a/libraries/wallet/CMakeLists.txt b/libraries/wallet/CMakeLists.txt index bc408110ab..96212a7c52 100644 --- a/libraries/wallet/CMakeLists.txt +++ b/libraries/wallet/CMakeLists.txt @@ -57,7 +57,7 @@ target_link_libraries( graphene::account_history graphene::operation_history graphene::account_by_key - graphene::witness_api + graphene::validator_api graphene_protocol graphene_utilities fc diff --git a/libraries/wallet/include/graphene/wallet/remote_node_api.hpp b/libraries/wallet/include/graphene/wallet/remote_node_api.hpp index 905967fa33..d105655f17 100644 --- a/libraries/wallet/include/graphene/wallet/remote_node_api.hpp +++ b/libraries/wallet/include/graphene/wallet/remote_node_api.hpp @@ -8,7 +8,7 @@ #include #include -#include +#include namespace graphene { namespace wallet { @@ -22,7 +22,7 @@ using namespace plugins; using namespace plugins::database_api; using namespace plugins::network_broadcast_api; using namespace graphene::api; -using namespace plugins::witness_api; +using namespace plugins::validator_api; /** * This is a dummy class exists only to provide method signature information to fc::api, not to execute calls. @@ -93,12 +93,12 @@ struct remote_network_broadcast_api { * This is a dummy class exists only to provide method signature information to fc::api, not to execute calls. * Class is used by wallet to send formatted API calls to witness_api plugin on remote node. */ -struct remote_witness_api { +struct remote_validator_api { vector< account_name_type > get_active_validators(); graphene::chain::validator_schedule_object get_validator_schedule(); - vector< optional< witness_api::validator_api_object > > get_validators( vector< validator_id_type > ); - vector< witness_api::validator_api_object > get_validators_by_vote( account_name_type, uint32_t ); - optional< witness_api::validator_api_object > get_validator_by_account( account_name_type ); + vector< optional< validator_api::validator_api_object > > get_validators( vector< validator_id_type > ); + vector< validator_api::validator_api_object > get_validators_by_vote( account_name_type, uint32_t ); + optional< validator_api::validator_api_object > get_validator_by_account( account_name_type ); vector< account_name_type > lookup_validator_accounts( string, uint32_t ); uint64_t get_validator_count(); }; @@ -167,7 +167,7 @@ FC_API( graphene::wallet::remote_account_by_key, /** * Declaration of remote API formatter to witness_api plugin on remote node */ -FC_API( graphene::wallet::remote_witness_api, +FC_API( graphene::wallet::remote_validator_api, (get_active_validators) (get_validator_schedule) (get_validators) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index df7c17ef76..76e2850cd2 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -624,7 +624,7 @@ namespace graphene { namespace wallet { * @param owner_account the name or id of the witness account owner, or the id of the witness * @returns the information about the witness stored in the block chain */ - optional< witness_api::validator_api_object > get_validator(string owner_account); + optional< validator_api::validator_api_object > get_validator(string owner_account); /** * Update a witness object owned by the given account. diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 9d343580cb..196a2a963e 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -216,7 +216,7 @@ namespace graphene { namespace wallet { _remote_account_history( con.get_remote_api< remote_account_history >( 0, "account_history" ) ), _remote_network_broadcast_api( con.get_remote_api< remote_network_broadcast_api >( 0, "network_broadcast_api" ) ), _remote_account_by_key( con.get_remote_api< remote_account_by_key>( 0, "account_by_key" ) ) , - _remote_witness_api( con.get_remote_api< remote_witness_api >( 0, "witness_api" ) ) + _remote_validator_api( con.get_remote_api< remote_validator_api >( 0, "validator_api" ) ) { init_prototype_ops(); @@ -274,7 +274,7 @@ namespace graphene { namespace wallet { auto median_props = _remote_database_api->get_chain_properties(); fc::mutable_variant_object result(fc::variant(dynamic_props).get_object()); result["witness_majority_version"] = - std::string(_remote_witness_api->get_validator_schedule().majority_version); + std::string(_remote_validator_api->get_validator_schedule().majority_version); result["hardfork_version"] = std::string(_remote_database_api->get_hardfork_version()); result["head_block_num"] = dynamic_props.head_block_number; @@ -657,8 +657,8 @@ namespace graphene { namespace wallet { return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (account_to_modify)(proxy)(broadcast) ) } - optional< witness_api::validator_api_object > get_witness( string owner_account ) { - return _remote_witness_api->get_validator_by_account( owner_account ); + optional< validator_api::validator_api_object > get_witness( string owner_account ) { + return _remote_validator_api->get_validator_by_account( owner_account ); } void set_transaction_expiration( uint32_t tx_expiration_seconds ) { @@ -885,7 +885,7 @@ namespace graphene { namespace wallet { fc::api< remote_account_history > _remote_account_history; fc::api< remote_network_broadcast_api> _remote_network_broadcast_api; fc::api< remote_account_by_key > _remote_account_by_key; - fc::api< remote_witness_api > _remote_witness_api; + fc::api< remote_validator_api > _remote_validator_api; uint32_t _tx_expiration_seconds = 30; flat_map _prototype_ops; @@ -952,7 +952,7 @@ namespace graphene { namespace wallet { } vector< account_name_type > wallet_api::get_active_validators()const { - return my->_remote_witness_api->get_active_validators(); + return my->_remote_validator_api->get_active_validators(); } brain_key_info wallet_api::suggest_brain_key()const { @@ -1044,10 +1044,10 @@ fc::ecc::private_key wallet_api::derive_private_key(const std::string& prefix_st vector< account_name_type > wallet_api::list_validators(const string& lowerbound, uint32_t limit) { - return my->_remote_witness_api->lookup_validator_accounts( lowerbound, limit ); + return my->_remote_validator_api->lookup_validator_accounts( lowerbound, limit ); } - optional< witness_api::validator_api_object > wallet_api::get_validator(string owner_account) + optional< validator_api::validator_api_object > wallet_api::get_validator(string owner_account) { return my->get_witness(owner_account); } @@ -1661,7 +1661,7 @@ fc::ecc::private_key wallet_api::derive_private_key(const std::string& prefix_st validator_update_operation op; if (url.empty()) { - auto wit = my->_remote_witness_api->get_validator_by_account(witness_account_name); + auto wit = my->_remote_validator_api->get_validator_by_account(witness_account_name); if (wit.valid()) { FC_ASSERT(wit->owner == witness_account_name); url = wit->url; diff --git a/plugins/p2p/CMakeLists.txt b/plugins/p2p/CMakeLists.txt index f4ee5fede8..9a2016fc83 100644 --- a/plugins/p2p/CMakeLists.txt +++ b/plugins/p2p/CMakeLists.txt @@ -36,7 +36,7 @@ target_include_directories( graphene_${CURRENT_TARGET} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/../../" - "${CMAKE_CURRENT_SOURCE_DIR}/../witness/include" + "${CMAKE_CURRENT_SOURCE_DIR}/../validator/include" ) install(TARGETS diff --git a/plugins/p2p/p2p_plugin.cpp b/plugins/p2p/p2p_plugin.cpp index 5693f94d9f..942c2dd3e2 100644 --- a/plugins/p2p/p2p_plugin.cpp +++ b/plugins/p2p/p2p_plugin.cpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include @@ -84,7 +84,7 @@ class dlt_delegate : public dlt_p2p_delegate { } bool has_emergency_private_key() const override { - auto* wit_plug = appbase::app().find_plugin(); + auto* wit_plug = appbase::app().find_plugin(); if (wit_plug) { return wit_plug->is_emergency_key_configured(); } @@ -607,7 +607,7 @@ void p2p_plugin::plugin_startup() { my->node->set_witness_diag_provider([]() -> std::string { try { auto* wp = appbase::app().find_plugin< - graphene::plugins::witness_plugin::witness_plugin>(); + graphene::plugins::validator_plugin::validator_plugin>(); if (wp && wp->get_state() == appbase::abstract_plugin::started) return wp->get_production_diagnostics(); } catch (...) {} diff --git a/plugins/snapshot/CMakeLists.txt b/plugins/snapshot/CMakeLists.txt index 6aa5b44291..019bb1d2fa 100644 --- a/plugins/snapshot/CMakeLists.txt +++ b/plugins/snapshot/CMakeLists.txt @@ -29,7 +29,7 @@ target_link_libraries ( graphene_chain graphene_chain_plugin graphene_protocol - graphene_witness + graphene_validator graphene_p2p appbase graphene_json_rpc diff --git a/plugins/snapshot/include/graphene/plugins/snapshot/snapshot_serializer.hpp b/plugins/snapshot/include/graphene/plugins/snapshot/snapshot_serializer.hpp index dd22b6d1e0..0d420c60d5 100644 --- a/plugins/snapshot/include/graphene/plugins/snapshot/snapshot_serializer.hpp +++ b/plugins/snapshot/include/graphene/plugins/snapshot/snapshot_serializer.hpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include #include #include diff --git a/plugins/snapshot/plugin.cpp b/plugins/snapshot/plugin.cpp index d6af101595..1bad00c67d 100644 --- a/plugins/snapshot/plugin.cpp +++ b/plugins/snapshot/plugin.cpp @@ -1,14 +1,14 @@ #include #include #include -#include +#include #include #include #include #include #include -#include +#include #include #include #include @@ -509,10 +509,10 @@ inline uint32_t import_block_post_validations( uint32_t count = 0; for (const auto& v : arr) { auto id_val = v["id"].as_int64(); - auto& mutable_idx = db.get_mutable_index(); - mutable_idx.set_next_id(block_post_validation_object_id_type(id_val)); + auto& mutable_idx = db.get_mutable_index(); + mutable_idx.set_next_id(validator_confirmation_object_id_type(id_val)); - db.create([&](block_post_validation_object& obj) { + db.create([&](validator_confirmation_object& obj) { fc::from_variant(v, obj); }); ++count; @@ -935,7 +935,7 @@ fc::mutable_variant_object snapshot_plugin::plugin_impl::serialize_state() { EXPORT_INDEX(block_summary_index, block_summary_object, "block_summary") EXPORT_INDEX(content_index, content_object, "content") EXPORT_INDEX(content_vote_index, content_vote_object, "content_vote") - EXPORT_INDEX(block_post_validation_index, block_post_validation_object, "block_post_validation") + EXPORT_INDEX(validator_confirmation_index, validator_confirmation_object, "block_post_validation") if (cancel_snapshot_requested.load(std::memory_order_relaxed)) { wlog("Snapshot cancelled during serialization (before transaction)"); @@ -1417,7 +1417,7 @@ void snapshot_plugin::plugin_impl::load_snapshot(const fc::path& input_path) { const auto& cv_idx = db.get_index().indices(); while (!cv_idx.empty()) { db.remove(*cv_idx.begin()); } - const auto& bpv_idx = db.get_index().indices(); + const auto& bpv_idx = db.get_index().indices(); while (!bpv_idx.empty()) { db.remove(*bpv_idx.begin()); } const auto& tx_idx = db.get_index().indices(); @@ -1770,7 +1770,7 @@ void snapshot_plugin::plugin_impl::on_applied_block(const graphene::protocol::si // snapshot can wait until after that slot is filled. auto is_witness_producing_soon = [&]() -> bool { try { - auto* witness_plug = appbase::app().find_plugin(); + auto* witness_plug = appbase::app().find_plugin(); if (witness_plug != nullptr && witness_plug->get_state() == appbase::abstract_plugin::started) { bool soon = witness_plug->is_witness_scheduled_soon(); if (soon) { @@ -2018,7 +2018,7 @@ void snapshot_plugin::plugin_impl::check_stalled_sync_loop() { // Skip stalled sync recovery entirely. bool we_are_master = false; try { - auto* witness_plug = appbase::app().find_plugin(); + auto* witness_plug = appbase::app().find_plugin(); if (witness_plug && witness_plug->get_state() == appbase::abstract_plugin::started) { we_are_master = witness_plug->is_emergency_master(); } diff --git a/plugins/witness/CMakeLists.txt b/plugins/validator/CMakeLists.txt similarity index 91% rename from plugins/witness/CMakeLists.txt rename to plugins/validator/CMakeLists.txt index 76010a9ddc..17ffa2886b 100644 --- a/plugins/witness/CMakeLists.txt +++ b/plugins/validator/CMakeLists.txt @@ -1,11 +1,11 @@ -set(CURRENT_TARGET witness) +set(CURRENT_TARGET validator) list(APPEND CURRENT_TARGET_HEADERS - include/graphene/plugins/witness/witness.hpp + include/graphene/plugins/validator/validator.hpp ) list(APPEND CURRENT_TARGET_SOURCES - witness.cpp + validator.cpp ) if(BUILD_SHARED_LIBRARIES) @@ -45,4 +45,3 @@ install(TARGETS LIBRARY DESTINATION lib ARCHIVE DESTINATION lib ) - diff --git a/plugins/witness/include/graphene/plugins/witness/witness.hpp b/plugins/validator/include/graphene/plugins/validator/validator.hpp similarity index 90% rename from plugins/witness/include/graphene/plugins/witness/witness.hpp rename to plugins/validator/include/graphene/plugins/validator/validator.hpp index 6a874f19eb..c1d06013fb 100644 --- a/plugins/witness/include/graphene/plugins/witness/witness.hpp +++ b/plugins/validator/include/graphene/plugins/validator/validator.hpp @@ -10,7 +10,7 @@ namespace graphene { namespace plugins { - namespace witness_plugin { + namespace validator_plugin { using std::string; using protocol::public_key_type; @@ -34,22 +34,22 @@ namespace graphene { } - class witness_plugin final : public appbase::plugin { + class validator_plugin final : public appbase::plugin { public: // Dependency list: chain, p2p, snapshot. - // Implemented in witness.cpp to avoid exposing snapshot headers to p2p (which includes witness.hpp). + // Implemented in validator.cpp to avoid exposing snapshot headers to p2p (which includes validator.hpp). virtual void plugin_for_each_dependency(std::function&& l) override; - constexpr static const char *plugin_name = "witness"; + constexpr static const char *plugin_name = "validator"; static const std::string &name() { static std::string name = plugin_name; return name; } - witness_plugin(); + validator_plugin(); - ~witness_plugin(); + ~validator_plugin(); void set_program_options(boost::program_options::options_description &command_line_options, @@ -99,4 +99,4 @@ namespace graphene { } } -} //graphene::witness_plugin +} //graphene::validator_plugin diff --git a/plugins/witness/witness.cpp b/plugins/validator/validator.cpp similarity index 98% rename from plugins/witness/witness.cpp rename to plugins/validator/validator.cpp index 61e3c8241a..4ed23720be 100644 --- a/plugins/witness/witness.cpp +++ b/plugins/validator/validator.cpp @@ -1,12 +1,12 @@ -#include +#include #include #include #include #include #include -#include +#include #include #include @@ -56,13 +56,13 @@ T dejsonify(const string &s) { namespace graphene { namespace plugins { - namespace witness_plugin { + namespace validator_plugin { namespace asio = boost::asio; namespace posix_time = boost::posix_time; namespace system = boost::system; - struct witness_plugin::impl final { + struct validator_plugin::impl final { impl(): p2p_(appbase::app().get_plugin()), chain_(appbase::app().get_plugin()), @@ -219,7 +219,7 @@ namespace graphene { void on_block_applied(const graphene::chain::signed_block &block); }; - void witness_plugin::set_program_options( + void validator_plugin::set_program_options( boost::program_options::options_description &command_line_options, boost::program_options::options_description &config_file_options) { string witness_id_example = "initwitness"; @@ -265,10 +265,10 @@ namespace graphene { using std::pair; using std::string; - void witness_plugin::plugin_initialize(const boost::program_options::variables_map &options) { + void validator_plugin::plugin_initialize(const boost::program_options::variables_map &options) { try { - ilog("witness plugin: plugin_initialize() begin"); - pimpl = std::make_unique(); + ilog("validator plugin: plugin_initialize() begin"); + pimpl = std::make_unique(); pimpl->total_hashes_.store(0, std::memory_order_relaxed); pimpl->_options = &options; @@ -338,13 +338,13 @@ namespace graphene { } } - ilog("witness plugin: plugin_initialize() end"); + ilog("validator plugin: plugin_initialize() end"); } FC_LOG_AND_RETHROW() } - void witness_plugin::plugin_startup() { + void validator_plugin::plugin_startup() { try { - ilog("witness plugin: plugin_startup() begin"); + ilog("validator plugin: plugin_startup() begin"); auto &d = pimpl->database(); //Start NTP time client graphene::time::now(); @@ -380,11 +380,11 @@ namespace graphene { pimpl->schedule_production_loop(); } else elog("No witnesses configured! Please add witness names and private keys to configuration."); - ilog("witness plugin: plugin_startup() end"); + ilog("validator plugin: plugin_startup() end"); } FC_CAPTURE_AND_RETHROW() } - void witness_plugin::plugin_shutdown() { + void validator_plugin::plugin_shutdown() { graphene::time::shutdown_ntp_time(); if (!pimpl->_witnesses.empty()) { ilog("shutting downing production timer"); @@ -397,17 +397,17 @@ namespace graphene { } } - void witness_plugin::plugin_for_each_dependency(plugin_processor&& l) { + void validator_plugin::plugin_for_each_dependency(plugin_processor&& l) { l(appbase::app().register_plugin()); l(appbase::app().register_plugin()); l(appbase::app().register_plugin()); } - witness_plugin::witness_plugin() {} + validator_plugin::validator_plugin() {} - witness_plugin::~witness_plugin() {} + validator_plugin::~validator_plugin() {} - bool witness_plugin::is_validator_scheduled_soon() const { + bool validator_plugin::is_validator_scheduled_soon() const { try { if (!pimpl || pimpl->_witnesses.empty() || pimpl->_private_keys.empty()) { return false; @@ -454,7 +454,7 @@ namespace graphene { return false; } - fc::time_point_sec witness_plugin::get_next_validator_slot_time() const { + fc::time_point_sec validator_plugin::get_next_validator_slot_time() const { try { if (!pimpl || pimpl->_witnesses.empty() || pimpl->_private_keys.empty()) { return fc::time_point_sec(); @@ -501,7 +501,7 @@ namespace graphene { return fc::time_point_sec(); } - bool witness_plugin::is_emergency_master() const { + bool validator_plugin::is_emergency_master() const { try { if (!pimpl || pimpl->_witnesses.empty()) { return false; @@ -533,7 +533,7 @@ namespace graphene { return false; } - bool witness_plugin::is_emergency_key_configured() const { + bool validator_plugin::is_emergency_key_configured() const { try { if (!pimpl || pimpl->_witnesses.empty()) { return false; @@ -546,7 +546,7 @@ namespace graphene { } } - std::string witness_plugin::get_production_diagnostics() const { + std::string validator_plugin::get_production_diagnostics() const { try { if (!pimpl) return "witness=no_pimpl"; std::lock_guard lk(pimpl->_diag_mutex); @@ -571,7 +571,7 @@ namespace graphene { } } - void witness_plugin::impl::on_block_applied(const graphene::chain::signed_block &block) { + void validator_plugin::impl::on_block_applied(const graphene::chain::signed_block &block) { try { std::lock_guard _diag_lk(_diag_mutex); uint64_t block_num = block.block_num(); @@ -728,7 +728,7 @@ namespace graphene { } } - void witness_plugin::impl::schedule_production_loop() { + void validator_plugin::impl::schedule_production_loop() { //Schedule for the next 250ms tick regardless of chain state // With +250ms look-ahead in maybe_validate_block(), the tick at // T_slot - 250ms aligns now exactly to the slot boundary for zero-lag production. @@ -753,7 +753,7 @@ namespace graphene { production_timer_.async_wait( [this](const system::error_code &) { block_validation_loop(); } ); } - block_validation_condition::block_validation_condition_enum witness_plugin::impl::block_validation_loop() { + block_validation_condition::block_validation_condition_enum validator_plugin::impl::block_validation_loop() { block_validation_condition::block_validation_condition_enum result; fc::mutable_variant_object capture; if (database()._debug_block_production) ilog("DEBUG_CRASH: block_validation_loop ENTER"); @@ -1261,7 +1261,7 @@ namespace graphene { return result; } - block_validation_condition::block_validation_condition_enum witness_plugin::impl::maybe_validate_block(fc::mutable_variant_object &capture) { + block_validation_condition::block_validation_condition_enum validator_plugin::impl::maybe_validate_block(fc::mutable_variant_object &capture) { auto &db = database(); if (db._debug_block_production) ilog("DEBUG_CRASH: maybe_validate_block ENTER"); fc::time_point now_fine = graphene::time::now(); @@ -1435,8 +1435,8 @@ namespace graphene { } bool ignore_witness = false; - if (db._debug_block_production) ilog("DEBUG_CRASH: get_block_post_validations for ${w}", ("w", witness_account)); - auto block_post_validations = db.get_block_post_validations(witness_account); + if (db._debug_block_production) ilog("DEBUG_CRASH: get_validator_confirmations for ${w}", ("w", witness_account)); + auto block_post_validations = db.get_validator_confirmations(witness_account); if (db._debug_block_production) ilog("DEBUG_CRASH: got ${n} post_validations for ${w}", ("n", block_post_validations.size())("w", witness_account)); if (block_post_validations.size() > 0) { const auto &witness_by_name = db.get_index().indices().get(); diff --git a/plugins/witness_api/CMakeLists.txt b/plugins/validator_api/CMakeLists.txt similarity index 84% rename from plugins/witness_api/CMakeLists.txt rename to plugins/validator_api/CMakeLists.txt index a330458514..5bfbc7b448 100644 --- a/plugins/witness_api/CMakeLists.txt +++ b/plugins/validator_api/CMakeLists.txt @@ -1,8 +1,8 @@ -set(CURRENT_TARGET witness_api) +set(CURRENT_TARGET validator_api) list(APPEND CURRENT_TARGET_HEADERS - include/graphene/plugins/witness_api/plugin.hpp - include/graphene/plugins/witness_api/api_objects/feed_history_api_object.hpp + include/graphene/plugins/validator_api/plugin.hpp + include/graphene/plugins/validator_api/api_objects/feed_history_api_object.hpp ) list(APPEND CURRENT_TARGET_SOURCES @@ -47,4 +47,4 @@ install(TARGETS RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib -) \ No newline at end of file +) diff --git a/plugins/witness_api/include/graphene/plugins/witness_api/api_objects/feed_history_api_object.hpp b/plugins/validator_api/include/graphene/plugins/validator_api/api_objects/feed_history_api_object.hpp similarity index 71% rename from plugins/witness_api/include/graphene/plugins/witness_api/api_objects/feed_history_api_object.hpp rename to plugins/validator_api/include/graphene/plugins/validator_api/api_objects/feed_history_api_object.hpp index 8a30fbe787..c574a51cdb 100644 --- a/plugins/witness_api/include/graphene/plugins/witness_api/api_objects/feed_history_api_object.hpp +++ b/plugins/validator_api/include/graphene/plugins/validator_api/api_objects/feed_history_api_object.hpp @@ -2,7 +2,7 @@ #include -namespace graphene { namespace plugins { namespace witness_api { +namespace graphene { namespace plugins { namespace validator_api { using namespace graphene::chain; using namespace graphene::protocol; diff --git a/plugins/witness_api/include/graphene/plugins/witness_api/api_objects/witness_api_object.hpp b/plugins/validator_api/include/graphene/plugins/validator_api/api_objects/validator_api_object.hpp similarity index 91% rename from plugins/witness_api/include/graphene/plugins/witness_api/api_objects/witness_api_object.hpp rename to plugins/validator_api/include/graphene/plugins/validator_api/api_objects/validator_api_object.hpp index c59d252ae2..23cae25916 100644 --- a/plugins/witness_api/include/graphene/plugins/witness_api/api_objects/witness_api_object.hpp +++ b/plugins/validator_api/include/graphene/plugins/validator_api/api_objects/validator_api_object.hpp @@ -1,10 +1,10 @@ #ifndef CHAIN_WITNESS_API_OBJ_HPP #define CHAIN_WITNESS_API_OBJ_HPP -#include +#include #include -namespace graphene { namespace plugins { namespace witness_api { +namespace graphene { namespace plugins { namespace validator_api { using namespace graphene::chain; using namespace graphene::protocol; @@ -47,10 +47,10 @@ namespace graphene { namespace plugins { namespace witness_api { } } -} // graphene::plugins::witness_api +} // graphene::plugins::validator_api -FC_REFLECT((graphene::plugins::witness_api::witness_api_object), +FC_REFLECT((graphene::plugins::validator_api::witness_api_object), (id)(owner)(created)(url)(votes)(penalty_percent)(counted_votes) (virtual_last_update)(virtual_position)(virtual_scheduled_time) (total_missed)(last_aslot)(last_confirmed_block_num)(signing_key)(props) diff --git a/plugins/witness_api/include/graphene/plugins/witness_api/plugin.hpp b/plugins/validator_api/include/graphene/plugins/validator_api/plugin.hpp similarity index 91% rename from plugins/witness_api/include/graphene/plugins/witness_api/plugin.hpp rename to plugins/validator_api/include/graphene/plugins/validator_api/plugin.hpp index 43436a9f3d..7e8091c3f0 100644 --- a/plugins/witness_api/include/graphene/plugins/witness_api/plugin.hpp +++ b/plugins/validator_api/include/graphene/plugins/validator_api/plugin.hpp @@ -25,8 +25,8 @@ #include #include -#include -#include +#include +#include #include @@ -34,7 +34,7 @@ #include -namespace graphene { namespace plugins { namespace witness_api { +namespace graphene { namespace plugins { namespace validator_api { using namespace chain; using namespace graphene::protocol; @@ -65,7 +65,7 @@ DEFINE_API_ARGS(lookup_validator_accounts, msg_pack, std::set { public: - constexpr static const char *plugin_name = "witness_api"; + constexpr static const char *plugin_name = "validator_api"; APPBASE_PLUGIN_REQUIRES( (json_rpc::plugin) @@ -112,9 +112,9 @@ class plugin : public appbase::plugin { (lookup_validator_accounts) ) private: - struct witness_plugin_impl; + struct validator_plugin_impl; - std::unique_ptr my; + std::unique_ptr my; }; -} } } // graphene::plugins::witness_api +} } } // graphene::plugins::validator_api diff --git a/plugins/witness_api/plugin.cpp b/plugins/validator_api/plugin.cpp similarity index 86% rename from plugins/witness_api/plugin.cpp rename to plugins/validator_api/plugin.cpp index a90142de6b..87374b241f 100644 --- a/plugins/witness_api/plugin.cpp +++ b/plugins/validator_api/plugin.cpp @@ -1,21 +1,21 @@ -#include +#include #include #define CHECK_ARG_SIZE(s) \ FC_ASSERT( args.args->size() == s, "Expected #s argument(s), was ${n}", ("n", args.args->size()) ); -namespace graphene { namespace plugins { namespace witness_api { +namespace graphene { namespace plugins { namespace validator_api { using namespace graphene::protocol; using namespace graphene::chain; -struct plugin::witness_plugin_impl { +struct plugin::validator_plugin_impl { public: - witness_plugin_impl() : database(appbase::app().get_plugin().db()) { + validator_plugin_impl() : database(appbase::app().get_plugin().db()) { } - ~witness_plugin_impl() = default; + ~validator_plugin_impl() = default; std::vector> get_witnesses(const std::vector &witness_ids) const; fc::optional get_witness_by_account(std::string account_name) const; @@ -48,7 +48,7 @@ DEFINE_API(plugin, get_witness_schedule) { }); } -std::vector> plugin::witness_plugin_impl::get_witnesses( +std::vector> plugin::validator_plugin_impl::get_witnesses( const std::vector &witness_ids ) const { std::vector> result; @@ -81,7 +81,7 @@ DEFINE_API(plugin, get_witness_by_account) { } -fc::optional plugin::witness_plugin_impl::get_witness_by_account(std::string account_name) const { +fc::optional plugin::validator_plugin_impl::get_witness_by_account(std::string account_name) const { const auto& idx = database.get_index().indices().get(); auto itr = idx.find(account_name); if (itr != idx.end()) { @@ -99,7 +99,7 @@ DEFINE_API(plugin, get_witnesses_by_vote) { }); } -std::vector plugin::witness_plugin_impl::get_witnesses_by_vote( +std::vector plugin::validator_plugin_impl::get_witnesses_by_vote( std::string from, uint32_t limit ) const { FC_ASSERT(limit <= 100); @@ -133,7 +133,7 @@ DEFINE_API(plugin, get_witnesses_by_counted_vote) { }); } -std::vector plugin::witness_plugin_impl::get_witnesses_by_counted_vote( +std::vector plugin::validator_plugin_impl::get_witnesses_by_counted_vote( std::string from, uint32_t limit ) const { FC_ASSERT(limit <= 100); @@ -164,7 +164,7 @@ DEFINE_API(plugin, get_witness_count) { }); } -uint64_t plugin::witness_plugin_impl::get_witness_count() const { +uint64_t plugin::validator_plugin_impl::get_witness_count() const { return database.get_index().indices().size(); } @@ -177,7 +177,7 @@ DEFINE_API(plugin, lookup_witness_accounts) { }); } -std::set plugin::witness_plugin_impl::lookup_witness_accounts( +std::set plugin::validator_plugin_impl::lookup_witness_accounts( const std::string &lower_bound_name, uint32_t limit ) const { @@ -219,15 +219,15 @@ void plugin::set_program_options( } void plugin::plugin_initialize(const boost::program_options::variables_map &options) { - ilog("witness_api plugin: plugin_initialize() begin"); + ilog("validator_api plugin: plugin_initialize() begin"); try { - my = std::make_unique(); + my = std::make_unique(); JSON_RPC_REGISTER_API(name()); } FC_CAPTURE_AND_RETHROW() - ilog("witness_api plugin: plugin_initialize() end"); + ilog("validator_api plugin: plugin_initialize() end"); } plugin::plugin() = default; @@ -235,10 +235,10 @@ plugin::plugin() = default; plugin::~plugin() = default; void plugin::plugin_startup() { - ilog("witness_api plugin: plugin_startup() begin"); + ilog("validator_api plugin: plugin_startup() begin"); - ilog("witness_api plugin: plugin_startup() end"); + ilog("validator_api plugin: plugin_startup() end"); } -} } } // graphene::plugins::witness_api +} } } // graphene::plugins::validator_api diff --git a/plugins/witness_guard/CMakeLists.txt b/plugins/validator_guard/CMakeLists.txt similarity index 88% rename from plugins/witness_guard/CMakeLists.txt rename to plugins/validator_guard/CMakeLists.txt index 428e7f0f44..c7b5ccf926 100644 --- a/plugins/witness_guard/CMakeLists.txt +++ b/plugins/validator_guard/CMakeLists.txt @@ -1,11 +1,11 @@ -set(CURRENT_TARGET witness_guard) +set(CURRENT_TARGET validator_guard) list(APPEND CURRENT_TARGET_HEADERS - include/graphene/plugins/witness_guard/witness_guard.hpp + include/graphene/plugins/validator_guard/validator_guard.hpp ) list(APPEND CURRENT_TARGET_SOURCES - witness_guard.cpp + validator_guard.cpp ) if(BUILD_SHARED_LIBRARIES) @@ -41,4 +41,4 @@ install(TARGETS RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib - ) \ No newline at end of file + ) diff --git a/plugins/witness_guard/include/graphene/plugins/witness_guard/witness_guard.hpp b/plugins/validator_guard/include/graphene/plugins/validator_guard/validator_guard.hpp similarity index 76% rename from plugins/witness_guard/include/graphene/plugins/witness_guard/witness_guard.hpp rename to plugins/validator_guard/include/graphene/plugins/validator_guard/validator_guard.hpp index 11fdaee2e7..5d3248e61a 100644 --- a/plugins/witness_guard/include/graphene/plugins/witness_guard/witness_guard.hpp +++ b/plugins/validator_guard/include/graphene/plugins/validator_guard/validator_guard.hpp @@ -6,25 +6,25 @@ namespace graphene { namespace plugins { -namespace witness_guard { +namespace validator_guard { -class witness_guard_plugin final - : public appbase::plugin { +class validator_guard_plugin final + : public appbase::plugin { public: APPBASE_PLUGIN_REQUIRES( (graphene::plugins::chain::plugin) (graphene::plugins::p2p::p2p_plugin) ) - constexpr static const char *plugin_name = "witness_guard"; + constexpr static const char *plugin_name = "validator_guard"; static const std::string &name() { static std::string name = plugin_name; return name; } - witness_guard_plugin(); - ~witness_guard_plugin(); + validator_guard_plugin(); + ~validator_guard_plugin(); void set_program_options( boost::program_options::options_description &command_line_options, @@ -43,6 +43,6 @@ class witness_guard_plugin final std::unique_ptr pimpl; }; -} // witness_guard +} // validator_guard } // plugins } // graphene \ No newline at end of file diff --git a/plugins/witness_guard/witness_guard.cpp b/plugins/validator_guard/validator_guard.cpp similarity index 87% rename from plugins/witness_guard/witness_guard.cpp rename to plugins/validator_guard/validator_guard.cpp index a96c225d97..80f24b3e7d 100644 --- a/plugins/witness_guard/witness_guard.cpp +++ b/plugins/validator_guard/validator_guard.cpp @@ -1,8 +1,8 @@ -#include +#include #include #include -#include +#include #include #include #include @@ -18,13 +18,13 @@ namespace graphene { namespace plugins { -namespace witness_guard { +namespace validator_guard { namespace bpo = boost::program_options; // ─── impl ──────────────────────────────────────────────────────────────────── -struct witness_guard_plugin::impl { +struct validator_guard_plugin::impl { impl() : chain_(appbase::app().get_plugin()) , p2p_(appbase::app().get_plugin()) @@ -80,7 +80,7 @@ struct witness_guard_plugin::impl { // ─── check_and_restore ─────────────────────────────────────────────────────── // Checks all monitored witnesses and restores signing keys when needed. // Returns true if the node is in sync and the full check was performed. -bool witness_guard_plugin::impl::check_and_restore_internal() { +bool validator_guard_plugin::impl::check_and_restore_internal() { auto& database = db(); // Skip auto-restore when stale production is enabled AND the network is @@ -92,12 +92,12 @@ bool witness_guard_plugin::impl::check_and_restore_internal() { if (!dgp.emergency_consensus_active) { uint32_t prate = database.witness_participation_rate(); if (prate >= 33 * CHAIN_1_PERCENT) { - ilog("witness_guard: network is healthy (participation ${p}%), " + ilog("validator_guard: network is healthy (participation ${p}%), " "auto-clearing stale production override", ("p", prate / CHAIN_1_PERCENT)); _stale_production_config = false; } else { - dlog("witness_guard: stale production enabled and network not yet healthy, " + dlog("validator_guard: stale production enabled and network not yet healthy, " "auto-restore disabled"); return false; } @@ -111,7 +111,7 @@ bool witness_guard_plugin::impl::check_and_restore_internal() { const auto head_time = database.head_block_time(); const auto now = fc::time_point_sec(graphene::time::now()); if (head_time < now - fc::seconds(CHAIN_BLOCK_INTERVAL * 2)) { - dlog("witness_guard: node not in sync, skipping check"); + dlog("validator_guard: node not in sync, skipping check"); return false; } @@ -123,7 +123,7 @@ bool witness_guard_plugin::impl::check_and_restore_internal() { if (lib_block) { const auto lib_time = lib_block->timestamp; if (now - lib_time > fc::seconds(200)) { - wlog("witness_guard: POTENTIAL LONG FORK DETECTED! LIB #${n} is ${sec}s old. Skipping restoration.", + wlog("validator_guard: POTENTIAL LONG FORK DETECTED! LIB #${n} is ${sec}s old. Skipping restoration.", ("n", lib_num)("sec", (now - lib_time).to_seconds())); return false; } @@ -155,7 +155,7 @@ bool witness_guard_plugin::impl::check_and_restore_internal() { auto itr = idx.find(name); if (itr == idx.end()) { - wlog("witness_guard: witness '${w}' not found in database", ("w", name)); + wlog("validator_guard: witness '${w}' not found in database", ("w", name)); continue; } @@ -171,7 +171,7 @@ bool witness_guard_plugin::impl::check_and_restore_internal() { // If this witness was auto-disabled by the consecutive-block guard, // do NOT auto-restore it — the operator must investigate and restart if (_auto_disabled_witnesses.count(name)) { - dlog("witness_guard: '${w}' was auto-disabled (consecutive block limit), " + dlog("validator_guard: '${w}' was auto-disabled (consecutive block limit), " "skipping auto-restore", ("w", name)); continue; } @@ -179,10 +179,10 @@ bool witness_guard_plugin::impl::check_and_restore_internal() { // A restore transaction is already in flight; wait for it to land or expire if (_restore_pending.count(name)) { if (now <= _restore_pending[name]) continue; - ilog("witness_guard: previous restore for '${w}' expired, retrying", ("w", name)); + ilog("validator_guard: previous restore for '${w}' expired, retrying", ("w", name)); } - ilog("witness_guard: '${w}' has null signing key on-chain — initiating restore", + ilog("validator_guard: '${w}' has null signing key on-chain — initiating restore", ("w", name)); send_witness_update(name, *itr, config); } @@ -194,7 +194,7 @@ bool witness_guard_plugin::impl::check_and_restore_internal() { // Builds, signs, and broadcasts a witness_update transaction that restores // the on-chain signing key for the given witness. -void witness_guard_plugin::impl::send_witness_update( +void validator_guard_plugin::impl::send_witness_update( const std::string& witness_name, const graphene::chain::validator_object& obj, const witness_info& config) @@ -223,11 +223,11 @@ void witness_guard_plugin::impl::send_witness_update( // Prevent unbounded growth of the confirmation tracker if (_pending_confirmations.size() > 1000) { - wlog("witness_guard: _pending_confirmations limit reached, clearing old entries"); + wlog("validator_guard: _pending_confirmations limit reached, clearing old entries"); _pending_confirmations.clear(); } - ilog("witness_guard: broadcasting witness_update [ID: ${id}] for '${w}' — restoring key to ${k}", + ilog("validator_guard: broadcasting witness_update [ID: ${id}] for '${w}' — restoring key to ${k}", ("id", tx_id)("w", witness_name)("k", signing_pub)); p2p_.broadcast_transaction(tx); @@ -236,10 +236,10 @@ void witness_guard_plugin::impl::send_witness_update( _restore_pending[witness_name] = expiration; _pending_confirmations[tx_id] = { witness_name, expiration }; - ilog("witness_guard: witness_update for '${w}' sent successfully", ("w", witness_name)); + ilog("validator_guard: witness_update for '${w}' sent successfully", ("w", witness_name)); } catch (const fc::exception& e) { - elog("witness_guard: witness_update FAILED for '${w}': ${e}", + elog("validator_guard: witness_update FAILED for '${w}': ${e}", ("w", witness_name)("e", e.to_detail_string())); // Do not mark as pending — retry will happen on the next check cycle } @@ -249,7 +249,7 @@ void witness_guard_plugin::impl::send_witness_update( // Builds, signs, and broadcasts a witness_update transaction that sets the // on-chain signing key to null, effectively disabling block production. -void witness_guard_plugin::impl::send_witness_disable( +void validator_guard_plugin::impl::send_witness_disable( const std::string& witness_name, const graphene::chain::validator_object& obj, const witness_info& config) @@ -277,7 +277,7 @@ void witness_guard_plugin::impl::send_witness_disable( tx.sign(active_priv, db().get_chain_id()); const auto tx_id = tx.id(); - ilog("witness_guard: broadcasting witness_update [ID: ${id}] for '${w}' — DISABLING (setting key to null)", + ilog("validator_guard: broadcasting witness_update [ID: ${id}] for '${w}' — DISABLING (setting key to null)", ("id", tx_id)("w", witness_name)); p2p_.broadcast_transaction(tx); @@ -285,20 +285,20 @@ void witness_guard_plugin::impl::send_witness_disable( // Mark this witness as auto-disabled so we don't auto-restore it _auto_disabled_witnesses.insert(witness_name); - ilog("witness_guard: witness_disable for '${w}' sent successfully", ("w", witness_name)); + ilog("validator_guard: witness_disable for '${w}' sent successfully", ("w", witness_name)); } catch (const fc::exception& e) { - elog("witness_guard: witness_disable FAILED for '${w}': ${e}", + elog("validator_guard: witness_disable FAILED for '${w}': ${e}", ("w", witness_name)("e", e.to_detail_string())); } } // ─── plugin lifecycle ──────────────────────────────────────────────────────── -witness_guard_plugin::witness_guard_plugin() = default; -witness_guard_plugin::~witness_guard_plugin() = default; +validator_guard_plugin::witness_guard_plugin() = default; +validator_guard_plugin::~witness_guard_plugin() = default; -void witness_guard_plugin::set_program_options( +void validator_guard_plugin::set_program_options( bpo::options_description& cli, bpo::options_description& cfg) { @@ -339,11 +339,11 @@ void witness_guard_plugin::set_program_options( cli.add(cfg); } -void witness_guard_plugin::plugin_initialize( +void validator_guard_plugin::plugin_initialize( const bpo::variables_map& options) { try { - ilog("witness_guard: plugin_initialize() begin"); + ilog("validator_guard: plugin_initialize() begin"); pimpl = std::make_unique(); // enabled flag — prefer validator-guard-enabled, fall back to deprecated witness-guard-enabled @@ -354,7 +354,7 @@ void witness_guard_plugin::plugin_initialize( pimpl->_enabled = options["witness-guard-enabled"].as(); } if (!pimpl->_enabled) { - ilog("witness_guard: disabled via config, skipping initialization"); + ilog("validator_guard: disabled via config, skipping initialization"); return; } @@ -366,7 +366,7 @@ void witness_guard_plugin::plugin_initialize( pimpl->_stale_production_config = options["enable-stale-production"].as(); } if (pimpl->_stale_production_config) { - wlog("witness_guard: enable-stale-production detected — " + wlog("validator_guard: enable-stale-production detected — " "auto-restore is DISABLED until network participation >= 33%%"); } @@ -387,10 +387,10 @@ void witness_guard_plugin::plugin_initialize( pimpl->_disable_threshold = options["validator-guard-disable"].as(); } if (pimpl->_disable_threshold > 0) { - ilog("witness_guard: auto-disable enabled — will disable witness after ${n} consecutive blocks", + ilog("validator_guard: auto-disable enabled — will disable witness after ${n} consecutive blocks", ("n", pimpl->_disable_threshold)); } else { - ilog("witness_guard: auto-disable feature is OFF (witness-guard-disable = 0)"); + ilog("validator_guard: auto-disable feature is OFF (witness-guard-disable = 0)"); } // validator configs — each entry is a JSON triplet: ["name", "signing_wif", "active_wif"] @@ -421,32 +421,32 @@ void witness_guard_plugin::plugin_initialize( pimpl->_witness_configs[name] = { *sign_priv, *active_priv }; - ilog("witness_guard: monitoring witness '${w}' (signing key: ${k})", + ilog("validator_guard: monitoring witness '${w}' (signing key: ${k})", ("w", name)("k", sign_priv->get_public_key())); } catch (const fc::exception& e) { - elog("witness_guard: failed to parse witness entry '${entry}': ${e}", + elog("validator_guard: failed to parse witness entry '${entry}': ${e}", ("entry", entry)("e", e.to_detail_string())); } } } if (pimpl->_witness_configs.empty()) { - wlog("witness_guard: no witnesses configured for monitoring"); + wlog("validator_guard: no witnesses configured for monitoring"); } - ilog("witness_guard: plugin_initialize() end — " + ilog("validator_guard: plugin_initialize() end — " "monitoring ${n} witness(es), interval=${i} blocks", ("n", pimpl->_witness_configs.size())("i", pimpl->_check_interval)); } FC_LOG_AND_RETHROW() } -void witness_guard_plugin::plugin_startup() { - ilog("witness_guard: plugin_startup() begin"); +void validator_guard_plugin::plugin_startup() { + ilog("validator_guard: plugin_startup() begin"); if (!pimpl->_enabled || pimpl->_witness_configs.empty()) { - ilog("witness_guard: nothing to monitor, plugin inactive"); + ilog("validator_guard: nothing to monitor, plugin inactive"); return; } // Verify on-chain authority for every configured witness. @@ -468,12 +468,12 @@ void witness_guard_plugin::plugin_startup() { } } if (!active_key_has_authority) { - elog("witness_guard: WARNING: Configured active key for witness '${w}' " + elog("validator_guard: WARNING: Configured active key for witness '${w}' " "does NOT have authority on-chain. Restoration will fail.", ("w", name)); } ++it; } catch (const fc::exception& e) { - elog("witness_guard: account '${w}' not found on chain, removing from monitor list", ("w", name)); + elog("validator_guard: account '${w}' not found on chain, removing from monitor list", ("w", name)); it = pimpl->_witness_configs.erase(it); } } @@ -506,7 +506,7 @@ void witness_guard_plugin::plugin_startup() { if (count >= pimpl->_disable_threshold) { // Already auto-disabled? Skip repeated broadcasts if (!pimpl->_auto_disabled_witnesses.count(producer)) { - wlog("witness_guard: witness '${w}' produced ${c} consecutive blocks — " + wlog("validator_guard: witness '${w}' produced ${c} consecutive blocks — " "auto-disabling (threshold=${t})", ("w", producer)("c", count)("t", pimpl->_disable_threshold)); @@ -547,7 +547,7 @@ void witness_guard_plugin::plugin_startup() { const auto w_name = it->second.first; pimpl->_restore_pending.erase(w_name); pimpl->_pending_confirmations.erase(it); - ilog("witness_guard: CONFIRMED restoration for '${w}' in block #${n} [TX: ${id}]", + ilog("validator_guard: CONFIRMED restoration for '${w}' in block #${n} [TX: ${id}]", ("w", w_name)("n", b.block_num())("id", id)); } } @@ -583,16 +583,16 @@ void witness_guard_plugin::plugin_startup() { } ); - ilog("witness_guard: plugin_startup() end — active"); + ilog("validator_guard: plugin_startup() end — active"); } -void witness_guard_plugin::plugin_shutdown() { +void validator_guard_plugin::plugin_shutdown() { if (pimpl && pimpl->_applied_block_connection.connected()) { pimpl->_applied_block_connection.disconnect(); } - ilog("witness_guard: plugin_shutdown()"); + ilog("validator_guard: plugin_shutdown()"); } -} // witness_guard +} // validator_guard } // plugins } // graphene \ No newline at end of file diff --git a/programs/cli_wallet/CMakeLists.txt b/programs/cli_wallet/CMakeLists.txt index 70e5e5ac2c..828736d46e 100644 --- a/programs/cli_wallet/CMakeLists.txt +++ b/programs/cli_wallet/CMakeLists.txt @@ -29,7 +29,7 @@ target_link_libraries( graphene::database_api graphene::account_history graphene::network_broadcast_api - graphene::witness_api + graphene::validator_api fc ${readline_libraries} ${CMAKE_DL_LIBS} diff --git a/programs/vizd/CMakeLists.txt b/programs/vizd/CMakeLists.txt index 18fc96b91b..c8166f0ba4 100644 --- a/programs/vizd/CMakeLists.txt +++ b/programs/vizd/CMakeLists.txt @@ -21,8 +21,8 @@ target_link_libraries( graphene_utilities graphene::chain_plugin graphene::network_broadcast_api - graphene::witness - graphene::witness_api + graphene::validator + graphene::validator_api graphene::database_api graphene::operation_history graphene::account_by_key @@ -36,7 +36,7 @@ target_link_libraries( graphene::paid_subscription_api graphene::custom_protocol_api graphene::snapshot - graphene::witness_guard + graphene::validator_guard graphene_protocol fc ${CMAKE_DL_LIBS} diff --git a/programs/vizd/main.cpp b/programs/vizd/main.cpp index d3a37afe76..165b2fb1ca 100644 --- a/programs/vizd/main.cpp +++ b/programs/vizd/main.cpp @@ -7,21 +7,21 @@ #include #include #include -#include +#include #include #include #include #include #include #include -#include +#include #include #include #include #include #include -#include +#include #include #include @@ -58,8 +58,8 @@ namespace graphene { appbase::app().register_plugin(); appbase::app().register_plugin(); appbase::app().register_plugin(); - appbase::app().register_plugin(); - appbase::app().register_plugin(); + appbase::app().register_plugin(); + appbase::app().register_plugin(); appbase::app().register_plugin(); graphene::plugins::database_api::register_database_api(); appbase::app().register_plugin(); @@ -72,7 +72,7 @@ namespace graphene { appbase::app().register_plugin(); appbase::app().register_plugin(); appbase::app().register_plugin(); - appbase::app().register_plugin(); + appbase::app().register_plugin(); ///plugins }; } diff --git a/share/vizd/config/config.ini b/share/vizd/config/config.ini index 84f558d2c9..c29dd224b4 100644 --- a/share/vizd/config/config.ini +++ b/share/vizd/config/config.ini @@ -77,11 +77,11 @@ inc-shared-file-size = 2G # and resizes. The optimal strategy is do checking of the free space, but not very often. block-num-check-free-size = 1000 # each 3000 seconds -plugin = witness +plugin = validator plugin = chain p2p plugin = network_broadcast_api database_api plugin = json_rpc webserver -plugin = committee_api invite_api witness_api paid_subscription_api custom_protocol_api +plugin = committee_api invite_api validator_api paid_subscription_api custom_protocol_api plugin = account_by_key block_info raw_block plugin = operation_history @@ -123,7 +123,7 @@ follow-max-feed-size = 500 # name of validator controlled by this node (e.g. initwitness ) # validator = -# # witness = # DEPRECATED: use 'validator' +# # validator = # DEPRECATED: use 'validator' # WIF PRIVATE KEY to be used by one or more validators # private-key = diff --git a/share/vizd/config/config_debug.ini b/share/vizd/config/config_debug.ini index 4ef36388cd..25c31e30de 100644 --- a/share/vizd/config/config_debug.ini +++ b/share/vizd/config/config_debug.ini @@ -73,7 +73,7 @@ inc-shared-file-size = 100M # and resizes. The optimal strategy is do checking of the free space, but not very often. block-num-check-free-size = 10 # each 30 seconds -plugin = chain p2p json_rpc webserver network_broadcast_api witness test_api database_api private_message follow social_network tags account_by_key account_history operation_history block_info raw_block debug_node witness_api +plugin = chain p2p json_rpc webserver network_broadcast_api validator test_api database_api private_message follow social_network tags account_by_key account_history operation_history block_info raw_block debug_node witness_api # Remove votes before defined block, should increase performance clear-votes-before-block = 0 # don't clear votes @@ -108,7 +108,7 @@ required-participation = 0 # name of validator controlled by this node (e.g. initwitness ) validator = "viz" -# witness = "viz" # DEPRECATED: use 'validator' +# validator = "viz" # DEPRECATED: use 'validator' # WIF PRIVATE KEY to be used by one or more validators private-key = 5JVFFWRLwz6JoP9kguuRFfytToGU6cLgBVTL9t6NB3D3BQLbUBS diff --git a/share/vizd/config/config_debug_mongo.ini b/share/vizd/config/config_debug_mongo.ini index d703c76bce..7377448471 100644 --- a/share/vizd/config/config_debug_mongo.ini +++ b/share/vizd/config/config_debug_mongo.ini @@ -73,7 +73,7 @@ inc-shared-file-size = 100M # and resizes. The optimal strategy is do checking of the free space, but not very often. block-num-check-free-size = 10 # each 30 seconds -plugin = chain p2p json_rpc webserver network_broadcast_api witness test_api database_api private_message follow social_network tags market_history account_by_key account_history operation_history block_info raw_block debug_node witness_api mongo_db +plugin = chain p2p json_rpc webserver network_broadcast_api validator test_api database_api private_message follow social_network tags market_history account_by_key account_history operation_history block_info raw_block debug_node validator_api mongo_db # For connect to mongodb which is running outside Docker (if vizd running inside) mongodb-uri = mongodb://172.17.0.1:27017/viz @@ -117,7 +117,7 @@ required-participation = 0 # name of validator controlled by this node (e.g. initwitness ) validator = "viz" -# witness = "viz" # DEPRECATED: use 'validator' +# validator = "viz" # DEPRECATED: use 'validator' # WIF PRIVATE KEY to be used by one or more validators private-key = 5JVFFWRLwz6JoP9kguuRFfytToGU6cLgBVTL9t6NB3D3BQLbUBS diff --git a/share/vizd/config/config_mongo.ini b/share/vizd/config/config_mongo.ini index e66b60359e..a8ee8be8e6 100644 --- a/share/vizd/config/config_mongo.ini +++ b/share/vizd/config/config_mongo.ini @@ -73,7 +73,7 @@ inc-shared-file-size = 2G # and resizes. The optimal strategy is do checking of the free space, but not very often. block-num-check-free-size = 1000 # each 3000 seconds -plugin = chain p2p json_rpc webserver network_broadcast_api witness test_api database_api private_message follow social_network tags market_history account_by_key operation_history account_history block_info raw_block witness_api mongo_db +plugin = chain p2p json_rpc webserver network_broadcast_api validator test_api database_api private_message follow social_network tags market_history account_by_key operation_history account_history block_info raw_block validator_api mongo_db # For connect to mongodb which is running outside Docker (if vizd running inside) mongodb-uri = mongodb://172.17.0.1:27017/viz @@ -117,7 +117,7 @@ required-participation = 0 # name of validator controlled by this node (e.g. initwitness ) # validator = -# # witness = # DEPRECATED: use 'validator' +# # validator = # DEPRECATED: use 'validator' # WIF PRIVATE KEY to be used by one or more validators # private-key = diff --git a/share/vizd/config/config_stock_exchange.ini b/share/vizd/config/config_stock_exchange.ini index ec3113b8ae..3a89b81b94 100644 --- a/share/vizd/config/config_stock_exchange.ini +++ b/share/vizd/config/config_stock_exchange.ini @@ -73,7 +73,7 @@ inc-shared-file-size = 2G # and resizes. The optimal strategy is do checking of the free space, but not very often. block-num-check-free-size = 1000 # each 3000 seconds -plugin = chain p2p json_rpc webserver network_broadcast_api witness database_api block_info raw_block operation_history account_history witness_api +plugin = chain p2p json_rpc webserver network_broadcast_api validator database_api block_info raw_block operation_history account_history witness_api # Remove votes before defined block, should increase performance clear-votes-before-block = 0 # clear votes after each cashout diff --git a/share/vizd/config/config_testnet.ini b/share/vizd/config/config_testnet.ini index e55b763861..7281d042b8 100644 --- a/share/vizd/config/config_testnet.ini +++ b/share/vizd/config/config_testnet.ini @@ -73,7 +73,7 @@ inc-shared-file-size = 2G # and resizes. The optimal strategy is do checking of the free space, but not very often. block-num-check-free-size = 1000 # each 3000 seconds -plugin = witness witness_api +plugin = validator witness_api plugin = chain p2p json_rpc webserver network_broadcast_api database_api plugin = account_history operation_history plugin = committee_api invite_api paid_subscription_api custom_protocol_api @@ -113,7 +113,7 @@ required-participation = 0 # name of validator controlled by this node (e.g. initwitness ) validator = "committee" # validator = "tester" -# witness = "committee" # DEPRECATED: use 'validator' +# validator = "committee" # DEPRECATED: use 'validator' # WIF PRIVATE KEY to be used by one or more validators # committee is a single active validator diff --git a/share/vizd/config/config_witness.ini b/share/vizd/config/config_witness.ini index 14e975e399..d40c244c86 100644 --- a/share/vizd/config/config_witness.ini +++ b/share/vizd/config/config_witness.ini @@ -76,12 +76,12 @@ inc-shared-file-size = 2G # and resizes. The optimal strategy is do checking of the free space, but not very often. block-num-check-free-size = 1000 # each 3000 seconds -plugin = witness -plugin = witness_guard +plugin = validator +plugin = validator_guard plugin = chain p2p plugin = network_broadcast_api database_api plugin = json_rpc webserver -plugin = committee_api invite_api witness_api paid_subscription_api custom_protocol_api +plugin = committee_api invite_api validator_api paid_subscription_api custom_protocol_api plugin = account_by_key block_info raw_block plugin = operation_history @@ -116,12 +116,12 @@ required-participation = 0 # name of validator controlled by this node (e.g. initwitness ) validator = "viz" -# witness = "viz" # DEPRECATED: use 'validator' above +# validator = "viz" # DEPRECATED: use 'validator' above # WIF PRIVATE KEY to be used by one or more validators private-key = 5JVFFWRLwz6JoP9kguuRFfytToGU6cLgBVTL9t6NB3D3BQLbUBS -# WIF PRIVATE KEY for the emergency committee witness (HF12). +# WIF PRIVATE KEY for the emergency committee validator (HF12). # Required only if this node should produce blocks during emergency consensus mode. # The key must correspond to CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY. # emergency-private-key = From 78379c0cf27546682651efd56e98635069986623 Mon Sep 17 00:00:00 2001 From: Anatoly Piskunov Date: Sun, 17 May 2026 18:27:26 +0400 Subject: [PATCH 12/29] fix constructor name --- plugins/validator_guard/validator_guard.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/validator_guard/validator_guard.cpp b/plugins/validator_guard/validator_guard.cpp index 80f24b3e7d..4a959b2030 100644 --- a/plugins/validator_guard/validator_guard.cpp +++ b/plugins/validator_guard/validator_guard.cpp @@ -295,8 +295,8 @@ void validator_guard_plugin::impl::send_witness_disable( // ─── plugin lifecycle ──────────────────────────────────────────────────────── -validator_guard_plugin::witness_guard_plugin() = default; -validator_guard_plugin::~witness_guard_plugin() = default; +validator_guard_plugin::validator_guard_plugin() = default; +validator_guard_plugin::~validator_guard_plugin() = default; void validator_guard_plugin::set_program_options( bpo::options_description& cli, From fc76f3c096cbaa5f8aa17f678951f5a07a97b7d0 Mon Sep 17 00:00:00 2001 From: Anatoly Piskunov Date: Sun, 17 May 2026 19:39:35 +0400 Subject: [PATCH 13/29] rename(api): account fields, config constants, get_config keys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename remaining API-visible witness fields and protocol constants. account_object / account_api_object: - witnesses_voted_for → validators_voted_for - witnesses_vote_weight → validators_vote_weight - witness_votes → validator_votes - witness_vote_weight() / witness_vote_fair_weight() / witness_vote_fair_weight_prehf5() → validator_* validator_confirmation_object (chain_objects.hpp): - current_shuffled_witnesses → current_shuffled_validators - current_shuffled_witnesses_validations → current_shuffled_validators_validations config.hpp / config_testnet.hpp constants: - CHAIN_MAX_WITNESSES → CHAIN_MAX_VALIDATORS - CHAIN_BLOCK_WITNESS_REPEAT → CHAIN_BLOCK_VALIDATOR_REPEAT - CHAIN_EMERGENCY_WITNESS_ACCOUNT / _PUBLIC_KEY → CHAIN_EMERGENCY_VALIDATOR_* - CHAIN_HARDFORK_REQUIRED_WITNESSES → CHAIN_HARDFORK_REQUIRED_VALIDATORS - CHAIN_MAX_ACCOUNT_WITNESS_VOTES[_PRE_HF4] → CHAIN_MAX_ACCOUNT_VALIDATOR_VOTES[_PRE_HF4] - CHAIN_MAX_WITNESS_URL_LENGTH → CHAIN_MAX_VALIDATOR_URL_LENGTH - CHAIN_MAX_WITNESS_MISSED_BLOCKS → CHAIN_MAX_VALIDATOR_MISSED_BLOCKS - CONSENSUS_WITNESS_MISS_PENALTY_{PERCENT,DURATION} → CONSENSUS_VALIDATOR_MISS_PENALTY_* - CONSENSUS_WITNESS_DECLARATION_FEE → CONSENSUS_VALIDATOR_DECLARATION_FEE - CHAIN_CONSENSUS_INFLATION_WITNESS_PERCENT → CHAIN_CONSENSUS_INFLATION_VALIDATOR_PERCENT get_config.cpp: API response keys updated to new constant names. Snapshot backward compat: - import_accounts: reads "witnesses_voted_for" from old snapshots - import_block_post_validations: reads "current_shuffled_witnesses[_validations]" from old snapshots; validator_schedule_object compat was already in place Also fixes validator_guard.cpp ctor/dtor that still used witness_guard_plugin name. --- ...itness-to-validator-migration-reference.md | 22 ++ .qoder/docs/witness-to-validator-rename.md | 31 ++- examples-plugins/mongo_db/mongo_db_state.cpp | 2 +- libraries/api/account_api_object.cpp | 2 +- .../graphene/api/account_api_object.hpp | 12 +- libraries/chain/chain_evaluator.cpp | 48 ++--- libraries/chain/database.cpp | 204 +++++++++--------- .../include/graphene/chain/account_object.hpp | 22 +- .../include/graphene/chain/chain_objects.hpp | 6 +- .../chain/include/graphene/chain/database.hpp | 2 +- .../graphene/chain/validator_objects.hpp | 4 +- libraries/protocol/chain_operations.cpp | 2 +- libraries/protocol/get_config.cpp | 12 +- .../graphene/protocol/chain_operations.hpp | 6 +- .../include/graphene/protocol/config.hpp | 34 +-- .../graphene/protocol/config_testnet.hpp | 32 +-- .../custom_protocol_api.cpp | 2 +- plugins/database_api/api.cpp | 2 +- plugins/snapshot/plugin.cpp | 14 +- plugins/validator/validator.cpp | 50 ++--- 20 files changed, 279 insertions(+), 230 deletions(-) diff --git a/.qoder/docs/witness-to-validator-migration-reference.md b/.qoder/docs/witness-to-validator-migration-reference.md index 645e8a9984..a99ac49ffc 100644 --- a/.qoder/docs/witness-to-validator-migration-reference.md +++ b/.qoder/docs/witness-to-validator-migration-reference.md @@ -53,6 +53,9 @@ This distinction is the most important rule for library developers. | Plugin directory and CMake target renames | **Done** | No — internal build | | Config key renames (`plugin = validator`, etc.) | **Done** | Yes — node operators must update `config.ini` | | API namespace (`validator_api`) | **Done** | Yes — JSON-RPC `"api"` field (see Section 2) | +| `account_api_object` fields (`validators_voted_for`, `validators_vote_weight`, `validator_votes`) | **Done** | Yes — `get_accounts` response | +| `get_config` keys (`CHAIN_MAX_VALIDATORS`, `CHAIN_HARDFORK_REQUIRED_VALIDATORS`, etc.) | **Done** | Yes — `get_config` response | +| Config constants (`CHAIN_MAX_VALIDATORS`, `CHAIN_BLOCK_VALIDATOR_REPEAT`, `CHAIN_EMERGENCY_VALIDATOR_ACCOUNT`, etc.) | **Done** | No — internal C++ only | --- @@ -477,3 +480,22 @@ Not directly relevant to JS/PHP libraries, but included for completeness: | `witness_miss_penalty_percent` | `validator_miss_penalty_percent` | `chain_properties_hf6` | | `witness_miss_penalty_duration` | `validator_miss_penalty_duration` | `chain_properties_hf6` | | `witness_declaration_fee` | `validator_declaration_fee` | `chain_properties_hf9` | + +### Account Object Fields (`get_accounts` response) + +| Old Field | New Field | Notes | +|-----------|-----------|-------| +| `witnesses_voted_for` | `validators_voted_for` | Count of validators the account voted for | +| `witnesses_vote_weight` | `validators_vote_weight` | Cached voting weight | +| `witness_votes` | `validator_votes` | Set of validator account names voted for | + +### `get_config` Response Keys + +| Old Key | New Key | +|---------|---------| +| `CHAIN_HARDFORK_REQUIRED_WITNESSES` | `CHAIN_HARDFORK_REQUIRED_VALIDATORS` | +| `CHAIN_MAX_ACCOUNT_WITNESS_VOTES` | `CHAIN_MAX_ACCOUNT_VALIDATOR_VOTES` | +| `CHAIN_MAX_WITNESSES` | `CHAIN_MAX_VALIDATORS` | +| `CHAIN_MAX_SUPPORT_WITNESSES` | `CHAIN_MAX_SUPPORT_VALIDATORS` | +| `CHAIN_MAX_TOP_WITNESSES` | `CHAIN_MAX_TOP_VALIDATORS` | +| `CHAIN_MAX_WITNESS_URL_LENGTH` | `CHAIN_MAX_VALIDATOR_URL_LENGTH` | diff --git a/.qoder/docs/witness-to-validator-rename.md b/.qoder/docs/witness-to-validator-rename.md index fa853ed92c..fcde264cb2 100644 --- a/.qoder/docs/witness-to-validator-rename.md +++ b/.qoder/docs/witness-to-validator-rename.md @@ -66,6 +66,7 @@ Nothing remaining. All renames complete. - `witness_vote_object` — internal vote-tracking object; not exposed by name in protocol - `witness_penalty_expire_object` — internal object; not exposed in protocol - `witness_penalty_expire_object::witness` field — internal back-reference, not a block header field +- `witness_vote_index`, `by_account_witness` — chainbase index tags; tied to `witness_vote_object`, not renamed --- @@ -314,9 +315,17 @@ Even with server-side fallback, clients will receive **responses** with new name 11. ✅ Rename skip flag: `skip_validator_signature`. 12. ✅ Plugin namespaces: `validator_plugin`, `validator_api`, `validator_guard`. `plugin_name` strings updated. +### Phase 2 — Additional API-visible fields ✅ Done + +1. ✅ Rename `account_object` fields: `witnesses_voted_for` → `validators_voted_for`, `witnesses_vote_weight` → `validators_vote_weight`; methods `witness_vote_weight()` → `validator_vote_weight()`, `witness_vote_fair_weight()` → `validator_vote_fair_weight()`, `witness_vote_fair_weight_prehf5()` → `validator_vote_fair_weight_prehf5()`. +2. ✅ Rename `account_api_object` fields: `witnesses_voted_for`, `witnesses_vote_weight`, `witness_votes` → `validators_voted_for`, `validators_vote_weight`, `validator_votes`. +3. ✅ Rename `config.hpp`/`config_testnet.hpp` constants: `CHAIN_MAX_WITNESSES` → `CHAIN_MAX_VALIDATORS`, `CHAIN_BLOCK_WITNESS_REPEAT` → `CHAIN_BLOCK_VALIDATOR_REPEAT`, `CHAIN_EMERGENCY_WITNESS_ACCOUNT` → `CHAIN_EMERGENCY_VALIDATOR_ACCOUNT`, `CHAIN_HARDFORK_REQUIRED_WITNESSES` → `CHAIN_HARDFORK_REQUIRED_VALIDATORS`, `CHAIN_MAX_ACCOUNT_WITNESS_VOTES` → `CHAIN_MAX_ACCOUNT_VALIDATOR_VOTES`, `CHAIN_MAX_WITNESS_URL_LENGTH` → `CHAIN_MAX_VALIDATOR_URL_LENGTH`, `CONSENSUS_WITNESS_MISS_PENALTY_*` → `CONSENSUS_VALIDATOR_MISS_PENALTY_*`, `CONSENSUS_WITNESS_DECLARATION_FEE` → `CONSENSUS_VALIDATOR_DECLARATION_FEE` etc. +4. ✅ `get_config.cpp` API key strings updated to new names. +5. ✅ Snapshot `import_accounts` — backward compat for old `witnesses_voted_for` key. + ### Phase 3 — External library updates -1. Update JS client library: operation name constants, API method names, response parsing, block header field names. +1. Update JS client library: operation name constants, API method names, response parsing, block header field names, `get_config` key names, `account_api_object` field names. 2. Update PHP client library: same scope. 3. After both libraries are released, schedule removal of the server-side fallback aliases. @@ -326,19 +335,19 @@ Even with server-side fallback, clients will receive **responses** with new name | File | Status | Scope | |------|--------|-------| -| `plugins/witness/include/graphene/plugins/witness/witness.hpp` | ✅ Done | Enum namespace, method declarations | -| `plugins/witness/witness.cpp` | ✅ Done | All enum references, method definitions, block field accesses | -| `plugins/witness_guard/witness_guard.cpp` | ✅ Done | Object types, block field accesses | -| `plugins/witness_guard/include/.../witness_guard.hpp` | ✅ Done | Class names, config declarations | -| `plugins/witness_api/plugin.cpp` | ✅ Done | API method names + deprecated aliases | -| `plugins/witness_api/include/.../plugin.hpp` | ✅ Done | API method declarations | +| `plugins/validator/include/graphene/plugins/validator/validator.hpp` | ✅ Done | Enum namespace, method declarations | +| `plugins/validator/validator.cpp` | ✅ Done | All enum references, method definitions, block field accesses | +| `plugins/validator_guard/validator_guard.cpp` | ✅ Done | Object types, block field accesses | +| `plugins/validator_guard/include/.../validator_guard.hpp` | ✅ Done | Class names, config declarations | +| `plugins/validator_api/plugin.cpp` | ✅ Done | API method names + deprecated aliases | +| `plugins/validator_api/include/.../plugin.hpp` | ✅ Done | API method declarations | | `plugins/p2p/p2p_plugin.cpp` | ✅ Done | `validator_signature` parameter | | `plugins/p2p/include/.../p2p_plugin.hpp` | ✅ Done | `validator_signature` parameter | | `plugins/chain/plugin.cpp` | ✅ Done | Block field access | | `plugins/snapshot/plugin.cpp` | ✅ Done | Object types, field accesses, backward compat import | | `plugins/database_api/api.cpp` | ✅ Done | Object type references | | `plugins/account_history/plugin.cpp` | ✅ Done | Operation visitor method names | -| `libraries/chain/include/graphene/chain/witness_objects.hpp` | ✅ Done | Object type names, field names (file not renamed yet) | +| `libraries/chain/include/graphene/chain/validator_objects.hpp` | ✅ Done | Object type names, field names, CHAIN_MAX_VALIDATORS array size | | `libraries/chain/include/graphene/chain/global_property_object.hpp` | ✅ Done | `current_validator` field + FC_REFLECT | | `libraries/chain/include/graphene/chain/chain_objects.hpp` | ✅ Done | `validator_confirmation_object`, `validator_confirmation_index` | | `libraries/chain/database.cpp` | ✅ Done | All object and block field references | @@ -365,6 +374,12 @@ Even with server-side fallback, clients will receive **responses** with new name | `libraries/wallet/wallet.cpp` | ✅ Done | CLI wallet command implementations | | `libraries/wallet/include/graphene/wallet/wallet.hpp` | ✅ Done | CLI wallet method declarations | | `libraries/wallet/include/graphene/wallet/remote_node_api.hpp` | ✅ Done | Remote API method names | +| `libraries/chain/include/graphene/chain/account_object.hpp` | ✅ Done | `witnesses_voted_for`→`validators_voted_for`, `witnesses_vote_weight`→`validators_vote_weight`, methods | +| `libraries/api/include/graphene/api/account_api_object.hpp` | ✅ Done | Same fields + `witness_votes`→`validator_votes` | +| `libraries/api/account_api_object.cpp` | ✅ Done | Field assignments | +| `libraries/protocol/include/graphene/protocol/config.hpp` | ✅ Done | All `WITNESS` constants → `VALIDATOR` | +| `libraries/protocol/include/graphene/protocol/config_testnet.hpp` | ✅ Done | Same | +| `libraries/protocol/get_config.cpp` | ✅ Done | API string keys updated | | `share/vizd/config/config_witness.ini` | ✅ Done | Plugin names → `validator`, `validator_api`, `validator_guard` | | `share/vizd/config/config.ini` | ✅ Done | Plugin names | | `share/vizd/config/config_debug.ini` | ✅ Done | Plugin names | diff --git a/examples-plugins/mongo_db/mongo_db_state.cpp b/examples-plugins/mongo_db/mongo_db_state.cpp index a5f08bec72..8c62c238aa 100644 --- a/examples-plugins/mongo_db/mongo_db_state.cpp +++ b/examples-plugins/mongo_db/mongo_db_state.cpp @@ -182,7 +182,7 @@ namespace mongo_db { body << "proxied_vsf_votes" << ben_array; } - format_value(body, "witnesses_voted_for", account.witnesses_voted_for); + format_value(body, "validators_voted_for", account.validators_voted_for); format_value(body, "last_root_post", account.last_root_post); format_value(body, "last_post", account.last_post); diff --git a/libraries/api/account_api_object.cpp b/libraries/api/account_api_object.cpp index 26590c92d4..408437053f 100644 --- a/libraries/api/account_api_object.cpp +++ b/libraries/api/account_api_object.cpp @@ -18,7 +18,7 @@ account_api_object::account_api_object(const account_object& a, const graphene:: delegated_vesting_shares(a.delegated_vesting_shares), received_vesting_shares(a.received_vesting_shares), vesting_withdraw_rate(a.vesting_withdraw_rate), next_vesting_withdrawal(a.next_vesting_withdrawal), withdrawn(a.withdrawn), to_withdraw(a.to_withdraw), withdraw_routes(a.withdraw_routes), - witnesses_voted_for(a.witnesses_voted_for), witnesses_vote_weight(a.witnesses_vote_weight), last_root_post(a.last_root_post), last_post(a.last_post), + validators_voted_for(a.validators_voted_for), validators_vote_weight(a.validators_vote_weight), last_root_post(a.last_root_post), last_post(a.last_post), average_bandwidth(a.average_bandwidth), lifetime_bandwidth(a.lifetime_bandwidth), last_bandwidth_update(a.last_bandwidth_update), valid(a.valid), account_seller(a.account_seller), account_offer_price(a.account_offer_price), account_on_sale(a.account_on_sale), account_on_sale_start_time(a.account_on_sale_start_time), subaccount_seller(a.subaccount_seller), subaccount_offer_price(a.subaccount_offer_price), subaccount_on_sale(a.subaccount_on_sale), diff --git a/libraries/api/include/graphene/api/account_api_object.hpp b/libraries/api/include/graphene/api/account_api_object.hpp index 04a895d806..9c6fd4fdbe 100644 --- a/libraries/api/include/graphene/api/account_api_object.hpp +++ b/libraries/api/include/graphene/api/account_api_object.hpp @@ -65,8 +65,8 @@ struct account_api_object { std::vector proxied_vsf_votes; - uint16_t witnesses_voted_for; - share_type witnesses_vote_weight; + uint16_t validators_voted_for; + share_type validators_vote_weight; time_point_sec last_root_post; time_point_sec last_post; @@ -74,7 +74,7 @@ struct account_api_object { share_type lifetime_bandwidth; time_point_sec last_bandwidth_update; - set witness_votes; + set validator_votes; bool valid; @@ -109,10 +109,10 @@ FC_REFLECT( (content_count)(awarded_rshares)(custom_sequence)(custom_sequence_block_num)(energy)(last_vote_time)(balance) (vesting_shares)(delegated_vesting_shares)(received_vesting_shares) (vesting_withdraw_rate)(next_vesting_withdrawal)(withdrawn)(to_withdraw)(withdraw_routes) - (curation_rewards)(posting_rewards)(receiver_awards)(benefactor_awards)(proxied_vsf_votes)(witnesses_voted_for) - (witnesses_vote_weight)(last_post)(last_root_post) + (curation_rewards)(posting_rewards)(receiver_awards)(benefactor_awards)(proxied_vsf_votes)(validators_voted_for) + (validators_vote_weight)(last_post)(last_root_post) (average_bandwidth)(lifetime_bandwidth)(last_bandwidth_update) - (witness_votes) + (validator_votes) (valid) (account_seller)(account_offer_price)(account_on_sale)(account_on_sale_start_time) (subaccount_seller)(subaccount_offer_price)(subaccount_on_sale) diff --git a/libraries/chain/chain_evaluator.cpp b/libraries/chain/chain_evaluator.cpp index 66c9148b9d..87473e1d29 100644 --- a/libraries/chain/chain_evaluator.cpp +++ b/libraries/chain/chain_evaluator.cpp @@ -1160,19 +1160,19 @@ namespace graphene { namespace chain { FC_ASSERT(o.approve, "Vote doesn't exist, user must indicate a desire to approve witness."); if(_db.has_hardfork(CHAIN_HARDFORK_4)){ - FC_ASSERT(voter.witnesses_voted_for < - CHAIN_MAX_ACCOUNT_WITNESS_VOTES, "Account has voted for too many witnesses."); + FC_ASSERT(voter.validators_voted_for < + CHAIN_MAX_ACCOUNT_VALIDATOR_VOTES, "Account has voted for too many witnesses."); } else{ - FC_ASSERT(voter.witnesses_voted_for < - CHAIN_MAX_ACCOUNT_WITNESS_VOTES_PRE_HF4, "Account has voted for too many witnesses."); // TODO: Remove after hardfork 2 + FC_ASSERT(voter.validators_voted_for < + CHAIN_MAX_ACCOUNT_VALIDATOR_VOTES_PRE_HF4, "Account has voted for too many witnesses."); // TODO: Remove after hardfork 2 } if(_db.has_hardfork(CHAIN_HARDFORK_5)){ const auto &vidx = _db.get_index().indices().get(); auto vitr = vidx.lower_bound(boost::make_tuple(voter.id, validator_id_type())); while (vitr != vidx.end() && vitr->account == voter.id) { - _db.adjust_validator_vote(_db.get(vitr->witness), -voter.witnesses_vote_weight); + _db.adjust_validator_vote(_db.get(vitr->witness), -voter.validators_vote_weight); ++vitr; } @@ -1183,17 +1183,17 @@ namespace graphene { namespace chain { }); _db.modify(voter, [&](account_object &a) { - a.witnesses_voted_for++; + a.validators_voted_for++; }); - share_type fair_vote_weight = voter.witness_vote_fair_weight(); + share_type fair_vote_weight = voter.validator_vote_fair_weight(); _db.modify(voter, [&](account_object &a) { - a.witnesses_vote_weight = fair_vote_weight; + a.validators_vote_weight = fair_vote_weight; }); const auto &vidx2 = _db.get_index().indices().get(); auto vitr2 = vidx2.lower_bound(boost::make_tuple(voter.id, validator_id_type())); while (vitr2 != vidx2.end() && vitr2->account == voter.id) { - _db.adjust_validator_vote(_db.get(vitr2->witness), voter.witnesses_vote_weight); + _db.adjust_validator_vote(_db.get(vitr2->witness), voter.validators_vote_weight); ++vitr2; } } @@ -1201,7 +1201,7 @@ namespace graphene { namespace chain { const auto &vidx = _db.get_index().indices().get(); auto vitr = vidx.lower_bound(boost::make_tuple(voter.id, validator_id_type())); while (vitr != vidx.end() && vitr->account == voter.id) { - _db.adjust_validator_vote(_db.get(vitr->witness), -voter.witness_vote_fair_weight_prehf5()); + _db.adjust_validator_vote(_db.get(vitr->witness), -voter.validator_vote_fair_weight_prehf5()); ++vitr; } @@ -1212,13 +1212,13 @@ namespace graphene { namespace chain { }); _db.modify(voter, [&](account_object &a) { - a.witnesses_voted_for++; + a.validators_voted_for++; }); const auto &vidx2 = _db.get_index().indices().get(); auto vitr2 = vidx2.lower_bound(boost::make_tuple(voter.id, validator_id_type())); while (vitr2 != vidx2.end() && vitr2->account == voter.id) { - _db.adjust_validator_vote(_db.get(vitr2->witness), voter.witness_vote_fair_weight_prehf5()); + _db.adjust_validator_vote(_db.get(vitr2->witness), voter.validator_vote_fair_weight_prehf5()); ++vitr2; } } @@ -1228,9 +1228,9 @@ namespace graphene { namespace chain { v.account = voter.id; v.vote_created_block = _db.head_block_num(); }); - _db.adjust_validator_vote(witness, voter.witness_vote_weight()); + _db.adjust_validator_vote(witness, voter.validator_vote_weight()); _db.modify(voter, [&](account_object &a) { - a.witnesses_voted_for++; + a.validators_voted_for++; }); } } else { @@ -1240,24 +1240,24 @@ namespace graphene { namespace chain { const auto &vidx = _db.get_index().indices().get(); auto vitr = vidx.lower_bound(boost::make_tuple(voter.id, validator_id_type())); while (vitr != vidx.end() && vitr->account == voter.id) { - _db.adjust_validator_vote(_db.get(vitr->witness), -voter.witnesses_vote_weight); + _db.adjust_validator_vote(_db.get(vitr->witness), -voter.validators_vote_weight); ++vitr; } _db.remove(*itr); _db.modify(voter, [&](account_object &a) { - a.witnesses_voted_for--; + a.validators_voted_for--; }); - share_type fair_vote_weight = voter.witness_vote_fair_weight(); + share_type fair_vote_weight = voter.validator_vote_fair_weight(); _db.modify(voter, [&](account_object &a) { - a.witnesses_vote_weight = fair_vote_weight; + a.validators_vote_weight = fair_vote_weight; }); const auto &vidx2 = _db.get_index().indices().get(); auto vitr2 = vidx2.lower_bound(boost::make_tuple(voter.id, validator_id_type())); while (vitr2 != vidx2.end() && vitr2->account == voter.id) { - _db.adjust_validator_vote(_db.get(vitr2->witness), voter.witnesses_vote_weight); + _db.adjust_validator_vote(_db.get(vitr2->witness), voter.validators_vote_weight); ++vitr2; } } @@ -1265,27 +1265,27 @@ namespace graphene { namespace chain { const auto &vidx = _db.get_index().indices().get(); auto vitr = vidx.lower_bound(boost::make_tuple(voter.id, validator_id_type())); while (vitr != vidx.end() && vitr->account == voter.id) { - _db.adjust_validator_vote(_db.get(vitr->witness), -voter.witness_vote_fair_weight_prehf5()); + _db.adjust_validator_vote(_db.get(vitr->witness), -voter.validator_vote_fair_weight_prehf5()); ++vitr; } _db.remove(*itr); _db.modify(voter, [&](account_object &a) { - a.witnesses_voted_for--; + a.validators_voted_for--; }); const auto &vidx2 = _db.get_index().indices().get(); auto vitr2 = vidx2.lower_bound(boost::make_tuple(voter.id, validator_id_type())); while (vitr2 != vidx2.end() && vitr2->account == voter.id) { - _db.adjust_validator_vote(_db.get(vitr2->witness), voter.witness_vote_fair_weight_prehf5()); + _db.adjust_validator_vote(_db.get(vitr2->witness), voter.validator_vote_fair_weight_prehf5()); ++vitr2; } } else{ - _db.adjust_validator_vote(witness, -voter.witness_vote_weight()); + _db.adjust_validator_vote(witness, -voter.validator_vote_weight()); _db.modify(voter, [&](account_object &a) { - a.witnesses_voted_for--; + a.validators_voted_for--; }); _db.remove(*itr); } diff --git a/libraries/chain/database.cpp b/libraries/chain/database.cpp index b0c0597ad4..053e1d8b60 100644 --- a/libraries/chain/database.cpp +++ b/libraries/chain/database.cpp @@ -440,7 +440,7 @@ namespace graphene { namespace chain { const validator_schedule_object &startup_wso = get_validator_schedule_object(); bool schedule_broken = false; - for (int i = 0; i < startup_wso.num_scheduled_validators; i += CHAIN_BLOCK_WITNESS_REPEAT) { + for (int i = 0; i < startup_wso.num_scheduled_validators; i += CHAIN_BLOCK_VALIDATOR_REPEAT) { if (startup_wso.current_shuffled_validators[i] == account_name_type()) { schedule_broken = true; break; @@ -466,15 +466,15 @@ namespace graphene { namespace chain { // Fill all schedule slots with committee modify(startup_wso, [&](validator_schedule_object &_wso) { - for (int i = 0; i < CHAIN_MAX_WITNESSES * CHAIN_BLOCK_WITNESS_REPEAT; i++) { - _wso.current_shuffled_validators[i] = CHAIN_EMERGENCY_WITNESS_ACCOUNT; + for (int i = 0; i < CHAIN_MAX_VALIDATORS * CHAIN_BLOCK_VALIDATOR_REPEAT; i++) { + _wso.current_shuffled_validators[i] = CHAIN_EMERGENCY_VALIDATOR_ACCOUNT; } - _wso.num_scheduled_validators = CHAIN_MAX_WITNESSES * CHAIN_BLOCK_WITNESS_REPEAT; + _wso.num_scheduled_validators = CHAIN_MAX_VALIDATORS * CHAIN_BLOCK_VALIDATOR_REPEAT; _wso.next_shuffle_block_num = head_block_num() + _wso.num_scheduled_validators; }); wlog("EMERGENCY SCHEDULE RECOVERY: schedule repaired, all ${n} slots set to committee", - ("n", CHAIN_MAX_WITNESSES)); + ("n", CHAIN_MAX_VALIDATORS)); }); } else if (startup_dgp.emergency_consensus_active) { // Schedule is valid but emergency mode is active — restore fork_db flag @@ -1499,7 +1499,7 @@ namespace graphene { namespace chain { bool has_emergency = false; for (const auto& item : branch) { const auto& wit_name = item->data.validator; - if (wit_name == CHAIN_EMERGENCY_WITNESS_ACCOUNT) { + if (wit_name == CHAIN_EMERGENCY_VALIDATOR_ACCOUNT) { has_emergency = true; continue; } @@ -2307,7 +2307,7 @@ namespace graphene { namespace chain { bool is_emergency_committee = has_hardfork(CHAIN_HARDFORK_12) && dgp_block.emergency_consensus_active && - witness_owner == CHAIN_EMERGENCY_WITNESS_ACCOUNT; + witness_owner == CHAIN_EMERGENCY_VALIDATOR_ACCOUNT; if (!is_emergency_committee) { if (hfp.current_hardfork_version < @@ -2762,7 +2762,7 @@ namespace graphene { namespace chain { } void database::update_witness_schedule() { - if ((head_block_num() % ( CHAIN_MAX_WITNESSES * CHAIN_BLOCK_WITNESS_REPEAT ) ) != 0) return; + if ((head_block_num() % ( CHAIN_MAX_VALIDATORS * CHAIN_BLOCK_VALIDATOR_REPEAT ) ) != 0) return; if (_debug_block_production) ilog("DEBUG_CRASH: update_witness_schedule ENTER at block ${b}", ("b", head_block_num())); @@ -2785,10 +2785,10 @@ namespace graphene { namespace chain { } vector active_witnesses; - active_witnesses.reserve(CHAIN_MAX_WITNESSES); + active_witnesses.reserve(CHAIN_MAX_VALIDATORS); vector support_witnesses; - support_witnesses.reserve(CHAIN_MAX_WITNESSES); + support_witnesses.reserve(CHAIN_MAX_VALIDATORS); /// Add the highest voted witnesses flat_set selected_voted; @@ -2805,7 +2805,7 @@ namespace graphene { namespace chain { // Exclude committee/emergency witness from top witness selection. // It fills gaps via the hybrid schedule, not through normal voting. if (has_hardfork(CHAIN_HARDFORK_12) && - itr->owner == CHAIN_EMERGENCY_WITNESS_ACCOUNT) { + itr->owner == CHAIN_EMERGENCY_VALIDATOR_ACCOUNT) { continue; } selected_voted.insert(itr->id); @@ -2821,7 +2821,7 @@ namespace graphene { namespace chain { vector processed_witnesses; for (auto witness_count = selected_voted.size(); sitr != schedule_idx.end() && - witness_count < CHAIN_MAX_WITNESSES; + witness_count < CHAIN_MAX_VALIDATORS; ++sitr) { new_virtual_time = sitr->virtual_scheduled_time; /// everyone advances to at least this time processed_witnesses.push_back(sitr); @@ -2832,7 +2832,7 @@ namespace graphene { namespace chain { // Exclude committee/emergency witness from support selection if (has_hardfork(CHAIN_HARDFORK_12) && - sitr->owner == CHAIN_EMERGENCY_WITNESS_ACCOUNT) { + sitr->owner == CHAIN_EMERGENCY_VALIDATOR_ACCOUNT) { continue; } @@ -2865,8 +2865,8 @@ namespace graphene { namespace chain { reset_virtual_schedule_time(); } - FC_ASSERT( ( active_witnesses.size() + support_witnesses.size() ) <= CHAIN_MAX_WITNESSES, "Number of active witnesses does cannot be more CHAIN_MAX_WITNESSES", - ("active_witnesses.size()", active_witnesses.size())("support_witnesses.size()", support_witnesses.size())("CHAIN_MAX_WITNESSES", CHAIN_MAX_WITNESSES)); + FC_ASSERT( ( active_witnesses.size() + support_witnesses.size() ) <= CHAIN_MAX_VALIDATORS, "Number of active witnesses does cannot be more CHAIN_MAX_VALIDATORS", + ("active_witnesses.size()", active_witnesses.size())("support_witnesses.size()", support_witnesses.size())("CHAIN_MAX_VALIDATORS", CHAIN_MAX_VALIDATORS)); auto majority_version = wso.majority_version; @@ -2875,7 +2875,7 @@ namespace graphene { namespace chain { const dynamic_global_property_object &_dgp = get_dynamic_global_properties(); - for (uint32_t i = 0; i < wso.num_scheduled_validators; i+=CHAIN_BLOCK_WITNESS_REPEAT) { + for (uint32_t i = 0; i < wso.num_scheduled_validators; i+=CHAIN_BLOCK_VALIDATOR_REPEAT) { auto wname = wso.current_shuffled_validators[i]; // During emergency mode, exclude committee witness from hardfork vote tally. @@ -2883,9 +2883,9 @@ namespace graphene { namespace chain { // default (0.0.0) version fields. Counting it would: // - Inflate its vote weight (counted once per slot, not once per witness) // - Drag majority_version to 0.0.0 - // - Block any hardfork from reaching CHAIN_HARDFORK_REQUIRED_WITNESSES + // - Block any hardfork from reaching CHAIN_HARDFORK_REQUIRED_VALIDATORS if (has_hardfork(CHAIN_HARDFORK_12) && _dgp.emergency_consensus_active && - wname == CHAIN_EMERGENCY_WITNESS_ACCOUNT) { + wname == CHAIN_EMERGENCY_VALIDATOR_ACCOUNT) { continue; } @@ -2914,7 +2914,7 @@ namespace graphene { namespace chain { witnesses_on_version += ver_itr->second; if (witnesses_on_version >= - CHAIN_HARDFORK_REQUIRED_WITNESSES) { + CHAIN_HARDFORK_REQUIRED_VALIDATORS) { majority_version = ver_itr->first; break; } @@ -2925,7 +2925,7 @@ namespace graphene { namespace chain { auto hf_itr = hardfork_version_votes.begin(); while (hf_itr != hardfork_version_votes.end()) { - if (hf_itr->second >= CHAIN_HARDFORK_REQUIRED_WITNESSES) { + if (hf_itr->second >= CHAIN_HARDFORK_REQUIRED_VALIDATORS) { const auto &hfp = get_hardfork_property_object(); if (hfp.next_hardfork != std::get<0>(hf_itr->first) || hfp.next_hardfork_time != @@ -2953,7 +2953,7 @@ namespace graphene { namespace chain { ("a", active_witnesses.size())("s", support_witnesses.size())); modify(wso, [&](validator_schedule_object &_wso) { - // active witnesses has exactly CHAIN_MAX_WITNESSES elements, asserted above + // active witnesses has exactly CHAIN_MAX_VALIDATORS elements, asserted above size_t j = 0; size_t support_witnesses_count = support_witnesses.size(); size_t active_witnesses_count = active_witnesses.size(); @@ -2962,26 +2962,26 @@ namespace graphene { namespace chain { { if(active_witnesses_count > 0){ --active_witnesses_count; - for(int repeat=0; repeat < CHAIN_BLOCK_WITNESS_REPEAT; ++repeat){ + for(int repeat=0; repeat < CHAIN_BLOCK_VALIDATOR_REPEAT; ++repeat){ _wso.current_shuffled_validators[j] = active_witnesses[i]; ++j; } } if(support_witnesses_count > 0){ --support_witnesses_count; - for(int repeat=0; repeat < CHAIN_BLOCK_WITNESS_REPEAT; ++repeat){ + for(int repeat=0; repeat < CHAIN_BLOCK_VALIDATOR_REPEAT; ++repeat){ _wso.current_shuffled_validators[j] = support_witnesses[i]; ++j; } } } - for (size_t i = sum_witnesses_count * CHAIN_BLOCK_WITNESS_REPEAT; - i < ( CHAIN_MAX_WITNESSES * CHAIN_BLOCK_WITNESS_REPEAT ); i++) { + for (size_t i = sum_witnesses_count * CHAIN_BLOCK_VALIDATOR_REPEAT; + i < ( CHAIN_MAX_VALIDATORS * CHAIN_BLOCK_VALIDATOR_REPEAT ); i++) { _wso.current_shuffled_validators[i] = account_name_type(); } - _wso.num_scheduled_validators = std::max(sum_witnesses_count * CHAIN_BLOCK_WITNESS_REPEAT , 1); + _wso.num_scheduled_validators = std::max(sum_witnesses_count * CHAIN_BLOCK_VALIDATOR_REPEAT , 1); /* // VIZ remove randomization /// shuffle current shuffled witnesses @@ -3027,12 +3027,12 @@ namespace graphene { namespace chain { modify(emergency_wso, [&](validator_schedule_object &_wso) { // First pass: replace unavailable/empty slots with committee - // Iterate the FULL schedule (CHAIN_MAX_WITNESSES), not just + // Iterate the FULL schedule (CHAIN_MAX_VALIDATORS), not just // num_scheduled_witnesses, because the normal schedule may have - // set num_scheduled_witnesses < CHAIN_MAX_WITNESSES when fewer + // set num_scheduled_witnesses < CHAIN_MAX_VALIDATORS when fewer // witnesses have valid signing keys. - for (int i = 0; i < CHAIN_MAX_WITNESSES; - i += CHAIN_BLOCK_WITNESS_REPEAT) { + for (int i = 0; i < CHAIN_MAX_VALIDATORS; + i += CHAIN_BLOCK_VALIDATOR_REPEAT) { // Read from slot i, but only if within current num_scheduled_witnesses const auto &wname = (i < _wso.num_scheduled_validators) ? _wso.current_shuffled_validators[i] @@ -3040,9 +3040,9 @@ namespace graphene { namespace chain { if (wname == account_name_type()) { // Empty slot -> assign committee - for (int j = 0; j < CHAIN_BLOCK_WITNESS_REPEAT; ++j) { + for (int j = 0; j < CHAIN_BLOCK_VALIDATOR_REPEAT; ++j) { _wso.current_shuffled_validators[i+j] = - CHAIN_EMERGENCY_WITNESS_ACCOUNT; + CHAIN_EMERGENCY_VALIDATOR_ACCOUNT; } committee_slots++; continue; @@ -3053,9 +3053,9 @@ namespace graphene { namespace chain { w->signing_key != public_key_type(); if (!witness_available) { - for (int j = 0; j < CHAIN_BLOCK_WITNESS_REPEAT; ++j) { + for (int j = 0; j < CHAIN_BLOCK_VALIDATOR_REPEAT; ++j) { _wso.current_shuffled_validators[i+j] = - CHAIN_EMERGENCY_WITNESS_ACCOUNT; + CHAIN_EMERGENCY_VALIDATOR_ACCOUNT; } committee_slots++; } else { @@ -3063,9 +3063,9 @@ namespace graphene { namespace chain { } } - // Expand num_scheduled_witnesses to CHAIN_MAX_WITNESSES so that + // Expand num_scheduled_witnesses to CHAIN_MAX_VALIDATORS so that // committee slots are included in the production cycle. - _wso.num_scheduled_validators = CHAIN_MAX_WITNESSES * CHAIN_BLOCK_WITNESS_REPEAT; + _wso.num_scheduled_validators = CHAIN_MAX_VALIDATORS * CHAIN_BLOCK_VALIDATOR_REPEAT; _wso.next_shuffle_block_num = head_block_num() + _wso.num_scheduled_validators; @@ -3084,7 +3084,7 @@ namespace graphene { namespace chain { // committee's props stay in sync and don't skew the next median // computation. Also keeps hardfork vote aligned with the currently // applied on-chain version. - auto committee_wit_itr = get_index().indices().get().find(CHAIN_EMERGENCY_WITNESS_ACCOUNT); + auto committee_wit_itr = get_index().indices().get().find(CHAIN_EMERGENCY_VALIDATOR_ACCOUNT); if (committee_wit_itr != get_index().indices().get().end()) { const auto &latest_hfp = get_hardfork_property_object(); const auto &latest_wso = get_validator_schedule_object(); @@ -3100,7 +3100,7 @@ namespace graphene { namespace chain { // EXIT CONDITION: enough real witnesses have re-enabled. // When >= 75% of schedule slots are real witnesses (not committee), // the network has recovered and can sustain normal consensus. - uint32_t exit_threshold = (CHAIN_MAX_WITNESSES * CHAIN_IRREVERSIBLE_THRESHOLD) / CHAIN_100_PERCENT; + uint32_t exit_threshold = (CHAIN_MAX_VALIDATORS * CHAIN_IRREVERSIBLE_THRESHOLD) / CHAIN_100_PERCENT; if (real_witness_slots >= exit_threshold) { modify(emergency_dgp, [&](dynamic_global_property_object &_dgp) { _dgp.emergency_consensus_active = false; @@ -3127,13 +3127,13 @@ namespace graphene { namespace chain { /// fetch all witness objects (excluding committee during emergency) vector active; active.reserve(wso.num_scheduled_validators); - for (int i = 0; i < wso.num_scheduled_validators; i+=CHAIN_BLOCK_WITNESS_REPEAT) { + for (int i = 0; i < wso.num_scheduled_validators; i+=CHAIN_BLOCK_VALIDATOR_REPEAT) { const auto &wname = wso.current_shuffled_validators[i]; // During emergency mode, exclude committee witness from median computation. // Committee has default chain_properties (zero fees, zero sizes) and // occupies multiple schedule slots, which would skew the median. if (has_hardfork(CHAIN_HARDFORK_12) && median_dgp.emergency_consensus_active && - wname == CHAIN_EMERGENCY_WITNESS_ACCOUNT) { + wname == CHAIN_EMERGENCY_VALIDATOR_ACCOUNT) { continue; } active.push_back(&get_witness(wname)); @@ -3255,10 +3255,10 @@ namespace graphene { namespace chain { void database::adjust_validator_votes(const account_object &a, share_type delta) { if(has_hardfork(CHAIN_HARDFORK_5)){//need to clear witness vote weight - if(a.witnesses_voted_for > 0){ - share_type fair_delta = delta / a.witnesses_voted_for; + if(a.validators_voted_for > 0){ + share_type fair_delta = delta / a.validators_voted_for; modify(a, [&](account_object &acc) { - acc.witnesses_vote_weight += fair_delta; + acc.validators_vote_weight += fair_delta; }); const auto &vidx = get_index().indices().get(); @@ -3326,9 +3326,9 @@ namespace graphene { namespace chain { } modify(a, [&](account_object &acc) { - acc.witnesses_voted_for = 0; + acc.validators_voted_for = 0; if(has_hardfork(CHAIN_HARDFORK_5)){//need to clear witness vote weight - acc.witnesses_vote_weight = 0; + acc.validators_vote_weight = 0; } }); } @@ -3939,7 +3939,7 @@ namespace graphene { namespace chain { const auto& stakeholder = get(itr->account); // Fair weight: total stake divided by number of validators voted for, // matching the actual per-validator weight used in consensus scheduling. - share_type vote_weight = stakeholder.witness_vote_fair_weight(); + share_type vote_weight = stakeholder.validator_vote_fair_weight(); if (vote_weight > 0) { uint32_t first_block = (itr->vote_created_block > epoch_start_block) ? itr->vote_created_block : epoch_start_block; @@ -5349,7 +5349,7 @@ namespace graphene { namespace chain { // forks with different shuffles to interoperate. const validator_schedule_object &wso = get_validator_schedule_object(); bool in_schedule = false; - for (int i = 0; i < wso.num_scheduled_validators; i += CHAIN_BLOCK_WITNESS_REPEAT) { + for (int i = 0; i < wso.num_scheduled_validators; i += CHAIN_BLOCK_VALIDATOR_REPEAT) { if (wso.current_shuffled_validators[i] == witness.owner) { in_schedule = true; break; @@ -5408,7 +5408,7 @@ namespace graphene { namespace chain { has_hardfork(CHAIN_HARDFORK_12) && _dgp.emergency_consensus_active && witness_missed.owner != b.validator && - witness_missed.owner != CHAIN_EMERGENCY_WITNESS_ACCOUNT; + witness_missed.owner != CHAIN_EMERGENCY_VALIDATOR_ACCOUNT; if (!is_emergency_offline_witness && witness_missed.owner != b.validator) { ilog("\033[91mMissed block: witness ${w} did not produce block #${n} at ${t} (next: ${next})\033[0m", @@ -5433,16 +5433,16 @@ namespace graphene { namespace chain { // Emergency mode: skip per-block vote penalties and total_missed. // Key-blanking uses a deterministic consensus check: // head_block_num - last_confirmed_block_num (both on-chain state) - // against CHAIN_EMERGENCY_MAX_WITNESS_MISSED_BLOCKS. + // against CHAIN_EMERGENCY_MAX_VALIDATOR_MISSED_BLOCKS. // All nodes compute the same result. if (w.signing_key != public_key_type() && head_block_num() - w.last_confirmed_block_num > - CHAIN_EMERGENCY_MAX_WITNESS_MISSED_BLOCKS) { + CHAIN_EMERGENCY_MAX_VALIDATOR_MISSED_BLOCKS) { elog("Emergency consensus: Witness ${w} missed ${missed} blocks since last confirmed ${lc} " "(threshold=${t}), blanking signing_key", ("w", w.owner)("missed", head_block_num() - w.last_confirmed_block_num) - ("lc", w.last_confirmed_block_num)("t", CHAIN_EMERGENCY_MAX_WITNESS_MISSED_BLOCKS)); + ("lc", w.last_confirmed_block_num)("t", CHAIN_EMERGENCY_MAX_VALIDATOR_MISSED_BLOCKS)); w.signing_key = public_key_type(); push_virtual_operation(shutdown_validator_operation(w.owner)); } @@ -5468,7 +5468,7 @@ namespace graphene { namespace chain { if (w.signing_key != public_key_type() && head_block_num() - w.last_confirmed_block_num > - CHAIN_MAX_WITNESS_MISSED_BLOCKS) { + CHAIN_MAX_VALIDATOR_MISSED_BLOCKS) { elog("Witness ${w} missed too many blocks (${missed} since last confirmed ${lc}), blanking signing_key (was ${k})", ("w", w.owner)("missed", head_block_num() - w.last_confirmed_block_num) ("lc", w.last_confirmed_block_num)("k", w.signing_key)); @@ -5603,12 +5603,12 @@ namespace graphene { namespace chain { // Change 5: Ensure emergency witness object exists with correct key const auto &witness_by_name = get_index().indices().get(); - auto wit_itr = witness_by_name.find(CHAIN_EMERGENCY_WITNESS_ACCOUNT); + auto wit_itr = witness_by_name.find(CHAIN_EMERGENCY_VALIDATOR_ACCOUNT); if (wit_itr == witness_by_name.end()) { create([&](validator_object &w) { - w.owner = CHAIN_EMERGENCY_WITNESS_ACCOUNT; - w.signing_key = CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY; + w.owner = CHAIN_EMERGENCY_VALIDATOR_ACCOUNT; + w.signing_key = CHAIN_EMERGENCY_VALIDATOR_PUBLIC_KEY; w.created = head_block_time(); w.schedule = validator_object::top; // Set running version to match the binary @@ -5629,7 +5629,7 @@ namespace graphene { namespace chain { }); } else { modify(*wit_itr, [&](validator_object &w) { - w.signing_key = CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY; + w.signing_key = CHAIN_EMERGENCY_VALIDATOR_PUBLIC_KEY; w.schedule = validator_object::top; // Update version fields on re-activation too w.running_version = CHAIN_VERSION; @@ -5647,7 +5647,7 @@ namespace graphene { namespace chain { const auto &witness_idx = get_index().indices().get(); uint32_t blanked_count = 0; for (auto witr = witness_idx.begin(); witr != witness_idx.end(); ++witr) { - if (witr->owner == CHAIN_EMERGENCY_WITNESS_ACCOUNT) continue; + if (witr->owner == CHAIN_EMERGENCY_VALIDATOR_ACCOUNT) continue; if (witr->signing_key != public_key_type()) blanked_count++; modify(*witr, [&](validator_object &w) { w.signing_key = public_key_type(); @@ -5678,7 +5678,7 @@ namespace graphene { namespace chain { const validator_schedule_object &wso = get_validator_schedule_object(); modify(wso, [&](validator_schedule_object &_wso) { for (int i = 0; i < _wso.num_scheduled_validators; i++) { - _wso.current_shuffled_validators[i] = CHAIN_EMERGENCY_WITNESS_ACCOUNT; + _wso.current_shuffled_validators[i] = CHAIN_EMERGENCY_VALIDATOR_ACCOUNT; } _wso.next_shuffle_block_num = head_block_num() + _wso.num_scheduled_validators; }); @@ -5691,7 +5691,7 @@ namespace graphene { namespace chain { "Emergency witness: ${w}", ("b", b.block_num())("sec", seconds_since_lib) ("lib", _dgp.last_irreversible_block_num) - ("w", CHAIN_EMERGENCY_WITNESS_ACCOUNT)); + ("w", CHAIN_EMERGENCY_VALIDATOR_ACCOUNT)); } // end if (seconds_since_lib >= TIMEOUT) } // end else (lib_time_available) } // end if (has_hardfork(HF12) && !emergency_active) @@ -5721,8 +5721,8 @@ namespace graphene { namespace chain { itr++; if((1 + dpo.last_irreversible_block_num) == current.block_num){ size_t count=0; - for (size_t j = 0; j< CHAIN_MAX_WITNESSES; j++) { - if(current.current_shuffled_witnesses_validations[j] == true){//already validated + for (size_t j = 0; j< CHAIN_MAX_VALIDATORS; j++) { + if(current.current_shuffled_validators_validations[j] == true){//already validated count++; } } @@ -5893,7 +5893,7 @@ namespace graphene { namespace chain { // exploited for spam. const validator_schedule_object &wso = get_validator_schedule_object(); bool is_scheduled = false; - for (int i = 0; i < wso.num_scheduled_validators; i += CHAIN_BLOCK_WITNESS_REPEAT) { + for (int i = 0; i < wso.num_scheduled_validators; i += CHAIN_BLOCK_VALIDATOR_REPEAT) { if (wso.current_shuffled_validators[i] == witness_account) { is_scheduled = true; break; @@ -5917,14 +5917,14 @@ namespace graphene { namespace chain { find_block_num = current.block_num; modify(current, [&](validator_confirmation_object &o) { //remove witness from shuffled witnesses - for (size_t j = 0; j< CHAIN_MAX_WITNESSES; j++) { - if(o.current_shuffled_witnesses[j] == witness_account){ - o.current_shuffled_witnesses_validations[j] = true; + for (size_t j = 0; j< CHAIN_MAX_VALIDATORS; j++) { + if(o.current_shuffled_validators[j] == witness_account){ + o.current_shuffled_validators_validations[j] = true; //need update find = true; find_obj=*itr; } - if(o.current_shuffled_witnesses_validations[j] == true){//already validated + if(o.current_shuffled_validators_validations[j] == true){//already validated count++; } } @@ -6094,9 +6094,9 @@ namespace graphene { namespace chain { //if witness is in the list add it to result //only count the first match per validation object (committee can occupy multiple slots) bool matched = false; - for (size_t j = 0; j < CHAIN_MAX_WITNESSES && !matched; j++) { - if(current.current_shuffled_witnesses[j] == witness_account){ - if(current.current_shuffled_witnesses_validations[j] == false){//need validate + for (size_t j = 0; j < CHAIN_MAX_VALIDATORS && !matched; j++) { + if(current.current_shuffled_validators[j] == witness_account){ + if(current.current_shuffled_validators_validations[j] == false){//need validate result[i] = validator_confirmation_object(current); ++i; matched = true; @@ -6148,16 +6148,16 @@ namespace graphene { namespace chain { const validator_schedule_object &wso = get_validator_schedule_object(); size_t validator_index=0; size_t i = 0; - for (; i < wso.num_scheduled_validators; i+=CHAIN_BLOCK_WITNESS_REPEAT) { + for (; i < wso.num_scheduled_validators; i+=CHAIN_BLOCK_VALIDATOR_REPEAT) { if(witness_account != wso.current_shuffled_validators[i]){ - o.current_shuffled_witnesses[validator_index] = account_name_type(wso.current_shuffled_validators[i]); - o.current_shuffled_witnesses_validations[validator_index] = false; + o.current_shuffled_validators[validator_index] = account_name_type(wso.current_shuffled_validators[i]); + o.current_shuffled_validators_validations[validator_index] = false; validator_index++; } } - for (; i < CHAIN_MAX_WITNESSES; i+=CHAIN_BLOCK_WITNESS_REPEAT) { - o.current_shuffled_witnesses[i] = account_name_type(); - o.current_shuffled_witnesses_validations[i] = false; + for (; i < CHAIN_MAX_VALIDATORS; i+=CHAIN_BLOCK_VALIDATOR_REPEAT) { + o.current_shuffled_validators[i] = account_name_type(); + o.current_shuffled_validators_validations[i] = false; } }); } @@ -6197,7 +6197,7 @@ namespace graphene { namespace chain { vector wit_objs; wit_objs.reserve(wso.num_scheduled_validators); - for (int i = 0; i < wso.num_scheduled_validators; i+=CHAIN_BLOCK_WITNESS_REPEAT) { + for (int i = 0; i < wso.num_scheduled_validators; i+=CHAIN_BLOCK_VALIDATOR_REPEAT) { wit_objs.push_back(&get_witness(wso.current_shuffled_validators[i])); } @@ -6714,8 +6714,8 @@ namespace graphene { namespace chain { const auto &widx = get_index().indices(); for(auto witr = widx.begin(); witr != widx.end(); ++witr) { const auto &voter = get(witr->account); - share_type old_weight=voter.witness_vote_weight(); - share_type new_weight=voter.witness_vote_fair_weight_prehf5(); + share_type old_weight=voter.validator_vote_weight(); + share_type new_weight=voter.validator_vote_fair_weight_prehf5(); adjust_validator_vote(get(witr->witness), -old_weight); adjust_validator_vote(get(witr->witness), new_weight); } @@ -6741,9 +6741,9 @@ namespace graphene { namespace chain { const auto &voter = get(witr->account); const auto &witness = get(witr->witness); - share_type fair_weight=voter.witness_vote_fair_weight(); + share_type fair_weight=voter.validator_vote_fair_weight(); modify(voter, [&](account_object &a) { - a.witnesses_vote_weight = fair_weight; + a.validators_vote_weight = fair_weight; }); elog("HF5 witness ${a} calc votes: ${n}", ("a", witness.owner)("n", fair_weight)); @@ -6893,9 +6893,9 @@ namespace graphene { namespace chain { const auto &voter = get(witr->account); const auto &witness = get(witr->witness); - share_type fair_weight=voter.witness_vote_fair_weight(); + share_type fair_weight=voter.validator_vote_fair_weight(); modify(voter, [&](account_object &a) { - a.witnesses_vote_weight = fair_weight; + a.validators_vote_weight = fair_weight; }); elog("HF6 witness ${a} recalc votes from ${a}: ${n}", ("a", witness.owner)("n", fair_weight)); @@ -7045,11 +7045,11 @@ namespace graphene { namespace chain { remove(delete_current); } - //decrease witnesses_vote_weight from all votes by invalid account + //decrease validators_vote_weight from all votes by invalid account const auto &vidx = get_index().indices().get(); auto vitr = vidx.lower_bound(boost::make_tuple(current.id, validator_id_type())); while (vitr != vidx.end() && vitr->account == current.id) { - adjust_validator_vote(get(vitr->witness),-current.witnesses_vote_weight); + adjust_validator_vote(get(vitr->witness),-current.validators_vote_weight); ++vitr; } @@ -7059,18 +7059,18 @@ namespace graphene { namespace chain { while(delete_itr10 != d10idx.end() && delete_itr10->account == current.id) { const auto &delete_current = *delete_itr10; - adjust_validator_vote(get(delete_itr10->witness),-current.witnesses_vote_weight); + adjust_validator_vote(get(delete_itr10->witness),-current.validators_vote_weight); modify(current, [&](account_object &a) { - a.witnesses_voted_for--; + a.validators_voted_for--; }); ++delete_itr10; remove(delete_current); } - //recalc witnesses_vote_weight (must be 0 after remove all witness votes from invalid account) - share_type current_fair_vote_weight = current.witness_vote_fair_weight(); + //recalc validators_vote_weight (must be 0 after remove all witness votes from invalid account) + share_type current_fair_vote_weight = current.validator_vote_fair_weight(); modify(current, [&](account_object &a) { - a.witnesses_vote_weight = current_fair_vote_weight; + a.validators_vote_weight = current_fair_vote_weight; }); //look witness object from invalid account @@ -7088,13 +7088,13 @@ namespace graphene { namespace chain { //remove invalid witness account from schedule const validator_schedule_object &wso = get_validator_schedule_object(); modify(wso, [&](validator_schedule_object &_wso) { - for (int i = 0; i < _wso.num_scheduled_validators; i+=CHAIN_BLOCK_WITNESS_REPEAT) { + for (int i = 0; i < _wso.num_scheduled_validators; i+=CHAIN_BLOCK_VALIDATOR_REPEAT) { if(_wso.current_shuffled_validators[i] == invalid_witness->owner){ _wso.current_shuffled_validators[i] = account_name_type(); } } }); - //recalc witnesses_vote_weight from all votes to invalid witness account (remove votes to invalid witness account) + //recalc validators_vote_weight from all votes to invalid witness account (remove votes to invalid witness account) const auto &vidx = get_index().indices().get(); auto vitr = vidx.lower_bound(boost::make_tuple(invalid_witness->id, account_id_type())); while (vitr != vidx.end() && vitr->witness == invalid_witness->id) { @@ -7102,25 +7102,25 @@ namespace graphene { namespace chain { const auto &vidx2 = get_index().indices().get(); auto vitr2 = vidx2.lower_bound(boost::make_tuple(voter_account.id, validator_id_type())); while (vitr2 != vidx2.end() && vitr2->account == voter_account.id) { - adjust_validator_vote(get(vitr2->witness), -voter_account.witnesses_vote_weight); + adjust_validator_vote(get(vitr2->witness), -voter_account.validators_vote_weight); ++vitr2; } remove(*vitr); modify(voter_account, [&](account_object &a) { - a.witnesses_voted_for--; + a.validators_voted_for--; }); - share_type fair_vote_weight = voter_account.witness_vote_fair_weight(); + share_type fair_vote_weight = voter_account.validator_vote_fair_weight(); modify(voter_account, [&](account_object &a) { - a.witnesses_vote_weight = fair_vote_weight; + a.validators_vote_weight = fair_vote_weight; }); const auto &vidx3 = get_index().indices().get(); auto vitr3 = vidx3.lower_bound(boost::make_tuple(voter_account.id, validator_id_type())); while (vitr3 != vidx3.end() && vitr3->account == voter_account.id) { - adjust_validator_vote(get(vitr3->witness), voter_account.witnesses_vote_weight); + adjust_validator_vote(get(vitr3->witness), voter_account.validators_vote_weight); ++vitr3; } ++vitr; @@ -7359,7 +7359,7 @@ namespace graphene { namespace chain { ++delete_itr8; remove(delete_current); } - //recalc witnesses_vote_weight from all votes to invalid witness account (remove votes to invalid witness account) + //recalc validators_vote_weight from all votes to invalid witness account (remove votes to invalid witness account) const auto &vidx = get_index().indices().get(); auto vitr = vidx.lower_bound(boost::make_tuple(current.id, account_id_type())); while (vitr != vidx.end() && vitr->witness == current.id) { @@ -7367,26 +7367,26 @@ namespace graphene { namespace chain { const auto &vidx2 = get_index().indices().get(); auto vitr2 = vidx2.lower_bound(boost::make_tuple(voter_account.id, validator_id_type())); while (vitr2 != vidx2.end() && vitr2->account == voter_account.id) { - adjust_validator_vote(get(vitr2->witness), -voter_account.witnesses_vote_weight); + adjust_validator_vote(get(vitr2->witness), -voter_account.validators_vote_weight); ++vitr2; } remove(*vitr); modify(voter_account, [&](account_object &a) { - a.witnesses_voted_for--; + a.validators_voted_for--; a.valid=false; }); - share_type fair_vote_weight = voter_account.witness_vote_fair_weight(); + share_type fair_vote_weight = voter_account.validator_vote_fair_weight(); modify(voter_account, [&](account_object &a) { - a.witnesses_vote_weight = fair_vote_weight; + a.validators_vote_weight = fair_vote_weight; }); const auto &vidx3 = get_index().indices().get(); auto vitr3 = vidx3.lower_bound(boost::make_tuple(voter_account.id, validator_id_type())); while (vitr3 != vidx3.end() && vitr3->account == voter_account.id) { - adjust_validator_vote(get(vitr3->witness), voter_account.witnesses_vote_weight); + adjust_validator_vote(get(vitr3->witness), voter_account.validators_vote_weight); ++vitr3; } ++vitr; diff --git a/libraries/chain/include/graphene/chain/account_object.hpp b/libraries/chain/include/graphene/chain/account_object.hpp index dcdf2cef16..5e493a3d80 100644 --- a/libraries/chain/include/graphene/chain/account_object.hpp +++ b/libraries/chain/include/graphene/chain/account_object.hpp @@ -68,8 +68,8 @@ class account_object fc::array proxied_vsf_votes;// = std::vector( CHAIN_MAX_PROXY_RECURSION_DEPTH, 0 ); ///< the total VFS votes proxied to this account - uint16_t witnesses_voted_for = 0; - share_type witnesses_vote_weight = 0; + uint16_t validators_voted_for = 0; + share_type validators_vote_weight = 0; time_point_sec last_root_post; time_point_sec last_post; @@ -78,27 +78,27 @@ class account_object share_type lifetime_bandwidth; time_point_sec last_bandwidth_update; - /// This function should be used only when the account votes for a witness directly - share_type witness_vote_weight() const { + /// This function should be used only when the account votes for a validator directly + share_type validator_vote_weight() const { return std::accumulate(proxied_vsf_votes.begin(), proxied_vsf_votes.end(), vesting_shares.amount); } - share_type witness_vote_fair_weight_prehf5() const { + share_type validator_vote_fair_weight_prehf5() const { share_type weight=0; - if(0 current_shuffled_witnesses; - fc::array current_shuffled_witnesses_validations; + fc::array current_shuffled_validators; + fc::array current_shuffled_validators_validations; }; typedef multi_index_container < validator_confirmation_object, @@ -221,5 +221,5 @@ FC_REFLECT((graphene::chain::award_shares_expire_object), CHAINBASE_SET_INDEX_TYPE(graphene::chain::award_shares_expire_object, graphene::chain::award_shares_expire_index) FC_REFLECT((graphene::chain::validator_confirmation_object), - (id)(block_num)(block_id)(current_shuffled_witnesses)(current_shuffled_witnesses_validations)) + (id)(block_num)(block_id)(current_shuffled_validators)(current_shuffled_validators_validations)) CHAINBASE_SET_INDEX_TYPE(graphene::chain::validator_confirmation_object, graphene::chain::validator_confirmation_index) diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index b8d5549b8e..e9e3cd59af 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -460,7 +460,7 @@ namespace graphene { namespace chain { /** clears all vote records for a particular account but does not update the * witness vote totals. Vote totals should be updated first via a call to - * adjust_proxied_validator_votes( a, -a.witness_vote_weight() ) + * adjust_proxied_validator_votes( a, -a.validator_vote_weight() ) */ void clear_validator_votes(const account_object &a); diff --git a/libraries/chain/include/graphene/chain/validator_objects.hpp b/libraries/chain/include/graphene/chain/validator_objects.hpp index 630c0a5284..df9349e86f 100644 --- a/libraries/chain/include/graphene/chain/validator_objects.hpp +++ b/libraries/chain/include/graphene/chain/validator_objects.hpp @@ -124,7 +124,7 @@ namespace graphene { namespace chain { fc::uint128_t current_virtual_time; uint32_t next_shuffle_block_num = 1; - fc::array current_shuffled_validators; + fc::array current_shuffled_validators; uint8_t num_scheduled_validators = 1; chain_properties median_props; version majority_version; @@ -175,7 +175,7 @@ namespace graphene { namespace chain { fc::uint128_t current_virtual_time; uint32_t next_shuffle_block_num = 1; - fc::array current_shuffled_validators; + fc::array current_shuffled_validators; uint8_t num_scheduled_validators = 1; chain_properties median_props; version majority_version; diff --git a/libraries/protocol/chain_operations.cpp b/libraries/protocol/chain_operations.cpp index 4023219641..06f5ac48de 100644 --- a/libraries/protocol/chain_operations.cpp +++ b/libraries/protocol/chain_operations.cpp @@ -183,7 +183,7 @@ namespace graphene { namespace protocol { void validator_update_operation::validate() const { validate_account_name(owner); FC_ASSERT(url.size() > 0, "URL size must be greater than 0"); - FC_ASSERT(url.size() < CHAIN_MAX_WITNESS_URL_LENGTH, "URL size must be lesser than CHAIN_MAX_WITNESS_URL_LENGTH"); + FC_ASSERT(url.size() < CHAIN_MAX_VALIDATOR_URL_LENGTH, "URL size must be lesser than CHAIN_MAX_VALIDATOR_URL_LENGTH"); FC_ASSERT(fc::is_utf8(url), "URL is not valid UTF8"); } diff --git a/libraries/protocol/get_config.cpp b/libraries/protocol/get_config.cpp index 1ee7695aff..db9ff351b9 100644 --- a/libraries/protocol/get_config.cpp +++ b/libraries/protocol/get_config.cpp @@ -26,7 +26,7 @@ namespace graphene { result["CHAIN_BLOCKS_PER_DAY"] = CHAIN_BLOCKS_PER_DAY; result["CHAIN_BLOCKS_PER_YEAR"] = CHAIN_BLOCKS_PER_YEAR; result["CHAIN_ID"] = CHAIN_ID; - result["CHAIN_HARDFORK_REQUIRED_WITNESSES"] = CHAIN_HARDFORK_REQUIRED_WITNESSES; + result["CHAIN_HARDFORK_REQUIRED_VALIDATORS"] = CHAIN_HARDFORK_REQUIRED_VALIDATORS; result["CHAIN_INITIATOR_NAME"] = CHAIN_INITIATOR_NAME; result["CHAIN_INITIATOR_PUBLIC_KEY_STR"] = CHAIN_INITIATOR_PUBLIC_KEY_STR; result["CHAIN_INIT_SUPPLY"] = CHAIN_INIT_SUPPLY; @@ -35,23 +35,23 @@ namespace graphene { result["CHAIN_IRREVERSIBLE_THRESHOLD"] = CHAIN_IRREVERSIBLE_THRESHOLD; result["CHAIN_IRREVERSIBLE_SUPPORT_MIN_RUN"] = CHAIN_IRREVERSIBLE_SUPPORT_MIN_RUN; result["CHAIN_MAX_ACCOUNT_NAME_LENGTH"] = CHAIN_MAX_ACCOUNT_NAME_LENGTH; - result["CHAIN_MAX_ACCOUNT_WITNESS_VOTES"] = CHAIN_MAX_ACCOUNT_WITNESS_VOTES; + result["CHAIN_MAX_ACCOUNT_VALIDATOR_VOTES"] = CHAIN_MAX_ACCOUNT_VALIDATOR_VOTES; result["CHAIN_BLOCK_SIZE"] = CHAIN_BLOCK_SIZE; result["CHAIN_MAX_COMMENT_DEPTH"] = CHAIN_MAX_COMMENT_DEPTH; result["CHAIN_MAX_MEMO_LENGTH"] = CHAIN_MAX_MEMO_LENGTH; - result["CHAIN_MAX_WITNESSES"] = CHAIN_MAX_WITNESSES; + result["CHAIN_MAX_VALIDATORS"] = CHAIN_MAX_VALIDATORS; result["CHAIN_MAX_PROXY_RECURSION_DEPTH"] = CHAIN_MAX_PROXY_RECURSION_DEPTH; result["CHAIN_MAX_RESERVE_RATIO"] = CHAIN_MAX_RESERVE_RATIO; - result["CHAIN_MAX_SUPPORT_WITNESSES"] = CHAIN_MAX_SUPPORT_WITNESSES; + result["CHAIN_MAX_SUPPORT_VALIDATORS"] = CHAIN_MAX_SUPPORT_VALIDATORS; result["CHAIN_MAX_SHARE_SUPPLY"] = CHAIN_MAX_SHARE_SUPPLY; result["CHAIN_MAX_SIG_CHECK_DEPTH"] = CHAIN_MAX_SIG_CHECK_DEPTH; result["CHAIN_MAX_TIME_UNTIL_EXPIRATION"] = CHAIN_MAX_TIME_UNTIL_EXPIRATION; result["CHAIN_MAX_TRANSACTION_SIZE"] = CHAIN_MAX_TRANSACTION_SIZE; result["CHAIN_MAX_UNDO_HISTORY"] = CHAIN_MAX_UNDO_HISTORY; result["CHAIN_MAX_VOTE_CHANGES"] = CHAIN_MAX_VOTE_CHANGES; - result["CHAIN_MAX_TOP_WITNESSES"] = CHAIN_MAX_TOP_WITNESSES; + result["CHAIN_MAX_TOP_VALIDATORS"] = CHAIN_MAX_TOP_VALIDATORS; result["CHAIN_MAX_WITHDRAW_ROUTES"] = CHAIN_MAX_WITHDRAW_ROUTES; - result["CHAIN_MAX_WITNESS_URL_LENGTH"] = CHAIN_MAX_WITNESS_URL_LENGTH; + result["CHAIN_MAX_VALIDATOR_URL_LENGTH"] = CHAIN_MAX_VALIDATOR_URL_LENGTH; result["CHAIN_MIN_ACCOUNT_CREATION_FEE"] = CHAIN_MIN_ACCOUNT_CREATION_FEE; result["CHAIN_MIN_ACCOUNT_NAME_LENGTH"] = CHAIN_MIN_ACCOUNT_NAME_LENGTH; result["CHAIN_MIN_BLOCK_SIZE_LIMIT"] = CHAIN_MIN_BLOCK_SIZE_LIMIT; diff --git a/libraries/protocol/include/graphene/protocol/chain_operations.hpp b/libraries/protocol/include/graphene/protocol/chain_operations.hpp index 7d66449c1e..7de8c40d89 100644 --- a/libraries/protocol/include/graphene/protocol/chain_operations.hpp +++ b/libraries/protocol/include/graphene/protocol/chain_operations.hpp @@ -485,12 +485,12 @@ namespace graphene { namespace protocol { /** * Consensus - Witness who missed the block will receive a penality of a percentage of the votes */ - int16_t validator_miss_penalty_percent = CONSENSUS_WITNESS_MISS_PENALTY_PERCENT; + int16_t validator_miss_penalty_percent = CONSENSUS_VALIDATOR_MISS_PENALTY_PERCENT; /** * Consensus - Witness who missed the block will receive a penality with duration (in seconds) */ - uint32_t validator_miss_penalty_duration = CONSENSUS_WITNESS_MISS_PENALTY_DURATION; + uint32_t validator_miss_penalty_duration = CONSENSUS_VALIDATOR_MISS_PENALTY_DURATION; void validate() const { chain_properties_hf4::validate(); @@ -543,7 +543,7 @@ namespace graphene { namespace protocol { /** * Consensus - Fee to the network committee for declare account as witness */ - asset validator_declaration_fee = asset(CONSENSUS_WITNESS_DECLARATION_FEE, TOKEN_SYMBOL); + asset validator_declaration_fee = asset(CONSENSUS_VALIDATOR_DECLARATION_FEE, TOKEN_SYMBOL); /** * Consensus - withdraw intervals (duration defined as CHAIN_VESTING_WITHDRAW_INTERVAL_SECONDS equal 1 day) diff --git a/libraries/protocol/include/graphene/protocol/config.hpp b/libraries/protocol/include/graphene/protocol/config.hpp index febccb8a31..dd0a1a9ed8 100644 --- a/libraries/protocol/include/graphene/protocol/config.hpp +++ b/libraries/protocol/include/graphene/protocol/config.hpp @@ -25,22 +25,22 @@ #define CHAIN_CLEAR_CLOSED_COMMITTEE_REQUEST_DELAY fc::days(7) #define CHAIN_BLOCK_INTERVAL 3 -#define CHAIN_BLOCK_WITNESS_REPEAT 1 +#define CHAIN_BLOCK_VALIDATOR_REPEAT 1 #define CHAIN_BLOCKS_PER_YEAR (365*24*60*60/CHAIN_BLOCK_INTERVAL) #define CHAIN_BLOCKS_PER_DAY (24*60*60/CHAIN_BLOCK_INTERVAL) #define CHAIN_BLOCKS_PER_HOUR (60*60/CHAIN_BLOCK_INTERVAL) -#define CHAIN_MAX_WITNESS_MISSED_BLOCKS 200 // ~10 min after first missed block for top witness -#define CHAIN_EMERGENCY_MAX_WITNESS_MISSED_BLOCKS (5 * CHAIN_MAX_WITNESSES) // in emergency mode, blank key after 5 full rounds of missed scheduled slots (~5 min) +#define CHAIN_MAX_VALIDATOR_MISSED_BLOCKS 200 // ~10 min after first missed block for top witness +#define CHAIN_EMERGENCY_MAX_VALIDATOR_MISSED_BLOCKS (5 * CHAIN_MAX_VALIDATORS) // in emergency mode, blank key after 5 full rounds of missed scheduled slots (~5 min) #define CHAIN_INITIATOR_NAME "viz" // Private key: 5JabcrvaLnBTCkCVFX5r4rmeGGfuJuVp4NAKRNLTey6pxhRQmf4 #define CHAIN_INITIATOR_PUBLIC_KEY_STR "VIZ6MyX5QiXAXRZk7SYCiqpi6Mtm8UbHWDFSV8HPpt7FJyahCnc2T" #define CHAIN_INITIATOR_PUBLIC_KEY (graphene::protocol::public_key_type(CHAIN_INITIATOR_PUBLIC_KEY_STR)) #define CHAIN_NUM_INITIATORS 0 -#define CHAIN_MAX_TOP_WITNESSES 11 -#define CHAIN_MAX_SUPPORT_WITNESSES 10 -#define CHAIN_MAX_WITNESSES (CHAIN_MAX_TOP_WITNESSES+CHAIN_MAX_SUPPORT_WITNESSES) /// 21 is more than enough -#define CHAIN_HARDFORK_REQUIRED_WITNESSES 17 // 17 of the 20 dpos witnesses (19 elected and 1 virtual time) required for hardfork. This guarantees 75% participation on all subsequent rounds. +#define CHAIN_MAX_TOP_VALIDATORS 11 +#define CHAIN_MAX_SUPPORT_VALIDATORS 10 +#define CHAIN_MAX_VALIDATORS (CHAIN_MAX_TOP_VALIDATORS+CHAIN_MAX_SUPPORT_VALIDATORS) /// 21 is more than enough +#define CHAIN_HARDFORK_REQUIRED_VALIDATORS 17 // 17 of the 20 dpos witnesses (19 elected and 1 virtual time) required for hardfork. This guarantees 75% participation on all subsequent rounds. #define CHAIN_MAX_BLOCK_POST_VALIDATION_COUNT 20 #define CHAIN_MAX_TIME_UNTIL_EXPIRATION (60*60) // seconds, aka: 1 hour #define CHAIN_MAX_PROXY_RECURSION_DEPTH 4 @@ -52,8 +52,8 @@ #define CHAIN_MAX_COMMENT_BENEFICIARIES 64 #define CHAIN_VOTE_ENERGY_RATE 1 -#define CHAIN_MAX_ACCOUNT_WITNESS_VOTES_PRE_HF4 2 -#define CHAIN_MAX_ACCOUNT_WITNESS_VOTES 100 +#define CHAIN_MAX_ACCOUNT_VALIDATOR_VOTES_PRE_HF4 2 +#define CHAIN_MAX_ACCOUNT_VALIDATOR_VOTES 100 #define CHAIN_100_PERCENT 10000 #define CHAIN_1_PERCENT (CHAIN_100_PERCENT/100) @@ -65,7 +65,7 @@ #define CHAIN_COMMITTEE_FUND_PERCENT (35*CHAIN_1_PERCENT) //35% of inflation #define CHAIN_DIGITAL_ASSET_ISSUED_PER_BLOCK int64_t(1000) //1.000 viz (new emission of digital asset per block) -#define CHAIN_CONSENSUS_INFLATION_WITNESS_PERCENT (20*CHAIN_1_PERCENT) //20% of inflation +#define CHAIN_CONSENSUS_INFLATION_VALIDATOR_PERCENT (20*CHAIN_1_PERCENT) //20% of inflation #define CHAIN_CONSENSUS_INFLATION_RATIO (50*CHAIN_1_PERCENT) //default: 50% of inflation minus witness percent (80% split between committee and reward fund) #define CHAIN_CONSENSUS_INFLATION_RECALC_PERIOD (28*CHAIN_BLOCKS_PER_DAY) //default: period for recalc inflation medians to global properties @@ -93,7 +93,7 @@ #define CHAIN_MAX_URL_LENGTH 256 #define CHAIN_MAX_MEMO_LENGTH 2048 -#define CHAIN_MAX_WITNESS_URL_LENGTH 2048 +#define CHAIN_MAX_VALIDATOR_URL_LENGTH 2048 #define CHAIN_INIT_SUPPLY int64_t(50000000000) #define CHAIN_MAX_SHARE_SUPPLY int64_t(1000000000000000ll) @@ -113,11 +113,11 @@ #define CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC 3600 // 1 hour /// The witness account name that produces blocks during emergency mode -#define CHAIN_EMERGENCY_WITNESS_ACCOUNT CHAIN_COMMITTEE_ACCOUNT // "committee" +#define CHAIN_EMERGENCY_VALIDATOR_ACCOUNT CHAIN_COMMITTEE_ACCOUNT // "committee" /// The public key used to sign blocks during emergency mode -#define CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY_STR "VIZ75CRHVHPwYiUESy1bgN3KhVFbZCQQRA9jT6TnpzKAmpxMPD6Xv" -#define CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY (graphene::protocol::public_key_type(CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY_STR)) +#define CHAIN_EMERGENCY_VALIDATOR_PUBLIC_KEY_STR "VIZ75CRHVHPwYiUESy1bgN3KhVFbZCQQRA9jT6TnpzKAmpxMPD6Xv" +#define CHAIN_EMERGENCY_VALIDATOR_PUBLIC_KEY (graphene::protocol::public_key_type(CHAIN_EMERGENCY_VALIDATOR_PUBLIC_KEY_STR)) /// Number of consecutive blocks produced by the emergency witness that /// triggers automatic exit from emergency mode (witnesses have rejoined). @@ -140,12 +140,12 @@ #define CONSENSUS_VOTE_ACCOUNTING_MIN_RSHARES uint32_t(5000000) // default 0.5 SHARES equivalent #define CONSENSUS_COMMITTEE_REQUEST_APPROVE_MIN_PERCENT 1000 // default: 10.00% #define CONSENSUS_DATA_OPERATIONS_COST_ADDITIONAL_BANDWIDTH 0 -#define CONSENSUS_WITNESS_MISS_PENALTY_PERCENT 100 // default: 1.00% -#define CONSENSUS_WITNESS_MISS_PENALTY_DURATION (CHAIN_BLOCKS_PER_DAY*CHAIN_BLOCK_INTERVAL) // default: 1 day +#define CONSENSUS_VALIDATOR_MISS_PENALTY_PERCENT 100 // default: 1.00% +#define CONSENSUS_VALIDATOR_MISS_PENALTY_DURATION (CHAIN_BLOCKS_PER_DAY*CHAIN_BLOCK_INTERVAL) // default: 1 day #define CONSENSUS_CREATE_INVITE_MIN_BALANCE 10000 #define CONSENSUS_COMMITTEE_CREATE_REQUEST_FEE 100000 -#define CONSENSUS_WITNESS_DECLARATION_FEE 10000 +#define CONSENSUS_VALIDATOR_DECLARATION_FEE 10000 #define CONSENSUS_CREATE_PAID_SUBSCRIPTION_FEE 100000 #define CONSENSUS_ACCOUNT_ON_SALE_FEE 10000 #define CONSENSUS_SUBACCOUNT_ON_SALE_FEE 100000 diff --git a/libraries/protocol/include/graphene/protocol/config_testnet.hpp b/libraries/protocol/include/graphene/protocol/config_testnet.hpp index ed027b5da1..a50f63a9d8 100644 --- a/libraries/protocol/include/graphene/protocol/config_testnet.hpp +++ b/libraries/protocol/include/graphene/protocol/config_testnet.hpp @@ -25,21 +25,21 @@ #define CHAIN_CLEAR_CLOSED_COMMITTEE_REQUEST_DELAY fc::minutes(10) #define CHAIN_BLOCK_INTERVAL 3 -#define CHAIN_BLOCK_WITNESS_REPEAT 1 +#define CHAIN_BLOCK_VALIDATOR_REPEAT 1 #define CHAIN_BLOCKS_PER_YEAR (365*24*60*60/CHAIN_BLOCK_INTERVAL) #define CHAIN_BLOCKS_PER_DAY (24*60*60/CHAIN_BLOCK_INTERVAL) #define CHAIN_BLOCKS_PER_HOUR (60*60/CHAIN_BLOCK_INTERVAL) -#define CHAIN_MAX_WITNESS_MISSED_BLOCKS 200 // ~10 min after first missed block for top witness +#define CHAIN_MAX_VALIDATOR_MISSED_BLOCKS 200 // ~10 min after first missed block for top witness #define CHAIN_INITIATOR_NAME "viz" // Private key: 5JabcrvaLnBTCkCVFX5r4rmeGGfuJuVp4NAKRNLTey6pxhRQmf4 #define CHAIN_INITIATOR_PUBLIC_KEY_STR "VIZ6MyX5QiXAXRZk7SYCiqpi6Mtm8UbHWDFSV8HPpt7FJyahCnc2T" #define CHAIN_INITIATOR_PUBLIC_KEY (graphene::protocol::public_key_type(CHAIN_INITIATOR_PUBLIC_KEY_STR)) #define CHAIN_NUM_INITIATORS 0 -#define CHAIN_MAX_TOP_WITNESSES 1 -#define CHAIN_MAX_SUPPORT_WITNESSES 0 -#define CHAIN_MAX_WITNESSES (CHAIN_MAX_TOP_WITNESSES+CHAIN_MAX_SUPPORT_WITNESSES) -#define CHAIN_HARDFORK_REQUIRED_WITNESSES 1 +#define CHAIN_MAX_TOP_VALIDATORS 1 +#define CHAIN_MAX_SUPPORT_VALIDATORS 0 +#define CHAIN_MAX_VALIDATORS (CHAIN_MAX_TOP_VALIDATORS+CHAIN_MAX_SUPPORT_VALIDATORS) +#define CHAIN_HARDFORK_REQUIRED_VALIDATORS 1 #define CHAIN_MAX_BLOCK_POST_VALIDATION_COUNT 20 #define CHAIN_MAX_TIME_UNTIL_EXPIRATION (60*60) // seconds, aka: 1 hour #define CHAIN_MAX_PROXY_RECURSION_DEPTH 4 @@ -51,8 +51,8 @@ #define CHAIN_MAX_COMMENT_BENEFICIARIES 64 #define CHAIN_VOTE_ENERGY_RATE 1 -#define CHAIN_MAX_ACCOUNT_WITNESS_VOTES_PRE_HF4 2 -#define CHAIN_MAX_ACCOUNT_WITNESS_VOTES 100 +#define CHAIN_MAX_ACCOUNT_VALIDATOR_VOTES_PRE_HF4 2 +#define CHAIN_MAX_ACCOUNT_VALIDATOR_VOTES 100 #define CHAIN_100_PERCENT 10000 #define CHAIN_1_PERCENT (CHAIN_100_PERCENT/100) @@ -64,7 +64,7 @@ #define CHAIN_COMMITTEE_FUND_PERCENT (35*CHAIN_1_PERCENT) //35% of inflation #define CHAIN_DIGITAL_ASSET_ISSUED_PER_BLOCK int64_t(1000) //1.000 viz (new emission of digital asset per block) -#define CHAIN_CONSENSUS_INFLATION_WITNESS_PERCENT (20*CHAIN_1_PERCENT) //20% of inflation +#define CHAIN_CONSENSUS_INFLATION_VALIDATOR_PERCENT (20*CHAIN_1_PERCENT) //20% of inflation #define CHAIN_CONSENSUS_INFLATION_RATIO (50*CHAIN_1_PERCENT) //default: 50% of inflation minus witness percent (80% split between committee and reward fund) #define CHAIN_CONSENSUS_INFLATION_RECALC_PERIOD (28*CHAIN_BLOCKS_PER_DAY) //default: period for recalc inflation medians to global properties @@ -92,7 +92,7 @@ #define CHAIN_MAX_URL_LENGTH 256 #define CHAIN_MAX_MEMO_LENGTH 2048 -#define CHAIN_MAX_WITNESS_URL_LENGTH 2048 +#define CHAIN_MAX_VALIDATOR_URL_LENGTH 2048 #define CHAIN_INIT_SUPPLY int64_t(50000000000) #define CHAIN_MAX_SHARE_SUPPLY int64_t(1000000000000000ll) @@ -112,11 +112,11 @@ #define CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC 3600 // 1 hour /// The witness account name that produces blocks during emergency mode -#define CHAIN_EMERGENCY_WITNESS_ACCOUNT CHAIN_COMMITTEE_ACCOUNT // "committee" +#define CHAIN_EMERGENCY_VALIDATOR_ACCOUNT CHAIN_COMMITTEE_ACCOUNT // "committee" /// The public key used to sign blocks during emergency mode -#define CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY_STR "VIZ75CRHVHPwYiUESy1bgN3KhVFbZCQQRA9jT6TnpzKAmpxMPD6Xv" -#define CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY (graphene::protocol::public_key_type(CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY_STR)) +#define CHAIN_EMERGENCY_VALIDATOR_PUBLIC_KEY_STR "VIZ75CRHVHPwYiUESy1bgN3KhVFbZCQQRA9jT6TnpzKAmpxMPD6Xv" +#define CHAIN_EMERGENCY_VALIDATOR_PUBLIC_KEY (graphene::protocol::public_key_type(CHAIN_EMERGENCY_VALIDATOR_PUBLIC_KEY_STR)) /// Number of consecutive blocks produced by the emergency witness that /// triggers automatic exit from emergency mode (witnesses have rejoined). @@ -140,12 +140,12 @@ #define CONSENSUS_VOTE_ACCOUNTING_MIN_RSHARES uint32_t(5000000) // default 0.5 SHARES equivalent #define CONSENSUS_COMMITTEE_REQUEST_APPROVE_MIN_PERCENT 1000 // default: 10.00% #define CONSENSUS_DATA_OPERATIONS_COST_ADDITIONAL_BANDWIDTH 0 -#define CONSENSUS_WITNESS_MISS_PENALTY_PERCENT 100 // default: 1.00% -#define CONSENSUS_WITNESS_MISS_PENALTY_DURATION (CHAIN_BLOCKS_PER_DAY*CHAIN_BLOCK_INTERVAL) // default: 1 day +#define CONSENSUS_VALIDATOR_MISS_PENALTY_PERCENT 100 // default: 1.00% +#define CONSENSUS_VALIDATOR_MISS_PENALTY_DURATION (CHAIN_BLOCKS_PER_DAY*CHAIN_BLOCK_INTERVAL) // default: 1 day #define CONSENSUS_CREATE_INVITE_MIN_BALANCE 10000 #define CONSENSUS_COMMITTEE_CREATE_REQUEST_FEE 100000 -#define CONSENSUS_WITNESS_DECLARATION_FEE 10000 +#define CONSENSUS_VALIDATOR_DECLARATION_FEE 10000 #define CONSENSUS_CREATE_PAID_SUBSCRIPTION_FEE 100000 #define CONSENSUS_ACCOUNT_ON_SALE_FEE 10000 #define CONSENSUS_SUBACCOUNT_ON_SALE_FEE 100000 diff --git a/plugins/custom_protocol_api/custom_protocol_api.cpp b/plugins/custom_protocol_api/custom_protocol_api.cpp index 0577c4e9a0..5d1605910e 100644 --- a/plugins/custom_protocol_api/custom_protocol_api.cpp +++ b/plugins/custom_protocol_api/custom_protocol_api.cpp @@ -126,7 +126,7 @@ namespace graphene { namespace plugins { namespace custom_protocol_api { auto vitr = vidx.lower_bound(boost::make_tuple(itr->id, validator_id_type())); while (vitr != vidx.end() && vitr->account == itr->id) { - result.witness_votes.insert(db.get(vitr->witness).owner); + result.validator_votes.insert(db.get(vitr->witness).owner); ++vitr; } diff --git a/plugins/database_api/api.cpp b/plugins/database_api/api.cpp index d3620348bc..bc349a9b99 100755 --- a/plugins/database_api/api.cpp +++ b/plugins/database_api/api.cpp @@ -385,7 +385,7 @@ std::vector plugin::api_impl::get_accounts(std::vectorid, validator_id_type())); while (vitr != vidx.end() && vitr->account == itr->id) { - results.back().witness_votes.insert(_db.get(vitr->witness).owner); + results.back().validator_votes.insert(_db.get(vitr->witness).owner); ++vitr; } } diff --git a/plugins/snapshot/plugin.cpp b/plugins/snapshot/plugin.cpp index 1bad00c67d..6bfb9f077a 100644 --- a/plugins/snapshot/plugin.cpp +++ b/plugins/snapshot/plugin.cpp @@ -221,6 +221,9 @@ inline uint32_t import_accounts( db.create([&](account_object& obj) { fc::from_variant(v, obj); + // backward compat: old snapshots used "witnesses_voted_for" + if (obj.validators_voted_for == 0 && v.get_object().contains("witnesses_voted_for")) + obj.validators_voted_for = v["witnesses_voted_for"].as(); }); ++count; } @@ -513,7 +516,16 @@ inline uint32_t import_block_post_validations( mutable_idx.set_next_id(validator_confirmation_object_id_type(id_val)); db.create([&](validator_confirmation_object& obj) { - fc::from_variant(v, obj); + // backward compat: old snapshots used current_shuffled_witnesses[_validations] + if (v.get_object().contains("current_shuffled_witnesses") && + !v.get_object().contains("current_shuffled_validators")) { + fc::mutable_variant_object mv(v.get_object()); + mv.set("current_shuffled_validators", v["current_shuffled_witnesses"]); + mv.set("current_shuffled_validators_validations", v["current_shuffled_witnesses_validations"]); + fc::from_variant(fc::variant(mv), obj); + } else { + fc::from_variant(v, obj); + } }); ++count; } diff --git a/plugins/validator/validator.cpp b/plugins/validator/validator.cpp index 4ed23720be..82432a3850 100644 --- a/plugins/validator/validator.cpp +++ b/plugins/validator/validator.cpp @@ -308,7 +308,7 @@ namespace graphene { } // Add the committee account to our witness set so we produce blocks // when the schedule assigns committee slots during emergency mode - pimpl->_witnesses.insert(CHAIN_EMERGENCY_WITNESS_ACCOUNT); + pimpl->_witnesses.insert(CHAIN_EMERGENCY_VALIDATOR_ACCOUNT); ilog("Emergency private key loaded. Will produce blocks during emergency consensus mode."); } @@ -508,9 +508,9 @@ namespace graphene { } // Condition 1: we hold the emergency-private-key. - // CHAIN_EMERGENCY_WITNESS_ACCOUNT is added to _witnesses only + // CHAIN_EMERGENCY_VALIDATOR_ACCOUNT is added to _witnesses only // when --emergency-private-key is configured (see plugin_initialize). - if (pimpl->_witnesses.find(CHAIN_EMERGENCY_WITNESS_ACCOUNT) == pimpl->_witnesses.end()) { + if (pimpl->_witnesses.find(CHAIN_EMERGENCY_VALIDATOR_ACCOUNT) == pimpl->_witnesses.end()) { return false; } @@ -518,8 +518,8 @@ namespace graphene { auto& db = pimpl->database(); return db.with_weak_read_lock([&]() -> bool { const validator_schedule_object& wso = db.get_validator_schedule_object(); - for (int i = 0; i < wso.num_scheduled_validators; i += CHAIN_BLOCK_WITNESS_REPEAT) { - if (wso.current_shuffled_validators[i] == CHAIN_EMERGENCY_WITNESS_ACCOUNT) { + for (int i = 0; i < wso.num_scheduled_validators; i += CHAIN_BLOCK_VALIDATOR_REPEAT) { + if (wso.current_shuffled_validators[i] == CHAIN_EMERGENCY_VALIDATOR_ACCOUNT) { return true; } } @@ -538,9 +538,9 @@ namespace graphene { if (!pimpl || pimpl->_witnesses.empty()) { return false; } - // CHAIN_EMERGENCY_WITNESS_ACCOUNT is added to _witnesses only + // CHAIN_EMERGENCY_VALIDATOR_ACCOUNT is added to _witnesses only // when --emergency-private-key is configured (see plugin_initialize). - return pimpl->_witnesses.find(CHAIN_EMERGENCY_WITNESS_ACCOUNT) != pimpl->_witnesses.end(); + return pimpl->_witnesses.find(CHAIN_EMERGENCY_VALIDATOR_ACCOUNT) != pimpl->_witnesses.end(); } catch (...) { return false; } @@ -1070,7 +1070,7 @@ namespace graphene { if (!_minority_fork_recovering && !_witnesses.empty()) { if (dgp_watch.emergency_consensus_active) { // Emergency mode: should produce if we have emergency key - should_be_producing = (_witnesses.count(CHAIN_EMERGENCY_WITNESS_ACCOUNT) > 0); + should_be_producing = (_witnesses.count(CHAIN_EMERGENCY_VALIDATOR_ACCOUNT) > 0); } else { // Normal mode: should produce if participation is healthy uint32_t prate_watch = database().witness_participation_rate(); @@ -1081,7 +1081,7 @@ namespace graphene { if (should_be_producing) { auto silent_for = fc::time_point::now() - _last_production_time; - bool is_emrg_master = _witnesses.count(CHAIN_EMERGENCY_WITNESS_ACCOUNT) > 0; + bool is_emrg_master = _witnesses.count(CHAIN_EMERGENCY_VALIDATOR_ACCOUNT) > 0; int64_t threshold_us = is_emrg_master ? 60000000 : 180000000; if (silent_for.count() > threshold_us) { // === AUTO-ENABLE DEBUG LOGGING ON FIRST WATCHDOG FIRE === @@ -1302,7 +1302,7 @@ namespace graphene { if (db._dlt_mode && chain().is_syncing()) { bool we_are_emergency_master = dgp.emergency_consensus_active && - _witnesses.find(CHAIN_EMERGENCY_WITNESS_ACCOUNT) != _witnesses.end(); + _witnesses.find(CHAIN_EMERGENCY_VALIDATOR_ACCOUNT) != _witnesses.end(); if (!we_are_emergency_master) { return block_validation_condition::not_synced; } @@ -1356,7 +1356,7 @@ namespace graphene { // producing on a stale head creates double-production // collisions and minority forks (p32.log). bool we_are_emergency_master = - _witnesses.find(CHAIN_EMERGENCY_WITNESS_ACCOUNT) != _witnesses.end(); + _witnesses.find(CHAIN_EMERGENCY_VALIDATOR_ACCOUNT) != _witnesses.end(); if (!we_are_emergency_master && _witnesses.empty()) { elog("EMERGENCY MODE ACTIVE but no witnesses configured! " "Block production impossible. Add --emergency-private-key to config."); @@ -1419,7 +1419,7 @@ namespace graphene { // bandwidth and CPU. const validator_schedule_object &wso = db.get_validator_schedule_object(); std::set scheduled_witnesses_set; - for (int i = 0; i < wso.num_scheduled_validators; i += CHAIN_BLOCK_WITNESS_REPEAT) { + for (int i = 0; i < wso.num_scheduled_validators; i += CHAIN_BLOCK_VALIDATOR_REPEAT) { if (wso.current_shuffled_validators[i] != account_name_type()) { scheduled_witnesses_set.insert(wso.current_shuffled_validators[i]); } @@ -1482,7 +1482,7 @@ namespace graphene { if (db._debug_block_production) ilog("DEBUG_CRASH: block_post_validation done, entering minority fork detection"); // === MINORITY FORK DETECTION === - // If the last CHAIN_MAX_WITNESSES (21) blocks in fork_db were ALL + // If the last CHAIN_MAX_VALIDATORS (21) blocks in fork_db were ALL // produced by our own configured witnesses, we are likely stuck on // a minority fork where no external witnesses are participating. // @@ -1505,7 +1505,7 @@ namespace graphene { uint32_t blocks_checked = 0; auto current = fork_head; - while (current && blocks_checked < CHAIN_MAX_WITNESSES) { + while (current && blocks_checked < CHAIN_MAX_VALIDATORS) { if (_witnesses.find(current->data.validator) == _witnesses.end()) { all_ours = false; break; @@ -1514,7 +1514,7 @@ namespace graphene { current = current->prev.lock(); } - if (all_ours && blocks_checked >= CHAIN_MAX_WITNESSES) { + if (all_ours && blocks_checked >= CHAIN_MAX_VALIDATORS) { if (_production_skip_flags & graphene::chain::database::skip_undo_history_check) { // enable-stale-production=true: operator override, continue dlog("Minority fork detected (last ${n} blocks from our witnesses) " @@ -1555,7 +1555,7 @@ namespace graphene { // our witnesses unless we're isolated from the master. This matches // the standard non-emergency minority fork threshold. // - // IMPORTANT: If committee (CHAIN_EMERGENCY_WITNESS_ACCOUNT) is in the + // IMPORTANT: If committee (CHAIN_EMERGENCY_VALIDATOR_ACCOUNT) is in the // current witness schedule AND we have its key (emergency-private-key // configured), this node IS the emergency master. All blocks being // "ours" is expected — other nodes sync from us. Skip minority fork @@ -1570,10 +1570,10 @@ namespace graphene { // (b) we have its key (committee is in _witnesses only when // emergency-private-key was configured — see plugin_initialize). bool we_are_master = false; - if (_witnesses.find(CHAIN_EMERGENCY_WITNESS_ACCOUNT) != _witnesses.end()) { + if (_witnesses.find(CHAIN_EMERGENCY_VALIDATOR_ACCOUNT) != _witnesses.end()) { const validator_schedule_object &wso = db.get_validator_schedule_object(); - for (int i = 0; i < wso.num_scheduled_validators; i += CHAIN_BLOCK_WITNESS_REPEAT) { - if (wso.current_shuffled_validators[i] == CHAIN_EMERGENCY_WITNESS_ACCOUNT) { + for (int i = 0; i < wso.num_scheduled_validators; i += CHAIN_BLOCK_VALIDATOR_REPEAT) { + if (wso.current_shuffled_validators[i] == CHAIN_EMERGENCY_VALIDATOR_ACCOUNT) { we_are_master = true; break; } @@ -1585,7 +1585,7 @@ namespace graphene { // the key. Run the existing fork_db isolation scan. auto fork_head = db.get_fork_db().head(); if (fork_head) { - const uint32_t dlt_minority_threshold = CHAIN_MAX_WITNESSES; // 21 blocks = 1 full round + const uint32_t dlt_minority_threshold = CHAIN_MAX_VALIDATORS; // 21 blocks = 1 full round bool all_ours = true; uint32_t blocks_checked = 0; auto current = fork_head; @@ -1662,7 +1662,7 @@ namespace graphene { // Emergency master diagnostic: log when we are stuck at slot=0 and // real time is well past the expected next slot (i.e. we should have // a slot available but get_slot_at_time says 0 — NTP or head-time anomaly) - if (_witnesses.find(CHAIN_EMERGENCY_WITNESS_ACCOUNT) != _witnesses.end()) { + if (_witnesses.find(CHAIN_EMERGENCY_VALIDATOR_ACCOUNT) != _witnesses.end()) { const auto &_dgp2 = db.get_dynamic_global_properties(); if (_dgp2.emergency_consensus_active) { static fc::time_point _last_slot0_log; @@ -1720,7 +1720,7 @@ namespace graphene { _last_scheduled_witness = scheduled_witness; // track for diagnostic // Emergency master diagnostic: log when committee is configured but // get_scheduled_validator returned a different name — reveals schedule misalignment - if (_witnesses.find(CHAIN_EMERGENCY_WITNESS_ACCOUNT) != _witnesses.end()) { + if (_witnesses.find(CHAIN_EMERGENCY_VALIDATOR_ACCOUNT) != _witnesses.end()) { const auto &_dgp3 = db.get_dynamic_global_properties(); if (_dgp3.emergency_consensus_active) { static fc::time_point _last_nmt_log; @@ -1770,8 +1770,8 @@ namespace graphene { // Check if witness has zero/null signing key (intentionally disabled for block production) if (scheduled_key == graphene::protocol::public_key_type()) { - if (scheduled_witness == CHAIN_EMERGENCY_WITNESS_ACCOUNT && - _witnesses.find(CHAIN_EMERGENCY_WITNESS_ACCOUNT) != _witnesses.end()) { + if (scheduled_witness == CHAIN_EMERGENCY_VALIDATOR_ACCOUNT && + _witnesses.find(CHAIN_EMERGENCY_VALIDATOR_ACCOUNT) != _witnesses.end()) { static fc::time_point _last_zerokey_log; auto _now_zk = fc::time_point::now(); if ((_now_zk - _last_zerokey_log).count() > 3000000) { @@ -1966,7 +1966,7 @@ namespace graphene { } catch (...) {} if (db._debug_block_production) ilog("DEBUG_CRASH: calling generate_block for ${w}", ("w", scheduled_witness)); - if (scheduled_witness == CHAIN_EMERGENCY_WITNESS_ACCOUNT) { + if (scheduled_witness == CHAIN_EMERGENCY_VALIDATOR_ACCOUNT) { dlog("EMRG-DIAG producing: slot=${s} scheduled_time=${st} head=#${h} aslot=${a}", ("s", slot)("st", scheduled_time)("h", db.head_block_num()) ("a", db.get_dynamic_global_properties().current_aslot)); From 92404181d79d3be39d5b5aca5bd42d6ded4e0593 Mon Sep 17 00:00:00 2001 From: Anatoly Piskunov Date: Sun, 17 May 2026 13:27:30 +0400 Subject: [PATCH 14/29] fix missing testnet conts in config --- .../include/graphene/protocol/config_testnet.hpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/libraries/protocol/include/graphene/protocol/config_testnet.hpp b/libraries/protocol/include/graphene/protocol/config_testnet.hpp index a50f63a9d8..68d920b14f 100644 --- a/libraries/protocol/include/graphene/protocol/config_testnet.hpp +++ b/libraries/protocol/include/graphene/protocol/config_testnet.hpp @@ -180,5 +180,20 @@ #define CHAIN_ROOT_POST_PARENT (account_name_type()) ///@} +/// HF13: Validator reward sharing constants +#define CHAIN_VALIDATOR_MAX_SHARING_RATE CHAIN_100_PERCENT + +/// Default epoch length (1 hour in testnet for faster iteration). +#define CHAIN_DEFAULT_DISTRIBUTION_EPOCH_LENGTH CHAIN_BLOCKS_PER_HOUR + +/// Minimum epoch length: 1 block in testnet to allow any interval. +#define CHAIN_MIN_DISTRIBUTION_EPOCH_LENGTH uint32_t(1) + +/// Minimum TOKEN amount per stakeholder payout (1 atomic unit = 0.001 VIZ). +#define CHAIN_MIN_STAKEHOLDER_REWARD_PAYOUT int64_t(1) + +/// Chainbase schema version — must match CHAIN_SCHEMA_VERSION in config.hpp. +#define CHAIN_SCHEMA_VERSION uint32_t(13) + // Deprecated defines #define CHAIN_CASHOUT_WINDOW_SECONDS (60*60*24) // 1 day From aebfab4c645c8117775c0bb95f34a4d0fa6ef427 Mon Sep 17 00:00:00 2001 From: Anatoly Piskunov Date: Sun, 17 May 2026 14:26:45 +0400 Subject: [PATCH 15/29] fix missed define --- .../protocol/include/graphene/protocol/config_testnet.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/protocol/include/graphene/protocol/config_testnet.hpp b/libraries/protocol/include/graphene/protocol/config_testnet.hpp index 68d920b14f..a56805b578 100644 --- a/libraries/protocol/include/graphene/protocol/config_testnet.hpp +++ b/libraries/protocol/include/graphene/protocol/config_testnet.hpp @@ -29,7 +29,8 @@ #define CHAIN_BLOCKS_PER_YEAR (365*24*60*60/CHAIN_BLOCK_INTERVAL) #define CHAIN_BLOCKS_PER_DAY (24*60*60/CHAIN_BLOCK_INTERVAL) #define CHAIN_BLOCKS_PER_HOUR (60*60/CHAIN_BLOCK_INTERVAL) -#define CHAIN_MAX_VALIDATOR_MISSED_BLOCKS 200 // ~10 min after first missed block for top witness +#define CHAIN_MAX_VALIDATOR_MISSED_BLOCKS 200 // ~10 min after first missed block for top validator +#define CHAIN_EMERGENCY_MAX_VALIDATOR_MISSED_BLOCKS (5 * CHAIN_MAX_VALIDATORS) // in emergency mode, blank key after 5 full rounds of missed scheduled slots (~5 min) #define CHAIN_INITIATOR_NAME "viz" // Private key: 5JabcrvaLnBTCkCVFX5r4rmeGGfuJuVp4NAKRNLTey6pxhRQmf4 From 1b81c607b9db9980c19551ebaf16bea24283f501 Mon Sep 17 00:00:00 2001 From: Anatoly Piskunov Date: Sun, 17 May 2026 20:04:09 +0400 Subject: [PATCH 16/29] fix naming --- .../protocol/include/graphene/protocol/chain_operations.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/protocol/include/graphene/protocol/chain_operations.hpp b/libraries/protocol/include/graphene/protocol/chain_operations.hpp index 7de8c40d89..cefd8da403 100644 --- a/libraries/protocol/include/graphene/protocol/chain_operations.hpp +++ b/libraries/protocol/include/graphene/protocol/chain_operations.hpp @@ -446,7 +446,7 @@ namespace graphene { namespace protocol { /** * Consensus - Witness reward percent from block inflation */ - int16_t inflation_validator_percent = CHAIN_CONSENSUS_INFLATION_WITNESS_PERCENT; + int16_t inflation_validator_percent = CHAIN_CONSENSUS_INFLATION_VALIDATOR_PERCENT; /** * Consensus - Inflation ratio between committee and reward fund From aa1416be7d226f644e7b80776ce42e59de4e8c44 Mon Sep 17 00:00:00 2001 From: Anatoly Piskunov Date: Sun, 17 May 2026 20:10:50 +0400 Subject: [PATCH 17/29] fix naming --- libraries/chain/database.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/chain/database.cpp b/libraries/chain/database.cpp index 053e1d8b60..0375dbd6c5 100644 --- a/libraries/chain/database.cpp +++ b/libraries/chain/database.cpp @@ -2792,12 +2792,12 @@ namespace graphene { namespace chain { /// Add the highest voted witnesses flat_set selected_voted; - selected_voted.reserve(CHAIN_MAX_TOP_WITNESSES); + selected_voted.reserve(CHAIN_MAX_TOP_VALIDATORS); const auto &widx = get_index().indices().get(); for (auto itr = widx.begin(); itr != widx.end() && - selected_voted.size() < CHAIN_MAX_TOP_WITNESSES; + selected_voted.size() < CHAIN_MAX_TOP_VALIDATORS; ++itr) { if (itr->signing_key == public_key_type()) { continue; From 293833639053083349fd9047f499067eb579300a Mon Sep 17 00:00:00 2001 From: Anatoly Piskunov Date: Sun, 17 May 2026 20:27:57 +0400 Subject: [PATCH 18/29] fix witness naming in logs --- examples-plugins/debug_node/plugin.cpp | 2 +- libraries/chain/chain_evaluator.cpp | 12 +-- .../chain/chain_properties_evaluators.cpp | 2 +- libraries/chain/database.cpp | 68 +++++++------- libraries/network/dlt_p2p_node.cpp | 16 ++-- plugins/chain/plugin.cpp | 2 +- plugins/snapshot/plugin.cpp | 12 +-- plugins/validator/validator.cpp | 90 +++++++++---------- plugins/validator_api/plugin.cpp | 4 +- plugins/validator_guard/validator_guard.cpp | 32 +++---- 10 files changed, 120 insertions(+), 120 deletions(-) diff --git a/examples-plugins/debug_node/plugin.cpp b/examples-plugins/debug_node/plugin.cpp index a4d24eb317..9bbf14afd5 100644 --- a/examples-plugins/debug_node/plugin.cpp +++ b/examples-plugins/debug_node/plugin.cpp @@ -263,7 +263,7 @@ uint32_t plugin::plugin_impl::debug_generate_blocks( if( scheduled_key != debug_public_key ) { if( edit_if_needed ) { if( logging ) { - wlog( "Modified key for witness ${w}", ("w", scheduled_witness_name) ); + wlog( "Modified key for validator ${w}", ("w", scheduled_witness_name) ); } debug_update( [=]( graphene::chain::database& db ) { diff --git a/libraries/chain/chain_evaluator.cpp b/libraries/chain/chain_evaluator.cpp index 87473e1d29..132fd4d79a 100644 --- a/libraries/chain/chain_evaluator.cpp +++ b/libraries/chain/chain_evaluator.cpp @@ -1148,7 +1148,7 @@ namespace graphene { namespace chain { } else{ FC_ASSERT(voter.proxy.size() == - 0, "A proxy is currently set, please clear the proxy before voting for a witness."); + 0, "A proxy is currently set, please clear the proxy before voting for a validator."); } const auto &witness = _db.get_witness(o.validator); @@ -1157,15 +1157,15 @@ namespace graphene { namespace chain { auto itr = by_account_witness_idx.find(boost::make_tuple(voter.id, witness.id)); if (itr == by_account_witness_idx.end()) { - FC_ASSERT(o.approve, "Vote doesn't exist, user must indicate a desire to approve witness."); + FC_ASSERT(o.approve, "Vote doesn't exist, user must indicate a desire to approve validator."); if(_db.has_hardfork(CHAIN_HARDFORK_4)){ FC_ASSERT(voter.validators_voted_for < - CHAIN_MAX_ACCOUNT_VALIDATOR_VOTES, "Account has voted for too many witnesses."); + CHAIN_MAX_ACCOUNT_VALIDATOR_VOTES, "Account has voted for too many validators."); } else{ FC_ASSERT(voter.validators_voted_for < - CHAIN_MAX_ACCOUNT_VALIDATOR_VOTES_PRE_HF4, "Account has voted for too many witnesses."); // TODO: Remove after hardfork 2 + CHAIN_MAX_ACCOUNT_VALIDATOR_VOTES_PRE_HF4, "Account has voted for too many validators."); // TODO: Remove after hardfork 2 } if(_db.has_hardfork(CHAIN_HARDFORK_5)){ @@ -1234,7 +1234,7 @@ namespace graphene { namespace chain { }); } } else { - FC_ASSERT(!o.approve, "Vote currently exists, user must indicate a desire to reject witness."); + FC_ASSERT(!o.approve, "Vote currently exists, user must indicate a desire to reject validator."); if(_db.has_hardfork(CHAIN_HARDFORK_5)){ const auto &vidx = _db.get_index().indices().get(); @@ -1567,7 +1567,7 @@ namespace graphene { namespace chain { else // Empty string recovery account defaults to top witness FC_ASSERT( _db.get_index().indices().get().begin()->owner == - o.recovery_account, "Top witness must recover an account with no recovery partner."); + o.recovery_account, "Top validator must recover an account with no recovery partner."); const auto &recovery_request_idx = _db.get_index().indices().get(); auto request = recovery_request_idx.find(o.account_to_recover); diff --git a/libraries/chain/chain_properties_evaluators.cpp b/libraries/chain/chain_properties_evaluators.cpp index 701f71e7be..a71eeb848c 100644 --- a/libraries/chain/chain_properties_evaluators.cpp +++ b/libraries/chain/chain_properties_evaluators.cpp @@ -20,7 +20,7 @@ namespace graphene { namespace chain { const dynamic_global_property_object &dgp = _db.get_dynamic_global_properties(); FC_ASSERT(owner.balance >= - median_props.validator_declaration_fee, "Account does not have sufficient funds to declare himself as witness: required ${a}.",("a",median_props.validator_declaration_fee)); + median_props.validator_declaration_fee, "Account does not have sufficient funds to declare himself as validator: required ${a}.",("a",median_props.validator_declaration_fee)); _db.adjust_balance(owner, -median_props.validator_declaration_fee); _db.modify(dgp, [&](dynamic_global_property_object &dgp) { diff --git a/libraries/chain/database.cpp b/libraries/chain/database.cpp index 0375dbd6c5..1a2606d5f7 100644 --- a/libraries/chain/database.cpp +++ b/libraries/chain/database.cpp @@ -448,9 +448,9 @@ namespace graphene { namespace chain { } if (schedule_broken) { - wlog("EMERGENCY SCHEDULE RECOVERY: detected empty witness slots " + wlog("EMERGENCY SCHEDULE RECOVERY: detected empty validator slots " "in schedule at startup (head=${h}, emergency=${e}). " - "Filling all slots with committee witness.", + "Filling all slots with committee validator.", ("h", head_block_num())("e", startup_dgp.emergency_consensus_active)); with_strong_write_lock([&]() { @@ -1470,11 +1470,11 @@ namespace graphene { namespace chain { if (same_parent) { wlog("Block num collision at block ${n}: ${cnt} blocks with SAME parent " - "(possible double-production), time_delta=${td}s, witnesses: ${w}", + "(possible double-production), time_delta=${td}s, validators: ${w}", ("n", height)("cnt", blocks.size())("td", time_delta_sec)("w", witness_time_pairs)); } else { wlog("Block num collision at block ${n}: ${cnt} blocks with DIFFERENT parents " - "(fork from divergent chain tips), time_delta=${td}s, witnesses: ${w}", + "(fork from divergent chain tips), time_delta=${td}s, validators: ${w}", ("n", height)("cnt", blocks.size())("td", time_delta_sec)("w", witness_time_pairs)); } @@ -2190,7 +2190,7 @@ namespace graphene { namespace chain { const auto* witness_acct = find_account(witness_owner); if (!witness_acct) { auto& acc_idx = get_index().indices().get(); - elog("CRITICAL: Witness ${w} account object MISSING from database! " + elog("CRITICAL: Validator ${w} account object MISSING from database! " "This is impossible state - shared memory may be corrupted. " "signing_key=${k} total_missed=${m} penalty=${p} last_confirmed=${lc} " "account_index_size=${idx_size}", @@ -2199,7 +2199,7 @@ namespace graphene { namespace chain { ("lc", witness_obj.last_confirmed_block_num) ("idx_size", acc_idx.size())); FC_THROW_EXCEPTION(shared_memory_corruption_exception, - "CRITICAL: Witness ${w} account not found in database! Shared memory corruption suspected.", + "CRITICAL: Validator ${w} account not found in database! Shared memory corruption suspected.", ("w", witness_owner)); } @@ -2764,7 +2764,7 @@ namespace graphene { namespace chain { void database::update_witness_schedule() { if ((head_block_num() % ( CHAIN_MAX_VALIDATORS * CHAIN_BLOCK_VALIDATOR_REPEAT ) ) != 0) return; - if (_debug_block_production) ilog("DEBUG_CRASH: update_witness_schedule ENTER at block ${b}", ("b", head_block_num())); + if (_debug_block_production) ilog("DEBUG_CRASH: update_validator_schedule ENTER at block ${b}", ("b", head_block_num())); if(has_hardfork(CHAIN_HARDFORK_6)){//remove expired witness penalty const auto &idx = get_index().indices().get(); @@ -2865,8 +2865,8 @@ namespace graphene { namespace chain { reset_virtual_schedule_time(); } - FC_ASSERT( ( active_witnesses.size() + support_witnesses.size() ) <= CHAIN_MAX_VALIDATORS, "Number of active witnesses does cannot be more CHAIN_MAX_VALIDATORS", - ("active_witnesses.size()", active_witnesses.size())("support_witnesses.size()", support_witnesses.size())("CHAIN_MAX_VALIDATORS", CHAIN_MAX_VALIDATORS)); + FC_ASSERT( ( active_witnesses.size() + support_witnesses.size() ) <= CHAIN_MAX_VALIDATORS, "Number of active validators cannot be more than CHAIN_MAX_VALIDATORS", + ("active_validators.size()", active_witnesses.size())("support_validators.size()", support_witnesses.size())("CHAIN_MAX_VALIDATORS", CHAIN_MAX_VALIDATORS)); auto majority_version = wso.majority_version; @@ -3069,7 +3069,7 @@ namespace graphene { namespace chain { _wso.next_shuffle_block_num = head_block_num() + _wso.num_scheduled_validators; - dlog(DB_LOG_YELLOW "Emergency hybrid schedule: ${r} real witness slots, " + dlog(DB_LOG_YELLOW "Emergency hybrid schedule: ${r} real validator slots, " "${c} committee slots" DB_LOG_RESET, ("r", real_witness_slots) ("c", committee_slots)); @@ -3110,7 +3110,7 @@ namespace graphene { namespace chain { _fork_db.set_emergency_mode(false); ilog("EMERGENCY CONSENSUS MODE deactivated at block ${b}. " - "${r} real witnesses active (threshold: ${t}).", + "${r} real validators active (threshold: ${t}).", ("b", head_block_num()) ("r", real_witness_slots) ("t", exit_threshold)); @@ -3784,7 +3784,7 @@ namespace graphene { namespace chain { const auto* witness_account = find_account(cwit.owner); if (!witness_account) { auto& acc_idx = get_index().indices().get(); - elog("CRITICAL: Witness ${w} account object MISSING from database! " + elog("CRITICAL: Validator ${w} account object MISSING from database! " "This is impossible state - shared memory may be corrupted. " "signing_key=${k} total_missed=${m} penalty=${p} last_confirmed=${lc} " "account_index_size=${idx_size}", @@ -3792,7 +3792,7 @@ namespace graphene { namespace chain { ("p", cwit.penalty_percent)("lc", cwit.last_confirmed_block_num) ("idx_size", acc_idx.size())); FC_THROW_EXCEPTION(shared_memory_corruption_exception, - "CRITICAL: Witness ${w} account not found in database! Shared memory corruption suspected.", + "CRITICAL: Validator ${w} account not found in database! Shared memory corruption suspected.", ("w", cwit.owner)); } if (has_hardfork(CHAIN_HARDFORK_13) && cwit.sharing_rate > 0) { @@ -3853,7 +3853,7 @@ namespace graphene { namespace chain { const auto* witness_account = find_account(cwit.owner); if (!witness_account) { auto& acc_idx = get_index().indices().get(); - elog("CRITICAL: Witness ${w} account object MISSING from database (HF4 path)! " + elog("CRITICAL: Validator ${w} account object MISSING from database (HF4 path)! " "This is impossible state - shared memory may be corrupted. " "signing_key=${k} total_missed=${m} penalty=${p} last_confirmed=${lc} " "account_index_size=${idx_size}", @@ -3861,7 +3861,7 @@ namespace graphene { namespace chain { ("p", cwit.penalty_percent)("lc", cwit.last_confirmed_block_num) ("idx_size", acc_idx.size())); FC_THROW_EXCEPTION(shared_memory_corruption_exception, - "CRITICAL: Witness ${w} account not found in database (HF4 path)! Shared memory corruption suspected.", + "CRITICAL: Validator ${w} account not found in database (HF4 path)! Shared memory corruption suspected.", ("w", cwit.owner)); } auto witness_reward_shares = create_vesting(*witness_account, asset(witness_reward, TOKEN_SYMBOL)); @@ -5100,8 +5100,8 @@ namespace graphene { namespace chain { const auto &witness = get_witness(next_block.validator); FC_ASSERT(witness.running_version >= hardfork_state.current_hardfork_version, - "Block produced by witness that is not running current hardfork", - ("witness", witness)("next_block.validator", next_block.validator)("hardfork_state", hardfork_state) + "Block produced by validator that is not running current hardfork", + ("validator", witness)("next_block.validator", next_block.validator)("hardfork_state", hardfork_state) ); for (const auto &trx : next_block.transactions) { @@ -5134,7 +5134,7 @@ namespace graphene { namespace chain { } update_bandwidth_reserve_candidates(); update_witness_schedule(); - if (_debug_block_production) ilog("DEBUG_CRASH: update_witness_schedule done"); + if (_debug_block_production) ilog("DEBUG_CRASH: update_validator_schedule done"); if (_debug_block_production) ilog("DEBUG_CRASH: process_funds start"); if(has_hardfork(CHAIN_HARDFORK_4)){ @@ -5315,8 +5315,8 @@ namespace graphene { namespace chain { if (!(skip & skip_validator_signature)) { FC_ASSERT(witness.signing_key != public_key_type(), - "Witness '${w}' has null signing key — cannot validate block #${n}. " - "The witness disabled their key or the node is on a different fork.", + "Validator '${w}' has null signing key — cannot validate block #${n}. " + "The validator disabled their key or the node is on a different fork.", ("w", next_block.validator)("n", next_block.block_num())); FC_ASSERT(next_block.validate_signee(witness.signing_key)); } @@ -5356,7 +5356,7 @@ namespace graphene { namespace chain { } } FC_ASSERT(in_schedule, - "Emergency mode: block from witness ${w} not in current schedule", + "Emergency mode: block from validator ${w} not in current schedule", ("w", witness.owner)); dlog("Emergency mode: accepting block from ${bw} at slot scheduled for ${sw} " "(slot_num=${slot}, block=#${num})", @@ -5365,8 +5365,8 @@ namespace graphene { namespace chain { } } else { FC_ASSERT(witness.owner == - scheduled_witness, "Witness produced block at wrong time", - ("block witness", next_block.validator)("scheduled", scheduled_witness)("slot_num", slot_num)); + scheduled_witness, "Validator produced block at wrong time", + ("block validator", next_block.validator)("scheduled", scheduled_witness)("slot_num", slot_num)); } } @@ -5411,7 +5411,7 @@ namespace graphene { namespace chain { witness_missed.owner != CHAIN_EMERGENCY_VALIDATOR_ACCOUNT; if (!is_emergency_offline_witness && witness_missed.owner != b.validator) { - ilog("\033[91mMissed block: witness ${w} did not produce block #${n} at ${t} (next: ${next})\033[0m", + ilog("\033[91mMissed block: validator ${w} did not produce block #${n} at ${t} (next: ${next})\033[0m", ("w", witness_missed.owner) ("n", head_block_num() + i + 1) ("t", get_slot_time(i + 1)) @@ -5439,7 +5439,7 @@ namespace graphene { namespace chain { head_block_num() - w.last_confirmed_block_num > CHAIN_EMERGENCY_MAX_VALIDATOR_MISSED_BLOCKS) { - elog("Emergency consensus: Witness ${w} missed ${missed} blocks since last confirmed ${lc} " + elog("Emergency consensus: Validator ${w} missed ${missed} blocks since last confirmed ${lc} " "(threshold=${t}), blanking signing_key", ("w", w.owner)("missed", head_block_num() - w.last_confirmed_block_num) ("lc", w.last_confirmed_block_num)("t", CHAIN_EMERGENCY_MAX_VALIDATOR_MISSED_BLOCKS)); @@ -5469,7 +5469,7 @@ namespace graphene { namespace chain { head_block_num() - w.last_confirmed_block_num > CHAIN_MAX_VALIDATOR_MISSED_BLOCKS) { - elog("Witness ${w} missed too many blocks (${missed} since last confirmed ${lc}), blanking signing_key (was ${k})", + elog("Validator ${w} missed too many blocks (${missed} since last confirmed ${lc}), blanking signing_key (was ${k})", ("w", w.owner)("missed", head_block_num() - w.last_confirmed_block_num) ("lc", w.last_confirmed_block_num)("k", w.signing_key)); w.signing_key = public_key_type(); @@ -5656,8 +5656,8 @@ namespace graphene { namespace chain { w.current_run = 0; }); } - elog("Emergency consensus started: blanked signing_key for ${n} witnesses. " - "Operators must send update_witness tx to re-enable after emergency ends.", + elog("Emergency consensus started: blanked signing_key for ${n} validators. " + "Operators must send update_validator tx to re-enable after emergency ends.", ("n", blanked_count)); // Remove all pending penalty expiration objects @@ -5688,7 +5688,7 @@ namespace graphene { namespace chain { ilog("EMERGENCY CONSENSUS MODE activated at block ${b}. " "No blocks for ${sec} seconds since LIB ${lib}. " - "Emergency witness: ${w}", + "Emergency validator: ${w}", ("b", b.block_num())("sec", seconds_since_lib) ("lib", _dgp.last_irreversible_block_num) ("w", CHAIN_EMERGENCY_VALIDATOR_ACCOUNT)); @@ -6730,7 +6730,7 @@ namespace graphene { namespace chain { itr != widx.end(); ++itr) { modify(*itr, [&](validator_object &w) { - elog("HF5 witness ${a} was votes: ${n}", ("a", w.owner)("n", w.votes)); + elog("HF5 validator ${a} was votes: ${n}", ("a", w.owner)("n", w.votes)); w.votes = 0; w.counted_votes = 0; }); @@ -6745,7 +6745,7 @@ namespace graphene { namespace chain { modify(voter, [&](account_object &a) { a.validators_vote_weight = fair_weight; }); - elog("HF5 witness ${a} calc votes: ${n}", ("a", witness.owner)("n", fair_weight)); + elog("HF5 validator ${a} calc votes: ${n}", ("a", witness.owner)("n", fair_weight)); adjust_validator_vote(get(witr->witness), fair_weight); } @@ -6882,7 +6882,7 @@ namespace graphene { namespace chain { itr != widx.end(); ++itr) { modify(*itr, [&](validator_object &w) { - elog("HF6 witness ${a} has votes: ${n}", ("a", w.owner)("n", w.votes)); + elog("HF6 validator ${a} has votes: ${n}", ("a", w.owner)("n", w.votes)); w.votes = 0; w.counted_votes = 0; }); @@ -6897,7 +6897,7 @@ namespace graphene { namespace chain { modify(voter, [&](account_object &a) { a.validators_vote_weight = fair_weight; }); - elog("HF6 witness ${a} recalc votes from ${a}: ${n}", ("a", witness.owner)("n", fair_weight)); + elog("HF6 validator ${a} recalc votes from ${a}: ${n}", ("a", witness.owner)("n", fair_weight)); adjust_validator_vote(get(witr->witness), fair_weight); } @@ -7391,7 +7391,7 @@ namespace graphene { namespace chain { } ++vitr; } - elog("HF9 remove empty/spam witness ${a}", ("a", current.owner)); + elog("HF9 remove empty/spam validator ${a}", ("a", current.owner)); remove(current); } } diff --git a/libraries/network/dlt_p2p_node.cpp b/libraries/network/dlt_p2p_node.cpp index 80809153f2..e2f04f6864 100644 --- a/libraries/network/dlt_p2p_node.cpp +++ b/libraries/network/dlt_p2p_node.cpp @@ -1338,7 +1338,7 @@ void dlt_p2p_node::on_dlt_block_range_reply(peer_id peer, const dlt_block_range_ _last_block_received_time = fc::time_point::now(); _last_network_block_time = fc::time_point::now(); - ilog(DLT_LOG_BWHITE "Got block #${n} with ${tx} transaction(s) by witness ${w} [${ep}]" DLT_LOG_RESET, + ilog(DLT_LOG_BWHITE "Got block #${n} with ${tx} transaction(s) by validator ${w} [${ep}]" DLT_LOG_RESET, ("n", block.block_num())("tx", block.transactions.size()) ("w", block.validator)("ep", state.endpoint)); @@ -1545,7 +1545,7 @@ void dlt_p2p_node::on_dlt_block_reply(peer_id peer, const dlt_block_reply_messag } if (_gap_rejected_count >= GAP_REJECT_MAX_RETRIES) { elog(DLT_LOG_RED "Block #${n} rejected ${c} times from peers — blacklisting gap fill for ${t}s " - "(witness key likely invalid on this fork)" DLT_LOG_RESET, + "(validator key likely invalid on this fork)" DLT_LOG_RESET, ("n", block_num)("c", _gap_rejected_count)("t", GAP_REJECT_BLACKLIST_SEC)); _gap_rejected_blacklist_until = fc::time_point::now() + fc::seconds(GAP_REJECT_BLACKLIST_SEC); _gap_rejected_count = 0; @@ -1565,7 +1565,7 @@ void dlt_p2p_node::on_dlt_block_reply(peer_id peer, const dlt_block_reply_messag } if (result == dlt_block_accept_result::ACCEPTED) { - ilog(DLT_LOG_BWHITE "Got block #${n} with ${tx} transaction(s) by witness ${w} [${ep}]" DLT_LOG_RESET, + ilog(DLT_LOG_BWHITE "Got block #${n} with ${tx} transaction(s) by validator ${w} [${ep}]" DLT_LOG_RESET, ("n", reply.block.block_num())("tx", reply.block.transactions.size()) ("w", reply.block.validator)("ep", state.endpoint)); @@ -1602,7 +1602,7 @@ void dlt_p2p_node::on_dlt_block_reply(peer_id peer, const dlt_block_reply_messag // FORK_DB_ONLY: block stored in fork_db but not applied to chain. // Do NOT call on_block_applied (which would corrupt mempool), // do NOT retransmit (block is not on our main chain). - dlog(DLT_LOG_ORANGE "Stored block #${n} by witness ${w} in fork_db (not applied, head=${h}) from ${ep}" DLT_LOG_RESET, + dlog(DLT_LOG_ORANGE "Stored block #${n} by validator ${w} in fork_db (not applied, head=${h}) from ${ep}" DLT_LOG_RESET, ("n", block_num)("w", reply.block.validator)("h", _delegate->get_head_block_num())("ep", state.endpoint)); } @@ -1911,7 +1911,7 @@ void dlt_p2p_node::on_dlt_gap_fill_reply(peer_id peer, const dlt_gap_fill_reply& _last_block_received_time = fc::time_point::now(); _last_network_block_time = fc::time_point::now(); - ilog(DLT_LOG_BWHITE "Got block #${n} with ${tx} transaction(s) by witness ${w} [${ep}] (gap fill)" DLT_LOG_RESET, + ilog(DLT_LOG_BWHITE "Got block #${n} with ${tx} transaction(s) by validator ${w} [${ep}] (gap fill)" DLT_LOG_RESET, ("n", block.block_num())("tx", block.transactions.size()) ("w", block.validator)("ep", it->second.endpoint)); @@ -1961,7 +1961,7 @@ void dlt_p2p_node::on_dlt_gap_fill_reply(peer_id peer, const dlt_gap_fill_reply& } if (_gap_rejected_count >= GAP_REJECT_MAX_RETRIES) { elog(DLT_LOG_RED "Gap fill: block #${n} rejected ${c} times — blacklisting for ${t}s " - "(witness key likely invalid on this fork)" DLT_LOG_RESET, + "(validator key likely invalid on this fork)" DLT_LOG_RESET, ("n", block.block_num())("c", _gap_rejected_count)("t", GAP_REJECT_BLACKLIST_SEC)); _gap_rejected_blacklist_until = fc::time_point::now() + fc::seconds(GAP_REJECT_BLACKLIST_SEC); _gap_rejected_count = 0; @@ -2308,7 +2308,7 @@ void dlt_p2p_node::drain_paused_block_queue() { _last_block_received_time = fc::time_point::now(); _last_network_block_time = fc::time_point::now(); - ilog(DLT_LOG_BWHITE "Got queued block #${n} with ${tx} transaction(s) by witness ${w}" DLT_LOG_RESET, + ilog(DLT_LOG_BWHITE "Got queued block #${n} with ${tx} transaction(s) by validator ${w}" DLT_LOG_RESET, ("n", block.block_num())("tx", block.transactions.size())("w", block.validator)); on_block_applied(block, /*caused_fork_switch=*/false); @@ -2389,7 +2389,7 @@ void dlt_p2p_node::transition_to_forward() { // transition, blocking witness production indefinitely. if (_catchup_after_pause) { _catchup_after_pause = false; - ilog(DLT_LOG_GREEN "Post-pause catchup complete, clearing flag (witness production may resume)" DLT_LOG_RESET); + ilog(DLT_LOG_GREEN "Post-pause catchup complete, clearing flag (validator production may resume)" DLT_LOG_RESET); } // Clear chain's currently_syncing flag so the witness plugin can produce. diff --git a/plugins/chain/plugin.cpp b/plugins/chain/plugin.cpp index 1d814ca4de..4ed946d25d 100644 --- a/plugins/chain/plugin.cpp +++ b/plugins/chain/plugin.cpp @@ -285,7 +285,7 @@ namespace chain { void plugin::clear_syncing() { if (my->currently_syncing.exchange(false, std::memory_order_relaxed)) { - ilog("Sync complete: cleared currently_syncing flag (witness block production may resume)"); + ilog("Sync complete: cleared currently_syncing flag (validator block production may resume)"); my->sync_start_logged = false; } } diff --git a/plugins/snapshot/plugin.cpp b/plugins/snapshot/plugin.cpp index 6bfb9f077a..d448664759 100644 --- a/plugins/snapshot/plugin.cpp +++ b/plugins/snapshot/plugin.cpp @@ -1736,7 +1736,7 @@ void snapshot_plugin::plugin_impl::on_applied_block(const graphene::protocol::si // read lock (the master generated blocks just 3ms after the // snapshot started), so cancellation is unnecessary. if (snapshot_in_progress.load(std::memory_order_relaxed)) { - dlog(SNAP_LOG_YELLOW "New block #${n} (witness ${w}) received while snapshot in progress \u2014 " + dlog(SNAP_LOG_YELLOW "New block #${n} (validator ${w}) received while snapshot in progress \u2014 " "block will be processed after snapshot completes" SNAP_LOG_RESET, ("n", b.block_num())("w", b.validator)); } @@ -1894,10 +1894,10 @@ void snapshot_plugin::plugin_impl::on_applied_block(const graphene::protocol::si snapshot_pending = false; pending_snapshot_path.clear(); pending_snapshot_safe_after_time = fc::time_point_sec(); - ilog(CLOG_GREEN "Creating deferred snapshot now (witness slot passed): ${p}" CLOG_RESET, ("p", output.string())); + ilog(CLOG_GREEN "Creating deferred snapshot now (validator slot passed): ${p}" CLOG_RESET, ("p", output.string())); schedule_async_snapshot(output, "deferred"); } else { - dlog("Deferred snapshot waiting for witness slot at ${t} (head_block_time=${h})", + dlog("Deferred snapshot waiting for validator slot at ${t} (head_block_time=${h})", ("t", pending_snapshot_safe_after_time)("h", db.head_block_time())); } } @@ -1911,7 +1911,7 @@ void snapshot_plugin::plugin_impl::on_applied_block(const graphene::protocol::si output = fc::path(snapshot_dir) / ("snapshot-block-" + std::to_string(block_num) + ".vizjson"); } if (is_witness_producing_soon()) { - ilog(CLOG_GREEN "Deferring snapshot-at-block ${b}: witness scheduled to produce next block" CLOG_RESET, ("b", block_num)); + ilog(CLOG_GREEN "Deferring snapshot-at-block ${b}: validator scheduled to produce next block" CLOG_RESET, ("b", block_num)); snapshot_pending = true; pending_snapshot_path = output.string(); } else { @@ -1928,7 +1928,7 @@ void snapshot_plugin::plugin_impl::on_applied_block(const graphene::protocol::si std::string dir = snapshot_dir; fc::path output = fc::path(dir) / ("snapshot-block-" + std::to_string(block_num) + ".vizjson"); if (is_witness_producing_soon()) { - ilog(CLOG_GREEN "Deferring urgent fresh snapshot at block ${b}: witness scheduled" CLOG_RESET, ("b", block_num)); + ilog(CLOG_GREEN "Deferring urgent fresh snapshot at block ${b}: validator scheduled" CLOG_RESET, ("b", block_num)); snapshot_pending = true; pending_snapshot_path = output.string(); } else { @@ -1942,7 +1942,7 @@ void snapshot_plugin::plugin_impl::on_applied_block(const graphene::protocol::si std::string dir = snapshot_dir; fc::path output = fc::path(dir) / ("snapshot-block-" + std::to_string(block_num) + ".vizjson"); if (is_witness_producing_soon()) { - ilog(CLOG_GREEN "Deferring periodic snapshot at block ${b}: witness scheduled to produce next block" CLOG_RESET, ("b", block_num)); + ilog(CLOG_GREEN "Deferring periodic snapshot at block ${b}: validator scheduled to produce next block" CLOG_RESET, ("b", block_num)); snapshot_pending = true; pending_snapshot_path = output.string(); } else { diff --git a/plugins/validator/validator.cpp b/plugins/validator/validator.cpp index 82432a3850..4bee3a0496 100644 --- a/plugins/validator/validator.cpp +++ b/plugins/validator/validator.cpp @@ -253,7 +253,7 @@ namespace graphene { "Minimum rejection threshold in milliseconds, applied regardless of the percentage rule (default: 5).") ("fork-collision-timeout-blocks", bpo::value()->default_value(21), "Number of consecutive fork-collision deferrals (block slots) before forcing production. " - "One full witness schedule round is 21 blocks (63 seconds). Default: 21.") + "One full validator schedule round is 21 blocks (63 seconds). Default: 21.") ("debug-block-production", bpo::value()->default_value(false), "Enable verbose debug logging for block production and chain internals. Default: false.") ; @@ -354,15 +354,15 @@ namespace graphene { // NTP on restart. graphene::time::update_ntp_time(); - // Log witness configuration for post-crash diagnostics - ilog("Witness config: ${n} witnesses, ${k} private keys", + // Log validator configuration for post-crash diagnostics + ilog("Validator config: ${n} validators, ${k} private keys", ("n", pimpl->_witnesses.size())("k", pimpl->_private_keys.size())); for (const auto& w : pimpl->_witnesses) { - ilog(" configured witness: ${w}", ("w", w)); + ilog(" configured validator: ${w}", ("w", w)); } if (!pimpl->_witnesses.empty()) { - ilog("Launching block production for ${n} witnesses.", ("n", pimpl->_witnesses.size())); + ilog("Launching block production for ${n} validators.", ("n", pimpl->_witnesses.size())); pimpl->p2p().set_block_production(true); // Connect to applied_block signal to detect missed slots @@ -379,7 +379,7 @@ namespace graphene { } pimpl->schedule_production_loop(); } else - elog("No witnesses configured! Please add witness names and private keys to configuration."); + elog("No validators configured! Please add validator names and private keys to configuration."); ilog("validator plugin: plugin_startup() end"); } FC_CAPTURE_AND_RETHROW() } @@ -548,7 +548,7 @@ namespace graphene { std::string validator_plugin::get_production_diagnostics() const { try { - if (!pimpl) return "witness=no_pimpl"; + if (!pimpl) return "validator=no_pimpl"; std::lock_guard lk(pimpl->_diag_mutex); std::string s = "prod_skip_flags="; s += std::to_string(pimpl->_production_skip_flags); @@ -565,9 +565,9 @@ namespace graphene { s += pimpl->_minority_fork_recovering ? "1" : "0"; s += " slot_hijacks="; s += std::to_string(pimpl->_slot_hijack_count); - return "witness[" + s + "]"; + return "validator[" + s + "]"; } catch (...) { - return "witness=err"; + return "validator=err"; } } @@ -612,7 +612,7 @@ namespace graphene { (_now_sj - _last_hijack_log).count() > 60000000) { _last_hijack_log = _now_sj; elog("SLOT-HIJACK: block #${bn} by '${wit}' but slot was assigned " - "to our witness '${exp}' (hijack #${cnt}). " + "to our validator '${exp}' (hijack #${cnt}). " "head=#${head} aslot=${aslot} num_sched=${nsched}", ("bn", block_num)("wit", block.validator)("exp", expected_witness) ("cnt", _slot_hijack_count) @@ -623,7 +623,7 @@ namespace graphene { } else if (was_our_slot && (block.validator == expected_witness || producer_is_ours)) { // Our witness (expected or another of ours) produced — reset hijack counter. if (_slot_hijack_count > 0) { - wlog("SLOT-HIJACK-RESOLVED: our witness '${wit}' produced " + wlog("SLOT-HIJACK-RESOLVED: our validator '${wit}' produced " "block #${bn} after ${cnt} hijacked slot(s).", ("wit", block.validator)("bn", block_num)("cnt", _slot_hijack_count)); } @@ -704,13 +704,13 @@ namespace graphene { } } - elog("MISSED-SLOT-OUR-WITNESS: block #${bn} arrived, ${missed} slot(s) missed between #${prev} and #${bn}. " - "Missed witnesses: [${mw}]. OUR witness was scheduled! " + elog("MISSED-SLOT-OUR-VALIDATOR: block #${bn} arrived, ${missed} slot(s) missed between #${prev} and #${bn}. " + "Missed validators: [${mw}]. OUR validator was scheduled! " "State: ever_produced=${ep} minority_recovering=${mr} " "last_slot_result=${sr} not_my_turn_streak=${nmts} slot0_streak=${sz} " "dlt_syncing=${ds} catching_up=${cu} head=#${h} aslot=${a} num_sched=${ns} " "ntp_offset=${ntp}us now=${now} next_slot_time=${nst} next_scheduled=${nsw} " - "witnesses=[${wn}] keys=[${ks}]", + "validators=[${wn}] keys=[${ks}]", ("bn", block_num)("missed", missed_count)("prev", prev_num) ("mw", missed_witnesses_list) ("ep", _ever_produced) @@ -861,9 +861,9 @@ namespace graphene { // ~125s with slot>0 but not our witness — investigate. // _not_my_turn_streak_start is production-only; no lock needed. auto elapsed = fc::time_point::now() - _not_my_turn_streak_start; - wlog("NOT_MY_TURN STREAK: ${n} consecutive slots (${s}s) went to other witnesses. " - "Last scheduled: ${sw}. Our witnesses: ${ow}. " - "Check: is our witness still in shuffled schedule?", + wlog("NOT_MY_TURN STREAK: ${n} consecutive slots (${s}s) went to other validators. " + "Last scheduled: ${sw}. Our validators: ${ow}. " + "Check: is our validator still in shuffled schedule?", ("n", _nmt_snap)("s", elapsed.count() / 1000000) ("sw", _last_scheduled_witness) ("ow", [_witnesses = _witnesses]() { @@ -913,7 +913,7 @@ namespace graphene { bool _ours3 = _witnesses.count(_next_w3) > 0; wlog("SLOT=0 STREAK: ${n} consecutive not_time_yet. " "now=${now} head_block_time=${hbt} (drift=${d}us) " - "next_slot=${nst} (gap=${g}ms) next_witness=${nw} is_ours=${o} head=#${h}.", + "next_slot=${nst} (gap=${g}ms) next_validator=${nw} is_ours=${o} head=#${h}.", ("n", _slot_zero_streak)("now", _now_init)("hbt", _hbt_init) ("d", _drift_us)("nst", _nst_init)("g", _gap_ms) ("nw", _next_w3)("o", _ours3) @@ -932,7 +932,7 @@ namespace graphene { wlog("slot=0 streak: ${n} consecutive not_time_yet (${s}s elapsed). " "now=${now}, head_block_time=${hbt}, next_slot_time=${nst}. " "Time to next slot: ${g}ms. NTP drift: ${d}us. " - "head_age=${ha}ms. next_witness=${nw} is_ours=${o}. Forcing NTP resync.", + "head_age=${ha}ms. next_validator=${nw} is_ours=${o}. Forcing NTP resync.", ("n", _slot_zero_streak)("s", (_now10 - _slot_zero_streak_start).count() / 1000000) ("now", _now10)("hbt", _hbt10)("nst", _nst10)("g", _gap_ms) ("d", _drift10)("ha", (_now10 - fc::time_point(_hbt10)).count() / 1000) @@ -955,7 +955,7 @@ namespace graphene { bool _ours60 = _witnesses.count(_next_w60) > 0; elog("SLOT=0 PROLONGED STALL: ${n} consecutive not_time_yet (${s}s). " "head_block_time=${hbt} is ${f}ms AHEAD of now=${now}! " - "next_slot_time=${nst} next_witness=${nw} is_ours=${o}, NTP drift=${d}us. " + "next_slot_time=${nst} next_validator=${nw} is_ours=${o}, NTP drift=${d}us. " "catching_up=${c}, dlt_syncing=${ds}, head=#${h}. " "Check: was a block with future timestamp applied?", ("n", _slot_zero_streak)("s", (_now60 - _slot_zero_streak_start).count() / 1000000) @@ -985,9 +985,9 @@ namespace graphene { std::string _next_w120 = database().get_scheduled_validator(1); bool _ours120 = _witnesses.count(_next_w120) > 0; elog("CRITICAL: slot=0 stall for ${s}s! head_block_time=${hbt} is ${f}ms in the future " - "relative to NTP time (now=${now}). next_slot_time=${nst} next_witness=${nw} is_ours=${o}, NTP drift=${d}us. " + "relative to NTP time (now=${now}). next_slot_time=${nst} next_validator=${nw} is_ours=${o}, NTP drift=${d}us. " "Network is stalled. catching_up=${c}, dlt_syncing=${ds}, head=#${h}. " - "shuffled_witnesses[0..2]=[${sw}]. " + "shuffled_validators[0..2]=[${sw}]. " "ACTION REQUIRED: Check NTP sync, system clock, or restart node.", ("s", (_now120 - _slot_zero_streak_start).count() / 1000000) ("hbt", _hbt120)("f", _future120_ms)("now", _now120)("nst", _nst120) @@ -997,11 +997,11 @@ namespace graphene { } break; case block_validation_condition::no_private_key: - ilog("Not producing block for ${scheduled_witness} because I don't have the private key for ${scheduled_key}", + ilog("Not producing block for ${scheduled_validator} because I don't have the private key for ${scheduled_key}", (capture)); break; case block_validation_condition::low_participation: - elog("Not producing block because node appears to be on a minority fork with only ${pct}% witness participation", + elog("Not producing block because node appears to be on a minority fork with only ${pct}% validator participation", (capture)); break; case block_validation_condition::lag: @@ -1015,7 +1015,7 @@ namespace graphene { _last_lag_slot_time = capture["scheduled_time"].as(); break; case block_validation_condition::consecutive: - elog("Not producing block because the last block was generated by the same witness.\nThis node is probably disconnected from the network so block production has been disabled.\nDisable this check with --allow-consecutive option."); + elog("Not producing block because the last block was generated by the same validator.\nThis node is probably disconnected from the network so block production has been disabled.\nDisable this check with --allow-consecutive option."); break; case block_validation_condition::exception_validating_block: elog("Failure when producing block with no transactions"); @@ -1153,14 +1153,14 @@ namespace graphene { uint32_t _shj_snap; { std::lock_guard lk(_diag_mutex); _shj_snap = _slot_hijack_count; } - elog("WITNESS-WATCHDOG: ${t} silent for ${s}s! " - "witnesses=${w} keys=${k} skip_flags=${sf} minority_recovering=${mr} " + elog("VALIDATOR-WATCHDOG: ${t} silent for ${s}s! " + "validators=${w} keys=${k} skip_flags=${sf} minority_recovering=${mr} " "slot_result=${sr} dlt_syncing=${ds} catching_up=${c} " "head=#${h} head_age=${ha}s scheduled_now=${sw} we_are_scheduled=${ws} " "in_schedule=${is}/${total} blanked_keys=[${bk}] " "slot0_streak=${sz} not_my_turn_streak=${nmt} last_scheduled=${nmtw} " "ntp_offset=${n}us slot_hijacks=${shj} debug_logging=${dl}", - ("t", is_emrg_master ? "emergency master" : "witness") + ("t", is_emrg_master ? "emergency master" : "validator") ("s", silent_for.count() / 1000000) ("w", witness_names) ("k", _private_keys.size()) @@ -1358,7 +1358,7 @@ namespace graphene { bool we_are_emergency_master = _witnesses.find(CHAIN_EMERGENCY_VALIDATOR_ACCOUNT) != _witnesses.end(); if (!we_are_emergency_master && _witnesses.empty()) { - elog("EMERGENCY MODE ACTIVE but no witnesses configured! " + elog("EMERGENCY MODE ACTIVE but no validators configured! " "Block production impossible. Add --emergency-private-key to config."); } } else { @@ -1380,7 +1380,7 @@ namespace graphene { // enable-stale-production=true: operator override, produce anyway // to bootstrap/recover a fully stalled network where all nodes // see low participation and would otherwise deadlock. - dlog("Witness participation is ${p}% but stale-production is enabled, " + dlog("Validator participation is ${p}% but stale-production is enabled, " "producing anyway to recover stalled network", ("p", uint32_t(prate / CHAIN_1_PERCENT))); } else { @@ -1410,7 +1410,7 @@ namespace graphene { if (db._debug_block_production) ilog("DEBUG_CRASH: emergency/participation check done, entering block_post_validation"); if(last_block_post_validation_time < now_fine ){ last_block_post_validation_time = now; - if (db._debug_block_production) ilog("DEBUG_CRASH: block_post_validation tick, iterating ${n} witnesses", ("n", _witnesses.size())); + if (db._debug_block_production) ilog("DEBUG_CRASH: block_post_validation tick, iterating ${n} validators", ("n", _witnesses.size())); // Pre-compute the current scheduled witnesses set so we can skip // configured witnesses that are not actually scheduled. A witness @@ -1442,7 +1442,7 @@ namespace graphene { const auto &witness_by_name = db.get_index().indices().get(); auto w_itr = witness_by_name.find(witness_account); if (w_itr == witness_by_name.end()) { - wlog("Witness ${w} not found in witness index, skipping block post validation", ("w", witness_account)); + wlog("Validator ${w} not found in validator index, skipping block post validation", ("w", witness_account)); continue; } graphene::protocol::public_key_type witness_pub_key = w_itr->signing_key; @@ -1517,12 +1517,12 @@ namespace graphene { if (all_ours && blocks_checked >= CHAIN_MAX_VALIDATORS) { if (_production_skip_flags & graphene::chain::database::skip_undo_history_check) { // enable-stale-production=true: operator override, continue - dlog("Minority fork detected (last ${n} blocks from our witnesses) " + dlog("Minority fork detected (last ${n} blocks from our validators) " "but stale production enabled, continuing", ("n", blocks_checked)); } else { // Wrong fork: trigger recovery - elog("MINORITY FORK DETECTED: last ${n} blocks all from our witnesses. " + elog("MINORITY FORK DETECTED: last ${n} blocks all from our validators. " "Resetting to LIB and resyncing from P2P network.", ("n", blocks_checked)); p2p().resync_from_lib(); @@ -1601,7 +1601,7 @@ namespace graphene { if (all_ours && blocks_checked >= dlt_minority_threshold) { elog("DLT EMERGENCY MINORITY FORK DETECTED: last ${n} blocks all from our " - "witnesses (1+ full rounds). Node is isolated from master. " + "validators (1+ full rounds). Node is isolated from master. " "Resetting to LIB and resyncing from P2P network.", ("n", blocks_checked)); p2p().resync_from_lib(true /*force_emergency*/); @@ -1643,11 +1643,11 @@ namespace graphene { std::string _wit_before = _slot_before > 0 ? db.get_scheduled_validator(_slot_before) : "none"; bool _our_slot_lost = _slot_before > 0 && _witnesses.count(_wit_before) > 0; if (_our_slot_lost) { - elog("WITNESS-SLOT-LOST: op_guard stall ${d}ms crossed slot boundary! " + elog("VALIDATOR-SLOT-LOST: op_guard stall ${d}ms crossed slot boundary! " "missed slot for ${w} — now points to next slot after refresh. head=#${h}", ("d", _guard_ms)("w", _wit_before)("h", db.head_block_num())); } else { - wlog("WITNESS-GUARD-STALL: op_guard blocked ${d}ms (slot before=${sb} witness=${w}). head=#${h}", + wlog("VALIDATOR-GUARD-STALL: op_guard blocked ${d}ms (slot before=${sb} validator=${w}). head=#${h}", ("d", _guard_ms)("sb", _slot_before)("w", _wit_before)("h", db.head_block_num())); } } @@ -1713,10 +1713,10 @@ namespace graphene { if (db._debug_block_production) ilog("DEBUG_CRASH: get_scheduled_validator(${s})", ("s", slot)); string scheduled_witness = db.get_scheduled_validator(slot); - if (db._debug_block_production) ilog("DEBUG_CRASH: scheduled_witness=${w}", ("w", scheduled_witness)); + if (db._debug_block_production) ilog("DEBUG_CRASH: scheduled_validator=${w}", ("w", scheduled_witness)); // we must control the witness scheduled to produce the next block. if (_witnesses.find(scheduled_witness) == _witnesses.end()) { - capture("scheduled_witness", scheduled_witness); + capture("scheduled_validator", scheduled_witness); _last_scheduled_witness = scheduled_witness; // track for diagnostic // Emergency master diagnostic: log when committee is configured but // get_scheduled_validator returned a different name — reveals schedule misalignment @@ -1741,10 +1741,10 @@ namespace graphene { return block_validation_condition::not_my_turn; } - if (db._debug_block_production) ilog("DEBUG_CRASH: looking up witness in index"); + if (db._debug_block_production) ilog("DEBUG_CRASH: looking up validator in index"); const auto &witness_by_name = db.get_index().indices().get(); auto itr = witness_by_name.find(scheduled_witness); - if (db._debug_block_production) ilog("DEBUG_CRASH: witness found=${f}", ("f", itr != witness_by_name.end())); + if (db._debug_block_production) ilog("DEBUG_CRASH: validator found=${f}", ("f", itr != witness_by_name.end())); fc::time_point_sec scheduled_time = db.get_slot_time(slot); graphene::protocol::public_key_type scheduled_key = itr->signing_key; @@ -1790,9 +1790,9 @@ namespace graphene { auto _now_zkr = fc::time_point::now(); if ((_now_zkr - _last_zerokey_regular_log).count() > 60000000) { _last_zerokey_regular_log = _now_zkr; - elog("Witness ${w} scheduled at slot=${s} but signing_key is ZERO on chain! " + elog("Validator ${w} scheduled at slot=${s} but signing_key is ZERO on chain! " "Key was blanked due to too many missed blocks. " - "Send update_witness transaction to re-enable. head=#${h}", + "Send update_validator transaction to re-enable. head=#${h}", ("w", scheduled_witness)("s", slot)("h", db.head_block_num())); } } @@ -1802,7 +1802,7 @@ namespace graphene { auto private_key_itr = _private_keys.find(scheduled_key); if (private_key_itr == _private_keys.end()) { - capture("scheduled_witness", scheduled_witness); + capture("scheduled_validator", scheduled_witness); capture("scheduled_key", scheduled_key); return block_validation_condition::no_private_key; } @@ -1812,7 +1812,7 @@ namespace graphene { uint32_t prate = db.witness_participation_rate(); if (prate < _required_witness_participation) { if (_production_skip_flags & graphene::chain::database::skip_undo_history_check) { - dlog("Witness participation is ${p}% but stale-production is enabled, " + dlog("Validator participation is ${p}% but stale-production is enabled, " "producing anyway to recover stalled network", ("p", uint32_t(prate / CHAIN_1_PERCENT))); } else { diff --git a/plugins/validator_api/plugin.cpp b/plugins/validator_api/plugin.cpp index 87374b241f..6b710173d7 100644 --- a/plugins/validator_api/plugin.cpp +++ b/plugins/validator_api/plugin.cpp @@ -113,7 +113,7 @@ std::vector plugin::validator_plugin_impl::get_witnesses_b auto itr = vote_idx.begin(); if (from.size()) { auto nameitr = name_idx.find(from); - FC_ASSERT(nameitr != name_idx.end(), "invalid witness name ${n}", ("n", from)); + FC_ASSERT(nameitr != name_idx.end(), "invalid validator name ${n}", ("n", from)); itr = vote_idx.iterator_to(*nameitr); } @@ -147,7 +147,7 @@ std::vector plugin::validator_plugin_impl::get_witnesses_b auto itr = vote_idx.begin(); if (from.size()) { auto nameitr = name_idx.find(from); - FC_ASSERT(nameitr != name_idx.end(), "invalid witness name ${n}", ("n", from)); + FC_ASSERT(nameitr != name_idx.end(), "invalid validator name ${n}", ("n", from)); itr = vote_idx.iterator_to(*nameitr); } diff --git a/plugins/validator_guard/validator_guard.cpp b/plugins/validator_guard/validator_guard.cpp index 4a959b2030..b0f905b8ba 100644 --- a/plugins/validator_guard/validator_guard.cpp +++ b/plugins/validator_guard/validator_guard.cpp @@ -155,7 +155,7 @@ bool validator_guard_plugin::impl::check_and_restore_internal() { auto itr = idx.find(name); if (itr == idx.end()) { - wlog("validator_guard: witness '${w}' not found in database", ("w", name)); + wlog("validator_guard: validator '${w}' not found in database", ("w", name)); continue; } @@ -227,7 +227,7 @@ void validator_guard_plugin::impl::send_witness_update( _pending_confirmations.clear(); } - ilog("validator_guard: broadcasting witness_update [ID: ${id}] for '${w}' — restoring key to ${k}", + ilog("validator_guard: broadcasting validator_update [ID: ${id}] for '${w}' — restoring key to ${k}", ("id", tx_id)("w", witness_name)("k", signing_pub)); p2p_.broadcast_transaction(tx); @@ -236,10 +236,10 @@ void validator_guard_plugin::impl::send_witness_update( _restore_pending[witness_name] = expiration; _pending_confirmations[tx_id] = { witness_name, expiration }; - ilog("validator_guard: witness_update for '${w}' sent successfully", ("w", witness_name)); + ilog("validator_guard: validator_update for '${w}' sent successfully", ("w", witness_name)); } catch (const fc::exception& e) { - elog("validator_guard: witness_update FAILED for '${w}': ${e}", + elog("validator_guard: validator_update FAILED for '${w}': ${e}", ("w", witness_name)("e", e.to_detail_string())); // Do not mark as pending — retry will happen on the next check cycle } @@ -277,7 +277,7 @@ void validator_guard_plugin::impl::send_witness_disable( tx.sign(active_priv, db().get_chain_id()); const auto tx_id = tx.id(); - ilog("validator_guard: broadcasting witness_update [ID: ${id}] for '${w}' — DISABLING (setting key to null)", + ilog("validator_guard: broadcasting validator_update [ID: ${id}] for '${w}' — DISABLING (setting key to null)", ("id", tx_id)("w", witness_name)); p2p_.broadcast_transaction(tx); @@ -285,10 +285,10 @@ void validator_guard_plugin::impl::send_witness_disable( // Mark this witness as auto-disabled so we don't auto-restore it _auto_disabled_witnesses.insert(witness_name); - ilog("validator_guard: witness_disable for '${w}' sent successfully", ("w", witness_name)); + ilog("validator_guard: validator_disable for '${w}' sent successfully", ("w", witness_name)); } catch (const fc::exception& e) { - elog("validator_guard: witness_disable FAILED for '${w}': ${e}", + elog("validator_guard: validator_disable FAILED for '${w}': ${e}", ("w", witness_name)("e", e.to_detail_string())); } } @@ -307,7 +307,7 @@ void validator_guard_plugin::set_program_options( bpo::value()->default_value(true), "Enable validator key auto-restore. " "When true, the plugin monitors configured validators and sends " - "witness_update if the on-chain signing key is reset to null.") + "validator_update if the on-chain signing key is reset to null.") ("witness-guard-enabled", // DEPRECATED alias bpo::value(), "[DEPRECATED] Use validator-guard-enabled.") @@ -387,10 +387,10 @@ void validator_guard_plugin::plugin_initialize( pimpl->_disable_threshold = options["validator-guard-disable"].as(); } if (pimpl->_disable_threshold > 0) { - ilog("validator_guard: auto-disable enabled — will disable witness after ${n} consecutive blocks", + ilog("validator_guard: auto-disable enabled — will disable validator after ${n} consecutive blocks", ("n", pimpl->_disable_threshold)); } else { - ilog("validator_guard: auto-disable feature is OFF (witness-guard-disable = 0)"); + ilog("validator_guard: auto-disable feature is OFF (validator-guard-disable = 0)"); } // validator configs — each entry is a JSON triplet: ["name", "signing_wif", "active_wif"] @@ -421,22 +421,22 @@ void validator_guard_plugin::plugin_initialize( pimpl->_witness_configs[name] = { *sign_priv, *active_priv }; - ilog("validator_guard: monitoring witness '${w}' (signing key: ${k})", + ilog("validator_guard: monitoring validator '${w}' (signing key: ${k})", ("w", name)("k", sign_priv->get_public_key())); } catch (const fc::exception& e) { - elog("validator_guard: failed to parse witness entry '${entry}': ${e}", + elog("validator_guard: failed to parse validator entry '${entry}': ${e}", ("entry", entry)("e", e.to_detail_string())); } } } if (pimpl->_witness_configs.empty()) { - wlog("validator_guard: no witnesses configured for monitoring"); + wlog("validator_guard: no validators configured for monitoring"); } ilog("validator_guard: plugin_initialize() end — " - "monitoring ${n} witness(es), interval=${i} blocks", + "monitoring ${n} validator(s), interval=${i} blocks", ("n", pimpl->_witness_configs.size())("i", pimpl->_check_interval)); } FC_LOG_AND_RETHROW() @@ -468,7 +468,7 @@ void validator_guard_plugin::plugin_startup() { } } if (!active_key_has_authority) { - elog("validator_guard: WARNING: Configured active key for witness '${w}' " + elog("validator_guard: WARNING: Configured active key for validator '${w}' " "does NOT have authority on-chain. Restoration will fail.", ("w", name)); } ++it; @@ -506,7 +506,7 @@ void validator_guard_plugin::plugin_startup() { if (count >= pimpl->_disable_threshold) { // Already auto-disabled? Skip repeated broadcasts if (!pimpl->_auto_disabled_witnesses.count(producer)) { - wlog("validator_guard: witness '${w}' produced ${c} consecutive blocks — " + wlog("validator_guard: validator '${w}' produced ${c} consecutive blocks — " "auto-disabling (threshold=${t})", ("w", producer)("c", count)("t", pimpl->_disable_threshold)); From 0a41045706354a268ae1d35ec2f2b0191dc72904 Mon Sep 17 00:00:00 2001 From: Anatoly Piskunov Date: Sun, 17 May 2026 21:18:09 +0400 Subject: [PATCH 19/29] update logs text --- libraries/network/dlt_p2p_node.cpp | 6 +++--- plugins/validator/validator.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/network/dlt_p2p_node.cpp b/libraries/network/dlt_p2p_node.cpp index e2f04f6864..861231e0bd 100644 --- a/libraries/network/dlt_p2p_node.cpp +++ b/libraries/network/dlt_p2p_node.cpp @@ -1338,7 +1338,7 @@ void dlt_p2p_node::on_dlt_block_range_reply(peer_id peer, const dlt_block_range_ _last_block_received_time = fc::time_point::now(); _last_network_block_time = fc::time_point::now(); - ilog(DLT_LOG_BWHITE "Got block #${n} with ${tx} transaction(s) by validator ${w} [${ep}]" DLT_LOG_RESET, + ilog(DLT_LOG_BWHITE "Got block #${n} with ${tx} transaction(s) by validator " DLT_LOG_YELLOW "${w}" DLT_LOG_BWHITE " [${ep}]" DLT_LOG_RESET, ("n", block.block_num())("tx", block.transactions.size()) ("w", block.validator)("ep", state.endpoint)); @@ -1565,7 +1565,7 @@ void dlt_p2p_node::on_dlt_block_reply(peer_id peer, const dlt_block_reply_messag } if (result == dlt_block_accept_result::ACCEPTED) { - ilog(DLT_LOG_BWHITE "Got block #${n} with ${tx} transaction(s) by validator ${w} [${ep}]" DLT_LOG_RESET, + ilog(DLT_LOG_BWHITE "Got block #${n} with ${tx} transaction(s) by validator " DLT_LOG_YELLOW "${w}" DLT_LOG_BWHITE " [${ep}]" DLT_LOG_RESET, ("n", reply.block.block_num())("tx", reply.block.transactions.size()) ("w", reply.block.validator)("ep", state.endpoint)); @@ -1911,7 +1911,7 @@ void dlt_p2p_node::on_dlt_gap_fill_reply(peer_id peer, const dlt_gap_fill_reply& _last_block_received_time = fc::time_point::now(); _last_network_block_time = fc::time_point::now(); - ilog(DLT_LOG_BWHITE "Got block #${n} with ${tx} transaction(s) by validator ${w} [${ep}] (gap fill)" DLT_LOG_RESET, + ilog(DLT_LOG_BWHITE "Got block #${n} with ${tx} transaction(s) by validator " DLT_LOG_YELLOW "${w}" DLT_LOG_BWHITE " [${ep}] (gap fill)" DLT_LOG_RESET, ("n", block.block_num())("tx", block.transactions.size()) ("w", block.validator)("ep", it->second.endpoint)); diff --git a/plugins/validator/validator.cpp b/plugins/validator/validator.cpp index 4bee3a0496..d37dac3045 100644 --- a/plugins/validator/validator.cpp +++ b/plugins/validator/validator.cpp @@ -781,7 +781,7 @@ namespace graphene { } switch (result) { case block_validation_condition::produced: - ilog("\033[92mGenerated block #${n} with timestamp ${t} at time ${c} by ${w} with ${tx} transactions\033[0m", (capture)); + ilog("\033[92mValidated block #${n} with timestamp ${t} at time ${c} by \033[93m${w}\033[92m with ${tx} transactions\033[0m", (capture)); fork_collision_defer_count_ = 0; // _watchdog_debug_enabled is production-only — no lock needed. if (_watchdog_debug_enabled) { From 3c8dd0ab51f435fd4c557ca0775afc89856327e3 Mon Sep 17 00:00:00 2001 From: Anatoly Piskunov Date: Mon, 18 May 2026 06:26:51 +0400 Subject: [PATCH 20/29] fix(chain): prevent exception propagation on invalid fork branch - Removed throwing exception for invalid forks in fork_db - Restored original head without triggering P2P error handling - Return false to signal block rejection without sync restarts - Allow natural network fork resolution to occur smoothly --- libraries/chain/database.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libraries/chain/database.cpp b/libraries/chain/database.cpp index 1a2606d5f7..9056ad0f1b 100644 --- a/libraries/chain/database.cpp +++ b/libraries/chain/database.cpp @@ -1956,7 +1956,14 @@ namespace graphene { namespace chain { ("h", head_block_num())); } - throw *except; + // The invalid branch has been removed from fork_db and + // the original head restored. Do not propagate the + // exception to the P2P layer — returning false signals + // "block rejected" without triggering sync restarts or + // peer backoff. Network fork resolution proceeds + // naturally: nodes on the invalid fork eventually switch + // to the valid chain when it becomes longer. + return false; } } From 05601b33e46cdd3f0855bcfce3c85172679f09c5 Mon Sep 17 00:00:00 2001 From: Anatoly Piskunov Date: Mon, 18 May 2026 06:33:52 +0400 Subject: [PATCH 21/29] fix(p2p): soft-ban peers that send only fork-db blocks with no progress (p77) When a peer consistently sends block ranges where every block goes to fork_db and none are applied, the node enters an infinite fetch loop: any_fork_db_only triggers request_blocks_from_peer(), which detects a large gap and re-requests from LIB, the peer replies with the same fork blocks, repeat forever. Add fork_only_batch_count to dlt_peer_state. After FORK_ONLY_BATCH_LIMIT (5) consecutive range replies with zero applied blocks, soft-ban the peer. The counter resets whenever any block is applied. Legitimate fork switches resolve within a few batches once the fork point is found and applied. --- libraries/network/dlt_p2p_node.cpp | 17 ++++++++++++++--- .../graphene/network/dlt_p2p_peer_state.hpp | 2 ++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/libraries/network/dlt_p2p_node.cpp b/libraries/network/dlt_p2p_node.cpp index 861231e0bd..63f2cafe85 100644 --- a/libraries/network/dlt_p2p_node.cpp +++ b/libraries/network/dlt_p2p_node.cpp @@ -1397,6 +1397,7 @@ void dlt_p2p_node::on_dlt_block_range_reply(peer_id peer, const dlt_block_range_ // (no applied, no fork_db) should NOT end sync mode. if (_node_status == DLT_NODE_STATUS_SYNC) { if (any_block_applied) { + state.fork_only_batch_count = 0; if (reply.is_last) { transition_to_forward(); } else if (reply.last_block_next_available > 0) { @@ -1407,9 +1408,19 @@ void dlt_p2p_node::on_dlt_block_range_reply(peer_id peer, const dlt_block_range_ // Blocks went to fork_db but weren't applied — competing fork. // Continue fetching so fork_db accumulates the full competing // chain and can evaluate a fork switch. - ilog(DLT_LOG_ORANGE "Range stored in fork_db only (competing fork?), continuing fetch from #${n}" DLT_LOG_RESET, - ("n", reply.last_block_next_available)); - request_blocks_from_peer(peer); + // P77 fix: if after FORK_ONLY_BATCH_LIMIT consecutive batches + // nothing is applied, this peer is on a permanently diverging + // fork — soft-ban to break the infinite fetch loop. + state.fork_only_batch_count++; + if (state.fork_only_batch_count >= dlt_peer_state::FORK_ONLY_BATCH_LIMIT) { + wlog(DLT_LOG_RED "Peer ${ep}: ${n} consecutive fork-only batches with no blocks applied — soft-banning" DLT_LOG_RESET, + ("ep", state.endpoint)("n", state.fork_only_batch_count)); + soft_ban_peer(peer, "fork-only spam: " + std::to_string(state.fork_only_batch_count) + " batches with no progress"); + } else { + ilog(DLT_LOG_ORANGE "Range stored in fork_db only (competing fork?), continuing fetch from #${n}" DLT_LOG_RESET, + ("n", reply.last_block_next_available)); + request_blocks_from_peer(peer); + } } } } diff --git a/libraries/network/include/graphene/network/dlt_p2p_peer_state.hpp b/libraries/network/include/graphene/network/dlt_p2p_peer_state.hpp index b22b9d8bd6..dcd843ced8 100644 --- a/libraries/network/include/graphene/network/dlt_p2p_peer_state.hpp +++ b/libraries/network/include/graphene/network/dlt_p2p_peer_state.hpp @@ -81,6 +81,8 @@ struct dlt_peer_state { uint32_t pending_sync_start = 0; // what block range we asked for uint32_t pending_sync_end = 0; bool range_fallback_mode = false; // single-block mode after range deserialization error + uint32_t fork_only_batch_count = 0; // consecutive range replies with no blocks applied + static constexpr uint32_t FORK_ONLY_BATCH_LIMIT = 5; // Block echo suppression: ring buffer of recent block IDs this peer // is known to have. Updated when we send a block to the peer or when From e22dcaa5cf6a04a6210c81076dd2d69275be0f42 Mon Sep 17 00:00:00 2001 From: Anatoly Piskunov Date: Mon, 18 May 2026 17:05:28 +0400 Subject: [PATCH 22/29] fix(network): clarify block validation log messages - Change log messages to specify blocks are "validated by" validators - Update multiple log instances for consistency and clarity - Include context for gap fill blocks in logs as well --- libraries/network/dlt_p2p_node.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/network/dlt_p2p_node.cpp b/libraries/network/dlt_p2p_node.cpp index 63f2cafe85..e18a58f394 100644 --- a/libraries/network/dlt_p2p_node.cpp +++ b/libraries/network/dlt_p2p_node.cpp @@ -1338,7 +1338,7 @@ void dlt_p2p_node::on_dlt_block_range_reply(peer_id peer, const dlt_block_range_ _last_block_received_time = fc::time_point::now(); _last_network_block_time = fc::time_point::now(); - ilog(DLT_LOG_BWHITE "Got block #${n} with ${tx} transaction(s) by validator " DLT_LOG_YELLOW "${w}" DLT_LOG_BWHITE " [${ep}]" DLT_LOG_RESET, + ilog(DLT_LOG_BWHITE "Got block #${n} with ${tx} transaction(s) validated by " DLT_LOG_YELLOW "${w}" DLT_LOG_BWHITE " [${ep}]" DLT_LOG_RESET, ("n", block.block_num())("tx", block.transactions.size()) ("w", block.validator)("ep", state.endpoint)); @@ -1576,7 +1576,7 @@ void dlt_p2p_node::on_dlt_block_reply(peer_id peer, const dlt_block_reply_messag } if (result == dlt_block_accept_result::ACCEPTED) { - ilog(DLT_LOG_BWHITE "Got block #${n} with ${tx} transaction(s) by validator " DLT_LOG_YELLOW "${w}" DLT_LOG_BWHITE " [${ep}]" DLT_LOG_RESET, + ilog(DLT_LOG_BWHITE "Got block #${n} with ${tx} transaction(s) validated by " DLT_LOG_YELLOW "${w}" DLT_LOG_BWHITE " [${ep}]" DLT_LOG_RESET, ("n", reply.block.block_num())("tx", reply.block.transactions.size()) ("w", reply.block.validator)("ep", state.endpoint)); @@ -1922,7 +1922,7 @@ void dlt_p2p_node::on_dlt_gap_fill_reply(peer_id peer, const dlt_gap_fill_reply& _last_block_received_time = fc::time_point::now(); _last_network_block_time = fc::time_point::now(); - ilog(DLT_LOG_BWHITE "Got block #${n} with ${tx} transaction(s) by validator " DLT_LOG_YELLOW "${w}" DLT_LOG_BWHITE " [${ep}] (gap fill)" DLT_LOG_RESET, + ilog(DLT_LOG_BWHITE "Got block #${n} with ${tx} transaction(s) validated by " DLT_LOG_YELLOW "${w}" DLT_LOG_BWHITE " [${ep}] (gap fill)" DLT_LOG_RESET, ("n", block.block_num())("tx", block.transactions.size()) ("w", block.validator)("ep", it->second.endpoint)); From eca9e3991c3481be75da17ff73ab50c5ac6cd5ff Mon Sep 17 00:00:00 2001 From: Anatoly Piskunov Date: Mon, 18 May 2026 17:14:50 +0400 Subject: [PATCH 23/29] docs(validator): rename witness to validator throughout documentation - Replace all occurrences of "witness" with "validator" in block log specification - Update block processing and plugin option parsing to reference validator nodes - Modify examples and references in chain properties and governance docs accordingly - Change CLI wallet command and operation sections for validator terminology - Adjust consensus emergency parameters content to use validator naming and context - Update data types document to refer to validator voting and rewards instead of witness - Ensure consistent use of validator terminology across all related documentation files --- .qoder/docs/block-processing.md | 34 +- .qoder/docs/chain-properties-governance.md | 80 +-- .qoder/docs/cli-wallet.md | 22 +- .qoder/docs/consensus-emergency-params.md | 64 +- .qoder/docs/data-types.md | 8 +- .../docs/dlt-4-node-code-audit-2026-05-08.md | 8 +- .qoder/docs/dlt-4-node-sync-scenarios.md | 16 +- .qoder/docs/dlt-forward-mode.md | 10 +- .../docs/dlt-p2p-network-redesign-review.md | 6 +- .qoder/docs/dlt-p2p-network-redesign.md | 18 +- .qoder/docs/dlt-p2p-stats-reference-ru.md | 4 +- .qoder/docs/dlt-p2p-stats-reference.md | 16 +- .qoder/docs/emergency-consensus-review.md | 208 +++--- .qoder/docs/emergency-consensus-workflow.md | 94 +-- .qoder/docs/expected-next-block.md | 8 +- .../docs/fork-collision-hardfork-proposal.md | 64 +- .qoder/docs/index.md | 16 +- .qoder/docs/op-recovery.md | 4 +- .../docs/{op-witness.md => op-validator.md} | 40 +- .qoder/docs/p2p-messages.md | 26 +- .qoder/docs/p2p-sync-workflow.md | 18 +- .qoder/docs/plugins.md | 96 +-- .qoder/docs/shared-memory.md | 32 +- .qoder/docs/snapshot-pause-workflow.md | 42 +- .qoder/docs/snapshot-plugin.md | 36 +- .qoder/docs/staking-and-dao-governance.md | 42 +- .../{witness-guard.md => validator-guard.md} | 68 +- ...alidator-plugin-refactoring-2026-05-14.md} | 28 +- ...{witness-plugin.md => validator-plugin.md} | 138 ++-- .qoder/docs/virtual-operations.md | 14 +- ...itness-to-validator-migration-reference.md | 42 +- .qoder/docs/witness-to-validator-rename.md | 56 +- .qoder/repowiki/en/content/API Reference.md | 6 +- .../Advanced Topics/Database Schema Design.md | 20 +- .../Advanced Topics/Hardfork Management.md | 84 +-- .../Architecture Overview.md | 8 +- .../Block Processing and Validation.md | 62 +- .../Chain Library/Chain Library.md | 30 +- .../Database Management.md | 208 +++--- .../Fork Resolution and Consensus.md | 138 ++-- .../Chain Library/Memory Management System.md | 6 +- .../Object Model and Persistence.md | 28 +- .../Chain Library/Transaction Processing.md | 8 +- .../Protocol Library/Block Structures.md | 56 +- .../Data Types and Serialization.md | 6 +- .../Protocol Library/Protocol Library.md | 18 +- .../Core Libraries/Wallet Library.md | 4 +- .../Block Processing and Validation.md | 80 +-- .../Data Flow and Processing.md | 6 +- .../Transaction Processing Pipeline.md | 4 +- .../Inter-Plugin Communication.md | 4 +- .../Plugin Architecture.md | 4 +- .../Plugin Lifecycle and Registration.md | 4 +- .qoder/repowiki/en/content/Chain Plugin.md | 4 +- .../Configuration Management.md | 62 +- .../Docker Configuration.md | 12 +- .../Network Configuration.md | 4 +- .../Node Configuration.md | 64 +- .../content/Contributing and Development.md | 4 +- .../content/Core Libraries/Core Libraries.md | 108 ++-- .../Emergency Consensus System.md | 124 ++-- .../NTP Synchronization System.md | 6 +- .../Cloud and Infrastructure.md | 18 +- .../Containerization and Docker.md | 8 +- .../Deployment and Operations.md | 38 +- .../Monitoring and Maintenance.md | 10 +- .../Node Deployment/Installation and Setup.md | 8 +- .../Node Deployment/Node Deployment.md | 36 +- .../Node Types and Configurations.md | 80 +-- .../Node Deployment/Security Hardening.md | 4 +- .../Node Deployment/Service Integration.md | 6 +- .../Build System/Build System.md | 10 +- .../CMake Configuration/Build Targets.md | 8 +- .../CMake Configuration.md | 4 +- .../Docker Integration/Docker Integration.md | 32 +- .../Production Dockerfile.md | 6 +- .../Docker Integration/Testnet Dockerfile.md | 12 +- .../Debugging Tools/Debug Node Plugin.md | 30 +- .../Debugging Tools/Debugging Tools.md | 30 +- .../Development Tools/Development Tools.md | 4 +- .../Development Tools/Development Workflow.md | 4 +- .qoder/repowiki/en/content/Getting Started.md | 26 +- .qoder/repowiki/en/content/P2p Plugin.md | 70 +-- .../en/content/Plugin System/Plugin System.md | 6 +- .../Plugin System/Snapshot Plugin System.md | 54 +- .../Plugin System/Witness Guard Plugin.md | 100 +-- .../repowiki/en/content/Project Overview.md | 58 +- .../en/content/{Witness.md => Validator.md} | 590 +++++++++--------- 88 files changed, 1891 insertions(+), 1891 deletions(-) rename .qoder/docs/{op-witness.md => op-validator.md} (79%) rename .qoder/docs/{witness-guard.md => validator-guard.md} (54%) rename .qoder/docs/{witness-plugin-refactoring-2026-05-14.md => validator-plugin-refactoring-2026-05-14.md} (89%) rename .qoder/docs/{witness-plugin.md => validator-plugin.md} (87%) rename .qoder/repowiki/en/content/{Witness.md => Validator.md} (65%) diff --git a/.qoder/docs/block-processing.md b/.qoder/docs/block-processing.md index b8effade91..a5a3161333 100644 --- a/.qoder/docs/block-processing.md +++ b/.qoder/docs/block-processing.md @@ -1,4 +1,4 @@ -# VIZ Blockchain — Block Processing & Pending Transactions +# VIZ Blockchain — Block Processing & Pending Transactions Internal mechanics of block application, transaction queuing, and the pending transaction lifecycle. @@ -122,7 +122,7 @@ if new_block.previous == head_block_id() fork_db.start_block(head_block) ← seeds fork_db with the head ``` -This also fixes **witness nodes that generate their own blocks**: `generate_block()` sets `pending_block.previous = head_block_id()`, and without the seed the self-generated block would fail to push into `fork_db`. +This also fixes **validator nodes that generate their own blocks**: `generate_block()` sets `pending_block.previous = head_block_id()`, and without the seed the self-generated block would fail to push into `fork_db`. --- @@ -294,9 +294,9 @@ Now the log only fires once, after both loops complete, with an accurate count o --- -## Bug Fix: Witness Plugin Option Parsing +## Bug Fix: Validator Plugin Option Parsing -Source: [witness.cpp](../../plugins/witness/witness.cpp) +Source: [validator.cpp](../../plugins/validator/validator.cpp) ### Bug 1: `enable-stale-production` with `implicit_value(false)` @@ -346,18 +346,18 @@ _required_witness_participation = options["required-participation"].as --- -## Witness Block Production Timing +## validator Block Production Timing -Source: [witness.cpp](../../plugins/witness/witness.cpp) +Source: [validator.cpp](../../plugins/validator/validator.cpp) ### Production Loop Mechanism -The witness plugin uses a timer-based production loop with a look-ahead to detect when it's time to produce a block: +The Validator Plugin uses a timer-based production loop with a look-ahead to detect when it's time to produce a block: 1. **Timer** fires every **250ms** (aligned to 250ms boundaries, minimum sleep 50ms) 2. On each tick, `maybe_produce_block()` computes `now = NTP_time + 250ms` (look-ahead) 3. `get_slot_at_time(now)` finds which slot corresponds to `now` -4. If the slot belongs to one of our witnesses and `|scheduled_time - now| <= 500ms`, the block is produced with `scheduled_time` as the timestamp +4. If the slot belongs to one of our validators and `|scheduled_time - now| <= 500ms`, the block is produced with `scheduled_time` as the timestamp The block timestamp is always the **deterministic slot time** (computed from `head_block_time` rounded to `CHAIN_BLOCK_INTERVAL` boundary + `slot_num × 3s`), never the current clock time. @@ -374,15 +374,15 @@ This gives a **500ms safety margin** against the LAG threshold, compared to 0ms ### Missed Block Behavior -When a witness misses their slot, the production loop does NOT wait or retry. The next tick simply finds a later slot: +When a validator misses their slot, the production loop does NOT wait or retry. The next tick simply finds a later slot: ``` -Slot T=3 missed (witness A absent): - Tick at T=3.000 → now=3.250 → slot=1 → witness A → not our witness → not_my_turn +Slot T=3 missed (validator A absent): + Tick at T=3.000 → now=3.250 → slot=1 → validator A → not our validator → not_my_turn (A's slot passes unclaimed) -Slot T=6 (witness B - our witness): - Tick at T=5.750 → now=6.000 → slot=2 → witness B → PRODUCE with timestamp T=6.000 +Slot T=6 (validator B - our validator): + Tick at T=5.750 → now=6.000 → slot=2 → validator B → PRODUCE with timestamp T=6.000 ``` When block at T=6 is pushed, `update_global_dynamic_data()` counts `missed_blocks = get_slot_at_time(6.000) - 1 = 1` and increments `current_aslot` accordingly. @@ -393,13 +393,13 @@ When block at T=6 is pushed, `update_global_dynamic_data()` counts `missed_block |---|---|---| | Sync status | Chain is not stale (or `enable-stale-production`) | `not_synced` | | Slot time | `get_slot_at_time(now) > 0` | `not_time_yet` | -| Witness ownership | Scheduled witness is in our `_witnesses` set | `not_my_turn` | -| Signing key | Witness has non-zero `signing_key` on chain | `not_my_turn` | +| validator ownership | Scheduled validator is in our `_witnesses` set | `not_my_turn` | +| Signing key | validator has non-zero `signing_key` on chain | `not_my_turn` | | Private key | We have the private key for the signing key | `no_private_key` | | Participation | Network participation ≥ required (pre-HF12 only) | `low_participation` | | Lag | `|scheduled_time - now| <= 500ms` | `lag` | | Fork collision | No competing block at same height in fork_db | `fork_collision` | -| Minority fork | Last 21 blocks NOT all from our own witnesses (or `enable-stale-production` or emergency mode) | `minority_fork` | +| Minority fork | Last 21 blocks NOT all from our own validators (or `enable-stale-production` or emergency mode) | `minority_fork` | --- @@ -410,4 +410,4 @@ When block at T=6 is pushed, `update_global_dynamic_data()` counts `missed_block | `CHAIN_PENDING_TRANSACTION_EXECUTION_LIMIT` | 200ms | Max time to spend re-applying pending txs after block push | | `CHAIN_BLOCK_GENERATION_POSTPONED_TX_LIMIT` | 5 | Max consecutive oversized txs to skip during block generation | | `CHAIN_BLOCK_SIZE` | 65536 bytes | Hard limit on block size | -| `maximum_block_size` | Dynamic (witness median) | Soft limit on block size | +| `maximum_block_size` | Dynamic (validator median) | Soft limit on block size | diff --git a/.qoder/docs/chain-properties-governance.md b/.qoder/docs/chain-properties-governance.md index 507903d43f..f147a95648 100644 --- a/.qoder/docs/chain-properties-governance.md +++ b/.qoder/docs/chain-properties-governance.md @@ -1,18 +1,18 @@ -# Chain Properties: How Witnesses Govern Network Parameters +# Chain Properties: How validators Govern Network Parameters ## Overview -In VIZ, there is no central authority that sets network fees, block sizes, inflation rates, or other critical parameters. Instead, **every active witness publishes their preferred values**, and the blockchain automatically calculates the **median** — the middle value that represents the consensus of all elected witnesses. +In VIZ, there is no central authority that sets network fees, block sizes, inflation rates, or other critical parameters. Instead, **every active validator publishes their preferred values**, and the blockchain automatically calculates the **median** — the middle value that represents the consensus of all elected validators. -Since witnesses are elected by stake-weighted voting from all SHARES holders, chain properties are ultimately governed by the community: users vote for witnesses whose parameter choices align with their vision of the public good. +Since validators are elected by stake-weighted voting from all SHARES holders, chain properties are ultimately governed by the community: users vote for validators whose parameter choices align with their vision of the public good. --- ## How It Works -### Step 1: Witnesses Publish Their Preferences +### Step 1: validators Publish Their Preferences -Each witness publishes their preferred chain properties using the `versioned_chain_properties_update_operation`: +Each validator publishes their preferred chain properties using the `versioned_chain_properties_update_operation`: ```json ["versioned_chain_properties_update", { @@ -51,14 +51,14 @@ The `[3, {...}]` format indicates the version — `3` means `chain_properties_hf ### Step 2: Median Calculation -Every time the witness schedule is updated, the blockchain runs `update_median_witness_props()`. For **each property independently**: +Every time the validator schedule is updated, the blockchain runs `update_median_witness_props()`. For **each property independently**: -1. Collect the property value from every active witness +1. Collect the property value from every active validator 2. Sort the values 3. Pick the **median** (the middle value) ``` -Example: 5 witnesses set account_creation_fee to: +Example: 5 validators set account_creation_fee to: 0.5 VIZ, 1.0 VIZ, 1.0 VIZ, 2.0 VIZ, 5.0 VIZ ↑ median = 1.0 VIZ @@ -66,7 +66,7 @@ Example: 5 witnesses set account_creation_fee to: The algorithm uses `std::nth_element` with position `active.size() / 2`, which selects the value at the middle index after partial sorting. -**Why median?** The median is resistant to extremes. A single witness cannot push a parameter to an absurdly high or low value — they can only shift the median by one position. To change a parameter significantly, a **majority of active witnesses** must agree. +**Why median?** The median is resistant to extremes. A single validator cannot push a parameter to an absurdly high or low value — they can only shift the median by one position. To change a parameter significantly, a **majority of active validators** must agree. ### Step 3: Application @@ -102,11 +102,11 @@ The calculated `median_props` is stored in the `witness_schedule_object` and use | Property | Type | Default | What It Controls | |---|---|---|---| -| `inflation_witness_percent` | int16 (bp) | 2000 (20%) | Witness share of block inflation | +| `inflation_witness_percent` | int16 (bp) | 2000 (20%) | validator share of block inflation | | `inflation_ratio_committee_vs_reward_fund` | int16 (bp) | 5000 (50%) | How remaining inflation is split between committee fund and reward fund | | `inflation_recalc_period` | uint32 (blocks) | 806400 (28 days) | How often inflation parameters are recalculated | -**How it's used**: Each block creates new tokens (inflation). First, `inflation_witness_percent` goes to the block-producing witness. The remainder is split: `inflation_ratio_committee_vs_reward_fund` percent goes to the committee DAO fund, the rest to the reward fund (used for awards). Witnesses directly control how the economy works. +**How it's used**: Each block creates new tokens (inflation). First, `inflation_witness_percent` goes to the block-producing validator. The remainder is split: `inflation_ratio_committee_vs_reward_fund` percent goes to the committee DAO fund, the rest to the reward fund (used for awards). validators directly control how the economy works. ### Reward System @@ -119,14 +119,14 @@ The calculated `median_props` is stored in the `witness_schedule_object` and use **How it's used**: When content receives awards, curation rewards are bounded by `[min_curation_percent, max_curation_percent]`. Awards with fewer than `vote_accounting_min_rshares` rshares produce zero reward (dust filter). `flag_energy_additional_cost` can make downvotes more expensive than upvotes. -### Witness Accountability +### validator Accountability | Property | Type | Default | What It Controls | |---|---|---|---| | `witness_miss_penalty_percent` | int16 (bp) | 100 (1%) | Vote reduction for missing a block | | `witness_miss_penalty_duration` | uint32 (sec) | 86400 (1 day) | How long the penalty lasts | -**How it's used**: When a witness misses their scheduled block, their effective votes are reduced by `witness_miss_penalty_percent` for `witness_miss_penalty_duration` seconds. This is self-governing accountability: witnesses vote on how harshly missed blocks are punished. +**How it's used**: When a validator misses their scheduled block, their effective votes are reduced by `witness_miss_penalty_percent` for `witness_miss_penalty_duration` seconds. This is self-governing accountability: validators vote on how harshly missed blocks are punished. ### Fee Structure @@ -136,10 +136,10 @@ The calculated `median_props` is stored in the `witness_schedule_object` and use | `create_paid_subscription_fee` | asset (VIZ) | 100.000 VIZ | Fee to create a paid subscription | | `account_on_sale_fee` | asset (VIZ) | 10.000 VIZ | Fee to list an account for sale | | `subaccount_on_sale_fee` | asset (VIZ) | 100.000 VIZ | Fee to list subaccounts for sale | -| `witness_declaration_fee` | asset (VIZ) | 10.000 VIZ | One-time fee for new witness registration | +| `witness_declaration_fee` | asset (VIZ) | 10.000 VIZ | One-time fee for new validator registration | | `create_invite_min_balance` | asset (VIZ) | 10.000 VIZ | Minimum balance to create an invite | -**How it's used**: All fees go to the **committee fund** (DAO treasury). Witnesses control how expensive various network operations are. Higher fees discourage spam; lower fees improve accessibility. The community decides the balance through witness elections. +**How it's used**: All fees go to the **committee fund** (DAO treasury). validators control how expensive various network operations are. Higher fees discourage spam; lower fees improve accessibility. The community decides the balance through validator elections. ### Vesting Withdrawal @@ -147,29 +147,29 @@ The calculated `median_props` is stored in the `witness_schedule_object` and use |---|---|---|---| | `withdraw_intervals` | uint16 | 28 | Number of daily installments for unstaking | -**How it's used**: When a user unstakes SHARES, the withdrawal happens over `withdraw_intervals` days (one installment per day). Witnesses can make unstaking faster or slower, affecting how liquid the network's governance token is. +**How it's used**: When a user unstakes SHARES, the withdrawal happens over `withdraw_intervals` days (one installment per day). validators can make unstaking faster or slower, affecting how liquid the network's governance token is. --- -## The Governance Loop: Users → Witnesses → Parameters +## The Governance Loop: Users → validators → Parameters -### Users Shape the Network Through Witness Selection +### Users Shape the Network Through validator Selection -Users cannot directly set chain properties. Instead, they **vote for witnesses** whose published properties match their preferences. This creates a representative governance system: +Users cannot directly set chain properties. Instead, they **vote for validators** whose published properties match their preferences. This creates a representative governance system: ``` Users (SHARES holders) │ - ├── Vote for witnesses who want LOW fees - │ → More witnesses with low fee props get elected + ├── Vote for validators who want LOW fees + │ → More validators with low fee props get elected │ → Median fees decrease │ - ├── Vote for witnesses who want HIGH inflation to reward fund - │ → More reward-focused witnesses get elected + ├── Vote for validators who want HIGH inflation to reward fund + │ → More reward-focused validators get elected │ → Inflation shifts toward reward fund │ - └── Vote for witnesses who want STRICT miss penalties - → More accountability-focused witnesses get elected + └── Vote for validators who want STRICT miss penalties + → More accountability-focused validators get elected → Miss penalties increase ``` @@ -177,33 +177,33 @@ Users (SHARES holders) Traditional blockchains set parameters through hard-coded values or foundation decisions. VIZ makes **every parameter a public good decision**: -1. **Transparency**: every witness's preferred properties are on-chain and publicly visible -2. **Accountability**: if a witness sets harmful parameters, users can unvote them -3. **Gradual change**: the median shifts slowly — no single witness can cause sudden parameter swings -4. **No single point of failure**: even if some witnesses are compromised, the median protects the network -5. **Aligned incentives**: witnesses earn block rewards, so they're incentivized to keep the network healthy +1. **Transparency**: every validator's preferred properties are on-chain and publicly visible +2. **Accountability**: if a validator sets harmful parameters, users can unvote them +3. **Gradual change**: the median shifts slowly — no single validator can cause sudden parameter swings +4. **No single point of failure**: even if some validators are compromised, the median protects the network +5. **Aligned incentives**: validators earn block rewards, so they're incentivized to keep the network healthy ### Example: How a Fee Change Happens Suppose the community wants to lower the `committee_create_request_fee` from 100 VIZ to 50 VIZ: 1. Users discuss in community channels that the fee is too high -2. Some witnesses update their properties: `committee_create_request_fee: "50.000 VIZ"` -3. Users shift votes to witnesses who support lower fees -4. As more low-fee witnesses enter the active set, the **median shifts down** -5. Once more than half of active witnesses publish 50 VIZ or less, the median becomes 50 VIZ +2. Some validators update their properties: `committee_create_request_fee: "50.000 VIZ"` +3. Users shift votes to validators who support lower fees +4. As more low-fee validators enter the active set, the **median shifts down** +5. Once more than half of active validators publish 50 VIZ or less, the median becomes 50 VIZ 6. The new fee takes effect automatically — no hardfork, no governance proposal, no vote counting ### Comparing Governance Models | Approach | VIZ Median Properties | Token Voting (e.g., Snapshot) | Foundation Governance | |---|---|---|---| -| **Who decides** | Elected witnesses (indirectly: all SHARES holders) | Token holders directly | Core team / foundation | +| **Who decides** | Elected validators (indirectly: all SHARES holders) | Token holders directly | Core team / foundation | | **Resistance to extremes** | Strong (median) | Weak (whale dominance) | N/A (centralized) | | **Speed of change** | Gradual (median shifts slowly) | Fast (single vote) | Fast or slow (depends on team) | | **Parameter granularity** | Every parameter independently | Usually binary proposals | Any | | **Sybil resistance** | Built-in (stake-weighted Fair-DPOS) | Depends on implementation | N/A | -| **Transparency** | Full (all witness props on-chain) | Partial (off-chain voting) | Low | +| **Transparency** | Full (all validator props on-chain) | Partial (off-chain voting) | Low | --- @@ -218,7 +218,7 @@ Properties were introduced in stages: | `chain_properties_hf6` | HF6 | data_operations_cost_additional_bandwidth, witness_miss_penalty_percent, witness_miss_penalty_duration | | `chain_properties_hf9` | HF9 | create_invite_min_balance, committee_create_request_fee, create_paid_subscription_fee, account_on_sale_fee, subaccount_on_sale_fee, witness_declaration_fee, withdraw_intervals | -Witnesses publish properties using `versioned_chain_properties` — a variant that accepts any version. The evaluator validates the version against the current hardfork (you can't publish HF9 properties before HF9 activates). Properties from older versions use default values for newer fields. +validators publish properties using `versioned_chain_properties` — a variant that accepts any version. The evaluator validates the version against the current hardfork (you can't publish HF9 properties before HF9 activates). Properties from older versions use default values for newer fields. --- @@ -226,10 +226,10 @@ Witnesses publish properties using `versioned_chain_properties` — a variant th Chain properties governance in VIZ is a **continuous, median-based, representative system** where: -- **Witnesses** are the direct governors who publish their preferred parameters -- **Users** are the ultimate governors who elect witnesses based on their published properties +- **validators** are the direct governors who publish their preferred parameters +- **Users** are the ultimate governors who elect validators based on their published properties - **The median** ensures no single actor can impose extreme values - **Every parameter** — from fees to inflation to bandwidth — is a public good decision made collectively -- **Changes happen organically**: as community preferences shift, witness elections shift, and the median follows +- **Changes happen organically**: as community preferences shift, validator elections shift, and the median follows This creates a self-regulating network where the "rules of the game" are constantly optimized by the people who have the most at stake. diff --git a/.qoder/docs/cli-wallet.md b/.qoder/docs/cli-wallet.md index 396859e68c..0375fce954 100644 --- a/.qoder/docs/cli-wallet.md +++ b/.qoder/docs/cli-wallet.md @@ -1,4 +1,4 @@ -# VIZ CLI Wallet — Complete Command Reference +# VIZ CLI Wallet — Complete Command Reference Complete reference for all `cli_wallet` commands with syntax and examples. @@ -11,7 +11,7 @@ Complete reference for all `cli_wallet` commands with syntax and examples. 3. [Query Operations](#query-operations) 4. [Account Operations](#account-operations) 5. [Transfer & Vesting](#transfer--vesting) -6. [Witness Operations](#witness-operations) +6. [validator Operations](#validator-operations) 7. [Content Operations](#content-operations) 8. [Escrow Operations](#escrow-operations) 9. [Recovery Operations](#recovery-operations) @@ -202,7 +202,7 @@ get_ops_in_block 1000000 true # only virtual operations ``` ### get_active_witnesses -Returns the list of witnesses producing blocks in the current round (21 blocks). +Returns the list of validators producing blocks in the current round (21 blocks). ```bash get_active_witnesses @@ -404,25 +404,25 @@ set_withdraw_vesting_route "alice" "charlie" 2500 true true --- -## Witness Operations +## validator Operations ### list_witnesses -Lists all witnesses registered in the blockchain. +Lists all validators registered in the blockchain. ```bash -list_witnesses "" 100 # First 100 witnesses -list_witnesses "bob" 100 # 100 witnesses starting from "bob" +list_witnesses "" 100 # First 100 validators +list_witnesses "bob" 100 # 100 validators starting from "bob" ``` ### get_witness -Returns information about the given witness. +Returns information about the given validator. ```bash get_witness "witnessname" ``` ### update_witness -Update a witness object. +Update a validator object. ```bash update_witness "mywitness" "https://mywitness.com" "VIZsigningkey..." true @@ -458,7 +458,7 @@ set_voting_proxy "alice" "" true ``` ### vote_for_witness -Vote for a witness to become a block producer. +Vote for a validator to become a block producer. ```bash vote_for_witness "alice" "mywitness" true true # Vote for @@ -1114,7 +1114,7 @@ Weight is in basis points (5000 = 50%). | Transfer VIZ | active | | Transfer SHARES | master | | Vesting operations | active | -| Witness operations | active | +| validator operations | active | | Content operations | regular | | Custom operation | active or regular (specified) | | Recovery operations | varies | diff --git a/.qoder/docs/consensus-emergency-params.md b/.qoder/docs/consensus-emergency-params.md index 6e8e41df7c..19c4c94aa4 100644 --- a/.qoder/docs/consensus-emergency-params.md +++ b/.qoder/docs/consensus-emergency-params.md @@ -1,4 +1,4 @@ -# VIZ Blockchain — Consensus Emergency Parameters +# VIZ Blockchain — Consensus Emergency Parameters Parameters used to restart stuck consensus, their operational mechanics, and the micro-fork risks when operators forget to revert them after the emergency is resolved. @@ -6,15 +6,15 @@ Parameters used to restart stuck consensus, their operational mechanics, and the ## Overview -When the VIZ network stalls — no blocks are produced because too few witnesses are online — operators can activate emergency parameters to unblock production: +When the VIZ network stalls — no blocks are produced because too few validators are online — operators can activate emergency parameters to unblock production: | Parameter | Normal Value | Emergency Value | Location | |---|---|---|---| -| `enable-stale-production` | `false` | `true` | witness plugin | -| `required-participation` | `3300` (33%) | `0` | witness plugin | +| `enable-stale-production` | `false` | `true` | Validator Plugin | +| `required-participation` | `3300` (33%) | `0` | Validator Plugin | | `fork_db` `_max_size` | `1024` (dynamic) | `1024` (initial) | chain library | -Starting with Hardfork 12, the on-chain **emergency consensus activation** is fully automatic and deterministic. When the network stalls for >1 hour (`b.timestamp - lib_block.timestamp >= 3600`), emergency consensus mode activates on every node — no config flags needed. The activation uses only signed block timestamps from the chain state, ensuring identical results during replay. **All real witnesses are disabled** on activation (signing_key zeroed); operators must re-register via `witness_update_operation`. LIB advances every block (capped at HEAD−1), so the undo limit is never reached. Emergency exits automatically when 75% (16/21) of schedule slots are real witnesses. The older `enable-stale-production` and `required-participation` overrides are still available for pre-HF12 scenarios or manual recovery. +Starting with Hardfork 12, the on-chain **emergency consensus activation** is fully automatic and deterministic. When the network stalls for >1 hour (`b.timestamp - lib_block.timestamp >= 3600`), emergency consensus mode activates on every node — no config flags needed. The activation uses only signed block timestamps from the chain state, ensuring identical results during replay. **All real validators are disabled** on activation (signing_key zeroed); operators must re-register via `witness_update_operation`. LIB advances every block (capped at HEAD−1), so the undo limit is never reached. Emergency exits automatically when 75% (16/21) of schedule slots are real validators. The older `enable-stale-production` and `required-participation` overrides are still available for pre-HF12 scenarios or manual recovery. These parameters are essential for chain recovery, but if left in emergency mode after the network stabilizes, they become the **root cause of micro-forks**: isolated delegates continue producing blocks on their own divergent chain during any subsequent network partition. @@ -24,7 +24,7 @@ These parameters are essential for chain recovery, but if left in emergency mode ### Definition -Controls whether the witness node produces blocks when the chain is "stale" — i.e., the node has not received recent blocks and its head block is behind the network. +Controls whether the validator node produces blocks when the chain is "stale" — i.e., the node has not received recent blocks and its head block is behind the network. | Property | Value | |---|---| @@ -32,11 +32,11 @@ Controls whether the witness node produces blocks when the chain is "stale" — | Default | `false` | | Command line | `--enable-stale-production` | | Config file | `enable-stale-production = true` | -| Source | [witness.cpp:126](../../plugins/witness/witness.cpp#L126) | +| Source | [validator.cpp:126](../../plugins/validator/validator.cpp#L126) | ### How It Works -When `enable-stale-production = false` (default), the witness plugin starts with `_production_enabled = false`. Before each production attempt, the check at [witness.cpp:333-339](../../plugins/witness/witness.cpp#L333) runs: +When `enable-stale-production = false` (default), the Validator Plugin starts with `_production_enabled = false`. Before each production attempt, the check at [validator.cpp:333-339](../../plugins/validator/validator.cpp#L333) runs: ```cpp if (!_production_enabled) { @@ -50,11 +50,11 @@ if (!_production_enabled) { The node will **not** produce blocks until it receives a block whose timestamp places the next slot in the present or future — confirming it is synchronized with the network. Once caught up, production auto-enables permanently. -When `enable-stale-production = true`, `_production_enabled` is set to `true` immediately at initialization ([witness.cpp:149-151](../../plugins/witness/witness.cpp#L149)), bypassing the sync check entirely. +When `enable-stale-production = true`, `_production_enabled` is set to `true` immediately at initialization ([validator.cpp:149-151](../../plugins/validator/validator.cpp#L149)), bypassing the sync check entirely. ### Side Effect: `skip_undo_history_check` -Setting `enable-stale-production = true` also activates the `skip_undo_history_check` production flag ([witness.cpp:184](../../plugins/witness/witness.cpp#L184)): +Setting `enable-stale-production = true` also activates the `skip_undo_history_check` production flag ([validator.cpp:184](../../plugins/validator/validator.cpp#L184)): ```cpp if (pimpl->_production_enabled) { @@ -73,17 +73,17 @@ if (!(skip & skip_undo_history_check)) { } ``` -`CHAIN_MAX_UNDO_HISTORY` = 10000 blocks ([config.hpp:108](../../libraries/protocol/include/graphene/protocol/config.hpp#L108)). Without this check, a node producing blocks alone can accumulate an unlimited gap between head and last irreversible block (LIB), since LIB only advances when enough witnesses sign off via block post-validation. +`CHAIN_MAX_UNDO_HISTORY` = 10000 blocks ([config.hpp:108](../../libraries/protocol/include/graphene/protocol/config.hpp#L108)). Without this check, a node producing blocks alone can accumulate an unlimited gap between head and last irreversible block (LIB), since LIB only advances when enough validators sign off via block post-validation. ### Emergency Use Case -When the network has completely stalled (no witnesses producing), setting `enable-stale-production = true` on at least one witness node allows it to start producing blocks from its current head, even if it considers the chain stale. This breaks the deadlock and restarts block production. +When the network has completely stalled (no validators producing), setting `enable-stale-production = true` on at least one validator node allows it to start producing blocks from its current head, even if it considers the chain stale. This breaks the deadlock and restarts block production. ### Micro-Fork Risk **If the operator forgets to revert this to `false` after the network recovers**, any subsequent network partition causes the node to continue producing blocks in isolation: -1. The node loses P2P connectivity to other witnesses +1. The node loses P2P connectivity to other validators 2. No new blocks are received, so `get_slot_time(1) < now` — but since `enable-stale-production = true`, this check is skipped 3. The node keeps producing blocks on its own fork 4. `skip_undo_history_check` means there is **no limit** on how far the head-LIB gap grows @@ -95,7 +95,7 @@ When the network has completely stalled (no witnesses producing), setting `enabl ### Definition -The minimum witness participation rate (in basis points) required for block production. The participation rate measures what fraction of the last 128 block slots were actually filled by witnesses. +The minimum validator participation rate (in basis points) required for block production. The participation rate measures what fraction of the last 128 block slots were actually filled by validators. | Property | Value | |---|---| @@ -104,7 +104,7 @@ The minimum witness participation rate (in basis points) required for block prod | Range | 0–9900 (0%–99%) | | Command line | `--required-participation ` | | Config file | `required-participation = ` | -| Source | [witness.cpp:127](../../plugins/witness/witness.cpp#L127) | +| Source | [validator.cpp:127](../../plugins/validator/validator.cpp#L127) | ### Internal Representation @@ -126,7 +126,7 @@ uint32_t database::witness_participation_rate() const { } ``` -`recent_slots_filled` is a 128-bit bitmask ([global_property_object.hpp:94](../../libraries/chain/include/graphene/chain/global_property_object.hpp#L94)) where each bit represents one of the last 128 block slots. A `1` means the slot was filled by a witness, `0` means it was missed. The rate is the percentage of filled slots. +`recent_slots_filled` is a 128-bit bitmask ([global_property_object.hpp:94](../../libraries/chain/include/graphene/chain/global_property_object.hpp#L94)) where each bit represents one of the last 128 block slots. A `1` means the slot was filled by a validator, `0` means it was missed. The rate is the percentage of filled slots. The bitmask is updated on each block application at [database.cpp:4060-4063](../../libraries/chain/database.cpp#L4060): @@ -142,7 +142,7 @@ For each missed block, a `0` is shifted in. For the current block, a `1` is shif ### Production Check -Before producing a block, the witness plugin checks ([witness.cpp:436-439](../../plugins/witness/witness.cpp#L436)): +Before producing a block, the Validator Plugin checks ([validator.cpp:436-439](../../plugins/validator/validator.cpp#L436)): ```cpp uint32_t prate = db.witness_participation_rate(); @@ -154,7 +154,7 @@ if (prate < _required_witness_participation) { If the participation rate is below the threshold, block production is suppressed and the error message logs: ``` -Not producing block because node appears to be on a minority fork with only X% witness participation +Not producing block because node appears to be on a minority fork with only X% validator participation ``` ### Emergency Use Case @@ -169,7 +169,7 @@ When the network has stalled, `recent_slots_filled` will be mostly zeros (many m 2. On the minority fork, `recent_slots_filled` decays (more missed slots) 3. With the default 33% threshold, production would stop within ~85 missed slots (when participation drops below 33%) 4. With `required-participation = 0`, the node **never stops producing** regardless of how isolated it is -5. A single witness on a completely isolated node continues producing blocks alone +5. A single validator on a completely isolated node continues producing blocks alone This is the most dangerous of the three parameters because it removes the **last automatic safeguard** against solo block production on a minority fork. @@ -255,11 +255,11 @@ Timeline: ### Step-by-step breakdown -1. **Network stalls**: Insufficient witnesses are online to meet the 33% participation threshold. No blocks are produced. +1. **Network stalls**: Insufficient validators are online to meet the 33% participation threshold. No blocks are produced. 2. **Emergency activated**: A delegate sets `enable-stale-production = true` and `required-participation = 0` in their config. The node starts producing blocks, restarting the chain. -3. **Network recovers**: Other witnesses come online, see the new blocks, and start participating. The network is healthy again — but the emergency settings are still active in the delegate's config. +3. **Network recovers**: Other validators come online, see the new blocks, and start participating. The network is healthy again — but the emergency settings are still active in the delegate's config. 4. **Normal operation with emergency settings**: Everything appears fine. The delegate is producing blocks normally. The participation rate is high, so `required-participation = 0` makes no difference. The node is synced, so `enable-stale-production = true` makes no difference. @@ -280,13 +280,13 @@ Timeline: With normal settings (`enable-stale-production = false`, `required-participation = 3300`): -1. **Network partition occurs**: The node stops receiving blocks from other witnesses. +1. **Network partition occurs**: The node stops receiving blocks from other validators. 2. **Participation decays**: `recent_slots_filled` shifts in zeros for each missed slot. After ~85 missed slots (about 4 minutes), participation drops below 33%. 3. **Production stops**: The `low_participation` check suppresses block production. The node logs: ``` - Not producing block because node appears to be on a minority fork with only X% witness participation + Not producing block because node appears to be on a minority fork with only X% validator participation ``` 4. **Even if participation hasn't decayed yet**: When the node's chain becomes stale (no recent blocks), the `_production_enabled` check would block production if `enable-stale-production = false` had been set and the node had somehow lost its synced state. @@ -319,7 +319,7 @@ When the network has stalled and block production must be restarted: Generated block #N with timestamp T at time C by W ``` -4. **Wait for network recovery** — once other witnesses are back online and the participation rate stabilizes above 33%, **immediately revert the settings**. +4. **Wait for network recovery** — once other validators are back online and the participation rate stabilizes above 33%, **immediately revert the settings**. ### Reverting Emergency Mode (Critical Step) @@ -343,21 +343,21 @@ Then restart the node. **Do not skip this step.** Leaving emergency settings act ### Red Flags: Emergency Settings Still Active -If you observe any of these log patterns, a witness is likely running with emergency settings still active: +If you observe any of these log patterns, a validator is likely running with emergency settings still active: -- **Blocks produced during a network partition**: A witness that should have stopped continues generating blocks +- **Blocks produced during a network partition**: A validator that should have stopped continues generating blocks - **Participation rate logs showing < 33% but blocks still produced**: `required-participation = 0` is active - **Head-LIB gap growing beyond 10000 blocks**: `skip_undo_history_check` is active (via `enable-stale-production = true`) -- **Fork collision warnings with rapid reoccurrence**: Isolated witness creates competing blocks at the same heights +- **Fork collision warnings with rapid reoccurrence**: Isolated validator creates competing blocks at the same heights --- ## Configuration File Examples -### Production Witness (Normal Operation) +### Production validator (Normal Operation) ```ini -# Normal witness configuration — SAFE +# Normal validator configuration — SAFE enable-stale-production = false required-participation = 3300 ``` @@ -394,7 +394,7 @@ required-participation = 0 | `CHAIN_1_PERCENT` | 100 | 1% in basis points | [config.hpp:58](../../libraries/protocol/include/graphene/protocol/config.hpp#L58) | | `CHAIN_MAX_UNDO_HISTORY` | 10000 | Max head-LIB gap before undo history exception | [config.hpp:108](../../libraries/protocol/include/graphene/protocol/config.hpp#L108) | | `CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC` | 3600 | Seconds since LIB before emergency activates | [config.hpp:112](../../libraries/protocol/include/graphene/protocol/config.hpp#L112) | -| `CHAIN_IRREVERSIBLE_THRESHOLD` | 7500 (75%) | Witness validation threshold for LIB advancement | [config.hpp:110](../../libraries/protocol/include/graphene/protocol/config.hpp#L110) | +| `CHAIN_IRREVERSIBLE_THRESHOLD` | 7500 (75%) | validator validation threshold for LIB advancement | [config.hpp:110](../../libraries/protocol/include/graphene/protocol/config.hpp#L110) | | `fork_db._max_size` | 1024 | Default fork database depth | [fork_database.hpp:117](../../libraries/chain/include/graphene/chain/fork_database.hpp#L117) | | `MAX_BLOCK_REORDERING` | 1024 | Max out-of-order block insertion distance | [fork_database.hpp:57](../../libraries/chain/include/graphene/chain/fork_database.hpp#L57) | @@ -404,7 +404,7 @@ With 3-second block intervals and 128-slot window: | Missed Slots | Participation Rate | Status | |---|---|---| -| 0 | 100% | All witnesses active | +| 0 | 100% | All validators active | | 32 (1.6 min) | 75% | Healthy | | 64 (3.2 min) | 50% | Degraded | | 85 (4.3 min) | 33.6% | Just above default threshold | @@ -442,7 +442,7 @@ maybe_produce_block() │ │ └─ No: return not_synced ← BYPASSED when enable-stale-production=true │ └─ Yes: continue │ - ├─ Is it my turn? (scheduled witness check) + ├─ Is it my turn? (scheduled validator check) │ └─ No: return not_my_turn │ ├─ Do I have the private key? diff --git a/.qoder/docs/data-types.md b/.qoder/docs/data-types.md index 6ba41ee848..f454271cb5 100644 --- a/.qoder/docs/data-types.md +++ b/.qoder/docs/data-types.md @@ -1,4 +1,4 @@ -# VIZ Blockchain — Common Data Types +# VIZ Blockchain — Common Data Types This document describes all shared data types used across VIZ protocol operations and virtual operations. These types appear as field types throughout operation structures. @@ -108,7 +108,7 @@ Multi-signature authority structure controlling an account's permission level. | Level | Used for | |---|---| | `master` | Highest security — changing keys, account recovery | -| `active` | Token operations — transfer, vesting, witness voting | +| `active` | Token operations — transfer, vesting, validator voting | | `regular` | Social operations — content, awards, committee voting | ### Checklist @@ -217,7 +217,7 @@ Example (hf9 = index 3): | Field | Type | Description | |---|---|---| -| `inflation_witness_percent` | `int16_t` | Witness reward % from block inflation | +| `inflation_witness_percent` | `int16_t` | validator reward % from block inflation | | `inflation_ratio_committee_vs_reward_fund` | `int16_t` | Ratio committee/reward fund | | `inflation_recalc_period` | `uint32_t` | Blocks per inflation recalc | @@ -238,7 +238,7 @@ Example (hf9 = index 3): | `create_paid_subscription_fee` | `asset` (VIZ) | Fee to create paid subscription | | `account_on_sale_fee` | `asset` (VIZ) | Fee to list account for sale | | `subaccount_on_sale_fee` | `asset` (VIZ) | Fee to list subaccounts for sale | -| `witness_declaration_fee` | `asset` (VIZ) | Fee to declare as witness | +| `witness_declaration_fee` | `asset` (VIZ) | Fee to declare as validator | | `withdraw_intervals` | `uint16_t` | Number of withdraw intervals | --- diff --git a/.qoder/docs/dlt-4-node-code-audit-2026-05-08.md b/.qoder/docs/dlt-4-node-code-audit-2026-05-08.md index 1980d06e33..0b174a6b9f 100644 --- a/.qoder/docs/dlt-4-node-code-audit-2026-05-08.md +++ b/.qoder/docs/dlt-4-node-code-audit-2026-05-08.md @@ -1,6 +1,6 @@ -# DLT P2P Node — Code Audit vs. Documentation (2026-05-08) +# DLT P2P Node — Code Audit vs. Documentation (2026-05-08) -Audit of the `fix-witness` branch against the problem/fix log in +Audit of the `fix-validator` branch against the problem/fix log in `dlt-4-node-sync-scenarios.md`. Checks which fixes are actually in the code, identifies logic errors not yet documented, and lists undocumented problems found via code comments. @@ -35,7 +35,7 @@ found via code comments. | P25 | `exchange_enabled` re-evaluated when peer block accepted | **DONE** — line 1247-1249 | | P26 | `check_sync_catchup()` called on single-block accept | **DONE** — line 1495 | | P31 | `constexpr uint32_t dlt_peer_state::MAX_RECONNECT_BACKOFF_SEC;` defined | **DONE** — line 25 | -| P28/P29/P30 | Build errors in p2p_plugin.cpp / multimap::erase / witness header | **DONE** | +| P28/P29/P30 | Build errors in p2p_plugin.cpp / multimap::erase / validator header | **DONE** | --- @@ -173,7 +173,7 @@ The code comments reference problems not yet in `dlt-4-node-sync-scenarios.md`. | **P39** | `fork_db._head` jumps ahead of database head via `_push_next` cascade when a parent block arrives. The block being pushed can then appear "too old" (block_too_old_exception). Fixed in `_push_block`. (`p2p_plugin.cpp:185-192`) | | **P40** | Iterator invalidation in `periodic_lifecycle_timeout_check()` when `handle_disconnect` erases entries from `_peer_states` during iteration. Fixed: collect timed-out peers into a vector first. (`dlt_p2p_node.cpp:417-418`) | | **P42** | `request_blocks_from_peer` uses stale `peer_dlt_latest` as the peer's chain tip. Fixed: `peer_latest = max(peer_dlt_latest, peer_head_num)` to use whichever is fresher. (`dlt_p2p_node.cpp:956-960`) | -| **P49** | Range request starts at `our_head + 1`, skipping our own head block. If two witnesses signed competing blocks at the same height, we'd never detect the divergence. Fixed: start at `our_head`. (`dlt_p2p_node.cpp:982-986`) | +| **P49** | Range request starts at `our_head + 1`, skipping our own head block. If two validators signed competing blocks at the same height, we'd never detect the divergence. Fixed: start at `our_head`. (`dlt_p2p_node.cpp:982-986`) | --- diff --git a/.qoder/docs/dlt-4-node-sync-scenarios.md b/.qoder/docs/dlt-4-node-sync-scenarios.md index e2987c1dd8..46e49a26c1 100644 --- a/.qoder/docs/dlt-4-node-sync-scenarios.md +++ b/.qoder/docs/dlt-4-node-sync-scenarios.md @@ -1,4 +1,4 @@ -# DLT 4-Node Sync Scenarios — Problems & Analysis +# DLT 4-Node Sync Scenarios — Problems & Analysis Analysis of a 4-node DLT network under emergency consensus: 1 master + 3 slaves, based on the current `dlt_p2p_node.cpp` implementation. @@ -499,12 +499,12 @@ This repeated for hundreds of iterations (minutes of wall time) with no blocks p ``` maybe_produce_block returned 1 (not my slot, but slot had a value) ``` -Here `scheduled_witness=social` (a slave witness), so the master correctly doesn't produce, but the slave's block never arrived. +Here `scheduled_witness=social` (a slave validator), so the master correctly doesn't produce, but the slave's block never arrived. **Severity:** **CRITICAL** — network stalls for minutes with no block production. **Related files:** -- [witness.cpp](file:///d:/Work/viz-cpp-node/plugins/witness/witness.cpp) — `maybe_produce_block` +- [validator.cpp](file:///d:/Work/viz-cpp-node/plugins/validator/validator.cpp) — `maybe_produce_block` --- @@ -651,7 +651,7 @@ trigger_resync: could not read head block (lock contention?): Unable to acquire **Observed on:** Slave (80.87.202.57) + Master (185.146.232.170) -**Symptom:** Slave produces block #79645211 (witness="social", slot time 10:20:24), sends it via P2P. Master never receives/processes it and produces its own block #79645211 (witness="committee", slot time 10:20:27) — 3 seconds later for the same block number: +**Symptom:** Slave produces block #79645211 (validator="social", slot time 10:20:24), sends it via P2P. Master never receives/processes it and produces its own block #79645211 (validator="committee", slot time 10:20:27) — 3 seconds later for the same block number: **Slave side:** ``` @@ -675,7 +675,7 @@ scheduled_witness=committee The master then produced a competing block because it didn't see the slave's block within the slot window. -**Severity:** **HIGH** — causes unnecessary fork switches, wastes blocks, confuses witnesses. +**Severity:** **HIGH** — causes unnecessary fork switches, wastes blocks, confuses validators. --- @@ -762,11 +762,11 @@ auto a_data = a.data(); // 'const class fc::ip::address' has no member named ' **Symptom:** ``` -fatal error: graphene/plugins/witness/witness_plugin.hpp: No such file or directory -#include +fatal error: graphene/plugins/validator/witness_plugin.hpp: No such file or directory +#include ``` -**Root cause:** The include path is wrong. The actual file is at `plugins/witness/include/graphene/plugins/witness/witness.hpp` (note: `witness.hpp` not `witness_plugin.hpp`). The CMake include directories may not include the witness plugin's include path. +**Root cause:** The include path is wrong. The actual file is at `plugins/validator/include/graphene/plugins/validator/validator.hpp` (note: `validator.hpp` not `witness_plugin.hpp`). The CMake include directories may not include the Validator Plugin's include path. **Severity:** **MEDIUM** — blocks compilation in certain build configurations. diff --git a/.qoder/docs/dlt-forward-mode.md b/.qoder/docs/dlt-forward-mode.md index dd33458916..dbd922f402 100644 --- a/.qoder/docs/dlt-forward-mode.md +++ b/.qoder/docs/dlt-forward-mode.md @@ -1,4 +1,4 @@ -# DLT Forward Mode — Block & Transaction Exchange +# DLT Forward Mode — Block & Transaction Exchange ## Overview @@ -106,10 +106,10 @@ Relay block_reply to 3 peers (0 skipped: no_exchange, 0 skipped: not_active, 1 s ### Self-Produced Blocks -When a witness produces a block, the flow is: +When a validator produces a block, the flow is: ``` -witness.cpp:1081 p2p().broadcast_block(block) +validator.cpp:1081 p2p().broadcast_block(block) → p2p_plugin.cpp:482 my->node->broadcast_block(block) → dlt_p2p_node.cpp:1117 ``` @@ -142,7 +142,7 @@ The `peer` (sender) is excluded via the `exclude` parameter. Additionally, peers ### Block Post-Validation Broadcast -`broadcast_block_post_validation()` sends a lightweight fork-status message (block ID + witness + signature) instead of the full block. This is called by the witness plugin for each block in the production round after validation completes. +`broadcast_block_post_validation()` sends a lightweight fork-status message (block ID + validator + signature) instead of the full block. This is called by the Validator Plugin for each block in the production round after validation completes. --- @@ -491,7 +491,7 @@ Between these events, the peer's actual chain head may advance significantly (e. | `libraries/network/include/graphene/network/dlt_p2p_messages.hpp` | `dlt_block_reply_message`, `dlt_transaction_message`, `dlt_message_type_enum` | | `libraries/network/include/graphene/network/dlt_p2p_peer_state.hpp` | `dlt_peer_state` (contains `exchange_enabled`, `fork_alignment`, `known_blocks` for echo suppression) | | `plugins/p2p/p2p_plugin.cpp` | `dlt_delegate::accept_block()`, `p2p_plugin::broadcast_block()`, `p2p_plugin::broadcast_transaction()` | -| `plugins/witness/witness.cpp` | Calls `p2p().broadcast_block()` after block production | +| `plugins/validator/validator.cpp` | Calls `p2p().broadcast_block()` after block production | --- diff --git a/.qoder/docs/dlt-p2p-network-redesign-review.md b/.qoder/docs/dlt-p2p-network-redesign-review.md index e6b7cb1b92..7c20fd6123 100644 --- a/.qoder/docs/dlt-p2p-network-redesign-review.md +++ b/.qoder/docs/dlt-p2p-network-redesign-review.md @@ -1,4 +1,4 @@ -# DLT P2P Network Redesign — Plan vs. Implementation Review +# DLT P2P Network Redesign — Plan vs. Implementation Review Review of [implementation status](dlt-p2p-network-redesign.md) against the [design plan](../plans/dlt-p2p-network-redesign_91a7ca29.md). @@ -258,9 +258,9 @@ Implemented the body: checks if DLT block log range exceeds `_dlt_block_log_max_ ### Fix 11: `has_emergency_private_key()` returns false (P2) -**Files**: `plugins/p2p/p2p_plugin.cpp`, `plugins/witness/include/graphene/plugins/witness/witness.hpp`, `plugins/witness/witness.cpp` +**Files**: `plugins/p2p/p2p_plugin.cpp`, `plugins/validator/include/graphene/plugins/validator/validator.hpp`, `plugins/validator/validator.cpp` -Cross-plugin fix. (1) Added `is_emergency_key_configured()` declaration to `witness_plugin` public API — returns true if `CHAIN_EMERGENCY_WITNESS_ACCOUNT` is in `_witnesses` set (which only happens when `--emergency-private-key` is configured). (2) Implemented in witness.cpp with try/catch guard. (3) Updated `has_emergency_private_key()` in `dlt_delegate` to call `appbase::app().find_plugin()->is_emergency_key_configured()` instead of returning `false`. Added include for `witness_plugin.hpp`. +Cross-plugin fix. (1) Added `is_emergency_key_configured()` declaration to `witness_plugin` public API — returns true if `CHAIN_EMERGENCY_WITNESS_ACCOUNT` is in `_witnesses` set (which only happens when `--emergency-private-key` is configured). (2) Implemented in validator.cpp with try/catch guard. (3) Updated `has_emergency_private_key()` in `dlt_delegate` to call `appbase::app().find_plugin()->is_emergency_key_configured()` instead of returning `false`. Added include for `witness_plugin.hpp`. ### Fix 12: `accept_block()` ignores `sync_mode` parameter (P2) diff --git a/.qoder/docs/dlt-p2p-network-redesign.md b/.qoder/docs/dlt-p2p-network-redesign.md index 964ab55e48..5d40fa4da4 100644 --- a/.qoder/docs/dlt-p2p-network-redesign.md +++ b/.qoder/docs/dlt-p2p-network-redesign.md @@ -1,4 +1,4 @@ -# DLT P2P Network Redesign — Implementation Status +# DLT P2P Network Redesign — Implementation Status Implementation of the [design plan](../plans/dlt-p2p-network-redesign_91a7ca29.md). @@ -224,12 +224,12 @@ Implemented in `dlt_p2p_node.cpp`: - `dlt_fork_resolution_state::CONFIRMATION_BLOCKS = 6` (hysteresis) - `track_fork_state()` called after each block application - `resolve_fork()` with vote-weight winner + consecutive-block confirmation -- `_fork_status` exposed via `is_on_majority_fork()` for witness plugin +- `_fork_status` exposed via `is_on_majority_fork()` for Validator Plugin ### Phase 5: In-Place Replacement ✅ - Same plugin name `"p2p"`, same `p2p-endpoint` port (2001/4243) -- Same public API — zero changes to witness, witness_guard, snapshot plugins +- Same public API — zero changes to validator, witness_guard, snapshot plugins - Old `node.cpp` and all related files removed from build and deleted from disk - `p2p_plugin.hpp` completely unchanged @@ -238,12 +238,12 @@ Implemented in `dlt_p2p_node.cpp`: | Decision | Rationale | |----------|-----------| | Delegate pattern instead of direct chain access | Network lib only links `fc` + `graphene_protocol`, not `graphene_chain`. Delegate avoids circular dependency. | -| Raw TCP instead of STCP encryption | DLT emergency mode means all witnesses switch simultaneously — no need for backward-compatible encryption. Simpler wire protocol. | +| Raw TCP instead of STCP encryption | DLT emergency mode means all validators switch simultaneously — no need for backward-compatible encryption. Simpler wire protocol. | | fc::thread fibers instead of per-peer threads | All I/O uses fc's cooperative fiber model. `readsome()`/`writesome()` yield the fiber, allowing multiple peers on one thread without mutexes. | | Manual header+data writes instead of `fc::raw::pack(msg)` | `fc::raw::pack(message)` adds varint length prefix to the data vector, creating mismatched wire format. Writing header and data separately matches the read side. | | Single `spam_strikes` counter | Simpler and more effective than old multi-counter system (`unlinkable_block_strikes`, `sync_spam_strikes`, etc.). Reset-on-good naturally recovers from transient issues. | | Separate P2P mempool | Chain's `_pending_tx` only applies after acceptance. P2P mempool provides earlier filtering (expiry, TaPoS, size limits) before pushing to chain. | -| In-place replacement, no dual-mode | Old and new protocols are incompatible. Dual-mode creates isolated sub-networks. Emergency mode means all witnesses can switch simultaneously. | +| In-place replacement, no dual-mode | Old and new protocols are incompatible. Dual-mode creates isolated sub-networks. Emergency mode means all validators can switch simultaneously. | ## Subsequent Enhancements & Fixes (2026-05-05) @@ -299,7 +299,7 @@ Full details in [DLT 4-Node Sync Scenarios](./dlt-4-node-sync-scenarios.md). | Method | Purpose | |--------|---------| -| `broadcast_block_post_validation()` | Broadcast block by ID+witness+signature after validation | +| `broadcast_block_post_validation()` | Broadcast block by ID+validator+signature after validation | | `broadcast_chain_status()` | Send hello to all connected peers | | `trigger_resync()` | Force re-enter SYNC mode and re-request blocks | | `reconnect_seeds()` | Re-connect to all seed nodes | @@ -361,7 +361,7 @@ When `_block_processing_paused` is true (snapshot in progress), `periodic_task() ## Known Limitations / Future Work -- `has_emergency_private_key()` **now queries witness plugin** (was hardcoded `false`) +- `has_emergency_private_key()` **now queries Validator Plugin** (was hardcoded `false`) - `switch_to_fork()` **now has full implementation** with fork_db fetch + `push_block()` - `resync_from_lib()` is handled at plugin level (by design — delegate returns early) - `compute_branch_info()` returns simplified info — detailed vote-weight computation needs fork_db traversal @@ -379,7 +379,7 @@ The following compilation/linking issues block building with newer GCC (13+) in |---|-------|------|-----| | P28 | `multimap::erase` with `std::pair` — C++17 removed value-erase overload | `dlt_p2p_node.cpp` | Use iterator-erase: `_mempool_by_expiry.erase(it_by_expiry)` | | P28b | `ip::address::data()` doesn't exist in fc | `dlt_p2p_node.cpp` | Use `fc::raw::pack()` | -| P29 | Missing `witness_plugin.hpp` — actual file is `witness.hpp` | `p2p_plugin.cpp` | Fix include path | +| P29 | Missing `witness_plugin.hpp` — actual file is `validator.hpp` | `p2p_plugin.cpp` | Fix include path | | P30 | 6+ API mismatches in `p2p_plugin.cpp` (see below) | `p2p_plugin.cpp` | Update delegate calls | | P31 | Linker error: `static constexpr` ODR-use | `dlt_p2p_peer_state.hpp` | **Fixed** — out-of-line definitions added | @@ -461,7 +461,7 @@ Full analysis in [DLT 4-Node Sync Scenarios](./dlt-4-node-sync-scenarios.md#new- ### P18: slot=0 Production Stall Detector -**Files:** `plugins/witness/witness.cpp` +**Files:** `plugins/validator/validator.cpp` **Root cause:** `get_slot_at_time()` returns 0 when NTP time is behind `head_block_time()`. After crash/restart with NTP desync, the master could loop on `not_time_yet` for minutes. diff --git a/.qoder/docs/dlt-p2p-stats-reference-ru.md b/.qoder/docs/dlt-p2p-stats-reference-ru.md index cd36b9bb84..da2b18d5b3 100644 --- a/.qoder/docs/dlt-p2p-stats-reference-ru.md +++ b/.qoder/docs/dlt-p2p-stats-reference-ru.md @@ -1,4 +1,4 @@ -# Справочник по статистике DLT P2P +# Справочник по статистике DLT P2P Этот документ объясняет вывод статистики DLT P2P — что означает каждое поле, почему оно имеет текущее значение и какие действия (если нужны) должен предпринять оператор. @@ -225,7 +225,7 @@ DLT Status | FORWARD | head=#79881136 lib=#79881130 | dlt_range=79000000-7988113 **Почему флаги важны:** - `+align` самый важный — подтверждает, что пир является валидным источником для блоков -- `+emrg` + `+ekey` вместе указывают, что пир является участником emergency witness +- `+emrg` + `+ekey` вместе указывают, что пир является участником emergency validator - `+sync` указывает на активную синхронизацию — пир используется как источник блоков ### Заблокированные пиры diff --git a/.qoder/docs/dlt-p2p-stats-reference.md b/.qoder/docs/dlt-p2p-stats-reference.md index 35bd924e52..62bc173fa0 100644 --- a/.qoder/docs/dlt-p2p-stats-reference.md +++ b/.qoder/docs/dlt-p2p-stats-reference.md @@ -1,4 +1,4 @@ -# DLT P2P Statistics Reference +# DLT P2P Statistics Reference This document explains the DLT P2P statistics output — what each field means, why it has its current value, and what actions (if any) an operator should take. @@ -64,11 +64,11 @@ Emitted every ~30 seconds. Intended for tail/grep monitoring without noise. |-------|---------| | `NORMAL` | Node is on the majority fork — no fork conflict detected. | | `LOOKING` | Node detected multiple competing fork tips and is actively comparing branches to determine which is the majority. | -| `MINORITY` | Node determined it is on a minority fork (fewer witnesses producing on this branch). A fork switch is likely pending. | +| `MINORITY` | Node determined it is on a minority fork (fewer validators producing on this branch). A fork switch is likely pending. | **Why it might not be `NORMAL`:** -- Two or more witnesses produced blocks at the same slot, creating a temporary fork -- Network partition caused different subsets of witnesses to build on different tips +- Two or more validators produced blocks at the same slot, creating a temporary fork +- Network partition caused different subsets of validators to build on different tips - Node just received a block from an alternative fork that doesn't link to its current head ### `head` / `lib` @@ -225,7 +225,7 @@ Number of spam strikes accumulated by this peer. Each invalid or malformed messa **Why flags matter:** - `+align` is the most important — it confirms the peer is a valid source for blocks -- `+emrg` + `+ekey` together indicate the peer is an emergency witness participant +- `+emrg` + `+ekey` together indicate the peer is an emergency validator participant - `+sync` indicates active synchronization — the peer is being used as a block source ### Banned Peers @@ -279,13 +279,13 @@ After the ban expires, the peer state resets to DISCONNECTED and normal reconnec **What it means:** Multiple peers believe they are on a minority fork. **Likely causes:** -- Network-wide fork event — witnesses are split between two competing chains -- The majority fork is being built by a different set of witnesses +- Network-wide fork event — validators are split between two competing chains +- The majority fork is being built by a different set of validators **What to do:** - Monitor the `fork` status in the header — if it transitions to `MINORITY`, our node will also switch forks - Wait for fork resolution — the protocol will eventually converge on a single chain -- If this persists, check witness activity and network connectivity +- If this persists, check validator activity and network connectivity ### Scenario 4: High `backoff` Values on Disconnected Peers diff --git a/.qoder/docs/emergency-consensus-review.md b/.qoder/docs/emergency-consensus-review.md index 40216ef74d..859d5becf0 100644 --- a/.qoder/docs/emergency-consensus-review.md +++ b/.qoder/docs/emergency-consensus-review.md @@ -1,4 +1,4 @@ -# Emergency Consensus Recovery — Implementation Review +# Emergency Consensus Recovery — Implementation Review ## Status: Implemented (Hardfork 12, version 3.1.0) — Bugs Found and Fixed (B1–B17) @@ -8,11 +8,11 @@ Research source: [consensus-emergency-recovery.md](../research/consensus-emergen ## System Overview -Hardfork 12 adds an on-chain **Emergency Consensus Mode** that activates automatically when the VIZ network stalls for >1 hour (no LIB advancement). The activation check is fully deterministic — it uses only block timestamps from the chain state (`b.timestamp - lib_block.timestamp`), ensuring identical results on every node and during every replay. A well-known committee key (`VIZ75CRHVHPwYiUESy1bgN3KhVFbZCQQRA9jT6TnpzKAmpxMPD6Xv`) becomes the block producer, keeping the chain alive until real witnesses return. +Hardfork 12 adds an on-chain **Emergency Consensus Mode** that activates automatically when the VIZ network stalls for >1 hour (no LIB advancement). The activation check is fully deterministic — it uses only block timestamps from the chain state (`b.timestamp - lib_block.timestamp`), ensuring identical results on every node and during every replay. A well-known committee key (`VIZ75CRHVHPwYiUESy1bgN3KhVFbZCQQRA9jT6TnpzKAmpxMPD6Xv`) becomes the block producer, keeping the chain alive until real validators return. -On activation, **all real witnesses are disabled** (`signing_key` set to null, penalties reset to zero, `current_run` reset). Only the committee produces blocks initially. Operators must manually re-enable witnesses via `witness_update_operation` transactions. This ensures a clean start where only intentionally re-registered witnesses participate. +On activation, **all real validators are disabled** (`signing_key` set to null, penalties reset to zero, `current_run` reset). Only the committee produces blocks initially. Operators must manually re-enable validators via `witness_update_operation` transactions. This ensures a clean start where only intentionally re-registered validators participate. -The committee witness is a **neutral voter**: it copies the current median chain properties and votes for the currently applied hardfork version (not a future one). This ensures that committee slots in the schedule don't skew governance parameters or push unvoted hardforks. +The committee validator is a **neutral voter**: it copies the current median chain properties and votes for the currently applied hardfork version (not a future one). This ensures that committee slots in the schedule don't skew governance parameters or push unvoted hardforks. ### Key Files Modified @@ -24,7 +24,7 @@ The committee witness is a **neutral voter**: it copies the current median chain | `libraries/chain/database.cpp` | Activation, hybrid schedule, LIB advancement (capped), startup recovery, vote-weighted fork comparison | | `libraries/chain/include/graphene/chain/fork_database.hpp` | Emergency mode flag, size increase 1024→2400 | | `libraries/chain/fork_database.cpp` | Hash tie-breaking, `set_emergency_mode()` | -| `plugins/witness/witness.cpp` | Three-state safety, emergency key config, fork collision | +| `plugins/validator/validator.cpp` | Three-state safety, emergency key config, fork collision | | `libraries/network/include/graphene/network/peer_connection.hpp` | `fork_rejected_until` soft-ban field, `sync_spam_strikes` counter | | `libraries/network/node.cpp` | P2P anti-spam (soft-ban vs disconnect), sync ping-pong loop fix, sync spam soft-ban | | `plugins/snapshot/plugin.cpp` | Forward-compatible DGP import | @@ -52,8 +52,8 @@ The committee witness is a **neutral voter**: it copies the current median chain │ ≥ 3600? │ │ ├── YES ─────────────│── Activate Emergency │ │ │ • emergency_consensus_active = true - │ │ │ • Create/update committee witness - │ │ │ • Disable ALL real witnesses + │ │ │ • Create/update committee validator + │ │ │ • Disable ALL real validators │ │ │ (signing_key = null, penalties = 0) │ │ │ • Override schedule → committee │ │ │ • next_shuffle_block_num = now+N @@ -68,9 +68,9 @@ The committee witness is a **neutral voter**: it copies the current median chain │ │ │ 1. Normal schedule build │ │ (may zero all slots if no │ - │ witnesses have valid keys) │ + │ validators have valid keys) │ │ 2. Hybrid schedule override: │ - │ • Real witnesses keep slots │ + │ • Real validators keep slots │ │ • Committee fills gaps │ │ • Expand to full 21 slots │ │ • Sync committee props/vote │ @@ -94,7 +94,7 @@ The committee witness is a **neutral voter**: it copies the current median chain │ update_last_irreversible_block│ │ │ │ During emergency: │ - │ • All schedule witnesses │ + │ • All schedule validators │ │ used (including committee) │ │ • 75% nth_element threshold │ │ • LIB capped at HEAD−1 │ @@ -129,27 +129,27 @@ The committee witness is a **neutral voter**: it copies the current median chain **Special case — snapshot restore**: After `open_from_snapshot()`, the block_log is empty, so `fetch_block_by_number(LIB)` returns invalid. If we fell back to `genesis_time`, emergency would activate immediately. This is now prevented: when the LIB block is unavailable, the emergency check is skipped entirely (B7 fix). **If it happens** (legitimate false activation): -1. Emergency activates, all real witnesses are **disabled** (`signing_key` zeroed). +1. Emergency activates, all real validators are **disabled** (`signing_key` zeroed). 2. Committee produces blocks alone initially. -3. Since all witnesses had valid keys before, operators quickly re-register via `witness_update_operation`. -4. Once **16+ real witnesses** (75% of 21) have re-registered with valid signing keys in the hybrid schedule, **emergency exits automatically**. -5. **Manual intervention required**: operators must re-register witnesses via transactions. +3. Since all validators had valid keys before, operators quickly re-register via `witness_update_operation`. +4. Once **16+ real validators** (75% of 21) have re-registered with valid signing keys in the hybrid schedule, **emergency exits automatically**. +5. **Manual intervention required**: operators must re-register validators via transactions. -**Worst case**: If all 21 witness operators are available, recovery takes as fast as they can broadcast `witness_update_operation` transactions. +**Worst case**: If all 21 validator operators are available, recovery takes as fast as they can broadcast `witness_update_operation` transactions. ### F2: Emergency Exit Does Not Trigger -**When**: Emergency is active but the exit condition (75% real witnesses in schedule) never becomes true. +**When**: Emergency is active but the exit condition (75% real validators in schedule) never becomes true. -**Root cause**: Fewer than 75% of the 21 schedule slots (i.e., <16) are occupied by real witnesses with valid `signing_key`. Since all witnesses are disabled on activation, operators must manually re-enable them. +**Root cause**: Fewer than 75% of the 21 schedule slots (i.e., <16) are occupied by real validators with valid `signing_key`. Since all validators are disabled on activation, operators must manually re-enable them. **Recovery procedure**: -1. **Check how many witnesses are active**: `get_dynamic_global_properties` → `participation_count`, plus inspect `witness_schedule` to see how many non-committee slots exist. -2. **Activate more witnesses**: Each witness operator re-registers via `witness_update_operation` from CLI wallet or service. The witness only needs a valid `signing_key` — the hybrid schedule will automatically assign their slot on the next schedule update. -3. **No config changes needed**: The `enable-stale-production` and `required-participation` settings are auto-bypassed during emergency. Witnesses just need to be connected to P2P and have their signing key registered. -4. **Threshold**: Once 16+ witnesses have valid signing keys in the schedule, emergency exits on the next `update_witness_schedule()` call. +1. **Check how many validators are active**: `get_dynamic_global_properties` → `participation_count`, plus inspect `witness_schedule` to see how many non-committee slots exist. +2. **Activate more validators**: Each validator operator re-registers via `witness_update_operation` from CLI wallet or service. The validator only needs a valid `signing_key` — the hybrid schedule will automatically assign their slot on the next schedule update. +3. **No config changes needed**: The `enable-stale-production` and `required-participation` settings are auto-bypassed during emergency. validators just need to be connected to P2P and have their signing key registered. +4. **Threshold**: Once 16+ validators have valid signing keys in the schedule, emergency exits on the next `update_witness_schedule()` call. -**If witnesses cannot re-register** (e.g., lost master keys): The network remains in emergency mode indefinitely but still produces blocks. Governance intervention (committee proposals) would be needed to resolve witness account recovery. +**If validators cannot re-register** (e.g., lost master keys): The network remains in emergency mode indefinitely but still produces blocks. Governance intervention (committee proposals) would be needed to resolve validator account recovery. ### F3: Committee Chain Split (Multiple Emergency Producers) @@ -157,13 +157,13 @@ The committee witness is a **neutral voter**: it copies the current median chain **Why this is expected and handled**: 1. **Hash tie-breaking** (`fork_database::_push_block()`): When two blocks are at the same height during emergency, the one with the lower `block_id` (SHA256 hash) wins deterministically. All nodes converge to the same block within 1 P2P propagation round. -2. **Fork collision check** (`witness.cpp`): After the first slot, nodes detect that a competing block already exists at the target height and skip production. This reduces multi-producer collisions to a transient 1–2 slot artifact. -3. **No permanent split**: Because all emergency blocks use the same `committee` witness account and the schedule is identical on all nodes, there is no ongoing disagreement. Convergence is guaranteed within seconds. +2. **Fork collision check** (`validator.cpp`): After the first slot, nodes detect that a competing block already exists at the target height and skip production. This reduces multi-producer collisions to a transient 1–2 slot artifact. +3. **No permanent split**: Because all emergency blocks use the same `committee` validator account and the schedule is identical on all nodes, there is no ongoing disagreement. Convergence is guaranteed within seconds. **If a persistent split occurs** (e.g., network partition during emergency): - LIB is 1 block behind head on all partitions → most emergency blocks are irreversible. - When partitions reconnect, **vote-weighted chain comparison** (`push_block()`) resolves the fork: - - Branches with real witnesses (non-committee) win over pure-committee branches. + - Branches with real validators (non-committee) win over pure-committee branches. - Among branches with only committee blocks, the longer chain wins, with hash tie-breaking as the final tiebreaker. - The losing partition unwinds its reversible blocks and syncs from the winner. @@ -177,32 +177,32 @@ The committee witness is a **neutral voter**: it copies the current median chain **Remaining risk**: If a bug prevents LIB from advancing despite the cap, the old failure mode would resurface. The startup recovery mechanism (B12) provides an additional safety net. -### F5: Witnesses Disabled On Emergency Activation +### F5: validators Disabled On Emergency Activation -**When**: On emergency activation, **all real witnesses are disabled**: `signing_key` is set to `public_key_type()` (null), `penalty_percent` reset to 0, `counted_votes` restored to `votes`, `current_run` reset to 0. All `witness_penalty_expire_object` entries are removed. +**When**: On emergency activation, **all real validators are disabled**: `signing_key` is set to `public_key_type()` (null), `penalty_percent` reset to 0, `counted_votes` restored to `votes`, `current_run` reset to 0. All `witness_penalty_expire_object` entries are removed. **Emergency behavior**: -1. On activation, **all real witnesses are immediately disabled** by zeroing their `signing_key`. This is intentional — it ensures a clean start where only the committee produces blocks. Operators must explicitly re-register witnesses. -2. **During emergency, offline witnesses do NOT accumulate new missed-block penalties**. The `update_global_dynamic_data()` penalty/shutdown logic is skipped for witnesses that are not the block producer and not the committee account. -3. Witnesses must broadcast `witness_update_operation` to re-register their signing key. +1. On activation, **all real validators are immediately disabled** by zeroing their `signing_key`. This is intentional — it ensures a clean start where only the committee produces blocks. Operators must explicitly re-register validators. +2. **During emergency, offline validators do NOT accumulate new missed-block penalties**. The `update_global_dynamic_data()` penalty/shutdown logic is skipped for validators that are not the block producer and not the committee account. +3. validators must broadcast `witness_update_operation` to re-register their signing key. -**Recovery**: Emergency blocks **allow transactions** (not forced empty). So witnesses can: +**Recovery**: Emergency blocks **allow transactions** (not forced empty). So validators can: 1. Connect their node to the P2P network. 2. Use CLI wallet or web services to broadcast `witness_update_operation` with their signing key. 3. The transaction enters the next emergency block. -4. On the next schedule update, the witness gets their slot back in the hybrid schedule. +4. On the next schedule update, the validator gets their slot back in the hybrid schedule. -**This is intentional**: Disabling all witnesses on activation ensures that only intentionally re-registered witnesses participate. This prevents stale/crashed witnesses from being included in the schedule and causing production failures. +**This is intentional**: Disabling all validators on activation ensures that only intentionally re-registered validators participate. This prevents stale/crashed validators from being included in the schedule and causing production failures. --- ## Bugs Found and Fixed -The following bugs were discovered during code review of the emergency consensus implementation, specifically for the scenario of **1 node running 11 top witnesses** with other witnesses expected to join later. +The following bugs were discovered during code review of the emergency consensus implementation, specifically for the scenario of **1 node running 11 top validators** with other validators expected to join later. ### B1 (Critical): Hybrid Schedule Doesn't Expand `num_scheduled_witnesses` -**Problem**: `update_witness_schedule()` sets `num_scheduled_witnesses` to the count of witnesses with valid signing keys (e.g., 11). The hybrid override loop only iterated up to `num_scheduled_witnesses`, so empty slots at indices 11-20 were never visited and never assigned to committee. After the first schedule round, committee disappeared from production entirely. +**Problem**: `update_witness_schedule()` sets `num_scheduled_witnesses` to the count of validators with valid signing keys (e.g., 11). The hybrid override loop only iterated up to `num_scheduled_witnesses`, so empty slots at indices 11-20 were never visited and never assigned to committee. After the first schedule round, committee disappeared from production entirely. **Fix**: The hybrid override now iterates the full `CHAIN_MAX_WITNESSES` range, reads entries beyond `num_scheduled_witnesses` as empty, assigns committee to all empty/unavailable slots, and sets `num_scheduled_witnesses = CHAIN_MAX_WITNESSES * CHAIN_BLOCK_WITNESS_REPEAT`. @@ -210,33 +210,33 @@ The following bugs were discovered during code review of the emergency consensus **Problem**: With `CHAIN_BLOCK_WITNESS_REPEAT = 1`, the hardfork vote tally iterates every schedule slot. Committee filling 10 slots caused `get_witness("committee")` to be called 10 times, incrementing the committee's vote count by 10. The committee's default `hardfork_version_vote = 0.0.0` dominated the tally and blocked any hardfork from reaching `CHAIN_HARDFORK_REQUIRED_WITNESSES = 17`. -**Fix**: The hardfork vote tally now skips `CHAIN_EMERGENCY_WITNESS_ACCOUNT` during emergency mode. Only real witnesses' votes count toward hardfork adoption. +**Fix**: The hardfork vote tally now skips `CHAIN_EMERGENCY_WITNESS_ACCOUNT` during emergency mode. Only real validators' votes count toward hardfork adoption. ### B3 (High): Committee Skews Median Chain Properties **Problem**: `update_median_witness_props()` collected all schedule entries including 10 committee copies. The committee's default `chain_properties` (zero fees, zero sizes, zero penalties) skewed the median, enabling spam attacks and removing miss penalties. **Fix** (two-part): -1. The committee witness is initialized with `props = median_props` (current median), and re-synced every schedule update. This makes committee entries neutral — they reinforce the existing median rather than distorting it. -2. As defense-in-depth, `update_median_witness_props()` skips committee entries during emergency mode. This ensures the median reflects only real witnesses' preferences. +1. The committee validator is initialized with `props = median_props` (current median), and re-synced every schedule update. This makes committee entries neutral — they reinforce the existing median rather than distorting it. +2. As defense-in-depth, `update_median_witness_props()` skips committee entries during emergency mode. This ensures the median reflects only real validators' preferences. -### B4 (High): Offline Witnesses Accumulate Penalties During Emergency +### B4 (High): Offline validators Accumulate Penalties During Emergency -**Problem**: When committee produced a block, the `missed_blocks` loop in `update_global_dynamic_data()` applied penalties to offline witnesses for every missed slot. After 200 missed blocks (~10 minutes), their `signing_key` was set to null again — the same problem that emergency activation's penalty reset tried to solve. +**Problem**: When committee produced a block, the `missed_blocks` loop in `update_global_dynamic_data()` applied penalties to offline validators for every missed slot. After 200 missed blocks (~10 minutes), their `signing_key` was set to null again — the same problem that emergency activation's penalty reset tried to solve. **Fix** (consensus-based, deterministic): During emergency mode, the penalty/shutdown logic is split: -- **Skipped**: `total_missed++`, `penalty_percent` increment, and `counted_votes` recalculation for offline witnesses. Only `current_run` is reset. -- **Applied (consensus check)**: Key-blanking uses `head_block_num() - last_confirmed_block_num > CHAIN_EMERGENCY_MAX_WITNESS_MISSED_BLOCKS` (105 blocks = 5 full rounds). Both `head_block_num()` and `last_confirmed_block_num` are on-chain consensus fields stored in `witness_object` shared memory, so all nodes compute identical results. This replaces the old non-consensus approach that used a local `_emergency_round_start_gap` map (which could diverge between nodes). The threshold (105) is tighter than the normal-mode threshold (200) because real witnesses should re-enable faster during emergency recovery. +- **Skipped**: `total_missed++`, `penalty_percent` increment, and `counted_votes` recalculation for offline validators. Only `current_run` is reset. +- **Applied (consensus check)**: Key-blanking uses `head_block_num() - last_confirmed_block_num > CHAIN_EMERGENCY_MAX_WITNESS_MISSED_BLOCKS` (105 blocks = 5 full rounds). Both `head_block_num()` and `last_confirmed_block_num` are on-chain consensus fields stored in `witness_object` shared memory, so all nodes compute identical results. This replaces the old non-consensus approach that used a local `_emergency_round_start_gap` map (which could diverge between nodes). The threshold (105) is tighter than the normal-mode threshold (200) because real validators should re-enable faster during emergency recovery. -### B5 (Medium): Committee Could Be Selected as Top/Support Witness +### B5 (Medium): Committee Could Be Selected as Top/Support validator -**Problem**: The top/support witness selection iterated by `counted_votes`. If the committee witness ever received votes, it could compete for a production slot, displacing a real witness. +**Problem**: The top/support validator selection iterated by `counted_votes`. If the committee validator ever received votes, it could compete for a production slot, displacing a real validator. -**Fix**: Both top and support witness selection loops now explicitly exclude `CHAIN_EMERGENCY_WITNESS_ACCOUNT`. +**Fix**: Both top and support validator selection loops now explicitly exclude `CHAIN_EMERGENCY_WITNESS_ACCOUNT`. ### B6 (Medium): Committee Hardfork Vote Auto-Injected via Block Extensions -**Problem**: `_generate_block()` auto-injects a `hardfork_version_vote` extension when the witness's on-chain vote doesn't match the binary's configured next hardfork. For the committee witness (which votes for `current_hardfork_version`), this extension overwrote the on-chain vote to the next hardfork version via `process_header_extensions()`, defeating the neutral-voter design. +**Problem**: `_generate_block()` auto-injects a `hardfork_version_vote` extension when the validator's on-chain vote doesn't match the binary's configured next hardfork. For the committee validator (which votes for `current_hardfork_version`), this extension overwrote the on-chain vote to the next hardfork version via `process_header_extensions()`, defeating the neutral-voter design. **Fix**: When the block producer is the emergency committee, hardfork vote auto-injection is skipped entirely. The committee's on-chain vote stays at `current_hardfork_version` and is re-synced every schedule update. @@ -246,9 +246,9 @@ The following bugs were discovered during code review of the emergency consensus The false activation triggers catastrophic side effects: 1. Schedule overridden to all-committee, but `next_shuffle_block_num` not updated → hybrid override can't run until the next shuffle boundary. -2. Blocks from real witnesses (via p2p) are rejected because the schedule expects `committee` → `head_block_num()` doesn't advance. +2. Blocks from real validators (via p2p) are rejected because the schedule expects `committee` → `head_block_num()` doesn't advance. 3. `next_shuffle_block_num` is never reached → **deadlock: the node permanently stops syncing**. Probability: ~20/21 (~95%) depending on how close the next shuffle was. -4. Side effects: all witness penalties reset, committee witness object created, consensus state corrupted. +4. Side effects: all validator penalties reset, committee validator object created, consensus state corrupted. **Fix** (two-part): 1. When `lib_block` is not found (block_log empty after snapshot restore), `lib_time_available` stays `false` and the emergency check is skipped entirely. Emergency cannot activate without a valid LIB timestamp. @@ -270,24 +270,24 @@ When a peer on a stale fork sends an unlinkable block at or below our head block 1. In the `unlinkable_block_exception` catch handler, compare the peer's block number against our head. If the block is at or below our head, the peer is on a stale fork — soft-ban for 1 hour and set `inhibit_fetching_sync_blocks = true`. If the block is ahead of us, resync is justified (keep original behavior). 2. Remove the dead `unlinkable_block_exception::code_enum::code_value` check from the `fc::exception` handler, leaving only the `block_num <= head` comparison for the soft-ban decision. -### B10 (Critical): All Real Witnesses Must Be Disabled On Emergency Activation +### B10 (Critical): All Real validators Must Be Disabled On Emergency Activation -**Problem**: When emergency consensus activated, real witnesses retained their `signing_key` values. If they had valid signing keys but were offline, the hybrid schedule assigned them slots — but they could never produce blocks at those slots. The schedule had a mix of online committee and offline real witnesses, making block production unreliable. +**Problem**: When emergency consensus activated, real validators retained their `signing_key` values. If they had valid signing keys but were offline, the hybrid schedule assigned them slots — but they could never produce blocks at those slots. The schedule had a mix of online committee and offline real validators, making block production unreliable. -**Fix**: On emergency activation, **all real witnesses are immediately disabled**: `signing_key` set to `public_key_type()` (null), `penalty_percent` reset to 0, `counted_votes` restored to `votes`, `current_run` reset to 0. All `witness_penalty_expire_object` entries are removed. This ensures the initial schedule is all-committee (100% available). Operators must explicitly re-register witnesses via `witness_update_operation` transactions. +**Fix**: On emergency activation, **all real validators are immediately disabled**: `signing_key` set to `public_key_type()` (null), `penalty_percent` reset to 0, `counted_votes` restored to `votes`, `current_run` reset to 0. All `witness_penalty_expire_object` entries are removed. This ensures the initial schedule is all-committee (100% available). Operators must explicitly re-register validators via `witness_update_operation` transactions. ### B11 (Critical): Schedule Crash — `get_witness("")` on Empty Schedule -**Problem**: During emergency, the normal schedule build in `update_witness_schedule()` iterates all witnesses but skips any with null `signing_key`. Since B10 zeroes all keys, `sum_witnesses_count = 0` → all 21 slots are set to `account_name_type()` (empty string). The execution order was: +**Problem**: During emergency, the normal schedule build in `update_witness_schedule()` iterates all validators but skips any with null `signing_key`. Since B10 zeroes all keys, `sum_witnesses_count = 0` → all 21 slots are set to `account_name_type()` (empty string). The execution order was: 1. `modify(wso, ...)` — builds normal schedule (all slots zeroed) 2. `update_median_witness_props()` — iterates schedule, calls `get_witness("")` → **crash: `unknown key`** 3. Emergency hybrid override — would have filled empty slots with committee, but runs too late -**Fix**: Moved the emergency hybrid schedule override to execute **before** `update_median_witness_props()`. The hybrid override fills empty/unavailable slots with committee first, so by the time `update_median_witness_props()` runs, all 21 slots contain valid witness names. +**Fix**: Moved the emergency hybrid schedule override to execute **before** `update_median_witness_props()`. The hybrid override fills empty/unavailable slots with committee first, so by the time `update_median_witness_props()` runs, all 21 slots contain valid validator names. ### B12 (Critical): Permanently Corrupted Schedule After LIB=HEAD Commit -**Problem**: During emergency, all 21 schedule slots point to the same committee witness. LIB computation via `nth_element` yields `last_supported_block_num == HEAD`. In `_apply_block`, the execution order is: +**Problem**: During emergency, all 21 schedule slots point to the same committee validator. LIB computation via `nth_element` yields `last_supported_block_num == HEAD`. In `_apply_block`, the execution order is: 1. `update_last_irreversible_block()` (line ~4455) — calls `commit(HEAD)`, which **destroys the undo session** for the current block 2. `update_witness_schedule()` (line ~4466) — zeros all slots (pre-B11 fix), then crashes at `get_witness("")` @@ -300,7 +300,7 @@ Since `commit(HEAD)` already consumed the undo session, the zeroed schedule from ### B13 (Critical): Stack Buffer Overflow in `get_block_post_validations()` -**Problem**: During emergency mode, the committee account fills 18 of 21 schedule slots. `get_block_post_validations()` iterates all `block_post_validation_object` entries and matches each against the schedule. For each match, it writes an entry to a fixed-size array of `CHAIN_MAX_BLOCK_POST_VALIDATION_COUNT = 20`. With the committee occupying 18 slots, each validation object generates up to 18 matches — a single object with its matching witnesses could write up to 18 entries per iteration. With 20 objects, this produced up to 360 writes into a 20-element array, causing a stack buffer overflow and silent segfault. +**Problem**: During emergency mode, the committee account fills 18 of 21 schedule slots. `get_block_post_validations()` iterates all `block_post_validation_object` entries and matches each against the schedule. For each match, it writes an entry to a fixed-size array of `CHAIN_MAX_BLOCK_POST_VALIDATION_COUNT = 20`. With the committee occupying 18 slots, each validation object generates up to 18 matches — a single object with its matching validators could write up to 18 entries per iteration. With 20 objects, this produced up to 360 writes into a 20-element array, causing a stack buffer overflow and silent segfault. **Fix** (two-part): 1. **Bounds check**: Break the inner loop when the result array reaches `CHAIN_MAX_BLOCK_POST_VALIDATION_COUNT`. @@ -322,23 +322,23 @@ Since `commit(HEAD)` already consumed the undo session, the zeroed schedule from 3. After 50 strikes, set `fork_rejected_until = now + 300s` (5 minute soft-ban). Reset strikes on legitimate sync (peer genuinely ahead). 4. At the observed spam rate (~23 requests per 4ms burst), the threshold is hit in under 1 second. -### B16 (High): Null Witness Signing Key Crashes Block Validation +### B16 (High): Null validator Signing Key Crashes Block Validation -**Problem**: When a witness has a null/empty `signing_key` (disabled via `shutdown_witness_operation` or emergency activation), and a block from that witness arrives via P2P, `validate_block_header()` calls `validate_signee(witness.signing_key)` with the empty key. The elliptic curve library crashes with `my->_key != empty_pub` deep inside `serialize()` — an opaque assertion that gives no indication of the root cause. The node then enters an infinite retry loop: gap fill requests the same block, receives it, rejects it with the crash, requests it again. +**Problem**: When a validator has a null/empty `signing_key` (disabled via `shutdown_witness_operation` or emergency activation), and a block from that validator arrives via P2P, `validate_block_header()` calls `validate_signee(validator.signing_key)` with the empty key. The elliptic curve library crashes with `my->_key != empty_pub` deep inside `serialize()` — an opaque assertion that gives no indication of the root cause. The node then enters an infinite retry loop: gap fill requests the same block, receives it, rejects it with the crash, requests it again. **Fix** (two parts): -1. **Clear error message**: Added a pre-check in `validate_block_header()` before `validate_signee()` that catches `witness.signing_key == public_key_type()` and throws a descriptive `FC_ASSERT` naming the witness and block number. +1. **Clear error message**: Added a pre-check in `validate_block_header()` before `validate_signee()` that catches `validator.signing_key == public_key_type()` and throws a descriptive `FC_ASSERT` naming the validator and block number. 2. **Gap fill blacklist**: Added rejection tracking in the DLT P2P node. When the same block is rejected 3 times (from any combination of broadcast + gap fill), gap fill is blacklisted for 120 seconds, breaking the infinite retry loop. -### B17 (Critical): Emergency Witness Blanking Was Non-Consensus +### B17 (Critical): Emergency validator Blanking Was Non-Consensus -**Problem**: The emergency witness blanking logic in `update_witness_schedule()` used `_emergency_round_start_gap` — a local `std::map` that is NOT consensus state. It was populated at runtime and not stored in shared memory. Different nodes could compute different values depending on startup timing, replay history, and when they entered emergency mode. This meant two nodes could reach different conclusions about whether to blank a witness's signing key, causing chain divergence. +**Problem**: The emergency validator blanking logic in `update_witness_schedule()` used `_emergency_round_start_gap` — a local `std::map` that is NOT consensus state. It was populated at runtime and not stored in shared memory. Different nodes could compute different values depending on startup timing, replay history, and when they entered emergency mode. This meant two nodes could reach different conclusions about whether to blank a validator's signing key, causing chain divergence. **Fix**: Replaced the non-consensus approach with the same deterministic pattern used in normal mode. Key-blanking now happens in `update_global_dynamic_data()` (the consensus path) and checks `head_block_num() - last_confirmed_block_num > CHAIN_EMERGENCY_MAX_WITNESS_MISSED_BLOCKS` (105). Both values are on-chain consensus fields in `witness_object` shared memory — all nodes compute identical results. Removed `_emergency_round_start_gap` entirely. ### Committee Neutral Voter Design -After all fixes, the committee witness has these properties: +After all fixes, the committee validator has these properties: | Field | Value | Rationale | |---|---|---| @@ -370,11 +370,11 @@ VIZ75CRHVHPwYiUESy1bgN3KhVFbZCQQRA9jT6TnpzKAmpxMPD6Xv - Config template (`config_witness.ini`) - Documentation -Any node running the witness plugin with `emergency-private-key` configured will attempt to produce blocks during emergency mode. This is by design — the goal is maximum availability during a network stall, not access control. +Any node running the Validator Plugin with `emergency-private-key` configured will attempt to produce blocks during emergency mode. This is by design — the goal is maximum availability during a network stall, not access control. ### How Many Nodes Have It -In practice: **every public witness node** should have it configured. During emergency: +In practice: **every public validator node** should have it configured. During emergency: - Multiple nodes may attempt to produce at the same slot. - Hash tie-breaking (lowest `block_id` wins) ensures deterministic convergence. - Fork collision check causes all but one node to back off after the first slot. @@ -386,7 +386,7 @@ In practice: **every public witness node** should have it configured. During eme | Attack | Impact | Mitigation | |---|---|---| -| Attacker produces emergency blocks during normal operation | **None.** Emergency mode only activates when `seconds_since_lib >= 3600`. During normal operation, the schedule does not contain `committee`, so the attacker's blocks are invalid (wrong scheduled witness). | Consensus-level gating | +| Attacker produces emergency blocks during normal operation | **None.** Emergency mode only activates when `seconds_since_lib >= 3600`. During normal operation, the schedule does not contain `committee`, so the attacker's blocks are invalid (wrong scheduled validator). | Consensus-level gating | | Attacker produces blocks during real emergency | Blocks are valid but **compete equally** with other emergency producers. Hash tie-breaking resolves conflicts deterministically. The attacker cannot produce *more* blocks than any other node with the key. | Hash tie-breaking + fork collision check | | Attacker produces blocks with invalid transactions | **Rejected.** Full consensus validation still applies to emergency blocks. Invalid operations, double-spends, etc. are caught by `apply_block()`. | Standard block validation | | Attacker floods P2P with emergency blocks | **Mitigated.** P2P anti-spam (`fork_rejected_until`) soft-bans peers sending blocks on rejected forks for 15 minutes (5 minutes for trusted peers). Sync request spam is detected separately: 50 repeated competing-fork sync requests trigger a 5-minute soft-ban, silently discarding further requests. Fork collision check limits production to 1 block per slot. After soft-ban expires, `inhibit_fetching_sync_blocks` is automatically reset (B8 fix) so the peer remains available for sync. | P2P soft-ban + sync spam ban + fork collision + auto-reset | @@ -402,8 +402,8 @@ In practice: **every public witness node** should have it configured. During eme ### Can Emergency Produce Non-Empty Blocks? **Yes, and this is required.** Emergency blocks must allow transactions because: -1. Witnesses need to broadcast `witness_update_operation` to re-register their signing key during recovery. -2. If emergency blocks were forced empty, witnesses couldn't re-activate → deadlock where emergency never exits. +1. validators need to broadcast `witness_update_operation` to re-register their signing key during recovery. +2. If emergency blocks were forced empty, validators couldn't re-activate → deadlock where emergency never exits. 3. In practice, most emergency blocks will be empty (low transaction volume during a stall), but the mechanism **must not** prohibit transactions. ### Summary @@ -414,7 +414,7 @@ The emergency key is a **public coordination mechanism**, not a secret. Its secu - **Snapshot safety**: When LIB block is unavailable (post-snapshot restore), emergency check is skipped to prevent false activation - **Convergence**: Hash tie-breaking + fork collision → single effective producer - **Validation**: Full consensus rules apply to emergency blocks -- **Deactivation**: Automatic when 75% (16/21) of schedule slots are real witnesses with valid signing keys +- **Deactivation**: Automatic when 75% (16/21) of schedule slots are real validators with valid signing keys - **Scope**: Delegates can immediately re-activate through wallets/services once their nodes reconnect to the P2P network, because the emergency chain is public and accepts transactions --- @@ -427,35 +427,35 @@ All scenarios should be tested before deployment. Each test specifies preconditi | | | |---|---| -| **Precondition** | 21 witnesses active, network healthy | -| **Action** | Shut down all 21 witnesses. Wait >1 hour. Start 1 node with emergency key. Gradually restart witnesses. | -| **Expected** | Emergency activates at LIB+3600s. All real witnesses disabled (signing_key zeroed). Committee produces blocks (full 21-slot schedule). LIB advances every block (capped at HEAD−1). Offline witnesses do NOT accumulate penalties. Committee props synced to median, hardfork vote synced to current version. Witnesses re-register via `witness_update_operation`. When 16+ real witnesses have valid signing keys in schedule (75%) → emergency exits automatically. | -| **Components** | Activation, witness disabling, hybrid schedule (full expansion), LIB advancement (capped at HEAD−1), penalty skip, committee neutral voter, exit condition (75% real witnesses) | +| **Precondition** | 21 validators active, network healthy | +| **Action** | Shut down all 21 validators. Wait >1 hour. Start 1 node with emergency key. Gradually restart validators. | +| **Expected** | Emergency activates at LIB+3600s. All real validators disabled (signing_key zeroed). Committee produces blocks (full 21-slot schedule). LIB advances every block (capped at HEAD−1). Offline validators do NOT accumulate penalties. Committee props synced to median, hardfork vote synced to current version. validators re-register via `witness_update_operation`. When 16+ real validators have valid signing keys in schedule (75%) → emergency exits automatically. | +| **Components** | Activation, validator disabling, hybrid schedule (full expansion), LIB advancement (capped at HEAD−1), penalty skip, committee neutral voter, exit condition (75% real validators) | ### T2: 2-Way Partition (Majority/Minority) | | | |---|---| -| **Precondition** | 21 witnesses, healthy network | -| **Action** | Partition: 16 witnesses on side A, 5 on side B. | -| **Expected** | Side A: participation >75% → continues normally, no emergency. Side B: participation drops to ~24% → production stops (below 33% threshold). After 1 hour, emergency activates on side B. On reconnect, side A's chain wins (higher vote weight from 16 real witnesses). Side B unwinds emergency blocks. | +| **Precondition** | 21 validators, healthy network | +| **Action** | Partition: 16 validators on side A, 5 on side B. | +| **Expected** | Side A: participation >75% → continues normally, no emergency. Side B: participation drops to ~24% → production stops (below 33% threshold). After 1 hour, emergency activates on side B. On reconnect, side A's chain wins (higher vote weight from 16 real validators). Side B unwinds emergency blocks. | | **Components** | Three-state safety (healthy vs distressed), vote-weighted comparison, fork resolution | ### T3: 2-Way Partition (Even Split, Both Enter Emergency) | | | |---|---| -| **Precondition** | 21 witnesses, healthy network | -| **Action** | Partition: 10 witnesses on A, 11 on B. Neither side has 75% → both stall. Wait 1 hour → both enter emergency. Reconnect after 2 hours. | -| **Expected** | Both sides produce emergency+hybrid blocks. LIB advances (capped at HEAD−1) on both. On reconnect, vote-weighted comparison: side with higher total `votes` wins. Losing side unwinds. Emergency exits when 16+ real witnesses have valid keys in the merged schedule. | +| **Precondition** | 21 validators, healthy network | +| **Action** | Partition: 10 validators on A, 11 on B. Neither side has 75% → both stall. Wait 1 hour → both enter emergency. Reconnect after 2 hours. | +| **Expected** | Both sides produce emergency+hybrid blocks. LIB advances (capped at HEAD−1) on both. On reconnect, vote-weighted comparison: side with higher total `votes` wins. Losing side unwinds. Emergency exits when 16+ real validators have valid keys in the merged schedule. | | **Components** | LIB advancement (capped), vote-weighted comparison, fork_db expansion, partition merge | ### T4: 3-Way Partition | | | |---|---| -| **Precondition** | 21 witnesses, healthy network | -| **Action** | Partition into 3 groups: 7+7+7 witnesses. All stall → all enter emergency. Reconnect sequentially (A↔B first, then AB↔C). | +| **Precondition** | 21 validators, healthy network | +| **Action** | Partition into 3 groups: 7+7+7 validators. All stall → all enter emergency. Reconnect sequentially (A↔B first, then AB↔C). | | **Expected** | Each partition produces emergency blocks independently. LIB advances (capped at HEAD−1) on all. First merge (A↔B): vote-weighted comparison picks winner. Second merge (AB↔C): vote-weighted comparison again. Final chain has highest cumulative vote weight. | | **Components** | Multi-partition merge, vote-weighted comparison, cascading fork resolution, LIB advancement (capped) | @@ -463,10 +463,10 @@ All scenarios should be tested before deployment. Each test specifies preconditi | | | |---|---| -| **Precondition** | Emergency active for 2 hours. 10 witnesses already back. | -| **Action** | Witness #11 comes online with stale chain (2 hours behind). | -| **Expected** | Peer syncs from the emergency chain. Once synced, witness re-registers via `witness_update_operation`. Their slot appears in hybrid schedule on next update. Witness produces at its assigned slot. LIB on the emergency chain is at HEAD−1, so only 1 reversible block exists — sync is fast. | -| **Components** | P2P sync during emergency, hybrid schedule, LIB advancement (capped), witness re-registration | +| **Precondition** | Emergency active for 2 hours. 10 validators already back. | +| **Action** | validator #11 comes online with stale chain (2 hours behind). | +| **Expected** | Peer syncs from the emergency chain. Once synced, validator re-registers via `witness_update_operation`. Their slot appears in hybrid schedule on next update. validator produces at its assigned slot. LIB on the emergency chain is at HEAD−1, so only 1 reversible block exists — sync is fast. | +| **Components** | P2P sync during emergency, hybrid schedule, LIB advancement (capped), validator re-registration | ### T6: Conflicting Emergency Producers @@ -481,18 +481,18 @@ All scenarios should be tested before deployment. Each test specifies preconditi | | | |---|---| -| **Precondition** | All witnesses offline. Emergency active. | +| **Precondition** | All validators offline. Emergency active. | | **Action** | Let emergency run for 8+ hours (well beyond old undo limit). | | **Expected** | LIB advances every block (capped at HEAD−1), so HEAD−LIB gap stays at exactly 1. fork_db size stays at 2 blocks. Emergency runs indefinitely without hitting any undo limits. No degradation over time. | | **Components** | LIB advancement (capped at HEAD−1), fork_db sizing, indefinite emergency operation | -### T8: Witness Shutdown + Re-registration During Emergency +### T8: validator Shutdown + Re-registration During Emergency | | | |---|---| -| **Precondition** | Emergency active. All witnesses had `signing_key` nullified by missed-block shutdown. | -| **Action** | Witness operator broadcasts `witness_update_operation` via CLI wallet during emergency. | -| **Expected** | Transaction included in emergency block. Witness object updated with new signing key. Next schedule update: witness gets their slot in hybrid schedule instead of committee. Witness begins producing. Offline witnesses do NOT get `signing_key` nullified again during emergency (penalty/shutdown skipped). | +| **Precondition** | Emergency active. All validators had `signing_key` nullified by missed-block shutdown. | +| **Action** | validator operator broadcasts `witness_update_operation` via CLI wallet during emergency. | +| **Expected** | Transaction included in emergency block. validator object updated with new signing key. Next schedule update: validator gets their slot in hybrid schedule instead of committee. validator begins producing. Offline validators do NOT get `signing_key` nullified again during emergency (penalty/shutdown skipped). | | **Components** | Transaction processing during emergency, hybrid schedule update, penalty skip during emergency | ### T9: Snapshot Restore + Emergency Interaction @@ -500,7 +500,7 @@ All scenarios should be tested before deployment. Each test specifies preconditi | | | |---|---| | **Precondition** | Snapshot taken during emergency mode (`emergency_consensus_active = true`). | -| **Action** | Restore snapshot on a fresh node. Start node with witness plugin and emergency key. | +| **Action** | Restore snapshot on a fresh node. Start node with Validator Plugin and emergency key. | | **Expected** | Snapshot import reads `emergency_consensus_active` and `emergency_consensus_start_block` from DGP (forward-compatible). Node resumes in emergency mode. Produces emergency blocks. Standard exit condition applies. **No false activation**: block_log is empty after snapshot restore, so `fetch_block_by_number(LIB)` returns invalid, but the emergency check is skipped (not triggered by fallback to genesis_time). | | **Components** | Snapshot import (forward-compatible fields), emergency state persistence, B7 fix (no false activation on empty block_log) | @@ -517,36 +517,36 @@ All scenarios should be tested before deployment. Each test specifies preconditi | | | |---|---| -| **Precondition** | HF12 active. All 21 witnesses online. Network healthy. | -| **Action** | Normal operation for extended period. Occasional witness restarts. | -| **Expected** | Emergency never activates (LIB advances every few seconds). Three-state safety: healthy mode enforces safe defaults regardless of `enable-stale-production` config. Vote-weighted comparison active but functionally equivalent to longest-chain (same witnesses on both sides of any micro-fork). Committee exclusion in hardfork tally and median props is a no-op (committee not in schedule). | +| **Precondition** | HF12 active. All 21 validators online. Network healthy. | +| **Action** | Normal operation for extended period. Occasional validator restarts. | +| **Expected** | Emergency never activates (LIB advances every few seconds). Three-state safety: healthy mode enforces safe defaults regardless of `enable-stale-production` config. Vote-weighted comparison active but functionally equivalent to longest-chain (same validators on both sides of any micro-fork). Committee exclusion in hardfork tally and median props is a no-op (committee not in schedule). | | **Components** | No-regression, three-state safety (healthy mode), vote-weighted comparison | ### T12: `enable-stale-production` Ignored in Healthy Mode | | | |---|---| -| **Precondition** | HF12 active. All witnesses online. Operator has `enable-stale-production = true` (forgot to revert from pre-HF12). | -| **Action** | Network partition isolates this witness. | -| **Expected** | Participation rate ≥33% → healthy mode → `enable-stale-production` is **ignored**. Witness stops producing when it detects it's isolated (no recent blocks). **This is the core micro-fork prevention feature.** | +| **Precondition** | HF12 active. All validators online. Operator has `enable-stale-production = true` (forgot to revert from pre-HF12). | +| **Action** | Network partition isolates this validator. | +| **Expected** | Participation rate ≥33% → healthy mode → `enable-stale-production` is **ignored**. validator stops producing when it detects it's isolated (no recent blocks). **This is the core micro-fork prevention feature.** | | **Components** | Three-state safety (healthy mode auto-enforces safe defaults) | -### T13: Partial Witness Set (11 Top Witnesses on 1 Node) +### T13: Partial validator Set (11 Top validators on 1 Node) | | | |---|---| -| **Precondition** | Network stalled >1 hour. 1 node with 11 top witnesses + emergency key. 10 other witnesses offline. | -| **Action** | Emergency activates. 11 real witnesses produce at their slots. Committee fills the other 10 slots. Over time, other witnesses re-join. | -| **Expected** | All witnesses initially disabled (signing_key zeroed). 11 witnesses re-register via `witness_update_operation`. Hybrid schedule expands to full 21 slots (11 real + 10 committee). Committee witness has `props = median_props` and `hardfork_version_vote = current_hardfork_version` — neutral voter. Hardfork vote tally excludes committee (only 11 real votes counted). Median props computed from real witnesses only. Offline witnesses don't accumulate penalties. When 16+ real witnesses have valid signing keys in schedule (75% of 21) → emergency exits automatically. | -| **Components** | Witness disabling, hybrid schedule expansion, committee neutral voter, hardfork tally exclusion, median props exclusion, penalty skip, exit condition (75% real witnesses) | +| **Precondition** | Network stalled >1 hour. 1 node with 11 top validators + emergency key. 10 other validators offline. | +| **Action** | Emergency activates. 11 real validators produce at their slots. Committee fills the other 10 slots. Over time, other validators re-join. | +| **Expected** | All validators initially disabled (signing_key zeroed). 11 validators re-register via `witness_update_operation`. Hybrid schedule expands to full 21 slots (11 real + 10 committee). Committee validator has `props = median_props` and `hardfork_version_vote = current_hardfork_version` — neutral voter. Hardfork vote tally excludes committee (only 11 real votes counted). Median props computed from real validators only. Offline validators don't accumulate penalties. When 16+ real validators have valid signing keys in schedule (75% of 21) → emergency exits automatically. | +| **Components** | validator disabling, hybrid schedule expansion, committee neutral voter, hardfork tally exclusion, median props exclusion, penalty skip, exit condition (75% real validators) | ### T14: Committee Hardfork Vote Neutrality | | | |---|---| -| **Precondition** | Emergency active. Binary version includes a pending hardfork (e.g., HF13) that has not been applied on-chain yet. 11 real witnesses running HF13 binary. | +| **Precondition** | Emergency active. Binary version includes a pending hardfork (e.g., HF13) that has not been applied on-chain yet. 11 real validators running HF13 binary. | | **Action** | Committee produces blocks. Verify committee's on-chain `hardfork_version_vote` stays at the current applied version. | -| **Expected** | Committee's block headers do NOT contain `hardfork_version_vote` extensions. `process_header_extensions()` does not update the committee's on-chain vote. Committee vote stays at `current_hardfork_version` (e.g., HF12). Only real witnesses' votes count toward HF13 adoption (need 17 of them). Committee props/hardfork vote re-synced every schedule update. | +| **Expected** | Committee's block headers do NOT contain `hardfork_version_vote` extensions. `process_header_extensions()` does not update the committee's on-chain vote. Committee vote stays at `current_hardfork_version` (e.g., HF12). Only real validators' votes count toward HF13 adoption (need 17 of them). Committee props/hardfork vote re-synced every schedule update. | | **Components** | Hardfork vote auto-injection skip, process_header_extensions, committee props sync | ### T15: Snapshot Restore Does Not False-Activate Emergency @@ -555,7 +555,7 @@ All scenarios should be tested before deployment. Each test specifies preconditi |---|---| | **Precondition** | Network healthy. Snapshot taken from a healthy state (`emergency_consensus_active = false`). | | **Action** | Restore snapshot on a fresh node (block_log empty). Start syncing from p2p. | -| **Expected** | First block from p2p: `fetch_block_by_number(LIB)` returns invalid (block_log empty). `lib_time_available = false`. Emergency check is **skipped**. Node processes the block normally. No false activation, no committee witness created, no penalties reset. Node syncs normally. | +| **Expected** | First block from p2p: `fetch_block_by_number(LIB)` returns invalid (block_log empty). `lib_time_available = false`. Emergency check is **skipped**. Node processes the block normally. No false activation, no committee validator created, no penalties reset. Node syncs normally. | | **Components** | B7 fix (lib_time_available guard), snapshot restore, block_log interaction | ### T16: Soft-Ban `inhibit_fetching_sync_blocks` Reset After Expiry @@ -571,7 +571,7 @@ All scenarios should be tested before deployment. Each test specifies preconditi | | | |---|---| -| **Precondition** | Node crashed during emergency with corrupted schedule (all empty witness slots in shared memory). | +| **Precondition** | Node crashed during emergency with corrupted schedule (all empty validator slots in shared memory). | | **Action** | Restart the node. | | **Expected** | `database::open()` detects empty slots in schedule. Fills all 21 slots with committee. Sets `emergency_consensus_active = true` if not already set. Restores `fork_db.set_emergency_mode(true)`. Node resumes producing emergency blocks. LIB advances normally (capped at HEAD−1). | | **Components** | Startup recovery (B12), schedule repair, fork_db flag restoration | diff --git a/.qoder/docs/emergency-consensus-workflow.md b/.qoder/docs/emergency-consensus-workflow.md index d3edcdbcc6..35a4662a1f 100644 --- a/.qoder/docs/emergency-consensus-workflow.md +++ b/.qoder/docs/emergency-consensus-workflow.md @@ -1,4 +1,4 @@ -# Emergency Consensus Mode — Full Workflow Tree +# Emergency Consensus Mode — Full Workflow Tree Comprehensive analysis of the emergency consensus system introduced in Hardfork 12. Covers all processes, code paths, component interactions, and guard conditions across the entire node codebase. @@ -6,7 +6,7 @@ Comprehensive analysis of the emergency consensus system introduced in Hardfork ## 1. Overview -Emergency consensus mode activates when the network has stalled for 1 hour (no blocks have been accepted since the Last Irreversible Block timestamp). During emergency, a special "committee" witness produces blocks to maintain chain continuity. Once enough real witnesses re-enable their signing keys (\(\ge\) 75% of schedule slots), emergency mode auto-deactivates. +Emergency consensus mode activates when the network has stalled for 1 hour (no blocks have been accepted since the Last Irreversible Block timestamp). During emergency, a special "committee" validator produces blocks to maintain chain continuity. Once enough real validators re-enable their signing keys (\(\ge\) 75% of schedule slots), emergency mode auto-deactivates. ### Key Constants @@ -15,9 +15,9 @@ Emergency consensus mode activates when the network has stalled for 1 hour (no b | `CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC` | 3600 | Seconds since LIB before activation | | `CHAIN_EMERGENCY_WITNESS_ACCOUNT` | `"committee"` | Emergency block producer | | `CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY` | `VIZ75CR...` | Deterministic signing key | -| `CHAIN_EMERGENCY_EXIT_NORMAL_BLOCKS` | 21 | Consecutive real-witness blocks for exit | +| `CHAIN_EMERGENCY_EXIT_NORMAL_BLOCKS` | 21 | Consecutive real-validator blocks for exit | | `CHAIN_IRREVERSIBLE_THRESHOLD` | 75% (`75 * CHAIN_1_PERCENT`) | Required to advance LIB / exit emergency | -| `CHAIN_MAX_WITNESSES` | 21 | Maximum unique witness slots | +| `CHAIN_MAX_WITNESSES` | 21 | Maximum unique validator slots | | `CHAIN_HARDFORK_12` | Hardfork #12 | Gates all emergency logic | ### System State @@ -64,8 +64,8 @@ Block applied (update_global_dynamic_data) │ dgp.emergency_consensus_active = true │ dgp.emergency_consensus_start_block = b.block_num() │ - ├── 2. Create/Update emergency witness object: - │ ├── Witness "committee" exists? + ├── 2. Create/Update emergency validator object: + │ ├── validator "committee" exists? │ │ ├── No → create: │ │ │ owner = "committee" │ │ │ signing_key = CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY @@ -77,8 +77,8 @@ Block applied (update_global_dynamic_data) │ │ schedule = top │ │ sync version + hardfork votes + props │ │ - │ ├── 3. Disable ALL real witnesses: - │ │ For each witness (except committee): + │ ├── 3. Disable ALL real validators: + │ │ For each validator (except committee): │ │ signing_key = zero (public_key_type()) │ │ penalty_percent = 0 │ │ counted_votes = votes @@ -86,7 +86,7 @@ Block applied (update_global_dynamic_data) │ │ │ ├── 4. Remove ALL penalty expiration objects │ │ - │ ├── 5. Override witness schedule: + │ ├── 5. Override validator schedule: │ │ All num_scheduled_witnesses slots → "committee" │ │ next_shuffle_block_num = head + num_scheduled │ │ @@ -111,8 +111,8 @@ Block applied (update_global_dynamic_data) ``` update_witness_schedule() │ - ├── 1. Build normal witness schedule - │ (may have empty slots if witnesses have zero signing_key) + ├── 1. Build normal validator schedule + │ (may have empty slots if validators have zero signing_key) │ ├── Gate: has_hardfork(HF12) && emergency_consensus_active? │ └── No → skip hybrid override, proceed with normal schedule @@ -122,37 +122,37 @@ update_witness_schedule() ├── For each slot i in [0, CHAIN_MAX_WITNESSES) (by CHAIN_BLOCK_WITNESS_REPEAT): │ ├── Slot name == "" ? │ │ └── Fill slot + repeats with "committee" → committee_slots++ - │ ├── Witness has signing_key != zero ? - │ │ └── Keep witness → real_witness_slots++ - │ └── Witness unavailable (key=0 or not found)? + │ ├── validator has signing_key != zero ? + │ │ └── Keep validator → real_witness_slots++ + │ └── validator unavailable (key=0 or not found)? │ └── Fill slot + repeats with "committee" → committee_slots++ │ ├── Expand: num_scheduled_witnesses = CHAIN_MAX_WITNESSES × REPEAT ├── Set: next_shuffle_block_num = head + num_scheduled ├── Log: "Emergency hybrid schedule: R real, C committee slots" │ - ├── Sync committee witness: + ├── Sync committee validator: │ props = current median_props │ hardfork votes = current applied version (neutral voter) │ └── === EXIT CHECK === ├── exit_threshold = (CHAIN_MAX_WITNESSES × 75%) / 100 (= 15) ├── real_witness_slots >= exit_threshold? - │ ├── No → emergency continues (not enough real witnesses) + │ ├── No → emergency continues (not enough real validators) │ └── Yes → DEACTIVATE: │ ├── dgp.emergency_consensus_active = false │ ├── _fork_db.set_emergency_mode(false) │ └── Log: "EMERGENCY CONSENSUS MODE deactivated at block #N. - │ R real witnesses active (threshold: T)." + │ R real validators active (threshold: T)." │ - └── After deactivation: witnesses with restored keys produce normally + └── After deactivation: validators with restored keys produce normally ``` --- -### 2C. Witness Block Production in Emergency Mode +### 2C. validator Block Production in Emergency Mode -**Location:** [`witness_plugin::maybe_produce_block()`](file://plugins/witness/witness.cpp#L468-L870) +**Location:** [`witness_plugin::maybe_produce_block()`](file://plugins/validator/validator.cpp#L468-L870) ``` maybe_produce_block() {now = NTP + 250ms} @@ -180,7 +180,7 @@ maybe_produce_block() {now = NTP + 250ms} │ │ │ ├── Yes → _production_enabled = true │ │ │ └── No → return not_synced │ │ └── _witnesses.empty()? - │ │ └── Yes → ERROR: "no witnesses configured" + │ │ └── Yes → ERROR: "no validators configured" │ │ │ ├── Not emergency, participation >= 33%? │ │ └── Yes → HEALTHY PATH: @@ -192,12 +192,12 @@ maybe_produce_block() {now = NTP + 250ms} │ ├── Honor enable-stale-production override │ └── Else: check sync + participation normally │ - ├── Block post-validation broadcast (every witness we have, scheduled only) + ├── Block post-validation broadcast (every validator we have, scheduled only) │ ├── === MINORITY FORK DETECTION === │ │ │ ├── NOT emergency: standard 21-block check - │ │ └── All 21 blocks from our witnesses? + │ │ └── All 21 blocks from our validators? │ │ ├── stale-production enabled → continue │ │ └── stale-production disabled → resync_from_lib() │ │ @@ -207,7 +207,7 @@ maybe_produce_block() {now = NTP + 250ms} │ │ │ └── Emergency + DLT + NOT MASTER (follower): │ └── 21-block check (1 full round via CHAIN_MAX_WITNESSES) - │ └── All 21 blocks from our witnesses? + │ └── All 21 blocks from our validators? │ └── Yes → DLT EMERGENCY MINORITY FORK: │ resync_from_lib() [but see guard in §2G below] │ @@ -224,7 +224,7 @@ maybe_produce_block() {now = NTP + 250ms} │ ├── Emergency mode: ANY block at this height IS competing │ │ → defer to fork_db deterministic hash resolution │ │ - │ └── Normal mode: only different witness + different parent + │ └── Normal mode: only different validator + different parent │ → vote-weight comparison │ └── generate_block() with committee private key @@ -315,7 +315,7 @@ fork_db._push_block(item) update_last_irreversible_block() │ ├── Normal LIB computation: - │ ├── Collect witness objects for all schedule slots + │ ├── Collect validator objects for all schedule slots │ ├── nth_element by last_supported_block_num │ └── new_lib = wit_objs[offset]->last_supported_block_num │ where offset = (100% - 75%) × num_witnesses / 100% @@ -331,7 +331,7 @@ update_last_irreversible_block() │ │ would leave permanently corrupted state (zeroed schedule). │ └── No → use computed value │ - ├── Committee witness: current_run advances by CHAIN_BLOCK_WITNESS_REPEAT each block + ├── Committee validator: current_run advances by CHAIN_BLOCK_WITNESS_REPEAT each block │ → after 3 blocks (CHAIN_IRREVERSIBLE_SUPPORT_MIN_RUN), LIB moves every block │ → gap between LIB and HEAD stays small → fork_db won't overflow │ @@ -440,15 +440,15 @@ resync_from_lib() — called from minority fork detection ``` verify_signing_witness(next_block) │ - ├── Normal mode: FC_ASSERT(witness.owner == scheduled_witness) - │ └── "Witness produced block at wrong time" + ├── Normal mode: FC_ASSERT(validator.owner == scheduled_witness) + │ └── "validator produced block at wrong time" │ └── Emergency mode: - └── If block.witness != scheduled_witness: + └── If block.validator != scheduled_witness: └── dlog (debug, not assertion) "Emergency mode: accepting block from BW at slot scheduled for SW" - → Block accepted regardless of slot-to-witness mapping - → Signature still validated against block.witness's signing_key + → Block accepted regardless of slot-to-validator mapping + → Signature still validated against block.validator's signing_key ``` --- @@ -503,7 +503,7 @@ handle_block(blk_msg, sync_mode) │ └── sync_mode AND gap 0-2 AND dlt_mode AND block_age < 30s? │ ├── Yes → treat as NORMAL block (sync_mode = false) │ │ Reason: Prevents "Syncing Blockchain started" triggers - │ │ when only a few blocks behind. Emergency witnesses must + │ │ when only a few blocks behind. Emergency validators must │ │ continue producing — entering full sync mode would set │ │ currently_syncing=true and disrupt the production loop. │ └── No → keep sync_mode @@ -515,12 +515,12 @@ handle_block(blk_msg, sync_mode) --- -### 2L. Witness Guard — Emergency-Aware Key Restoration +### 2L. validator guard — Emergency-Aware Key Restoration **Location:** [`witness_guard::plugin.cpp`](file://plugins/witness_guard/witness_guard.cpp#L87-L107) ``` -Witness key auto-restore check: +validator key auto-restore check: │ ├── stale_production_config override active? │ ├── Non-emergency + participation >= 33% → auto-clear stale flag @@ -541,7 +541,7 @@ Witness key auto-restore check: **Location:** [`database::update_witness_schedule()`](file://libraries/chain/database.cpp#L2553-L2564) ``` -During emergency mode, committee witness is excluded from: +During emergency mode, committee validator is excluded from: ├── running_version (majority_version) tally └── hardfork_version_vote tally @@ -552,7 +552,7 @@ majority_version to 0.0.0, blocking hardfork progression. --- -### 2N. Median Witness Props — Committee Exclusion +### 2N. Median validator Props — Committee Exclusion **Location:** [`database::update_median_witness_props()`](file://libraries/chain/database.cpp#L2796-L2820) @@ -561,7 +561,7 @@ update_median_witness_props(): └── Excludes CHAIN_EMERGENCY_WITNESS_ACCOUNT from the active set when emergency_consensus_active is true. -Reason: Committee witness copies current median_props and should +Reason: Committee validator copies current median_props and should not skew the median computation. Its entries are invisible to the median — they reinforce the existing value. ``` @@ -573,9 +573,9 @@ the median — they reinforce the existing value. ```mermaid graph TB subgraph "NORMAL OPERATION" - N1[Blocks produced by witnesses] + N1[Blocks produced by validators] N2[LIB advances normally] - N3[Witness schedule normal] + N3[validator schedule normal] end subgraph "ACTIVATION TRIGGER" @@ -586,8 +586,8 @@ graph TB subgraph "ACTIVATION SEQUENCE" AS1[Set emergency_consensus_active=true] - AS2[Create/Update committee witness] - AS3[Disable all real witnesses] + AS2[Create/Update committee validator] + AS3[Disable all real validators] AS4[Reset all penalties] AS5[Override schedule → all committee] AS6[Notify fork_db] @@ -652,7 +652,7 @@ graph TB │ emergency guards ▼ ┌──────────┐ ┌──────────────────────┐ ┌──────────────┐ - │ Witness │◄───│ Database (chain) │───►│ Fork DB │ + │ validator │◄───│ Database (chain) │───►│ Fork DB │ │ Plugin │ │ • activation │ │ • hash tie- │ │ │ │ • deactivation │ │ breaking │ │ • prod. │ │ • hybrid schedule │ │ • emergency │ @@ -664,7 +664,7 @@ graph TB │ └──────────┬───────────┘ │ Snapshot │ │ │ │ Plugin │ ┌────┴─────┐ ┌────┴──────┐ │ • import │ - │ Witness │ │ Dynamic │ │ • stall │ + │ validator │ │ Dynamic │ │ • stall │ │ Guard │ │ Global │ │ detection │ │ • key │ │ Properties│ └──────────────┘ │ rest. │ │ • flags │ @@ -678,12 +678,12 @@ graph TB | # | Location | File | Guard | |---|----------|------|-------| | 1 | `update_global_dynamic_data` | `database.cpp` | Only activate if HF12 + !already_active + LIB available | -| 2 | `update_witness_schedule` | `database.cpp` | Hybrid override + exit check via 75% real witness slots | +| 2 | `update_witness_schedule` | `database.cpp` | Hybrid override + exit check via 75% real validator slots | | 3 | `update_last_irreversible_block` | `database.cpp` | Cap LIB to HEAD-1 during emergency | | 4 | `check_block_post_validation_chain` | `database.cpp` | Skip BPV-based LIB advancement during emergency | -| 5 | `verify_signing_witness` | `database.cpp` | Relax slot-to-witness mapping during emergency | +| 5 | `verify_signing_witness` | `database.cpp` | Relax slot-to-validator mapping during emergency | | 6 | `fork_db._push_block` | `fork_database.cpp` | Deterministic hash tie-breaking during emergency | -| 7 | `maybe_produce_block` | `witness.cpp` | Emergency master: bypass sync+stale+participation, skip minority fork; slave: must sync first, standard production gate | +| 7 | `maybe_produce_block` | `validator.cpp` | Emergency master: bypass sync+stale+participation, skip minority fork; slave: must sync first, standard production gate | | 8 | `resync_from_lib` | `p2p_plugin.cpp` | SKIP entirely during emergency (prevent crash) | | 9 | `stale_sync_check_task` | `p2p_plugin.cpp` | Skip recovery if master's head advancing; allow if follower stuck | | 10 | `handle_block` | `p2p_plugin.cpp` | Near-caught-up blocks treated as normal in DLT emergency | diff --git a/.qoder/docs/expected-next-block.md b/.qoder/docs/expected-next-block.md index 54edaccebc..29886a3290 100644 --- a/.qoder/docs/expected-next-block.md +++ b/.qoder/docs/expected-next-block.md @@ -1,4 +1,4 @@ -# DLT P2P `expected_next_block` — Design, Data Flow, and Fixes +# DLT P2P `expected_next_block` — Design, Data Flow, and Fixes ## 1. Overview @@ -192,10 +192,10 @@ There is a small race between when a block is applied and when the next block ar ## 7. Concrete Bug Scenario (from production logs) ``` -206774ms witness.cpp:431 Generated block #79720273 ... by creativity +206774ms validator.cpp:431 Generated block #79720273 ... by creativity ... 209818ms dlt_p2p_node.cpp:1208 Block #79720274 from 80.87.202.57 out of order (expected #79720273) -209822ms dlt_p2p_node.cpp:1298 Got block #79720274 ... by witness m0ssa99 [80.87.202.57] +209822ms dlt_p2p_node.cpp:1298 Got block #79720274 ... by validator m0ssa99 [80.87.202.57] ``` **Trace:** @@ -300,4 +300,4 @@ This eliminates the per-peer tracking entirely for FORWARD mode and uses the onl | `libraries/network/dlt_p2p_node.cpp:1678` | `on_block_applied()` call in gap fill reply | | `libraries/network/dlt_p2p_node.cpp:1849-1861` | `broadcast_block()` — now calls `on_block_applied()` (Fix 8.2) | | `libraries/network/dlt_p2p_node.cpp:2457-2469` | `on_block_applied()` — bulk peer advance (Fix 8.1) | -| `plugins/witness/witness.cpp:1092-1099` | Block production → broadcast | +| `plugins/validator/validator.cpp:1092-1099` | Block production → broadcast | diff --git a/.qoder/docs/fork-collision-hardfork-proposal.md b/.qoder/docs/fork-collision-hardfork-proposal.md index 82cb84d70a..0c50a1d30e 100644 --- a/.qoder/docs/fork-collision-hardfork-proposal.md +++ b/.qoder/docs/fork-collision-hardfork-proposal.md @@ -1,8 +1,8 @@ -# Fork Collision Reduction Hardfork Proposal +# Fork Collision Reduction Hardfork Proposal ## Problem Statement -The VIZ blockchain experiences recurring "block num collision" events — situations where two witnesses produce blocks at the same height on different chain tips, creating a fork. With a 3-second block interval (`CHAIN_BLOCK_INTERVAL = 3`), there is minimal margin for block propagation, making collisions frequent during periods of network latency or clock drift. +The VIZ blockchain experiences recurring "block num collision" events — situations where two validators produce blocks at the same height on different chain tips, creating a fork. With a 3-second block interval (`CHAIN_BLOCK_INTERVAL = 3`), there is minimal margin for block propagation, making collisions frequent during periods of network latency or clock drift. ### Observed Pattern (Block 79162800–79162802) @@ -12,19 +12,19 @@ Block 79162801: lexai @ 11:53:24 (latency 47ms) | denis-skripnik @ 11:53:21 Block 79162802: micu @ 11:53:30 (latency 78ms) | creativity @ 11:53:27 (latency 4044ms) ``` -Three consecutive blocks had collisions, indicating a sustained network partition between two witness subsets. The high latency values (7–10 seconds) on one fork branch confirm severe propagation delay. +Three consecutive blocks had collisions, indicating a sustained network partition between two validator subsets. The high latency values (7–10 seconds) on one fork branch confirm severe propagation delay. ### Root Causes 1. **Tight block interval with no propagation margin** — 3-second slots leave no buffer for cross-region propagation (typical P2P propagation is 1–4 seconds for a global network). -2. **Deterministic witness ordering without shuffling** — The witness shuffle was [commented out](../libraries/chain/database.cpp) (`// VIZ remove randomization`), making the schedule predictable. If two consecutive witnesses have poor connectivity, collisions recur every round. +2. **Deterministic validator ordering without shuffling** — The validator shuffle was [commented out](../libraries/chain/database.cpp) (`// VIZ remove randomization`), making the schedule predictable. If two consecutive validators have poor connectivity, collisions recur every round. 3. **No on-chain fork telemetry** — The current `_maybe_warn_multiple_production()` only logs a console warning. There is no on-chain metric for fork frequency, making it impossible to monitor network health programmatically. -4. **No production-time fork awareness** — Witnesses produce blocks on whatever chain tip they see, even if a competing block already exists in their fork database for the same height. +4. **No production-time fork awareness** — validators produce blocks on whatever chain tip they see, even if a competing block already exists in their fork database for the same height. -5. **Clock drift susceptibility** — `get_slot_at_time()` uses wall-clock time. NTP drift between witness nodes can cause slot mismatches. API load can indirectly degrade NTP precision due to thread contention. +5. **Clock drift susceptibility** — `get_slot_at_time()` uses wall-clock time. NTP drift between validator nodes can cause slot mismatches. API load can indirectly degrade NTP precision due to thread contention. --- @@ -92,22 +92,22 @@ void database::_maybe_warn_multiple_production(uint32_t height) const { **Why this requires a hardfork**: Adding new fields to `dynamic_global_property_object` changes its serialized representation. All nodes must agree on the object layout for consensus. The snapshot plugin's export/import must also be updated. -**Consensus benefit**: Enables monitoring dashboards, alerting, and data-driven decisions about network topology. Witnesses with high collision rates can be identified and their connectivity improved. +**Consensus benefit**: Enables monitoring dashboards, alerting, and data-driven decisions about network topology. validators with high collision rates can be identified and their connectivity improved. --- ### Change 2: Fork-Aware Block Production Deferral -**Type**: Non-consensus-breaking (witness plugin behavior only) +**Type**: Non-consensus-breaking (Validator Plugin behavior only) -Already implemented in `plugins/witness/witness.cpp` — before producing a block, check if a competing block already exists in the fork database for the target height. If so, defer production to allow fork resolution. +Already implemented in `plugins/validator/validator.cpp` — before producing a block, check if a competing block already exists in the fork database for the target height. If so, defer production to allow fork resolution. This does **not** require a hardfork because: -- It only affects the witness plugin's production timing +- It only affects the Validator Plugin's production timing - It does not change block validation rules - A deferred block will be produced in the next available slot -However, the hardfork proposal could make this behavior **mandatory** by adding a consensus rule: if a witness observes a fork collision for the current slot, they MUST wait for the competing block's next witness to produce before building on top. This would formalize the deferral as a consensus rule rather than a best-effort optimization. +However, the hardfork proposal could make this behavior **mandatory** by adding a consensus rule: if a validator observes a fork collision for the current slot, they MUST wait for the competing block's next validator to produce before building on top. This would formalize the deferral as a consensus rule rather than a best-effort optimization. **Formalized version** (requires hardfork): @@ -117,7 +117,7 @@ Add to `validate_block_header()`: if (has_hardfork(CHAIN_HARDFORK_12)) { // After a fork collision at height H, the next block must be // built on top of the longest chain's block at height H. - // Witnesses must not produce on the shorter fork's tip. + // validators must not produce on the shorter fork's tip. auto existing = _fork_db.fetch_block_by_number(next_block.block_num()); if (existing.size() > 1) { // There was a collision at this height; verify this block @@ -130,15 +130,15 @@ if (has_hardfork(CHAIN_HARDFORK_12)) { } ``` -**Consensus benefit**: Prevents witnesses from extending a minority fork after a collision is detected, reducing the duration and depth of forks. +**Consensus benefit**: Prevents validators from extending a minority fork after a collision is detected, reducing the duration and depth of forks. --- ### Change 2a: Minority Fork Detection & Auto-Resync -**Type**: Non-consensus-breaking (witness + P2P plugin behavior only) +**Type**: Non-consensus-breaking (validator + P2P plugin behavior only) -Implemented in `plugins/witness/witness.cpp` and `plugins/p2p/p2p_plugin.cpp` — before producing a block, the witness plugin walks back the last `CHAIN_MAX_WITNESSES` (21) blocks in fork_db and checks if ALL were produced by the node's own configured witnesses. If so, the node is stuck on a minority fork (no external witnesses are participating on this chain). +Implemented in `plugins/validator/validator.cpp` and `plugins/p2p/p2p_plugin.cpp` — before producing a block, the Validator Plugin walks back the last `CHAIN_MAX_WITNESSES` (21) blocks in fork_db and checks if ALL were produced by the node's own configured validators. If so, the node is stuck on a minority fork (no external validators are participating on this chain). **Behavior by configuration:** @@ -159,7 +159,7 @@ Implemented in `plugins/witness/witness.cpp` and `plugins/p2p/p2p_plugin.cpp` This replicates the effect of a manual docker stop/start without node downtime. -**Files modified:** `witness.hpp` (new enum value `minority_fork`), `witness.cpp` (detection logic + switch case), `p2p_plugin.hpp` (new `resync_from_lib()` method), `p2p_plugin.cpp` (implementation) +**Files modified:** `validator.hpp` (new enum value `minority_fork`), `validator.cpp` (detection logic + switch case), `p2p_plugin.hpp` (new `resync_from_lib()` method), `p2p_plugin.cpp` (implementation) --- @@ -167,7 +167,7 @@ This replicates the effect of a manual docker stop/start without node downtime. **Type**: Consensus-breaking (changes block timing expectations) -Add a configurable production delay of `CHAIN_PRODUCTION_DELAY_MS` milliseconds (e.g., 500ms) that a witness must wait after receiving a new block before producing its own. This gives the network time to propagate the latest block before the next witness builds on it. +Add a configurable production delay of `CHAIN_PRODUCTION_DELAY_MS` milliseconds (e.g., 500ms) that a validator must wait after receiving a new block before producing its own. This gives the network time to propagate the latest block before the next validator builds on it. **Implementation**: @@ -175,7 +175,7 @@ Add a configurable production delay of `CHAIN_PRODUCTION_DELAY_MS` milliseconds // In config.hpp: #define CHAIN_PRODUCTION_DELAY_MS 500 // Wait 500ms after receiving block before producing -// In witness.cpp maybe_produce_block(): +// In validator.cpp maybe_produce_block(): fc::time_point_sec earliest_production_time = db.head_block_time() + fc::milliseconds(CHAIN_PRODUCTION_DELAY_MS); if (now < earliest_production_time) { @@ -184,9 +184,9 @@ if (now < earliest_production_time) { } ``` -**Why this requires a hardfork**: Changes the timing semantics of when blocks are expected. The current consensus assumes witnesses produce as close to their scheduled slot time as possible. A mandatory delay effectively shortens the usable production window. +**Why this requires a hardfork**: Changes the timing semantics of when blocks are expected. The current consensus assumes validators produce as close to their scheduled slot time as possible. A mandatory delay effectively shortens the usable production window. -**Consensus benefit**: A 500ms delay on a 3-second interval gives the P2P network 500ms to propagate the previous block to all witnesses before the next one starts building. This dramatically reduces the probability of two witnesses building on different chain tips simultaneously. +**Consensus benefit**: A 500ms delay on a 3-second interval gives the P2P network 500ms to propagate the previous block to all validators before the next one starts building. This dramatically reduces the probability of two validators building on different chain tips simultaneously. **Trade-off**: Block latency increases by up to 500ms per block. Over a day (28,800 blocks), this adds ~4 hours of total latency, but the actual user-perceived latency increase is only 500ms per transaction confirmation. @@ -203,17 +203,17 @@ These changes are already implemented and can be deployed immediately: | Enhanced collision diagnostics with fork topology classification | `database.cpp` `_maybe_warn_multiple_production()` | Done | | Rate-limited collision warnings (prevent log spam) | `database.cpp` `_maybe_warn_multiple_production()` | Done | | Parent block ID logging for fork topology analysis | `database.cpp` `_maybe_warn_multiple_production()` | Done | -| Pre-production fork collision check in witness plugin | `witness.cpp` `maybe_produce_block()` | Done | -| `fork_collision` block production condition | `witness.hpp` enum, `witness.cpp` handler | Done | -| NTP re-sync on fork collision detection | `witness.cpp` `block_production_loop()` | Done | -| Minority fork detection & auto-resync | `witness.cpp`, `p2p_plugin.cpp/.hpp`, `witness.hpp` | Done | +| Pre-production fork collision check in Validator Plugin | `validator.cpp` `maybe_produce_block()` | Done | +| `fork_collision` block production condition | `validator.hpp` enum, `validator.cpp` handler | Done | +| NTP re-sync on fork collision detection | `validator.cpp` `block_production_loop()` | Done | +| Minority fork detection & auto-resync | `validator.cpp`, `p2p_plugin.cpp/.hpp`, `validator.hpp` | Done | ### Phase 2: Hardfork 12 (Requires Network-Wide Upgrade) | Change | Breaking | Files Modified | |--------|----------|----------------| | Fork collision counter in DGP | Yes (serialized object) | `global_property_object.hpp`, `database.cpp`, `snapshot/plugin.cpp` | -| Production delay buffer | Yes (timing) | `config.hpp`, `witness.cpp` | +| Production delay buffer | Yes (timing) | `config.hpp`, `validator.cpp` | | Mandatory fork deferral rule | Yes (validation) | `database.cpp` `validate_block_header()` | ### Hardfork 12 Definition @@ -224,7 +224,7 @@ These changes are already implemented and can be deployed immediately: // 12 Hardfork — Fork Resilience Improvements #ifndef CHAIN_HARDFORK_12 #define CHAIN_HARDFORK_12 12 -#define CHAIN_HARDFORK_12_TIME // To be determined by witness vote +#define CHAIN_HARDFORK_12_TIME // To be determined by validator vote #define CHAIN_HARDFORK_12_VERSION hardfork_version( version(3, 1, 0) ) #endif ``` @@ -239,7 +239,7 @@ These changes are already implemented and can be deployed immediately: | Scenario | Current | After HF12 | Reduction | |----------|---------|------------|-----------| -| Consecutive witness pair with poor connectivity | ~95% collision/round | ~5% collision/round | ~19× | +| Consecutive validator pair with poor connectivity | ~95% collision/round | ~5% collision/round | ~19× | | Random single-slot collision (network hiccup) | 100% produces fork | ~30% (delay absorbs transient) | ~3× | | Sustained partition (2+ round) | 100% collision every block | 100% (delay can't help) | 0× | | NTP drift <500ms | ~50% collision | ~10% (delay + re-sync) | ~5× | @@ -262,11 +262,11 @@ Would eliminate most collisions by giving 2+ seconds of propagation margin. **Re ### B. Batch Block Production (Produce 2+ Blocks per Slot) -Similar to EOS's approach where a witness produces a batch of consecutive blocks. **Rejected** because it increases centralization (longer production windows favor better-connected witnesses) and complicates missed-block accounting. +Similar to EOS's approach where a validator produces a batch of consecutive blocks. **Rejected** because it increases centralization (longer production windows favor better-connected validators) and complicates missed-block accounting. -### C. Fork Choice by Witness Priority +### C. Fork Choice by validator Priority -Instead of longest-chain, use witness priority (e.g., higher-voted witness's block wins). **Rejected** because it breaks the fundamental longest-chain consensus rule and could enable voting attacks. +Instead of longest-chain, use validator priority (e.g., higher-voted validator's block wins). **Rejected** because it breaks the fundamental longest-chain consensus rule and could enable voting attacks. ### D. P2P Block Prefetch / Fast Relay Network @@ -278,5 +278,5 @@ A dedicated relay network for block propagation (similar to Bitcoin's FIBRE). ** - [DLT Hardfork New Objects](dlt-hardfork-new-objects.md) — Procedure for adding consensus objects in hardforks - [Block Processing](block-processing.md) — Block application flow and fork resolution -- [Witness Operations](op-witness.md) — Witness update, vote, chain properties -- [Plugins](plugins.md) — Plugin architecture including witness and P2P plugins +- [validator Operations](op-validator.md) — validator update, vote, chain properties +- [Plugins](plugins.md) — Plugin architecture including validator and P2P plugins diff --git a/.qoder/docs/index.md b/.qoder/docs/index.md index 462dae3ad6..75f63249e3 100644 --- a/.qoder/docs/index.md +++ b/.qoder/docs/index.md @@ -1,4 +1,4 @@ -# VIZ Blockchain — Operations & Structures Spec +# VIZ Blockchain — Operations & Structures Spec Full specification and implementation checklist for building VIZ blockchain client libraries in PHP, Node.js, and other languages. @@ -27,7 +27,7 @@ Full specification and implementation checklist for building VIZ blockchain clie | [emergency-consensus-review.md](emergency-consensus-review.md) | HF12 implementation review: failure/rollback procedures, threat model, test matrix | | [op-account.md](op-account.md) | Account create, update, metadata operations | | [op-transfer-vesting.md](op-transfer-vesting.md) | Transfer, transfer_to_vesting, withdraw_vesting, set route, delegate | -| [op-witness.md](op-witness.md) | Witness update, vote, proxy, chain properties | +| [op-validator.md](op-validator.md) | validator update, vote, proxy, chain properties | | [op-content.md](op-content.md) | Content, vote, delete_content (deprecated), custom | | [op-recovery.md](op-recovery.md) | Request/recover account, change recovery account | | [op-escrow.md](op-escrow.md) | Escrow transfer, approve, dispute, release | @@ -53,9 +53,9 @@ Full specification and implementation checklist for building VIZ blockchain clie | 3 | `transfer_to_vesting_operation` | active | op-transfer-vesting.md | | 4 | `withdraw_vesting_operation` | active | op-transfer-vesting.md | | 5 | `account_update_operation` | master/active | op-account.md | -| 6 | `witness_update_operation` | active | op-witness.md | -| 7 | `account_witness_vote_operation` | active | op-witness.md | -| 8 | `account_witness_proxy_operation` | active | op-witness.md | +| 6 | `witness_update_operation` | active | op-validator.md | +| 7 | `account_witness_vote_operation` | active | op-validator.md | +| 8 | `account_witness_proxy_operation` | active | op-validator.md | | 9 | `delete_content_operation` *(deprecated)* | regular | op-content.md | | 10 | `custom_operation` | active/regular | op-content.md | | 11 | `set_withdraw_vesting_route_operation` | active | op-transfer-vesting.md | @@ -72,14 +72,14 @@ Full specification and implementation checklist for building VIZ blockchain clie | 22 | `proposal_create_operation` | active | op-proposal.md | | 23 | `proposal_update_operation` | varies | op-proposal.md | | 24 | `proposal_delete_operation` | active | op-proposal.md | -| 25 | `chain_properties_update_operation` | active | op-witness.md | +| 25 | `chain_properties_update_operation` | active | op-validator.md | | 35 | `committee_worker_create_request_operation` | regular | op-committee.md | | 36 | `committee_worker_cancel_request_operation` | regular | op-committee.md | | 37 | `committee_vote_request_operation` | regular | op-committee.md | | 43 | `create_invite_operation` | active | op-invite.md | | 44 | `claim_invite_balance_operation` | active | op-invite.md | | 45 | `invite_registration_operation` | active | op-invite.md | -| 46 | `versioned_chain_properties_update_operation` | active | op-witness.md | +| 46 | `versioned_chain_properties_update_operation` | active | op-validator.md | | 47 | `award_operation` | regular | op-award.md | | 50 | `set_paid_subscription_operation` | active | op-subscription.md | | 51 | `paid_subscribe_operation` | active | op-subscription.md | @@ -98,7 +98,7 @@ Full specification and implementation checklist for building VIZ blockchain clie | 27 | `curation_reward_operation` | Content payout | virtual-operations.md | | 28 | `content_reward_operation` | Content payout | virtual-operations.md | | 29 | `fill_vesting_withdraw_operation` | Withdrawal interval | virtual-operations.md | -| 30 | `shutdown_witness_operation` | Witness deactivated | virtual-operations.md | +| 30 | `shutdown_witness_operation` | validator deactivated | virtual-operations.md | | 31 | `hardfork_operation` | Hardfork activation | virtual-operations.md | | 32 | `content_payout_update_operation` | Content payout update | virtual-operations.md | | 33 | `content_benefactor_reward_operation` | Content payout | virtual-operations.md | diff --git a/.qoder/docs/op-recovery.md b/.qoder/docs/op-recovery.md index eccb12abaf..8c9c279161 100644 --- a/.qoder/docs/op-recovery.md +++ b/.qoder/docs/op-recovery.md @@ -1,4 +1,4 @@ -# VIZ Blockchain — Account Recovery Operations +# VIZ Blockchain — Account Recovery Operations Spec for implementing account recovery operations in PHP/Node.js libraries. @@ -184,5 +184,5 @@ const op = ['change_recovery_account', { - [ ] 30-day delay between submitting the change and it taking effect - [ ] This prevents attackers from changing the recovery account during an active attack - [ ] `new_recovery_account` must be an existing account -- [ ] If `new_recovery_account == ""`, top-voted witness becomes recovery account +- [ ] If `new_recovery_account == ""`, top-voted validator becomes recovery account - [ ] Sign with `account_to_recover`'s master key diff --git a/.qoder/docs/op-witness.md b/.qoder/docs/op-validator.md similarity index 79% rename from .qoder/docs/op-witness.md rename to .qoder/docs/op-validator.md index fb152c317a..8849d2ed59 100644 --- a/.qoder/docs/op-witness.md +++ b/.qoder/docs/op-validator.md @@ -1,6 +1,6 @@ -# VIZ Blockchain — Witness Operations +# VIZ Blockchain — validator Operations -Spec for implementing witness-related operations in PHP/Node.js libraries. +Spec for implementing validator-related operations in PHP/Node.js libraries. --- @@ -9,17 +9,17 @@ Spec for implementing witness-related operations in PHP/Node.js libraries. **Type ID:** `6` **Required authority:** `active` of `owner` -Registers or updates a witness. Setting `block_signing_key` to the null key removes the witness from block production contention. +Registers or updates a validator. Setting `block_signing_key` to the null key removes the validator from block production contention. ### Fields | Field | Type | Required | Description | |---|---|---|---| -| `owner` | `account_name_type` | yes | Witness account name | -| `url` | `string` | yes | Witness website or info URL | +| `owner` | `account_name_type` | yes | validator account name | +| `url` | `string` | yes | validator website or info URL | | `block_signing_key` | `public_key_type` | yes | Key used to sign blocks (set to null key to deactivate) | -**Null key** (deactivate witness): `"VIZ1111111111111111111111111111111114T1Anm"` +**Null key** (deactivate validator): `"VIZ1111111111111111111111111111111114T1Anm"` ### JSON Example @@ -56,7 +56,7 @@ const op = ['witness_update', { ### Checklist - [ ] `block_signing_key` must be a valid VIZ public key or the null key -- [ ] Null key = `VIZ1111111111111111111111111111111114T1Anm` (deactivates witness) +- [ ] Null key = `VIZ1111111111111111111111111111111114T1Anm` (deactivates validator) - [ ] `url` must be non-empty and < `CHAIN_MAX_URL_LENGTH` (256) bytes - [ ] Requires `witness_declaration_fee` paid to committee (see chain properties) - [ ] Sign with `owner`'s active key @@ -68,13 +68,13 @@ const op = ['witness_update', { **Type ID:** `25` **Required authority:** `active` of `owner` -Witness votes on base chain properties (`chain_properties_init` format only). Use `versioned_chain_properties_update_operation` for extended properties. +validator votes on base chain properties (`chain_properties_init` format only). Use `versioned_chain_properties_update_operation` for extended properties. ### Fields | Field | Type | Required | Description | |---|---|---|---| -| `owner` | `account_name_type` | yes | Witness account voting | +| `owner` | `account_name_type` | yes | validator account voting | | `props` | `chain_properties_init` | yes | Proposed chain properties | ### JSON Example @@ -105,7 +105,7 @@ Witness votes on base chain properties (`chain_properties_init` format only). Us - [ ] `bandwidth_reserve_below.symbol` must be `SHARES` - [ ] `min_curation_percent` <= `max_curation_percent` - [ ] All percent fields in basis points (0–10000) -- [ ] Median of all active witness values is used as actual chain property +- [ ] Median of all active validator values is used as actual chain property --- @@ -114,13 +114,13 @@ Witness votes on base chain properties (`chain_properties_init` format only). Us **Type ID:** `46` **Required authority:** `active` of `owner` -Witness votes on versioned chain properties (supports all hardfork extensions). +validator votes on versioned chain properties (supports all hardfork extensions). ### Fields | Field | Type | Required | Description | |---|---|---|---| -| `owner` | `account_name_type` | yes | Witness account voting | +| `owner` | `account_name_type` | yes | validator account voting | | `props` | `versioned_chain_properties` | yes | Versioned props variant | ### JSON Example (using hf9 = index 3) @@ -170,14 +170,14 @@ Witness votes on versioned chain properties (supports all hardfork extensions). **Type ID:** `7` **Required authority:** `active` of `account` -Votes for or against a witness to be included in block production. +Votes for or against a validator to be included in block production. ### Fields | Field | Type | Required | Description | |---|---|---|---| | `account` | `account_name_type` | yes | Voting account | -| `witness` | `account_name_type` | yes | Witness to vote for/against | +| `validator` | `account_name_type` | yes | validator to vote for/against | | `approve` | `bool` | yes | `true` to add vote, `false` to remove vote | ### JSON Example @@ -185,7 +185,7 @@ Votes for or against a witness to be included in block production. ```json [7, { "account": "alice", - "witness": "bob", + "validator": "bob", "approve": true }] ``` @@ -197,7 +197,7 @@ $op = [ 'type' => 'account_witness_vote_operation', 'value' => [ 'account' => 'alice', - 'witness' => 'bob', + 'validator' => 'bob', 'approve' => true, ], ]; @@ -208,7 +208,7 @@ $op = [ ```js const op = ['account_witness_vote', { account: 'alice', - witness: 'bob', + validator: 'bob', approve: true, }]; ``` @@ -216,7 +216,7 @@ const op = ['account_witness_vote', { ### Checklist - [ ] Account must have SHARES to have meaningful voting weight - [ ] `approve: false` removes a previously cast vote -- [ ] Top 21 witnesses by vote weight produce blocks +- [ ] Top 21 validators by vote weight produce blocks - [ ] Sign with `account`'s active key --- @@ -226,7 +226,7 @@ const op = ['account_witness_vote', { **Type ID:** `8` **Required authority:** `active` of `account` -Assigns a proxy account for witness voting. All existing votes are removed when a proxy is set. +Assigns a proxy account for validator voting. All existing votes are removed when a proxy is set. ### Fields @@ -248,5 +248,5 @@ Assigns a proxy account for witness voting. All existing votes are removed when - [ ] Setting `proxy` to `""` (empty string) removes the proxy - [ ] Cannot set proxy to self - [ ] Proxy chains are resolved (A→B→C); max depth is limited -- [ ] Setting a proxy removes all direct witness votes +- [ ] Setting a proxy removes all direct validator votes - [ ] Sign with `account`'s active key diff --git a/.qoder/docs/p2p-messages.md b/.qoder/docs/p2p-messages.md index eafc2feffc..334d9ee45f 100644 --- a/.qoder/docs/p2p-messages.md +++ b/.qoder/docs/p2p-messages.md @@ -1,4 +1,4 @@ -# VIZ P2P Message Protocol Reference +# VIZ P2P Message Protocol Reference Complete reference for all P2P network message types, their structures, flow diagrams, and the transaction exchange pipeline through JSON-RPC, chain, and network broadcast plugins. @@ -85,7 +85,7 @@ struct trx_message { | Caller | File | Line | |--------|------|------| | JSON-RPC `broadcast_transaction` | [network_broadcast_api.cpp](file:///d:/Work/viz-cpp-node/plugins/network_broadcast_api/network_broadcast_api.cpp) | 50 | -| Witness block production (contained txs) | [witness.cpp](file:///d:/Work/viz-cpp-node/plugins/witness/witness.cpp) | — | +| validator block production (contained txs) | [validator.cpp](file:///d:/Work/viz-cpp-node/plugins/validator/validator.cpp) | — | | P2P relay (incoming tx → other peers) | [node.cpp](file:///d:/Work/viz-cpp-node/libraries/network/node.cpp) | 5146 | --- @@ -112,13 +112,13 @@ struct block_message { 4. [p2p_plugin.cpp](file:///d:/Work/viz-cpp-node/plugins/p2p/p2p_plugin.cpp) `handle_block()` (line 145) — calls `chain.accept_block(blk_msg.block, ...)` 5. On success: broadcast to other peers -**DLT emergency near-caught-up logic** (p2p_plugin.cpp:219): When `sync_mode && gap <= 2 && dlt_mode && block_age < 30s`, treats the sync block as a normal block to avoid triggering "Syncing Blockchain started" and disrupting witness production. +**DLT emergency near-caught-up logic** (p2p_plugin.cpp:219): When `sync_mode && gap <= 2 && dlt_mode && block_age < 30s`, treats the sync block as a normal block to avoid triggering "Syncing Blockchain started" and disrupting validator production. **Usage sites:** | Caller | File | Line | |--------|------|------| | JSON-RPC `broadcast_block` | [network_broadcast_api.cpp](file:///d:/Work/viz-cpp-node/plugins/network_broadcast_api/network_broadcast_api.cpp) | 88 | -| Witness block production | [witness.cpp](file:///d:/Work/viz-cpp-node/plugins/witness/witness.cpp) | — | +| validator block production | [validator.cpp](file:///d:/Work/viz-cpp-node/plugins/validator/validator.cpp) | — | | P2P relay | [node.cpp](file:///d:/Work/viz-cpp-node/libraries/network/node.cpp) | 5146 | --- @@ -396,7 +396,7 @@ Sent on every new connection in `new_peer_just_added()` (node.cpp:5186). ### 6009 — block_post_validation_message -**Purpose:** Block signing witness verification (Hardfork 11+). Sent after a block is applied to confirm the signing witness identity. The receiving node validates the signature against the witness's signing key on-chain and triggers `apply_block_post_validation()`. +**Purpose:** Block signing validator verification (Hardfork 11+). Sent after a block is applied to confirm the signing validator identity. The receiving node validates the signature against the validator's signing key on-chain and triggers `apply_block_post_validation()`. **Structure:** ```cpp @@ -410,9 +410,9 @@ struct block_post_validation_message { **Handler:** [p2p_plugin.cpp](file:///d:/Work/viz-cpp-node/plugins/p2p/p2p_plugin.cpp) `handle_message()` (line 304) -**Validation:** Recovers the public key from the signature, compares against the witness's on-chain `signing_key`. If matched, calls `apply_block_post_validation()`. +**Validation:** Recovers the public key from the signature, compares against the validator's on-chain `signing_key`. If matched, calls `apply_block_post_validation()`. -**Broadcast:** Sent by witnesses after producing a block. Also emitted via [p2p_plugin.cpp](file:///d:/Work/viz-cpp-node/plugins/p2p/p2p_plugin.cpp) `broadcast_block_post_validation()` (line 1470). +**Broadcast:** Sent by validators after producing a block. Also emitted via [p2p_plugin.cpp](file:///d:/Work/viz-cpp-node/plugins/p2p/p2p_plugin.cpp) `broadcast_block_post_validation()` (line 1470). --- @@ -441,7 +441,7 @@ struct chain_status_announcement_message { **Broadcast trigger:** [p2p_plugin.cpp](file:///d:/Work/viz-cpp-node/plugins/p2p/p2p_plugin.cpp) `connection_count_changed()` (line 793) — when the connection count increases (new peer joined), a `chain_status_announcement_message` is built from the chain database and broadcast to all peers. -**Manual call site:** `p2p_plugin::broadcast_chain_status()` — can be called from any plugin (e.g., witness or snapshot) when chain state materially changes. +**Manual call site:** `p2p_plugin::broadcast_chain_status()` — can be called from any plugin (e.g., validator or snapshot) when chain state materially changes. --- @@ -525,7 +525,7 @@ When remaining == 0 and all items known: ### Broadcast/Inventory Mode Flow (Push-Based) ``` -Witness Node Peer A Peer B (us) Peer C +validator Node Peer A Peer B (us) Peer C | | | | | 1. generate_block | | 2. broadcast_block| | | @@ -560,7 +560,7 @@ Witness Node Peer A Peer B (us) Peer C ### Block Post-Validation Flow ``` -Witness Node Other Peers +validator Node Other Peers | | | 1. produce_block() + broadcast | | 2. broadcast_block_post_validation() | @@ -568,7 +568,7 @@ Witness Node Other Peers |--------------------------------------->| | | 3. handle_message() in p2p_plugin | | - recover public key from signature - | | - compare with witness on-chain signing_key + | | - compare with validator on-chain signing_key | | - if match: apply_block_post_validation() ``` @@ -610,7 +610,7 @@ External Client Transaction propagates through the network via gossip (each peer that accepts it broadcasts to its other peers) -Eventually included in a block by a witness. +Eventually included in a block by a validator. ``` ### Callback-Based Variant @@ -727,7 +727,7 @@ When a DLT node (rolling window of blocks) receives a `fetch_blockchain_item_ids ### Near-Caught-Up Sync Blocks -[p2p_plugin.cpp](file:///d:/Work/viz-cpp-node/plugins/p2p/p2p_plugin.cpp#L202-L226): When a sync block arrives with `gap <= 2 && dlt_mode && block_age < 30s`, it's treated as a normal (non-sync) block. This prevents "Syncing Blockchain started" from firing when the node is only 1-2 blocks behind, which would set `currently_syncing=true` and disrupt witness block production. +[p2p_plugin.cpp](file:///d:/Work/viz-cpp-node/plugins/p2p/p2p_plugin.cpp#L202-L226): When a sync block arrives with `gap <= 2 && dlt_mode && block_age < 30s`, it's treated as a normal (non-sync) block. This prevents "Syncing Blockchain started" from firing when the node is only 1-2 blocks behind, which would set `currently_syncing=true` and disrupt validator block production. --- diff --git a/.qoder/docs/p2p-sync-workflow.md b/.qoder/docs/p2p-sync-workflow.md index 842553de63..a6293bb044 100644 --- a/.qoder/docs/p2p-sync-workflow.md +++ b/.qoder/docs/p2p-sync-workflow.md @@ -1,4 +1,4 @@ -# P2P Synchronization & Block Push Workflow +# P2P Synchronization & Block Push Workflow ## Overview @@ -127,7 +127,7 @@ Active after sync completes — the node is caught up and receives new blocks in ### Flow: Block Production and Propagation ``` -Witness Node Peer A Peer B (us) +validator Node Peer A Peer B (us) | | | | 1. generate_block() | | | 2. broadcast_block() | | @@ -153,7 +153,7 @@ Witness Node Peer A Peer B (us) ### Detailed Steps #### Step 1-2: Block Production -A witness node produces a block and calls `p2p_plugin::broadcast_block()`, which sends the block to all connected peers via `node::broadcast()`. +A validator node produces a block and calls `p2p_plugin::broadcast_block()`, which sends the block to all connected peers via `node::broadcast()`. #### Step 3: Inventory Advertisement When a peer receives a new block (either via broadcast or sync), it advertises it to all its OTHER connected peers via `item_ids_inventory_message`. This is the gossip protocol — blocks propagate through the network hop by hop. @@ -252,7 +252,7 @@ AND this state has persisted for **30+ seconds** (measured via `last_sync_item_r ### Detection -In `witness_plugin::impl::maybe_produce_block()` (witness.cpp), if the last 21 blocks in `fork_db` were ALL produced by the node's own configured witnesses, the node is likely on a minority fork (isolated from the network). +In `witness_plugin::impl::maybe_produce_block()` (validator.cpp), if the last 21 blocks in `fork_db` were ALL produced by the node's own configured validators, the node is likely on a minority fork (isolated from the network). ### Recovery Flow @@ -285,7 +285,7 @@ In `witness_plugin::impl::maybe_produce_block()` (witness.cpp), if the last 21 b The peer state reset logic lives in `node_impl::reset_active_peer_states()` (node.cpp) and is shared by two callers: 1. **`resync()`** — called during minority fork recovery via `resync_from_lib()`. Resets all peer state, clears `_active_sync_requests`, then calls `start_synchronizing()`. -2. **`reconnect_seeds()`** — called by the witness plugin when producing a block with <2 peers. Resets all peer state, then force-reconnects seed nodes. +2. **`reconnect_seeds()`** — called by the Validator Plugin when producing a block with <2 peers. Resets all peer state, then force-reconnects seed nodes. The `resync()` function calls `reset_active_peer_states()` which clears per-peer state: @@ -340,10 +340,10 @@ P2P seed node not responding (5 consecutive failures), check config an ### Low-Peer Seed Reconnection -The witness plugin checks the connection count after each successfully produced block. If fewer than 2 peers are connected: +The Validator Plugin checks the connection count after each successfully produced block. If fewer than 2 peers are connected: ``` -1. Witness produces block, broadcasts it +1. validator produces block, broadcasts it 2. Check: get_connections_count() < 2? 3. YES → p2p_plugin::reconnect_seeds() ├── node->reset_active_peer_states() (clear all blocking state) @@ -455,8 +455,8 @@ The `_last_block_received_time` is reset to `now` in these situations: The stale sync detector acts as a **last-resort safety net**. It complements: -- **Minority fork detection** (witness plugin) — triggers faster (after 21 own-witness blocks), but only if the node is actively producing. Stale sync covers the case where the node is NOT a witness or production is already disabled. -- **Low-peer seed reconnection** (witness plugin) — triggers per-block when <2 peers, but only while producing. Stale sync covers periods when production is halted. +- **Minority fork detection** (Validator Plugin) — triggers faster (after 21 own-validator blocks), but only if the node is actively producing. Stale sync covers the case where the node is NOT a validator or production is already disabled. +- **Low-peer seed reconnection** (Validator Plugin) — triggers per-block when <2 peers, but only while producing. Stale sync covers periods when production is halted. - **Connection loop backoff** (node.cpp) — handles normal reconnection with exponential backoff. Stale sync overrides this by calling `resync()` which does a full peer state reset + `add_node()` on seeds. - **Snapshot stalled sync detection** (snapshot plugin) — a separate, heavier mechanism described below. diff --git a/.qoder/docs/plugins.md b/.qoder/docs/plugins.md index 573defc801..0f4aea80d3 100644 --- a/.qoder/docs/plugins.md +++ b/.qoder/docs/plugins.md @@ -1,4 +1,4 @@ -# VIZ Blockchain — Plugins Reference +# VIZ Blockchain — Plugins Reference Complete specification of all VIZ node plugins: what they do, dependencies, status (active/deprecated), and JSON-RPC API methods. @@ -159,7 +159,7 @@ p2p-stale-sync-timeout-seconds = 120 - **Block storage diagnostics:** head, LIB, earliest available block, DLT block log range, regular block log end, fork_db linked/unlinked counts and ranges, DLT mode flag, and total `resize()` count - In DLT mode, also runs `dlt_block_log::verify_mapping()` to detect and self-heal stale memory-mapped file state -**Minority fork auto-recovery:** The P2P plugin exposes `resync_from_lib()` which is called by the witness plugin when a minority fork is detected (last 21 blocks all from our own witnesses). It pops all reversible blocks back to LIB, resets fork_db, re-initiates P2P sync, and reconnects seed nodes. This replicates the effect of a manual node restart. See [fork-collision-hardfork-proposal.md](fork-collision-hardfork-proposal.md) for details. +**Minority fork auto-recovery:** The P2P plugin exposes `resync_from_lib()` which is called by the Validator Plugin when a minority fork is detected (last 21 blocks all from our own validators). It pops all reversible blocks back to LIB, resets fork_db, re-initiates P2P sync, and reconnects seed nodes. This replicates the effect of a manual node restart. See [fork-collision-hardfork-proposal.md](fork-collision-hardfork-proposal.md) for details. **Post-snapshot `trigger_resync()`:** The P2P plugin exposes `trigger_resync()` which is called by the snapshot plugin after a hot-reload (snapshot import while the node is running). It re-initiates P2P sync from the new head block so the P2P layer picks up the chain state change. Without this, the P2P layer would continue advertising stale block IDs. @@ -173,25 +173,25 @@ p2p-stale-sync-timeout-seconds = 120 --- -### `witness` +### `validator` **Status:** Active **Category:** Producer **Dependencies:** `chain`, `p2p` -Block production and witness scheduling. +Block production and validator scheduling. **Purpose:** - Produces blocks when scheduled -- Manages witness signing keys +- Manages validator signing keys - Detects fork collisions and defers production -- Detects minority fork (last 21 blocks all from own witnesses) and triggers auto-recovery +- Detects minority fork (last 21 blocks all from own validators) and triggers auto-recovery - Supports emergency consensus block production **JSON-RPC:** None (internal only) **Config options:** ```ini -witness = "mywitness" +validator = "mywitness" private-key = 5K... enable-stale-production = false required-participation = 3300 @@ -200,16 +200,16 @@ fork-collision-timeout-blocks = 21 | Option | Default | Description | |---|---|---| -| `witness` | (none) | Witness account name(s) to produce blocks for | +| `validator` | (none) | validator account name(s) to produce blocks for | | `private-key` | (none) | WIF private key(s) for block signing | | `emergency-private-key` | (none) | WIF key for emergency consensus production | | `enable-stale-production` | `false` | Allow production even if chain is stale or on a minority fork | -| `required-participation` | `3300` (33%) | Minimum witness participation rate (basis points) | +| `required-participation` | `3300` (33%) | Minimum validator participation rate (basis points) | | `fork-collision-timeout-blocks` | `21` | Deferrals before forcing production past a fork collision | -**Minority fork detection:** Before producing a block, the witness plugin walks the last `CHAIN_MAX_WITNESSES` (21) blocks in fork_db. If ALL were produced by the node's own configured witnesses, the node is stuck on a minority fork. With `enable-stale-production=false` (default), the plugin calls `p2p.resync_from_lib()` to pop back to LIB and resync. With `enable-stale-production=true`, production continues (for bootstrap/testnet scenarios). Detection is skipped during emergency consensus mode. +**Minority fork detection:** Before producing a block, the Validator Plugin walks the last `CHAIN_MAX_WITNESSES` (21) blocks in fork_db. If ALL were produced by the node's own configured validators, the node is stuck on a minority fork. With `enable-stale-production=false` (default), the plugin calls `p2p.resync_from_lib()` to pop back to LIB and resync. With `enable-stale-production=true`, production continues (for bootstrap/testnet scenarios). Detection is skipped during emergency consensus mode. -**Emergency consensus:** When `emergency-private-key` is configured, the committee account is added to the witness set. During emergency consensus mode (`dgp.emergency_consensus_active`), the node produces blocks using the committee account's schedule. +**Emergency consensus:** When `emergency-private-key` is configured, the committee account is added to the validator set. During emergency consensus mode (`dgp.emergency_consensus_active`), the node produces blocks using the committee account's schedule. See [block-processing.md](block-processing.md) for production timing details and [fork-collision-hardfork-proposal.md](fork-collision-hardfork-proposal.md) for fork handling. @@ -284,7 +284,7 @@ Primary read API for blockchain state queries. | `database_api.set_block_applied_callback` | Subscribe to new blocks | | `database_api.get_config` | Get compile-time chain constants | | `database_api.get_dynamic_global_properties` | Get current chain state | -| `database_api.get_chain_properties` | Get median witness properties | +| `database_api.get_chain_properties` | Get median validator properties | | `database_api.get_hardfork_version` | Get current hardfork version | | `database_api.get_next_scheduled_hardfork` | Get next hardfork info | | `database_api.get_accounts` | Get accounts by names | @@ -319,7 +319,7 @@ Broadcasts transactions and blocks to the network. **Purpose:** - Broadcast signed transactions -- Broadcast signed blocks (for witnesses) +- Broadcast signed blocks (for validators) - Synchronous transaction confirmation **JSON-RPC Methods:** @@ -338,25 +338,25 @@ Broadcasts transactions and blocks to the network. **Category:** API **Dependencies:** `json_rpc`, `chain` -Query witness information. +Query validator information. **Purpose:** -- List active/scheduled witnesses -- Query witness by account or vote rank -- Get witness schedule +- List active/scheduled validators +- Query validator by account or vote rank +- Get validator schedule **JSON-RPC Methods:** | Method | Description | |---|---| -| `witness_api.get_active_witnesses` | Get current active witness set | -| `witness_api.get_witness_schedule` | Get witness schedule object | -| `witness_api.get_witnesses` | Get witnesses by IDs | -| `witness_api.get_witness_by_account` | Get witness by account name | -| `witness_api.get_witnesses_by_vote` | Get witnesses ranked by votes | -| `witness_api.get_witnesses_by_counted_vote` | Get witnesses by counted votes | -| `witness_api.get_witness_count` | Get total witness count | -| `witness_api.lookup_witness_accounts` | List witness accounts by prefix | +| `witness_api.get_active_witnesses` | Get current active validator set | +| `witness_api.get_witness_schedule` | Get validator schedule object | +| `witness_api.get_witnesses` | Get validators by IDs | +| `witness_api.get_witness_by_account` | Get validator by account name | +| `witness_api.get_witnesses_by_vote` | Get validators ranked by votes | +| `witness_api.get_witnesses_by_counted_vote` | Get validators by counted votes | +| `witness_api.get_witness_count` | Get total validator count | +| `witness_api.lookup_witness_accounts` | List validator accounts by prefix | --- @@ -744,32 +744,32 @@ Get raw serialized blocks. --- -## Witness/Producer Plugins +## validator/Producer Plugins -### `witness` +### `validator` **Status:** Active **Category:** Core (for block producers) **Dependencies:** `chain`, `p2p` -Block production plugin for witnesses. +Block production plugin for validators. **Purpose:** - Sign and produce blocks on schedule -- Manage witness private keys +- Manage validator private keys **JSON-RPC:** None **Config options:** ```ini -witness = "your-witness-account" +validator = "your-validator-account" private-key = 5K... enable-stale-production = true # Produce blocks even on a stale chain (default: false) -required-participation = 3300 # Min witness participation in basis points to produce (default: 33% = 3300) +required-participation = 3300 # Min validator participation in basis points to produce (default: 33% = 3300) ``` **Bug Fix: `enable-stale-production` and `required-participation` option parsing** -Two bugs were fixed in the witness plugin option definitions ([witness.cpp](../../plugins/witness/witness.cpp)): +Two bugs were fixed in the Validator Plugin option definitions ([validator.cpp](../../plugins/validator/validator.cpp)): | Bug | Before | After | |---|---|---| @@ -783,9 +783,9 @@ The `required-participation` value is now always in **basis points** (0–10000 **Optimization: Block Production Timing** -The witness plugin's production loop uses a timer + look-ahead mechanism to determine when to produce a block. The timer ticks at regular intervals and the look-ahead shifts `now` forward so the slot boundary is detected earlier. +The Validator Plugin's production loop uses a timer + look-ahead mechanism to determine when to produce a block. The timer ticks at regular intervals and the look-ahead shifts `now` forward so the slot boundary is detected earlier. -Source: [witness.cpp](../../plugins/witness/witness.cpp) — `schedule_production_loop()`, `maybe_produce_block()` +Source: [validator.cpp](../../plugins/validator/validator.cpp) — `schedule_production_loop()`, `maybe_produce_block()` | Parameter | Value | Meaning | |---|---|---| @@ -833,7 +833,7 @@ Development and testing utilities. **NOT for production use.** | `debug_node.debug_push_blocks` | Push blocks from database | | `debug_node.debug_push_json_blocks` | Push blocks from JSON file | | `debug_node.debug_pop_block` | Pop and return last block | -| `debug_node.debug_get_witness_schedule` | Get witness schedule | +| `debug_node.debug_get_witness_schedule` | Get validator schedule | | `debug_node.debug_set_hardfork` | Force set hardfork | | `debug_node.debug_has_hardfork` | Check if hardfork applied | @@ -899,7 +899,7 @@ mongodb-db-name = viz | `auth_util` | Active | Yes | API | | `block_info` | Active | Yes | API | | `raw_block` | Active | Yes | API | -| `witness` | Active | No | Producer | +| `validator` | Active | No | Producer | | `debug_node` | Dev only | Yes | Debug | | `test_api` | Test only | Yes | Test | | `mongo_db` | Active | No | External | @@ -930,7 +930,7 @@ All methods use JSON-RPC 2.0 format: | `database_api` | `set_block_applied_callback` | Subscribe to blocks | | `database_api` | `get_config` | Chain constants | | `database_api` | `get_dynamic_global_properties` | Current chain state | -| `database_api` | `get_chain_properties` | Median witness props | +| `database_api` | `get_chain_properties` | Median validator props | | `database_api` | `get_hardfork_version` | Current HF version | | `database_api` | `get_next_scheduled_hardfork` | Next HF info | | `database_api` | `get_accounts` | Accounts by names | @@ -957,14 +957,14 @@ All methods use JSON-RPC 2.0 format: | `network_broadcast_api` | `broadcast_transaction_synchronous` | Broadcast TX (sync) | | `network_broadcast_api` | `broadcast_transaction_with_callback` | Broadcast TX (callback) | | `network_broadcast_api` | `broadcast_block` | Broadcast block | -| `witness_api` | `get_active_witnesses` | Active witnesses | -| `witness_api` | `get_witness_schedule` | Witness schedule | -| `witness_api` | `get_witnesses` | Witnesses by ID | -| `witness_api` | `get_witness_by_account` | Witness by account | -| `witness_api` | `get_witnesses_by_vote` | Witnesses by votes | -| `witness_api` | `get_witnesses_by_counted_vote` | Witnesses by counted votes | -| `witness_api` | `get_witness_count` | Witness count | -| `witness_api` | `lookup_witness_accounts` | List witnesses | +| `witness_api` | `get_active_witnesses` | Active validators | +| `witness_api` | `get_witness_schedule` | validator schedule | +| `witness_api` | `get_witnesses` | validators by ID | +| `witness_api` | `get_witness_by_account` | validator by account | +| `witness_api` | `get_witnesses_by_vote` | validators by votes | +| `witness_api` | `get_witnesses_by_counted_vote` | validators by counted votes | +| `witness_api` | `get_witness_count` | validator count | +| `witness_api` | `lookup_witness_accounts` | List validators | | `account_by_key` | `get_key_references` | Accounts by key | | `account_history` | `get_account_history` | Account operations | | `operation_history` | `get_ops_in_block` | Block operations | @@ -1022,7 +1022,7 @@ All methods use JSON-RPC 2.0 format: | `debug_node` | `debug_push_blocks` | Push blocks | | `debug_node` | `debug_push_json_blocks` | Push JSON blocks | | `debug_node` | `debug_pop_block` | Pop block | -| `debug_node` | `debug_get_witness_schedule` | Witness schedule | +| `debug_node` | `debug_get_witness_schedule` | validator schedule | | `debug_node` | `debug_set_hardfork` | Set hardfork | | `debug_node` | `debug_has_hardfork` | Check hardfork | @@ -1061,11 +1061,11 @@ plugin = social_network plugin = private_message ``` -### Witness Node +### validator Node ```ini plugin = chain plugin = p2p -plugin = witness +plugin = validator plugin = json_rpc plugin = webserver plugin = database_api diff --git a/.qoder/docs/shared-memory.md b/.qoder/docs/shared-memory.md index 8bd537ada0..8d9e50d923 100644 --- a/.qoder/docs/shared-memory.md +++ b/.qoder/docs/shared-memory.md @@ -1,4 +1,4 @@ -# Shared Memory Architecture +# Shared Memory Architecture The VIZ node stores all blockchain state in a memory-mapped file (`shared_memory.bin`) managed by the **chainbase** library, which wraps Boost.Interprocess `managed_mapped_file`. This is the sole database for chain state — the node **cannot operate without shared memory**. @@ -42,7 +42,7 @@ The VIZ node stores all blockchain state in a memory-mapped file (`shared_memory | `libraries/chain/database.cpp` | Chain-level `open()`, `_resize()`, `check_free_memory()`, `push_block()`, `_generate_block()` | | `libraries/chain/include/graphene/chain/database.hpp` | Chain database class declaration, resize/memory parameters | | `plugins/chain/plugin.cpp` | Config option definitions, initialization, snapshot loading | -| `plugins/witness/witness.cpp` | Lockless reads in `maybe_produce_block()` and `is_witness_scheduled_soon()`, guarded by `operation_guard` | +| `plugins/validator/validator.cpp` | Lockless reads in `maybe_produce_block()` and `is_witness_scheduled_soon()`, guarded by `operation_guard` | | `plugins/p2p/p2p_plugin.cpp` | Lockless reads in block post-validation (`get_witness_key()`), guarded by `operation_guard` | --- @@ -60,7 +60,7 @@ The `shared_memory.bin` file is a Boost.Interprocess `managed_mapped_file`: ### Internal Structure -All chainbase objects (accounts, witnesses, transactions, etc.) are stored as C++ objects allocated inside the mapped segment using Boost.Interprocess allocators: +All chainbase objects (accounts, validators, transactions, etc.) are stored as C++ objects allocated inside the mapped segment using Boost.Interprocess allocators: ```cpp template @@ -139,7 +139,7 @@ All parameters are defined in `plugins/chain/plugin.cpp` and read from `config.i ### Recommended Configurations -**Witness node (production):** +**validator node (production):** ```ini shared-file-size = 4G inc-shared-file-size = 2G @@ -239,8 +239,8 @@ The **resize barrier** (`chainbase::database`) solves this with an atomic operat | `with_read_lock()` / `with_write_lock()` | `operation_guard` acquired automatically inside the lock wrapper before the `boost::shared_mutex` lock | | `_generate_block()` lockless reads (pre-write-lock) | Explicit scoped `operation_guard` around `get_slot_at_time()`, `get_scheduled_witness()`, `get_witness()`, `find_account()` | | `_generate_block()` lockless reads (post-write-lock) | Second `operation_guard` (`op_guard2`) around `get_dynamic_global_properties()`, `head_block_id()`, `get_witness()`, `get_hardfork_property_object()`; released via `release()` before `push_block()` | -| Witness plugin `maybe_produce_block()` | Explicit `operation_guard` around `get_slot_at_time()`, `get_scheduled_witness()`, index lookups; released via `release()` before `generate_block()` | -| Witness plugin `is_witness_scheduled_soon()` | Explicit `operation_guard` around `get_slot_at_time()`, `get_scheduled_witness()`, index lookups; released via `release()` before return | +| Validator Plugin `maybe_produce_block()` | Explicit `operation_guard` around `get_slot_at_time()`, `get_scheduled_witness()`, index lookups; released via `release()` before `generate_block()` | +| Validator Plugin `is_witness_scheduled_soon()` | Explicit `operation_guard` around `get_slot_at_time()`, `get_scheduled_witness()`, index lookups; released via `release()` before return | | P2P plugin block post-validation | Explicit `operation_guard` around `get_witness_key()` calls; released via `release()` before `apply_block_post_validation()` | **Key classes:** @@ -256,15 +256,15 @@ The **resize barrier** (`chainbase::database`) solves this with an atomic operat Before the resize barrier was added, the resize used `with_strong_write_lock()` which only blocked threads holding or waiting for a `boost::shared_mutex` lock. This left lockless reads unprotected: - `boost::shared_mutex` does **not** protect against segment destruction — a read lock only prevents concurrent writes, but the resize **is** the write -- Threads performing lockless reads (witness plugin, `_generate_block()`) could hold stale pointers into the old mapping after `_segment.reset()` +- Threads performing lockless reads (Validator Plugin, `_generate_block()`) could hold stale pointers into the old mapping after `_segment.reset()` -This was the root cause of shared memory corruption symptoms like `CRITICAL: Witness X account object MISSING from database!`. +This was the root cause of shared memory corruption symptoms like `CRITICAL: validator X account object MISSING from database!`. ### Corruption Symptoms Typical corruption indicators (should not occur with the resize barrier in place, but listed for historical reference and diagnostics): -- `CRITICAL: Witness X account object MISSING from database!` — account index entry not found despite `account_index_size` showing entries exist +- `CRITICAL: validator X account object MISSING from database!` — account index entry not found despite `account_index_size` showing entries exist - `Could not modify object, most likely a uniqueness constraint was violated` — internal index pointers corrupted, uniqueness check fails - Node crashes and restarts in an infinite loop — each restart opens the corrupted file, fails to produce/apply blocks, crashes again @@ -319,12 +319,12 @@ with_strong_write_lock([&]() { // operation_guard acquired inside }); ``` -### Lockless Reads (Witness Plugin, Block Generation, P2P) +### Lockless Reads (Validator Plugin, Block Generation, P2P) Some code paths read from chainbase indices **without** holding a `boost::shared_mutex` lock. These must explicitly use an `operation_guard` to participate in the resize barrier: ```cpp -// witness.cpp — maybe_produce_block() +// validator.cpp — maybe_produce_block() auto op_guard = db.make_operation_guard(); uint32_t slot = db.get_slot_at_time(now); // lockless read string scheduled = db.get_scheduled_witness(slot); // lockless read @@ -333,7 +333,7 @@ op_guard.release(); // release before generate_block() which has its own guard ``` ```cpp -// witness.cpp — is_witness_scheduled_soon() +// validator.cpp — is_witness_scheduled_soon() auto op_guard = db.make_operation_guard(); uint32_t slot = db.get_slot_at_time(now); string scheduled = db.get_scheduled_witness(s); @@ -358,7 +358,7 @@ auto op_guard2 = make_operation_guard(); auto maximum_block_size = get_dynamic_global_properties().maximum_block_size; // ... with_strong_write_lock, then post-lock reads ... pending_block.previous = head_block_id(); -const auto& witness = get_witness(witness_owner); +const auto& validator = get_witness(witness_owner); const auto& hfp = get_hardfork_property_object(); const auto& dgp_block = get_dynamic_global_properties(); op_guard2.release(); // release before push_block() @@ -450,7 +450,7 @@ Approximate memory usage for a VIZ mainnet node at ~79M blocks: | Component | Estimated Size | |-----------|---------------| | Account index (~14K accounts) | ~50 MB | -| Witness index | ~5 MB | +| validator index | ~5 MB | | Transaction history (operation_history plugin) | ~200–500 MB | | Account history (account_history plugin) | ~100–300 MB | | Follow/social indexes | ~50–100 MB | @@ -474,7 +474,7 @@ Memory is almost full on block N, increasing to XmM ### Detect corruption ``` -CRITICAL: Witness X account object MISSING from database! +CRITICAL: validator X account object MISSING from database! Could not modify object, most likely a uniqueness constraint was violated ``` @@ -493,7 +493,7 @@ FATAL write lock timeout!!! 1. **`min-free-shared-file-size` must be less than `inc-shared-file-size`** — otherwise cascading resizes occur, causing frequent operation pauses 2. **Pre-allocate generously** — set `shared-file-size` large enough that resize is rare. Each resize pauses all operations while the segment is remapped. 3. **Use `single-write-thread = true`** in production — prevents write lock contention -4. **Avoid resize during witness production** — a witness node should have enough pre-allocated memory that resize never triggers during block generation. The resize barrier guarantees safety but introduces latency. +4. **Avoid resize during validator production** — a validator node should have enough pre-allocated memory that resize never triggers during block generation. The resize barrier guarantees safety but introduces latency. 5. **After corruption, always replay** — there is no safe way to repair a corrupted `shared_memory.bin`. Use `--replay-blockchain` or `--snapshot` to rebuild state from block_log/dlt_block_log. 6. **Backup before config changes** — changing `shared-file-size` to a larger value triggers `grow()` on next startup, which is safe. Reducing it has no effect (file doesn't shrink). 7. **Any new lockless read path must use `operation_guard`** — if you add code that reads from chainbase indices without `with_read_lock()`/`with_write_lock()`, wrap it with `make_operation_guard()` to participate in the resize barrier. Failing to do so can cause stale pointer access during resize. diff --git a/.qoder/docs/snapshot-pause-workflow.md b/.qoder/docs/snapshot-pause-workflow.md index 4fad0f630d..76688f89b5 100644 --- a/.qoder/docs/snapshot-pause-workflow.md +++ b/.qoder/docs/snapshot-pause-workflow.md @@ -1,4 +1,4 @@ -# Snapshot Pause Block Workflow +# Snapshot Pause Block Workflow ## Overview @@ -6,7 +6,7 @@ When the snapshot plugin creates a snapshot, it **pauses P2P block processing** prevent concurrent database modifications. During this pause, incoming blocks from peers are **buffered in a queue** instead of being dropped. After the pause ends, the P2P layer drains the queue (applying all buffered blocks), then checks whether -peers are still ahead. The witness plugin defers block production until all queued +peers are still ahead. The Validator Plugin defers block production until all queued blocks are applied and any remaining gap is filled. ## Sequence Diagram: Snapshot Pause Lifecycle @@ -15,7 +15,7 @@ blocks are applied and any remaining gap is filled. sequenceDiagram participant SP as Snapshot Plugin participant P2P as P2P Layer - participant W as Witness Plugin + participant W as Validator Plugin participant Peer as Remote Peer Note over SP: Block N applied @@ -74,7 +74,7 @@ flowchart TD J -->|REJECTED| N["Log + track rejections"] ``` -## Witness Production Workflow (With Catchup Gate) +## validator Production Workflow (With Catchup Gate) ```mermaid flowchart TD @@ -84,13 +84,13 @@ flowchart TD Gate -->|Yes| Ret2["return not_synced
(defer production)"] Gate -->|No| HF12{"Hardfork 12 checks
(prate, emergency)"} HF12 -->|prate < 33% AND no stale-production override| RetLP["return low_participation
⚠ partition guard"] - HF12 -->|prate >= 33% or override| MinFork{"Minority fork check:
last 21 fork_db blocks
all from our witnesses?"} + HF12 -->|prate >= 33% or override| MinFork{"Minority fork check:
last 21 fork_db blocks
all from our validators?"} MinFork -->|Yes — isolated| RetMF["resync_from_lib()
return minority_fork"] MinFork -->|No| Slot{"get_slot_at_time()"} Slot -->|slot == 0| Ret3["return not_time_yet"] - Slot -->|slot > 0| Witness{"Our witness scheduled?"} - Witness -->|No| Ret4["return not_my_turn"] - Witness -->|Yes| Stale{"scheduled_time <=
head_block_time?"} + Slot -->|slot > 0| validator{"Our validator scheduled?"} + validator -->|No| Ret4["return not_my_turn"] + validator -->|Yes| Stale{"scheduled_time <=
head_block_time?"} Stale -->|Yes| Ret5["return not_time_yet
(slot already filled)"] Stale -->|No| Fork{"Competing block in fork_db?"} Fork -->|Yes, weaker fork| Produce @@ -107,18 +107,18 @@ different failure modes and must both be active: | Guard | Trigger | Scenario it stops | |-------|---------|-------------------| -| `low_participation` | `prate < 33%` (< 7 of 21 witnesses active) | Node in a small isolated segment — stops it from building a chain alone | -| `minority_fork` | Last 21 fork_db blocks are ALL from our witnesses | Node is producing in isolation despite appearing to have enough witnesses locally | +| `low_participation` | `prate < 33%` (< 7 of 21 validators active) | Node in a small isolated segment — stops it from building a chain alone | +| `minority_fork` | Last 21 fork_db blocks are ALL from our validators | Node is producing in isolation despite appearing to have enough validators locally | **Why `low_participation` must not be removed:** If a network partitions into two datacenters and one segment holds fewer than 7 of the -21 scheduled witnesses, it sees participation drop below 33% within ~85 missed slots +21 scheduled validators, it sees participation drop below 33% within ~85 missed slots (~4 minutes). Without this guard that segment would keep building a competing chain that neither side recognises as a minority fork, because `minority_fork` only fires when -**all** recent fork_db blocks are from our witnesses — possible only once the other +**all** recent fork_db blocks are from our validators — possible only once the other segment's blocks are completely absent from our fork_db. -The operator escape hatch for legitimate outages (many witnesses offline but network +The operator escape hatch for legitimate outages (many validators offline but network not partitioned) is `enable-stale-production = true`, which bypasses the `low_participation` check explicitly. See `consensus-emergency-params.md` for the full workflow. @@ -146,14 +146,14 @@ stateDiagram-v2 | `libraries/network/include/graphene/network/dlt_p2p_node.hpp` | `_catchup_after_pause` flag and getter | | `plugins/p2p/p2p_plugin.cpp` | Exposes `is_catching_up_after_pause()` to other plugins | | `plugins/p2p/include/graphene/plugins/p2p/p2p_plugin.hpp` | Public API declaration | -| `plugins/witness/witness.cpp` | Production gate that checks catchup flag | +| `plugins/validator/validator.cpp` | Production gate that checks catchup flag | | `plugins/snapshot/plugin.cpp` | Calls `pause/resume_block_processing()` | ## The Bug (Before Fix) Two interrelated bugs: -**Bug 1 — Write lock deadlock**: The emergency master's witness production loop +**Bug 1 — Write lock deadlock**: The emergency master's validator production loop (250ms tick) bypasses all sync checks. During snapshot creation, the snapshot thread holds a strong DB read lock for 30–120s. The production loop called `generate_block()` → `push_block()` → write lock → **deadlocked** behind the read lock, producing @@ -176,7 +176,7 @@ Sequence: **Production gate during pause**: `is_catching_up_after_pause()` returns true when either `_block_processing_paused` OR `_catchup_after_pause` is set. This prevents the -witness plugin from calling `generate_block()` while the snapshot holds the DB read +Validator Plugin from calling `generate_block()` while the snapshot holds the DB read lock (avoids write-lock deadlock) AND during post-pause catchup (avoids stale-head fork). **Block queue**: During the pause, block-carrying messages (block_reply, block_range_reply, @@ -193,7 +193,7 @@ runs and cleared when: - `transition_to_forward()` runs after a SYNC gap fill (delayed path), or - `periodic_task()` confirms no gap exists (5s fallback) -The witness plugin checks `is_catching_up_after_pause()` in `maybe_produce_block()` and +The Validator Plugin checks `is_catching_up_after_pause()` in `maybe_produce_block()` and defers production while the flag is set. --- @@ -229,7 +229,7 @@ The chain of events in p72: Sync completes → transition_to_forward() → currently_syncing NOT cleared ← BUG -10:54:41–11:04:01 witness loop: +10:54:41–11:04:01 validator loop: is_syncing()=true → return not_synced (rate-limited, silent) not_my_turn_streak stays at 0–2 (resets on not_synced) @@ -237,7 +237,7 @@ The chain of events in p72: → currently_syncing=false → production resumes ``` -The circular deadlock: `currently_syncing=true` blocks our witnesses; our witnesses are +The circular deadlock: `currently_syncing=true` blocks our validators; our validators are the only remaining producers; no FORWARD block arrives to self-clear the flag. ### Why the WATCHDOG evidence confirms this @@ -311,7 +311,7 @@ returned. Deadlock. `std::atomic` — no P2P-thread dispatch needed. **Critical ordering:** `_catchup_after_pause` is set **before** `snapshot_in_progress` is -cleared. If reversed, the witness plugin could observe the snapshot complete while the +cleared. If reversed, the Validator Plugin could observe the snapshot complete while the catchup flag is still false, misclassify the head as a stale fork, and refuse to produce. **Phase 2 (async, P2P thread, no wait):** An async post (fire-and-forget) to the P2P @@ -348,7 +348,7 @@ void p2p_plugin::resume_block_processing() { If the snapshot async task encountered an error or was cancelled, `_block_processing_paused` and `_catchup_after_pause` could remain in their paused state indefinitely, permanently -blocking witness production. +blocking validator production. ### Root cause diff --git a/.qoder/docs/snapshot-plugin.md b/.qoder/docs/snapshot-plugin.md index edce247412..abed79ee50 100644 --- a/.qoder/docs/snapshot-plugin.md +++ b/.qoder/docs/snapshot-plugin.md @@ -1,4 +1,4 @@ -# Snapshot Plugin (DLT Mode) +# Snapshot Plugin (DLT Mode) The snapshot plugin enables near-instant node startup by serializing and restoring the full blockchain state as a JSON snapshot file. Instead of replaying millions of blocks from the block log, a node can load a pre-built snapshot and begin syncing from the snapshot's block height via P2P. @@ -19,7 +19,7 @@ The snapshot plugin enables near-instant node startup by serializing and restori | `--snapshot ` | `string` | Load state from a snapshot file instead of replaying blockchain. The node opens in DLT mode (no block log). Safe for restarts — skips import if shared_memory already exists, and renames the file to `.used` after successful import. | | `--snapshot-auto-latest` | `bool` (default: `false`) | Auto-discover the latest snapshot file in `snapshot-dir` by parsing block numbers from filenames (`snapshot-block-NNNNN.vizjson` or `.json`). Typically used with `--replay-from-snapshot` to avoid specifying the file path manually. Ignored if `--snapshot` is already specified. | | `--replay-from-snapshot` | `bool` (default: `false`) | Crash recovery mode: import a snapshot and then replay blocks from `dlt_block_log` to bring the node up to the latest available state. Unlike `--snapshot`, this always wipes shared memory (assumes corruption), does NOT rename the snapshot to `.used`, and replays subsequent blocks from the DLT rolling block log. Requires `--snapshot ` or `--snapshot-auto-latest`. | -| `--auto-recover-from-snapshot` | `bool` (default: `true`) | Automatic runtime recovery from shared memory corruption. When corruption is detected during block processing or generation (e.g., missing witness account), the node immediately closes the database, finds the latest snapshot, wipes shared memory, imports the snapshot, replays `dlt_block_log`, and resumes P2P sync — all without a restart. Requires `plugin = snapshot` and snapshots in `snapshot-dir`. | +| `--auto-recover-from-snapshot` | `bool` (default: `true`) | Automatic runtime recovery from shared memory corruption. When corruption is detected during block processing or generation (e.g., missing validator account), the node immediately closes the database, finds the latest snapshot, wipes shared memory, imports the snapshot, replays `dlt_block_log`, and resumes P2P sync — all without a restart. Requires `plugin = snapshot` and snapshots in `snapshot-dir`. | | `--create-snapshot ` | `string` | Create a snapshot file at the given path using the current database state, then exit. | | `--sync-snapshot-from-trusted-peer` | `bool` (default: `false`) | Download and load snapshot from trusted peers when state is empty (`head_block_num == 0`). Requires `trusted-snapshot-peer` to be configured. Defaults to `false` (opt-in) — must be explicitly enabled to prevent accidental state wipe via `chainbase::wipe()`. | @@ -60,7 +60,7 @@ vizd --plugin snapshot ### Method 1: One-shot snapshot (stop node, create, exit) -Stop the node, then restart it with the `--create-snapshot` flag. The node will open the existing database (block log + shared memory), replay if needed to bring the state up to date, create the snapshot, and exit — **before** P2P or witness plugins activate: +Stop the node, then restart it with the `--create-snapshot` flag. The node will open the existing database (block log + shared memory), replay if needed to bring the state up to date, create the snapshot, and exit — **before** P2P or validator plugins activate: ```bash vizd --create-snapshot /data/snapshots/viz-snapshot.json --plugin snapshot @@ -71,7 +71,7 @@ vizd --create-snapshot /data/snapshots/viz-snapshot.json --plugin snapshot 1. All plugins call `plugin_initialize()`. The **snapshot plugin** registers a `snapshot_create_callback` on the **chain plugin**. 2. The **chain plugin** `plugin_startup()` opens the database normally — block log, shared memory, and replays from block log if the chainbase revision doesn't match the head block. 3. After the database is fully loaded, the chain plugin calls `snapshot_create_callback()` — the **snapshot plugin** serializes all 32 tracked object types as JSON arrays with a SHA-256 checksum, writes the file, and calls `app().quit()`. -4. The chain plugin **never calls `on_sync()`** — P2P and witness plugins never activate. +4. The chain plugin **never calls `on_sync()`** — P2P and validator plugins never activate. All snapshot creation happens **inside** `chain::plugin_startup()`. The database is fully consistent (post-replay) and no new blocks arrive during serialization. @@ -204,7 +204,7 @@ vizd --snapshot /path/to/snapshot.json --plugin snapshot 8. The chain plugin emits `on_sync` so other plugins (webserver, APIs, etc.) know the node is ready. 9. **P2P plugin** starts — sees the snapshot's head block and begins syncing from **LIB + 1** via the P2P network. -All snapshot loading happens **inside** `chain::plugin_startup()`, before any other plugin starts. P2P and witness never see incomplete/genesis state. +All snapshot loading happens **inside** `chain::plugin_startup()`, before any other plugin starts. P2P and validator never see incomplete/genesis state. ### Restart safety @@ -243,7 +243,7 @@ The snapshot is a single JSON file with this structure: "payload_checksum": "sha256...", "object_counts": { "account": 50000, - "witness": 100, + "validator": 100, "content": 200000, ... } @@ -254,7 +254,7 @@ The snapshot is a single JSON file with this structure: "hardfork_property": [ ... ], "account": [ ... ], "account_authority": [ ... ], - "witness": [ ... ], + "validator": [ ... ], ... "fork_db_head_block": { ... } } @@ -266,12 +266,12 @@ The snapshot is a single JSON file with this structure: **Critical (11)** — consensus-essential, always required: - `dynamic_global_property` — global chain state (singleton) -- `witness_schedule` — current witness schedule (singleton) +- `witness_schedule` — current validator schedule (singleton) - `hardfork_property` — hardfork tracking state (singleton) - `account` — all accounts - `account_authority` — master/active/regular authorities -- `witness` — witness registrations -- `witness_vote` — witness votes +- `validator` — validator registrations +- `witness_vote` — validator votes - `block_summary` — block ID summaries (65536 entries) - `content` — content/posts - `content_vote` — content votes @@ -293,7 +293,7 @@ The snapshot is a single JSON file with this structure: - `award_shares_expire` — expiring award shares - `paid_subscription` — paid subscription offers - `paid_subscribe` — active subscriptions -- `witness_penalty_expire` — witness penalty expirations +- `witness_penalty_expire` — validator penalty expirations **Optional (5)** — metadata and recovery: @@ -440,7 +440,7 @@ At startup, the snapshot plugin detects this condition: 1. Checks if in DLT mode with snapshot serving or periodic creation enabled 2. Compares latest snapshot block number against `dlt_block_log.start_block_num()` 3. If snapshot block < DLT start block, logs a `STALE SNAPSHOT DETECTED` warning -4. On the first fully-synced block, creates an urgent fresh snapshot (async, with witness-aware deferral) +4. On the first fully-synced block, creates an urgent fresh snapshot (async, with validator-aware deferral) This ensures serving nodes always have a snapshot that peers can actually sync from. The check runs automatically — no configuration needed beyond the existing `snapshot-every-n-blocks` or `allow-snapshot-serving` settings. @@ -549,10 +549,10 @@ While `--replay-from-snapshot` requires a manual restart, `--auto-recover-from-s ### How it works -When the node detects corruption during normal operation (e.g., a witness account object is missing from the database during block processing or block generation), it throws a `shared_memory_corruption_exception`. This exception is caught at the plugin level: +When the node detects corruption during normal operation (e.g., a validator account object is missing from the database during block processing or block generation), it throws a `shared_memory_corruption_exception`. This exception is caught at the plugin level: 1. **Block acceptance path** (P2P sync): `plugin::accept_block()` catches the exception and calls `attempt_auto_recovery()`. -2. **Block generation path** (witness): The witness plugin's `generate_block` retry loop catches the exception before the generic `fc::exception` handler, and calls `chain().attempt_auto_recovery()`. +2. **Block generation path** (validator): The Validator Plugin's `generate_block` retry loop catches the exception before the generic `fc::exception` handler, and calls `chain().attempt_auto_recovery()`. ### Recovery flow (`attempt_auto_recovery`) @@ -652,7 +652,7 @@ If the node has 0 blocks and no `trusted-snapshot-peer` is configured, a console 4. **Verification**: Streams the downloaded file through SHA-256 to verify checksum (without loading into memory). 5. **Import**: Clears database state, loads the verified snapshot, initializes hardforks. Each stage (decompress, parse, validate, import) is logged to console with timing. -All operations happen during `chain::plugin_startup()`, **before** P2P and witness plugins activate. The node is fully blocked during download and import — no blocks are processed until the snapshot is loaded. +All operations happen during `chain::plugin_startup()`, **before** P2P and validator plugins activate. The node is fully blocked during download and import — no blocks are processed until the snapshot is loaded. ### Security features @@ -1001,7 +1001,7 @@ As blocks are applied: - `dlt_block_log` receives new irreversible blocks - `block_summary` is updated with new block IDs - `fork_db` grows with new blocks -- LIB advances as witnesses produce super-majority +- LIB advances as validators produce super-majority #### Step 9: Subsequent synopsis rounds @@ -1194,10 +1194,10 @@ The snapshot plugin required changes to several core components: |-----------|--------| | `chainbase::generic_index` | Added `set_next_id()` / `next_id()` for ID preservation during import | | `fork_database.cpp` | Fixed `_unlinked_index.insert()` dead code (moved before `throw`); added `_push_next(item)` call at end of `_push_block` to resolve previously-unlinkable blocks; added duplicate block check in `_push_block`; added `_unlinked_index.clear()` to `reset()` | -| `database.hpp/cpp` | Added `open_from_snapshot()`, `initialize_hardforks()`, `reindex_from_dlt()`, `get_fork_db()`, `_dlt_mode` flag with `set_dlt_mode()` setter; DLT mode skips block_log writes and detects empty block_log on restart; `is_known_block()` in DLT mode checks block_summary then verifies preferred chain via `find_block_id_for_num()` (prevents both lying to peers and breaking sync negotiation); DLT restart seeds fork_db from dlt_block_log when available; early rejection in `_push_block`: duplicate blocks (at/before head, same ID), blocks at/before head on different fork with unknown parent (prevents P2P sync restart loop), far-ahead broadcast blocks (parent unknown and not head_block_id), with immediate successor (`previous == head_block_id()`) always allowed; DLT block log gap logging via `wlog`; DLT block log empty-skip logic for fresh snapshot imports; corruption detection throws `shared_memory_corruption_exception` (instead of `FC_ASSERT`) from all witness-account-missing checks (generate_block, process_funds, HF4 path) | +| `database.hpp/cpp` | Added `open_from_snapshot()`, `initialize_hardforks()`, `reindex_from_dlt()`, `get_fork_db()`, `_dlt_mode` flag with `set_dlt_mode()` setter; DLT mode skips block_log writes and detects empty block_log on restart; `is_known_block()` in DLT mode checks block_summary then verifies preferred chain via `find_block_id_for_num()` (prevents both lying to peers and breaking sync negotiation); DLT restart seeds fork_db from dlt_block_log when available; early rejection in `_push_block`: duplicate blocks (at/before head, same ID), blocks at/before head on different fork with unknown parent (prevents P2P sync restart loop), far-ahead broadcast blocks (parent unknown and not head_block_id), with immediate successor (`previous == head_block_id()`) always allowed; DLT block log gap logging via `wlog`; DLT block log empty-skip logic for fresh snapshot imports; corruption detection throws `shared_memory_corruption_exception` (instead of `FC_ASSERT`) from all validator-account-missing checks (generate_block, process_funds, HF4 path) | | `database_exceptions.hpp` | Added `shared_memory_corruption_exception` (code 4140000) for runtime corruption detection | | `dlt_block_log.hpp/cpp` | New class: offset-aware rolling block log with 8-byte header index, `truncate_before()` for rotation, read/write with mutex locking | -| `chain plugin` | Added `snapshot_load_callback`, `snapshot_create_callback`, `snapshot_p2p_sync_callback`; restart safety (shared_memory check + `.used` rename + file existence check); LIB promotion after snapshot import (LIB = head_block_num for correct P2P synopsis); `dlt-block-log-max-blocks` config option; diagnostic warning when node has 0 blocks and no snapshot sync configured; `--replay-from-snapshot` crash recovery mode (wipes shared_memory, imports snapshot, replays dlt_block_log via `reindex_from_dlt`, does not rename snapshot); `--snapshot-auto-latest` auto-discovery of latest snapshot in `snapshot-dir`; `--auto-recover-from-snapshot` (default: `true`) immediate runtime recovery — `accept_block` and witness `generate_block` catch `shared_memory_corruption_exception`, call `attempt_auto_recovery()` which closes DB, finds latest snapshot, wipes shared memory, imports snapshot, replays dlt_block_log, and resumes P2P sync without restart | +| `chain plugin` | Added `snapshot_load_callback`, `snapshot_create_callback`, `snapshot_p2p_sync_callback`; restart safety (shared_memory check + `.used` rename + file existence check); LIB promotion after snapshot import (LIB = head_block_num for correct P2P synopsis); `dlt-block-log-max-blocks` config option; diagnostic warning when node has 0 blocks and no snapshot sync configured; `--replay-from-snapshot` crash recovery mode (wipes shared_memory, imports snapshot, replays dlt_block_log via `reindex_from_dlt`, does not rename snapshot); `--snapshot-auto-latest` auto-discovery of latest snapshot in `snapshot-dir`; `--auto-recover-from-snapshot` (default: `true`) immediate runtime recovery — `accept_block` and validator `generate_block` catch `shared_memory_corruption_exception`, call `attempt_auto_recovery()` which closes DB, finds latest snapshot, wipes shared memory, imports snapshot, replays dlt_block_log, and resumes P2P sync without restart | | `snapshot plugin` | Full implementation: create/load/periodic snapshots; P2P TCP sync (serve + download with concurrent fiber handling on a dedicated `fc::thread`); 5-second peer operation timeout (connect, send, read); anti-spam (rate limiting, mutex-protected session tracking, enforced per-operation connection deadline); cached snapshot info; streaming SHA-256 checksum; built-in rotation (`snapshot-max-age-days`); elapsed time logging for all operations; console progress logging for download/import; `sync-snapshot-from-trusted-peer` defaults to `false` (opt-in); auto-creates snapshot directory; periodic snapshots only trigger on live blocks (skipped during P2P sync); memory optimization: compressed data freed immediately after decompression; `--snapshot-auto-latest` auto-discovery of latest snapshot file by block number; `find_latest_snapshot()` helper; **async snapshot creation**: `on_applied_block` schedules snapshot on a dedicated `fc::thread` (not the main thread's fc scheduler, which is blocked by `io_serv->run()`); `create_snapshot` splits into Phase 1 (read lock for DB serialization only) and Phase 2 (compression + file I/O without lock); `snapshot_in_progress` atomic flag prevents overlapping snapshots; `write_snapshot_to_file` accepts pre-captured header+state (no DB access needed); shutdown waits for in-progress snapshot before quitting thread; **DLT restart safety**: `load_snapshot()` persists the snapshot's head block into `dlt_block_log` so `database::open()` can seed `fork_db` on restart; **stale snapshot detection**: at startup, compares latest snapshot block against `dlt_block_log.start_block_num()` — if snapshot is older than DLT start, creates an urgent fresh snapshot on first synced block to prevent serving a broken snapshot with unsyncable gap | | `content_object.hpp` | Added missing `FC_REFLECT` for `content_object`, `content_type_object`, `content_vote_object` | | `witness_objects.hpp` | Added missing `FC_REFLECT` for `witness_vote_object` | diff --git a/.qoder/docs/staking-and-dao-governance.md b/.qoder/docs/staking-and-dao-governance.md index be5146770b..0905c6814d 100644 --- a/.qoder/docs/staking-and-dao-governance.md +++ b/.qoder/docs/staking-and-dao-governance.md @@ -1,4 +1,4 @@ -# VIZ Staking (Vesting Shares) and DAO Self-Governance +# VIZ Staking (Vesting Shares) and DAO Self-Governance ## What is Staking in VIZ @@ -82,27 +82,27 @@ When removing delegation, the SHARES enter a 5-day expiration period (matching e SHARES are the universal governance token. Every meaningful action in VIZ is weighted by `effective_vesting_shares`: -### 1. Witness Voting +### 1. validator Voting -Witnesses produce blocks and set chain parameters. Every account can vote for up to 100 witnesses. +validators produce blocks and set chain parameters. Every account can vote for up to 100 validators. ```json ["account_witness_vote", { "account": "alice", - "witness": "node1", + "validator": "node1", "approve": true }] ``` -**Fair-DPOS weighting**: vote weight is divided equally across all witnesses the account votes for: +**Fair-DPOS weighting**: vote weight is divided equally across all validators the account votes for: ``` fair_weight = (vesting_shares + proxied_votes) / witnesses_voted_for ``` -This prevents concentration of power — if you vote for 10 witnesses, each gets 1/10 of your stake weight, not the full amount. +This prevents concentration of power — if you vote for 10 validators, each gets 1/10 of your stake weight, not the full amount. -Accounts can also set a **proxy** (`account_witness_proxy_operation`), delegating their witness voting to another account. +Accounts can also set a **proxy** (`account_witness_proxy_operation`), delegating their validator voting to another account. ### 2. Committee DAO Voting @@ -155,7 +155,7 @@ A `fixed_award_operation` variant lets you specify an exact reward amount — th ### 4. Chain Parameter Governance -Witnesses publish their preferred chain parameters, and the **median** value across all active witnesses becomes the consensus setting. Since witnesses are elected by stake-weighted voting, the chain parameters are indirectly controlled by all SHARES holders. +validators publish their preferred chain parameters, and the **median** value across all active validators becomes the consensus setting. Since validators are elected by stake-weighted voting, the chain parameters are indirectly controlled by all SHARES holders. Parameters governed this way include: - `account_creation_fee` — cost to create new accounts @@ -189,11 +189,11 @@ VIZ is not just a blockchain — it is a **Decentralized Autonomous Organization | DAO treasury | Committee fund + reward fund | | DAO shares / governance tokens | SHARES (vesting shares) | | Proposal voting | Committee worker requests | -| Board of directors | Elected witnesses | -| Director elections | Witness voting (Fair-DPOS) | +| Board of directors | Elected validators | +| Director elections | validator voting (Fair-DPOS) | | Dividend distribution | Award mechanism (reward fund) | | Bylaws / parameters | Chain properties (median governance) | -| Delegation of voting rights | `delegate_vesting_shares` + witness proxy | +| Delegation of voting rights | `delegate_vesting_shares` + validator proxy | ### How Each Member "Controls Their Share of the DAO" @@ -205,9 +205,9 @@ If you hold 1% of all SHARES, your vote carries exactly 1% weight in every commi #### 2. Electing Leadership -Witnesses are the "board of directors" — they produce blocks, validate transactions, and **set chain parameters** through median voting. Every SHARES holder votes for witnesses, and Fair-DPOS ensures your voting power is split equally across your chosen witnesses, preventing vote concentration. +validators are the "board of directors" — they produce blocks, validate transactions, and **set chain parameters** through median voting. Every SHARES holder votes for validators, and Fair-DPOS ensures your voting power is split equally across your chosen validators, preventing vote concentration. -If you don't want to vote directly, you can **set a proxy** — delegate your witness voting to someone you trust, just like a proxy vote in a shareholder meeting. +If you don't want to vote directly, you can **set a proxy** — delegate your validator voting to someone you trust, just like a proxy vote in a shareholder meeting. #### 3. Direct Value Distribution @@ -228,14 +228,14 @@ The delegatee gains your voting power for awards, committee votes, and bandwidth #### 5. Rule-Setting -Chain parameters are set by the **median** of witness-published values. Since you elect witnesses, you indirectly control: +Chain parameters are set by the **median** of validator-published values. Since you elect validators, you indirectly control: - How expensive it is to create accounts - How the reward fund is distributed between content creators and curators - What the minimum participation threshold is for committee proposals - How long vesting withdrawals take - Network capacity and transaction limits -If the current parameters don't serve your interests, you vote for witnesses who share your vision. The median mechanism ensures no single witness (or voter) can impose extreme values — only the community consensus prevails. +If the current parameters don't serve your interests, you vote for validators who share your vision. The median mechanism ensures no single validator (or voter) can impose extreme values — only the community consensus prevails. ### Why This Works as Self-Governance @@ -243,11 +243,11 @@ If the current parameters don't serve your interests, you vote for witnesses who 2. **Bipolar voting**: negative votes are first-class citizens. Opposing a bad proposal is just as powerful as supporting a good one. This prevents apathy-driven approval. -3. **Continuous governance**: there are no "governance seasons" or "voting periods" for witnesses — you can change your votes at any time. Committee proposals have time-bounded voting, but you can update your vote throughout. +3. **Continuous governance**: there are no "governance seasons" or "voting periods" for validators — you can change your votes at any time. Committee proposals have time-bounded voting, but you can update your vote throughout. 4. **Skin in the game**: SHARES are locked tokens. To gain governance power, you must commit capital. To exit, you wait 28 days. This ensures voters have long-term alignment with the ecosystem. -5. **No trusted intermediaries**: all governance rules are enforced by protocol code, not by humans. Committee payouts, witness elections, parameter changes — everything is automatic and verifiable. +5. **No trusted intermediaries**: all governance rules are enforced by protocol code, not by humans. Committee payouts, validator elections, parameter changes — everything is automatic and verifiable. 6. **Delegation without custody**: you can amplify others' power without giving them your tokens. Revocation is always possible. This enables trust hierarchies without centralization. @@ -255,13 +255,13 @@ If the current parameters don't serve your interests, you vote for witnesses who ``` Stake VIZ → Get SHARES → Governance Power - ├── Vote for witnesses → Control block production & chain parameters + ├── Vote for validators → Control block production & chain parameters ├── Vote on committee → Control treasury spending ├── Award other accounts → Distribute value from reward fund ├── Delegate to others → Amplify allies' governance power - └── Set chain parameters → Shape the rules (via witness election) + └── Set chain parameters → Shape the rules (via validator election) ↓ - Witnesses produce blocks → Rewards generated → Reward fund grows + validators produce blocks → Rewards generated → Reward fund grows ↓ Committee fund grows → Proposals funded → Ecosystem develops ↓ @@ -284,7 +284,7 @@ This is a complete, self-sustaining DAO where every participant's influence is e | `CHAIN_MIN_DELEGATION` | 1 VIZ | Minimum delegation amount | | `CHAIN_CREATE_ACCOUNT_DELEGATION_RATIO` | 10× | Delegation multiplier for account creation | | `CHAIN_CREATE_ACCOUNT_DELEGATION_TIME` | 30 days | Minimum delegation lock for account creation | -| `CHAIN_MAX_ACCOUNT_WITNESS_VOTES` | 100 | Maximum witnesses per account | +| `CHAIN_MAX_ACCOUNT_WITNESS_VOTES` | 100 | Maximum validators per account | | `CONSENSUS_VOTE_ACCOUNTING_MIN_RSHARES` | 5000000 | Minimum rshares for award to have effect | | `CONSENSUS_COMMITTEE_REQUEST_APPROVE_MIN_PERCENT` | 1000 (10%) | Default participation threshold for proposals | | `CONSENSUS_BANDWIDTH_RESERVE_BELOW` | 500 SHARES | Threshold for bandwidth reserve | diff --git a/.qoder/docs/witness-guard.md b/.qoder/docs/validator-guard.md similarity index 54% rename from .qoder/docs/witness-guard.md rename to .qoder/docs/validator-guard.md index 3e684a6bc4..082a14a27f 100644 --- a/.qoder/docs/witness-guard.md +++ b/.qoder/docs/validator-guard.md @@ -1,12 +1,12 @@ -# Witness Guard Plugin +# validator guard Plugin -The `witness_guard` plugin is an automated maintenance tool for VIZ witness node operators. It monitors configured witness accounts and automatically restores their signing keys when they are reset to null (which disables the witness). +The `witness_guard` plugin is an automated maintenance tool for VIZ validator node operators. It monitors configured validator accounts and automatically restores their signing keys when they are reset to null (which disables the validator). ## Purpose -In VIZ a witness can be disabled (signing key set to null) by manual intervention, security protocols, or certain network conditions. This plugin automates the restore process so the witness stays active without manual monitoring. +In VIZ a validator can be disabled (signing key set to null) by manual intervention, security protocols, or certain network conditions. This plugin automates the restore process so the validator stays active without manual monitoring. -When the plugin detects a null signing key on-chain, it constructs, signs, and broadcasts a `witness_update_operation` to re-enable the witness using the provided private keys. +When the plugin detects a null signing key on-chain, it constructs, signs, and broadcasts a `witness_update_operation` to re-enable the validator using the provided private keys. ## Configuration @@ -16,12 +16,12 @@ The plugin is configured via `config.ini` or command-line arguments. | Option | Type | Default | Description | | :--- | :--- | :--- | :--- | -| `witness-guard-enabled` | boolean | `true` | Enables or disables the plugin logic globally. | -| `witness-guard-interval` | uint32 | `20` | Frequency of periodic checks in blocks (20 blocks ≈ 60 seconds). | -| `witness-guard-witness` | vector\ | N/A | A JSON triplet: witness name, signing WIF, active WIF. Repeatable. | -| `witness-guard-disable` | uint32 | `5` | Number of consecutive blocks produced by the same witness before auto-disabling (setting signing key to null). Set to `0` to disable this feature. | +| `validator-guard-enabled` | boolean | `true` | Enables or disables the plugin logic globally. | +| `validator-guard-interval` | uint32 | `20` | Frequency of periodic checks in blocks (20 blocks ≈ 60 seconds). | +| `validator-guard-validator` | vector\ | N/A | A JSON triplet: validator name, signing WIF, active WIF. Repeatable. | +| `validator-guard-disable` | uint32 | `5` | Number of consecutive blocks produced by the same validator before auto-disabling (setting signing key to null). Set to `0` to disable this feature. | -The plugin also reads the shared `enable-stale-production` option from the witness plugin configuration. +The plugin also reads the shared `enable-stale-production` option from the Validator Plugin configuration. ### Enabling the Plugin @@ -35,41 +35,41 @@ plugin = witness_guard ```ini # Monitor and auto-restore winet1 -witness-guard-witness = ["winet1", "5K_SIGNING_PRIVATE_WIF", "5K_ACTIVE_PRIVATE_WIF"] +validator-guard-validator = ["winet1", "5K_SIGNING_PRIVATE_WIF", "5K_ACTIVE_PRIVATE_WIF"] -# Monitor multiple witnesses by repeating the option -witness-guard-witness = ["winet2", "5J_SIGNING_PRIVATE_WIF", "5J_ACTIVE_PRIVATE_WIF"] +# Monitor multiple validators by repeating the option +validator-guard-validator = ["winet2", "5J_SIGNING_PRIVATE_WIF", "5J_ACTIVE_PRIVATE_WIF"] # Check every 10 blocks instead of 20 -witness-guard-interval = 10 +validator-guard-interval = 10 ``` ## Internal Logic ### Startup -1. **Key parsing**: Each `witness-guard-witness` entry is parsed as a JSON array of three strings. Both WIF keys are validated. -2. **Stale production detection**: If `enable-stale-production=true` is set in the witness config, auto-restore is initially disabled (see Safety Guards below). -3. **Disable threshold**: If `witness-guard-disable` is greater than 0, the consecutive-block auto-disable feature is enabled (see below). -4. **Authority validation**: After the chain database is open, the plugin verifies that each configured active key actually has authority on-chain. Witnesses whose accounts are not found are removed from monitoring. +1. **Key parsing**: Each `validator-guard-validator` entry is parsed as a JSON array of three strings. Both WIF keys are validated. +2. **Stale production detection**: If `enable-stale-production=true` is set in the validator config, auto-restore is initially disabled (see Safety Guards below). +3. **Disable threshold**: If `validator-guard-disable` is greater than 0, the consecutive-block auto-disable feature is enabled (see below). +4. **Authority validation**: After the chain database is open, the plugin verifies that each configured active key actually has authority on-chain. validators whose accounts are not found are removed from monitoring. 5. **Initial check**: An immediate check is attempted. If the node is already in sync, the result is cached so the plugin switches to its normal periodic schedule. ### Per-Block Signal Handler (`applied_block`) On every new block the plugin: -0. **Consecutive-block auto-disable**: If `witness-guard-disable > 0` and the block was produced by one of our monitored witnesses, the plugin increments a per-witness consecutive-block counter. When the counter reaches the configured threshold, a `witness_update_operation` with a null signing key is broadcast to disable the witness, and it is marked as auto-disabled. If the block was produced by a *different* witness, all counters are reset to zero (the streak is broken). +0. **Consecutive-block auto-disable**: If `validator-guard-disable > 0` and the block was produced by one of our monitored validators, the plugin increments a per-validator consecutive-block counter. When the counter reaches the configured threshold, a `witness_update_operation` with a null signing key is broadcast to disable the validator, and it is marked as auto-disabled. If the block was produced by a *different* validator, all counters are reset to zero (the streak is broken). 1. **Transaction confirmation**: Scans the block for any pending restore transaction IDs. When found, the restore is marked as confirmed and tracking state is cleared. -2. **Look-ahead scheduling**: If any monitored witness is scheduled to produce within the next 3 slots, an immediate check is triggered so the key can be restored before the slot arrives. +2. **Look-ahead scheduling**: If any monitored validator is scheduled to produce within the next 3 slots, an immediate check is triggered so the key can be restored before the slot arrives. 3. **Periodic check**: Otherwise runs `check_and_restore_internal` at the configured interval. While the node is still syncing after startup, checks run every 10 blocks instead. ### Core Check (`check_and_restore_internal`) -1. **Stale production guard**: If `enable-stale-production` is active and network participation is below 33%, all checks are skipped. Once participation reaches ≥ 33% the stale flag is auto-cleared (same logic as the witness plugin) and auto-restore resumes. During emergency consensus mode the guard is bypassed. +1. **Stale production guard**: If `enable-stale-production` is active and network participation is below 33%, all checks are skipped. Once participation reaches ≥ 33% the stale flag is auto-cleared (same logic as the Validator Plugin) and auto-restore resumes. During emergency consensus mode the guard is bypassed. 2. **Sync check**: Head block time must be within `2 × CHAIN_BLOCK_INTERVAL` seconds of wall-clock time. 3. **Long fork safety**: If the Last Irreversible Block (LIB) is older than 200 seconds, restoration is skipped to avoid acting on a stale fork. 4. **Expiry cleanup**: Stale entries in `_pending_confirmations` are expired so that failed broadcasts can be retried. -5. **Witness iteration**: For each configured witness, the on-chain signing key is checked. If the key is present, any pending restore state and auto-disabled flag are cleared. If null and the witness was auto-disabled by the consecutive-block guard, auto-restore is skipped (the operator must investigate and restart). Otherwise, if no restore is currently in-flight (or the previous one expired), `send_witness_update` is called. +5. **validator iteration**: For each configured validator, the on-chain signing key is checked. If the key is present, any pending restore state and auto-disabled flag are cleared. If null and the validator was auto-disabled by the consecutive-block guard, auto-restore is skipped (the operator must investigate and restart). Otherwise, if no restore is currently in-flight (or the previous one expired), `send_witness_update` is called. ### Restore Transaction (`send_witness_update`) @@ -77,7 +77,7 @@ On every new block the plugin: 2. Wraps it in a `signed_transaction` with a 30-second expiration and head block reference. 3. Signs with the configured active private key. 4. Broadcasts via the P2P plugin. -5. Tracks the transaction ID in `_pending_confirmations` and the witness in `_restore_pending` to prevent duplicate broadcasts. +5. Tracks the transaction ID in `_pending_confirmations` and the validator in `_restore_pending` to prevent duplicate broadcasts. ### Disable Transaction (`send_witness_disable`) @@ -85,7 +85,7 @@ On every new block the plugin: 2. Wraps it in a `signed_transaction` with a 30-second expiration and head block reference. 3. Signs with the configured active private key. 4. Broadcasts via the P2P plugin. -5. Adds the witness to the `_auto_disabled_witnesses` set so that the auto-restore logic does **not** re-enable it automatically. +5. Adds the validator to the `_auto_disabled_witnesses` set so that the auto-restore logic does **not** re-enable it automatically. ## Safety Guards @@ -96,7 +96,7 @@ On every new block the plugin: | **Sync check** | Restoration only runs when the node is synchronized (head block recent). | | **Long fork detection** | If LIB is older than 200 seconds, restoration is skipped. | | **Authority validation** | At startup, configured active keys are verified against on-chain authority. | -| **Consecutive-block auto-disable** | When a monitored witness produces `witness-guard-disable` consecutive blocks, it is automatically disabled (signing key set to null). Auto-restore is suppressed until the operator manually restores the key (at which point the flag is cleared). | +| **Consecutive-block auto-disable** | When a monitored validator produces `validator-guard-disable` consecutive blocks, it is automatically disabled (signing key set to null). Auto-restore is suppressed until the operator manually restores the key (at which point the flag is cleared). | | **Duplicate prevention** | In-flight restores are tracked with expiration; no duplicate transactions are sent. | ## Security Considerations @@ -108,7 +108,7 @@ On every new block the plugin: ## Logs * **Initialization**: - `witness_guard: monitoring witness 'winet1' (signing key: VIZ...)` + `witness_guard: monitoring validator 'winet1' (signing key: VIZ...)` * **Stale production detected**: `witness_guard: enable-stale-production detected — auto-restore is DISABLED until network participation >= 33%` * **Stale production auto-cleared**: @@ -122,30 +122,30 @@ On every new block the plugin: * **Long fork warning**: `witness_guard: POTENTIAL LONG FORK DETECTED! LIB #N is Xs old. Skipping restoration.` * **Consecutive-block auto-disable triggered**: - `witness_guard: witness '${w}' produced ${c} consecutive blocks — auto-disabling (threshold=${t})` + `witness_guard: validator '${w}' produced ${c} consecutive blocks — auto-disabling (threshold=${t})` * **Disable broadcast sent**: `witness_guard: broadcasting witness_update [ID: ...] for '${w}' — DISABLING (setting key to null)` -* **Auto-restore skipped (auto-disabled witness)**: +* **Auto-restore skipped (auto-disabled validator)**: `witness_guard: '${w}' was auto-disabled (consecutive block limit), skipping auto-restore` * **Failure**: `witness_guard: witness_update FAILED for 'winet1': [error details]` ## Troubleshooting -**Error: witness-guard-witness expects [name, signing_wif, active_wif]** +**Error: validator-guard-validator expects [name, signing_wif, active_wif]** Ensure each entry is a valid JSON array with exactly 3 strings. Use double quotes inside the brackets. **Restore not triggering** -1. Check that `witness-guard-enabled` is `true`. +1. Check that `validator-guard-enabled` is `true`. 2. Ensure the node is fully synchronized. -3. Verify the account name is a registered witness on the network. +3. Verify the account name is a registered validator on the network. 4. If `enable-stale-production=true` is set, auto-restore is disabled until network participation reaches ≥ 33%. **Transaction failed** -Check that the `active_wif` belongs to the witness account. If the account's active authority has been changed, the plugin cannot sign the update. The startup log will warn about mismatched keys. +Check that the `active_wif` belongs to the validator account. If the account's active authority has been changed, the plugin cannot sign the update. The startup log will warn about mismatched keys. -**Witness auto-disabled and not restoring** -If the log shows `was auto-disabled (consecutive block limit), skipping auto-restore`, the witness produced too many consecutive blocks and was automatically disabled as a safety measure. The operator must investigate, manually restore the signing key (or restart the node), at which point the auto-disabled flag clears. +**validator auto-disabled and not restoring** +If the log shows `was auto-disabled (consecutive block limit), skipping auto-restore`, the validator produced too many consecutive blocks and was automatically disabled as a safety measure. The operator must investigate, manually restore the signing key (or restart the node), at which point the auto-disabled flag clears. **Authority warning at startup** -`WARNING: Configured active key for witness 'X' does NOT have authority on-chain` — the configured active WIF does not match any key in the account's active authority. Update the key in `config.ini`. +`WARNING: Configured active key for validator 'X' does NOT have authority on-chain` — the configured active WIF does not match any key in the account's active authority. Update the key in `config.ini`. diff --git a/.qoder/docs/witness-plugin-refactoring-2026-05-14.md b/.qoder/docs/validator-plugin-refactoring-2026-05-14.md similarity index 89% rename from .qoder/docs/witness-plugin-refactoring-2026-05-14.md rename to .qoder/docs/validator-plugin-refactoring-2026-05-14.md index 7b5b6d5eac..1ba5dfbe51 100644 --- a/.qoder/docs/witness-plugin-refactoring-2026-05-14.md +++ b/.qoder/docs/validator-plugin-refactoring-2026-05-14.md @@ -1,14 +1,14 @@ -# Witness Plugin Refactoring: Remove Cached Flags +# Validator Plugin Refactoring: Remove Cached Flags ## Date: 2026-05-14 ## Summary -Refactored witness plugin to eliminate cached internal state flags and instead query actual state directly from database and other plugins on every production tick. +Refactored Validator Plugin to eliminate cached internal state flags and instead query actual state directly from database and other plugins on every production tick. ## Problem -The witness plugin was caching critical state in internal variables: +The Validator Plugin was caching critical state in internal variables: - `_production_enabled` (bool): Whether production should be active - Indirect checks via `p2p().is_catching_up_after_pause()` for snapshot status @@ -25,7 +25,7 @@ This created several issues: **File:** `plugins/snapshot/include/graphene/plugins/snapshot/plugin.hpp` ```cpp /// Returns true if a snapshot creation is currently in progress. -/// Used by the witness plugin to defer block production during +/// Used by the Validator Plugin to defer block production during /// snapshot serialization (avoids write-lock contention). bool is_snapshot_in_progress() const; ``` @@ -38,9 +38,9 @@ bool snapshot_plugin::is_snapshot_in_progress() const { } ``` -### 2. Added Snapshot Plugin Dependency to Witness Plugin +### 2. Added Snapshot Plugin Dependency to Validator Plugin -**File:** `plugins/witness/include/graphene/plugins/witness/witness.hpp` +**File:** `plugins/validator/include/graphene/plugins/validator/validator.hpp` ```cpp #include @@ -49,7 +49,7 @@ public: APPBASE_PLUGIN_REQUIRES((chain::plugin) (p2p::p2p_plugin) (snapshot::snapshot_plugin)) ``` -**File:** `plugins/witness/witness.cpp` +**File:** `plugins/validator/validator.cpp` ```cpp struct witness_plugin::impl final { impl(): @@ -206,13 +206,13 @@ if (_ever_produced) { **Before:** ```cpp -elog("WITNESS-WATCHDOG: ... prod=${pe} ...", +elog("validator-WATCHDOG: ... prod=${pe} ...", ("pe", _production_enabled)); ``` **After:** ```cpp -elog("WITNESS-WATCHDOG: ... skip_flags=${sf} ...", +elog("validator-WATCHDOG: ... skip_flags=${sf} ...", ("sf", _production_skip_flags)); ``` @@ -251,7 +251,7 @@ Diagnostic logs now show actual skip flags and state from database, not cached b **None** - all config options remain the same: - `--enable-stale-production` still works (sets `skip_undo_history_check` flag) - `--required-participation` unchanged -- `--witness`, `--private-key`, `--emergency-private-key` unchanged +- `--validator`, `--private-key`, `--emergency-private-key` unchanged ### Behavior Changes **Minimal** - production logic identical, just queries state differently: @@ -265,14 +265,14 @@ Diagnostic logs now show actual skip flags and state from database, not cached b bool snapshot_plugin::is_snapshot_in_progress() const; ``` -**New dependency in witness plugin:** +**New dependency in Validator Plugin:** ```cpp APPBASE_PLUGIN_REQUIRES((chain::plugin) (p2p::p2p_plugin) (snapshot::snapshot_plugin)) ``` ## Testing Recommendations -1. **Normal production**: Verify witness produces blocks normally +1. **Normal production**: Verify validator produces blocks normally 2. **Snapshot creation**: Verify production defers during snapshot (check logs for "snapshot creation in progress") 3. **Emergency mode**: Verify emergency master produces regardless of sync state 4. **Minority fork recovery**: Verify production resumes after resync completes @@ -283,8 +283,8 @@ APPBASE_PLUGIN_REQUIRES((chain::plugin) (p2p::p2p_plugin) (snapshot::snapshot_pl 1. `plugins/snapshot/include/graphene/plugins/snapshot/plugin.hpp` - Added `is_snapshot_in_progress()` declaration 2. `plugins/snapshot/plugin.cpp` - Implemented `is_snapshot_in_progress()` -3. `plugins/witness/include/graphene/plugins/witness/witness.hpp` - Added snapshot plugin dependency -4. `plugins/witness/witness.cpp` - Major refactoring: +3. `plugins/validator/include/graphene/plugins/validator/validator.hpp` - Added snapshot plugin dependency +4. `plugins/validator/validator.cpp` - Major refactoring: - Added snapshot plugin reference - Removed `_production_enabled` flag - Updated all production readiness checks diff --git a/.qoder/docs/witness-plugin.md b/.qoder/docs/validator-plugin.md similarity index 87% rename from .qoder/docs/witness-plugin.md rename to .qoder/docs/validator-plugin.md index a0b26dbcd0..39e0564b7c 100644 --- a/.qoder/docs/witness-plugin.md +++ b/.qoder/docs/validator-plugin.md @@ -1,10 +1,10 @@ -# Witness Plugin Documentation +# Validator Plugin Documentation ## Overview -The witness plugin is responsible for block production in the VIZ blockchain. It manages witness scheduling, block signing, broadcast, and implements sophisticated safety mechanisms including minority fork detection, emergency consensus support, and production watchdog recovery. +The validator plugin is responsible for block production in the VIZ blockchain. It manages validator scheduling, block signing, broadcast, and implements sophisticated safety mechanisms including minority fork detection, emergency consensus support, and production watchdog recovery. -**Location:** `plugins/witness/witness.cpp`, `plugins/witness/include/graphene/plugins/witness/witness.hpp` +**Location:** `plugins/validator/validator.cpp`, `plugins/validator/include/graphene/plugins/validator/validator.hpp` --- @@ -15,16 +15,16 @@ The witness plugin is responsible for block production in the VIZ blockchain. It | Option | Type | Default | Description | |--------|------|---------|-------------| | `enable-stale-production` | bool | false | Enable block production even if the chain is stale. Overrides sync and participation checks. | -| `required-participation` | uint32_t | 33% (33 * CHAIN_1_PERCENT) | Minimum witness participation percentage required to produce blocks. | -| `witness` / `-w` | string (multi) | - | Name of witness controlled by this node. Can be specified multiple times. | +| `required-participation` | uint32_t | 33% (33 * CHAIN_1_PERCENT) | Minimum validator participation percentage required to produce blocks. | +| `validator` / `-w` | string (multi) | - | Name of validator controlled by this node. Can be specified multiple times. | | `private-key` | string (WIF, multi) | - | WIF private key(s) for signing blocks. | -| `emergency-private-key` | string (WIF, multi) | - | WIF private key for emergency consensus production. Auto-adds `CHAIN_EMERGENCY_WITNESS_ACCOUNT` to witness set. | +| `emergency-private-key` | string (WIF, multi) | - | WIF private key for emergency consensus production. Auto-adds `CHAIN_EMERGENCY_WITNESS_ACCOUNT` to validator set. | ### Fork Collision Resolution | Option | Type | Default | Description | |--------|------|---------|-------------| -| `fork-collision-timeout-blocks` | uint32_t | 21 | Number of consecutive fork-collision deferrals before forcing production. One full witness round = 21 blocks (63 seconds). | +| `fork-collision-timeout-blocks` | uint32_t | 21 | Number of consecutive fork-collision deferrals before forcing production. One full validator round = 21 blocks (63 seconds). | ### NTP Synchronization @@ -52,8 +52,8 @@ The witness plugin is responsible for block production in the VIZ blockchain. It APPBASE_PLUGIN_REQUIRES((chain::plugin) (p2p::p2p_plugin) (snapshot::snapshot_plugin)) ``` -The witness plugin requires: -- **chain::plugin**: Access to database, fork_db, witness schedule, block generation +The validator plugin requires: +- **chain::plugin**: Access to database, fork_db, validator schedule, block generation - **p2p::p2p_plugin**: Block broadcast, sync status, peer connections, snapshot pause detection - **snapshot::snapshot_plugin**: Query snapshot creation status via `is_snapshot_in_progress()` @@ -69,9 +69,9 @@ The witness plugin requires: ### Production Control - `_production_skip_flags` (uint32_t): Flags passed to `generate_block()` (e.g., `skip_undo_history_check`) -- `_required_witness_participation` (uint32_t): Participation threshold from config +- `_required_validator_participation` (uint32_t): Participation threshold from config - `_private_keys` (map): Loaded private keys for signing -- `_witnesses` (set): Configured witness names (includes `CHAIN_EMERGENCY_WITNESS_ACCOUNT` if emergency key configured) +- `_validators` (set): Configured validator names (includes `CHAIN_EMERGENCY_VALIDATOR_ACCOUNT` if emergency key configured) - `_last_lag_slot_time` (fc::time_point_sec): Scheduled time of the most recent `lag` condition. Zero when no lag is active. Used by `schedule_production_loop()` to skip ahead past the missed slot and avoid rechecking it every 250ms. ### Fork Detection & Recovery @@ -83,9 +83,9 @@ The witness plugin requires: ### Stall Detection - `_slot_zero_streak` (uint32_t): Consecutive `not_time_yet` returns (NTP/clock issues) - `_slot_zero_streak_start` (fc::time_point): When slot=0 streak started -- `_not_my_turn_streak` (uint32_t): Consecutive slots assigned to other witnesses +- `_not_my_turn_streak` (uint32_t): Consecutive slots assigned to other validators - `_not_my_turn_streak_start` (fc::time_point): When not_my_turn streak started -- `_last_scheduled_witness` (string): Last witness that got the slot +- `_last_scheduled_validator` (string): Last validator that got the slot ### Watchdog & Diagnostics - `_ever_produced` (bool): Whether we've ever produced a block @@ -107,7 +107,7 @@ The witness plugin requires: **Actions:** 1. Create `impl` instance -2. Load witness names from `--witness` option into `_witnesses` set +2. Load validator names from `--validator` option into `_witnesses` set 3. Load `--enable-stale-production` flag → sets `skip_undo_history_check` in `_production_skip_flags` if true 4. Load `--required-participation` → `_required_witness_participation` 5. Parse `--private-key` WIF strings → `_private_keys` map @@ -127,7 +127,7 @@ The witness plugin requires: **Actions:** 1. Start NTP time client: `graphene::time::now()` 2. **Force NTP sync**: `graphene::time::update_ntp_time()` to minimize startup drift -3. Log witness configuration (count, names) +3. Log validator configuration (count, names) 4. If `_witnesses` is not empty: - Call `p2p().set_block_production(true)` to enable P2P block production mode - Connect to `database::applied_block` signal → `on_block_applied()` handler @@ -135,7 +135,7 @@ The witness plugin requires: - If `skip_undo_history_check` set in `_production_skip_flags` (from `--enable-stale-production`): - If `head_block_num() == 0`: Print new chain banner - **Start production loop**: `schedule_production_loop()` -5. If no witnesses configured: Log error message +5. If no validators configured: Log error message --- @@ -144,7 +144,7 @@ The witness plugin requires: **Actions:** 1. Shutdown NTP: `graphene::time::shutdown_ntp_time()` -2. Cancel production timer if witnesses configured +2. Cancel production timer if validators configured --- @@ -229,7 +229,7 @@ if (!_minority_fork_recovering && !_witnesses.empty()) { ``` - Emergency master: 60s threshold -- Regular witness: 180s threshold +- Regular validator: 180s threshold - Logs every 30s once triggered - **WATCHDOG-RECOVERY**: If conditions met (head advancing < 30s, not syncing, has peers, has active keys): - Clear `_minority_fork_recovering` @@ -280,7 +280,7 @@ if (db._dlt_mode && chain().is_syncing()) { - If emergency consensus active AND we have emergency key → production proceeds - **Why:** Master is sole producer; waiting for sync would deadlock (no blocks arrive to clear syncing flag) -**Outside DLT mode:** This check is NOT applied. Normal witnesses must produce on canonical head even while network catching up. +**Outside DLT mode:** This check is NOT applied. Normal validators must produce on canonical head even while network catching up. --- @@ -307,7 +307,7 @@ if (p2p().is_catching_up_after_pause()) { 1. **Snapshot in progress**: snapshot plugin holds DB read lock; producing would cause write-lock starvation 2. **P2P catchup after pause**: snapshot finished, but queued blocks haven't drained yet; producing on stale head → fork -**Applies to:** ALL witness types (emergency and normal) +**Applies to:** ALL validator types (emergency and normal) **Flag cleared when:** - `snapshot_in_progress`: Cleared by snapshot plugin on completion @@ -402,17 +402,17 @@ if (db.get_slot_time(1) < now) { if(last_block_post_validation_time < now_fine) { last_block_post_validation_time = now; // Build scheduled_witnesses_set from witness_schedule_object - // For each witness in _witnesses: + // For each validator in _witnesses: // - Skip if not in current schedule // - Get block_post_validations from database - // - Sign with witness private key + // - Sign with validator private key // - Broadcast via p2p().broadcast_block_post_validation() } ``` -**What it does:** Signs and broadcasts post-validation messages for scheduled witnesses to contribute to LIB advancement. +**What it does:** Signs and broadcasts post-validation messages for scheduled validators to contribute to LIB advancement. -**Optimization:** Skips witnesses not in current schedule (can't contribute to LIB). +**Optimization:** Skips validators not in current schedule (can't contribute to LIB). --- @@ -422,7 +422,7 @@ if(last_block_post_validation_time < now_fine) { if (!dgp.emergency_consensus_active) { auto fork_head = db.get_fork_db().head(); // Walk back CHAIN_MAX_WITNESSES (21) blocks in fork_db - // If ALL from our witnesses → minority fork + // If ALL from our validators → minority fork if (all_ours && blocks_checked >= CHAIN_MAX_WITNESSES) { if (_production_skip_flags & skip_undo_history_check) { // enable-stale-production: continue @@ -448,7 +448,7 @@ if (dgp.emergency_consensus_active && db._dlt_mode) { // Check if we are emergency master bool we_are_master = false; if (_witnesses.find(CHAIN_EMERGENCY_WITNESS_ACCOUNT) != _witnesses.end()) { - // Check if committee is in current witness schedule + // Check if committee is in current validator schedule const witness_schedule_object &wso = db.get_witness_schedule_object(); for (int i = 0; i < wso.num_scheduled_witnesses; i += CHAIN_BLOCK_WITNESS_REPEAT) { if (wso.current_shuffled_witnesses[i] == CHAIN_EMERGENCY_WITNESS_ACCOUNT) { @@ -460,7 +460,7 @@ if (dgp.emergency_consensus_active && db._dlt_mode) { if (!we_are_master) { // Slave DLT node: run fork_db isolation scan - // If last 21 blocks all from our witnesses → isolated from master + // If last 21 blocks all from our validators → isolated from master p2p().resync_from_lib(true /*force_emergency*/); _minority_fork_recovering = true; return minority_fork; @@ -481,7 +481,7 @@ if (dgp.emergency_consensus_active && db._dlt_mode) { auto op_guard = db.make_operation_guard(); ``` -**Why:** Guards lockless reads into shared memory against concurrent resize. Prevents pointer invalidation while reading witness schedule, slot time, etc. +**Why:** Guards lockless reads into shared memory against concurrent resize. Prevents pointer invalidation while reading validator schedule, slot time, etc. **Stall detection:** If guard blocks > 100ms, logs warning. If crosses slot boundary and we lost our slot → critical error. @@ -508,7 +508,7 @@ if (_witnesses.find(scheduled_witness) == _witnesses.end()) { --- -##### Step 10: Witness Validation (Line ~1563) +##### Step 10: validator Validation (Line ~1563) ```cpp const auto &witness_by_name = db.get_index().indices().get(); @@ -522,7 +522,7 @@ if (scheduled_time <= db.head_block_time()) { return not_time_yet; // Slot filled during/after snapshot pause } -// Check if witness disabled (zero key) +// Check if validator disabled (zero key) if (scheduled_key == public_key_type()) { // Log warning (every 60s for regular, 3s for emergency) return not_my_turn; @@ -535,7 +535,7 @@ if (private_key_itr == _private_keys.end()) { } ``` -**Slot already filled check:** Critical for snapshot pause scenario. Another witness may have filled the slot during pause. +**Slot already filled check:** Critical for snapshot pause scenario. Another validator may have filled the slot during pause. --- @@ -577,7 +577,7 @@ auto existing_blocks = db.get_fork_db().fetch_block_by_number(db.head_block_num( if (existing_blocks.size() > 0) { // Determine if competing block exists // Emergency mode: ANY block at this height is competing - // Normal mode: only different witness + different parent + // Normal mode: only different validator + different parent if (has_competing_block) { fork_collision_defer_count_++; @@ -614,7 +614,7 @@ if (existing_blocks.size() > 0) { 1. **Vote-weighted comparison** (HF12+): Compare fork branches by accumulated vote weight 2. **Stuck-head timeout** (all versions): After 21 deferrals (63s), assume competing block is on dead fork -**Why 21 blocks:** One full witness round. If head hasn't advanced after 21 slots, longer chain had all scheduled witnesses produce → canonical chain confirmed. +**Why 21 blocks:** One full validator round. If head hasn't advanced after 21 slots, longer chain had all scheduled validators produce → canonical chain confirmed. --- @@ -683,15 +683,15 @@ if (database()._dlt_mode && !_witnesses.empty() uint64_t slot_idx = dgp.current_aslot % nsw; const std::string& expected_witness = wso.current_shuffled_witnesses[slot_idx]; - // True hijack: expected slot is ours AND actual producer is not one of our witnesses + // True hijack: expected slot is ours AND actual producer is not one of our validators if (_witnesses.count(expected_witness) > 0 - && _witnesses.count(block.witness) == 0) { + && _witnesses.count(block.validator) == 0) { _slot_hijack_count++; // Committee / emergency master filled our slot // Log first 3 hijacks, then once per minute - } else if (_witnesses.count(block.witness) > 0) { - // ANY of our witnesses produced → reset (false-positive guard) + } else if (_witnesses.count(block.validator) > 0) { + // ANY of our validators produced → reset (false-positive guard) if (_slot_hijack_count > 0) { - ilog("Hijack counter reset: our witness '${w}' produced...", ...); + ilog("Hijack counter reset: our validator '${w}' produced...", ...); } _slot_hijack_count = 0; } @@ -699,9 +699,9 @@ if (database()._dlt_mode && !_witnesses.empty() } ``` -**What it detects:** In DLT emergency mode, the emergency master may blank our witness's signing_key and fill our scheduled slots with committee blocks. +**What it detects:** In DLT emergency mode, the emergency master may blank our validator's signing_key and fill our scheduled slots with committee blocks. -**Important:** The reset condition checks `_witnesses.count(block.witness) > 0` — i.e., ANY of our configured witnesses produced the block, not just the slot-assigned one. Without this, a legitimate block from a different one of our witnesses would be mis-counted as a hijack. +**Important:** The reset condition checks `_witnesses.count(block.validator) > 0` — i.e., ANY of our configured validators produced the block, not just the slot-assigned one. Without this, a legitimate block from a different one of our validators would be mis-counted as a hijack. --- @@ -711,7 +711,7 @@ if (database()._dlt_mode && !_witnesses.empty() if (block_num > prev_num + 1) { uint32_t missed_count = block_num - prev_num - 1; - // Calculate which witnesses were scheduled for missed slots + // Calculate which validators were scheduled for missed slots for (uint32_t i = 0; i < missed_count && i < 100; ++i) { uint64_t abs_slot = cur_aslot - missed_count + i; const std::string &wname = wso.current_shuffled_witnesses[abs_slot % num_witnesses]; @@ -724,14 +724,14 @@ if (block_num > prev_num + 1) { // Dump full diagnostic state: // - Production flags, NTP offset, sync status // - On-chain signing key status (blanked?) - // - Next slot time, scheduled witness + // - Next slot time, scheduled validator // - Streak counters - elog("MISSED-SLOT-OUR-WITNESS: ..."); + elog("MISSED-SLOT-OUR-validator: ..."); } } ``` -**Why check missed slots:** When incoming blocks reveal gaps, determines if our witness was scheduled for any missed slots and logs full diagnostic state for troubleshooting. +**Why check missed slots:** When incoming blocks reveal gaps, determines if our validator was scheduled for any missed slots and logs full diagnostic state for troubleshooting. --- @@ -739,19 +739,19 @@ if (block_num > prev_num + 1) { ### `is_witness_scheduled_soon()` -**Returns:** `true` if locally-controlled witness is scheduled to produce in next 4 slots (~12 seconds) +**Returns:** `true` if locally-controlled validator is scheduled to produce in next 4 slots (~12 seconds) **Implementation:** 1. Check 4 upcoming slots (covers snapshot creation time ~10s + safety margin) 2. For each slot: - - Get scheduled witness name + - Get scheduled validator name - Check if in `_witnesses` set - - Look up witness object in database + - Look up validator object in database - Check if signing key is non-zero - Check if we have corresponding private key 3. Return `true` if any match found -**Used by:** Snapshot plugin to defer snapshot creation when witness about to produce (avoids fork on stale head) +**Used by:** Snapshot plugin to defer snapshot creation when validator about to produce (avoids fork on stale head) --- @@ -761,7 +761,7 @@ if (block_num > prev_num + 1) { **Conditions:** 1. Holds `emergency-private-key` (`CHAIN_EMERGENCY_WITNESS_ACCOUNT` in `_witnesses`) -2. Committee account is in current witness schedule +2. Committee account is in current validator schedule **Why both conditions:** Multiple nodes can have emergency key, but only the one where committee is scheduled should produce solo. Others are followers that must sync. @@ -784,7 +784,7 @@ if (block_num > prev_num + 1) { **Returns:** Compact diagnostic string with key production-state flags -**Format:** `witness[skip_flags=0x0 catching_up=0 head=#123 last_prod=45s_ago minority_rcv=0 slot_hijacks=2]` +**Format:** `validator[skip_flags=0x0 catching_up=0 head=#123 last_prod=45s_ago minority_rcv=0 slot_hijacks=2]` **Used by:** P2P layer when FORWARD stagnation fires with no peer ahead, so stagnation log shows why master isn't filling gap. @@ -814,8 +814,8 @@ if (block_num > prev_num + 1) { - HF12+: Vote-weighted comparison (Level 1) + timeout (Level 2) 5. **Block post validation**: - - Pre-HF12: LIB advancement via 2/3 witness signatures - - HF12+: LIB advancement via witness participation rate, emergency mode disables post-validation chain + - Pre-HF12: LIB advancement via 2/3 validator signatures + - HF12+: LIB advancement via validator participation rate, emergency mode disables post-validation chain --- @@ -828,10 +828,10 @@ if (block_num > prev_num + 1) { - `db.get_slot_at_time(now)` — determine if slot available - `db.get_scheduled_witness(slot)` — who should produce - `db.get_slot_time(slot)` — scheduled slot time -- `db.get_witness_schedule_object()` — shuffled witness list, num_scheduled_witnesses +- `db.get_witness_schedule_object()` — shuffled validator list, num_scheduled_witnesses - `db.head_block_num()`, `db.head_block_time()` — current chain state - `db.get_fork_db().head()`, `db.get_fork_db().fetch_block_by_number()` — fork detection -- `db.get_index().indices().get().find()` — witness signing key status +- `db.get_index().indices().get().find()` — validator signing key status - `db.witness_participation_rate()` — network health check - `db.has_hardfork(CHAIN_HARDFORK_12)` — feature gate @@ -847,12 +847,12 @@ if (block_num > prev_num + 1) { ### Does NOT cache results **All database reads are fresh on every call:** -- No caching of witness schedule +- No caching of validator schedule - No caching of DGP state - No caching of fork_db state - No caching of signing key status -**Why:** State changes every block (emergency mode can activate/deactivate, witness schedule changes, signing keys can be blanked). Caching would create race conditions and stale decisions. +**Why:** State changes every block (emergency mode can activate/deactivate, validator schedule changes, signing keys can be blanked). Caching would create race conditions and stale decisions. **Exception:** `_witnesses` set and `_private_keys` map are loaded once during `plugin_initialize()` and never refreshed (operator must restart to change configuration). @@ -923,7 +923,7 @@ bool snapshot_plugin::is_snapshot_in_progress() const { **Called by:** Snapshot plugin in `on_applied_block()` before scheduling snapshot -**Why:** Defer snapshot if witness about to produce (~12s window). Producing during snapshot → read-lock contention, producing after on stale head → fork. +**Why:** Defer snapshot if validator about to produce (~12s window). Producing during snapshot → read-lock contention, producing after on stale head → fork. --- @@ -939,12 +939,12 @@ bool snapshot_plugin::is_snapshot_in_progress() const { [maybe_produce_block()] ├─→ not_synced (DLT sync, snapshot pause) ├─→ not_time_yet (slot=0, NTP drift, slot already filled) - ├─→ not_my_turn (witness disabled, wrong witness scheduled) + ├─→ not_my_turn (validator disabled, wrong validator scheduled) ├─→ no_private_key (missing key) ├─→ low_participation (< 33%, no override) ├─→ lag (> 500ms past slot time) ├─→ fork_collision (competing block, defer) - ├─→ minority_fork (21 blocks from our witnesses, rollback to LIB) + ├─→ minority_fork (21 blocks from our validators, rollback to LIB) └─→ produced (success, broadcast block) ``` @@ -953,7 +953,7 @@ bool snapshot_plugin::is_snapshot_in_progress() const { ## Critical Safety Mechanisms ### 1. Minority Fork Detection -- **Trigger:** Last 21 blocks in fork_db all from our witnesses +- **Trigger:** Last 21 blocks in fork_db all from our validators - **Action:** Rollback to LIB, resync from P2P network - **Override:** `enable-stale-production=true` - **Emergency mode:** Skipped (committee blocks would always trigger), DLT-specific check for slaves @@ -964,13 +964,13 @@ bool snapshot_plugin::is_snapshot_in_progress() const { - **Emergency mode:** ANY competing block triggers defer ### 3. Network Partition Guard -- **Trigger:** Witness participation < 33% +- **Trigger:** validator participation < 33% - **Action:** Stop production (return `low_participation`) - **Override:** `enable-stale-production=true` for bootstrap/recovery ### 4. Slot Already Filled Guard - **Trigger:** `scheduled_time <= head_block_time()` -- **Why:** Another witness filled slot during/after snapshot pause +- **Why:** Another validator filled slot during/after snapshot pause - **Action:** Skip production (return `not_time_yet`) ### 5. Production Watchdog @@ -1001,7 +1001,7 @@ bool snapshot_plugin::is_snapshot_in_progress() const { 4. **Emergency master must always produce:** Sole producer, waiting = deadlock 5. **Slave nodes must sync first:** Producing on stale head = minority fork 6. **Participation < 33% = stop production:** Network partition guard (unless override) -7. **21 consecutive blocks from our witnesses = minority fork:** Rollback to LIB +7. **21 consecutive blocks from our validators = minority fork:** Rollback to LIB 8. **All database reads are fresh:** No caching, state changes every block --- @@ -1013,7 +1013,7 @@ bool snapshot_plugin::is_snapshot_in_progress() const { **Check logs for:** - `not_synced`: DLT sync active or snapshot pause → wait for sync/pause to complete - `not_time_yet`: NTP drift or slot=0 → check NTP offset in logs -- `not_my_turn`: Wrong witness scheduled or key blanked → check `keys=[...]` in watchdog log +- `not_my_turn`: Wrong validator scheduled or key blanked → check `keys=[...]` in watchdog log - `no_private_key`: Missing private key → check config - `low_participation`: Network participation < 33% → set `enable-stale-production=true` - `fork_collision`: Competing block → wait for resolution (21 blocks max) @@ -1042,7 +1042,7 @@ bool snapshot_plugin::is_snapshot_in_progress() const { **Check:** 1. Is emergency key configured? → `--emergency-private-key` -2. Is committee in witness schedule? → Check `witness_schedule_object` +2. Is committee in validator schedule? → Check `witness_schedule_object` 3. Is NTP synchronized? → Check NTP offset 4. Is sync flag stuck? → Watchdog should auto-clear @@ -1054,12 +1054,12 @@ bool snapshot_plugin::is_snapshot_in_progress() const { **Symptoms:** `SLOT-HIJACK` logs, `slot_hijacks=N` in watchdog diagnostics -**Cause:** Emergency master blanked our witness key and producing committee blocks in our slots +**Cause:** Emergency master blanked our validator key and producing committee blocks in our slots -**Normal behavior:** In DLT emergency mode, master may blank offline witnesses to maintain chain progress +**Normal behavior:** In DLT emergency mode, master may blank offline validators to maintain chain progress **Action:** -1. Check witness signing key status: `keys=[witness:key=BLANK]` +1. Check validator signing key status: `keys=[validator:key=BLANK]` 2. Send `update_witness` transaction to restore key 3. Wait for emergency mode to end (LIB advances) @@ -1070,7 +1070,7 @@ bool snapshot_plugin::is_snapshot_in_progress() const { - **Chain plugin:** `plugins/chain/plugin.cpp`, `plugins/chain/include/graphene/plugins/chain/plugin.hpp` - **P2P plugin:** `plugins/p2p/p2p_plugin.cpp`, `plugins/p2p/include/graphene/plugins/p2p/p2p_plugin.hpp` - **Snapshot plugin:** `plugins/snapshot/plugin.cpp`, `plugins/snapshot/include/graphene/plugins/snapshot/plugin.hpp` -- **Witness guard plugin:** `plugins/witness_guard/witness_guard.cpp` +- **validator guard plugin:** `plugins/witness_guard/witness_guard.cpp` - **DLT P2P node:** `libraries/network/dlt_p2p_node.cpp`, `libraries/network/include/graphene/network/dlt_p2p_node.hpp` - **Database:** `libraries/chain/database.cpp` (emergency consensus, hardfork logic, block generation) - **NTP time:** `libraries/time/time.cpp` (time synchronization) diff --git a/.qoder/docs/virtual-operations.md b/.qoder/docs/virtual-operations.md index c2daf8a14e..c62a43e6e6 100644 --- a/.qoder/docs/virtual-operations.md +++ b/.qoder/docs/virtual-operations.md @@ -1,4 +1,4 @@ -# VIZ Blockchain — Virtual Operations +# VIZ Blockchain — Virtual Operations Virtual operations are generated by the blockchain itself (not broadcast by users). They appear in operation history and can be tracked via account history or operation history APIs. They **cannot** be included in user transactions. @@ -116,13 +116,13 @@ Fired each time a vesting withdrawal interval completes (weekly). ## `shutdown_witness_operation` **Type ID:** `30` -**Trigger:** Witness removed due to vote weight falling below threshold +**Trigger:** validator removed due to vote weight falling below threshold ### Fields | Field | Type | Description | |---|---|---| -| `owner` | `account_name_type` | Witness that was shut down | +| `owner` | `account_name_type` | validator that was shut down | --- @@ -265,20 +265,20 @@ Fired for each beneficiary when content is paid out. **Type ID:** `42` **Trigger:** Block production reward -Fired when a witness receives their block production reward. +Fired when a validator receives their block production reward. ### Fields | Field | Type | Description | |---|---|---| -| `witness` | `account_name_type` | Witness account | +| `validator` | `account_name_type` | validator account | | `shares` | `asset` (SHARES) | Reward in SHARES | ### JSON Example ```json [42, { - "witness": "alice", + "validator": "alice", "shares": "1.234567 SHARES" }] ``` @@ -498,7 +498,7 @@ client.database.getAccountHistory('alice', -1, 100).then(history => { // handle award break; case 'witness_reward': - // handle witness reward + // handle validator reward break; } } diff --git a/.qoder/docs/witness-to-validator-migration-reference.md b/.qoder/docs/witness-to-validator-migration-reference.md index a99ac49ffc..f98db5e4eb 100644 --- a/.qoder/docs/witness-to-validator-migration-reference.md +++ b/.qoder/docs/witness-to-validator-migration-reference.md @@ -1,8 +1,8 @@ -# VIZ Blockchain — Witness → Validator Migration Reference +# VIZ Blockchain — validator → Validator Migration Reference ## Quick Summary -The VIZ blockchain is renaming "witness" terminology to "validator" across the entire stack. This document is a reference for JS/PHP library developers to support both old and new names during migration. +The VIZ blockchain is renaming "validator" terminology to "validator" across the entire stack. This document is a reference for JS/PHP library developers to support both old and new names during migration. **Key principle: binary wire format uses integer type IDs, not string names. Submitting transactions by integer ID never breaks. Only JSON string names change.** @@ -26,11 +26,11 @@ This distinction is the most important rule for library developers. | What | Reason | |------|--------| -| Block header fields | **Done.** Node now returns `validator` / `validator_signature`. Libraries that relay raw block objects with no field-specific code need no changes. Code explicitly accessing `.witness` / `.witness_signature` on a block header must be updated to `.validator` / `.validator_signature`. | +| Block header fields | **Done.** Node now returns `validator` / `validator_signature`. Libraries that relay raw block objects with no field-specific code need no changes. Code explicitly accessing `.validator` / `.witness_signature` on a block header must be updated to `.validator` / `.validator_signature`. | | Raw API response objects (beyond type definitions) | JSON deserialization is dynamic — field access works regardless of name if library doesn't validate field names | | Historical transaction data from node | Same — the node returns it, library relays it | -**Practical rule:** if your library has a hardcoded string `"witness"` as a field name when *building* a JSON object to *send* to a node — that string must be updated. If the string `"witness"` only appears in a *comment*, a *display label*, or a *type definition that only affects IDE autocomplete* — it can wait. +**Practical rule:** if your library has a hardcoded string `"validator"` as a field name when *building* a JSON object to *send* to a node — that string must be updated. If the string `"validator"` only appears in a *comment*, a *display label*, or a *type definition that only affects IDE autocomplete* — it can wait. --- @@ -68,12 +68,12 @@ These are the operations users submit in transactions. The **integer type ID nev | Type ID | Current JSON Name (old) | New JSON Name | Virtual? | Fields (unchanged) | |---------|------------------------|---------------|----------|---------------------| | `6` | `witness_update` | `validator_update` | no | `owner`, `url`, `block_signing_key` | -| `7` | `account_witness_vote` | `account_validator_vote` | no | `account`, **`witness` → `validator`**, `approve` | +| `7` | `account_witness_vote` | `account_validator_vote` | no | `account`, **`validator` → `validator`**, `approve` | | `8` | `account_witness_proxy` | `account_validator_proxy` | no | `account`, `proxy` | | `30` | `shutdown_witness` | `shutdown_validator` | **yes** | `owner` | -| `42` | `witness_reward` | `validator_reward` | **yes** | **`witness` → `validator`**, `shares` | +| `42` | `witness_reward` | `validator_reward` | **yes** | **`validator` → `validator`**, `shares` | -> **Field renames inside operations:** In type 7 (`account_validator_vote`) the field `witness` (the target account name) is renamed to `validator`. In type 42 (`validator_reward`) the field `witness` is renamed to `validator`. The node accepts both old and new field names in incoming JSON, but responses use new names only. +> **Field renames inside operations:** In type 7 (`account_validator_vote`) the field `validator` (the target account name) is renamed to `validator`. In type 42 (`validator_reward`) the field `validator` is renamed to `validator`. The node accepts both old and new field names in incoming JSON, but responses use new names only. ### What JS/PHP Developers Must Handle @@ -99,7 +99,7 @@ const op = ['validator_update', { // new name ```js // Old (still accepted by node, but deprecated): -const op = [7, { account: 'alice', witness: 'bob', approve: true }]; +const op = [7, { account: 'alice', validator: 'bob', approve: true }]; // New (correct): const op = [7, { account: 'alice', validator: 'bob', approve: true }]; @@ -286,8 +286,8 @@ function getShuffledValidators(schedule) { | Field (old) | Field (new) | Operation | Note | |-------------|-------------|-----------|------| -| `witness` | `validator` | `account_validator_vote` (type 7) | Target account name | -| `witness` | `validator` | `validator_reward` (type 42) | Virtual op — library receives, not constructs | +| `validator` | `validator` | `account_validator_vote` (type 7) | Target account name | +| `validator` | `validator` | `validator_reward` (type 42) | Virtual op — library receives, not constructs | The node accepts both old and new field names in incoming JSON (backward compat). Responses always use new names. @@ -307,7 +307,7 @@ The node accepts both old and new field names in incoming JSON (backward compat) ## 5. Chain Properties Field Renames -**This section is critical for library developers.** The `chain_properties_update_operation` and `versioned_chain_properties_update_operation` carry governance parameters with `witness` in their names. These field names change in JSON. Libraries that construct these operations must update field names. +**This section is critical for library developers.** The `chain_properties_update_operation` and `versioned_chain_properties_update_operation` carry governance parameters with `validator` in their names. These field names change in JSON. Libraries that construct these operations must update field names. **Binary format is safe** — field order is preserved in binary serialization, names are not written. Only JSON field names change. @@ -358,14 +358,14 @@ Not directly relevant to JS/PHP libraries, but included for completeness: | Current Config Key (old) | New Config Key | |--------------------------|----------------| -| `plugin = witness` | `plugin = validator` | +| `plugin = validator` | `plugin = validator` | | `plugin = witness_api` | `plugin = validator_api` | | `plugin = witness_guard` | `plugin = validator_guard` | -| `--witness = "name"` | `--validator = "name"` | -| `witness-guard-enabled` | `validator-guard-enabled` | -| `witness-guard-disable` | `validator-guard-disable` | -| `witness-guard-interval` | `validator-guard-interval` | -| `witness-guard-witness` | `validator-guard-validator` | +| `--validator = "name"` | `--validator = "name"` | +| `validator-guard-enabled` | `validator-guard-enabled` | +| `validator-guard-disable` | `validator-guard-disable` | +| `validator-guard-interval` | `validator-guard-interval` | +| `validator-guard-validator` | `validator-guard-validator` | --- @@ -406,7 +406,7 @@ Not directly relevant to JS/PHP libraries, but included for completeness: - Accept both `account_witness_vote` and `account_validator_vote` as name for type ID 7 - Same for types 8, 30, 42 2. Update field names in operation builders: - - Type 7: accept both `witness` and `validator` for the target account field; send `validator` + - Type 7: accept both `validator` and `validator` for the target account field; send `validator` - Chain properties: add new field names; keep old for backward compat with old nodes 3. Add dual-name support for API methods: - Try new method name first, fall back to old name @@ -434,10 +434,10 @@ Not directly relevant to JS/PHP libraries, but included for completeness: | Type ID | Old JSON Name | New JSON Name | Field changes | |---------|--------------|---------------|---------------| | 6 | `witness_update` | `validator_update` | none | -| 7 | `account_witness_vote` | `account_validator_vote` | `witness` → `validator` | +| 7 | `account_witness_vote` | `account_validator_vote` | `validator` → `validator` | | 8 | `account_witness_proxy` | `account_validator_proxy` | none | | 30 | `shutdown_witness` | `shutdown_validator` | none | -| 42 | `witness_reward` | `validator_reward` | `witness` → `validator` | +| 42 | `witness_reward` | `validator_reward` | `validator` → `validator` | ### API Methods @@ -463,7 +463,7 @@ Not directly relevant to JS/PHP libraries, but included for completeness: | Old Field | New Field | |-----------|-----------| -| `witness` | `validator` | +| `validator` | `validator` | | `witness_signature` | `validator_signature` | ### Dynamic Global Property Fields diff --git a/.qoder/docs/witness-to-validator-rename.md b/.qoder/docs/witness-to-validator-rename.md index fcde264cb2..100a1aad34 100644 --- a/.qoder/docs/witness-to-validator-rename.md +++ b/.qoder/docs/witness-to-validator-rename.md @@ -1,14 +1,14 @@ -# Naming Analysis: VIZ "Witness" → "Validator" Rename Proposal +# Naming Analysis: VIZ "validator" → "Validator" Rename Proposal -## 1. What VIZ Witnesses Actually Do +## 1. What VIZ validators Actually Do -Looking at the codebase, VIZ witnesses perform two distinct roles: +Looking at the codebase, VIZ validators perform two distinct roles: **Block production** — scheduled in rotation, they run `block_production_loop()`, sign blocks with `block_signing_key`, and broadcast them via P2P. -**Block post validation (BPV)** — they sign `block_post_validation_object`s to confirm blocks produced by other witnesses, which drives LIB (Last Irreversible Block) advancement. +**Block post validation (BPV)** — they sign `block_post_validation_object`s to confirm blocks produced by other validators, which drives LIB (Last Irreversible Block) advancement. -So witnesses are both producers and validators — which is exactly why "witness" is semantically weak. It sounds passive ("I saw this happen") and says nothing about their active role in consensus. +So validators are both producers and validators — which is exactly why "validator" is semantically weak. It sounds passive ("I saw this happen") and says nothing about their active role in consensus. --- @@ -23,7 +23,7 @@ XRPL uses: | **Validation vote** | A cryptographic fingerprint published post-consensus round | | **Participant** | Generic term for any consensus node | -XRPL does not split "producer" from "validator" — validators do both. This matches exactly how VIZ witnesses work. +XRPL does not split "producer" from "validator" — validators do both. This matches exactly how VIZ validators work. The same pattern holds across other major PoS ecosystems: - **Ethereum PoS** — validators attest and propose blocks @@ -41,7 +41,7 @@ The same pattern holds across other major PoS ecosystems: | Item | Notes | |------|-------| | Protocol operations (types 6, 7, 8, 30, 42) | Renamed; old-name alias table in `operation_util_impl.cpp` | -| Operation field names (`witness` → `validator` in types 7 and 42) | Node accepts both old and new on input | +| Operation field names (`validator` → `validator` in types 7 and 42) | Node accepts both old and new on input | | Chain properties fields (`inflation_validator_percent`, etc.) | Old names accepted in snapshot import | | Chain objects (`validator_object`, `validator_schedule_object`) | C++ types renamed; files not renamed yet | | Schedule fields (`current_shuffled_validators`, `num_scheduled_validators`) | Done | @@ -65,7 +65,7 @@ Nothing remaining. All renames complete. - `witness_vote_object` — internal vote-tracking object; not exposed by name in protocol - `witness_penalty_expire_object` — internal object; not exposed in protocol -- `witness_penalty_expire_object::witness` field — internal back-reference, not a block header field +- `witness_penalty_expire_object::validator` field — internal back-reference, not a block header field - `witness_vote_index`, `by_account_witness` — chainbase index tags; tied to `witness_vote_object`, not renamed --- @@ -154,7 +154,7 @@ These are the on-chain operations. The C++ struct rename is a code-only change ( | `shutdown_witness_operation` | `shutdown_validator_operation` | `shutdown_witness` | `shutdown_validator` | 50 | yes | | `witness_reward_operation` | `validator_reward_operation` | `witness_reward` | `validator_reward` | 66 | yes | -> `chain_properties_update_operation` and `versioned_chain_properties_update_operation` — these are submitted by witnesses but describe chain property voting, not the witness role itself. **Keep names as-is.** +> `chain_properties_update_operation` and `versioned_chain_properties_update_operation` — these are submitted by validators but describe chain property voting, not the validator role itself. **Keep names as-is.** ### 5.2 Core Objects and Types @@ -171,7 +171,7 @@ These are the on-chain operations. The C++ struct rename is a code-only change ( | Current Name | New Name | File | |-------------|----------|------| -| namespace `block_production_condition` | `block_validation_condition` | `plugins/witness/include/graphene/plugins/witness/witness.hpp` | +| namespace `block_production_condition` | `block_validation_condition` | `plugins/validator/include/graphene/plugins/validator/validator.hpp` | | `block_production_condition_enum` | `block_validation_condition_enum` | same | | `exception_producing_block` | `exception_validating_block` | same | @@ -181,22 +181,22 @@ All other enum values (`produced`, `not_synced`, `not_my_turn`, `not_time_yet`, | Current Name | New Name | File | |-------------|----------|------| -| `block_production_loop()` | `block_validation_loop()` | `plugins/witness/witness.cpp` | -| `maybe_produce_block()` | `maybe_validate_block()` | `plugins/witness/witness.cpp` | -| `is_witness_scheduled_soon()` | `is_validator_scheduled_soon()` | `plugins/witness/witness.hpp` | +| `block_production_loop()` | `block_validation_loop()` | `plugins/validator/validator.cpp` | +| `maybe_produce_block()` | `maybe_validate_block()` | `plugins/validator/validator.cpp` | +| `is_witness_scheduled_soon()` | `is_validator_scheduled_soon()` | `plugins/validator/validator.hpp` | ### 5.5 Plugins (Directories and CMake Targets) | Current Name | New Name | |-------------|----------| | `witness_plugin` | `validator_plugin` | -| `plugins/witness/` | `plugins/validator/` | +| `plugins/validator/` | `plugins/validator/` | | `witness_api_plugin` | `validator_api_plugin` | | `plugins/witness_api/` | `plugins/validator_api/` | | `witness_guard_plugin` | `validator_guard_plugin` | | `plugins/witness_guard/` | `plugins/validator_guard/` | -### 5.6 Witness API Endpoints (JSON-RPC) +### 5.6 validator API Endpoints (JSON-RPC) | Current Name | New Name | |-------------|----------| @@ -228,16 +228,16 @@ File: `libraries/wallet/wallet.cpp` and `libraries/wallet/include/graphene/walle | Current Key | New Key | File | |------------|---------|------| -| `plugin = witness` | `plugin = validator` | `config_witness.ini` | +| `plugin = validator` | `plugin = validator` | `config_witness.ini` | | `plugin = witness_guard` | `plugin = validator_guard` | `config_witness.ini` | | `plugin = witness_api` | `plugin = validator_api` | `config_witness.ini` | -| `--witness = "name"` | `--validator = "name"` | `config_witness.ini` | -| `witness-guard-enabled` | `validator-guard-enabled` | `config_witness.ini` | -| `witness-guard-disable` | `validator-guard-disable` | `config_witness.ini` | -| `witness-guard-interval` | `validator-guard-interval` | `config_witness.ini` | -| `witness-guard-witness` | `validator-guard-validator` | `config_witness.ini` | +| `--validator = "name"` | `--validator = "name"` | `config_witness.ini` | +| `validator-guard-enabled` | `validator-guard-enabled` | `config_witness.ini` | +| `validator-guard-disable` | `validator-guard-disable` | `config_witness.ini` | +| `validator-guard-interval` | `validator-guard-interval` | `config_witness.ini` | +| `validator-guard-validator` | `validator-guard-validator` | `config_witness.ini` | -Config keys fallback: on startup, if an old key is detected emit a warning — `"Config key 'witness' is deprecated, use 'validator'"` — and continue reading the value. +Config keys fallback: on startup, if an old key is detected emit a warning — `"Config key 'validator' is deprecated, use 'validator'"` — and continue reading the value. --- @@ -283,7 +283,7 @@ Even with server-side fallback, clients will receive **responses** with new name |------------|-------------| | `block_signing_key` / `signing_key` | Accurately describes the cryptographic key used to sign blocks and post-validations | | `delegate_vesting_shares_operation` | "Delegate" is already taken in VIZ for vesting share delegation — **do not use "delegate" as the consensus-role name** | -| `chain_properties_update_operation` | Describes chain governance, not the witness role | +| `chain_properties_update_operation` | Describes chain governance, not the validator role | | `versioned_chain_properties_update_operation` | Same | | Enum values `top`, `support`, `none` | Scheduling tier names, not role names | @@ -293,7 +293,7 @@ Even with server-side fallback, clients will receive **responses** with new name ### Phase 1 — Internal rename (zero breaking changes) ✅ Done -1. ✅ Rename `block_production_condition` namespace, enum, and `exception_producing_block` in `witness.hpp` + `witness.cpp`. +1. ✅ Rename `block_production_condition` namespace, enum, and `exception_producing_block` in `validator.hpp` + `validator.cpp`. 2. ✅ Rename internal method names: `block_production_loop`, `maybe_produce_block`, `is_witness_scheduled_soon`. 3. ⏳ Rename `block_post_validation_object` → `validator_confirmation_object` — deferred with physical file renames. 4. ✅ Rename `current_shuffled_witnesses[]` field. @@ -306,7 +306,7 @@ Even with server-side fallback, clients will receive **responses** with new name 2. ✅ Rename `witness_update_operation` → `validator_update_operation` and the other four operations (Section 5.1). Binary type IDs preserved. 3. ✅ Add deprecated endpoint aliases in `validator_api` plugin for all `get_witness_*` methods. 4. ✅ Rename CLI wallet commands; keep old names as deprecated aliases. -5. ✅ Config keys updated (`plugin = validator`, `plugin = validator_api`, `plugin = validator_guard`). `--witness` kept as deprecated alias for `--validator` in config.ini backward compat. +5. ✅ Config keys updated (`plugin = validator`, `plugin = validator_api`, `plugin = validator_guard`). `--validator` kept as deprecated alias for `--validator` in config.ini backward compat. 6. ✅ Plugin directories renamed: `plugins/validator/`, `plugins/validator_api/`, `plugins/validator_guard/`. CMake targets updated. 7. ✅ Rename `witness_object`, `witness_schedule_object`, `witness_api_object` (C++ types and files). 8. ✅ `config_witness.ini` and all other `config*.ini` updated to new plugin names. @@ -377,7 +377,7 @@ Even with server-side fallback, clients will receive **responses** with new name | `libraries/chain/include/graphene/chain/account_object.hpp` | ✅ Done | `witnesses_voted_for`→`validators_voted_for`, `witnesses_vote_weight`→`validators_vote_weight`, methods | | `libraries/api/include/graphene/api/account_api_object.hpp` | ✅ Done | Same fields + `witness_votes`→`validator_votes` | | `libraries/api/account_api_object.cpp` | ✅ Done | Field assignments | -| `libraries/protocol/include/graphene/protocol/config.hpp` | ✅ Done | All `WITNESS` constants → `VALIDATOR` | +| `libraries/protocol/include/graphene/protocol/config.hpp` | ✅ Done | All `validator` constants → `VALIDATOR` | | `libraries/protocol/include/graphene/protocol/config_testnet.hpp` | ✅ Done | Same | | `libraries/protocol/get_config.cpp` | ✅ Done | API string keys updated | | `share/vizd/config/config_witness.ini` | ✅ Done | Plugin names → `validator`, `validator_api`, `validator_guard` | @@ -392,11 +392,11 @@ Even with server-side fallback, clients will receive **responses** with new name ## 10. Summary -`witness` → `validator` is the right rename. It: +`validator` → `validator` is the right rename. It: - Matches XRPL, Ethereum PoS, Cosmos, and Polkadot terminology - Accurately describes both the block production and post-validation duties -- Removes the passive/observational connotation of "witness" +- Removes the passive/observational connotation of "validator" - Makes `block_post_validation_object` → `validator_confirmation_object` semantically clear **The rename is safe for unupdated JS/PHP clients** — the binary wire format uses integer type IDs, not string names, so old clients submitting transactions continue to work. A server-side name alias table handles the JSON string name fallback at zero cost. The only visible breakage for old clients is in response parsing, where they may encounter new names (`validator_update` instead of `witness_update`) in block history reads. diff --git a/.qoder/repowiki/en/content/API Reference.md b/.qoder/repowiki/en/content/API Reference.md index 1ae3e8e52d..80730cbc9a 100644 --- a/.qoder/repowiki/en/content/API Reference.md +++ b/.qoder/repowiki/en/content/API Reference.md @@ -1,4 +1,4 @@ -# API Reference +# API Reference **Referenced Files in This Document** @@ -64,7 +64,7 @@ SN["Social Network API"] PS["Paid Subscription API"] CP["Custom Protocol API"] IW["Invite API"] -WT["Witness API"] +WT["validator API"] NB["Network Broadcast API"] AH["Account History API"] FL["Follow API"] @@ -310,7 +310,7 @@ Example invocation pattern: ### Additional API Categories - Paid Subscription API: Namespace paid_subscription_api - Invite API: Namespace invite_api -- Witness API: Namespace witness_api +- validator API: Namespace witness_api - Network Broadcast API: Namespace network_broadcast_api - Follow API: Namespace follow - Private Message API: Namespace private_message diff --git a/.qoder/repowiki/en/content/Advanced Topics/Database Schema Design.md b/.qoder/repowiki/en/content/Advanced Topics/Database Schema Design.md index 1896d8cddd..7da4617ba1 100644 --- a/.qoder/repowiki/en/content/Advanced Topics/Database Schema Design.md +++ b/.qoder/repowiki/en/content/Advanced Topics/Database Schema Design.md @@ -1,4 +1,4 @@ -# Database Schema Design +# Database Schema Design **Referenced Files in This Document** @@ -44,7 +44,7 @@ The database layer is built on top of ChainBase and integrates with Protocol typ - Database lifecycle and schema initialization - Fork database for chain branching and conflict resolution - Index registration and plugin index hooks -- Object definitions for accounts, content, witnesses, proposals, invites, and auxiliary objects +- Object definitions for accounts, content, validators, proposals, invites, and auxiliary objects ```mermaid graph TB @@ -167,7 +167,7 @@ database --> invite_object : "persists" Object types are enumerated and mapped to persistent objects. Each object inherits from a base object class and is associated with a MultiIndex container that defines its indexes. - Enumerated object types: - - Dynamic global property, account, authority, witness, transaction, block summary, witness schedule, content, content type, content vote, witness vote, hardfork property, vesting routes, authorities history, recovery requests, escrow, block stats, vesting delegation, fixed delegation, delegation expiration, metadata, proposal, required approvals, committee request/vote, invite, award shares expiration, paid subscriptions, witness penalties, block post validation + - Dynamic global property, account, authority, validator, transaction, block summary, validator schedule, content, content type, content vote, validator vote, hardfork property, vesting routes, authorities history, recovery requests, escrow, block stats, vesting delegation, fixed delegation, delegation expiration, metadata, proposal, required approvals, committee request/vote, invite, award shares expiration, paid subscriptions, validator penalties, block post validation - Object identity and serialization: - Object IDs are typed identifiers; reflection and raw packing/unpacking are supported for persistence and RPC @@ -232,8 +232,8 @@ Optimization notes: - [content_object.hpp](file://libraries/chain/include/graphene/chain/content_object.hpp#L197-L248) - [content_object.hpp](file://libraries/chain/include/graphene/chain/content_object.hpp#L144-L184) -### Witness and Governance Objects -Witness objects track scheduling, votes, signing keys, and penalties. Governance objects include votes and schedules. +### validator and Governance Objects +validator objects track scheduling, votes, signing keys, and penalties. Governance objects include votes and schedules. Indexes: - witness_object: @@ -245,8 +245,8 @@ Indexes: - by_schedule_time (composite: virtual_scheduled_time + id) - witness_vote_object: - by_id (unique) - - by_account_witness (unique composite: account + witness) - - by_witness_account (unique composite: witness + account) + - by_account_witness (unique composite: account + validator) + - by_witness_account (unique composite: validator + account) - witness_schedule_object: - by_id (unique) - witness_penalty_expire_object: @@ -256,7 +256,7 @@ Indexes: Optimization notes: - Virtual scheduled time index supports O(log N) scheduling decisions -- Composite indexes on account-witness pairs enable fast vote lookups +- Composite indexes on account-validator pairs enable fast vote lookups **Section sources** - [witness_objects.hpp](file://libraries/chain/include/graphene/chain/witness_objects.hpp#L183-L219) @@ -382,7 +382,7 @@ Common optimization techniques: ### Object Relationship Patterns Relationships among entities: - Accounts own content and votes; content references authors and parents; votes reference accounts and content -- Witnesses schedule and produce blocks; witness votes link accounts to witnesses +- validators schedule and produce blocks; validator votes link accounts to validators - Proposals require approvals from active/master/regular sets; required approvals link accounts to proposals - Invites connect creators/receivers and keys to balances - Vesting delegation links delegators to delegatees; expiration objects track time-based cleanup @@ -393,7 +393,7 @@ ACCOUNT ||--o{ CONTENT : "author" ACCOUNT ||--o{ CONTENT_VOTE : "voter" CONTENT ||--o{ CONTENT_VOTE : "content" ACCOUNT ||--o{ WITNESS_VOTE : "account" -WITNESS ||--o{ WITNESS_VOTE : "witness" +validator ||--o{ WITNESS_VOTE : "validator" ACCOUNT ||--o{ VESTING_DELEGATION : "delegator" ACCOUNT ||--o{ VESTING_DELEGATION_EXPIRATION : "delegator" ACCOUNT ||--o{ PROPOSAL : "author" diff --git a/.qoder/repowiki/en/content/Advanced Topics/Hardfork Management.md b/.qoder/repowiki/en/content/Advanced Topics/Hardfork Management.md index 383eee0f3e..60eb37ed22 100644 --- a/.qoder/repowiki/en/content/Advanced Topics/Hardfork Management.md +++ b/.qoder/repowiki/en/content/Advanced Topics/Hardfork Management.md @@ -1,4 +1,4 @@ -# Hardfork Management +# Hardfork Management **Referenced Files in This Document** @@ -20,17 +20,17 @@ - [config.hpp](file://libraries/protocol/include/graphene/protocol/config.hpp) - [fork_database.hpp](file://libraries/chain/include/graphene/chain/fork_database.hpp) - [global_property_object.hpp](file://libraries/chain/include/graphene/chain/global_property_object.hpp) -- [witness.cpp](file://plugins/witness/witness.cpp) +- [validator.cpp](file://plugins/validator/validator.cpp) ## Update Summary **Changes Made** - Added Emergency Consensus Recovery (HF12) as the latest hardfork implementation - Updated hardfork numbering from 11 to 12 with CHAIN_HARDFORK_12_VERSION and CHAIN_HARDFORK_12_TIME configurations -- Enhanced witness scheduling logic with emergency mode activation +- Enhanced validator scheduling logic with emergency mode activation - Added emergency consensus recovery mechanisms and three-state safety enforcement - Updated CHAIN_NUM_HARDFORKS from 11 to 12 in preamble -- Added comprehensive emergency mode functionality including fork switching and witness management +- Added comprehensive emergency mode functionality including fork switching and validator management ## Table of Contents 1. [Introduction](#introduction) @@ -45,7 +45,7 @@ 10. [Appendices](#appendices) ## Introduction -This document explains the hardfork management system in the VIZ C++ Node. It covers how hardforks are defined, stored, loaded, and enforced during node runtime; how scheduled upgrades are coordinated with witness voting; how backward compatibility is maintained; and how migrations and state transitions are executed. The system now includes Emergency Consensus Recovery (HF12) as the latest hardfork implementation with sophisticated emergency mode activation and witness scheduling changes designed to recover from network consensus failures. +This document explains the hardfork management system in the VIZ C++ Node. It covers how hardforks are defined, stored, loaded, and enforced during node runtime; how scheduled upgrades are coordinated with validator voting; how backward compatibility is maintained; and how migrations and state transitions are executed. The system now includes Emergency Consensus Recovery (HF12) as the latest hardfork implementation with sophisticated emergency mode activation and validator scheduling changes designed to recover from network consensus failures. ## Project Structure The hardfork system is centered around: @@ -73,7 +73,7 @@ HF12["HF12
12.hf"] DBHdr["Database Hardfork Properties
database.hpp"] DBInit["Hardfork Init & Evaluation
database.cpp"] Emergency["Emergency Consensus
Recovery"] -Witness["Enhanced Witness
Scheduling"] +validator["Enhanced validator
Scheduling"] Config["Configuration
Parameters"] HFDir --> Preamble HFDir --> HF1 @@ -92,7 +92,7 @@ Preamble --> DBHdr HFDir --> DBInit DBHdr --> DBInit HF12 --> Emergency -HF12 --> Witness +HF12 --> validator HF12 --> Config ``` @@ -117,7 +117,7 @@ HF12 --> Config Key responsibilities: - Version management: Maintains arrays of hardfork times and versions used by the node, now supporting up to 12 hardforks. -- Scheduled upgrades: Watches witness votes and sets next hardfork according to majority. +- Scheduled upgrades: Watches validator votes and sets next hardfork according to majority. - Backward compatibility: Uses has_hardfork() checks to branch behavior depending on applied hardfork level. - Migration and state transitions: Applies changes to chain properties, validators, and runtime logic when crossing hardfork boundaries. - **Network recovery**: Implements automatic emergency mode activation and recovery mechanisms. @@ -151,7 +151,7 @@ DB-->>Node : ready to validate/apply blocks loop Block Processing Node->>DB : push_block(new_block) DB->>DB : evaluate hardfork boundaries -DB->>HFProps : update next_hardfork if witness consensus changes +DB->>HFProps : update next_hardfork if validator consensus changes DB->>Emergency : monitor LIB timestamp Emergency-->>DB : activate/deactivate emergency mode DB-->>Node : block accepted or rejected based on hardfork rules @@ -173,14 +173,14 @@ end - Hardfork 1: timestamp and version for fixing a median calculation. - Hardfork 2: timestamp and version for fixing a committee approval threshold. - Hardfork 4: introduces major protocol changes (award operations, custom sequences). - - Hardfork 6: modifies witness penalties and vote counting. + - Hardfork 6: modifies validator penalties and vote counting. - Hardfork 9: adjusts chain parameters for invites, subscriptions, and fees. - Hardfork 11: emission model changes. - - **Hardfork 12: Emergency Consensus Recovery with emergency mode activation and witness scheduling changes**. + - **Hardfork 12: Emergency Consensus Recovery with emergency mode activation and validator scheduling changes**. Practical notes: - Modify *.hf files to define a new hardfork; do not edit generated files. -- Keep timestamps realistic and coordinated with witness voting. +- Keep timestamps realistic and coordinated with validator voting. - **Updated CHAIN_NUM_HARDFORKS from 11 to 12 in preamble**. **Section sources** @@ -201,7 +201,7 @@ Practical notes: ### Hardfork Property Object and Runtime State - Schema: Contains processed hardforks vector, last hardfork ID, current hardfork version, and next hardfork version/time. - Initialization: During open(), the node loads persisted hardfork state and ensures consistency with the chainbase revision and block log. -- Updates: During block application, the node evaluates witness consensus and updates next_hardfork accordingly. +- Updates: During block application, the node evaluates validator consensus and updates next_hardfork accordingly. - **Enhanced**: Now supports emergency consensus properties for HF12. ```mermaid @@ -244,18 +244,18 @@ Database --> HardforkPropertyObject : "reads/writes" #### Emergency Mode Activation - **Automatic Detection**: Monitors last irreversible block (LIB) timestamp and activates emergency mode after CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC (1 hour) without new blocks. -- **Emergency Witness**: Creates and activates the CHAIN_EMERGENCY_WITNESS_ACCOUNT ("committee") with CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY to produce blocks. -- **Neutral Voting**: Emergency witness votes for the currently applied hardfork version (status quo) to avoid pushing for new hardforks. +- **Emergency validator**: Creates and activates the CHAIN_EMERGENCY_WITNESS_ACCOUNT ("committee") with CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY to produce blocks. +- **Neutral Voting**: Emergency validator votes for the currently applied hardfork version (status quo) to avoid pushing for new hardforks. #### Three-State Safety Enforcement - **Healthy Network**: Participation ≥ 33% - enforces safe defaults automatically, overriding manual config overrides. - **Distressed Network**: Participation < 33% - honors manual config overrides to allow operators to accelerate recovery. - **Emergency Mode**: Automatically bypasses both stale and participation checks when emergency consensus is active. -#### Enhanced Witness Scheduling -- **Hybrid Schedule**: During emergency mode, replaces unavailable witness slots with emergency witness while maintaining real witness positions. -- **Vote Weighted Fork Switching**: HF12 implements vote-weighted chain comparison during fork switching, using sum of raw votes from unique non-committee witnesses as primary criterion. -- **Median Computation**: Excludes emergency witness from median property computations to prevent skewing chain parameters. +#### Enhanced validator Scheduling +- **Hybrid Schedule**: During emergency mode, replaces unavailable validator slots with emergency validator while maintaining real validator positions. +- **Vote Weighted Fork Switching**: HF12 implements vote-weighted chain comparison during fork switching, using sum of raw votes from unique non-committee validators as primary criterion. +- **Median Computation**: Excludes emergency validator from median property computations to prevent skewing chain parameters. ```mermaid flowchart TD @@ -273,7 +273,7 @@ Legacy --> End ``` **Diagram sources** -- [witness.cpp:354-392](file://plugins/witness/witness.cpp#L354-L392) +- [validator.cpp:354-392](file://plugins/validator/validator.cpp#L354-L392) - [database.cpp:2052-2143](file://libraries/chain/database.cpp#L2052-L2143) - [database.cpp:1096-1135](file://libraries/chain/database.cpp#L1096-L1135) @@ -282,21 +282,21 @@ Legacy --> End - [database.cpp:4334-4438](file://libraries/chain/database.cpp#L4334-L4438) - [database.cpp:2052-2143](file://libraries/chain/database.cpp#L2052-L2143) - [database.cpp:1096-1135](file://libraries/chain/database.cpp#L1096-L1135) -- [witness.cpp:354-392](file://plugins/witness/witness.cpp#L354-L392) +- [validator.cpp:354-392](file://plugins/validator/validator.cpp#L354-L392) ### Hardfork Evaluation and Block Application - During block validation and application, the node checks: - Whether the current hardfork version requires applying changes. - - Whether witness consensus indicates a scheduled upgrade. + - Whether validator consensus indicates a scheduled upgrade. - **Emergency mode activation conditions** based on LIB timestamp monitoring. -- The node compares witness votes against configured hardfork versions/times and updates next_hardfork accordingly. +- The node compares validator votes against configured hardfork versions/times and updates next_hardfork accordingly. - Behavior changes are gated behind has_hardfork() checks to preserve backward compatibility. - **Enhanced**: HF12 adds emergency consensus detection and three-state safety enforcement logic. ```mermaid flowchart TD Start(["Start Block Apply"]) --> CheckHF["Check current_hardfork_version vs next_hardfork"] -CheckHF --> Consensus{"Witness consensus
requires upgrade?"} +CheckHF --> Consensus{"validator consensus
requires upgrade?"} Consensus --> |Yes| UpdateNext["Update next_hardfork and time"] Consensus --> |No| CheckEmergency["Check Emergency Mode Conditions"] CheckEmergency --> EmergencyActive{"Emergency Mode
Active?"} @@ -345,7 +345,7 @@ Operational guidance: ### Rollback and Recovery for Failed Upgrades - Undo sessions: The database uses undo sessions to roll back partial changes when block application fails. - Fork switching: If a new head does not build off the current head, the node switches forks and reapplies blocks safely. -- **Enhanced fork switching**: HF12 implements vote-weighted chain comparison during fork switching, using sum of raw votes from unique non-committee witnesses as primary criterion. +- **Enhanced fork switching**: HF12 implements vote-weighted chain comparison during fork switching, using sum of raw votes from unique non-committee validators as primary criterion. - Pop block: Removes the head block and restores transactions to pending state. - **Emergency mode recovery**: Automatic recovery mechanisms handle emergency consensus state transitions. @@ -391,7 +391,7 @@ Steps to add a new hardfork: Examples of where to add logic: - Operation evaluators: Wrap validation or execution in has_hardfork() branches. - Chain properties: Adjust median properties or inflation logic based on hardfork level. -- Witness scheduling: Update vote counting or penalties depending on hardfork. +- validator scheduling: Update vote counting or penalties depending on hardfork. - **Emergency mode handling**: Implement recovery mechanisms for network failure scenarios. **Section sources** @@ -408,7 +408,7 @@ Examples of where to add logic: - **Consider emergency mode compatibility** for critical operations. ### Modifying Existing Behavior -- Identify the affected subsystem (e.g., witness voting, inflation, chain properties, emergency mode). +- Identify the affected subsystem (e.g., validator voting, inflation, chain properties, emergency mode). - Add a new hardfork constant and timestamp. - Update the relevant runtime logic to branch on has_hardfork(). - **Implement emergency mode considerations** for network recovery scenarios. @@ -422,11 +422,11 @@ Examples of where to add logic: ### Common Hardfork Scenarios - Protocol changes: Introduce new operations or modify semantics (e.g., award operations, custom sequences). - Bug fixes: Correct calculations or state transitions (e.g., median properties, vote accounting). -- Feature additions: Enable new chain parameters or fee structures (e.g., invites, subscriptions, witness penalties). +- Feature additions: Enable new chain parameters or fee structures (e.g., invites, subscriptions, validator penalties). - **Network recovery**: Emergency consensus activation and recovery mechanisms for consensus failures. Validation procedures: -- Confirm hardfork timestamps align with witness votes. +- Confirm hardfork timestamps align with validator votes. - Verify has_hardfork() branches execute the intended logic. - Run reindex tests to ensure deterministic replay. - **Test emergency mode activation and recovery scenarios**. @@ -444,7 +444,7 @@ Validation procedures: The hardfork system depends on: - Hardfork definition files for compile-time constants. - Database hardfork property object for runtime state. -- Witness consensus for determining next hardfork. +- validator consensus for determining next hardfork. - Block log and fork database for replay and fork switching. - **Emergency consensus parameters and configurations**. @@ -459,7 +459,7 @@ HFProps["HardforkPropertyObject"] ForkDB["Fork DB"] BlockLog["Block Log"] Config["config.hpp
Emergency Params"] -Witness["witness.cpp
Three-State Safety"] +validator["validator.cpp
Three-State Safety"] Emergency["Emergency Logic"] HFFiles --> Preamble Preamble --> DBHdr @@ -469,7 +469,7 @@ ForkDB --> DBInit BlockLog --> DBInit HF12 --> DBInit Config --> DBInit -Witness --> DBInit +validator --> DBInit Emergency --> DBInit ``` @@ -479,7 +479,7 @@ Emergency --> DBInit - [database.hpp:577-578](file://libraries/chain/include/graphene/chain/database.hpp#L577-L578) - [database.cpp:4334-4438](file://libraries/chain/database.cpp#L4334-L4438) - [config.hpp:110-123](file://libraries/protocol/include/graphene/protocol/config.hpp#L110-L123) -- [witness.cpp:354-392](file://plugins/witness/witness.cpp#L354-L392) +- [validator.cpp:354-392](file://plugins/validator/validator.cpp#L354-L392) **Section sources** - [0-preamble.hf:54-56](file://libraries/chain/hardfork.d/0-preamble.hf#L54-L56) @@ -492,14 +492,14 @@ Emergency --> DBInit - Reindexing with skip flags reduces overhead by bypassing expensive validations. - Periodic revision setting during reindex prevents excessive memory pressure. - Auto-scaling shared memory helps avoid failures during heavy reindex workloads. -- **Emergency mode optimization**: Minimal performance impact through efficient emergency witness creation and activation. +- **Emergency mode optimization**: Minimal performance impact through efficient emergency validator creation and activation. - **Enhanced fork switching**: Vote-weighted chain comparison adds computational overhead but improves network stability. Recommendations: - Configure shared memory sizing appropriately for your hardware. - Monitor free memory and adjust thresholds to trigger resizing proactively. - **Monitor emergency mode activation frequency** to assess network health. -- **Optimize witness scheduling algorithms** for emergency mode scenarios. +- **Optimize validator scheduling algorithms** for emergency mode scenarios. **Section sources** - [database.cpp:270-350](file://libraries/chain/database.cpp#L270-L350) @@ -511,15 +511,15 @@ Common issues and resolutions: - Revision mismatch on open: Indicates chainbase revision does not match head block number; reindex or restore from a compatible backup. - Chain state mismatch with block log: Requires reindex to reconcile state. - Memory exhaustion during reindex: Increase shared memory size or tune auto-resize parameters. -- Hardfork not triggering: Verify witness consensus matches configured hardfork version/time; check has_hardfork() branches. +- Hardfork not triggering: Verify validator consensus matches configured hardfork version/time; check has_hardfork() branches. - **Emergency mode activation failures**: Check CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC configuration and LIB timestamp monitoring. -- **Emergency witness creation issues**: Verify CHAIN_EMERGENCY_WITNESS_ACCOUNT and CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY settings. -- **Three-state safety enforcement problems**: Review witness participation rate calculations and configuration overrides. +- **Emergency validator creation issues**: Verify CHAIN_EMERGENCY_WITNESS_ACCOUNT and CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY settings. +- **Three-state safety enforcement problems**: Review validator participation rate calculations and configuration overrides. Diagnostic steps: - Review logs around open() and reindex() for explicit assertions or exceptions. - Confirm hardfork timestamps and versions in *.hf files. -- Validate that next_hardfork updates according to witness votes. +- Validate that next_hardfork updates according to validator votes. - **Monitor emergency mode activation and deactivation events**. - **Check fork switching behavior** during emergency mode. @@ -528,7 +528,7 @@ Diagnostic steps: - [database.cpp:270-350](file://libraries/chain/database.cpp#L270-L350) - [database.cpp:1600-1654](file://libraries/chain/database.cpp#L1600-L1654) - [database.cpp:4334-4438](file://libraries/chain/database.cpp#L4334-L4438) -- [witness.cpp:354-392](file://plugins/witness/witness.cpp#L354-L392) +- [validator.cpp:354-392](file://plugins/validator/validator.cpp#L354-L392) ## Conclusion The VIZ hardfork system provides a robust, versioned mechanism for managing protocol upgrades, now enhanced with comprehensive emergency consensus recovery capabilities. By combining compile-time definitions, runtime state tracking, consensus-driven scheduling, and automatic network recovery mechanisms, it enables safe, backward-compatible upgrades even during network failure scenarios. The addition of HF12 (Emergency Consensus Recovery) significantly strengthens the system's resilience and reliability for critical network operations. @@ -540,11 +540,11 @@ The VIZ hardfork system provides a robust, versioned mechanism for managing prot - Deploy *.hf files and rebuild (remember to increment CHAIN_NUM_HARDFORKS to 12). - Start node in read-only mode to validate. - Monitor logs and metrics. - - Coordinate with witnesses to reach consensus. + - Coordinate with validators to reach consensus. - **Monitor emergency mode activation and recovery scenarios**. - Proceed with full node operation after verification. - **Emergency mode operational procedures**: - Monitor CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC threshold. - - Verify emergency witness activation and block production. + - Verify emergency validator activation and block production. - Track emergency mode duration and automatic deactivation. - Test fork switching behavior during emergency scenarios. \ No newline at end of file diff --git a/.qoder/repowiki/en/content/Architecture Overview/Architecture Overview.md b/.qoder/repowiki/en/content/Architecture Overview/Architecture Overview.md index e2e7a82bde..11a1f01001 100644 --- a/.qoder/repowiki/en/content/Architecture Overview/Architecture Overview.md +++ b/.qoder/repowiki/en/content/Architecture Overview/Architecture Overview.md @@ -1,4 +1,4 @@ -# Architecture Overview +# Architecture Overview **Referenced Files in This Document** @@ -61,7 +61,7 @@ P_JSONRPC["plugins/json_rpc"] P_DB_API["plugins/database_api"] P_P2P["plugins/p2p"] P_WBS["plugins/webserver"] -P_WITNESS["plugins/witness"] +P_WITNESS["plugins/validator"] P_TAGS["plugins/tags"] P_ACCOUNT_HISTORY["plugins/account_history"] P_ACCOUNT_BY_KEY["plugins/account_by_key"] @@ -169,7 +169,7 @@ P_JSONRPC["plugins/json_rpc
plugin.cpp"] P_DB_API["plugins/database_api"] P_P2P["plugins/p2p"] P_WBS["plugins/webserver"] -P_WITNESS["plugins/witness"] +P_WITNESS["plugins/validator"] P_TAGS["plugins/tags"] P_ACCOUNT_HISTORY["plugins/account_history"] P_ACCOUNT_BY_KEY["plugins/account_by_key"] @@ -458,7 +458,7 @@ P_P2P["p2p"] P_JSONRPC["json_rpc"] P_DB_API["database_api"] P_WBS["webserver"] -P_WITNESS["witness"] +P_WITNESS["validator"] P_TAGS["tags"] P_ACCOUNT_HISTORY["account_history"] P_ACCOUNT_BY_KEY["account_by_key"] diff --git a/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Chain Library/Block Processing and Validation.md b/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Chain Library/Block Processing and Validation.md index 4a29498c9e..47c662e950 100644 --- a/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Chain Library/Block Processing and Validation.md +++ b/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Chain Library/Block Processing and Validation.md @@ -1,4 +1,4 @@ -# Block Processing and Validation +# Block Processing and Validation **Referenced Files in This Document** @@ -36,11 +36,11 @@ ## Introduction This document explains the Block Processing and Validation system responsible for accepting incoming blocks, validating their integrity and consensus compliance, applying state changes, and maintaining blockchain consistency. It focuses on: - Efficient block storage and retrieval via the block log -- The block validation pipeline (header, size, merkle, witness scheduling) +- The block validation pipeline (header, size, merkle, validator scheduling) - The push_block() and validate_block() orchestration -- Block summary object creation and witness participation tracking +- Block summary object creation and validator participation tracking - Block replay for synchronization and state reconstruction -- Integration with fork database, witness scheduling, and state persistence +- Integration with fork database, validator scheduling, and state persistence - Block size limits, transaction ordering, and consensus enforcement - Enhanced logging system with production-ready visibility @@ -49,7 +49,7 @@ The block processing pipeline spans several core modules: - Chain database: orchestrates validation, fork selection, state application, and persistence - Fork database: manages canonical chain and forks for reorganization decisions - Block log: persistent append-only storage enabling fast replay and random access -- Block summary and witness schedule: support consensus checks and participation metrics +- Block summary and validator schedule: support consensus checks and participation metrics - Network layer: handles block propagation and synchronization with enhanced logging ```mermaid @@ -104,7 +104,7 @@ CHAIN --> DB - Network layer: Handles block propagation, synchronization, and enhanced logging with console color formatting. Key responsibilities: -- validate_block(): Validates Merkle root, block size, and optionally witness signature and schedule alignment +- validate_block(): Validates Merkle root, block size, and optionally validator signature and schedule alignment - push_block(): Coordinates fork selection, reorganization, and state application - apply_block(): Applies all transactions and operations, updates dynamic properties, and creates block summaries - block_log: Provides deterministic replay and persistence @@ -246,7 +246,7 @@ fork_database --> fork_item : "manages" ### Database: Validation Pipeline and Block Application The database coordinates validation and application: -- validate_block(): Enforces Merkle root, block size, and optionally witness signature and schedule alignment +- validate_block(): Enforces Merkle root, block size, and optionally validator signature and schedule alignment - push_block(): Orchestrates fork selection and reorganization; calls apply_block() on success - apply_block(): Applies all transactions and operations, updates dynamic properties, and creates block summaries @@ -280,8 +280,8 @@ DB-->>Caller : "fork_switched?" Validation steps: - Merkle root verification against transaction set - Block size enforcement against dynamic maximum -- Optional witness signature validation -- Optional witness schedule alignment (slot correctness) +- Optional validator signature validation +- Optional validator schedule alignment (slot correctness) Fork selection: - If new head extends beyond current head, compute branches and switch if heavier @@ -301,30 +301,30 @@ State application: - [database.cpp:3750-3757](file://libraries/chain/database.cpp#L3750-L3757) - [database.cpp:3759-3873](file://libraries/chain/database.cpp#L3759-L3873) -### Enhanced Witness Account Validation and Graceful Error Handling +### Enhanced validator Account Validation and Graceful Error Handling -**Updated** Enhanced witness account validation during block production with comprehensive pre-check mechanisms +**Updated** Enhanced validator account validation during block production with comprehensive pre-check mechanisms -The system now performs preliminary verification using find_account() calls instead of relying solely on get_account() which would throw exceptions if accounts are missing. This ensures graceful handling of missing witness accounts and prevents node crashes during block production. +The system now performs preliminary verification using find_account() calls instead of relying solely on get_account() which would throw exceptions if accounts are missing. This ensures graceful handling of missing validator accounts and prevents node crashes during block production. Key improvements: - Pre-check mechanism using find_account() before block generation - Graceful error handling with critical logging for shared memory corruption detection -- Prevention of exceptions during block production when witness accounts are missing -- Comprehensive error reporting with witness metadata for debugging +- Prevention of exceptions during block production when validator accounts are missing +- Comprehensive error reporting with validator metadata for debugging ```mermaid flowchart TD -WStart(["Witness Block Production"]) --> PreCheck["Pre-check: find_account(witness_owner)"] +WStart(["validator Block Production"]) --> PreCheck["Pre-check: find_account(witness_owner)"] PreCheck --> Found{"Account found?"} -Found --> |Yes| SigCheck["Validate witness signature"] +Found --> |Yes| SigCheck["Validate validator signature"] Found --> |No| CriticalLog["Critical: Log missing account details"] CriticalLog --> Assert["Assert with detailed error message"] SigCheck --> GenBlock["Generate block"] GenBlock --> PostCheck["Post-check: find_account(cwit.owner)"] PostCheck --> PostFound{"Account found?"} PostFound --> |Yes| ApplyBlock["Apply block and distribute rewards"] -PostFound --> |No| CriticalLog2["Critical: Log missing witness account"] +PostFound --> |No| CriticalLog2["Critical: Log missing validator account"] CriticalLog2 --> Assert2["Assert with replay recommendation"] ``` @@ -339,14 +339,14 @@ CriticalLog2 --> Assert2["Assert with replay recommendation"] - [database.cpp:2871-2884](file://libraries/chain/database.cpp#L2871-L2884) - [database.hpp:185-187](file://libraries/chain/include/graphene/chain/database.hpp#L185-L187) -### Block Header Validation and Witness Scheduling +### Block Header Validation and validator Scheduling Header validation ensures: - Previous block ID matches current head - Timestamp monotonicity -- Witness signature verification (optional) -- Witness schedule alignment (slot correctness) +- validator signature verification (optional) +- validator schedule alignment (slot correctness) -Witness participation tracking: +validator participation tracking: - Missed block counters and penalties - Participation rate computation via recent slots - Irreversible block updates based on validator majorities @@ -356,11 +356,11 @@ flowchart TD HStart(["validate_block_header"]) --> PrevCheck["Verify previous == head_block_id"] PrevCheck --> TSCheck["Verify timestamp > head_block_time"] TSCheck --> SigCheck{"skip_witness_signature?"} -SigCheck --> |No| VerifySig["Validate block.signee against witness key"] +SigCheck --> |No| VerifySig["Validate block.signee against validator key"] SigCheck --> |Yes| SkipSig["Skip signature check"] VerifySig --> SchCheck{"skip_witness_schedule_check?"} SkipSig --> SchCheck -SchCheck --> |No| SlotCheck["Get slot_at_time(timestamp) and verify scheduled witness"] +SchCheck --> |No| SlotCheck["Get slot_at_time(timestamp) and verify scheduled validator"] SchCheck --> |Yes| SkipSch["Skip schedule check"] SlotCheck --> HEnd(["Valid"]) SkipSch --> HEnd @@ -451,7 +451,7 @@ The enhanced logging system follows a production-ready strategy: - **Info level logging**: Critical block processing events are logged at info level for production visibility - **Console color formatting**: Terminal output uses color codes for improved readability -- **Structured information**: Logs include block numbers, timestamps, witness information, and performance metrics +- **Structured information**: Logs include block numbers, timestamps, validator information, and performance metrics - **Minimal noise**: Debug-level verbose logging is reduced while maintaining essential operational information ```mermaid @@ -515,16 +515,16 @@ CHAIN["chain plugin.cpp"] --> DB - Skip flags: During reindex or trusted operations, validations can be selectively skipped to improve throughput. - Undo sessions: State changes are wrapped in undo sessions to support rollback on errors and efficient reversion during forks. - Pending transactions: During block generation, transactions are re-applied to reflect time-dependent semantics and respect block size limits. -- **Enhanced witness validation**: Pre-check mechanisms using find_account() avoid exception overhead and improve block production reliability. +- **Enhanced validator validation**: Pre-check mechanisms using find_account() avoid exception overhead and improve block production reliability. - **Enhanced logging**: Info level logging provides production visibility without debug-level overhead, while console color formatting improves terminal readability. ## Troubleshooting Guide Common issues and remedies: - Block log/head mismatch: The block log automatically detects inconsistencies and reconstructs the index. If repeated failures occur, inspect logs around index construction and ensure proper shutdown procedures. -- Fork reorganization failures: If applying a fork branch fails, the system removes invalid blocks from the fork database, restores the good fork, and rethrows the error. Review the failing block and witness participation. +- Fork reorganization failures: If applying a fork branch fails, the system removes invalid blocks from the fork database, restores the good fork, and rethrows the error. Review the failing block and validator participation. - Excessive memory usage during replay: The database reserves memory and resizes shared memory if allocation fails mid-replay. Monitor logs for forced resizing events. -- Invalid witness schedule: If a block's timestamp slot does not align with the scheduled witness, validation fails. Verify time synchronization and witness schedules. -- **Missing witness accounts**: Enhanced pre-check mechanisms now detect missing witness accounts gracefully, logging critical details and preventing node crashes. Such issues typically indicate shared memory corruption requiring node restart with replay. +- Invalid validator schedule: If a block's timestamp slot does not align with the scheduled validator, validation fails. Verify time synchronization and validator schedules. +- **Missing validator accounts**: Enhanced pre-check mechanisms now detect missing validator accounts gracefully, logging critical details and preventing node crashes. Such issues typically indicate shared memory corruption requiring node restart with replay. - **Enhanced logging visibility**: Production environments can now monitor block processing through info level logs without debug configuration, while console color formatting improves terminal readability for operators. **Section sources** @@ -538,10 +538,10 @@ Common issues and remedies: - [p2p_plugin.cpp:152-157](file://plugins/p2p/p2p_plugin.cpp#L152-L157) ## Conclusion -The Block Processing and Validation system integrates robust storage (block log), fork management (fork database), and strict consensus validation (database) to ensure blockchain consistency. The validate_block() and push_block() methods coordinate header checks, witness scheduling, Merkle roots, and block size limits. The system supports efficient replay for synchronization and maintains witness participation metrics to enforce consensus. +The Block Processing and Validation system integrates robust storage (block log), fork management (fork database), and strict consensus validation (database) to ensure blockchain consistency. The validate_block() and push_block() methods coordinate header checks, validator scheduling, Merkle roots, and block size limits. The system supports efficient replay for synchronization and maintains validator participation metrics to enforce consensus. -**Enhanced witness account validation** provides improved reliability during block production by performing preliminary verification using find_account() calls instead of relying on get_account() which would throw exceptions. This change ensures graceful handling of missing witness accounts and prevents node crashes, while maintaining comprehensive error reporting for debugging shared memory corruption scenarios. +**Enhanced validator account validation** provides improved reliability during block production by performing preliminary verification using find_account() calls instead of relying on get_account() which would throw exceptions. This change ensures graceful handling of missing validator accounts and prevents node crashes, while maintaining comprehensive error reporting for debugging shared memory corruption scenarios. -**Enhanced logging system** provides production-ready visibility with info level logging for sync blocks and normal blocks, upgraded from debug level for better production monitoring. Console color formatting improves terminal readability with white text for block processing notifications and structured information including block numbers, witness information, and performance metrics. +**Enhanced logging system** provides production-ready visibility with info level logging for sync blocks and normal blocks, upgraded from debug level for better production monitoring. Console color formatting improves terminal readability with white text for block processing notifications and structured information including block numbers, validator information, and performance metrics. Proper use of skip flags, memory mapping, and undo sessions yields strong performance and reliability, making the system resilient to various operational challenges while maintaining strict consensus enforcement. The enhanced logging system ensures that block processing activities are visible in production environments without overwhelming debug-level verbosity, supporting effective monitoring and troubleshooting of blockchain operations. \ No newline at end of file diff --git a/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Chain Library/Chain Library.md b/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Chain Library/Chain Library.md index 59802d8dbd..c27426cf05 100644 --- a/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Chain Library/Chain Library.md +++ b/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Chain Library/Chain Library.md @@ -1,4 +1,4 @@ -# Chain Library +# Chain Library **Referenced Files in This Document** @@ -22,7 +22,7 @@ - [plugin.cpp](file://plugins/chain/plugin.cpp) - [snapshot_plugin.cpp](file://plugins/snapshot/plugin.cpp) - [node.cpp](file://libraries/network/node.cpp) -- [witness.cpp](file://plugins/witness/witness.cpp) +- [validator.cpp](file://plugins/validator/validator.cpp) - [console_appender.cpp](file://thirdparty/fc/src/log/console_appender.cpp) - [main.cpp](file://programs/vizd/main.cpp) @@ -59,7 +59,7 @@ The Chain Library is organized around a central database class that orchestrates - Fork handling with a fork database - Block log for durable storage - DLT rolling block log for selective block retention -- Object model definitions for accounts, witnesses, committee requests, and more +- Object model definitions for accounts, validators, committee requests, and more - Evaluator registry for operation processing - Observer signals for event-driven integrations - Snapshot loading and state restoration capabilities @@ -92,7 +92,7 @@ subgraph "Logging System" LOG["console_appender.cpp"] MAIN["main.cpp"] SYNC["node.cpp"] -WITNESS["witness.cpp"] +validator["validator.cpp"] end DB --> FDB DB --> BLK @@ -110,7 +110,7 @@ CHAINPLUG --> SNAP CHAINPLUG --> DB SNAP --> LOG SYNC --> LOG -WITNESS --> LOG +validator --> LOG MAIN --> LOG ``` @@ -146,7 +146,7 @@ MAIN --> LOG - Fork Database: Maintains a tree of candidate blocks, supports branch resolution, and selects the longest chain. - Block Log: Provides durable, memory-mapped storage for blocks and an index for fast random access. - DLT Rolling Block Log: Enhanced block storage system that maintains only a rolling window of recent blocks for selective retention. -- Object Model: Defines all persistent objects (accounts, witnesses, committee requests, transactions, etc.) and their multi-index containers. +- Object Model: Defines all persistent objects (accounts, validators, committee requests, transactions, etc.) and their multi-index containers. - Evaluator System: Registry and base classes for operation processing with a standardized interface. - Observer Pattern: Signals for pre/post operation application, applied block, and transaction events. - Snapshot Plugin: Handles state restoration from snapshots and manages snapshot lifecycle. @@ -389,12 +389,12 @@ WriteIndex --> UpdateHead["Update head"] ### Data Model: Objects and Indices The object model defines persistent entities and their indices: - Object types enumerate all managed object kinds -- Account, witness, committee request/vote, transaction, escrow, vesting delegation, and more +- Account, validator, committee request/vote, transaction, escrow, vesting delegation, and more - Multi-index containers provide unique and composite keys for efficient lookups Representative object categories: - Accounts: balances, vesting shares, delegation, auction metadata, bandwidth tracking -- Witnesses: votes, virtual scheduling, signing keys, version/hardfork votes +- validators: votes, virtual scheduling, signing keys, version/hardfork votes - Committee: requests with statuses, funding, payouts - Transactions: deduplication and expiration tracking - Escrow and routes: multi-signature transfers and routing @@ -410,7 +410,7 @@ received_vesting_shares : asset energy : int16 last_vote_time : time_point_sec } -WITNESS { +validator { owner : account_name_type votes : share_type signing_key : public_key_type @@ -693,7 +693,7 @@ During blockchain synchronization, the system provides enhanced color-coded prog - **Green messages** indicate synchronization start (only logged once per sync session) - **Yellow messages** indicate synchronization progress every 500 blocks -- **Green messages** confirm successful block generation by witnesses +- **Green messages** confirm successful block generation by validators - **Orange messages** highlight snapshot import operations - **Default messages** show internal synchronization mechanics @@ -709,7 +709,7 @@ The logging system supports configurable color schemes through program options: - [console_appender.cpp:132-154](file://thirdparty/fc/src/log/console_appender.cpp#L132-L154) - [main.cpp:234-253](file://programs/vizd/main.cpp#L234-L253) - [snapshot_plugin.cpp:1018-1032](file://plugins/snapshot/plugin.cpp#L1018-L1032) -- [witness.cpp:286](file://plugins/witness/witness.cpp#L286) +- [validator.cpp:286](file://plugins/validator/validator.cpp#L286) ### Enhanced Troubleshooting with Color Coding The enhanced color-coded logging system significantly improves troubleshooting capabilities: @@ -737,7 +737,7 @@ The ANSI color codes provide immediate visual feedback in terminal environments: **Section sources** - [node.cpp:3446-3456](file://libraries/network/node.cpp#L3446-L3456) - [snapshot_plugin.cpp:1770-1771](file://plugins/snapshot/plugin.cpp#L1770-L1771) -- [witness.cpp:286](file://plugins/witness/witness.cpp#L286) +- [validator.cpp:286](file://plugins/validator/validator.cpp#L286) ### Network Node Synchronization Logging Enhancements **Updated** The network node synchronization system now uses info-level logging for sync restart reasons, providing clearer insights into synchronization behavior: @@ -797,7 +797,7 @@ CHAINPLUG["chain plugin.cpp"] --> SNAP["snapshot_plugin.cpp"] CHAINPLUG --> DB LOGSYS["console_appender.cpp"] --> MAIN["main.cpp"] LOGSYS --> SYNC["node.cpp"] -LOGSYS --> WITNESS["witness.cpp"] +LOGSYS --> validator["validator.cpp"] ``` **Diagram sources** @@ -877,7 +877,7 @@ The Chain Library provides a robust, modular framework for blockchain state mana - Push transaction: - Validate transaction size, apply within a pending session, record changes, and emit pending/applied transaction signals. - Query state: - - Retrieve account, witness, content, escrow, dynamic global properties, witness schedule, and hardfork property objects by name or identifier. + - Retrieve account, validator, content, escrow, dynamic global properties, validator schedule, and hardfork property objects by name or identifier. - Fork resolution: - Compute branches from current head to a candidate fork head, pop blocks until common ancestor, then push new fork blocks. - **New**: Snapshot operations: @@ -972,7 +972,7 @@ Usage scenarios: - [snapshot_plugin.cpp:50-53](file://plugins/snapshot/plugin.cpp#L50-L53) - [console_appender.cpp:132-154](file://thirdparty/fc/src/log/console_appender.cpp#L132-L154) - [node.cpp:3446-3456](file://libraries/network/node.cpp#L3446-L3456) -- [witness.cpp:286](file://plugins/witness/witness.cpp#L286) +- [validator.cpp:286](file://plugins/validator/validator.cpp#L286) ### Network Synchronization Logging Reference **New Section** Enhanced logging improvements for network synchronization diff --git a/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Chain Library/Database Management/Database Management.md b/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Chain Library/Database Management/Database Management.md index a6c304fb24..30a886b208 100644 --- a/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Chain Library/Database Management/Database Management.md +++ b/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Chain Library/Database Management/Database Management.md @@ -1,4 +1,4 @@ -# Database Management +# Database Management **Referenced Files in This Document** @@ -15,8 +15,8 @@ - [database_exceptions.hpp](file://libraries/chain/include/graphene/chain/database_exceptions.hpp) - [plugin.cpp](file://plugins/snapshot/plugin.cpp) - [db_with.hpp](file://libraries/chain/include/graphene/chain/db_with.hpp) -- [witness.cpp](file://plugins/witness/witness.cpp) -- [witness.hpp](file://plugins/witness/include/graphene/plugins/witness/witness.hpp) +- [validator.cpp](file://plugins/validator/validator.cpp) +- [validator.hpp](file://plugins/validator/include/graphene/plugins/validator/validator.hpp) - [config.hpp](file://libraries/protocol/include/graphene/protocol/config.hpp) - [node.cpp](file://libraries/network/node.cpp) - [exceptions.hpp](file://libraries/network/include/graphene/network/exceptions.hpp) @@ -49,9 +49,9 @@ 10. [Conclusion](#conclusion) ## Introduction -This document describes the Database Management system that serves as the core state persistence layer for the VIZ blockchain. It covers the database class lifecycle, initialization and cleanup, validation steps, session management, memory allocation strategies, shared memory configuration, checkpoints for fast synchronization, block log integration, observer pattern usage, DLT mode detection and conditional operations, enhanced block fetching logic with DLT mode awareness, the new `_dlt_gap_logged` flag mechanism for intelligent warning suppression, comprehensive operation guard implementation for concurrent access protection, dual operation guard patterns for witness scheduling safety, enhanced P2P plugin block validation with operation guard protection, and practical examples of database operations and performance optimization. +This document describes the Database Management system that serves as the core state persistence layer for the VIZ blockchain. It covers the database class lifecycle, initialization and cleanup, validation steps, session management, memory allocation strategies, shared memory configuration, checkpoints for fast synchronization, block log integration, observer pattern usage, DLT mode detection and conditional operations, enhanced block fetching logic with DLT mode awareness, the new `_dlt_gap_logged` flag mechanism for intelligent warning suppression, comprehensive operation guard implementation for concurrent access protection, dual operation guard patterns for validator scheduling safety, enhanced P2P plugin block validation with operation guard protection, and practical examples of database operations and performance optimization. -**Updated** - Enhanced with sophisticated exception handling mechanisms that preserve derived exception types during rethrow operations, comprehensive fork database management with improved diagnostic capabilities, and enhanced early rejection logic for blocks far ahead with unknown parents. The system now includes comprehensive database crash debugging capabilities with debug_crash logging throughout critical code paths, debug-block-production configuration option for detailed block production logging, and enhanced diagnostic visibility into database operations. The system also features stacktrace crash handlers for improved crash diagnostics and extensive debug logging markers (DEBUG_CRASH) throughout database and witness production code. +**Updated** - Enhanced with sophisticated exception handling mechanisms that preserve derived exception types during rethrow operations, comprehensive fork database management with improved diagnostic capabilities, and enhanced early rejection logic for blocks far ahead with unknown parents. The system now includes comprehensive database crash debugging capabilities with debug_crash logging throughout critical code paths, debug-block-production configuration option for detailed block production logging, and enhanced diagnostic visibility into database operations. The system also features stacktrace crash handlers for improved crash diagnostics and extensive debug logging markers (DEBUG_CRASH) throughout database and validator production code. ## Project Structure The database subsystem is implemented primarily in the chain library with enhanced support for operation guards, concurrent access protection, and comprehensive crash debugging: @@ -64,7 +64,7 @@ The database subsystem is implemented primarily in the chain library with enhanc - Database exceptions including unlinkable_block_exception: libraries/chain/include/graphene/chain/database_exceptions.hpp - Snapshot plugin integration: plugins/snapshot/plugin.cpp for DLT mode initialization - Postponed transaction processing: libraries/chain/include/graphene/chain/db_with.hpp for transaction queue management -- Witness plugin integration with dual operation guard patterns and debug logging: plugins/witness/witness.cpp and plugins/witness/include/graphene/plugins/witness/witness.hpp +- Validator Plugin integration with dual operation guard patterns and debug logging: plugins/validator/validator.cpp and plugins/validator/include/graphene/plugins/validator/validator.hpp - Protocol configuration: libraries/protocol/include/graphene/protocol/config.hpp for emergency consensus constants - Network layer integration: libraries/network/node.cpp for peer connectivity management - P2P plugin integration with operation guard protection: plugins/p2p/p2p_plugin.cpp for enhanced exception handling and concurrent access safety @@ -95,8 +95,8 @@ CBCPP["chainbase.cpp"] end subgraph "Plugins" SNAPH["snapshot/plugin.cpp"] -WITNESS["witness/witness.cpp"] -WITNESSH["witness.hpp"] +validator["validator/validator.cpp"] +WITNESSH["validator.hpp"] P2PH["p2p/p2p_plugin.cpp"] end subgraph "Network Layer" @@ -125,8 +125,8 @@ DBCPP --> EXCEPTIONCPP DBCPP --> PROTOEX DBCPP --> STH SNAPH --> DBH -WITNESS --> DBH -WITNESSH --> WITNESS +validator --> DBH +WITNESSH --> validator P2PH --> DBH NODE --> DBH EXC --> NODE @@ -146,8 +146,8 @@ EXC --> NODE - [database_exceptions.hpp:1-136](file://libraries/chain/include/graphene/chain/database_exceptions.hpp#L1-L136) - [db_with.hpp:1-154](file://libraries/chain/include/graphene/chain/db_with.hpp#L1-L154) - [plugin.cpp:1180-1379](file://plugins/snapshot/plugin.cpp#L1180-L1379) -- [witness.cpp:270-469](file://plugins/witness/witness.cpp#L270-L469) -- [witness.hpp:38-73](file://plugins/witness/include/graphene/plugins/witness/witness.hpp#L38-L73) +- [validator.cpp:270-469](file://plugins/validator/validator.cpp#L270-L469) +- [validator.hpp:38-73](file://plugins/validator/include/graphene/plugins/validator/validator.hpp#L38-L73) - [config.hpp:111-118](file://libraries/protocol/include/graphene/protocol/config.hpp#L111-L118) - [node.cpp:3185-3384](file://libraries/network/node.cpp#L3185-L3384) - [exceptions.hpp:27-48](file://libraries/network/include/graphene/network/exceptions.hpp#L27-L48) @@ -171,8 +171,8 @@ EXC --> NODE - [database_exceptions.hpp:1-136](file://libraries/chain/include/graphene/chain/database_exceptions.hpp#L1-L136) - [db_with.hpp:1-154](file://libraries/chain/include/graphene/chain/db_with.hpp#L1-L154) - [plugin.cpp:1180-1379](file://plugins/snapshot/plugin.cpp#L1180-L1379) -- [witness.cpp:270-469](file://plugins/witness/witness.cpp#L270-L469) -- [witness.hpp:38-73](file://plugins/witness/include/graphene/plugins/witness/witness.hpp#L38-L73) +- [validator.cpp:270-469](file://plugins/validator/validator.cpp#L270-L469) +- [validator.hpp:38-73](file://plugins/validator/include/graphene/plugins/validator/validator.hpp#L38-L73) - [config.hpp:111-118](file://libraries/protocol/include/graphene/protocol/config.hpp#L111-L118) - [node.cpp:3185-3384](file://libraries/network/node.cpp#L3185-L3384) - [exceptions.hpp:27-48](file://libraries/network/include/graphene/network/exceptions.hpp#L27-L48) @@ -195,11 +195,11 @@ EXC --> NODE - **Enhanced Fork Database Exception Prevention**: Proper unlinkable_block_exception throwing for dead fork detection and improved fork switching logic with deterministic tie-breaking. - **Enhanced Memory Management**: Comprehensive logging system for shared memory allocation with detailed free memory and maximum memory state reporting, plus deferred resize operations. - **Enhanced P2P Synchronization**: Improved unlinkable block classification with soft-banning for dead forks and sync restart prevention for far-ahead blocks. -- **Enhanced Operation Guard System**: Comprehensive concurrent access protection using operation_guard RAII pattern, dual operation guard patterns for witness scheduling safety, and resize barrier mechanisms. +- **Enhanced Operation Guard System**: Comprehensive concurrent access protection using operation_guard RAII pattern, dual operation guard patterns for validator scheduling safety, and resize barrier mechanisms. - **Enhanced Multi-Layered Block Retrieval**: Systematic fallback mechanisms that check fork database when primary block log fails to locate required data, ensuring consistent behavior across different block logging configurations. - **Enhanced Last Irreversible Block Advancement**: Enhanced logic that falls back to fork database when block log lacks required data, maintaining data consistency and availability. - **Enhanced Emergency Consensus**: Automatic recovery system with comprehensive logging and safety checks for network stall detection and recovery. -- **Enhanced Shared Memory Corruption Detection**: New shared_memory_corruption_exception type for structured error handling during witness account validation and block processing. +- **Enhanced Shared Memory Corruption Detection**: New shared_memory_corruption_exception type for structured error handling during validator account validation and block processing. - **Enhanced Auto-Recovery System**: Integrated automatic recovery from snapshot for shared memory corruption scenarios with comprehensive error handling and node restart procedures. - **Enhanced Crash Debugging Capabilities**: Comprehensive debug_crash logging throughout critical code paths for improved crash diagnostics and troubleshooting. - **Enhanced Block Production Debugging**: debug-block-production configuration option for detailed block production logging and monitoring. @@ -219,10 +219,10 @@ Key responsibilities: - Enhanced Early Rejection: Intelligent block rejection for far-ahead blocks with unknown parents using gap-based decision system - Enhanced Fork Database Exception Prevention: Comprehensive mechanisms to prevent fork database exceptions through early rejection and proper dead fork detection - Enhanced Memory Management: Detailed logging of memory states before and after resizing operations for administrator visibility -- Enhanced P2P Protection: Operation guard integration in P2P plugin for safe concurrent access during block validation and witness key retrieval -- Enhanced Witness Scheduling Safety: Dual operation guard patterns in witness scheduling calculations to ensure thread safety during slot determination and witness validation -- Enhanced Shared Memory Validation: Graceful error handling for witness account validation with structured exception reporting -- Enhanced Auto-Recovery Integration: Seamless integration with witness plugin for automatic recovery from shared memory corruption +- Enhanced P2P Protection: Operation guard integration in P2P plugin for safe concurrent access during block validation and validator key retrieval +- Enhanced validator Scheduling Safety: Dual operation guard patterns in validator scheduling calculations to ensure thread safety during slot determination and validator validation +- Enhanced Shared Memory Validation: Graceful error handling for validator account validation with structured exception reporting +- Enhanced Auto-Recovery Integration: Seamless integration with Validator Plugin for automatic recovery from shared memory corruption - **Enhanced Crash Debugging**: Comprehensive debug_crash logging markers throughout database operations for improved troubleshooting - **Enhanced Block Production Monitoring**: debug-block-production configuration option for detailed block production logging and monitoring - **Enhanced Stacktrace Generation**: Automatic stacktrace generation for crash diagnostics and improved debugging experience @@ -258,12 +258,12 @@ The database composes four primary subsystems with enhanced DLT mode support, em - **Enhanced Fork Database Exception Prevention**: Proper unlinkable_block_exception throwing for dead fork detection and improved fork switching logic with deterministic tie-breaking - **Enhanced Memory Management**: Comprehensive logging system for shared memory allocation with detailed state reporting, plus deferred resize operations - **Enhanced P2P Synchronization**: Improved unlinkable block classification with soft-banning for dead forks and sync restart prevention for far-ahead blocks -- **Enhanced Operation Guard System**: Comprehensive concurrent access protection using operation_guard RAII pattern, dual operation guard patterns for witness scheduling safety, and resize barrier mechanisms +- **Enhanced Operation Guard System**: Comprehensive concurrent access protection using operation_guard RAII pattern, dual operation guard patterns for validator scheduling safety, and resize barrier mechanisms - **Enhanced Multi-Layered Block Retrieval**: Systematic fallback mechanisms that check fork database when primary block log fails to locate required data, ensuring consistent behavior across different block logging configurations - **Enhanced Last Irreversible Block Advancement**: Enhanced logic that falls back to fork database when block log lacks required data, maintaining data consistency and availability - **Enhanced Emergency Consensus**: Automatic recovery system with comprehensive logging and safety checks for network stall detection and recovery - **Enhanced Shared Memory Corruption Detection**: New shared_memory_corruption_exception type for structured error reporting during critical validation failures -- **Enhanced Auto-Recovery Integration**: Seamless integration with witness plugin for automatic recovery from shared memory corruption scenarios +- **Enhanced Auto-Recovery Integration**: Seamless integration with Validator Plugin for automatic recovery from shared memory corruption scenarios - **Enhanced Crash Debugging System**: Comprehensive debug_crash logging throughout critical code paths for improved crash diagnostics and troubleshooting - **Enhanced Block Production Monitoring**: debug-block-production configuration option for detailed block production logging and monitoring - **Enhanced Stacktrace Crash Handlers**: Automatic stacktrace generation for crash diagnostics and improved debugging experience @@ -292,7 +292,7 @@ class database { +_maybe_warn_multiple_production(height) +CHIAN_PENDING_TRANSACTION_EXECUTION_LIMIT : time limit constant +emergency_consensus_activation : automatic recovery system -+hybrid_witness_scheduling : dynamic witness replacement ++hybrid_witness_scheduling : dynamic validator replacement +lib_monitoring : timestamp analysis +check_free_memory(skip_print, current_block_num, immediate_resize) +set_min_free_shared_memory_size(value) @@ -693,10 +693,10 @@ UpdatePeers --> End(["Complete"]) **New** - The database now features comprehensive operation guard implementation for concurrent access protection: - **Operation Guard RAII Pattern**: The `operation_guard` class provides automatic concurrent access protection using RAII pattern, ensuring proper cleanup when guards go out of scope. -- **Dual Operation Guard Patterns**: Systematic implementation of dual operation guards in witness scheduling calculations to prevent race conditions during complex slot determination operations. +- **Dual Operation Guard Patterns**: Systematic implementation of dual operation guards in validator scheduling calculations to prevent race conditions during complex slot determination operations. - **Resize Barrier Integration**: Operation guards participate in resize barrier mechanisms, blocking during memory resizing operations to prevent stale pointer issues. -- **P2P Plugin Protection**: Operation guard integration in P2P plugin for safe concurrent access during block validation and witness key retrieval operations. -- **Witness Scheduling Safety**: Dual operation guard patterns ensure thread safety during witness scheduling calculations, protecting lockless reads from concurrent memory resizing. +- **P2P Plugin Protection**: Operation guard integration in P2P plugin for safe concurrent access during block validation and validator key retrieval operations. +- **validator Scheduling Safety**: Dual operation guard patterns ensure thread safety during validator scheduling calculations, protecting lockless reads from concurrent memory resizing. - **Concurrent Resize Safety**: Enhanced resize barrier mechanisms that pause all database operations during memory resizing, preventing data corruption and stale pointer issues. ```mermaid @@ -713,16 +713,16 @@ NormalOp --> End **Diagram sources** - [database.cpp:1556-1588](file://libraries/chain/database.cpp#L1556-L1588) - [database.cpp:1593-1594](file://libraries/chain/database.cpp#L1593-L1594) -- [witness.cpp:271-300](file://plugins/witness/witness.cpp#L271-L300) -- [witness.cpp:506-507](file://plugins/witness/witness.cpp#L506-L507) +- [validator.cpp:271-300](file://plugins/validator/validator.cpp#L271-L300) +- [validator.cpp:506-507](file://plugins/validator/validator.cpp#L506-L507) - [p2p_plugin.cpp:232-243](file://plugins/p2p/p2p_plugin.cpp#L232-L243) **Section sources** - [chainbase.hpp:1078-1120](file://thirdparty/chainbase/include/chainbase/chainbase.hpp#L1078-L1120) - [database.cpp:1556-1588](file://libraries/chain/database.cpp#L1556-L1588) - [database.cpp:1593-1594](file://libraries/chain/database.cpp#L1593-L1594) -- [witness.cpp:271-300](file://plugins/witness/witness.cpp#L271-L300) -- [witness.cpp:506-507](file://plugins/witness/witness.cpp#L506-L507) +- [validator.cpp:271-300](file://plugins/validator/validator.cpp#L271-L300) +- [validator.cpp:506-507](file://plugins/validator/validator.cpp#L506-L507) - [p2p_plugin.cpp:232-243](file://plugins/p2p/p2p_plugin.cpp#L232-L243) ### Enhanced Memory Allocation Strategies and Shared Memory Configuration @@ -735,7 +735,7 @@ NormalOp --> End - **Comprehensive Memory State Reporting**: The `_resize` function now logs detailed information about memory states before and after resizing operations, providing administrators with crucial information about memory usage patterns during blockchain operation. - **Deferred Memory Resize**: The new `_pending_resize` and `_pending_resize_target` fields store resize requests until a safe point when no read locks are held, preventing race conditions and stale pointer issues. - **Thread-Safe Memory Management**: The `apply_pending_resize()` method acquires its own write lock, waiting for all readers to finish before performing memory operations, ensuring thread safety during high-load scenarios. -- **Enhanced Error Handling**: Graceful handling of boost::interprocess::bad_alloc exceptions by returning false instead of throwing, preserving peer connectivity and logging witness slot-misses. +- **Enhanced Error Handling**: Graceful handling of boost::interprocess::bad_alloc exceptions by returning false instead of throwing, preserving peer connectivity and logging validator slot-misses. ```mermaid flowchart TD @@ -821,7 +821,7 @@ These fields enable the deferred resize mechanism to work seamlessly with the ex - **push_block()**: Calls `apply_pending_resize()` at the beginning of block processing, before acquiring the main write lock, ensuring memory operations don't interfere with concurrent read operations. - **generate_block()**: Calls `apply_pending_resize()` before lockless reads, preventing stale pointer issues when memory is resized during block generation. -- **Exception Handling**: When memory exhaustion occurs, the system schedules a deferred resize and lets the exception propagate, preserving peer connectivity and logging witness slot-misses. +- **Exception Handling**: When memory exhaustion occurs, the system schedules a deferred resize and lets the exception propagate, preserving peer connectivity and logging validator slot-misses. **Updated** - Enhanced error handling for shared memory exhaustion: @@ -835,7 +835,7 @@ These fields enable the deferred resize mechanism to work seamlessly with the ex - **Exception Detection**: The system detects boost::interprocess::bad_alloc exceptions by searching for the specific error message pattern "boost::interprocess::bad_alloc". - **Graceful Degradation**: Instead of throwing the exception and potentially disconnecting peers, the system schedules a deferred resize and returns false to indicate the block was not applied. - **State Preservation**: The system preserves memory state by setting reserved memory to current free memory level before scheduling the resize. -- **Peer Connectivity**: This approach prevents P2P layer disconnections and maintains witness slot-miss logging while preserving node connectivity. +- **Peer Connectivity**: This approach prevents P2P layer disconnections and maintains validator slot-miss logging while preserving node connectivity. - **Automatic Recovery**: The next push_block() call will apply the deferred resize safely, allowing the missed block to be re-received during normal sync. ```mermaid @@ -1022,7 +1022,7 @@ UpdateFields --> End - **Same-Parent vs Different-Parent Detection**: The system differentiates between same-parent double production (colliding blocks from the same parent) and different-parent fork scenarios (divergent chain tips). - **Rate-Limited Warnings**: Uses a static counter and timestamp to suppress repeated warnings at the same block height to avoid log spam during sustained fork conditions. - **Timestamp Delta Analysis**: Calculates time differences between colliding blocks to help diagnose timing issues. -- **Witness Information Logging**: Logs witness names and timestamps for all colliding blocks to aid in forensic analysis. +- **validator Information Logging**: Logs validator names and timestamps for all colliding blocks to aid in forensic analysis. - **Parent Block ID Tracking**: Records previous block IDs to help analyze fork topology and collision origins. ```mermaid @@ -1030,7 +1030,7 @@ flowchart TD Start(["Block Height Collision"]) --> FetchBlocks["fetch_block_by_number(height)"] FetchBlocks --> CheckSize{"blocks.size() > 1?"} CheckSize --> |No| Return["No collision"] -CheckSize --> |Yes| ExtractInfo["Extract witness, timestamp, previous_id"] +CheckSize --> |Yes| ExtractInfo["Extract validator, timestamp, previous_id"] ExtractInfo --> SameParent{"all previous_ids identical?"} SameParent --> |Yes| DoubleProd["Same Parent - Possible Double Production"] SameParent --> |No| ForkScenario["Different Parents - Fork Scenario"] @@ -1083,8 +1083,8 @@ BatchApply --> Finalize["Finalize processing"] ### Validation Steps Enumeration and Use Cases Validation flags control which checks are performed during block and transaction validation: - skip_nothing: Perform all validations -- skip_witness_signature: Skip witness signature verification (used during reindex) -- skip_transaction_signatures: Skip transaction signatures (used by non-witness nodes) +- skip_witness_signature: Skip validator signature verification (used during reindex) +- skip_transaction_signatures: Skip transaction signatures (used by non-validator nodes) - skip_transaction_dupe_check: Skip duplicate transaction checks - skip_fork_db: Skip fork database checks - skip_block_size_check: Allow oversized blocks when generating locally @@ -1092,7 +1092,7 @@ Validation flags control which checks are performed during block and transaction - skip_authority_check: Skip authority checks - skip_merkle_check: Skip Merkle root verification - skip_undo_history_check: Skip undo history bounds -- skip_witness_schedule_check: Skip witness schedule validation +- skip_witness_schedule_check: Skip validator schedule validation - skip_validate_operations: Skip operation validation - skip_undo_block: Skip undo db on reindex - skip_block_log: Skip writing to block log (used in DLT mode) @@ -1169,7 +1169,7 @@ Match --> |No| Apply - Block log: Append-only storage with a secondary index enabling O(1) random access by block number. - DLT Block Log: Rolling window storage for DLT mode nodes, maintaining a configurable number of recent blocks. -- IRV advancement: When sufficient witness validations are collected, the system advances last irreversible block, commits the revision, writes blocks to appropriate log based on DLT mode, and updates dynamic global properties with reference fields. +- IRV advancement: When sufficient validator validations are collected, the system advances last irreversible block, commits the revision, writes blocks to appropriate log based on DLT mode, and updates dynamic global properties with reference fields. - **Enhanced Gap Logging**: Improved logging for DLT block log gaps during block processing to help diagnose synchronization issues with contextual information. ```mermaid @@ -1231,8 +1231,8 @@ These signals are used by plugins to react to blockchain events without tight co - **Enhanced Diagnostic Visibility**: Debug logging throughout critical code paths provides comprehensive visibility into database operations for improved troubleshooting and performance analysis. The debug_crash logging system includes markers for: -- push_block operations with witness information -- witness schedule updates and emergency consensus handling +- push_block operations with validator information +- validator schedule updates and emergency consensus handling - block production scheduling and execution - fund processing and block notification cycles - LIB advancement and fork database operations @@ -1258,21 +1258,21 @@ The debug_crash logging system includes markers for: ### Enhanced Block Production Debugging **New** - The debug-block-production configuration option provides detailed block production logging and monitoring: -- **Configuration Option**: The debug-block-production option is available in the witness plugin configuration with default value false. +- **Configuration Option**: The debug-block-production option is available in the Validator Plugin configuration with default value false. - **Runtime Control**: The option can be enabled/disabled at runtime through command-line configuration. - **Production Loop Monitoring**: Comprehensive logging for block production loop including entry/exit points, maybe_produce_block results, and scheduling operations. -- **Witness Production Tracking**: Detailed logging for witness production scheduling, slot determination, and block generation processes. -- **Emergency Consensus Monitoring**: Enhanced logging for emergency consensus mode operations including witness schedule overrides and hybrid production scenarios. +- **validator Production Tracking**: Detailed logging for validator production scheduling, slot determination, and block generation processes. +- **Emergency Consensus Monitoring**: Enhanced logging for emergency consensus mode operations including validator schedule overrides and hybrid production scenarios. **Section sources** -- [witness.cpp:159-160](file://plugins/witness/witness.cpp#L159-L160) -- [witness.cpp:228-233](file://plugins/witness/witness.cpp#L228-L233) -- [witness.cpp:338-340](file://plugins/witness/witness.cpp#L338-L340) -- [witness.cpp:356-357](file://plugins/witness/witness.cpp#L356-L357) -- [witness.cpp:403-405](file://plugins/witness/witness.cpp#L403-L405) -- [witness.cpp:411-412](file://plugins/witness/witness.cpp#L411-L412) -- [witness.cpp:416-417](file://plugins/witness/witness.cpp#L416-L417) -- [witness.cpp:418-419](file://plugins/witness/witness.cpp#L418-L419) +- [validator.cpp:159-160](file://plugins/validator/validator.cpp#L159-L160) +- [validator.cpp:228-233](file://plugins/validator/validator.cpp#L228-L233) +- [validator.cpp:338-340](file://plugins/validator/validator.cpp#L338-L340) +- [validator.cpp:356-357](file://plugins/validator/validator.cpp#L356-L357) +- [validator.cpp:403-405](file://plugins/validator/validator.cpp#L403-L405) +- [validator.cpp:411-412](file://plugins/validator/validator.cpp#L411-L412) +- [validator.cpp:416-417](file://plugins/validator/validator.cpp#L416-L417) +- [validator.cpp:418-419](file://plugins/validator/validator.cpp#L418-L419) ### Enhanced Stacktrace Crash Handlers **New** - The stacktrace crash handlers provide improved crash diagnostics and debugging experience: @@ -1334,15 +1334,15 @@ LogNoRecover --> SuppressWarning - **Enhanced Early Rejection Logic**: Gap-based decision system (≤100 gap deferred to fork_db, >100 gap rejected) for intelligent block rejection of far-ahead blocks with unknown parents to prevent unnecessary fork database operations and sync restart loops - **Enhanced Fork Database Exception Prevention**: Comprehensive mechanisms to prevent fork database exceptions through early rejection and proper dead fork detection - **Enhanced Memory Management**: Comprehensive logging of memory states before and after resizing operations for administrator visibility -- **Enhanced P2P Protection**: Operation guard integration in P2P plugin for safe concurrent access during block validation and witness key retrieval -- **Enhanced Witness Scheduling Safety**: Dual operation guard patterns in witness scheduling calculations to ensure thread safety during slot determination and witness validation +- **Enhanced P2P Protection**: Operation guard integration in P2P plugin for safe concurrent access during block validation and validator key retrieval +- **Enhanced validator Scheduling Safety**: Dual operation guard patterns in validator scheduling calculations to ensure thread safety during slot determination and validator validation - **Enhanced Multi-Layered Block Retrieval**: Hierarchical block fetching with systematic fallback mechanisms that check fork database when primary block log fails to locate required data - **Enhanced Last Irreversible Block Advancement**: Improved logic that falls back to fork database when block log lacks required data, maintaining data consistency - **Enhanced Collision Detection**: Sophisticated logging for block number collisions with scenario differentiation and rate-limiting - **Postponed Transaction Processing**: Automatic transaction queuing with time-based execution limits and smart recovery - **Enhanced Emergency Consensus**: Automatic recovery system with comprehensive logging and safety checks for network stall detection and recovery - **Enhanced Shared Memory Corruption Detection**: New shared_memory_corruption_exception type for structured error reporting during critical validation failures -- **Enhanced Auto-Recovery Integration**: Seamless integration with witness plugin for automatic recovery from shared memory corruption scenarios +- **Enhanced Auto-Recovery Integration**: Seamless integration with Validator Plugin for automatic recovery from shared memory corruption scenarios - **Enhanced Crash Debugging**: Comprehensive debug_crash logging throughout critical code paths for improved crash diagnostics and troubleshooting - **Enhanced Block Production Monitoring**: debug-block-production configuration option for detailed block production logging and monitoring - **Enhanced Stacktrace Crash Handlers**: Automatic stacktrace generation for crash diagnostics and improved debugging experience @@ -1379,9 +1379,9 @@ Valid --> |Yes| CalcTime["Calculate Time Since LIB"] CalcTime --> CheckTimeout{"Time >= Timeout?"} CheckTimeout --> |No| Normal CheckTimeout --> |Yes| Activate["Activate Emergency Mode"] -Activate --> CreateWitness["Create/Update Emergency Witness"] -CreateWitness --> ResetPenalties["Reset Witness Penalties"] -ResetPenalties --> OverrideSchedule["Override Witness Schedule"] +Activate --> CreateWitness["Create/Update Emergency validator"] +CreateWitness --> ResetPenalties["Reset validator Penalties"] +ResetPenalties --> OverrideSchedule["Override validator Schedule"] OverrideSchedule --> NotifyFork["Notify Fork DB"] NotifyFork --> LogActivation["Log Emergency Activation"] LogActivation --> Normal @@ -1395,13 +1395,13 @@ LogActivation --> Normal - [database.cpp:4334-4463](file://libraries/chain/database.cpp#L4334-L4463) - [config.hpp:111-118](file://libraries/protocol/include/graphene/protocol/config.hpp#L111-L118) -### Hybrid Witness Scheduling System -During emergency mode, the system implements a hybrid witness scheduling approach: +### Hybrid validator Scheduling System +During emergency mode, the system implements a hybrid validator scheduling approach: -- **Real Witness Priority**: Real witnesses maintain their scheduled slots during normal operation -- **Committee Replacement**: When real witnesses are unavailable (offline, shutdown, or missing signing keys), committee members automatically replace their slots +- **Real validator Priority**: Real validators maintain their scheduled slots during normal operation +- **Committee Replacement**: When real validators are unavailable (offline, shutdown, or missing signing keys), committee members automatically replace their slots - **Full Coverage**: Emergency schedule expands to cover all CHAIN_MAX_WITNESSES slots, ensuring continuous block production -- **Dynamic Adjustment**: Schedule updates dynamically based on real witness availability and network conditions +- **Dynamic Adjustment**: Schedule updates dynamically based on real validator availability and network conditions ```mermaid flowchart TD @@ -1409,8 +1409,8 @@ Start(["Schedule Update"]) --> CheckEmergency{"Emergency Active?"} CheckEmergency --> |No| NormalSchedule["Normal Schedule Update"] CheckEmergency --> |Yes| IterateSlots["Iterate All Schedule Slots"] IterateSlots --> CheckSlot{"Slot Available?"} -CheckSlot --> |Yes| KeepReal["Keep Real Witness"] -CheckSlot --> |No| ReplaceWithCommittee["Replace with Emergency Witness"] +CheckSlot --> |Yes| KeepReal["Keep Real validator"] +CheckSlot --> |No| ReplaceWithCommittee["Replace with Emergency validator"] ReplaceWithCommittee --> ExpandSchedule["Expand to Full Schedule"] ExpandSchedule --> SyncProps["Sync Props with Latest Median"] SyncProps --> CheckExit{"LIB > Start Block?"} @@ -1427,20 +1427,20 @@ LogDeactivation --> NormalSchedule **Section sources** - [database.cpp:2047-2144](file://libraries/chain/database.cpp#L2047-L2144) -### Emergency Witness Object Management -The system creates and manages a dedicated emergency witness object: +### Emergency validator Object Management +The system creates and manages a dedicated emergency validator object: -- **Emergency Witness Account**: Uses CHAIN_EMERGENCY_WITNESS_ACCOUNT (committee account) for emergency operations +- **Emergency validator Account**: Uses CHAIN_EMERGENCY_WITNESS_ACCOUNT (committee account) for emergency operations - **Public Key Management**: Assigns CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY for block signing during emergencies - **Properties Synchronization**: Copies current median chain properties to prevent skewing median computations - **Version Management**: Maintains current binary version and hardfork voting alignment -- **Penalty Reset**: Emergency witness operates independently of normal penalty systems +- **Penalty Reset**: Emergency validator operates independently of normal penalty systems ```mermaid flowchart TD -Start(["Emergency Activation"]) --> CheckWitness{"Emergency Witness Exists?"} -CheckWitness --> |No| CreateWitness["Create Emergency Witness"] -CheckWitness --> |Yes| UpdateWitness["Update Existing Witness"] +Start(["Emergency Activation"]) --> CheckWitness{"Emergency validator Exists?"} +CheckWitness --> |No| CreateWitness["Create Emergency validator"] +CheckWitness --> |Yes| UpdateWitness["Update Existing validator"] CreateWitness --> SetKey["Set Emergency Public Key"] SetKey --> SyncProps["Copy Median Properties"] SyncProps --> SyncVersion["Sync Version and Votes"] @@ -1450,7 +1450,7 @@ SyncVersion --> ResetPenalties["Reset Penalties"] ResetPenalties --> RemoveExpire["Remove Penalty Expires"] RemoveExpire --> OverrideSchedule["Override Schedule"] OverrideSchedule --> NotifyFork["Notify Fork DB"] -NotifyFork --> LogCreate["Log Witness Creation"] +NotifyFork --> LogCreate["Log validator Creation"] LogCreate --> Continue["Continue Operation"] ``` @@ -1488,19 +1488,19 @@ LogDeactivation --> Wait **Section sources** - [database.cpp:2125-2142](file://libraries/chain/database.cpp#L2125-L2142) -### Witness Penalty Handling During Emergencies -Emergency mode includes special handling for witness penalties: +### validator Penalty Handling During Emergencies +Emergency mode includes special handling for validator penalties: -- **Offline Witness Protection**: During emergency mode, penalties for offline witnesses are not applied +- **Offline validator Protection**: During emergency mode, penalties for offline validators are not applied - **Hybrid Schedule Impact**: Committee members filling slots still count as "missed" blocks for normal penalty calculations -- **Recovery Prevention**: Prevents offline witnesses from accumulating penalties that could lead to permanent shutdown -- **Network Recovery**: Ensures offline witnesses can recover and resume participation after emergency mode ends +- **Recovery Prevention**: Prevents offline validators from accumulating penalties that could lead to permanent shutdown +- **Network Recovery**: Ensures offline validators can recover and resume participation after emergency mode ends ```mermaid flowchart TD -Start(["Witness Missed Blocks"]) --> CheckEmergency{"Emergency Active?"} +Start(["validator Missed Blocks"]) --> CheckEmergency{"Emergency Active?"} CheckEmergency --> |No| ApplyPenalties["Apply Normal Penalties"] -CheckEmergency --> |Yes| CheckOffline{"Is Offline Witness?"} +CheckEmergency --> |Yes| CheckOffline{"Is Offline validator?"} CheckOffline --> |No| ApplyPenalties CheckOffline --> |Yes| CheckProducer{"Is Producer?"} CheckProducer --> |Yes| ApplyPenalties @@ -1527,11 +1527,11 @@ The emergency consensus system includes comprehensive LIB monitoring: ### Enhanced Error Logging Throughout Consensus Process **New** - The emergency consensus implementation includes comprehensive error logging and critical error handling: -- **Critical Error Logging**: All emergency consensus activation and deactivation events are logged with detailed context including block numbers, timestamps, and witness information +- **Critical Error Logging**: All emergency consensus activation and deactivation events are logged with detailed context including block numbers, timestamps, and validator information - **Safety Check Logging**: Extensive logging of safety checks to prevent false activations and deadlocks - **Transition Logging**: Detailed logging of emergency mode entry and exit conditions -- **Witness Management Logging**: Comprehensive logging of emergency witness creation, updates, and penalty management -- **Schedule Override Logging**: Detailed logging of witness schedule overrides and hybrid scheduling decisions +- **validator Management Logging**: Comprehensive logging of emergency validator creation, updates, and penalty management +- **Schedule Override Logging**: Detailed logging of validator schedule overrides and hybrid scheduling decisions - **LIB Monitoring Logging**: Continuous logging of LIB timestamp analysis and recovery detection - **Error Recovery Logging**: Logging of error recovery mechanisms and fallback procedures @@ -1541,17 +1541,17 @@ The enhanced error logging system ensures that operators have comprehensive visi **New** - The database now includes comprehensive shared memory corruption detection and automatic recovery mechanisms: - **Structured Exception Handling**: New shared_memory_corruption_exception type replaces direct assertion failures with structured exception handling for critical validation failures. -- **Enhanced Witness Validation**: Graceful error handling for witness account validation with detailed logging and structured exception reporting when witness accounts are missing from database. -- **Auto-Recovery Integration**: Seamless integration with witness plugin for automatic recovery from shared memory corruption scenarios. +- **Enhanced validator Validation**: Graceful error handling for validator account validation with detailed logging and structured exception reporting when validator accounts are missing from database. +- **Auto-Recovery Integration**: Seamless integration with Validator Plugin for automatic recovery from shared memory corruption scenarios. - **Plugin-Level Recovery**: Comprehensive auto-recovery system in plugin.cpp that can automatically recover from snapshots when corruption is detected. -- **Structured Error Reporting**: Detailed logging of corruption detection events with comprehensive context including witness information, signing keys, and memory state. +- **Structured Error Reporting**: Detailed logging of corruption detection events with comprehensive context including validator information, signing keys, and memory state. ```mermaid flowchart TD -Start(["Shared Memory Corruption Detection"]) --> DetectCorruption["Detect Missing Witness Account"] +Start(["Shared Memory Corruption Detection"]) --> DetectCorruption["Detect Missing validator Account"] DetectCorruption --> LogCritical["Log Critical Error Details"] LogCritical --> ThrowException["Throw shared_memory_corruption_exception"] -ThrowException --> CatchInWitness["Catch in witness plugin"] +ThrowException --> CatchInWitness["Catch in Validator Plugin"] CatchInWitness --> AttemptRecovery["Attempt Auto-Recovery"] AttemptRecovery --> FindSnapshot["Find Latest Snapshot"] FindSnapshot --> CloseDatabase["Close Corrupted Database"] @@ -1564,14 +1564,14 @@ ResumeOperation --> End(["Complete"]) - [database.cpp:1680-1693](file://libraries/chain/database.cpp#L1680-L1693) - [database.cpp:3224-3236](file://libraries/chain/database.cpp#L3224-L3236) - [database.cpp:3272-3284](file://libraries/chain/database.cpp#L3272-L3284) -- [witness.cpp:738-742](file://plugins/witness/witness.cpp#L738-L742) +- [validator.cpp:738-742](file://plugins/validator/validator.cpp#L738-L742) - [plugin.cpp:760-770](file://plugins/chain/plugin.cpp#L760-L770) **Section sources** - [database.cpp:1680-1693](file://libraries/chain/database.cpp#L1680-L1693) - [database.cpp:3224-3236](file://libraries/chain/database.cpp#L3224-L3236) - [database.cpp:3272-3284](file://libraries/chain/database.cpp#L3272-L3284) -- [witness.cpp:738-742](file://plugins/witness/witness.cpp#L738-L742) +- [validator.cpp:738-742](file://plugins/validator/validator.cpp#L738-L742) - [plugin.cpp:760-770](file://plugins/chain/plugin.cpp#L760-L770) ## Dependency Analysis @@ -1588,8 +1588,8 @@ The database depends on: - **Enhanced Early Rejection Logic**: Gap-based decision system (≤100 gap deferred to fork_db, >100 gap rejected) for intelligent block validation with early rejection strategies for blocks far ahead with unknown parents - **Enhanced Fork Database Exception Prevention**: Comprehensive mechanisms to prevent fork database exceptions through early rejection and proper dead fork detection - **Enhanced Memory Management**: Comprehensive logging system for shared memory allocation with detailed state reporting, plus deferred resize operations -- **Enhanced P2P Plugin Protection**: Operation guard integration in P2P plugin for safe concurrent access during block validation and witness key retrieval -- **Enhanced Operation Guard System**: Comprehensive concurrent access protection using operation_guard RAII pattern, dual operation guard patterns for witness scheduling safety, and resize barrier mechanisms +- **Enhanced P2P Plugin Protection**: Operation guard integration in P2P plugin for safe concurrent access during block validation and validator key retrieval +- **Enhanced Operation Guard System**: Comprehensive concurrent access protection using operation_guard RAII pattern, dual operation guard patterns for validator scheduling safety, and resize barrier mechanisms - **Enhanced Multi-Layered Block Retrieval**: Systematic fallback mechanisms for critical block data retrieval across multiple storage layers - **Enhanced Last Irreversible Block Advancement**: Enhanced fallback logic for LIB advancement when primary storage fails - **Enhanced Emergency Consensus**: Automatic recovery system with comprehensive logging and safety checks for network stall detection and recovery @@ -1664,7 +1664,7 @@ DB --> DLTGAP["enhanced DLT gap recovery system"] - **Enhanced Memory Management**: Comprehensive logging provides administrators with detailed visibility into memory usage patterns, enabling proactive capacity planning and performance optimization. - **Enhanced Error Handling**: Graceful handling of shared memory exhaustion prevents peer disconnections and maintains network connectivity during memory pressure situations. - **Enhanced Operation Guard Performance**: The operation guard RAII pattern provides automatic concurrent access protection with minimal overhead, ensuring thread safety without significant performance impact. -- **Enhanced Dual Guard Patterns**: Systematic implementation of dual operation guards in witness scheduling provides comprehensive thread safety with optimized performance characteristics. +- **Enhanced Dual Guard Patterns**: Systematic implementation of dual operation guards in validator scheduling provides comprehensive thread safety with optimized performance characteristics. - **Enhanced P2P Concurrent Safety**: Operation guard protection in P2P plugin ensures safe concurrent access during block validation with minimal performance overhead. - **Enhanced Resize Barrier Efficiency**: Enhanced resize barrier mechanisms provide comprehensive concurrent access protection during memory resizing with optimized performance characteristics. - **Enhanced Multi-Layered Block Retrieval**: Systematic fallback mechanisms eliminate single points of failure and improve system reliability without significant performance impact. @@ -1675,7 +1675,7 @@ DB --> DLTGAP["enhanced DLT gap recovery system"] - **Enhanced Thread-Safe Memory Operations**: Proper lock management during memory resize operations ensures data consistency and prevents performance degradation from thread contention. - **Enhanced P2P Sync Restart Prevention**: The enhanced early rejection logic prevents sync restart loops during snapshot imports, improving synchronization performance and reducing network overhead. - **Enhanced Shared Memory Corruption Detection**: Structured exception handling provides detailed error context without significant performance impact during critical validation failures. -- **Enhanced Auto-Recovery Performance**: Seamless integration with witness plugin enables rapid recovery from shared memory corruption without significant downtime. +- **Enhanced Auto-Recovery Performance**: Seamless integration with Validator Plugin enables rapid recovery from shared memory corruption without significant downtime. - **Enhanced Crash Debugging Overhead**: The debug_crash logging system adds minimal overhead during normal operation while providing comprehensive debugging capabilities when enabled. - **Enhanced Block Production Monitoring**: The debug-block-production option provides detailed monitoring capabilities with minimal performance impact. - **Enhanced Stacktrace Performance**: Stacktrace crash handlers add minimal overhead and provide significant debugging benefits for crash diagnostics. @@ -1696,7 +1696,7 @@ Common issues and remedies: - **Enhanced P2P Sync Restart Loops**: Verify that the enhanced early rejection logic is working correctly to prevent sync restart loops during snapshot imports and normal operation. - **Enhanced Emergency Consensus Logging**: Verify that critical error logs are being generated and that emergency mode activation/deactivation events are properly recorded. - **Enhanced Safety Check Failures**: Monitor emergency consensus safety checks to ensure they're functioning correctly and preventing false activations. -- **Enhanced Witness Management Problems**: Verify that emergency witness objects are being created and updated correctly during emergency mode activation. +- **Enhanced validator Management Problems**: Verify that emergency validator objects are being created and updated correctly during emergency mode activation. - **Enhanced Deferred Memory Resize Issues**: Monitor the new deferred resize mechanism to ensure it's properly deferring operations until safe points and applying them correctly. - **Enhanced Thread Safety Problems**: Verify that memory resize operations are not causing race conditions or stale pointer issues during concurrent access. - **Enhanced Performance Degradation**: Check if the deferred memory resize mechanism is causing unexpected delays or if memory operations are blocking other threads. @@ -1706,18 +1706,18 @@ Common issues and remedies: - **Enhanced Dead Fork Detection**: Check that the enhanced fork database properly throws unlinkable_block_exception for blocks from dead forks to prevent wasted processing resources. - **Enhanced Emergency Consensus Tie-Breaking**: Verify that deterministic hash-based tie-breaking is working correctly during emergency mode to ensure consistent block selection across all nodes. - **Enhanced Operation Guard Issues**: Monitor operation guard functionality to ensure concurrent access protection is working correctly during high-load scenarios. -- **Enhanced Dual Guard Pattern Problems**: Verify that dual operation guards are properly protecting witness scheduling calculations and preventing race conditions. +- **Enhanced Dual Guard Pattern Problems**: Verify that dual operation guards are properly protecting validator scheduling calculations and preventing race conditions. - **Enhanced P2P Concurrent Access Issues**: Check that operation guard protection is working correctly in P2P plugin for safe concurrent access during block validation. - **Enhanced Resize Barrier Failures**: Monitor resize barrier mechanisms to ensure they're properly pausing all database operations during memory resizing. - **Enhanced Concurrent Resize Safety**: Verify that resize barrier mechanisms are preventing stale pointer issues and data corruption during memory operations. - **Enhanced Multi-Layered Block Retrieval Issues**: Monitor the new fallback mechanisms to ensure they're properly checking fork database when primary block log fails to locate required data. - **Enhanced Last Irreversible Block Advancement Problems**: Verify that the enhanced fallback logic is working correctly when block log lacks required data. - **Enhanced Emergency Mode Activation**: Monitor emergency consensus activation logs and verify LIB timestamp analysis is working correctly. -- **Enhanced Hybrid Schedule Issues**: Verify that emergency witness is properly replacing unavailable witnesses during network recovery. +- **Enhanced Hybrid Schedule Issues**: Verify that emergency validator is properly replacing unavailable validators during network recovery. - **Enhanced Emergency Mode Deactivation**: Check that LIB advancement is properly detected to trigger emergency mode termination. -- **Enhanced Witness Penalty Problems**: During emergency mode, verify that offline witness penalties are properly bypassed to prevent network recovery issues. +- **Enhanced validator Penalty Problems**: During emergency mode, verify that offline validator penalties are properly bypassed to prevent network recovery issues. - **Enhanced Shared Memory Corruption Detection**: Monitor shared_memory_corruption_exception logging to ensure critical validation failures are properly reported. -- **Enhanced Auto-Recovery Integration**: Verify that witness plugin auto-recovery is properly integrated with plugin-level recovery system for seamless corruption handling. +- **Enhanced Auto-Recovery Integration**: Verify that Validator Plugin auto-recovery is properly integrated with plugin-level recovery system for seamless corruption handling. - **Enhanced Auto-Recovery Performance**: Monitor auto-recovery performance to ensure rapid recovery from shared memory corruption without significant downtime. - **Enhanced Crash Debugging Issues**: Verify that debug_crash logging is working correctly and providing comprehensive debugging information when enabled. - **Enhanced Block Production Monitoring Problems**: Check that debug-block-production option is properly configured and providing detailed block production logging. @@ -1751,7 +1751,7 @@ Common issues and remedies: - [database.cpp:5616-5635](file://libraries/chain/database.cpp#L5616-L5635) ## Conclusion -The Database Management system provides a robust, event-driven, and efficient state persistence layer for the VIZ blockchain with enhanced DLT mode support, emergency consensus implementation, operation guard integration, improved error handling, and comprehensive crash debugging capabilities. It integrates chainbase for persistent storage with comprehensive concurrent access protection, fork_database for reversible blocks, block_log for immutable history, and dlt_block_log for rolling window storage in DLT mode. Through configurable validation flags, checkpointing, memory management, DLT mode detection with proper setter implementation, enhanced block fetching logic with DLT mode awareness, improved gap logging, the new `_dlt_gap_logged` flag mechanism for intelligent warning suppression, comprehensive operation guard implementation for concurrent access protection, dual operation guard patterns for witness scheduling safety, enhanced P2P plugin block validation with operation guard protection, systematic implementation of resize barrier mechanisms, comprehensive debug_crash logging throughout critical code paths, debug-block-production configuration option for detailed block production monitoring, and enhanced stacktrace crash handlers for improved crash diagnostics, it supports fast synchronization, reliable block processing, conditional block log operations, and extensibility via observer signals. +The Database Management system provides a robust, event-driven, and efficient state persistence layer for the VIZ blockchain with enhanced DLT mode support, emergency consensus implementation, operation guard integration, improved error handling, and comprehensive crash debugging capabilities. It integrates chainbase for persistent storage with comprehensive concurrent access protection, fork_database for reversible blocks, block_log for immutable history, and dlt_block_log for rolling window storage in DLT mode. Through configurable validation flags, checkpointing, memory management, DLT mode detection with proper setter implementation, enhanced block fetching logic with DLT mode awareness, improved gap logging, the new `_dlt_gap_logged` flag mechanism for intelligent warning suppression, comprehensive operation guard implementation for concurrent access protection, dual operation guard patterns for validator scheduling safety, enhanced P2P plugin block validation with operation guard protection, systematic implementation of resize barrier mechanisms, comprehensive debug_crash logging throughout critical code paths, debug-block-production configuration option for detailed block production monitoring, and enhanced stacktrace crash handlers for improved crash diagnostics, it supports fast synchronization, reliable block processing, conditional block log operations, and extensibility via observer signals. **Updated** - The system now includes comprehensive database robustness improvements including refined gap-based decision system for unlinkable blocks, enhanced exception handling infrastructure that preserves derived exception types during rethrow operations, comprehensive fork database management with improved diagnostic capabilities and enhanced logging for fork recovery operations, and sophisticated early rejection logic for blocks far ahead with unknown parents. The enhanced fork management logic with improved early rejection mechanisms provides better handling of blocks far ahead with unknown parents, significantly improving the efficiency of the synchronization process and reducing the likelihood of encountering unlinkable blocks that would require peer soft-banning or sync restarts. The enhanced multi-layered block retrieval system represents a significant advancement in database reliability and fault tolerance. The improved last irreversible block advancement logic demonstrates the system's commitment to data consistency and availability. The enhanced exception handling infrastructure ensures that derived exception types are properly preserved during rethrow operations, improving debugging and troubleshooting capabilities. The enhanced memory management system provides comprehensive logging capabilities that offer administrators detailed visibility into memory usage patterns during blockchain operation, while the deferred shared memory resize mechanism significantly improves efficiency during high-load scenarios by preventing race conditions and stale pointer issues through proper thread synchronization and lock management. @@ -1767,7 +1767,7 @@ The enhanced exception handling infrastructure represents a fundamental improvem **Enhanced P2P Synchronization** - The enhanced P2P synchronization system with improved unlinkable block classification and sync restart prevention represents a significant improvement in network reliability and performance. The P2P layer now properly distinguishes between dead fork blocks (at or below head) and far-ahead blocks that slipped past early rejection, enabling appropriate handling for each scenario. The dead fork handling with soft-banning prevents continued transmission of stale blocks, while the far-ahead block handling with sync restart prevention allows sequential block fetching without causing network stalls. The integration with deferred resize scenarios ensures that P2P operations can continue smoothly even when memory pressure occurs during block processing. -**Enhanced Operation Guard Implementation** - The comprehensive operation guard system represents a fundamental improvement in concurrent access protection throughout the database management system. The systematic implementation of operation_guard RAII pattern provides automatic concurrent access protection across all critical sections, while the dual operation guard patterns in witness scheduling calculations ensure thread safety during complex slot determination operations. The integration of operation guards in P2P plugin block validation protects witness key retrieval operations from concurrent memory modifications, and the comprehensive resize barrier mechanisms prevent data corruption during memory resizing operations. These enhancements ensure that the database management system can handle high-load scenarios safely and reliably while maintaining data consistency and preventing race conditions that could lead to system instability or data corruption. +**Enhanced Operation Guard Implementation** - The comprehensive operation guard system represents a fundamental improvement in concurrent access protection throughout the database management system. The systematic implementation of operation_guard RAII pattern provides automatic concurrent access protection across all critical sections, while the dual operation guard patterns in validator scheduling calculations ensure thread safety during complex slot determination operations. The integration of operation guards in P2P plugin block validation protects validator key retrieval operations from concurrent memory modifications, and the comprehensive resize barrier mechanisms prevent data corruption during memory resizing operations. These enhancements ensure that the database management system can handle high-load scenarios safely and reliably while maintaining data consistency and preventing race conditions that could lead to system instability or data corruption. **Enhanced Multi-Layered Block Retrieval System** - The new multi-layered block retrieval system represents a significant advancement in database reliability and fault tolerance. By implementing systematic fallback mechanisms that check fork database when primary block log fails to locate required data, the database ensures consistent behavior across different block logging configurations. This approach eliminates single points of failure and provides improved resilience against storage layer issues, network interruptions, and other operational challenges. The hierarchical retrieval strategy prioritizes current/main branch blocks in the fork database, then checks the primary block log for irreversible blocks, and finally uses the DLT block log as a fallback in DLT mode. This ensures that block retrieval works reliably regardless of which storage layer contains the requested data, providing improved fault tolerance and system reliability. @@ -1775,13 +1775,13 @@ The enhanced exception handling infrastructure represents a fundamental improvem **Enhanced Emergency Consensus** - The enhanced emergency consensus implementation provides comprehensive logging and safety checks for network stall detection and recovery. The automatic recovery system with detailed logging and safety mechanisms ensures that operators have complete visibility into emergency operations without impacting performance. The enhanced safety check optimization prevents false activations while ensuring network stability, making the system more reliable and suitable for production environments where network reliability is critical. -**Enhanced Shared Memory Corruption Detection** - The introduction of the new shared_memory_corruption_exception type represents a significant advancement in error handling robustness. This structured exception handling approach replaces direct assertion failures with comprehensive error reporting, enabling better debugging and troubleshooting capabilities. The enhanced witness account validation with graceful error handling during block acceptance and generation processes ensures that critical validation failures are properly detected and reported with detailed context information. +**Enhanced Shared Memory Corruption Detection** - The introduction of the new shared_memory_corruption_exception type represents a significant advancement in error handling robustness. This structured exception handling approach replaces direct assertion failures with comprehensive error reporting, enabling better debugging and troubleshooting capabilities. The enhanced validator account validation with graceful error handling during block acceptance and generation processes ensures that critical validation failures are properly detected and reported with detailed context information. -**Enhanced Auto-Recovery Integration** - The seamless integration with witness plugin for automatic recovery from shared memory corruption scenarios represents a major improvement in system reliability and uptime. The comprehensive auto-recovery system in plugin.cpp provides rapid recovery from corruption scenarios with minimal downtime, while the structured error reporting ensures that operators have complete visibility into recovery operations. This integration makes the database management system more resilient to critical hardware and software failures, improving overall system reliability and operator confidence. +**Enhanced Auto-Recovery Integration** - The seamless integration with Validator Plugin for automatic recovery from shared memory corruption scenarios represents a major improvement in system reliability and uptime. The comprehensive auto-recovery system in plugin.cpp provides rapid recovery from corruption scenarios with minimal downtime, while the structured error reporting ensures that operators have complete visibility into recovery operations. This integration makes the database management system more resilient to critical hardware and software failures, improving overall system reliability and operator confidence. **Enhanced Crash Debugging Capabilities** - The comprehensive crash debugging system with debug_crash logging throughout critical code paths represents a significant advancement in troubleshooting and diagnostics capabilities. The extensive debug markers (DEBUG_CRASH) provide detailed visibility into database operations, while the debug-block-production configuration option enables comprehensive monitoring of block production processes. The enhanced stacktrace crash handlers provide automatic crash diagnostics with detailed stacktrace information, significantly improving the debugging experience and reducing troubleshooting time for critical system issues. -**Enhanced Block Production Monitoring** - The debug-block-production configuration option provides operators with detailed visibility into block production processes, enabling comprehensive monitoring and troubleshooting of production-related issues. The integration with witness plugin ensures that block production logging is comprehensive and actionable, while the detailed logging markers throughout the production pipeline provides granular visibility into production scheduling, slot determination, and block generation processes. +**Enhanced Block Production Monitoring** - The debug-block-production configuration option provides operators with detailed visibility into block production processes, enabling comprehensive monitoring and troubleshooting of production-related issues. The integration with Validator Plugin ensures that block production logging is comprehensive and actionable, while the detailed logging markers throughout the production pipeline provides granular visibility into production scheduling, slot determination, and block generation processes. **Enhanced Stacktrace Crash Handlers** - The enhanced stacktrace crash handlers provide automatic crash diagnostics with detailed stacktrace information, significantly improving the debugging experience for fatal errors. The integration with the crash debugging system ensures that operators have complete visibility into system crashes and can quickly identify and resolve critical issues through comprehensive stacktrace analysis and crash diagnostics. diff --git a/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Chain Library/Fork Resolution and Consensus.md b/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Chain Library/Fork Resolution and Consensus.md index f4a3ee2f82..32c5020a11 100644 --- a/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Chain Library/Fork Resolution and Consensus.md +++ b/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Chain Library/Fork Resolution and Consensus.md @@ -1,4 +1,4 @@ -# Fork Resolution and Consensus +# Fork Resolution and Consensus **Referenced Files in This Document** @@ -10,7 +10,7 @@ - [dlt_block_log.hpp](file://libraries/chain/include/graphene/chain/dlt_block_log.hpp) - [dlt_block_log.cpp](file://libraries/chain/dlt_block_log.cpp) - [p2p_plugin.cpp](file://plugins/p2p/p2p_plugin.cpp) -- [witness.cpp](file://plugins/witness/witness.cpp) +- [validator.cpp](file://plugins/validator/validator.cpp) - [config.hpp](file://libraries/protocol/include/graphene/protocol/config.hpp) - [12.hf](file://libraries/chain/hardfork.d/12.hf) @@ -50,9 +50,9 @@ The fork resolution and consensus logic spans several core files with enhanced e - database.hpp/cpp: Blockchain database integration, block pushing with early rejection logic, chain reorganization, DLT mode management, sophisticated block validation, vote-weighted fork comparison, and **NEW**: fork database access for diagnostic monitoring - block_log.hpp: Append-only persistence of blocks for recovery and irreversible state - dlt_block_log.hpp/cpp: Separate rolling block log for DLT nodes to serve recent irreversible blocks to P2P peers -- witness.cpp: Witness scheduling integration with emergency mode awareness, fork collision handling, two-level fork collision resolution, stuck-head timeout mechanism, and automatic chain linking +- validator.cpp: validator scheduling integration with emergency mode awareness, fork collision handling, two-level fork collision resolution, stuck-head timeout mechanism, and automatic chain linking - p2p_plugin.cpp: **NEW**: Real-time monitoring of fork database storage statistics with comprehensive analytics -- config.hpp: Emergency consensus configuration constants including timeout settings and emergency witness parameters +- config.hpp: Emergency consensus configuration constants including timeout settings and emergency validator parameters - 12.hf: Hardfork configuration defining HF12 parameters and activation time ```mermaid @@ -103,7 +103,7 @@ PMON --> DIAG - [fork_database.cpp:80-87](file://libraries/chain/fork_database.cpp#L80-L87) - [database.cpp:1204-1270](file://libraries/chain/database.cpp#L1204-L1270) - [p2p_plugin.cpp:739-760](file://plugins/p2p/p2p_plugin.cpp#L739-L760) -- [witness.cpp:521-544](file://plugins/witness/witness.cpp#L521-L544) +- [validator.cpp:521-544](file://plugins/validator/validator.cpp#L521-L544) **Section sources** - [fork_database.hpp:1-168](file://libraries/chain/include/graphene/chain/fork_database.hpp#L1-L168) @@ -112,7 +112,7 @@ PMON --> DIAG - [database.cpp:1-6669](file://libraries/chain/database.cpp#L1-L6669) - [dlt_block_log.hpp:1-76](file://libraries/chain/include/graphene/chain/dlt_block_log.hpp#L1-L76) - [dlt_block_log.cpp:1-454](file://libraries/chain/dlt_block_log.cpp#L1-L454) -- [witness.cpp:1-697](file://plugins/witness/witness.cpp#L1-L697) +- [validator.cpp:1-697](file://plugins/validator/validator.cpp#L1-L697) - [p2p_plugin.cpp:735-771](file://plugins/p2p/p2p_plugin.cpp#L735-L771) - [config.hpp:110-124](file://libraries/protocol/include/graphene/protocol/config.hpp#L110-L124) - [12.hf:1-7](file://libraries/chain/hardfork.d/12.hf#L1-L7) @@ -122,9 +122,9 @@ PMON --> DIAG - database: Integrates fork resolution into block application with sophisticated early rejection logic, comprehensive block validation, performs chain reorganization when a better fork emerges, manages DLT mode for snapshot-based nodes, implements emergency consensus mode activation/deactivation, provides vote-weighted fork comparison for HF12, and **NEW**: exposes fork database access for diagnostic monitoring - block_log: Provides persistent storage for blocks, enabling recovery and serving as the source of irreversible blocks - dlt_block_log: Separate rolling block log for DLT nodes that maintains a sliding window of recent irreversible blocks for P2P synchronization -- witness: Integrates witness scheduling with emergency mode awareness, handles fork collisions through two-level resolution system, manages stuck-head timeout mechanism, implements HF12 fork collision resolution, and provides automatic chain linking when parent blocks arrive +- validator: Integrates validator scheduling with emergency mode awareness, handles fork collisions through two-level resolution system, manages stuck-head timeout mechanism, implements HF12 fork collision resolution, and provides automatic chain linking when parent blocks arrive - **NEW**: P2P monitoring system: Real-time monitoring of fork database storage statistics including linked/unlinked index sizes, minimum/maximum block numbers, and comprehensive storage health metrics -- emergency consensus: Implements timeout-based emergency mode activation, hybrid witness scheduling, and deterministic tie-breaking mechanisms +- emergency consensus: Implements timeout-based emergency mode activation, hybrid validator scheduling, and deterministic tie-breaking mechanisms - compare_fork_branches: New HF12 function that performs vote-weighted fork comparison with +10% bonus for longer chains - remove_blocks_by_number: New function that removes all blocks at a specific height to prevent memory bloat from dead-fork blocks @@ -135,10 +135,10 @@ Key responsibilities: - Reorganize the chain when a higher fork becomes active with better error recovery, emergency mode integration, and fork collision resolution - Manage DLT mode for snapshot-based nodes with automatic fork database seeding capabilities - Implement emergency consensus mode activation based on timeout thresholds -- Provide hybrid witness scheduling during emergency periods with deterministic tie-breaking +- Provide hybrid validator scheduling during emergency periods with deterministic tie-breaking - Persist irreversible blocks to both block_log and dlt_block_log with enhanced reliability and emergency mode awareness - Serve recent blocks to P2P peers through dlt_block_log for faster synchronization -- Handle emergency witness account creation and key management for consensus recovery +- Handle emergency validator account creation and key management for consensus recovery - Distinguish between different types of invalid blocks and handle them appropriately to prevent system degradation - **New**: Provide comprehensive diagnostic accessors for monitoring fork database storage statistics including linked/unlinked index sizes and block number ranges - **New**: Integrate diagnostic accessors into P2P monitoring system for real-time storage analytics @@ -152,7 +152,7 @@ Key responsibilities: - [fork_database.cpp:33-92](file://libraries/chain/fork_database.cpp#L33-L92) - [database.cpp:1223-1267](file://libraries/chain/database.cpp#L1223-L1267) - [dlt_block_log.hpp:13-33](file://libraries/chain/include/graphene/chain/dlt_block_log.hpp#L13-L33) -- [witness.cpp:521-544](file://plugins/witness/witness.cpp#L521-L544) +- [validator.cpp:521-544](file://plugins/validator/validator.cpp#L521-L544) - [p2p_plugin.cpp:739-760](file://plugins/p2p/p2p_plugin.cpp#L739-L760) ## Architecture Overview @@ -163,7 +163,7 @@ sequenceDiagram participant Net as "Network" participant DB as "database.cpp" participant FDB as "fork_database.cpp" -participant WIT as "witness.cpp" +participant WIT as "validator.cpp" participant BL as "block_log.hpp" participant DLTL as "dlt_block_log.cpp" participant MON as "P2P Monitoring" @@ -179,7 +179,7 @@ else "Valid block" FDB-->>DB : "Insert and _push_next" DB->>DB : "Check emergency consensus timeout" alt "Emergency mode activated" -DB->>WIT : "Override witness schedule to emergency witness" +DB->>WIT : "Override validator schedule to emergency validator" DB->>FDB : "set_emergency_mode(true)" DB->>DB : "Skip LIB advancement during emergency" else "Normal mode" @@ -201,7 +201,7 @@ MON->>MON : "analyze fork storage statistics" **Diagram sources** - [database.cpp:1204-1270](file://libraries/chain/database.cpp#L1204-L1270) - [fork_database.cpp:80-87](file://libraries/chain/fork_database.cpp#L80-L87) -- [witness.cpp:521-544](file://plugins/witness/witness.cpp#L521-L544) +- [validator.cpp:521-544](file://plugins/validator/validator.cpp#L521-L544) - [dlt_block_log.cpp:336-340](file://libraries/chain/dlt_block_log.cpp#L336-L340) - [p2p_plugin.cpp:739-760](file://plugins/p2p/p2p_plugin.cpp#L739-L760) @@ -667,9 +667,9 @@ end - Mechanism: Gap logging suppression prevents log flooding while maintaining operational awareness - **New Scenario F**: Emergency consensus mode activation - Behavior: When no blocks are produced for CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC seconds, emergency mode activates with deterministic tie-breaking - - Mechanism: Emergency witness takes over all witness slots, emergency mode flag is set, and deterministic hash-based tie-breaking ensures consensus stability + - Mechanism: Emergency validator takes over all validator slots, emergency mode flag is set, and deterministic hash-based tie-breaking ensures consensus stability - **New Scenario G**: Emergency consensus mode deactivation - - Behavior: When LIB advances past emergency_consensus_start_block, emergency mode is deactivated and normal witness scheduling resumes + - Behavior: When LIB advances past emergency_consensus_start_block, emergency mode is deactivated and normal validator scheduling resumes - Mechanism: Emergency mode flag is cleared and fork database is notified of emergency mode termination - **New Scenario H**: Sophisticated early rejection in action - Behavior: Database rejects blocks that are already applied, on different forks, or far ahead with unknown parents to prevent infinite sync loops @@ -679,9 +679,9 @@ end - Mechanism: Comprehensive duplicate detection prevents redundant CPU usage and improves synchronization reliability - **New Scenario J**: HF12 fork collision resolution - Behavior: When competing blocks appear at the same height, database uses vote-weighted comparison to determine the stronger fork - - Mechanism: `compare_fork_branches()` calculates total vote weight per witness, applies +10% bonus to longer chain, and resolves ties deterministically + - Mechanism: `compare_fork_branches()` calculates total vote weight per validator, applies +10% bonus to longer chain, and resolves ties deterministically - **New Scenario K**: Stuck-head timeout mechanism - - Behavior: After 21 consecutive deferrals (one full witness round), database removes stale competing blocks and produces on the canonical chain + - Behavior: After 21 consecutive deferrals (one full validator round), database removes stale competing blocks and produces on the canonical chain - Mechanism: `fork_collision_defer_count_` tracks deferral attempts, `remove_blocks_by_number()` clears stale blocks, and timeout ensures network progress - **New Scenario L**: Automatic stale fork pruning - Behavior: Database periodically removes stale competing blocks from dead forks to prevent memory bloat and improve performance @@ -713,21 +713,21 @@ end - [database.cpp:1300-1399](file://libraries/chain/database.cpp#L1300-L1399) - [database.cpp:2125-2142](file://libraries/chain/database.cpp#L2125-L2142) - [database.cpp:1223-1267](file://libraries/chain/database.cpp#L1223-L1267) -- [witness.cpp:597-612](file://plugins/witness/witness.cpp#L597-L612) +- [validator.cpp:597-612](file://plugins/validator/validator.cpp#L597-L612) - [fork_database.cpp:269-274](file://libraries/chain/fork_database.cpp#L269-L274) - [p2p_plugin.cpp:739-760](file://plugins/p2p/p2p_plugin.cpp#L739-L760) ## Emergency Consensus Recovery System ### Emergency Consensus Mode Activation -The emergency consensus mode activates automatically when no blocks are produced for more than CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC seconds (1 hour by default). This mechanism ensures blockchain continuity during extended network partitions or witness failures. +The emergency consensus mode activates automatically when no blocks are produced for more than CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC seconds (1 hour by default). This mechanism ensures blockchain continuity during extended network partitions or validator failures. Emergency mode activation process: - **Timeout detection**: The database checks if seconds_since_LIB >= CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC - **Mode activation**: Sets emergency_consensus_active = true and records emergency_consensus_start_block -- **Emergency witness setup**: Creates or updates emergency witness account with CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY -- **Penalty reset**: Resets all witness penalties and re-enables shut-down witnesses -- **Schedule override**: Overrides witness schedule so all slots are filled by emergency witness +- **Emergency validator setup**: Creates or updates emergency validator account with CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY +- **Penalty reset**: Resets all validator penalties and re-enables shut-down validators +- **Schedule override**: Overrides validator schedule so all slots are filled by emergency validator - **Fork database notification**: Sets emergency mode flag in fork database for deterministic tie-breaking ```mermaid @@ -736,9 +736,9 @@ Start(["Block Application"]) --> CheckTimeout["Check LIB timestamp vs block time CheckTimeout --> TimeoutExceeded{"seconds_since_LIB >= TIMEOUT?"} TimeoutExceeded --> |No| NormalOperation["Continue normal operation"] TimeoutExceeded --> |Yes| ActivateEmergency["Activate Emergency Mode"] -ActivateEmergency --> CreateWitness["Create/Update Emergency Witness"] -CreateWitness --> ResetPenalties["Reset All Witness Penalties"] -ResetPenalties --> OverrideSchedule["Override Witness Schedule"] +ActivateEmergency --> CreateWitness["Create/Update Emergency validator"] +CreateWitness --> ResetPenalties["Reset All validator Penalties"] +ResetPenalties --> OverrideSchedule["Override validator Schedule"] OverrideSchedule --> NotifyForkDB["Notify Fork Database"] NotifyForkDB --> LogActivation["Log Emergency Mode Activation"] LogActivation --> End(["Emergency Mode Active"]) @@ -748,20 +748,20 @@ NormalOperation --> End **Diagram sources** - [database.cpp:4334-4438](file://libraries/chain/database.cpp#L4334-L4438) -### Hybrid Witness Scheduling During Emergency -During emergency mode, the witness scheduling system operates differently to ensure consensus stability: -- **All slots filled by emergency witness**: All CHAIN_MAX_WITNESSES slots are assigned to CHAIN_EMERGENCY_WITNESS_ACCOUNT +### Hybrid validator Scheduling During Emergency +During emergency mode, the validator scheduling system operates differently to ensure consensus stability: +- **All slots filled by emergency validator**: All CHAIN_MAX_WITNESSES slots are assigned to CHAIN_EMERGENCY_WITNESS_ACCOUNT - **Deterministic tie-breaking**: Emergency mode uses hash-based tie-breaking for consensus stability - **Skip LIB advancement**: Post-validation chain does not advance LIB during emergency mode -- **Committee exclusion**: Committee witness is excluded from hardfork vote tally and median computation during emergency +- **Committee exclusion**: Committee validator is excluded from hardfork vote tally and median computation during emergency ```mermaid flowchart TD -EmergencyMode["Emergency Mode Active"] --> OverrideSchedule["Override Schedule: All Slots -> Emergency Witness"] +EmergencyMode["Emergency Mode Active"] --> OverrideSchedule["Override Schedule: All Slots -> Emergency validator"] OverrideSchedule --> DeterministicTie["Deterministic Hash-Based Tie-Breaking"] DeterministicTie --> SkipLIB["Skip LIB Advancement"] SkipLIB --> CommitteeExclusion["Exclude Committee from Hardfork Votes"] -CommitteeExclusion --> EmergencyWitness["Emergency Witness Produces All Blocks"] +CommitteeExclusion --> EmergencyWitness["Emergency validator Produces All Blocks"] EmergencyWitness --> ExitCondition["Check Exit Condition: LIB > Start Block"] ExitCondition --> |True| DeactivateEmergency["Deactivate Emergency Mode"] ExitCondition --> |False| ContinueEmergency["Continue Emergency Mode"] @@ -773,15 +773,15 @@ NotifyForkDB --> NormalOperation["Resume Normal Operation"] - [database.cpp:4420-4438](file://libraries/chain/database.cpp#L4420-L4438) - [database.cpp:4444-4450](file://libraries/chain/database.cpp#L4444-L4450) -### Emergency Witness Account Management -The emergency witness account serves as the single producer during emergency consensus mode: +### Emergency validator Account Management +The emergency validator account serves as the single producer during emergency consensus mode: - **Account name**: CHAIN_EMERGENCY_WITNESS_ACCOUNT (defaults to "committee") - **Signing key**: CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY (deterministic emergency key) - **Properties**: Copies median chain properties to avoid skewing median computations - **Hardfork votes**: Votes for currently applied hardfork version to maintain status quo -- **Penalty management**: Emergency witness participates in penalty reset process +- **Penalty management**: Emergency validator participates in penalty reset process -Emergency witness lifecycle: +Emergency validator lifecycle: - **Creation**: Created automatically during emergency mode activation if not exists - **Updates**: Key and properties updated during emergency mode reactivation - **Participation**: Produces blocks for CHAIN_EMERGENCY_EXIT_NORMAL_BLOCKS consecutive blocks @@ -807,19 +807,19 @@ Tie-breaking algorithm: **Section sources** - [fork_database.cpp:80-87](file://libraries/chain/fork_database.cpp#L80-L87) -- [witness.cpp:521-526](file://plugins/witness/witness.cpp#L521-L526) +- [validator.cpp:521-526](file://plugins/validator/validator.cpp#L521-L526) ### Emergency Exit Conditions and Recovery Emergency consensus mode deactivates automatically when: - **LIB advancement**: Last Irreversible Block number exceeds emergency_consensus_start_block -- **Normal witness rejoin**: Regular witnesses resume production after emergency period +- **Normal validator rejoin**: Regular validators resume production after emergency period - **Manual intervention**: System administrator can manually deactivate emergency mode Emergency exit process: - **LIB check**: Monitor LIB advancement past emergency start block - **Mode deactivation**: Set emergency_consensus_active = false - **Fork database notification**: Clear emergency mode flag in fork database -- **Normal operation resume**: Resume normal witness scheduling and LIB advancement +- **Normal operation resume**: Resume normal validator scheduling and LIB advancement - **Penalty restoration**: Restore normal penalty calculations for emergency period **Section sources** @@ -834,7 +834,7 @@ The two-level fork collision resolution system provides robust handling of compe ### Level 1: Vote-Weighted Comparison (HF12) When HF12 is active and competing blocks exist at the same height, the system performs immediate vote-weighted comparison: -1. **Comparison**: `compare_fork_branches()` calculates total vote weight per witness for both forks +1. **Comparison**: `compare_fork_branches()` calculates total vote weight per validator for both forks 2. **Bonus Application**: Longer chain receives +10% bonus to vote weight 3. **Decision Making**: - If one fork has significantly more weight: produce on stronger fork @@ -844,7 +844,7 @@ When HF12 is active and competing blocks exist at the same height, the system pe ### Level 2: Stuck-Head Timeout If the network remains stuck with competing blocks for extended periods: -1. **Timeout Detection**: After 21 consecutive deferrals (one full witness round) +1. **Timeout Detection**: After 21 consecutive deferrals (one full validator round) 2. **Action**: Remove all stale competing blocks from the dead fork 3. **Resolution**: Produce on the canonical chain with confirmed majority support 4. **Prevention**: Ensures network doesn't stall indefinitely due to fork collisions @@ -873,12 +873,12 @@ DeferMore --> End ``` **Diagram sources** -- [witness.cpp:565-656](file://plugins/witness/witness.cpp#L565-L656) +- [validator.cpp:565-656](file://plugins/validator/validator.cpp#L565-L656) - [database.cpp:1223-1267](file://libraries/chain/database.cpp#L1223-L1267) **Section sources** -- [witness.cpp:565-656](file://plugins/witness/witness.cpp#L565-L656) -- [witness.cpp:121](file://plugins/witness/witness.cpp#L121) +- [validator.cpp:565-656](file://plugins/validator/validator.cpp#L565-L656) +- [validator.cpp:121](file://plugins/validator/validator.cpp#L121) ## Vote-Weighted Fork Comparison Algorithm @@ -888,14 +888,14 @@ The `compare_fork_branches()` function implements HF12's vote-weighted fork comp #### Algorithm Steps: 1. **Validation**: Ensure both fork tips exist in fork database 2. **Branch Extraction**: Use `fetch_branch_from()` to get branches to common ancestor -3. **Weight Calculation**: Compute total vote weight per witness for each branch +3. **Weight Calculation**: Compute total vote weight per validator for each branch 4. **Bonus Application**: Apply +10% bonus to longer chain 5. **Comparison**: Determine stronger fork or tie #### Weight Calculation Details: -- **Per-Witness Weight**: Sum of vote weights for each unique witness -- **Emergency Witness Exclusion**: Emergency witness votes are excluded from calculation -- **Unique Witness Counting**: Each witness contributes only once per branch +- **Per-validator Weight**: Sum of vote weights for each unique validator +- **Emergency validator Exclusion**: Emergency validator votes are excluded from calculation +- **Unique validator Counting**: Each validator contributes only once per branch #### Bonus System: - **Longer Chain Advantage**: +10% bonus applied to the fork with more blocks @@ -1124,8 +1124,8 @@ The fork resolution system depends on: - database for integrating fork resolution into block application, DLT mode management, automatic seeding, emergency consensus mode activation/deactivation with sophisticated early rejection logic, block validation, and HF12 vote-weighted fork comparison - block_log for persistence of irreversible blocks in normal mode - **New**: dlt_block_log for DLT mode persistence and P2P synchronization support -- **New**: witness plugin for emergency mode awareness, fork collision handling, two-level fork collision resolution, stuck-head timeout mechanism, HF12 fork comparison integration, and automatic chain linking -- **New**: emergency consensus configuration for timeout thresholds and emergency witness parameters +- **New**: Validator Plugin for emergency mode awareness, fork collision handling, two-level fork collision resolution, stuck-head timeout mechanism, HF12 fork comparison integration, and automatic chain linking +- **New**: emergency consensus configuration for timeout thresholds and emergency validator parameters - **New**: compare_fork_branches function for HF12 vote-weighted fork comparison - **New**: Enhanced exception handling for different types of block validation failures - **New**: Automatic stale fork pruning system with remove_blocks_by_number() function @@ -1140,7 +1140,7 @@ graph LR FDB["fork_database.cpp"] --> DBCPP["database.cpp"] DBCPP --> BLH["block_log.hpp"] DBCPP --> DLTH["dlt_block_log.hpp"] -DBCPP --> WIT["witness.cpp"] +DBCPP --> WIT["validator.cpp"] DBH["database.hpp"] --> DBCPP DLTH --> DBCPP WIT --> DBCPP @@ -1160,7 +1160,7 @@ MON["P2P Monitoring"] --> DIAG - [fork_database.cpp:1-278](file://libraries/chain/fork_database.cpp#L1-L278) - [database.cpp:1-6669](file://libraries/chain/database.cpp#L1-L6669) - [dlt_block_log.cpp:1-454](file://libraries/chain/dlt_block_log.cpp#L1-L454) -- [witness.cpp:1-697](file://plugins/witness/witness.cpp#L1-L697) +- [validator.cpp:1-697](file://plugins/validator/validator.cpp#L1-L697) - [config.hpp:110-124](file://libraries/protocol/include/graphene/protocol/config.hpp#L110-L124) - [12.hf:1-7](file://libraries/chain/hardfork.d/12.hf#L1-L7) @@ -1168,7 +1168,7 @@ MON["P2P Monitoring"] --> DIAG - [fork_database.cpp:1-278](file://libraries/chain/fork_database.cpp#L1-L278) - [database.cpp:1-6669](file://libraries/chain/database.cpp#L1-L6669) - [dlt_block_log.cpp:1-454](file://libraries/chain/dlt_block_log.cpp#L1-L454) -- [witness.cpp:1-697](file://plugins/witness/witness.cpp#L1-L697) +- [validator.cpp:1-697](file://plugins/validator/validator.cpp#L1-L697) - [config.hpp:110-124](file://libraries/protocol/include/graphene/protocol/config.hpp#L110-L124) - [12.hf:1-7](file://libraries/chain/hardfork.d/12.hf#L1-L7) @@ -1187,7 +1187,7 @@ MON["P2P Monitoring"] --> DIAG - **DLT mode optimization**: Automatic seeding eliminates synchronization delays for snapshot-based nodes, improving overall network health - **Gap handling**: DLT block log gap logging suppression prevents performance impact from excessive warning messages - **Emergency mode efficiency**: Emergency mode uses optimized tie-breaking with minimal computational overhead while ensuring consensus stability -- **Hybrid scheduling**: Emergency witness scheduling minimizes complexity compared to full witness rotation during emergency periods +- **Hybrid scheduling**: Emergency validator scheduling minimizes complexity compared to full validator rotation during emergency periods - **Penalty management**: Emergency penalty reset avoids complex penalty calculations during emergency mode, reducing computational load - **Sophisticated validation**: Early rejection logic prevents unnecessary processing and reduces system load during network partitions - **HF12 fork comparison**: Vote-weighted comparison adds computational overhead but provides more robust consensus decisions @@ -1216,14 +1216,14 @@ Common issues and remedies: - **Gap logging**: Monitor DLT block log gaps and adjust configuration if gaps persist beyond acceptable limits - **Emergency mode activation failures**: Verify CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC configuration and check LIB timestamp calculations - **Emergency mode deactivation issues**: Monitor LIB advancement and ensure emergency_consensus_start_block tracking is accurate -- **Emergency witness problems**: Verify emergency witness account creation and key management during emergency mode activation -- **Hybrid scheduling conflicts**: Check witness schedule overrides and ensure emergency witness has proper signing key configuration +- **Emergency validator problems**: Verify emergency validator account creation and key management during emergency mode activation +- **Hybrid scheduling conflicts**: Check validator schedule overrides and ensure emergency validator has proper signing key configuration - **Tie-breaking anomalies**: Monitor emergency mode tie-breaking behavior and verify hash-based resolution consistency across network nodes - **Infinite sync loops**: Check early rejection logic and ensure proper block validation to prevent continuous sync restarts - **Block validation failures**: Monitor different types of block validation errors and ensure appropriate exception handling -- **HF12 fork comparison failures**: Verify compare_fork_branches() function returns valid results and check witness vote weight calculations +- **HF12 fork comparison failures**: Verify compare_fork_branches() function returns valid results and check validator vote weight calculations - **Two-level collision resolution issues**: Monitor fork collision timeout counters and ensure stuck-head timeout mechanism is functioning correctly -- **Vote-weighted comparison anomalies**: Check witness vote weight calculations and ensure emergency witness exclusion is working properly +- **Vote-weighted comparison anomalies**: Check validator vote weight calculations and ensure emergency validator exclusion is working properly - **Automatic pruning failures**: Verify remove_blocks_by_number() function is cleaning stale competing blocks and check set_max_size() pruning effectiveness - **Timeout configuration problems**: Adjust fork-collision-timeout-blocks parameter if network experiences frequent fork collisions or insufficient timeout - **Gap-based rejection issues**: Verify 100-block threshold is working correctly and check that legitimate out-of-order blocks are not being rejected @@ -1242,12 +1242,12 @@ Common issues and remedies: - [database.cpp:4581-4594](file://libraries/chain/database.cpp#L4581-L4594) - [database.cpp:1300-1399](file://libraries/chain/database.cpp#L1300-L1399) - [database.cpp:2125-2142](file://libraries/chain/database.cpp#L2125-L2142) -- [witness.cpp:597-612](file://plugins/witness/witness.cpp#L597-L612) +- [validator.cpp:597-612](file://plugins/validator/validator.cpp#L597-L612) - [fork_database.cpp:269-274](file://libraries/chain/fork_database.cpp#L269-L274) - [p2p_plugin.cpp:739-760](file://plugins/p2p/p2p_plugin.cpp#L739-L760) ## Conclusion -**Updated** The fork resolution and consensus system combines an efficient in-memory fork database with robust chain reorganization, irreversible block persistence, comprehensive DLT mode support, and advanced emergency consensus recovery mechanisms. The system has been significantly enhanced with sophisticated gap-based early rejection logic, comprehensive duplicate detection, DLT mode integration, automatic seeding capabilities, comprehensive emergency consensus implementation, HF12 vote-weighted fork comparison, two-level fork collision resolution, automatic stale fork pruning, and automatic chain linking. The enhanced fork database now supports snapshot-based nodes with immediate P2P synchronization, while the DLT block log provides efficient serving of recent irreversible blocks to peers. The emergency consensus recovery system ensures blockchain continuity through timeout-based activation, hybrid witness scheduling, and deterministic tie-breaking mechanisms. The HF12 fork comparison system provides more robust consensus decisions by weighting chains based on witness vote support with +10% bonus for longer chains. The two-level fork collision resolution system combines immediate vote-weighted comparison with stuck-head timeout to ensure network progress while maintaining consensus integrity. The automatic stale fork pruning system prevents memory bloat and maintains optimal performance under fork collision conditions. The gap-based early rejection logic with 100-block threshold prevents memory bloat from dead-fork chains while maintaining network efficiency. The automatic chain linking system via _push_next() ensures efficient processing of out-of-order blocks. The system integrates tightly with witness scheduling to ensure timely and valid block production, with emergency mode awareness enabling seamless transition between normal and emergency operations. The enhanced APIs enable reliable fork detection, chain validation, and recovery with DLT mode, emergency consensus, HF12 fork comparison, gap-based protection, and automatic chain linking awareness. Performance controls keep resource usage manageable while improving synchronization reliability, network health, and consensus stability during emergency conditions. The sophisticated early rejection logic and block validation mechanisms prevent infinite synchronization loops and system degradation, ensuring robust operation under various network conditions. The separate handling paths for linear extensions vs actual fork switches improve efficiency by avoiding unnecessary operations. The detailed debug logging with FORK-SWITCH-POP and FORK-RECOVER-POP prefixes provides excellent traceability for troubleshooting and monitoring fork resolution operations. **NEW**: The comprehensive diagnostic monitoring system provides real-time insights into fork database storage statistics, enabling proactive optimization and issue detection. The diagnostic accessors offer O(1) access to critical storage metrics including linked/unlinked index sizes and block number ranges, facilitating informed capacity planning and performance tuning. The P2P monitoring integration delivers comprehensive analytics for storage health, DLT coverage gaps, and synchronization performance, ensuring optimal system operation under varying network conditions. +**Updated** The fork resolution and consensus system combines an efficient in-memory fork database with robust chain reorganization, irreversible block persistence, comprehensive DLT mode support, and advanced emergency consensus recovery mechanisms. The system has been significantly enhanced with sophisticated gap-based early rejection logic, comprehensive duplicate detection, DLT mode integration, automatic seeding capabilities, comprehensive emergency consensus implementation, HF12 vote-weighted fork comparison, two-level fork collision resolution, automatic stale fork pruning, and automatic chain linking. The enhanced fork database now supports snapshot-based nodes with immediate P2P synchronization, while the DLT block log provides efficient serving of recent irreversible blocks to peers. The emergency consensus recovery system ensures blockchain continuity through timeout-based activation, hybrid validator scheduling, and deterministic tie-breaking mechanisms. The HF12 fork comparison system provides more robust consensus decisions by weighting chains based on validator vote support with +10% bonus for longer chains. The two-level fork collision resolution system combines immediate vote-weighted comparison with stuck-head timeout to ensure network progress while maintaining consensus integrity. The automatic stale fork pruning system prevents memory bloat and maintains optimal performance under fork collision conditions. The gap-based early rejection logic with 100-block threshold prevents memory bloat from dead-fork chains while maintaining network efficiency. The automatic chain linking system via _push_next() ensures efficient processing of out-of-order blocks. The system integrates tightly with validator scheduling to ensure timely and valid block production, with emergency mode awareness enabling seamless transition between normal and emergency operations. The enhanced APIs enable reliable fork detection, chain validation, and recovery with DLT mode, emergency consensus, HF12 fork comparison, gap-based protection, and automatic chain linking awareness. Performance controls keep resource usage manageable while improving synchronization reliability, network health, and consensus stability during emergency conditions. The sophisticated early rejection logic and block validation mechanisms prevent infinite synchronization loops and system degradation, ensuring robust operation under various network conditions. The separate handling paths for linear extensions vs actual fork switches improve efficiency by avoiding unnecessary operations. The detailed debug logging with FORK-SWITCH-POP and FORK-RECOVER-POP prefixes provides excellent traceability for troubleshooting and monitoring fork resolution operations. **NEW**: The comprehensive diagnostic monitoring system provides real-time insights into fork database storage statistics, enabling proactive optimization and issue detection. The diagnostic accessors offer O(1) access to critical storage metrics including linked/unlinked index sizes and block number ranges, facilitating informed capacity planning and performance tuning. The P2P monitoring integration delivers comprehensive analytics for storage health, DLT coverage gaps, and synchronization performance, ensuring optimal system operation under varying network conditions. ## Appendices @@ -1272,7 +1272,7 @@ Common issues and remedies: - **New**: Diagnostic accessors: O(1) access to storage statistics with comprehensive metrics - **New**: database compare_fork_branches(): - O(B) where B = number of blocks in longer branch - - Calculates vote weights for each unique witness + - Calculates vote weights for each unique validator - Applies +10% bonus to longer chain - Returns comparison result (-1, 0, or 1) - **New**: database early rejection logic: @@ -1287,7 +1287,7 @@ Common issues and remedies: - truncate_before: O(n) for window compaction with safe file swapping - **New**: Emergency consensus configuration: - CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC: 3600 seconds (1 hour) timeout threshold - - CHAIN_EMERGENCY_WITNESS_ACCOUNT: Emergency witness account name ("committee") + - CHAIN_EMERGENCY_WITNESS_ACCOUNT: Emergency validator account name ("committee") - CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY: Deterministic emergency signing key - CHAIN_EMERGENCY_EXIT_NORMAL_BLOCKS: 21 blocks to trigger emergency mode exit - Hardfork version: CHAIN_HARDFORK_12 (version 3.1.0) @@ -1297,11 +1297,11 @@ Common issues and remedies: - Gap handling: O(1) logging suppression with periodic re-enabling - **New**: Emergency mode integration: - Activation detection: O(1) timestamp comparison for timeout checks - - Hybrid scheduling: O(N) override of all witness slots to emergency witness - - Penalty reset: O(N) iteration through all witnesses for penalty clearing + - Hybrid scheduling: O(N) override of all validator slots to emergency validator + - Penalty reset: O(N) iteration through all validators for penalty clearing - **New**: HF12 fork comparison: - Vote-weighted comparison: O(B) where B = number of blocks in longer branch - - Unique witness counting: O(W) where W = number of unique witnesses per branch + - Unique validator counting: O(W) where W = number of unique validators per branch - +10% bonus application: O(1) constant time operation - **New**: Two-level fork collision resolution: - Level 1 timeout: O(1) constant time comparison @@ -1338,7 +1338,7 @@ Common issues and remedies: - [config.hpp:110-124](file://libraries/protocol/include/graphene/protocol/config.hpp#L110-L124) - [database.cpp:4334-4438](file://libraries/chain/database.cpp#L4334-L4438) - [database.cpp:2125-2142](file://libraries/chain/database.cpp#L2125-L2142) -- [witness.cpp:597-612](file://plugins/witness/witness.cpp#L597-L612) +- [validator.cpp:597-612](file://plugins/validator/validator.cpp#L597-L612) - [p2p_plugin.cpp:739-760](file://plugins/p2p/p2p_plugin.cpp#L739-L760) ### Appendix B: Emergency Consensus Configuration Parameters @@ -1346,8 +1346,8 @@ Common issues and remedies: Emergency consensus parameters: - **Timeout threshold**: CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC (default: 3600 seconds) -- **Emergency witness account**: CHAIN_EMERGENCY_WITNESS_ACCOUNT (default: "committee") -- **Emergency witness key**: CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY (deterministic emergency key) +- **Emergency validator account**: CHAIN_EMERGENCY_WITNESS_ACCOUNT (default: "committee") +- **Emergency validator key**: CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY (deterministic emergency key) - **Exit condition**: CHAIN_EMERGENCY_EXIT_NORMAL_BLOCKS (default: 21 blocks) - **Hardfork version**: CHAIN_HARDFORK_12 (version 3.1.0) - **Activation time**: CHAIN_HARDFORK_12_TIME (Unix timestamp for HF12 activation) @@ -1396,7 +1396,7 @@ Exception handling strategies: - [fork_database.cpp:59-75](file://libraries/chain/fork_database.cpp#L59-L75) - [database.cpp:1300-1399](file://libraries/chain/database.cpp#L1300-L1399) - [database.cpp:1390-1465](file://libraries/chain/database.cpp#L1390-L1465) -- [witness.cpp:614-646](file://plugins/witness/witness.cpp#L614-L646) +- [validator.cpp:614-646](file://plugins/validator/validator.cpp#L614-L646) ### Appendix D: Diagnostic Accessors Usage Examples **New Section** Practical examples of using diagnostic accessors for monitoring and troubleshooting fork database storage statistics. diff --git a/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Chain Library/Memory Management System.md b/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Chain Library/Memory Management System.md index 92ab9ed2d5..48e5ec50f8 100644 --- a/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Chain Library/Memory Management System.md +++ b/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Chain Library/Memory Management System.md @@ -1,4 +1,4 @@ -# Memory Management System +# Memory Management System **Referenced Files in This Document** @@ -19,7 +19,7 @@ - [thread_specific.hpp](file://thirdparty/fc/include/fc/thread/thread_specific.hpp) - [scoped_exit.hpp](file://thirdparty/fc/include/fc/scoped_exit.hpp) - [aligned.hpp](file://thirdparty/fc/include/fc/aligned.hpp) -- [witness.cpp](file://plugins/witness/witness.cpp) +- [validator.cpp](file://plugins/validator/validator.cpp) ## Update Summary @@ -107,7 +107,7 @@ INTERPROCESS --> FILE_MAPPING **Diagram sources** - [chainbase.hpp:1319-1328](file://thirdparty/chainbase/include/chainbase/chainbase.hpp#L1319-L1328) - [database.cpp:613-653](file://libraries/chain/database.cpp#L613-L653) -- [witness.cpp:503-507](file://plugins/witness/witness.cpp#L503-L507) +- [validator.cpp:503-507](file://plugins/validator/validator.cpp#L503-L507) ## Memory Management Components diff --git a/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Chain Library/Object Model and Persistence.md b/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Chain Library/Object Model and Persistence.md index 8c3c30ebb4..65ff7484cd 100644 --- a/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Chain Library/Object Model and Persistence.md +++ b/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Chain Library/Object Model and Persistence.md @@ -1,4 +1,4 @@ -# Object Model and Persistence +# Object Model and Persistence **Referenced Files in This Document** @@ -98,7 +98,7 @@ P --> D This section summarizes the primary object types and their roles in the blockchain state. - Dynamic Global Property - - Purpose: Tracks global blockchain state (head block, supply, witness participation, reserve ratios). + - Purpose: Tracks global blockchain state (head block, supply, validator participation, reserve ratios). - Schema: See [dynamic_global_property_object](file://libraries/chain/include/graphene/chain/global_property_object.hpp#L24-L133). - Index: Single-entry unique index by id. @@ -122,10 +122,10 @@ This section summarizes the primary object types and their roles in the blockcha - Indices: by_id, by_cashout_time, by_permlink, by_root, by_parent, and more; plus content_vote indices. - Reference: [content_object.hpp](file://libraries/chain/include/graphene/chain/content_object.hpp#L56-L270). -- Witnesses and Voting - - witness_object: Witness identity, votes, virtual scheduling, signing key, props. - - witness_vote_object: Voter-to-witness mapping. - - witness_schedule_object: Current shuffled witnesses and majority version. +- validators and Voting + - witness_object: validator identity, votes, virtual scheduling, signing key, props. + - witness_vote_object: Voter-to-validator mapping. + - witness_schedule_object: Current shuffled validators and majority version. - References: [witness_objects.hpp](file://libraries/chain/include/graphene/chain/witness_objects.hpp#L27-L313). - Committee and Proposals @@ -402,8 +402,8 @@ UpdateVotes --> Done **Section sources** - [content_object.hpp](file://libraries/chain/include/graphene/chain/content_object.hpp#L56-L270) -### Witness Object Lifecycle and Indices -- Purpose: Track witness identities, votes, virtual scheduling, and penalties. +### validator Object Lifecycle and Indices +- Purpose: Track validator identities, votes, virtual scheduling, and penalties. - Indices: - witness_index: by_id, by_work, by_name, by_vote_name, by_counted_vote_name, by_schedule_time - witness_vote_index: by_id, by_account_witness, by_witness_account @@ -427,7 +427,7 @@ class witness_object { } class witness_vote_object { +id -+witness ++validator +account } class witness_schedule_object { @@ -438,7 +438,7 @@ class witness_schedule_object { } class witness_penalty_expire_object { +id -+witness ++validator +penalty_percent +expires } @@ -538,7 +538,7 @@ DBH --> PS - Hashed indices (e.g., by_trx_id) offer near O(1) lookup for deduplication. - Composite indices enable efficient range queries and uniqueness constraints for complex relationships. - Shared memory layout and allocation via chainbase minimize memory fragmentation. -- Virtual scheduling for witnesses uses large integer arithmetic; keep computations localized to reduce overhead. +- Virtual scheduling for validators uses large integer arithmetic; keep computations localized to reduce overhead. [No sources needed since this section provides general guidance] @@ -550,7 +550,7 @@ Common issues and diagnostics: - Reference: [account_object.hpp](file://libraries/chain/include/graphene/chain/account_object.hpp#L291-L315) - Content not found by permlink: Confirm by_permlink index and string comparison behavior. - Reference: [content_object.hpp](file://libraries/chain/include/graphene/chain/content_object.hpp#L210-L226) -- Witness scheduling anomalies: Inspect virtual_scheduled_time and vote indices. +- validator scheduling anomalies: Inspect virtual_scheduled_time and vote indices. - Reference: [witness_objects.hpp](file://libraries/chain/include/graphene/chain/witness_objects.hpp#L183-L219) - Committee vote conflicts: Ensure by_voter and by_request_id uniqueness. - Reference: [committee_objects.hpp](file://libraries/chain/include/graphene/chain/committee_objects.hpp#L107-L122) @@ -563,7 +563,7 @@ Common issues and diagnostics: - [committee_objects.hpp](file://libraries/chain/include/graphene/chain/committee_objects.hpp#L107-L122) ## Conclusion -The VIZ blockchain object model leverages a robust registry of object types, multi-index containers for efficient querying, and chainbase-backed persistence. The design cleanly separates consensus-critical indices from API-friendly ones, supports complex relationships (accounts, content, witnesses, committees), and provides strong serialization hooks. Proper index selection and lifecycle management are key to maintaining performance and correctness. +The VIZ blockchain object model leverages a robust registry of object types, multi-index containers for efficient querying, and chainbase-backed persistence. The design cleanly separates consensus-critical indices from API-friendly ones, supports complex relationships (accounts, content, validators, committees), and provides strong serialization hooks. Proper index selection and lifecycle management are key to maintaining performance and correctness. [No sources needed since this section summarizes without analyzing specific files] @@ -584,7 +584,7 @@ The VIZ blockchain object model leverages a robust registry of object types, mul - Insert or update content_vote_object; maintain content_index and permlink indices. - References: [content_object.hpp](file://libraries/chain/include/graphene/chain/content_object.hpp#L144-L184) -- Vote for a witness +- Vote for a validator - Insert witness_vote_object; update witness_object votes and virtual scheduling. - References: [witness_objects.hpp](file://libraries/chain/include/graphene/chain/witness_objects.hpp#L224-L248) diff --git a/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Chain Library/Transaction Processing.md b/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Chain Library/Transaction Processing.md index d114ccb47b..154436e0e1 100644 --- a/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Chain Library/Transaction Processing.md +++ b/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Chain Library/Transaction Processing.md @@ -1,4 +1,4 @@ -# Transaction Processing +# Transaction Processing **Referenced Files in This Document** @@ -32,7 +32,7 @@ This document explains the Transaction Processing system responsible for validat - Execution context and rollback mechanisms for apply_transaction() and _apply_transaction() - Pending transaction management, transaction pool operations, and broadcast mechanisms - Examples of transaction processing workflows, operation evaluation, and error handling -- Interactions with witness scheduling, fee markets, and state transitions +- Interactions with validator scheduling, fee markets, and state transitions - Transaction size limits, priority handling, and performance optimization strategies ## Project Structure @@ -333,8 +333,8 @@ Notif-->>Net : on_applied_transaction callback - [database.cpp](file://libraries/chain/database.cpp#L960-L970) - [database.cpp](file://libraries/chain/database.cpp#L1192-L1198) -### Relationship with Witness Scheduling, Fee Markets, and State Transitions -- Witness scheduling: The database computes scheduled witnesses and validates block headers; transaction processing occurs within the context of block production and validation. +### Relationship with validator Scheduling, Fee Markets, and State Transitions +- validator scheduling: The database computes scheduled validators and validates block headers; transaction processing occurs within the context of block production and validation. - Fee markets and state transitions: Operations modify state objects (e.g., balances, vesting shares, reward funds). Bandwidth accounting influences fee market dynamics indirectly by controlling resource usage and reserve ratios. [No sources needed since this section synthesizes relationships without analyzing specific files] diff --git a/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Protocol Library/Block Structures.md b/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Protocol Library/Block Structures.md index 36b8953de1..8515728009 100644 --- a/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Protocol Library/Block Structures.md +++ b/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Protocol Library/Block Structures.md @@ -1,4 +1,4 @@ -# Block Structures +# Block Structures **Referenced Files in This Document** @@ -29,8 +29,8 @@ This document explains the blockchain block format and consensus mechanisms in the VIZ C++ node. It focuses on: - Block header structure and metadata - Transaction inclusion and Merkle root computation -- Witness signature validation and block hashing -- Validation rules for blocks (Merkle root, witness signature, fork resolution) +- validator signature validation and block hashing +- Validation rules for blocks (Merkle root, validator signature, fork resolution) - Block production and propagation workflows - Network synchronization and state progression @@ -38,7 +38,7 @@ This document explains the blockchain block format and consensus mechanisms in t The block-related logic spans protocol-level definitions and chain-level validation and persistence: - Protocol-level block definitions and cryptographic helpers - Chain-level validation, fork management, and block logging -- Witness scheduling and participation metrics +- validator scheduling and participation metrics ```mermaid graph TB @@ -89,13 +89,13 @@ C_DB_IMPL --> C_Wit - [witness_objects.hpp](file://libraries/chain/include/graphene/chain/witness_objects.hpp#L1-L200) ## Core Components -- Block header and signed header: define block metadata, hash computation, and witness signature validation. +- Block header and signed header: define block metadata, hash computation, and validator signature validation. - Signed block: extends the signed header with a transaction list and Merkle root calculation. - Types: defines cryptographic primitives used by blocks and transactions. - Fork database: manages unlinked and linked forks, head selection, and branch resolution. -- Database validation pipeline: validates block headers, Merkle roots, sizes, and witness scheduling; pushes blocks and updates state. +- Database validation pipeline: validates block headers, Merkle roots, sizes, and validator scheduling; pushes blocks and updates state. - Block log: persists blocks to disk for replay and synchronization. -- Witness objects: track scheduling eligibility, participation, and penalties. +- validator objects: track scheduling eligibility, participation, and penalties. **Section sources** - [block_header.hpp](file://libraries/protocol/include/graphene/protocol/block_header.hpp#L8-L35) @@ -113,7 +113,7 @@ The block lifecycle integrates protocol definitions, validation, persistence, an - Chain layer validates incoming blocks against local state and schedules. - Fork database resolves competing chains and selects the heaviest branch. - Block log persists blocks and supports fast random access by number. -- Witness objects enforce scheduling and participation rules. +- validator objects enforce scheduling and participation rules. ```mermaid sequenceDiagram @@ -138,8 +138,8 @@ DB-->>Peer : "result" ## Detailed Component Analysis ### Block Header and Hashing -- The block header contains previous block identifier, timestamp, witness name, and the transaction Merkle root. -- The signed block header adds the witness signature and exposes signing and validation helpers. +- The block header contains previous block identifier, timestamp, validator name, and the transaction Merkle root. +- The signed block header adds the validator signature and exposes signing and validation helpers. - The block ID is derived from a compact hash with the block number embedded. ```mermaid @@ -147,7 +147,7 @@ classDiagram class block_header { +block_id_type previous +time_point_sec timestamp -+string witness ++string validator +checksum_type transaction_merkle_root +digest() digest_type +num_from_id(id) uint32_t @@ -209,8 +209,8 @@ Cast --> End(["Return Merkle root"]) ### Block Validation Rules - Merkle root verification: The computed Merkle root must match the header’s field. - Block size check: Enforced against dynamic global properties. -- Witness signature validation: The witness signature must be recoverable to the expected signing key. -- Witness scheduling: The block must be produced by the scheduled witness for the slot derived from the timestamp. +- validator signature validation: The validator signature must be recoverable to the expected signing key. +- validator scheduling: The block must be produced by the scheduled validator for the slot derived from the timestamp. - Fork resolution: If the new block does not extend the current head, the fork database chooses the heaviest chain and replays or undoes blocks as needed. ```mermaid @@ -221,9 +221,9 @@ participant Fork as "fork_database.hpp" DB->>DB : "validate_block(new_block)" DB->>DB : "calculate_merkle_root()" DB->>DB : "compare with header" -DB->>FH : "validate_signee(witness.signing_key)" +DB->>FH : "validate_signee(validator.signing_key)" FH-->>DB : "ok" -DB->>DB : "check scheduled witness" +DB->>DB : "check scheduled validator" DB->>Fork : "push_block(new_block)" Fork-->>DB : "new_head" DB->>DB : "apply_block() if needed" @@ -240,13 +240,13 @@ DB->>DB : "apply_block() if needed" - [fork_database.hpp](file://libraries/chain/include/graphene/chain/fork_database.hpp#L78-L91) ### Block Production and Propagation -- Block production is governed by witness scheduling and participation. The witness that produces a block is determined by the slot derived from the block timestamp and the witness schedule. +- Block production is governed by validator scheduling and participation. The validator that produces a block is determined by the slot derived from the block timestamp and the validator schedule. - Propagation occurs when peers receive blocks and validate them before pushing to their chain. - Persistence is handled by the block log, which stores blocks in an append-only manner and supports random access by block number. ```mermaid sequenceDiagram -participant W as "Witness" +participant W as "validator" participant Net as "Network" participant Node as "database.cpp" participant Log as "block_log.hpp" @@ -298,14 +298,14 @@ K --> L["abort"] ### Relationship Between Blocks and Blockchain State Progression - Dynamic global properties are updated per block, including head block number/id, timestamp, participation metrics, and reserve ratios. -- Witness participation and penalties are tracked; missed blocks increment counters and can lead to penalties and potential shutdown of inactive witnesses. -- The irreversible block number advances when sufficient witness validations reach consensus thresholds. +- validator participation and penalties are tracked; missed blocks increment counters and can lead to penalties and potential shutdown of inactive validators. +- The irreversible block number advances when sufficient validator validations reach consensus thresholds. ```mermaid flowchart TD S["Start apply_block"] --> U["update_global_dynamic_data()"] U --> P["compute missed_blocks and participation"] -P --> W["modify witness stats (missed, penalties)"] +P --> W["modify validator stats (missed, penalties)"] W --> D["update dgp fields (head, time, sizes)"] D --> IR["check irreversible threshold"] IR --> E["advance last_irreversible_block_num if met"] @@ -324,7 +324,7 @@ F --> T["End"] ## Dependency Analysis - Protocol definitions depend on cryptographic types and reflection macros. - Chain validation depends on protocol structures, fork database, and block log. -- Witness objects provide scheduling and participation data used by validation. +- validator objects provide scheduling and participation data used by validation. ```mermaid graph LR @@ -364,16 +364,16 @@ DBIMPL --> Wit["witness_objects.hpp"] - Merkle root computation is O(n log n) for n transactions due to the binary hash tree. - Fork resolution involves popping and pushing blocks; keep the maximum reordering window reasonable to avoid excessive memory and CPU usage. - Block size checks prevent oversized blocks from consuming resources. -- Witness participation metrics and penalties help maintain network health and reduce spam. +- validator participation metrics and penalties help maintain network health and reduce spam. [No sources needed since this section provides general guidance] ## Troubleshooting Guide Common validation failures and remedies: - Merkle mismatch: Verify transaction ordering and ensure the Merkle root is recomputed consistently. -- Witness signature mismatch: Confirm the witness signing key matches the expected key and that the digest used for signing is correct. -- Incorrect witness scheduling: Ensure the block timestamp yields the correct slot and that the scheduled witness matches the block producer. -- Fork conflicts: Investigate why the new block did not extend the current head; check timestamps, witness assignments, and fork database logs. +- validator signature mismatch: Confirm the validator signing key matches the expected key and that the digest used for signing is correct. +- Incorrect validator scheduling: Ensure the block timestamp yields the correct slot and that the scheduled validator matches the block producer. +- Fork conflicts: Investigate why the new block did not extend the current head; check timestamps, validator assignments, and fork database logs. - Disk or memory errors during block push: The system attempts to resize shared memory on allocation failure; ensure adequate disk space and memory. **Section sources** @@ -382,15 +382,15 @@ Common validation failures and remedies: - [database.cpp](file://libraries/chain/database.cpp#L847-L925) ## Conclusion -Blocks in the VIZ node are defined by a clear protocol structure, validated rigorously by the chain layer, and persisted for resilience. Consensus relies on deterministic witness scheduling, strict signature validation, and robust fork resolution. Understanding these components enables reliable block construction, validation, propagation, and synchronization across the network. +Blocks in the VIZ node are defined by a clear protocol structure, validated rigorously by the chain layer, and persisted for resilience. Consensus relies on deterministic validator scheduling, strict signature validation, and robust fork resolution. Understanding these components enables reliable block construction, validation, propagation, and synchronization across the network. [No sources needed since this section summarizes without analyzing specific files] ## Appendices ### Example Workflows -- Constructing a block: Build a signed block with transactions, compute the Merkle root, populate the header fields, and sign with the witness key. -- Validating a block: Compute the Merkle root, compare against the header; verify the witness signature; confirm the scheduled witness; enforce block size limits. +- Constructing a block: Build a signed block with transactions, compute the Merkle root, populate the header fields, and sign with the validator key. +- Validating a block: Compute the Merkle root, compare against the header; verify the validator signature; confirm the scheduled validator; enforce block size limits. - Resolving a fork: Use the fork database to select the heaviest chain; if necessary, pop blocks from the current chain and push blocks from the new fork. [No sources needed since this section provides general guidance] \ No newline at end of file diff --git a/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Protocol Library/Data Types and Serialization.md b/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Protocol Library/Data Types and Serialization.md index cb792508f2..9b50b03485 100644 --- a/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Protocol Library/Data Types and Serialization.md +++ b/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Protocol Library/Data Types and Serialization.md @@ -1,4 +1,4 @@ -# Data Types and Serialization +# Data Types and Serialization **Referenced Files in This Document** @@ -327,9 +327,9 @@ Usage examples (conceptual): - [base.hpp](file://libraries/protocol/include/graphene/protocol/base.hpp#L12-L62) ### Concrete Operations and Extensions -- chain_operations.hpp: account creation/update, transfers, vesting, witnesses, escrow, chain properties, invites, subscriptions, sales, awards, bids +- chain_operations.hpp: account creation/update, transfers, vesting, validators, escrow, chain properties, invites, subscriptions, sales, awards, bids - proposal_operations.hpp: proposal lifecycle operations -- chain_virtual_operations.hpp: reward payouts, hardfork triggers, committee actions, witness rewards, subscription actions, sales, and auction events +- chain_virtual_operations.hpp: reward payouts, hardfork triggers, committee actions, validator rewards, subscription actions, sales, and auction events Serialization highlights: - FC_REFLECT for each operation struct diff --git a/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Protocol Library/Protocol Library.md b/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Protocol Library/Protocol Library.md index 827069c5a0..6928acec34 100644 --- a/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Protocol Library/Protocol Library.md +++ b/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Protocol Library/Protocol Library.md @@ -1,4 +1,4 @@ -# Protocol Library +# Protocol Library **Referenced Files in This Document** @@ -115,7 +115,7 @@ COCPP --> CO - Operations: A static variant enumerating all transaction operations, including account, asset, content, governance, and virtual operations. Includes helpers to detect virtual and data operations. - Transaction: Transaction and signed transaction structures with validation, signature digest computation, Merkle digest, and authority extraction/minimization. - Authority: Multi-signature authority model with thresholds, account and key maps, validation, and account name rules. -- Blocks: Block header and signed block structures with Merkle root computation and witness signature verification. +- Blocks: Block header and signed block structures with Merkle root computation and validator signature verification. - Chain Operations: Strongly-typed operation structs with validation logic and required authority hooks. - Types: Core blockchain types (names, keys, amounts, hashes, digests) and serialization support. @@ -129,7 +129,7 @@ COCPP --> CO - [types.hpp](file://libraries/protocol/include/graphene/protocol/types.hpp#L75-L235) ## Architecture Overview -The Protocol Library composes operations into transactions, validates each operation, computes digests for signing and Merkle roots, and enforces authority requirements. Blocks encapsulate transactions and enforce witness signatures. +The Protocol Library composes operations into transactions, validates each operation, computes digests for signing and Merkle roots, and enforces authority requirements. Blocks encapsulate transactions and enforce validator signatures. ```mermaid graph TB @@ -238,12 +238,12 @@ CheckAccounts --> End(["Done"]) - [authority.cpp](file://libraries/protocol/authority.cpp#L7-L228) ### Blocks and Block Headers: Validation and Consensus -- Block header includes previous ID, timestamp, witness, and Merkle root of transactions. -- Signed block header adds witness signature and methods to compute ID and validate signee. +- Block header includes previous ID, timestamp, validator, and Merkle root of transactions. +- Signed block header adds validator signature and methods to compute ID and validate signee. - Signed block computes Merkle root over transaction digests. Consensus implications: -- Witness signature verification ensures block validity +- validator signature verification ensures block validity - Merkle root ensures transaction integrity ```mermaid @@ -265,7 +265,7 @@ C --> D["Set signed_block.transaction_merkle_root"] ### Chain Operations: Evaluation Logic and Constraints - Strongly typed operation structs define required authorities and validation rules. -- Examples include account creation/update/metadata, transfers, vesting, witness updates, chain property updates, escrow operations, custom operations, and governance-related operations (committee, invite, paid subscription, account sales, awards, etc.). +- Examples include account creation/update/metadata, transfers, vesting, validator updates, chain property updates, escrow operations, custom operations, and governance-related operations (committee, invite, paid subscription, account sales, awards, etc.). - Validation enforces symbol types, numeric bounds, UTF-8 and JSON constraints, permlink rules, and account name rules. Evaluation highlights: @@ -353,7 +353,7 @@ Common issues and diagnostics: - Transaction missing required signatures or approvals - Irrelevant signatures or approvals detected during verification - Authority thresholds not met or invalid account names -- Block header signature mismatch or invalid witness +- Block header signature mismatch or invalid validator Diagnostics: - verify_authority throws specific exceptions for missing authorities and irrelevant inputs @@ -398,7 +398,7 @@ The Protocol Library provides a robust foundation for blockchain operations, tra - [sign_state.hpp](file://libraries/protocol/include/graphene/protocol/sign_state.hpp#L10-L42) #### Block Validation -- Compute signed block header ID and validate witness signature +- Compute signed block header ID and validate validator signature - Recompute Merkle root from transaction digests and compare with block header **Section sources** diff --git a/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Wallet Library.md b/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Wallet Library.md index 8e23a84db5..2be020cd50 100644 --- a/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Wallet Library.md +++ b/.qoder/repowiki/en/content/Architecture Overview/Core Libraries/Wallet Library.md @@ -1,4 +1,4 @@ -# Wallet Library +# Wallet Library **Referenced Files in This Document** @@ -542,7 +542,7 @@ Wallet --> NetBCast["graphene::network_broadcast_api"] Wallet --> Follow["graphene::follow"] Wallet --> PM["graphene::private_message"] Wallet --> AccByKey["graphene::account_by_key"] -Wallet --> Witness["graphene::witness_api"] +Wallet --> validator["graphene::witness_api"] Wallet --> Util["graphene_utilities"] Wallet --> FC["fc"] Wallet --> ContentAPI["content_api_object.hpp"] diff --git a/.qoder/repowiki/en/content/Architecture Overview/Data Flow and Processing/Block Processing and Validation.md b/.qoder/repowiki/en/content/Architecture Overview/Data Flow and Processing/Block Processing and Validation.md index 4df30d8df2..92d302accc 100644 --- a/.qoder/repowiki/en/content/Architecture Overview/Data Flow and Processing/Block Processing and Validation.md +++ b/.qoder/repowiki/en/content/Architecture Overview/Data Flow and Processing/Block Processing and Validation.md @@ -1,4 +1,4 @@ -# Block Processing and Validation +# Block Processing and Validation **Referenced Files in This Document** @@ -12,8 +12,8 @@ - [database.cpp](file://libraries/chain/database.cpp) - [block.hpp](file://libraries/protocol/include/graphene/protocol/block.hpp) - [block_header.hpp](file://libraries/protocol/include/graphene/protocol/block_header.hpp) -- [witness.hpp](file://plugins/witness/include/graphene/plugins/witness/witness.hpp) -- [witness.cpp](file://plugins/witness/witness.cpp) +- [validator.hpp](file://plugins/validator/include/graphene/plugins/validator/validator.hpp) +- [validator.cpp](file://plugins/validator/validator.cpp) - [database_exceptions.hpp](file://libraries/chain/include/graphene/chain/database_exceptions.hpp) @@ -23,7 +23,7 @@ - Improved block log accessibility with dual-path retrieval: block_log → dlt_block_log → fork_database fallback chain - Added robust gap detection and logging for DLT block log gaps during LIB advancement - Strengthened chain validation reliability by preventing chain stalls due to missing blocks -- Enhanced witness plugin block post-validation logic with defensive programming to prevent runtime errors when witness accounts aren't found during validation +- Enhanced Validator Plugin block post-validation logic with defensive programming to prevent runtime errors when validator accounts aren't found during validation ## Table of Contents 1. [Introduction](#introduction) @@ -37,7 +37,7 @@ 9. [Conclusion](#conclusion) ## Introduction -This document explains the complete block processing and validation pipeline in the VIZ node, including header validation, transaction extraction, state application, fork resolution, block persistence, and witness block production coordination. It also covers performance characteristics and optimization techniques used for high-throughput block processing. +This document explains the complete block processing and validation pipeline in the VIZ node, including header validation, transaction extraction, state application, fork resolution, block persistence, and validator block production coordination. It also covers performance characteristics and optimization techniques used for high-throughput block processing. **Updated** Enhanced with comprehensive fallback mechanisms for missing irreversible blocks during fork resolution and chain reorganization events, improving chain validation reliability and preventing chain stalls. The block log accessibility system now provides a robust fallback chain: block_log → dlt_block_log → fork_database, ensuring blocks remain accessible even when intermediate storage systems are unavailable. @@ -64,8 +64,8 @@ DLTH["dlt_block_log.hpp"] D LTCPP["dlt_block_log.cpp"] end subgraph "Plugins" -WIT["witness.hpp"] -WITCPP["witness.cpp"] +WIT["validator.hpp"] +WITCPP["validator.cpp"] end BH --> SB SB --> DBH @@ -91,8 +91,8 @@ WITCPP --> DBH - [block_log.cpp:238-300](file://libraries/chain/block_log.cpp#L238-L300) - [dlt_block_log.hpp:1-76](file://libraries/chain/include/graphene/chain/dlt_block_log.hpp#L1-L76) - [dlt_block_log.cpp:162-242](file://libraries/chain/dlt_block_log.cpp#L162-L242) -- [witness.hpp:1-70](file://plugins/witness/include/graphene/plugins/witness/witness.hpp#L1-L70) -- [witness.cpp:295-341](file://plugins/witness/witness.cpp#L295-L341) +- [validator.hpp:1-70](file://plugins/validator/include/graphene/plugins/validator/validator.hpp#L1-L70) +- [validator.cpp:295-341](file://plugins/validator/validator.cpp#L295-L341) **Section sources** - [block_header.hpp:1-43](file://libraries/protocol/include/graphene/protocol/block_header.hpp#L1-L43) @@ -101,18 +101,18 @@ WITCPP --> DBH - [fork_database.hpp:1-125](file://libraries/chain/include/graphene/chain/fork_database.hpp#L1-L125) - [block_log.hpp:1-75](file://libraries/chain/include/graphene/chain/block_log.hpp#L1-L75) - [dlt_block_log.hpp:1-76](file://libraries/chain/include/graphene/chain/dlt_block_log.hpp#L1-L76) -- [witness.hpp:1-70](file://plugins/witness/include/graphene/plugins/witness/witness.hpp#L1-L70) -- [witness.cpp:295-341](file://plugins/witness/witness.cpp#L295-L341) +- [validator.hpp:1-70](file://plugins/validator/include/graphene/plugins/validator/validator.hpp#L1-L70) +- [validator.cpp:295-341](file://plugins/validator/validator.cpp#L295-L341) ## Core Components -- Protocol block model: Defines the signed block structure and signed block header, including merkle roots and witness signatures. +- Protocol block model: Defines the signed block structure and signed block header, including merkle roots and validator signatures. - Fork database: Maintains a tree of candidate blocks, supports branching, linking, and selection of the heaviest chain. - Dual block log system: Provides append-only, random-access persistence of blocks with fallback mechanisms: - Primary block_log for irreversible blocks - DLT rolling block_log for recent blocks with sliding window capability - Fork database as final fallback for reversible blocks - Database: Orchestrates validation, fork resolution, state application, and block logging with comprehensive fallback logic. -- Witness plugin: Coordinates block production for witness nodes and defines acceptance criteria with enhanced defensive programming. +- Validator Plugin: Coordinates block production for validator nodes and defines acceptance criteria with enhanced defensive programming. **Updated** The dual block log system now provides comprehensive fallback mechanisms, with block retrieval following the chain: block_log → dlt_block_log → fork_database. This ensures chain validation reliability by preventing chain stalls due to missing blocks in any single storage system. @@ -123,8 +123,8 @@ WITCPP --> DBH - [block_log.hpp:38-68](file://libraries/chain/include/graphene/chain/block_log.hpp#L38-L68) - [dlt_block_log.hpp:13-33](file://libraries/chain/include/graphene/chain/dlt_block_log.hpp#L13-L33) - [database.hpp:36-287](file://libraries/chain/include/graphene/chain/database.hpp#L36-L287) -- [witness.hpp:20-32](file://plugins/witness/include/graphene/plugins/witness/witness.hpp#L20-L32) -- [witness.cpp:295-341](file://plugins/witness/witness.cpp#L295-L341) +- [validator.hpp:20-32](file://plugins/validator/include/graphene/plugins/validator/validator.hpp#L20-L32) +- [validator.cpp:295-341](file://plugins/validator/validator.cpp#L295-L341) ## Architecture Overview The block processing pipeline integrates protocol definitions, fork management, and dual persistent storage systems with comprehensive fallback mechanisms: @@ -162,7 +162,7 @@ end - [fork_database.cpp:33-90](file://libraries/chain/fork_database.cpp#L33-L90) - [block_log.cpp:253-257](file://libraries/chain/block_log.cpp#L253-L257) - [dlt_block_log.cpp:336-340](file://libraries/chain/dlt_block_log.cpp#L336-L340) -- [witness.hpp:20-32](file://plugins/witness/include/graphene/plugins/witness/witness.hpp#L20-L32) +- [validator.hpp:20-32](file://plugins/validator/include/graphene/plugins/validator/validator.hpp#L20-L32) ## Detailed Component Analysis @@ -310,25 +310,25 @@ end - [fork_database.cpp:79-90](file://libraries/chain/fork_database.cpp#L79-L90) - [database.cpp:846-913](file://libraries/chain/database.cpp#L846-L913) -### Enhanced Block Production Coordination for Witness Nodes -**Updated** Witness nodes coordinate block production through a dedicated plugin with enhanced defensive programming: +### Enhanced Block Production Coordination for validator Nodes +**Updated** validator nodes coordinate block production through a dedicated plugin with enhanced defensive programming: - Acceptance criteria include synchronization status, turn-based scheduling, time windows, participation thresholds, and availability of signing keys. - The plugin integrates with the chain plugin and P2P plugin to manage production loops. -- **Enhanced defensive programming**: Added error handling to gracefully skip witness accounts that aren't found in the witness index during block post-validation, preventing runtime errors and improving system reliability. +- **Enhanced defensive programming**: Added error handling to gracefully skip validator accounts that aren't found in the validator index during block post-validation, preventing runtime errors and improving system reliability. - **Enhanced gap detection**: DLT block log gaps are logged with detailed information about LIB advancement progress. ```mermaid flowchart TD -Init(["Initialize witness plugin"]) --> Options["Parse options"] +Init(["Initialize Validator Plugin"]) --> Options["Parse options"] Options --> Startup["Startup"] Startup --> Schedule["Compute slot times"] Schedule --> PostValidation["Block Post-Validation Loop"] -PostValidation --> CheckWitness["Check witness account exists"] +PostValidation --> CheckWitness["Check validator account exists"] CheckWitness --> |Exists| ValidateBlock["Validate block and sign"] CheckWitness --> |Not Found| Skip["Skip with warning log"] ValidateBlock --> Broadcast["Broadcast to peers"] -Skip --> Continue["Continue with next witness"] -Continue --> Produce{"Is witness turn?"} +Skip --> Continue["Continue with next validator"] +Continue --> Produce{"Is validator turn?"} Produce --> |Yes| Build["generate_block()"] Produce --> |No| Wait["Wait for next slot"] Build --> Sign["Sign block header"] @@ -337,15 +337,15 @@ Apply --> Persist["append to block_log"] ``` **Diagram sources** -- [witness.hpp:34-65](file://plugins/witness/include/graphene/plugins/witness/witness.hpp#L34-L65) -- [witness.cpp:295-341](file://plugins/witness/witness.cpp#L295-L341) +- [validator.hpp:34-65](file://plugins/validator/include/graphene/plugins/validator/validator.hpp#L34-L65) +- [validator.cpp:295-341](file://plugins/validator/validator.cpp#L295-L341) - [database.hpp:214-226](file://libraries/chain/include/graphene/chain/database.hpp#L214-L226) - [database.cpp:3443-3509](file://libraries/chain/database.cpp#L3443-L3509) **Section sources** -- [witness.hpp:20-32](file://plugins/witness/include/graphene/plugins/witness/witness.hpp#L20-L32) -- [witness.hpp:34-65](file://plugins/witness/include/graphene/plugins/witness/witness.hpp#L34-L65) -- [witness.cpp:295-341](file://plugins/witness/witness.cpp#L295-L341) +- [validator.hpp:20-32](file://plugins/validator/include/graphene/plugins/validator/validator.hpp#L20-L32) +- [validator.hpp:34-65](file://plugins/validator/include/graphene/plugins/validator/validator.hpp#L34-L65) +- [validator.cpp:295-341](file://plugins/validator/validator.cpp#L295-L341) - [database.hpp:214-226](file://libraries/chain/include/graphene/chain/database.hpp#L214-L226) ### Enhanced Transaction Extraction and State Application @@ -387,8 +387,8 @@ DBC --> DLTH["dlt_block_log.hpp"] FDH --> FDC["fork_database.cpp"] BLH --> BLC["block_log.cpp"] DLTH --> D LTCPP["dlt_block_log.cpp"] -WIT["witness.hpp"] --> DBH -WITCPP["witness.cpp"] --> DBH +WIT["validator.hpp"] --> DBH +WITCPP["validator.cpp"] --> DBH ``` **Diagram sources** @@ -398,16 +398,16 @@ WITCPP["witness.cpp"] --> DBH - [fork_database.hpp:1-125](file://libraries/chain/include/graphene/chain/fork_database.hpp#L1-L125) - [block_log.hpp:1-75](file://libraries/chain/include/graphene/chain/block_log.hpp#L1-L75) - [dlt_block_log.hpp:1-76](file://libraries/chain/include/graphene/chain/dlt_block_log.hpp#L1-L76) -- [witness.hpp:1-70](file://plugins/witness/include/graphene/plugins/witness/witness.hpp#L1-L70) -- [witness.cpp:295-341](file://plugins/witness/witness.cpp#L295-L341) +- [validator.hpp:1-70](file://plugins/validator/include/graphene/plugins/validator/validator.hpp#L1-L70) +- [validator.cpp:295-341](file://plugins/validator/validator.cpp#L295-L341) **Section sources** - [database.hpp:1-561](file://libraries/chain/include/graphene/chain/database.hpp#L1-L561) - [fork_database.hpp:1-125](file://libraries/chain/include/graphene/chain/fork_database.hpp#L1-L125) - [block_log.hpp:1-75](file://libraries/chain/include/graphene/chain/block_log.hpp#L1-L75) - [dlt_block_log.hpp:1-76](file://libraries/chain/include/graphene/chain/dlt_block_log.hpp#L1-L76) -- [witness.hpp:1-70](file://plugins/witness/include/graphene/plugins/witness/witness.hpp#L1-L70) -- [witness.cpp:295-341](file://plugins/witness/witness.cpp#L295-L341) +- [validator.hpp:1-70](file://plugins/validator/include/graphene/plugins/validator/validator.hpp#L1-L70) +- [validator.cpp:295-341](file://plugins/validator/validator.cpp#L295-L341) ## Performance Considerations - Skip flags: Validation and checks can be selectively disabled during reindexing or trusted operations to reduce overhead. @@ -417,15 +417,15 @@ WITCPP["witness.cpp"] --> DBH - Parallelism: Network and plugin layers can operate concurrently with chain operations under appropriate locking. - **Enhanced fallback mechanisms**: Comprehensive fallback chain (block_log → dlt_block_log → fork_database) improves reliability without significant performance impact. - **Gap detection optimization**: DLT block log gap detection prevents unnecessary retries and reduces logging overhead during normal operations. -- **Enhanced defensive programming**: The witness plugin now includes additional error checking and graceful degradation to prevent runtime errors during block post-validation, improving overall system stability. +- **Enhanced defensive programming**: The Validator Plugin now includes additional error checking and graceful degradation to prevent runtime errors during block post-validation, improving overall system stability. ## Troubleshooting Guide Common issues and diagnostics: - Unlinkable blocks: Detected when a block's previous ID is not present in the fork database; the block is cached and linked when the parent arrives. - Hardfork application errors: Exceptions indicate attempting to apply unknown hardforks or missing hardfork state. - Block log inconsistencies: Startup reconciliation reconstructs the index if the log and index are out of sync. -- Witness production failures: Conditions such as not being scheduled, insufficient participation, or missing private keys prevent block production. -- **Enhanced error handling**: Witness accounts not found in witness index are now handled gracefully with warning logs, preventing runtime errors and allowing the system to continue processing other witnesses. +- validator production failures: Conditions such as not being scheduled, insufficient participation, or missing private keys prevent block production. +- **Enhanced error handling**: validator accounts not found in validator index are now handled gracefully with warning logs, preventing runtime errors and allowing the system to continue processing other validators. - **DLT gap detection**: DLT block log gaps are logged with detailed information about LIB advancement progress, helping diagnose synchronization issues. - **Fallback chain failures**: When all fallback mechanisms fail, the system returns block_id_type(), indicating the block is not available in any storage system. @@ -433,8 +433,8 @@ Common issues and diagnostics: - [fork_database.cpp:38-44](file://libraries/chain/fork_database.cpp#L38-L44) - [database_exceptions.hpp:83-83](file://libraries/chain/include/graphene/chain/database_exceptions.hpp#L83-L83) - [block_log.cpp:163-193](file://libraries/chain/block_log.cpp#L163-L193) -- [witness.hpp:20-32](file://plugins/witness/include/graphene/plugins/witness/witness.hpp#L20-L32) -- [witness.cpp:305-307](file://plugins/witness/witness.cpp#L305-L307) +- [validator.hpp:20-32](file://plugins/validator/include/graphene/plugins/validator/validator.hpp#L20-L32) +- [validator.cpp:305-307](file://plugins/validator/validator.cpp#L305-L307) - [database.cpp:5395-5419](file://libraries/chain/database.cpp#L5395-L5419) ## Conclusion @@ -442,6 +442,6 @@ The VIZ node implements a robust block processing pipeline that separates concer **Updated** The enhanced fork handling system now includes comprehensive fallback mechanisms for missing irreversible blocks during fork resolution and chain reorganization events, significantly improving chain validation reliability and preventing chain stalls. The block log accessibility system provides a robust fallback chain: block_log → dlt_block_log → fork_database, ensuring blocks remain accessible even when intermediate storage systems are unavailable. -The database orchestrates validation, state application, and integration with plugins such as the witness node coordinator. The enhanced defensive programming in the witness plugin prevents runtime errors when witness accounts aren't found during block post-validation, significantly improving system reliability and error handling. +The database orchestrates validation, state application, and integration with plugins such as the validator node coordinator. The enhanced defensive programming in the Validator Plugin prevents runtime errors when validator accounts aren't found during block post-validation, significantly improving system reliability and error handling. Performance is optimized through selective validation, memory-mapped IO, bounded caches, and controlled flushing, enabling high-throughput operation in production environments. The addition of comprehensive fallback mechanisms and gap detection further enhances the system's resilience and operational stability, making it highly reliable for production blockchain operations. \ No newline at end of file diff --git a/.qoder/repowiki/en/content/Architecture Overview/Data Flow and Processing/Data Flow and Processing.md b/.qoder/repowiki/en/content/Architecture Overview/Data Flow and Processing/Data Flow and Processing.md index a733257de3..a8cfcaa554 100644 --- a/.qoder/repowiki/en/content/Architecture Overview/Data Flow and Processing/Data Flow and Processing.md +++ b/.qoder/repowiki/en/content/Architecture Overview/Data Flow and Processing/Data Flow and Processing.md @@ -1,4 +1,4 @@ -# Data Flow and Processing +# Data Flow and Processing **Referenced Files in This Document** @@ -203,7 +203,7 @@ NotifyPost --> Done(["Transaction applied"]) ### Block Processing Flow - validate_block performs Merkle root and block size checks. -- push_block integrates a block into the chain, updating dynamic properties, witness participation, and last irreversible block. +- push_block integrates a block into the chain, updating dynamic properties, validator participation, and last irreversible block. - apply_block coordinates per-block state transitions and emits applied_block signals. ```mermaid @@ -334,7 +334,7 @@ DB --> BLOCK["protocol::signed_block"] - Batch processing: JSON-RPC plugin supports batch requests and streams responses efficiently. Recommendations: -- Tune skip flags for trusted environments (e.g., skip signatures for non-witness nodes). +- Tune skip flags for trusted environments (e.g., skip signatures for non-validator nodes). - Monitor free memory and adjust shared file sizing to avoid frequent resizes. - Use checkpoints to reduce validation overhead on startup. - Leverage evaluators’ early exits and minimal authority checks where safe. diff --git a/.qoder/repowiki/en/content/Architecture Overview/Data Flow and Processing/Transaction Processing Pipeline.md b/.qoder/repowiki/en/content/Architecture Overview/Data Flow and Processing/Transaction Processing Pipeline.md index 6552643d72..0feac2c849 100644 --- a/.qoder/repowiki/en/content/Architecture Overview/Data Flow and Processing/Transaction Processing Pipeline.md +++ b/.qoder/repowiki/en/content/Architecture Overview/Data Flow and Processing/Transaction Processing Pipeline.md @@ -1,4 +1,4 @@ -# Transaction Processing Pipeline +# Transaction Processing Pipeline **Referenced Files in This Document** @@ -331,7 +331,7 @@ DB --> TO["transaction_object.hpp"] ## Performance Considerations - Batch-like processing: pending transactions are accumulated and applied together during block processing, reducing repeated validations -- Caching: database caches frequently accessed state (accounts, witnesses, etc.) to avoid repeated lookups +- Caching: database caches frequently accessed state (accounts, validators, etc.) to avoid repeated lookups - Validation flags: skip flags allow bypassing expensive checks during reindex or trusted contexts - Indexing: transaction index supports duplicate detection and expiration cleanup diff --git a/.qoder/repowiki/en/content/Architecture Overview/Plugin Architecture/Inter-Plugin Communication.md b/.qoder/repowiki/en/content/Architecture Overview/Plugin Architecture/Inter-Plugin Communication.md index 99637a68e7..a95daf87f5 100644 --- a/.qoder/repowiki/en/content/Architecture Overview/Plugin Architecture/Inter-Plugin Communication.md +++ b/.qoder/repowiki/en/content/Architecture Overview/Plugin Architecture/Inter-Plugin Communication.md @@ -1,4 +1,4 @@ -# Inter-Plugin Communication +# Inter-Plugin Communication **Referenced Files in This Document** @@ -424,7 +424,7 @@ Chain --> RawBlock["raw_block::plugin"] Chain --> BlockInfo["block_info::plugin"] Chain --> Tags["tags::plugin"] Chain --> Follow["follow::plugin"] -Chain --> Witness["witness_plugin::witness_plugin"] +Chain --> validator["witness_plugin::witness_plugin"] Chain --> CommitteeAPI["committee_api::committee_api"] Chain --> InviteAPI["invite_api::invite_api"] Chain --> PaidSubAPI["paid_subscription_api::paid_subscription_api"] diff --git a/.qoder/repowiki/en/content/Architecture Overview/Plugin Architecture/Plugin Architecture.md b/.qoder/repowiki/en/content/Architecture Overview/Plugin Architecture/Plugin Architecture.md index 6fd8b3ea8e..442d21c298 100644 --- a/.qoder/repowiki/en/content/Architecture Overview/Plugin Architecture/Plugin Architecture.md +++ b/.qoder/repowiki/en/content/Architecture Overview/Plugin Architecture/Plugin Architecture.md @@ -1,4 +1,4 @@ -# Plugin Architecture +# Plugin Architecture **Referenced Files in This Document** @@ -729,7 +729,7 @@ Snapshot->>Snapshot : Create Snapshot end end Chain->>Chain : Fire on_sync() -Chain->>Chain : Start P2P/Witness +Chain->>Chain : Start P2P/validator ``` **Diagram sources** diff --git a/.qoder/repowiki/en/content/Architecture Overview/Plugin Architecture/Plugin Lifecycle and Registration.md b/.qoder/repowiki/en/content/Architecture Overview/Plugin Architecture/Plugin Lifecycle and Registration.md index 459d578e23..2c3ab84a00 100644 --- a/.qoder/repowiki/en/content/Architecture Overview/Plugin Architecture/Plugin Lifecycle and Registration.md +++ b/.qoder/repowiki/en/content/Architecture Overview/Plugin Architecture/Plugin Lifecycle and Registration.md @@ -1,4 +1,4 @@ -# Plugin Lifecycle and Registration +# Plugin Lifecycle and Registration **Referenced Files in This Document** @@ -327,7 +327,7 @@ end The deferred execution model ensures that snapshot operations occur at the appropriate time in the plugin lifecycle: - **Snapshot Loading**: Executed during chain plugin startup, before on_sync() fires, ensuring P2P sync starts from the snapshot head block. -- **Snapshot Creation**: Executed after full database load (including replay), but before on_sync(), preventing P2P/witness startup. +- **Snapshot Creation**: Executed after full database load (including replay), but before on_sync(), preventing P2P/validator startup. - **P2P Snapshot Sync**: Executed when state is empty (head_block_num == 0), before on_sync(), enabling bootstrap from trusted peers. **Section sources** diff --git a/.qoder/repowiki/en/content/Chain Plugin.md b/.qoder/repowiki/en/content/Chain Plugin.md index 719ab51969..ca73407da8 100644 --- a/.qoder/repowiki/en/content/Chain Plugin.md +++ b/.qoder/repowiki/en/content/Chain Plugin.md @@ -1,4 +1,4 @@ -# Chain Plugin +# Chain Plugin **Referenced Files in This Document** @@ -668,7 +668,7 @@ The plugin integrates with several other components with enhanced coordination: - JSON-RPC plugin for API exposure - Snapshot plugin for state recovery with deferred execution support - P2P plugin for block propagation -- Witness plugin for block production +- Validator Plugin for block production - Database plugin for state persistence **Updated** Enhanced integration with snapshot plugin includes sophisticated deferred execution mechanisms, automatic callback registration, and comprehensive recovery system coordination. The default shared memory directory has been changed from 'blockchain' to 'state' for better organizational structure and consistent data directory usage. The enhanced logging system provides visual feedback for better debugging experience. **New** The automatic recovery system provides seamless protection against shared memory corruption with immediate detection and restoration capabilities. diff --git a/.qoder/repowiki/en/content/Configuration Management/Configuration Management.md b/.qoder/repowiki/en/content/Configuration Management/Configuration Management.md index 7564177402..f6121ac762 100644 --- a/.qoder/repowiki/en/content/Configuration Management/Configuration Management.md +++ b/.qoder/repowiki/en/content/Configuration Management/Configuration Management.md @@ -1,4 +1,4 @@ -# Configuration Management +# Configuration Management **Referenced Files in This Document** @@ -17,7 +17,7 @@ - [building.md](file://documentation/building.md) - [testnet.md](file://documentation/testnet.md) - [plugin.md](file://documentation/plugin.md) -- [witness.cpp](file://plugins/witness/witness.cpp) +- [validator.cpp](file://plugins/validator/validator.cpp) - [config_testnet.hpp](file://libraries/protocol/include/graphene/protocol/config_testnet.hpp) - [config.hpp](file://libraries/protocol/include/graphene/protocol/config.hpp) - [snapshot-plugin.md](file://documentation/snapshot-plugin.md) @@ -46,13 +46,13 @@ 10. [Appendices](#appendices) ## Introduction -This document describes the configuration management system for VIZ CPP Node. It explains configuration file structure, runtime parameters, environment variable overrides, node types (full node, witness node, low-memory node), network configuration, plugin activation, performance tuning, logging, Docker-specific configuration, build-time options, and troubleshooting guidance. Practical deployment scenarios (production, testnet, development) are included. +This document describes the configuration management system for VIZ CPP Node. It explains configuration file structure, runtime parameters, environment variable overrides, node types (full node, validator node, low-memory node), network configuration, plugin activation, performance tuning, logging, Docker-specific configuration, build-time options, and troubleshooting guidance. Practical deployment scenarios (production, testnet, development) are included. ## Project Structure The configuration system centers around: - A primary configuration file template for mainnet - Testnet-specific configuration -- Witness-specific configuration +- validator-specific configuration - MongoDB-enabled configuration variants - Dockerfiles for production, testnet, and low-memory builds - A container entrypoint script that supports environment variable overrides @@ -62,7 +62,7 @@ The configuration system centers around: graph TB cfg_main["Config Template
share/vizd/config/config.ini"] cfg_test["Testnet Config
share/vizd/config/config_testnet.ini"] -cfg_wit["Witness Config
share/vizd/config/config_witness.ini"] +cfg_wit["validator Config
share/vizd/config/config_witness.ini"] cfg_mongo["Mongo Config
share/vizd/config/config_mongo.ini"] cfg_dbg["Debug Config
share/vizd/config/config_debug.ini"] cfg_dbg_mongo["Debug+Mongo Config
share/vizd/config/config_debug_mongo.ini"] @@ -122,7 +122,7 @@ Key configuration categories: - Lock/wait tuning for database operations - Shared memory sizing and growth policy - Plugin list and activation -- Witness production controls +- validator production controls - Logging configuration - **Snapshot configuration** (updated with new default behavior) @@ -141,7 +141,7 @@ participant Env as "Environment Variables" participant Node as "Node Binary
main.cpp" participant Config as "INI Loader
load_config_sections/load_logging_config" Entrypoint->>Env : Read VIZD_* variables -Entrypoint->>Node : Pass CLI flags (--p2p-endpoint, --rpc-endpoint,
--p2p-seed-node, --witness, --private-key) +Entrypoint->>Node : Pass CLI flags (--p2p-endpoint, --rpc-endpoint,
--p2p-seed-node, --validator, --private-key) Node->>Node : Parse program options Node->>Config : Load logging config from config path Config-->>Node : fc : : logging_config @@ -179,7 +179,7 @@ Program options include logging configuration and standard node options. The nod ### Environment Variable Overrides (Docker) The container entrypoint supports the following environment variables: - VIZD_SEED_NODES: Comma-separated seed nodes -- VIZD_WITNESS_NAME: Witness name to operate +- VIZD_WITNESS_NAME: validator name to operate - VIZD_PRIVATE_KEY: Private key for signing - VIZD_RPC_ENDPOINT: Override RPC endpoint - VIZD_P2P_ENDPOINT: Override P2P endpoint @@ -197,8 +197,8 @@ These variables override defaults and inject CLI flags at runtime. - Uses the main configuration template with default plugin sets suitable for full synchronization and API exposure. - Testnet: - Includes testnet-specific defaults and enables stale production for continuous block production. -- Witness node: - - Activates witness and witness_api plugins, binds RPC endpoints to localhost by default, and includes witness credentials. +- validator node: + - Activates validator and witness_api plugins, binds RPC endpoints to localhost by default, and includes validator credentials. - Low-memory node: - Built with a dedicated flag to reduce memory footprint; Dockerfile demonstrates enabling this build-time option. @@ -206,11 +206,11 @@ These variables override defaults and inject CLI flags at runtime. flowchart TD Start(["Select Node Type"]) --> Full["Full Node
Mainnet"] Start --> Testnet["Testnet Node"] -Start --> Witness["Witness Node"] +Start --> validator["validator Node"] Start --> LowMem["Low-Memory Node"] Full --> FullCfg["Use config.ini"] Testnet --> TestCfg["Use config_testnet.ini"] -Witness --> WitCfg["Use config_witness.ini"] +validator --> WitCfg["Use config_witness.ini"] LowMem --> LowMemFlag["Build with LOW_MEMORY_NODE=TRUE"] ``` @@ -419,10 +419,10 @@ These are set via CMake flags in Dockerfiles and documented in the building guid - Use the debug configuration templates to enable additional plugins and adjust logging. - Optionally enable MongoDB plugin configuration. -- Witness node - - Use the witness configuration template and bind RPC endpoints to localhost. - - Provide witness name and private key via environment variables. - - **Updated**: Snapshot configuration supports witness-aware deferral to prevent missed production slots. +- validator node + - Use the validator configuration template and bind RPC endpoints to localhost. + - Provide validator name and private key via environment variables. + - **Updated**: Snapshot configuration supports validator-aware deferral to prevent missed production slots. **Section sources** - [config.ini:1-136](file://share/vizd/config/config.ini#L1-L136) @@ -474,15 +474,15 @@ DataDir --> SnapshotDefault["Snapshot Default Dir
/snapshots"] ## Troubleshooting Guide -### Witness Configuration Issues +### validator Configuration Issues -**Updated** The witness configuration defaults have been updated to improve reliability and participation calculations: +**Updated** The validator configuration defaults have been updated to improve reliability and participation calculations: -- **enable-stale-production default changed**: The default value for `enable-stale-production` has been changed from `false` to `true`. This means witness nodes will now automatically continue producing blocks even when the chain appears stale, improving network resilience during network partitions or temporary forks. +- **enable-stale-production default changed**: The default value for `enable-stale-production` has been changed from `false` to `true`. This means validator nodes will now automatically continue producing blocks even when the chain appears stale, improving network resilience during network partitions or temporary forks. - **required-participation calculation**: The `required-participation` parameter now uses the formula `33 * CHAIN_1_PERCENT` instead of a hardcoded percentage. With `CHAIN_1_PERCENT` equal to 100 (representing 1% in the 10000-point scale), this calculates to 3300, which represents 33% participation threshold. -- **Witness production failures**: If witness production is failing, check the participation threshold calculation. The system now requires at least 33% of witnesses to be participating for block production to continue. +- **validator production failures**: If validator production is failing, check the participation threshold calculation. The system now requires at least 33% of validators to be participating for block production to continue. ### Snapshot Configuration Issues @@ -523,10 +523,10 @@ Common configuration issues and validation techniques: - Docker volume and permissions - Ensure persistent volumes are mounted and owned by the node user. - Confirm snapshot extraction occurs when present. -- Witness production issues - - Verify witness name and private key are correctly configured. +- validator production issues + - Verify validator name and private key are correctly configured. - Check participation threshold calculations using the CHAIN_1_PERCENT constant. - - Monitor for "low participation" errors indicating below-threshold witness participation. + - Monitor for "low participation" errors indicating below-threshold validator participation. - **Updated**: Snapshot configuration issues - Verify snapshot directory permissions and disk space - Check snapshot file integrity and naming conventions @@ -538,7 +538,7 @@ Common configuration issues and validation techniques: - [config.ini:118-136](file://share/vizd/config/config.ini#L118-L136) - [plugin.md:14-18](file://documentation/plugin.md#L14-L18) - [vizd.sh:44-53](file://share/vizd/vizd.sh#L44-L53) -- [witness.cpp:125-130](file://plugins/witness/witness.cpp#L125-L130) +- [validator.cpp:125-130](file://plugins/validator/validator.cpp#L125-L130) - [config_testnet.hpp:57-59](file://libraries/protocol/include/graphene/protocol/config_testnet.hpp#L57-L59) - [config.hpp:57-59](file://libraries/protocol/include/graphene/protocol/config.hpp#L57-L59) - [plugin.cpp:2974-2983](file://plugins/snapshot/plugin.cpp#L2974-L2983) @@ -574,10 +574,10 @@ VIZ CPP Node offers a flexible configuration system combining INI-based settings - block-num-check-free-size - Plugins - plugin (repeatable) -- Witness +- validator - enable-stale-production (default: true) - required-participation (default: 33% calculated as 33 * CHAIN_1_PERCENT) - - witness + - validator - private-key - Logging - log.console_appender.* @@ -598,7 +598,7 @@ VIZ CPP Node offers a flexible configuration system combining INI-based settings - test-trusted-seeds (default: false) - dlt-block-log-max-blocks (default: 100000) -**Updated** Witness configuration defaults now use improved defaults for better network reliability and accurate participation calculations. +**Updated** validator configuration defaults now use improved defaults for better network reliability and accurate participation calculations. **Section sources** - [config.ini:1-136](file://share/vizd/config/config.ini#L1-L136) @@ -607,7 +607,7 @@ VIZ CPP Node offers a flexible configuration system combining INI-based settings - [config_mongo.ini:1-135](file://share/vizd/config/config_mongo.ini#L1-L135) - [config_debug.ini:1-126](file://share/vizd/config/config_debug.ini#L1-L126) - [config_debug_mongo.ini:1-135](file://share/vizd/config/config_debug_mongo.ini#L1-L135) -- [witness.cpp:125-130](file://plugins/witness/witness.cpp#L125-L130) +- [validator.cpp:125-130](file://plugins/validator/validator.cpp#L125-L130) - [config_testnet.hpp:57-59](file://libraries/protocol/include/graphene/protocol/config_testnet.hpp#L57-L59) - [config.hpp:57-59](file://libraries/protocol/include/graphene/protocol/config.hpp#L57-L59) - [plugin.cpp:2974-2983](file://plugins/snapshot/plugin.cpp#L2974-L2983) @@ -627,9 +627,9 @@ VIZ CPP Node offers a flexible configuration system combining INI-based settings - [vizd.sh:62-72](file://share/vizd/vizd.sh#L62-L72) - [vizd.sh:74-81](file://share/vizd/vizd.sh#L74-L81) -### Appendix C: Witness Participation Calculation Details +### Appendix C: validator Participation Calculation Details -**New** The witness participation calculation system now uses the CHAIN_1_PERCENT constant for precise percentage calculations: +**New** The validator participation calculation system now uses the CHAIN_1_PERCENT constant for precise percentage calculations: - **CHAIN_1_PERCENT definition**: Defined as `CHAIN_100_PERCENT/100` where `CHAIN_100_PERCENT` equals 10000 - **Participation threshold**: Default required participation is 33% (3300/10000) @@ -639,7 +639,7 @@ VIZ CPP Node offers a flexible configuration system combining INI-based settings This change ensures more accurate participation calculations and consistent behavior across mainnet and testnet configurations. **Section sources** -- [witness.cpp:125-130](file://plugins/witness/witness.cpp#L125-L130) +- [validator.cpp:125-130](file://plugins/validator/validator.cpp#L125-L130) - [config_testnet.hpp:57-59](file://libraries/protocol/include/graphene/protocol/config_testnet.hpp#L57-L59) - [config.hpp:57-59](file://libraries/protocol/include/graphene/protocol/config.hpp#L57-L59) diff --git a/.qoder/repowiki/en/content/Configuration Management/Docker Configuration.md b/.qoder/repowiki/en/content/Configuration Management/Docker Configuration.md index 0d5f9fbb2c..fa03236047 100644 --- a/.qoder/repowiki/en/content/Configuration Management/Docker Configuration.md +++ b/.qoder/repowiki/en/content/Configuration Management/Docker Configuration.md @@ -1,4 +1,4 @@ -# Docker Configuration +# Docker Configuration **Referenced Files in This Document** @@ -189,8 +189,8 @@ RuntimeStage --> Entrypoint["Start via vizd.sh"] ### Environment Variables The container supports the following environment variables to customize runtime behavior: - VIZD_SEED_NODES: Space-delimited list of seed nodes to connect to at startup. Overrides default seed list. -- VIZD_WITNESS_NAME: Name of the witness to operate when block production is enabled. -- VIZD_PRIVATE_KEY: Private key for signing blocks (when operating a witness). +- VIZD_WITNESS_NAME: Name of the validator to operate when block production is enabled. +- VIZD_PRIVATE_KEY: Private key for signing blocks (when operating a validator). - VIZD_RPC_ENDPOINT: RPC endpoint binding (default: 0.0.0.0:8090). - VIZD_P2P_ENDPOINT: P2P endpoint binding (default: 0.0.0.0:2001). - VIZD_EXTRA_OPTS: Additional command-line options appended to the node invocation. @@ -289,8 +289,8 @@ These are defined in the Dockerfiles and used by the entrypoint script. - Use the testnet image tag and adjust seed nodes if required. - Reference: [README.md:16-20](file://README.md#L16-L20), [Dockerfile-testnet:94-103](file://share/vizd/docker/Dockerfile-testnet#L94-L103) -- Witness node - - Set VIZD_WITNESS_NAME and VIZD_PRIVATE_KEY to operate a witness. +- validator node + - Set VIZD_WITNESS_NAME and VIZD_PRIVATE_KEY to operate a validator. - Reference: [vizd.sh:31-37](file://share/vizd/vizd.sh#L31-L37) - MongoDB analytics node @@ -422,7 +422,7 @@ The VIZ C++ Node provides multiple Docker images tailored for production, testne ### Environment Variable Reference - VIZD_SEED_NODES: Seed nodes to connect to. -- VIZD_WITNESS_NAME: Witness name for block production. +- VIZD_WITNESS_NAME: validator name for block production. - VIZD_PRIVATE_KEY: Private key for signing blocks. - VIZD_RPC_ENDPOINT: RPC endpoint binding. - VIZD_P2P_ENDPOINT: P2P endpoint binding. diff --git a/.qoder/repowiki/en/content/Configuration Management/Network Configuration.md b/.qoder/repowiki/en/content/Configuration Management/Network Configuration.md index b631504ff5..44835079c0 100644 --- a/.qoder/repowiki/en/content/Configuration Management/Network Configuration.md +++ b/.qoder/repowiki/en/content/Configuration Management/Network Configuration.md @@ -1,4 +1,4 @@ -# Network Configuration +# Network Configuration **Referenced Files in This Document** @@ -494,7 +494,7 @@ Example keys: #### Testnet - Use testnet-specific defaults and endpoints. -- Enable witness participation and adjust required participation as needed. +- Enable validator participation and adjust required participation as needed. - Configure stale sync detection with appropriate timeout for testnet conditions. Example keys: diff --git a/.qoder/repowiki/en/content/Configuration Management/Node Configuration.md b/.qoder/repowiki/en/content/Configuration Management/Node Configuration.md index 34f63c938c..18915da869 100644 --- a/.qoder/repowiki/en/content/Configuration Management/Node Configuration.md +++ b/.qoder/repowiki/en/content/Configuration Management/Node Configuration.md @@ -1,4 +1,4 @@ -# Node Configuration +# Node Configuration **Referenced Files in This Document** @@ -13,7 +13,7 @@ - [webserver_plugin.hpp](file://plugins/webserver/include/graphene/plugins/webserver/webserver_plugin.hpp) - [p2p_plugin.hpp](file://plugins/p2p/include/graphene/plugins/p2p/p2p_plugin.hpp) - [chain_plugin.hpp](file://plugins/chain/include/graphene/plugins/chain/plugin.hpp) -- [witness_plugin.hpp](file://plugins/witness/include/graphene/plugins/witness/witness.hpp) +- [witness_plugin.hpp](file://plugins/validator/include/graphene/plugins/validator/validator.hpp) - [config.hpp](file://libraries/protocol/include/graphene/protocol/config.hpp) - [Dockerfile-production](file://share/vizd/docker/Dockerfile-production) - [Dockerfile-lowmem](file://share/vizd/docker/Dockerfile-lowmem) @@ -32,7 +32,7 @@ 10. [Appendices](#appendices) ## Introduction -This document provides comprehensive guidance for configuring a VIZ CPP Node. It explains the configuration file structure, available parameters, defaults, and acceptable ranges. It also covers different node types (full node, witness node, low-memory node, testnet node), essential settings (database location, plugin activation, network parameters, performance tuning), authentication and API access controls, security configurations, and practical deployment examples. Finally, it includes validation tips, syntax guidance, and organizational best practices for configuration files. +This document provides comprehensive guidance for configuring a VIZ CPP Node. It explains the configuration file structure, available parameters, defaults, and acceptable ranges. It also covers different node types (full node, validator node, low-memory node, testnet node), essential settings (database location, plugin activation, network parameters, performance tuning), authentication and API access controls, security configurations, and practical deployment examples. Finally, it includes validation tips, syntax guidance, and organizational best practices for configuration files. ## Project Structure The configuration system centers around a primary configuration file and several prebuilt templates for different deployment profiles. The node binary loads plugins and applies logging configuration from the same file. @@ -76,7 +76,7 @@ cfg_stock --> bin_main - [webserver_plugin.hpp](file://plugins/webserver/include/graphene/plugins/webserver/webserver_plugin.hpp#L32-L57) - [p2p_plugin.hpp](file://plugins/p2p/include/graphene/plugins/p2p/p2p_plugin.hpp#L18-L52) - [chain_plugin.hpp](file://plugins/chain/include/graphene/plugins/chain/plugin.hpp#L21-L96) -- [witness_plugin.hpp](file://plugins/witness/include/graphene/plugins/witness/witness_plugin.hpp#L34-L65) +- [witness_plugin.hpp](file://plugins/validator/include/graphene/plugins/validator/witness_plugin.hpp#L34-L65) **Section sources** - [config.ini](file://share/vizd/config/config.ini#L1-L130) @@ -123,11 +123,11 @@ This section enumerates the most important configuration parameters grouped by c - follow-max-feed-size: Integer; default 500. See [config.ini](file://share/vizd/config/config.ini#L94-L94). - pm-account-range: JSON pair ["from","to"]; default unset. See [config.ini](file://share/vizd/config/config.ini#L97-L97). -- Witness Production +- validator Production - enable-stale-production: Boolean; default false (production), true (testnet). See [config.ini](file://share/vizd/config/config.ini#L100-L100), [config_testnet.ini](file://share/vizd/config/config_testnet.ini#L100-L100). - required-participation: Integer percentage (0–99); default unset (plugin default). See [config.ini](file://share/vizd/config/config.ini#L103-L103), [config_testnet.ini](file://share/vizd/config/config_testnet.ini#L103-L103). - - witness: String name; default unset (non-witness), "committee" (testnet). See [config.ini](file://share/vizd/config/config.ini#L106-L106), [config_testnet.ini](file://share/vizd/config/config_testnet.ini#L106-L106). - - private-key: WIF key; default unset (non-witness), testnet committee key shown. See [config.ini](file://share/vizd/config/config.ini#L109-L109), [config_testnet.ini](file://share/vizd/config/config_testnet.ini#L111-L111). + - validator: String name; default unset (non-validator), "committee" (testnet). See [config.ini](file://share/vizd/config/config.ini#L106-L106), [config_testnet.ini](file://share/vizd/config/config_testnet.ini#L106-L106). + - private-key: WIF key; default unset (non-validator), testnet committee key shown. See [config.ini](file://share/vizd/config/config.ini#L109-L109), [config_testnet.ini](file://share/vizd/config/config_testnet.ini#L111-L111). - MongoDB (when enabled) - mongodb-uri: URI string; default unset. See [config_mongo.ini](file://share/vizd/config/config_mongo.ini#L72-L72), [config_debug_mongo.ini](file://share/vizd/config/config_debug_mongo.ini#L72-L72). @@ -153,7 +153,7 @@ Notes on defaults and ranges: - [main.cpp](file://programs/vizd/main.cpp#L167-L191) ## Architecture Overview -The node binary initializes plugins and applies logging configuration from the selected configuration file. The webserver plugin exposes HTTP and WebSocket endpoints. The P2P plugin manages peer connections. The chain plugin coordinates block acceptance and transaction processing. The witness plugin participates in block production when configured. +The node binary initializes plugins and applies logging configuration from the selected configuration file. The webserver plugin exposes HTTP and WebSocket endpoints. The P2P plugin manages peer connections. The chain plugin coordinates block acceptance and transaction processing. The Validator Plugin participates in block production when configured. ```mermaid sequenceDiagram @@ -163,14 +163,14 @@ participant Log as "Logging Loader" participant WS as "Webserver Plugin" participant P2P as "P2P Plugin" participant Chain as "Chain Plugin" -participant Wit as "Witness Plugin" +participant Wit as "Validator Plugin" CLI->>Bin : Start process with config path Bin->>Log : Load logging config from config file Log-->>Bin : Apply logging configuration Bin->>WS : Initialize webserver plugin Bin->>P2P : Initialize p2p plugin Bin->>Chain : Initialize chain plugin -Bin->>Wit : Initialize witness plugin (if enabled) +Bin->>Wit : Initialize Validator Plugin (if enabled) Bin->>Bin : Startup and exec loop ``` @@ -179,19 +179,19 @@ Bin->>Bin : Startup and exec loop - [webserver_plugin.hpp](file://plugins/webserver/include/graphene/plugins/webserver/webserver_plugin.hpp#L32-L57) - [p2p_plugin.hpp](file://plugins/p2p/include/graphene/plugins/p2p/p2p_plugin.hpp#L18-L52) - [chain_plugin.hpp](file://plugins/chain/include/graphene/plugins/chain/plugin.hpp#L21-L96) -- [witness_plugin.hpp](file://plugins/witness/include/graphene/plugins/witness/witness_plugin.hpp#L34-L65) +- [witness_plugin.hpp](file://plugins/validator/include/graphene/plugins/validator/witness_plugin.hpp#L34-L65) ## Detailed Component Analysis ### Node Types and Templates - Full node (mainnet) - - Typical characteristics: Public P2P endpoint, broad plugin set, production RPC endpoints, witness disabled by default. + - Typical characteristics: Public P2P endpoint, broad plugin set, production RPC endpoints, validator disabled by default. - Reference template: [config.ini](file://share/vizd/config/config.ini#L1-L130) - Testnet node - - Characteristics: enable-stale-production=true, required-participation=0, witness="committee", private-key for committee. + - Characteristics: enable-stale-production=true, required-participation=0, validator="committee", private-key for committee. - Reference template: [config_testnet.ini](file://share/vizd/config/config_testnet.ini#L1-L132) -- Witness node - - Characteristics: webserver endpoints bound to localhost, witness and private-key configured, skip-virtual-ops=true. +- validator node + - Characteristics: webserver endpoints bound to localhost, validator and private-key configured, skip-virtual-ops=true. - Reference template: [config_witness.ini](file://share/vizd/config/config_witness.ini#L1-L107) - Low-memory node - Build-time flag: LOW_MEMORY_NODE=TRUE via CMake; exposed via Dockerfile-lowmem. @@ -205,7 +205,7 @@ Bin->>Bin : Startup and exec loop Practical selection guidance: - Choose config_testnet.ini for development and testing. -- Use config_witness.ini when operating a validating witness with private keys. +- Use config_witness.ini when operating a validating validator with private keys. - Use config_mongo.ini when integrating external analytics or historical archiving. - Use config_stock_exchange.ini for market data consumers requiring minimal overhead. @@ -248,7 +248,7 @@ Operational notes: - Examples: - Full node: [config.ini](file://share/vizd/config/config.ini#L69-L73) - Testnet: [config_testnet.ini](file://share/vizd/config/config_testnet.ini#L69-L73) - - Witness: [config_witness.ini](file://share/vizd/config/config_witness.ini#L68-L68) + - validator: [config_witness.ini](file://share/vizd/config/config_witness.ini#L68-L68) - MongoDB: [config_mongo.ini](file://share/vizd/config/config_mongo.ini#L69-L69), [config_debug_mongo.ini](file://share/vizd/config/config_debug_mongo.ini#L69-L69) - Stock exchange: [config_stock_exchange.ini](file://share/vizd/config/config_stock_exchange.ini#L69-L69) @@ -272,7 +272,7 @@ Validation tip: - webserver-ws-endpoint: [config.ini](file://share/vizd/config/config.ini#L20-L20) Security note: -- Bind RPC to localhost for witness nodes to prevent external exposure: [config_witness.ini](file://share/vizd/config/config_witness.ini#L17-L20). +- Bind RPC to localhost for validator nodes to prevent external exposure: [config_witness.ini](file://share/vizd/config/config_witness.ini#L17-L20). **Section sources** - [config.ini](file://share/vizd/config/config.ini#L1-L20) @@ -299,16 +299,16 @@ Recommendations: - RPC endpoints: - HTTP: [config.ini](file://share/vizd/config/config.ini#L17-L17) - WebSocket: [config.ini](file://share/vizd/config/config.ini#L20-L20) -- Binding to localhost for witness nodes: +- Binding to localhost for validator nodes: - [config_witness.ini](file://share/vizd/config/config_witness.ini#L17-L20) -- Witness credentials: - - witness and private-key: [config_witness.ini](file://share/vizd/config/config_witness.ini#L83-L86), [config_testnet.ini](file://share/vizd/config/config_testnet.ini#L106-L111) +- validator credentials: + - validator and private-key: [config_witness.ini](file://share/vizd/config/config_witness.ini#L83-L86), [config_testnet.ini](file://share/vizd/config/config_testnet.ini#L106-L111) - Logging security: - Console and file appenders: [config.ini](file://share/vizd/config/config.ini#L112-L130) - Program options for logging: [main.cpp](file://programs/vizd/main.cpp#L167-L191) Best practices: -- Restrict RPC access to trusted networks or bind to localhost for witness nodes. +- Restrict RPC access to trusted networks or bind to localhost for validator nodes. - Rotate private keys and store them securely; avoid committing secrets to repositories. - Use file appenders for persistent logs and monitor log rotation externally if needed. @@ -322,9 +322,9 @@ Best practices: - Full node for public API: - Use [config.ini](file://share/vizd/config/config.ini#L1-L130) with public RPC endpoints and broad plugin set. - Testnet validator: - - Use [config_testnet.ini](file://share/vizd/config/config_testnet.ini#L1-L132) with enable-stale-production and committee witness settings. -- Witness operator: - - Use [config_witness.ini](file://share/vizd/config/config_witness.ini#L1-L107) with localhost RPC and configured witness/private-key. + - Use [config_testnet.ini](file://share/vizd/config/config_testnet.ini#L1-L132) with enable-stale-production and committee validator settings. +- validator operator: + - Use [config_witness.ini](file://share/vizd/config/config_witness.ini#L1-L107) with localhost RPC and configured validator/private-key. - Low-memory deployment: - Build with LOW_MEMORY_NODE=TRUE via [Dockerfile-lowmem](file://share/vizd/docker/Dockerfile-lowmem#L48-L48). - MongoDB integration: @@ -349,7 +349,7 @@ Best practices: - Ensure numeric values are within reasonable bounds for your hardware. - Verify plugin names match available plugins. - Confirm file paths for log appenders are writable. - - For witness nodes, confirm witness and private-key are set consistently. + - For validator nodes, confirm validator and private-key are set consistently. - Example references: - Logging program options: [main.cpp](file://programs/vizd/main.cpp#L167-L191) - Config parsing and logging loader: [main.cpp](file://programs/vizd/main.cpp#L194-L288) @@ -371,7 +371,7 @@ Best practices: [No sources needed since this section provides general guidance] ## Dependency Analysis -The node binary registers and initializes plugins, which in turn depend on each other. The webserver plugin requires the JSON-RPC plugin. The P2P plugin depends on the chain plugin. The witness plugin depends on both chain and P2P. +The node binary registers and initializes plugins, which in turn depend on each other. The webserver plugin requires the JSON-RPC plugin. The P2P plugin depends on the chain plugin. The Validator Plugin depends on both chain and P2P. ```mermaid graph LR @@ -395,14 +395,14 @@ Wit --> P2P - [webserver_plugin.hpp](file://plugins/webserver/include/graphene/plugins/webserver/webserver_plugin.hpp#L38-L43) - [p2p_plugin.hpp](file://plugins/p2p/include/graphene/plugins/p2p/p2p_plugin.hpp#L20-L21) - [chain_plugin.hpp](file://plugins/chain/include/graphene/plugins/chain/plugin.hpp#L23-L24) -- [witness_plugin.hpp](file://plugins/witness/include/graphene/plugins/witness/witness_plugin.hpp#L36-L37) +- [witness_plugin.hpp](file://plugins/validator/include/graphene/plugins/validator/witness_plugin.hpp#L36-L37) **Section sources** - [main.cpp](file://programs/vizd/main.cpp#L62-L90) - [webserver_plugin.hpp](file://plugins/webserver/include/graphene/plugins/webserver/webserver_plugin.hpp#L32-L57) - [p2p_plugin.hpp](file://plugins/p2p/include/graphene/plugins/p2p/p2p_plugin.hpp#L18-L52) - [chain_plugin.hpp](file://plugins/chain/include/graphene/plugins/chain/plugin.hpp#L21-L96) -- [witness_plugin.hpp](file://plugins/witness/include/graphene/plugins/witness/witness_plugin.hpp#L34-L65) +- [witness_plugin.hpp](file://plugins/validator/include/graphene/plugins/validator/witness_plugin.hpp#L34-L65) ## Performance Considerations - Single write thread: Reduces lock contention for database writes; recommended for high RPC throughput. @@ -420,11 +420,11 @@ Common issues and resolutions: - Insufficient disk space for shared memory: - Increase min-free-shared-file-size and inc-shared-file-size; monitor growth. - References: [config.ini](file://share/vizd/config/config.ini#L58-L62) -- Witness not producing blocks: - - Verify witness name and private-key; ensure enable-stale-production and required-participation are appropriate for the network. +- validator not producing blocks: + - Verify validator name and private-key; ensure enable-stale-production and required-participation are appropriate for the network. - References: [config_witness.ini](file://share/vizd/config/config_witness.ini#L83-L86), [config_testnet.ini](file://share/vizd/config/config_testnet.ini#L100-L103) - RPC endpoints unreachable: - - Confirm binding address and port; for witness nodes, ensure localhost binding is intended. + - Confirm binding address and port; for validator nodes, ensure localhost binding is intended. - References: [config.ini](file://share/vizd/config/config.ini#L17-L20), [config_witness.ini](file://share/vizd/config/config_witness.ini#L17-L20) - Logging misconfiguration: - Validate dotted section names and file paths; ensure appenders are writable. diff --git a/.qoder/repowiki/en/content/Contributing and Development.md b/.qoder/repowiki/en/content/Contributing and Development.md index 42b8c0305b..1c0b1c909d 100644 --- a/.qoder/repowiki/en/content/Contributing and Development.md +++ b/.qoder/repowiki/en/content/Contributing and Development.md @@ -1,4 +1,4 @@ -# Contributing and Development +# Contributing and Development **Referenced Files in This Document** @@ -219,7 +219,7 @@ HTML --> End ### Performance Criteria - Build types: Prefer Release builds for production and benchmarking; use Debug with coverage for development and profiling -- Low-memory builds: Consider low-memory node builds for resource-constrained environments (e.g., witnesses) +- Low-memory builds: Consider low-memory node builds for resource-constrained environments (e.g., validators) **Section sources** - [documentation/building.md](file://documentation/building.md#L3-L16) diff --git a/.qoder/repowiki/en/content/Core Libraries/Core Libraries.md b/.qoder/repowiki/en/content/Core Libraries/Core Libraries.md index cbb356985b..69b97b12b0 100644 --- a/.qoder/repowiki/en/content/Core Libraries/Core Libraries.md +++ b/.qoder/repowiki/en/content/Core Libraries/Core Libraries.md @@ -1,4 +1,4 @@ -# Core Libraries +# Core Libraries **Referenced Files in This Document** @@ -27,15 +27,15 @@ - [libraries/wallet/include/graphene/wallet/api_documentation.hpp](file://libraries/wallet/include/graphene/wallet/api_documentation.hpp) - [plugins/chain/include/graphene/plugins/chain/plugin.hpp](file://plugins/chain/include/graphene/plugins/chain/plugin.hpp) - [plugins/p2p/include/graphene/plugins/p2p/p2p_plugin.hpp](file://plugins/p2p/include/graphene/plugins/p2p/p2p_plugin.hpp) -- [plugins/witness/witness.cpp](file://plugins/witness/witness.cpp) +- [plugins/validator/validator.cpp](file://plugins/validator/validator.cpp) - [programs/vizd/main.cpp](file://programs/vizd/main.cpp) ## Update Summary **Changes Made** -- Enhanced witness scheduling system documentation with emergency mode integration +- Enhanced validator scheduling system documentation with emergency mode integration - Added comprehensive peer connection management for emergency consensus -- Updated witness scheduling with hybrid schedule implementation during emergency mode +- Updated validator scheduling with hybrid schedule implementation during emergency mode - Expanded emergency consensus activation and deactivation logic - Added peer soft-banning mechanism for emergency fork management - Updated fork database tie-breaking with deterministic hash-based selection @@ -64,9 +64,9 @@ This document explains the VIZ CPP Node core libraries that form the foundation - Network library: peer-to-peer communication and synchronization - Wallet library: transaction signing and key management -These libraries interact closely: the Chain library validates and applies operations, the Protocol library defines operations and transactions, the Network library propagates blocks and transactions across peers, and the Wallet library signs transactions before they are broadcast. The system now includes enhanced emergency consensus mode with integrated witness scheduling and improved peer connection management for maintaining network stability during critical situations. +These libraries interact closely: the Chain library validates and applies operations, the Protocol library defines operations and transactions, the Network library propagates blocks and transactions across peers, and the Wallet library signs transactions before they are broadcast. The system now includes enhanced emergency consensus mode with integrated validator scheduling and improved peer connection management for maintaining network stability during critical situations. -**Updated** Enhanced documentation now includes comprehensive coverage of emergency consensus mode, hybrid witness scheduling, peer connection management, blockchain operations, data types, protocol specifications, DNS nameserver helper functionality, and accurate postponed transactions processing with corrected logging behavior. +**Updated** Enhanced documentation now includes comprehensive coverage of emergency consensus mode, hybrid validator scheduling, peer connection management, blockchain operations, data types, protocol specifications, DNS nameserver helper functionality, and accurate postponed transactions processing with corrected logging behavior. ## Project Structure The core libraries are organized under the libraries/ directory, with each library providing focused capabilities: @@ -75,7 +75,7 @@ The core libraries are organized under the libraries/ directory, with each libra - libraries/network: P2P node, peer connections, message handling, synchronization, emergency peer management - libraries/wallet: transaction builder, signing, key management, APIs, DNS nameserver helpers -Plugins integrate these libraries into a full node via the appbase framework. The main entry point initializes plugins and starts the node. Emergency consensus mode adds new components for witness scheduling and peer management. +Plugins integrate these libraries into a full node via the appbase framework. The main entry point initializes plugins and starts the node. Emergency consensus mode adds new components for validator scheduling and peer management. ```mermaid graph TB @@ -88,11 +88,11 @@ end subgraph "Plugins" PL_CHAIN["plugins/chain/plugin.hpp"] PL_P2P["plugins/p2p/p2p_plugin.hpp"] -PL_WITNESS["plugins/witness/witness.hpp"] +PL_WITNESS["plugins/validator/validator.hpp"] end subgraph "Emergency Consensus Components" EMERGENCY_MODE["Emergency Consensus
Mode Activation
Deactivation Logic"] -HYBRID_SCHED["Hybrid Witness Schedule
Real + Committee Slots"] +HYBRID_SCHED["Hybrid validator Schedule
Real + Committee Slots"] PEER_MANAGEMENT["Peer Connection
Management & Soft-Banning"] END MAIN["programs/vizd/main.cpp"] @@ -113,7 +113,7 @@ WALLET --> PROTO - [programs/vizd/main.cpp:106-140](file://programs/vizd/main.cpp#L106-L140) - [plugins/chain/include/graphene/plugins/chain/plugin.hpp:21-46](file://plugins/chain/include/graphene/plugins/chain/plugin.hpp#L21-L46) - [plugins/p2p/include/graphene/plugins/p2p/p2p_plugin.hpp:18-46](file://plugins/p2p/include/graphene/plugins/p2p/p2p_plugin.hpp#L18-L46) -- [plugins/witness/witness.cpp:170-198](file://plugins/witness/witness.cpp#L170-L198) +- [plugins/validator/validator.cpp:170-198](file://plugins/validator/validator.cpp#L170-L198) - [libraries/chain/include/graphene/chain/database.hpp:36-561](file://libraries/chain/include/graphene/chain/database.hpp#L36-L561) - [libraries/protocol/include/graphene/protocol/config.hpp:110-124](file://libraries/protocol/include/graphene/protocol/config.hpp#L110-L124) - [libraries/network/include/graphene/network/node.hpp:190-304](file://libraries/network/include/graphene/network/node.hpp#L190-L304) @@ -124,7 +124,7 @@ WALLET --> PROTO - [programs/vizd/main.cpp:62-91](file://programs/vizd/main.cpp#L62-L91) - [plugins/chain/include/graphene/plugins/chain/plugin.hpp:21-46](file://plugins/chain/include/graphene/plugins/chain/plugin.hpp#L21-L46) - [plugins/p2p/include/graphene/plugins/p2p/p2p_plugin.hpp:18-46](file://plugins/p2p/include/graphene/plugins/p2p/p2p_plugin.hpp#L18-L46) -- [plugins/witness/witness.cpp:170-198](file://plugins/witness/witness.cpp#L170-L198) +- [plugins/validator/validator.cpp:170-198](file://plugins/validator/validator.cpp#L170-L198) ## Core Components This section introduces the primary responsibilities and key classes of each library. @@ -136,13 +136,13 @@ This section introduces the primary responsibilities and key classes of each lib - db_with: pending transaction processing, postponed transactions handling, and restoration logic - global_property_object: dynamic chain properties including emergency consensus state - fork_database: fork management with emergency mode tie-breaking and state tracking - - Responsibilities: block validation, transaction validation, state transitions, hardfork handling, witness scheduling, emergency consensus management + - Responsibilities: block validation, transaction validation, state transitions, hardfork handling, validator scheduling, emergency consensus management - Protocol Library - operations: static_variant of all supported operations (transfers, governance, content, etc.) - transaction: structure with operations, expiration, reference block, and cryptographic signing - types: comprehensive data type definitions including cryptographic keys, asset types, and authority structures - - config: emergency consensus constants and witness scheduling parameters + - config: emergency consensus constants and validator scheduling parameters - Responsibilities: define canonical operation semantics, transaction signing and verification, authority checks, emergency mode configuration - Network Library @@ -156,7 +156,7 @@ This section introduces the primary responsibilities and key classes of each lib - DNS Nameserver Helpers: validation, extraction, and management of DNS records in account metadata - Responsibilities: construct transactions, sign with private keys, manage encrypted key storage, expose APIs, handle DNS metadata -**Updated** Enhanced with comprehensive emergency consensus mode integration, hybrid witness scheduling, peer connection management, and DNS nameserver helper functionality. +**Updated** Enhanced with comprehensive emergency consensus mode integration, hybrid validator scheduling, peer connection management, and DNS nameserver helper functionality. **Section sources** - [libraries/chain/include/graphene/chain/database.hpp:36-561](file://libraries/chain/include/graphene/chain/database.hpp#L36-L561) @@ -175,7 +175,7 @@ This section introduces the primary responsibilities and key classes of each lib - [libraries/wallet/include/graphene/wallet/api_documentation.hpp:37-75](file://libraries/wallet/include/graphene/wallet/api_documentation.hpp#L37-L75) ## Architecture Overview -The libraries integrate through explicit interfaces and signals. The Chain library exposes a database interface and signals for operation application. The Protocol library defines the canonical operation types and transaction structures. The Network library consumes blocks and transactions from the Chain library and broadcasts them to peers. The Wallet library constructs and signs transactions using the Protocol library and sends them to the Chain library via the P2P plugin. The DNS nameserver helper functionality extends the wallet library to manage DNS metadata within account JSON metadata. The db_with module handles postponed transactions processing with accurate counting and logging. Emergency consensus mode adds new components for witness scheduling and peer management. +The libraries integrate through explicit interfaces and signals. The Chain library exposes a database interface and signals for operation application. The Protocol library defines the canonical operation types and transaction structures. The Network library consumes blocks and transactions from the Chain library and broadcasts them to peers. The Wallet library constructs and signs transactions using the Protocol library and sends them to the Chain library via the P2P plugin. The DNS nameserver helper functionality extends the wallet library to manage DNS metadata within account JSON metadata. The db_with module handles postponed transactions processing with accurate counting and logging. Emergency consensus mode adds new components for validator scheduling and peer management. ```mermaid graph TB @@ -187,14 +187,14 @@ EVAL["Evaluators
evaluator.hpp"] CHAIN_OBJ["Chain Objects
chain_objects.hpp"] DB_WITH["Postponed Transactions
db_with.hpp"] EMERGENCY_MODE["Emergency Consensus
Mode Management"] -HYBRID_SCHED["Hybrid Witness Schedule
Real + Committee Slots"] +HYBRID_SCHED["Hybrid validator Schedule
Real + Committee Slots"] PEER_CONN["Peer Connections
Soft-Banning & Fork Management"] NET["Network Node
node.hpp"] PL_CHAIN["Chain Plugin
plugin.hpp"] PL_P2P["P2P Plugin
p2p_plugin.hpp"] -PL_WITNESS["Witness Plugin
witness.hpp"] +PL_WITNESS["Validator Plugin
validator.hpp"] DNS_HELPERS["DNS Nameserver Helpers
ns_validate_*
ns_create_metadata
ns_set_records"] -WITNESS_PLUGIN["Witness Plugin
Emergency Key Loading
Block Production"] +WITNESS_PLUGIN["Validator Plugin
Emergency Key Loading
Block Production"] FORK_DB["Fork Database
Emergency Mode Tie-Breaking"] WALLET --> API_DOC WALLET --> PROTO @@ -233,7 +233,7 @@ WITNESS_PLUGIN --> CHAIN - [libraries/network/include/graphene/network/peer_connection.hpp:276-277](file://libraries/network/include/graphene/network/peer_connection.hpp#L276-L277) - [plugins/chain/include/graphene/plugins/chain/plugin.hpp:21-46](file://plugins/chain/include/graphene/plugins/chain/plugin.hpp#L21-L46) - [plugins/p2p/include/graphene/plugins/p2p/p2p_plugin.hpp:18-46](file://plugins/p2p/include/graphene/plugins/p2p/p2p_plugin.hpp#L18-L46) -- [plugins/witness/witness.cpp:170-198](file://plugins/witness/witness.cpp#L170-L198) +- [plugins/validator/validator.cpp:170-198](file://plugins/validator/validator.cpp#L170-L198) ## Detailed Component Analysis @@ -246,7 +246,7 @@ The Chain library is the core state machine. It manages: - Hardfork handling: versioning and activation logic - Postponed transactions: accurate counting and processing with proper logging - Emergency consensus: automatic activation/deactivation based on network health -- Witness scheduling: hybrid schedule during emergency mode with real and committee witnesses +- validator scheduling: hybrid schedule during emergency mode with real and committee validators Key classes and responsibilities: - database: open/reindex, push/pop blocks, push transactions, notify signals, hardfork control, emergency mode management @@ -336,7 +336,7 @@ The Protocol library defines the canonical operation types and transaction struc - transaction: operations, expiration, reference block, and signing/verification helpers - types: comprehensive data type definitions including cryptographic keys, asset types, and authority structures - Authority and sign_state: required authorities and signature verification -- config: emergency consensus constants and witness scheduling parameters +- config: emergency consensus constants and validator scheduling parameters ```mermaid classDiagram @@ -606,7 +606,7 @@ CHAIN-->>P2P : "transaction propagated" - Network receives a block from peers - Chain plugin accepts the block and validates it - Database validates block header, extensions, and applies block-level operations -- Database updates global properties, witness schedules, and emits applied_block signal +- Database updates global properties, validator schedules, and emits applied_block signal ```mermaid sequenceDiagram @@ -723,7 +723,7 @@ Transactions follow a strict validation pipeline: ## Emergency Consensus Mode -The VIZ blockchain now includes a comprehensive emergency consensus mode designed to maintain network stability during prolonged network stalls or witness failures. This system automatically activates when no blocks are produced for a specified timeout period and ensures continuous block production through committee witnesses. +The VIZ blockchain now includes a comprehensive emergency consensus mode designed to maintain network stability during prolonged network stalls or validator failures. This system automatically activates when no blocks are produced for a specified timeout period and ensures continuous block production through committee validators. ### Emergency Consensus Activation Logic @@ -736,9 +736,9 @@ CheckLIB --> CalcTime["Calculate Time Since LIB"] CalcTime --> Timeout{"Timeout Exceeded?
> CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC"} Timeout --> |No| Normal["Normal Operation"] Timeout --> |Yes| Activate["Activate Emergency Mode"] -Activate --> CreateWitness["Create/Update Emergency Witness"] -CreateWitness --> ResetPenalties["Reset Witness Penalties"] -ResetPenalties --> OverrideSchedule["Override Witness Schedule"] +Activate --> CreateWitness["Create/Update Emergency validator"] +CreateWitness --> ResetPenalties["Reset validator Penalties"] +ResetPenalties --> OverrideSchedule["Override validator Schedule"] OverrideSchedule --> NotifyForkDB["Notify Fork Database"] NotifyForkDB --> LogActivation["Log Emergency Mode Activation"] ``` @@ -747,35 +747,35 @@ NotifyForkDB --> LogActivation["Log Emergency Mode Activation"] - [libraries/chain/database.cpp:4334-4438](file://libraries/chain/database.cpp#L4334-L4438) - [libraries/protocol/include/graphene/protocol/config.hpp:110-112](file://libraries/protocol/include/graphene/protocol/config.hpp#L110-L112) -### Hybrid Witness Scheduling System +### Hybrid validator Scheduling System -During emergency mode, the witness scheduling system operates as a hybrid between real witnesses and committee witnesses: +During emergency mode, the validator scheduling system operates as a hybrid between real validators and committee validators: -- **Real Witness Slots**: Maintained for witnesses with valid signing keys -- **Committee Slots**: Filled by the emergency witness account for offline or unavailable witnesses +- **Real validator Slots**: Maintained for validators with valid signing keys +- **Committee Slots**: Filled by the emergency validator account for offline or unavailable validators - **Full Schedule Expansion**: The schedule expands to include all committee slots during emergency The hybrid schedule ensures that: -- Real witnesses keep their scheduled slots -- Offline witnesses are replaced by committee witnesses +- Real validators keep their scheduled slots +- Offline validators are replaced by committee validators - The full CHAIN_MAX_WITNESSES schedule is maintained for consistent block production -- Committee witnesses maintain neutral voting positions aligned with current hardfork state +- Committee validators maintain neutral voting positions aligned with current hardfork state ```mermaid sequenceDiagram -participant SCHED as "Witness Scheduler" -participant REAL as "Real Witnesses" -participant COMMITTEE as "Committee Witnesses" +participant SCHED as "validator Scheduler" +participant REAL as "Real validators" +participant COMMITTEE as "Committee validators" participant DATABASE as "Database" SCHED->>DATABASE : "Get Current Schedule" DATABASE-->>SCHED : "wso.current_shuffled_witnesses" SCHED->>SCHED : "Iterate Full Schedule (MAX_WITNESSES)" loop For Each Slot -SCHED->>REAL : "Check Witness Availability" -alt Witness Available -SCHED->>SCHED : "Keep Real Witness Slot" -else Witness Unavailable -SCHED->>COMMITTEE : "Assign Emergency Witness" +SCHED->>REAL : "Check validator Availability" +alt validator Available +SCHED->>SCHED : "Keep Real validator Slot" +else validator Unavailable +SCHED->>COMMITTEE : "Assign Emergency validator" SCHED->>SCHED : "Replace with Committee Slot" end end @@ -791,21 +791,21 @@ DATABASE-->>SCHED : "Updated Schedule" Emergency mode automatically deactivates when: - The last irreversible block advances beyond the emergency start block -- 21 consecutive blocks are produced by the emergency witness (full round completion) -- Network conditions return to normal with sufficient witness participation +- 21 consecutive blocks are produced by the emergency validator (full round completion) +- Network conditions return to normal with sufficient validator participation The exit process restores normal operations: - Disables emergency consensus flag - Resets fork database emergency mode state -- Removes emergency witness from schedule -- Restores normal witness participation requirements +- Removes emergency validator from schedule +- Restores normal validator participation requirements ### Emergency Consensus Configuration Key configuration parameters: - `CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC`: 3600 seconds (1 hour) timeout for emergency activation - `CHAIN_EMERGENCY_WITNESS_ACCOUNT`: "committee" account for emergency block production -- `CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY`: Public key for emergency witness signature verification +- `CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY`: Public key for emergency validator signature verification - `CHAIN_EMERGENCY_EXIT_NORMAL_BLOCKS`: 21 blocks for automatic emergency mode exit **Section sources** @@ -1022,7 +1022,7 @@ The libraries exhibit layered dependencies with enhanced emergency consensus int - Chain depends on Protocol for operation types, transaction structures, and emergency mode configuration - Network depends on Protocol for message serialization, types, and emergency peer management - Wallet depends on Protocol for transaction construction and signing, plus includes DNS helpers -- Plugins depend on Chain for database access, on Network for P2P operations, and on Emergency Mode for witness scheduling +- Plugins depend on Chain for database access, on Network for P2P operations, and on Emergency Mode for validator scheduling - db_with module depends on Chain database for transaction processing and logging - Emergency consensus components depend on all core libraries for coordinated operation @@ -1039,7 +1039,7 @@ EMERGENCY_MODE --> HYBRID_SCHED["Hybrid Schedule"] EMERGENCY_MODE --> FORK_DB["Fork Database
Tie-Breaking"] PL_P2P["P2P Plugin"] --> NET PL_CHAIN["Chain Plugin"] --> CHAIN -PL_WITNESS["Witness Plugin"] --> CHAIN +PL_WITNESS["Validator Plugin"] --> CHAIN MAIN["Main Entry"] --> PL_CHAIN MAIN --> PL_P2P MAIN --> PL_WITNESS @@ -1056,14 +1056,14 @@ MAIN --> PL_WITNESS - [libraries/network/include/graphene/network/peer_connection.hpp:276-277](file://libraries/network/include/graphene/network/peer_connection.hpp#L276-L277) - [plugins/p2p/include/graphene/plugins/p2p/p2p_plugin.hpp:3-3](file://plugins/p2p/include/graphene/plugins/p2p/p2p_plugin.hpp#L3-L3) - [plugins/chain/include/graphene/plugins/chain/plugin.hpp:7-7](file://plugins/chain/include/graphene/plugins/chain/plugin.hpp#L7-L7) -- [plugins/witness/witness.cpp:170-198](file://plugins/witness/witness.cpp#L170-L198) +- [plugins/validator/validator.cpp:170-198](file://plugins/validator/validator.cpp#L170-L198) - [programs/vizd/main.cpp:106-140](file://programs/vizd/main.cpp#L106-L140) **Section sources** - [programs/vizd/main.cpp:106-140](file://programs/vizd/main.cpp#L106-L140) - [plugins/chain/include/graphene/plugins/chain/plugin.hpp:7-7](file://plugins/chain/include/graphene/plugins/chain/plugin.hpp#L7-L7) - [plugins/p2p/include/graphene/plugins/p2p/p2p_plugin.hpp:3-3](file://plugins/p2p/include/graphene/plugins/p2p/p2p_plugin.hpp#L3-L3) -- [plugins/witness/witness.cpp:170-198](file://plugins/witness/witness.cpp#L170-L198) +- [plugins/validator/validator.cpp:170-198](file://plugins/validator/validator.cpp#L170-L198) - [libraries/chain/include/graphene/chain/database.hpp:8-8](file://libraries/chain/include/graphene/chain/database.hpp#L8-L8) - [libraries/network/include/graphene/network/node.hpp:26-30](file://libraries/network/include/graphene/network/node.hpp#L26-L30) - [libraries/wallet/include/graphene/wallet/wallet.hpp:18-21](file://libraries/wallet/include/graphene/wallet/wallet.hpp#L18-L21) @@ -1097,8 +1097,8 @@ Common issues and diagnostics: - TTL validation errors: verify positive integer values for DNS record TTL settings - Postponed transactions issues: check block size limits, execution time limits, and known transaction filtering - Logging accuracy: verify postponed transaction counters and avoid false 'Postponed' messages for skipped known transactions -- Emergency mode activation: verify timeout thresholds and emergency witness configuration -- Hybrid schedule issues: check witness availability and schedule expansion during emergency mode +- Emergency mode activation: verify timeout thresholds and emergency validator configuration +- Hybrid schedule issues: check validator availability and schedule expansion during emergency mode - Peer soft-banning: monitor fork_rejected_until timestamps and emergency peer connection management - Fork database tie-breaking: ensure deterministic hash comparison during emergency mode conflicts @@ -1117,6 +1117,6 @@ Common issues and diagnostics: - [libraries/chain/fork_database.cpp:80-87](file://libraries/chain/fork_database.cpp#L80-L87) ## Conclusion -The VIZ CPP Node core libraries form a cohesive architecture with enhanced emergency consensus capabilities: Protocol defines canonical operations and transactions, Chain manages state and validation with emergency mode integration, Network enables peer synchronization with emergency peer management, and Wallet provides signing and key management. The enhanced documentation now provides comprehensive coverage of emergency consensus mode, hybrid witness scheduling, peer connection management, blockchain operations, data types, protocol specifications, DNS nameserver helper functionality, and accurate postponed transactions processing with corrected logging behavior, supporting robust transaction processing, block validation, peer coordination, and emergency network stability essential to a production blockchain node. +The VIZ CPP Node core libraries form a cohesive architecture with enhanced emergency consensus capabilities: Protocol defines canonical operations and transactions, Chain manages state and validation with emergency mode integration, Network enables peer synchronization with emergency peer management, and Wallet provides signing and key management. The enhanced documentation now provides comprehensive coverage of emergency consensus mode, hybrid validator scheduling, peer connection management, blockchain operations, data types, protocol specifications, DNS nameserver helper functionality, and accurate postponed transactions processing with corrected logging behavior, supporting robust transaction processing, block validation, peer coordination, and emergency network stability essential to a production blockchain node. -**Updated** Enhanced documentation provides expanded coverage of emergency consensus mode, hybrid witness scheduling, peer connection management, blockchain operations, data types, protocol specifications, DNS nameserver helper functionality, and accurate postponed transactions processing with corrected logging behavior, making it easier for developers to understand and work with the VIZ blockchain protocol, manage emergency network conditions, and implement DNS records within account metadata. \ No newline at end of file +**Updated** Enhanced documentation provides expanded coverage of emergency consensus mode, hybrid validator scheduling, peer connection management, blockchain operations, data types, protocol specifications, DNS nameserver helper functionality, and accurate postponed transactions processing with corrected logging behavior, making it easier for developers to understand and work with the VIZ blockchain protocol, manage emergency network conditions, and implement DNS records within account metadata. \ No newline at end of file diff --git a/.qoder/repowiki/en/content/Core Libraries/Emergency Consensus System.md b/.qoder/repowiki/en/content/Core Libraries/Emergency Consensus System.md index 4eca505d91..b22f2019a7 100644 --- a/.qoder/repowiki/en/content/Core Libraries/Emergency Consensus System.md +++ b/.qoder/repowiki/en/content/Core Libraries/Emergency Consensus System.md @@ -1,4 +1,4 @@ -# Emergency Consensus System +# Emergency Consensus System **Referenced Files in This Document** @@ -10,8 +10,8 @@ - [fork_database.hpp](file://libraries/chain/include/graphene/chain/fork_database.hpp) - [config.hpp](file://libraries/protocol/include/graphene/protocol/config.hpp) - [config_testnet.hpp](file://libraries/protocol/include/graphene/protocol/config_testnet.hpp) -- [witness.cpp](file://plugins/witness/witness.cpp) -- [witness.hpp](file://plugins/witness/include/graphene/plugins/witness/witness.hpp) +- [validator.cpp](file://plugins/validator/validator.cpp) +- [validator.hpp](file://plugins/validator/include/graphene/plugins/validator/validator.hpp) - [12.hf](file://libraries/chain/hardfork.d/12.hf) - [chainbase.cpp](file://thirdparty/chainbase/src/chainbase.cpp) - [chainbase.hpp](file://thirdparty/chainbase/include/chainbase/chainbase.hpp) @@ -21,9 +21,9 @@ **Changes Made** - Enhanced emergency recovery mechanisms with new emergency threshold constants and improved network resilience during critical failure scenarios - Updated emergency consensus activation with comprehensive LIB availability validation and deterministic synchronization detection -- Improved emergency exit conditions with refined real witness recovery validation using 75% threshold -- Enhanced emergency mode flag management across fork database and witness plugin integration -- Strengthened emergency witness management with comprehensive penalty reset and schedule override logic +- Improved emergency exit conditions with refined real validator recovery validation using 75% threshold +- Enhanced emergency mode flag management across fork database and Validator Plugin integration +- Strengthened emergency validator management with comprehensive penalty reset and schedule override logic - Added enhanced memory management protection through operation guards during emergency mode operations ## Table of Contents @@ -44,9 +44,9 @@ ## Introduction -The Emergency Consensus System is a critical safety mechanism implemented in the VIZ blockchain to maintain network continuity during extended periods of network stall or witness failure. This system automatically activates when the blockchain stops producing blocks for a predetermined timeout period, ensuring the network remains functional even when regular witness production is compromised. +The Emergency Consensus System is a critical safety mechanism implemented in the VIZ blockchain to maintain network continuity during extended periods of network stall or validator failure. This system automatically activates when the blockchain stops producing blocks for a predetermined timeout period, ensuring the network remains functional even when regular validator production is compromised. -The system operates as a three-state safety enforcement mechanism, providing automatic recovery capabilities that prevent network paralysis during emergencies. It maintains consensus integrity while allowing the network to recover from various failure scenarios including witness failures, network partitions, or other catastrophic events. +The system operates as a three-state safety enforcement mechanism, providing automatic recovery capabilities that prevent network paralysis during emergencies. It maintains consensus integrity while allowing the network to recover from various failure scenarios including validator failures, network partitions, or other catastrophic events. **Updated** Enhanced with comprehensive emergency consensus constants and configuration options including CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC, CHAIN_EMERGENCY_WITNESS_ACCOUNT, CHAIN_EMERGENCY_EXIT_NORMAL_BLOCKS, CHAIN_IRREVERSIBLE_THRESHOLD, and CHAIN_MAX_WITNESSES * 10 threshold that establishes the foundation for emergency consensus mode activation and operation with deterministic synchronization detection during replay, reindex, and live sync scenarios. @@ -58,13 +58,13 @@ The Emergency Consensus System is built on a distributed architecture that integ graph TB subgraph "Consensus Layer" DB[Database Engine] -WS[Witness Schedule] +WS[validator Schedule] DGP[Dynamic Global Properties] END subgraph "Emergency Components" -EW[Emergency Witness] +EW[Emergency validator] FD[Fork Database] -WC[Witness Plugin] +WC[Validator Plugin] OG[Operation Guards] END subgraph "Network Layer" @@ -113,14 +113,14 @@ OG -.-> DB **Diagram sources** - [database.cpp:4863-5004](file://libraries/chain/database.cpp#L4863-L5004) - [fork_database.cpp:81-88](file://libraries/chain/fork_database.cpp#L81-L88) -- [witness.cpp:422-427](file://plugins/witness/witness.cpp#L422-L427) +- [validator.cpp:422-427](file://plugins/validator/validator.cpp#L422-L427) - [database.cpp:1556](file://libraries/chain/database.cpp#L1556) - [chainbase.hpp:1097-1115](file://thirdparty/chainbase/include/chainbase/chainbase.hpp#L1097-L1115) The architecture consists of several key layers: -- **Consensus Layer**: Core blockchain state management and witness scheduling -- **Emergency Components**: Specialized emergency witness and fork database modifications with operation guards +- **Consensus Layer**: Core blockchain state management and validator scheduling +- **Emergency Components**: Specialized emergency validator and fork database modifications with operation guards - **Network Layer**: Peer-to-peer communication and block propagation - **Safety Mechanisms**: Hardfork coordination, timeout monitoring, deterministic synchronization detection, memory management, error handling, automatic schedule recovery, hybrid schedule override, and irreversible threshold validation @@ -167,16 +167,16 @@ witness_schedule_object --> witness_object : "contains" - [global_property_object.hpp:24-146](file://libraries/chain/include/graphene/chain/global_property_object.hpp#L24-L146) - [witness_objects.hpp:27-132](file://libraries/chain/include/graphene/chain/witness_objects.hpp#L27-L132) -### Enhanced Emergency Witness Implementation +### Enhanced Emergency validator Implementation -The emergency witness serves as the automated consensus producer during emergency conditions with comprehensive management: +The emergency validator serves as the automated consensus producer during emergency conditions with comprehensive management: | Property | Value | Description | |----------|-------|-------------| -| Account Name | `committee` | Emergency witness account identifier | +| Account Name | `committee` | Emergency validator account identifier | | Public Key | `VIZ75CRHVHPwYiUESy1bgN3KhVFbZCQQRA9jT6TnpzKAmpxMPD6Xv` | Block signing key | | Role | Automated Producer | Produces blocks when network is stalled | -| Schedule Priority | Top | Takes precedence over all other witnesses | +| Schedule Priority | Top | Takes precedence over all other validators | | Version Synchronization | Automatic | Matches current binary version | | Hardfork Alignment | Current Status | Votes for currently applied hardfork | | Penalty Management | Reset | All penalties cleared during emergency | @@ -204,9 +204,9 @@ CheckEmpty --> |No| CalcTime["Calculate Time Since LIB"] CalcTime --> CheckTimeout{"Seconds Since LIB ≥ 3600?"} CheckTimeout --> |No| Normal CheckTimeout --> |Yes| Activate["Activate Emergency Mode"] -Activate --> CreateWitness["Create/Update Emergency Witness Object"] -CreateWitness --> ResetPenalties["Reset All Witness Penalties"] -ResetPenalties --> OverrideSchedule["Override Schedule with Emergency Witness"] +Activate --> CreateWitness["Create/Update Emergency validator Object"] +CreateWitness --> ResetPenalties["Reset All validator Penalties"] +ResetPenalties --> OverrideSchedule["Override Schedule with Emergency validator"] OverrideSchedule --> NotifyFork["Notify Fork Database"] NotifyFork --> LogEvent["Log Emergency Activation"] LogEvent --> Normal @@ -238,16 +238,16 @@ The system now implements comprehensive validation with deterministic synchroniz ### Startup Schedule Repair Mechanism -The system now includes comprehensive automatic schedule recovery that repairs broken witness schedules during node startup: +The system now includes comprehensive automatic schedule recovery that repairs broken validator schedules during node startup: ```mermaid sequenceDiagram participant DB as Database -participant WSO as Witness Schedule +participant WSO as validator Schedule participant DGP as Dynamic Global Properties DB->>DB : Node Startup DB->>DGP : Load DGP Object -DB->>WSO : Load Witness Schedule +DB->>WSO : Load validator Schedule DB->>DB : Check for Empty Slots alt Empty Slots Found DB->>DGP : Activate Emergency Mode @@ -267,9 +267,9 @@ DB->>DB : Continue Normal Operation The automatic schedule recovery system addresses several critical scenarios: - **Crash Recovery**: Repairs schedules that became corrupted when nodes shut down during emergency mode -- **Empty Slot Detection**: Identifies witness schedules with null witness names in shuffled positions +- **Empty Slot Detection**: Identifies validator schedules with null validator names in shuffled positions - **Emergency Mode Restoration**: Reactivates emergency mode when broken schedules are detected -- **Complete Override**: Fills all schedule slots with emergency witness to ensure network stability +- **Complete Override**: Fills all schedule slots with emergency validator to ensure network stability - **Next Shuffle Adjustment**: Updates next shuffle block number to ensure immediate schedule override **Section sources** @@ -279,7 +279,7 @@ The automatic schedule recovery system addresses several critical scenarios: ### Dynamic Schedule Adjustment Logic -The emergency system now implements sophisticated hybrid schedule override that dynamically adjusts witness assignments based on real witness availability: +The emergency system now implements sophisticated hybrid schedule override that dynamically adjusts validator assignments based on real validator availability: ```mermaid flowchart TD @@ -289,10 +289,10 @@ CheckHF --> |Yes| CheckEmergency{"Emergency Active?"} CheckEmergency --> |No| Normal CheckEmergency --> |Yes| ScanSchedule["Scan Current Shuffled Schedule"] ScanSchedule --> CountSlots["Count Real vs Committee Slots"] -CountSlots --> CheckAvailability{"Real Witnesses Available?"} +CountSlots --> CheckAvailability{"Real validators Available?"} CheckAvailability --> |Yes| FillCommittee["Fill Empty Slots with Committee"] CheckAvailability --> |No| AllCommittee["All Slots = Committee"] -FillCommittee --> ExpandSchedule["Expand to Max Witnesses"] +FillCommittee --> ExpandSchedule["Expand to Max validators"] AllCommittee --> ExpandSchedule ExpandSchedule --> UpdateNextShuffle["Update Next Shuffle Block"] UpdateNextShuffle --> SyncCommittee["Sync Committee Props"] @@ -306,13 +306,13 @@ Normal --> End ### Advanced Hybrid Schedule Features -The emergency hybrid schedule override provides sophisticated witness management: +The emergency hybrid schedule override provides sophisticated validator management: -- **Real Witness Detection**: Identifies available real witnesses vs. empty/invalid slots -- **Dynamic Allocation**: Fills empty slots with emergency witness automatically -- **Schedule Expansion**: Expands schedule to include all 21 witnesses for proper rotation +- **Real validator Detection**: Identifies available real validators vs. empty/invalid slots +- **Dynamic Allocation**: Fills empty slots with emergency validator automatically +- **Schedule Expansion**: Expands schedule to include all 21 validators for proper rotation - **Next Shuffle Optimization**: Adjusts next shuffle block to ensure immediate override -- **Committee Synchronization**: Keeps emergency witness properties synchronized with current state +- **Committee Synchronization**: Keeps emergency validator properties synchronized with current state - **Threshold-Based Logic**: Uses 75% threshold for emergency exit conditions **Section sources** @@ -330,9 +330,9 @@ flowchart TD Start([Emergency Active]) --> MonitorLIB["Monitor LIB Progress"] MonitorLIB --> CheckProgress{"LIB > Start Block?"} CheckProgress --> |No| Continue["Continue Emergency Mode"] -CheckProgress --> |Yes| CheckRecovery["Check Real Witness Recovery"] -CheckRecovery --> CountReal["Count Real Witness Slots"] -CountReal --> CheckThreshold{"Real Witnesses ≥ 75%?"} +CheckProgress --> |Yes| CheckRecovery["Check Real validator Recovery"] +CheckRecovery --> CountReal["Count Real validator Slots"] +CountReal --> CheckThreshold{"Real validators ≥ 75%?"} CheckThreshold --> |No| Continue CheckThreshold --> |Yes| Deactivate["Deactivate Emergency Mode"] Deactivate --> ClearFlag["Clear Emergency Flag"] @@ -350,8 +350,8 @@ Continue --> End([End]) The system evaluates several sophisticated conditions for emergency mode exit: 1. **LIB Advancement**: Last Irreversible Block number exceeds start block -2. **Network Recovery**: 75% of schedule slots are real witnesses (not committee) -3. **Automatic Trigger**: 21 consecutive blocks produced by emergency witness +2. **Network Recovery**: 75% of schedule slots are real validators (not committee) +3. **Automatic Trigger**: 21 consecutive blocks produced by emergency validator 4. **Manual Intervention**: System administrator override possible 5. **Real-time Monitoring**: Continuous LIB progress tracking during emergency 6. **Deterministic Synchronization**: Prevents premature exit during replay scenarios @@ -365,16 +365,16 @@ The system evaluates several sophisticated conditions for emergency mode exit: ### Normal LIB Advancement During Emergency -The emergency system now implements redesigned LIB computation that advances normally using all witnesses including committee: +The emergency system now implements redesigned LIB computation that advances normally using all validators including committee: ```mermaid sequenceDiagram participant DB as Database -participant WSO as Witness Schedule +participant WSO as validator Schedule participant DPO as Dynamic Properties DB->>DB : Update Last Irreversible Block -DB->>WSO : Get Scheduled Witnesses -WSO-->>DB : Committee + Real Witnesses +DB->>WSO : Get Scheduled validators +WSO-->>DB : Committee + Real validators DB->>DB : Calculate Support Threshold DB->>DB : Find Median Support alt Emergency Mode @@ -393,9 +393,9 @@ DB->>DB : Update Block Log The redesigned emergency LIB computation provides: -- **Normal Advancement**: LIB advances using all witnesses in schedule (including committee) +- **Normal Advancement**: LIB advances using all validators in schedule (including committee) - **Safety Cap**: Caps LIB at head-1 during emergency to preserve undo protection -- **Median Calculation**: Uses witness support thresholds to determine LIB safely +- **Median Calculation**: Uses validator support thresholds to determine LIB safely - **Emergency Protection**: Prevents permanent state corruption during crashes - **Seamless Transition**: Allows normal LIB computation to resume after emergency exit @@ -417,20 +417,20 @@ During emergency mode, the system implements special peer connection handling wi | Fork Collisions | Deterministic resolution | Reduces network fragmentation | | Replay Scenarios | Deterministic handling | Prevents false activations | -### Comprehensive Witness Participation Override +### Comprehensive validator Participation Override -The emergency system bypasses normal witness participation requirements with enhanced error handling and deterministic synchronization: +The emergency system bypasses normal validator participation requirements with enhanced error handling and deterministic synchronization: - **Participation Rate Checks**: Automatically enabled during emergency - **Stale Block Production**: Allowed without penalties -- **Production Scheduling**: Emergency witness takes precedence +- **Production Scheduling**: Emergency validator takes precedence - **Conflict Resolution**: Enhanced tie-breaking algorithms - **Schedule Updates**: Hybrid schedule during emergency mode - **Deterministic Sync Detection**: Prevents immediate participation during replay -- **Penalty Management**: Comprehensive reset of all witness penalties +- **Penalty Management**: Comprehensive reset of all validator penalties **Section sources** -- [witness.cpp:422-427](file://plugins/witness/witness.cpp#L422-L427) +- [validator.cpp:422-427](file://plugins/validator/validator.cpp#L422-L427) - [fork_database.cpp:81-88](file://libraries/chain/fork_database.cpp#L81-L88) ## Configuration and Constants @@ -446,7 +446,7 @@ The system uses comprehensive configurable constants with enhanced monitoring an | CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY | VIZ75CR... | Key | Block signing key | | CHAIN_EMERGENCY_EXIT_NORMAL_BLOCKS | 21 | Blocks | Consecutive blocks to exit | | CHAIN_IRREVERSIBLE_THRESHOLD | 75% | Percent | Recovery threshold | -| CHAIN_MAX_WITNESSES | 21 | Witnesses | Total witness count | +| CHAIN_MAX_WITNESSES | 21 | validators | Total validator count | | CHAIN_MAX_WITNESSES * 10 | 210 | Blocks | Deterministic sync threshold | ### Hardfork Configuration with Enhanced Protection @@ -563,7 +563,7 @@ The system implements comprehensive error handling throughout the consensus proc - **Snapshot Compatibility**: Handles DLT mode scenarios gracefully - **Memory Management Errors**: Provides detailed logging for memory operations - **Fork Database Exceptions**: Enhanced error reporting for fork operations -- **Witness Creation Failures**: Comprehensive error handling for emergency witness setup +- **validator Creation Failures**: Comprehensive error handling for emergency validator setup - **Operation Guard Protection**: Thread-safe emergency mode operations - **Deterministic Behavior**: Same results on replay as original application @@ -580,13 +580,13 @@ The system implements comprehensive error handling throughout the consensus proc | Emergency Mode Not Activating | No automatic blocks produced | Verify hardfork 12 activation, LIB availability, and sync detection | | Emergency Mode Stuck | Cannot exit emergency mode | Check LIB advancement, memory management logs, and sync detection validation | | Network Instability | Frequent disconnections | Review fork database settings, memory usage, and deterministic sync detection | -| Witness Production Failures | Emergency witness cannot produce blocks | Verify emergency key configuration, memory allocation, and operation guard protection | +| validator Production Failures | Emergency validator cannot produce blocks | Verify emergency key configuration, memory allocation, and operation guard protection | | Memory Issues | Low free memory warnings | Check memory management configuration, resize logs, and operation guard usage | | Replay Scenarios | Delayed emergency activation | Verify replay detection and ensure CHAIN_MAX_WITNESSES * 10 threshold is observed | | False Activations | Premature emergency activation | Check deterministic sync detection and LIB timestamp availability | | Snapshot Restores | Deadlock during emergency activation | Verify DLT mode handling and LIB timestamp validation | -| Broken Schedules | Empty witness slots after crash | Check automatic schedule recovery and emergency mode flags | -| Hybrid Schedule Issues | Incorrect witness assignments | Verify hybrid schedule override logic and real witness detection | +| Broken Schedules | Empty validator slots after crash | Check automatic schedule recovery and emergency mode flags | +| Hybrid Schedule Issues | Incorrect validator assignments | Verify hybrid schedule override logic and real validator detection | ### Advanced Diagnostic Commands @@ -598,10 +598,10 @@ To troubleshoot emergency consensus issues with enhanced monitoring: 4. **Validate Timeout Logs**: Check activation/deactivation timestamps and LIB availability 5. **Validate Deterministic Sync**: Ensure sync detection passes during replay scenarios 6. **Check Operation Guards**: Monitor thread safety and concurrent access protection -7. **Validate Witness Configuration**: Ensure emergency witness exists with correct key and schedule +7. **Validate validator Configuration**: Ensure emergency validator exists with correct key and schedule 8. **Monitor Memory Usage**: Check free, reserved, and maximum memory states with operation guard protection 9. **Check Schedule Recovery**: Verify automatic schedule repair and emergency mode restoration -10. **Validate Hybrid Override**: Monitor dynamic witness assignment during emergency +10. **Validate Hybrid Override**: Monitor dynamic validator assignment during emergency ### Performance Considerations @@ -628,10 +628,10 @@ The Emergency Consensus System represents a sophisticated safety mechanism desig The system's three-state safety enforcement approach ensures that the network can recover from various failure scenarios without requiring manual intervention. Through careful integration with existing consensus mechanisms, network protocols, and comprehensive operation guard protection, the emergency system operates seamlessly with minimal disruption to normal network operations. Key enhancements include: -- **Automatic Schedule Recovery**: Comprehensive repair of broken witness schedules during node startup -- **Emergency Hybrid Schedule Override**: Dynamic adjustment of witness assignments based on real witness availability -- **Refined Exit Conditions**: Improved real witness recovery validation using 75% threshold -- **Redesigned LIB Computation**: Normal LIB advancement using all witnesses during emergency +- **Automatic Schedule Recovery**: Comprehensive repair of broken validator schedules during node startup +- **Emergency Hybrid Schedule Override**: Dynamic adjustment of validator assignments based on real validator availability +- **Refined Exit Conditions**: Improved real validator recovery validation using 75% threshold +- **Redesigned LIB Computation**: Normal LIB advancement using all validators during emergency - **Deterministic Sync Detection**: CHAIN_MAX_WITNESSES * 10 threshold prevents false activations during replay and catch-up scenarios - **Automatic Recovery**: No manual intervention required for activation with comprehensive validation - **Network Stability**: Prevents cascade failures during emergencies with enhanced tie-breaking @@ -640,7 +640,7 @@ Key enhancements include: - **Enhanced Reliability**: Improved detection algorithms, memory management, and operation guard protection - **Better Troubleshooting**: Detailed logging and monitoring capabilities for easier diagnostics - **Configurable Parameters**: Flexible timeout thresholds, exit conditions, and sync detection for different network conditions -- **Robust Emergency Witness**: Dedicated emergency witness with proper key configuration, schedule override, and comprehensive penalty management +- **Robust Emergency validator**: Dedicated emergency validator with proper key configuration, schedule override, and comprehensive penalty management - **Thread-Safe Operations**: Comprehensive operation guard protection ensures concurrent access safety - **Deterministic Behavior**: Same results on replay as original application with comprehensive sync detection - **Advanced Concurrency Control**: Operation guards provide comprehensive thread safety for emergency mode operations diff --git a/.qoder/repowiki/en/content/Core Libraries/NTP Synchronization System.md b/.qoder/repowiki/en/content/Core Libraries/NTP Synchronization System.md index 6aa6bb1ad0..d2c044bd59 100644 --- a/.qoder/repowiki/en/content/Core Libraries/NTP Synchronization System.md +++ b/.qoder/repowiki/en/content/Core Libraries/NTP Synchronization System.md @@ -1,4 +1,4 @@ -# NTP Synchronization System +# NTP Synchronization System **Referenced Files in This Document** @@ -8,7 +8,7 @@ - [time.cpp](file://libraries/time/time.cpp) - [main.cpp](file://programs/vizd/main.cpp) - [ntp_test.cpp](file://thirdparty/fc/tests/network/ntp_test.cpp) -- [witness.cpp](file://plugins/witness/witness.cpp) +- [validator.cpp](file://plugins/validator/validator.cpp) ## Update Summary @@ -333,7 +333,7 @@ The NTP system integrates with the blockchain's block production mechanism to en | Component | Integration Point | Purpose | |-----------|-------------------|---------| -| Witness Plugin | Block slot calculation | Determines when witnesses can produce blocks | +| Validator Plugin | Block slot calculation | Determines when validators can produce blocks | | Chain Database | Block timestamp validation | Ensures block timestamps are reasonable | | P2P Plugin | Peer synchronization | Maintains time consistency across network | | Wallet | Transaction timestamping | Provides accurate timestamps for operations | diff --git a/.qoder/repowiki/en/content/Deployment and Operations/Cloud and Infrastructure.md b/.qoder/repowiki/en/content/Deployment and Operations/Cloud and Infrastructure.md index 2e54a6b4c5..6f7deab33e 100644 --- a/.qoder/repowiki/en/content/Deployment and Operations/Cloud and Infrastructure.md +++ b/.qoder/repowiki/en/content/Deployment and Operations/Cloud and Infrastructure.md @@ -1,4 +1,4 @@ -# Cloud and Infrastructure +# Cloud and Infrastructure **Referenced Files in This Document** @@ -115,7 +115,7 @@ The recommended deployment model is container-first: - Run one or more VIZ node containers behind a load balancer - Persist blockchain data via volumes - Mount configuration files and seed node lists as needed -- Use environment variables to override endpoints and enable witness operation when required +- Use environment variables to override endpoints and enable validator operation when required ```mermaid graph TB @@ -152,7 +152,7 @@ Nn --> CFG - Similar structure to production but enables a low-memory mode during build - Testnet image - Includes testnet-specific configuration and snapshot - - Enables witness operation by default + - Enables validator operation by default Operational notes: - The images are published by the CI/CD workflows @@ -169,18 +169,18 @@ Operational notes: The node reads configuration from a mounted config file and supports environment-driven overrides via the entrypoint script. Notable runtime parameters include: - RPC endpoint binding - P2P endpoint binding -- Witness name and private key +- validator name and private key - Seed nodes list - Optional extra arguments Configuration templates: - Mainnet template defines default endpoints, plugin list, and logging configuration -- Testnet template adds witness operation and adjusts participation thresholds +- Testnet template adds validator operation and adjusts participation thresholds ```mermaid flowchart TD Start(["Container Start"]) --> LoadCfg["Load config.ini or config_testnet.ini"] -LoadCfg --> ApplyEnv["Apply environment overrides
RPC, P2P, Seed, Witness, Private Key"] +LoadCfg --> ApplyEnv["Apply environment overrides
RPC, P2P, Seed, validator, Private Key"] ApplyEnv --> InitData["Initialize data directory
and blockchain cache if present"] InitData --> RunNode["Start vizd with merged args"] RunNode --> End(["Running"]) @@ -250,12 +250,12 @@ Common operational checks: - Confirm persistent volume is mounted and writable - Review logs written to the configured log directories - Validate seed nodes connectivity and adjust seed list if needed -- For witness nodes, confirm witness name and private key are set appropriately +- For validator nodes, confirm validator name and private key are set appropriately Environment overrides: - Override RPC and P2P endpoints if necessary - Provide custom seed nodes via environment variable -- Enable witness operation by setting witness name and private key +- Enable validator operation by setting validator name and private key **Section sources** - [share/vizd/vizd.sh](file://share/vizd/vizd.sh#L62-L72) @@ -335,7 +335,7 @@ The repository provides a solid foundation for deploying VIZ CPP Node in contain - Reserved instances - Commit to steady-state capacity with savings plans or reserved instances - Spot instances - - Use for fault-tolerant, stateless workers; avoid critical witness slots + - Use for fault-tolerant, stateless workers; avoid critical validator slots [No sources needed since this section provides general guidance] diff --git a/.qoder/repowiki/en/content/Deployment and Operations/Containerization and Docker.md b/.qoder/repowiki/en/content/Deployment and Operations/Containerization and Docker.md index c64f6cccd4..c5d88b64b9 100644 --- a/.qoder/repowiki/en/content/Deployment and Operations/Containerization and Docker.md +++ b/.qoder/repowiki/en/content/Deployment and Operations/Containerization and Docker.md @@ -1,4 +1,4 @@ -# Containerization and Docker +# Containerization and Docker **Referenced Files in This Document** @@ -84,7 +84,7 @@ E["MongoDB Plugin"] --> E1["mongo_db_plugin.hpp"] Key runtime environment variables supported by the entrypoint: - VIZD_SEED_NODES: Override seed nodes. -- VIZD_WITNESS_NAME: Configure witness name for block production. +- VIZD_WITNESS_NAME: Configure validator name for block production. - VIZD_PRIVATE_KEY: Private key for signing blocks. - VIZD_RPC_ENDPOINT: Override RPC HTTP endpoint. - VIZD_P2P_ENDPOINT: Override P2P endpoint. @@ -195,7 +195,7 @@ R7 --> R8["Start vizd process"] Responsibilities: - Initialize ownership for data/config directories. - Seed peers from /etc/vizd/seednodes if no explicit override is provided. -- Apply environment overrides for witness name/private key, RPC/P2P endpoints, and extra options. +- Apply environment overrides for validator name/private key, RPC/P2P endpoints, and extra options. - Optionally initialize blockchain from a cached snapshot if present. - Start the node with chpst under the non-root user. @@ -226,7 +226,7 @@ ExecNode --> End(["Container Running"]) ### Configuration Templates - config.ini: Default production configuration with RPC endpoints, plugin list, and logging. -- config_testnet.ini: Testnet-specific configuration with witness participation enabled and test keys. +- config_testnet.ini: Testnet-specific configuration with validator participation enabled and test keys. - config_mongo.ini: MongoDB-enabled configuration with mongodb-uri and mongo_db plugin enabled. - Debug configs: Smaller shared memory and debug logging for development/testing. diff --git a/.qoder/repowiki/en/content/Deployment and Operations/Deployment and Operations.md b/.qoder/repowiki/en/content/Deployment and Operations/Deployment and Operations.md index cd5a73dc78..069f172ab3 100644 --- a/.qoder/repowiki/en/content/Deployment and Operations/Deployment and Operations.md +++ b/.qoder/repowiki/en/content/Deployment and Operations/Deployment and Operations.md @@ -1,4 +1,4 @@ -# Deployment and Operations +# Deployment and Operations **Referenced Files in This Document** @@ -32,7 +32,7 @@ 10. [Appendices](#appendices) ## Introduction -This document provides comprehensive deployment and operations guidance for the VIZ CPP Node. It covers production deployment strategies, hardware and security considerations, containerization with multiple image variants, orchestration options, cloud deployment, high availability, node types (full, witness, seed), monitoring and maintenance, security hardening, troubleshooting, and backup/disaster recovery. +This document provides comprehensive deployment and operations guidance for the VIZ CPP Node. It covers production deployment strategies, hardware and security considerations, containerization with multiple image variants, orchestration options, cloud deployment, high availability, node types (full, validator, seed), monitoring and maintenance, security hardening, troubleshooting, and backup/disaster recovery. ## Project Structure The repository organizes deployment assets and operational artifacts primarily under share/vizd, with configuration templates, Dockerfiles, and scripts for containerized deployments. Documentation for building and testnet operations resides under documentation/. @@ -87,8 +87,8 @@ A --> E[".github/workflows/ (CI/CD)"] - Low-memory image: Built from Dockerfile-lowmem, optimized for resource-constrained environments. - Configuration templates - config.ini: General-purpose configuration for mainnet. - - config_testnet.ini: Testnet-specific configuration with witness participation enabled. - - config_witness.ini: Witness node configuration with RPC bound to localhost and virtual ops skipped. + - config_testnet.ini: Testnet-specific configuration with validator participation enabled. + - config_witness.ini: validator node configuration with RPC bound to localhost and virtual ops skipped. - config_mongo.ini: Extended configuration including MongoDB plugin for analytics. - Entrypoint script - vizd.sh: Orchestrates seed node injection, RPC/P2P endpoints, replay initialization, and runtime arguments. @@ -99,7 +99,7 @@ Key operational parameters and behaviors: - P2P and RPC endpoints are configurable via environment variables and config files. - Shared memory sizing and growth thresholds are tunable to manage memory pressure. - Lock wait timeouts and retries are configurable to balance throughput and latency. -- Plugin selection determines node capabilities (e.g., witness, mongo, debug_node). +- Plugin selection determines node capabilities (e.g., validator, mongo, debug_node). **Section sources** - [Dockerfile-production](file://share/vizd/docker/Dockerfile-production#L1-L88) @@ -113,7 +113,7 @@ Key operational parameters and behaviors: - [main.cpp](file://programs/vizd/main.cpp#L60-L91) ## Architecture Overview -The VIZ node is a modular application with pluggable subsystems for chain processing, P2P networking, webserver APIs, and optional plugins (e.g., witness, mongo, debug_node). Containerization encapsulates dependencies and exposes standardized ports for RPC (HTTP/WebSocket) and P2P communication. +The VIZ node is a modular application with pluggable subsystems for chain processing, P2P networking, webserver APIs, and optional plugins (e.g., validator, mongo, debug_node). Containerization encapsulates dependencies and exposes standardized ports for RPC (HTTP/WebSocket) and P2P communication. ```mermaid graph TB @@ -121,7 +121,7 @@ subgraph "Container Runtime" C1["vizd container"] end subgraph "Node Process" -N1["vizd binary
plugins: chain, p2p, webserver, witness, mongo, etc."] +N1["vizd binary
plugins: chain, p2p, webserver, validator, mongo, etc."] end subgraph "Storage" S1["/var/lib/vizd (data dir)"] @@ -194,10 +194,10 @@ Operational notes: - Use config.ini as baseline. - Typical plugins include chain, p2p, webserver, database_api, account_history, operation_history, and others. - Full node (testnet) - - Use config_testnet.ini; includes witness participation and a default witness identity. -- Witness node + - Use config_testnet.ini; includes validator participation and a default validator identity. +- validator node - Use config_witness.ini; RPC endpoints bound to localhost, skip virtual ops for reduced overhead. - - Configure witness name and private key for block production. + - Configure validator name and private key for block production. - Analytics node (MongoDB) - Use config_mongo.ini; includes mongo_db plugin and market history settings. @@ -237,7 +237,7 @@ ExecNode --> End(["Run"]) - [vizd.sh](file://share/vizd/vizd.sh#L1-L82) ### Node Binary and Plugin Registration -The node binary registers a comprehensive set of plugins, including chain, p2p, webserver, witness, database_api, social_network, account_history, private_message, tags, follow, and optional mongo_db plugin. This defines the node’s capabilities and API surface. +The node binary registers a comprehensive set of plugins, including chain, p2p, webserver, validator, database_api, social_network, account_history, private_message, tags, follow, and optional mongo_db plugin. This defines the node’s capabilities and API surface. ```mermaid classDiagram @@ -275,9 +275,9 @@ VizNode --> MongoDbPlugin : "registers (optional)" - Health checks - Use HTTP RPC to query dynamic global properties or chain info. - Monitor P2P connectivity and sync progress. -- Witness operations - - Configure witness name and private key for block production. - - Bind RPC to localhost for witness nodes to minimize exposure. +- validator operations + - Configure validator name and private key for block production. + - Bind RPC to localhost for validator nodes to minimize exposure. **Section sources** - [config.ini](file://share/vizd/config/config.ini#L16-L20) @@ -340,13 +340,13 @@ Common operational issues and remedies: - Increase write/read wait micros and retries; consider single-write-thread. - Insufficient disk space - Monitor free space thresholds and ensure adequate headroom for shared memory growth. -- Witness node not producing blocks - - Confirm witness name and private key are set; ensure required participation threshold is appropriate. +- validator node not producing blocks + - Confirm validator name and private key are set; ensure required participation threshold is appropriate. - Debugging state changes - Use debug_node plugin in isolated, localhost-bound RPC for controlled experiments. Security hardening tips: -- Bind RPC to localhost for witness nodes; expose externally via reverse proxy with authentication. +- Bind RPC to localhost for validator nodes; expose externally via reverse proxy with authentication. - Use firewalls to restrict P2P ingress to trusted peers. - Rotate private keys and restrict filesystem permissions on /var/lib/vizd. @@ -368,8 +368,8 @@ This guide consolidates deployment and operations practices for VIZ CPP Node acr ### Appendix A: Node Types and Operational Procedures - Full node - Use config.ini; expose RPC publicly as needed; monitor P2P connectivity. -- Witness node - - Use config_witness.ini; bind RPC to localhost; configure witness and private key. +- validator node + - Use config_witness.ini; bind RPC to localhost; configure validator and private key. - Seed node - Use config.ini; focus on stable connectivity and minimal external exposure; consider low-memory image for constrained environments. diff --git a/.qoder/repowiki/en/content/Deployment and Operations/Monitoring and Maintenance.md b/.qoder/repowiki/en/content/Deployment and Operations/Monitoring and Maintenance.md index 945a90a903..56f6e3179d 100644 --- a/.qoder/repowiki/en/content/Deployment and Operations/Monitoring and Maintenance.md +++ b/.qoder/repowiki/en/content/Deployment and Operations/Monitoring and Maintenance.md @@ -1,4 +1,4 @@ -# Monitoring and Maintenance +# Monitoring and Maintenance **Referenced Files in This Document** @@ -101,7 +101,7 @@ BIN --> LOGS - Webserver and JSON-RPC: Provide HTTP and WebSocket endpoints for API access and dispatch JSON-RPC requests to registered APIs. - Logging: Configured via file appenders and loggers in the configuration file. - P2P Peer Statistics System: Real-time monitoring of peer connections, latency, bandwidth usage, and blocking status for network observability. -- Plugins: Chain, Account History, Operation History, Mongo DB, P2P, Network Broadcast API, Witness API, Block Info, Raw Block, Debug Node, and others. +- Plugins: Chain, Account History, Operation History, Mongo DB, P2P, Network Broadcast API, validator API, Block Info, Raw Block, Debug Node, and others. - Runtime launcher: Sets endpoints, seed nodes, and replay options for Docker-based deployments. Key configuration and runtime elements: @@ -152,7 +152,7 @@ STATS-->>P2P : "Latency, Bandwidth, Status" ### Health Checks and Readiness - HTTP/WS endpoints: Configure readiness by ensuring the webserver plugin is active and reachable on the configured HTTP and WebSocket endpoints. - JSON-RPC health: Use a lightweight method (e.g., a read-only chain property) to validate API responsiveness. -- P2P connectivity: Confirm peers are connected and block production is progressing (for witness nodes). +- P2P connectivity: Confirm peers are connected and block production is progressing (for validator nodes). - **Updated** P2P peer statistics: Monitor peer health through the statistics system for real-time network observability. Operational references: @@ -417,7 +417,7 @@ This guide outlines how to operate, monitor, and maintain VIZ CPP Node instances - Database API: Provides chain state queries and operations. - Account History and Operation History: Index and expose historical data. - P2P and Network Broadcast API: Manage peer connections and broadcast transactions. -- Witness API: Expose witness-specific operations. +- validator API: Expose validator-specific operations. - Block Info and Raw Block: Provide block-level insights. - Debug Node: Simulate chain state for testing and development. @@ -427,7 +427,7 @@ References: - Operation History plugin: [operation_history_plugin.cpp:1-200](file://plugins/operation_history/plugin.cpp#L1-L200) - P2P plugin: [p2p_plugin.cpp:1-200](file://plugins/p2p/p2p_plugin.cpp#L1-L200) - Network Broadcast API plugin: [network_broadcast_api_plugin.cpp:1-200](file://plugins/network_broadcast_api/network_broadcast_api.cpp#L1-L200) -- Witness API plugin: [witness_api_plugin.cpp:1-200](file://plugins/witness_api/plugin.cpp#L1-L200) +- validator API plugin: [witness_api_plugin.cpp:1-200](file://plugins/witness_api/plugin.cpp#L1-L200) - Block Info plugin: [block_info_plugin.cpp:1-200](file://plugins/block_info/plugin.cpp#L1-L200) - Raw Block plugin: [raw_block_plugin.cpp:1-200](file://plugins/raw_block/plugin.cpp#L1-L200) - Debug Node plugin: [debug_node_plugin.cpp:1-200](file://plugins/debug_node/plugin.cpp#L1-L200) diff --git a/.qoder/repowiki/en/content/Deployment and Operations/Node Deployment/Installation and Setup.md b/.qoder/repowiki/en/content/Deployment and Operations/Node Deployment/Installation and Setup.md index 55c5c04408..19071642e2 100644 --- a/.qoder/repowiki/en/content/Deployment and Operations/Node Deployment/Installation and Setup.md +++ b/.qoder/repowiki/en/content/Deployment and Operations/Node Deployment/Installation and Setup.md @@ -1,4 +1,4 @@ -# Installation and Setup +# Installation and Setup **Referenced Files in This Document** @@ -245,7 +245,7 @@ These options influence compilation flags and conditional compilation macros. #### Multi-Stage Dockerfiles - Production: full-featured node with optimized runtime - Low memory: consensus-only node for resource-constrained environments -- Testnet: preconfigured for local testnet with snapshot and default witness +- Testnet: preconfigured for local testnet with snapshot and default validator - Each Dockerfile: - Installs build dependencies - Copies minimal source subsets @@ -274,7 +274,7 @@ Runtime --> Image["Final image with vizd"] - [share/vizd/docker/Dockerfile-testnet](file://share/vizd/docker/Dockerfile-testnet#L1-L88) #### Container Entrypoint and Environment -- The container entrypoint script sets up seed nodes, optional witness configuration, copies configuration, initializes blockchain cache if present, and launches the node with environment-provided endpoints and arguments. +- The container entrypoint script sets up seed nodes, optional validator configuration, copies configuration, initializes blockchain cache if present, and launches the node with environment-provided endpoints and arguments. ```mermaid sequenceDiagram @@ -304,7 +304,7 @@ Node-->>Docker : Logs and runtime ### Configuration Management - Production configuration template defines P2P endpoints, RPC endpoints, threading, shared memory sizing, plugin list, and logging -- Testnet configuration template adds witness-related settings and enables stale production for local testing +- Testnet configuration template adds validator-related settings and enables stale production for local testing **Section sources** - [share/vizd/config/config.ini](file://share/vizd/config/config.ini#L1-L130) diff --git a/.qoder/repowiki/en/content/Deployment and Operations/Node Deployment/Node Deployment.md b/.qoder/repowiki/en/content/Deployment and Operations/Node Deployment/Node Deployment.md index ccae829bdb..a5a1779740 100644 --- a/.qoder/repowiki/en/content/Deployment and Operations/Node Deployment/Node Deployment.md +++ b/.qoder/repowiki/en/content/Deployment and Operations/Node Deployment/Node Deployment.md @@ -1,4 +1,4 @@ -# Node Deployment +# Node Deployment **Referenced Files in This Document** @@ -31,7 +31,7 @@ 11. [Appendices](#appendices) ## Introduction -This document provides comprehensive deployment guidance for VIZ CPP Node across production, testnet, and specialized configurations. It covers hardware and system prerequisites, installation procedures for multiple operating systems, Docker-based deployments, node types (full, witness, seed), configuration management, service integration, performance tuning, capacity planning, security hardening, and troubleshooting. +This document provides comprehensive deployment guidance for VIZ CPP Node across production, testnet, and specialized configurations. It covers hardware and system prerequisites, installation procedures for multiple operating systems, Docker-based deployments, node types (full, validator, seed), configuration management, service integration, performance tuning, capacity planning, security hardening, and troubleshooting. ## Project Structure At a high level, the repository provides: @@ -95,7 +95,7 @@ VIZD --> LOG Key behaviors: - Plugin registration and initialization occur at startup - Logging configuration is parsed from the config file sections -- Docker entrypoint supports environment-driven customization (RPC, P2P, witness identity, private key) +- Docker entrypoint supports environment-driven customization (RPC, P2P, validator identity, private key) **Section sources** - [programs/vizd/main.cpp](file://programs/vizd/main.cpp#L62-L91) @@ -117,7 +117,7 @@ participant Log as "Logging Config" participant P2P as "P2P Plugin" participant RPC as "Webserver Plugin" Entrypoint->>Env : Read VIZD_* variables -Entrypoint->>Vizd : Pass --p2p-endpoint, --rpc-endpoint, --data-dir, seed nodes, witness, private-key +Entrypoint->>Vizd : Pass --p2p-endpoint, --rpc-endpoint, --data-dir, seed nodes, validator, private-key Vizd->>Cfg : Load config.ini and parse sections Cfg-->>Log : Build logging config from [log.*] and [logger.*] Vizd->>Log : Configure logging @@ -136,16 +136,16 @@ Vizd-->>Entrypoint : Running ### Node Types and Roles - Full node: Participates in P2P gossip, serves RPC APIs, optionally tracks history and feeds -- Witness node: Produces blocks; requires a configured witness name and private key +- validator node: Produces blocks; requires a configured validator name and private key - Seed node: Minimal footprint, connects peers and advertises connectivity; recommended low-memory build Configuration templates: - Production template: [config.ini](file://share/vizd/config/config.ini#L1-L130) -- Witness template: [config_witness.ini](file://share/vizd/config/config_witness.ini#L1-L107) +- validator template: [config_witness.ini](file://share/vizd/config/config_witness.ini#L1-L107) - Testnet template: [config_testnet.ini](file://share/vizd/config/config_testnet.ini#L1-L132) Operational differences: -- Witness nodes enable block production and require private keys +- validator nodes enable block production and require private keys - Seed nodes typically bind RPC to localhost and disable verbose plugins - Testnet enables special participation rules and snapshot-based initialization @@ -159,7 +159,7 @@ Operational differences: - Endpoints: P2P, HTTP RPC, WebSocket RPC - Locking and threading: Read/write wait limits and single-write-thread behavior - Shared memory sizing: Initial size, minimum free space, increment step, and periodic checks -- Plugins: Enabled via plugin directives; witness and API plugins commonly enabled +- Plugins: Enabled via plugin directives; validator and API plugins commonly enabled - Logging: Console and file appenders, logger levels, and appender routing Environment overrides: @@ -201,8 +201,8 @@ flowchart TD Start(["Start"]) --> Pull["Pull or Build Image"] Pull --> Run["Run Container with Volumes and Ports"] Run --> Seed["Set VIZD_SEED_NODES (optional)"] -Seed --> Witness["Set VIZD_WITNESS_NAME and VIZD_PRIVATE_KEY (optional)"] -Witness --> Logs["Monitor Logs"] +Seed --> validator["Set VIZD_WITNESS_NAME and VIZD_PRIVATE_KEY (optional)"] +validator --> Logs["Monitor Logs"] Logs --> End(["Ready"]) ``` @@ -245,7 +245,7 @@ participant Bin as "vizd" participant Net as "P2P" participant API as "RPC" Init->>FS : Read /etc/vizd/seednodes -Init->>Bin : exec vizd with --p2p-endpoint, --rpc-endpoint, --data-dir, --p2p-seed-node, --witness, --private-key +Init->>Bin : exec vizd with --p2p-endpoint, --rpc-endpoint, --data-dir, --p2p-seed-node, --validator, --private-key Bin->>Bin : Register plugins Bin->>Bin : Load logging config Bin->>Net : Start P2P @@ -313,7 +313,7 @@ Plugins --> Chain["Chain"] - [share/vizd/config/config_testnet.ini](file://share/vizd/config/config_testnet.ini#L13-L67) ## Security Hardening -- Bind RPC to localhost for witness nodes to prevent external exposure +- Bind RPC to localhost for validator nodes to prevent external exposure - Use environment variables to inject secrets (private keys) and restrict filesystem access - Restrict P2P exposure to trusted networks; consider firewall rules to allow only necessary ports - Monitor logs and set appropriate logger levels to detect anomalies early @@ -333,8 +333,8 @@ Common issues and resolutions: - Adjust read-wait-micro and max-read-wait-retries; consider single-write-thread - No peers or slow bootstrapping - Confirm p2p-seed-node entries; validate network accessibility on port 2001 -- Witness node not producing blocks - - Ensure witness name and private key are set; verify required-participation and enable-stale-production as appropriate +- validator node not producing blocks + - Ensure validator name and private key are set; verify required-participation and enable-stale-production as appropriate - Testnet initialization problems - Confirm snapshot availability and permissions; check testnet-specific configuration @@ -346,13 +346,13 @@ Common issues and resolutions: - [share/vizd/seednodes](file://share/vizd/seednodes#L1-L6) ## Conclusion -Deploying a VIZ CPP Node involves selecting the appropriate configuration template, preparing the environment (native or Docker), and integrating with monitoring and security controls. Use the provided Docker images for production, leverage witness and testnet configurations for specialized roles, and tune performance parameters according to workload profiles. +Deploying a VIZ CPP Node involves selecting the appropriate configuration template, preparing the environment (native or Docker), and integrating with monitoring and security controls. Use the provided Docker images for production, leverage validator and testnet configurations for specialized roles, and tune performance parameters according to workload profiles. ## Appendices ### Appendix A: Node Type Reference - Full node: General-purpose node with comprehensive plugins -- Witness node: Block producer with configured witness and private key +- validator node: Block producer with configured validator and private key - Seed node: Minimal footprint, focused on peer connectivity **Section sources** @@ -364,8 +364,8 @@ Deploying a VIZ CPP Node involves selecting the appropriate configuration templa - VIZD_RPC_ENDPOINT: Override RPC endpoint - VIZD_P2P_ENDPOINT: Override P2P endpoint - VIZD_SEED_NODES: Comma-separated seed nodes -- VIZD_WITNESS_NAME: Witness name for block production -- VIZD_PRIVATE_KEY: Private key for witness signing +- VIZD_WITNESS_NAME: validator name for block production +- VIZD_PRIVATE_KEY: Private key for validator signing - VIZD_EXTRA_OPTS: Additional arguments to pass to vizd **Section sources** diff --git a/.qoder/repowiki/en/content/Deployment and Operations/Node Deployment/Node Types and Configurations.md b/.qoder/repowiki/en/content/Deployment and Operations/Node Deployment/Node Types and Configurations.md index 1bf4f9f30c..157ede7a73 100644 --- a/.qoder/repowiki/en/content/Deployment and Operations/Node Deployment/Node Types and Configurations.md +++ b/.qoder/repowiki/en/content/Deployment and Operations/Node Deployment/Node Types and Configurations.md @@ -1,4 +1,4 @@ -# Node Types and Configurations +# Node Types and Configurations **Referenced Files in This Document** @@ -16,7 +16,7 @@ - [Dockerfile-lowmem](file://share/vizd/docker/Dockerfile-lowmem) - [testnet.md](file://documentation/testnet.md) - [debug_node_plugin.md](file://documentation/debug_node_plugin.md) -- [witness.hpp](file://plugins/witness/include/graphene/plugins/witness/witness.hpp) +- [validator.hpp](file://plugins/validator/include/graphene/plugins/validator/validator.hpp) - [mongo_db_plugin.hpp](file://plugins/mongo_db/include/graphene/plugins/mongo_db/mongo_db_plugin.hpp) - [plugin.hpp](file://plugins/debug_node/include/graphene/plugins/debug_node/plugin.hpp) @@ -42,7 +42,7 @@ 10. [Appendices](#appendices) ## Introduction -This document explains the different VIZ node types and their specific configurations. It covers full node setup with blockchain synchronization and API exposure, witness node configuration for block production and key management, seed node configuration for network bootstrap, and specialized configurations for testnet, debug, and MongoDB-integrated nodes. It also provides configuration file templates, parameter explanations, operational differences, performance tuning, resource allocation recommendations, monitoring requirements, and a comparison matrix across node configurations. +This document explains the different VIZ node types and their specific configurations. It covers full node setup with blockchain synchronization and API exposure, validator node configuration for block production and key management, seed node configuration for network bootstrap, and specialized configurations for testnet, debug, and MongoDB-integrated nodes. It also provides configuration file templates, parameter explanations, operational differences, performance tuning, resource allocation recommendations, monitoring requirements, and a comparison matrix across node configurations. ## Project Structure The repository organizes node configuration templates under share/vizd/config, Dockerfiles for different deployment modes under share/vizd/docker, and documentation under documentation. The main executable initializes plugins and loads configuration. @@ -86,7 +86,7 @@ L["share/vizd/docker/Dockerfile-lowmem"] --> B ## Core Components - Full node: default configuration with broad plugin set for general-purpose operation and API exposure. -- Witness node: enables block production with witness and witness_api plugins, requires witness name and private key. +- validator node: enables block production with validator and witness_api plugins, requires validator name and private key. - Debug node: specialized configuration for simulation and experimentation with debug_node plugin. - Testnet node: minimal configuration optimized for testnet operation with enabled stale production. - MongoDB-integrated node: includes mongo_db plugin for external database indexing and analytics. @@ -99,7 +99,7 @@ Key configuration parameters: - Plugin selection for functional capabilities. - Shared memory sizing and growth thresholds for database performance. - Lock wait timeouts and retries for RPC concurrency. -- Witness participation and block production controls. +- validator participation and block production controls. - Logging configuration via appenders and loggers. **Updated** Standardized P2P endpoint configuration to port 2001 across all configuration files, removing the previous inconsistency where some configurations used different ports. @@ -129,7 +129,7 @@ NB["network_broadcast_api"] DBAPI["database_api"] ACC_HIST["account_history"] OP_HIST["operation_history"] -WIT["witness"] +WIT["validator"] WIT_API["witness_api"] DEBUG["debug_node"] MONGO["mongo_db"] @@ -169,13 +169,13 @@ MAIN --> STOCK_EX **Diagram sources** - [main.cpp:62-91](file://programs/vizd/main.cpp#L62-L91) -- [witness.hpp:34-65](file://plugins/witness/include/graphene/plugins/witness/witness.hpp#L34-L65) +- [validator.hpp:34-65](file://plugins/validator/include/graphene/plugins/validator/validator.hpp#L34-L65) - [mongo_db_plugin.hpp:14-47](file://plugins/mongo_db/include/graphene/plugins/mongo_db/mongo_db_plugin.hpp#L14-L47) - [plugin.hpp:38-108](file://plugins/debug_node/include/graphene/plugins/debug_node/plugin.hpp#L38-L108) **Section sources** - [main.cpp:62-91](file://programs/vizd/main.cpp#L62-L91) -- [witness.hpp:34-65](file://plugins/witness/include/graphene/plugins/witness/witness.hpp#L34-L65) +- [validator.hpp:34-65](file://plugins/validator/include/graphene/plugins/validator/validator.hpp#L34-L65) - [mongo_db_plugin.hpp:14-47](file://plugins/mongo_db/include/graphene/plugins/mongo_db/mongo_db_plugin.hpp#L14-L47) - [plugin.hpp:38-108](file://plugins/debug_node/include/graphene/plugins/debug_node/plugin.hpp#L38-L108) @@ -200,42 +200,42 @@ MAIN --> STOCK_EX **Section sources** - [config.ini:1-136](file://share/vizd/config/config.ini#L1-L136) -### Witness Node Configuration -- Purpose: Block production node with witness and witness_api plugins. +### validator Node Configuration +- Purpose: Block production node with validator and witness_api plugins. - Key parameters: - P2P endpoint standardized to port 2001 with integrated seed nodes. - Local webserver endpoints for internal access. - - Plugins: chain, p2p, json_rpc, webserver, network_broadcast_api, database_api, witness, witness_api. - - Witness participation and stale production controls. - - Required witness name and private key for block signing. + - Plugins: chain, p2p, json_rpc, webserver, network_broadcast_api, database_api, validator, witness_api. + - validator participation and stale production controls. + - Required validator name and private key for block signing. - Optimized logging configuration. - Operational differences: - - Requires valid witness credentials. + - Requires valid validator credentials. - Can operate with stricter network isolation (local webserver endpoints). - - Enables witness-specific APIs. + - Enables validator-specific APIs. ```mermaid sequenceDiagram participant Operator as "Operator" participant Node as "VIZ Node" -participant Witness as "Witness Plugin" +participant validator as "Validator Plugin" participant Chain as "Chain Plugin" -Operator->>Node : Start with witness config -Node->>Witness : Initialize with witness name and private key -Witness->>Chain : Request scheduled slot -Chain-->>Witness : Block production slot -Witness->>Chain : Produce block with private key +Operator->>Node : Start with validator config +Node->>validator : Initialize with validator name and private key +validator->>Chain : Request scheduled slot +Chain-->>validator : Block production slot +validator->>Chain : Produce block with private key Chain-->>Node : Accept block Node-->>Operator : Report block production status ``` **Diagram sources** - [config_witness.ini:82-86](file://share/vizd/config/config_witness.ini#L82-L86) -- [witness.hpp:20-32](file://plugins/witness/include/graphene/plugins/witness/witness.hpp#L20-L32) +- [validator.hpp:20-32](file://plugins/validator/include/graphene/plugins/validator/validator.hpp#L20-L32) **Section sources** - [config_witness.ini:68-138](file://share/vizd/config/config_witness.ini#L68-L138) -- [witness.hpp:34-65](file://plugins/witness/include/graphene/plugins/witness/witness.hpp#L34-L65) +- [validator.hpp:34-65](file://plugins/validator/include/graphene/plugins/validator/validator.hpp#L34-L65) ### Debug Node Configuration - Purpose: Simulation and experimentation with debug_node plugin for "what-if" scenarios. @@ -272,9 +272,9 @@ Verify --> End(["Stop and analyze"]) - Key parameters: - P2P endpoint on port 4243 (different from mainnet) with integrated seed nodes. - HTTP and WebSocket endpoints. - - Minimal plugin set focused on chain, p2p, json_rpc, webserver, network_broadcast_api, database_api, witness, witness_api. - - Stale production enabled and witness participation set to minimal. - - Predefined testnet witness and private key. + - Minimal plugin set focused on chain, p2p, json_rpc, webserver, network_broadcast_api, database_api, validator, witness_api. + - Stale production enabled and validator participation set to minimal. + - Predefined testnet validator and private key. - Operational differences: - Optimized for testnet with snapshot support. - Simplified plugin set reduces overhead. @@ -305,7 +305,7 @@ Verify --> End(["Stop and analyze"]) - Key parameters: - P2P endpoint on port 4243 with integrated seed nodes. - Local webserver endpoints for internal trading systems. - - Optimized plugin set focusing on chain, p2p, json_rpc, webserver, network_broadcast_api, witness, database_api, block_info, raw_block, operation_history, account_history, witness_api. + - Optimized plugin set focusing on chain, p2p, json_rpc, webserver, network_broadcast_api, validator, database_api, block_info, raw_block, operation_history, account_history, witness_api. - Skip virtual operations and clear votes before block for improved performance. - Reduced read wait retries for faster response times. - Operational differences: @@ -333,12 +333,12 @@ Verify --> End(["Stop and analyze"]) - [config.ini:7-12](file://share/vizd/config/config.ini#L7-L12) ## Dependency Analysis -The main executable registers and initializes plugins. Witness and MongoDB plugins depend on the chain plugin. The debug node plugin depends on the chain plugin for state manipulation. Dockerfiles embed configuration templates and expose ports for RPC and P2P. +The main executable registers and initializes plugins. validator and MongoDB plugins depend on the chain plugin. The debug node plugin depends on the chain plugin for state manipulation. Dockerfiles embed configuration templates and expose ports for RPC and P2P. ```mermaid graph LR MAIN["programs/vizd/main.cpp"] --> REG["Register plugins"] -REG --> WIT["witness.hpp"] +REG --> WIT["validator.hpp"] REG --> MONGO["mongo_db_plugin.hpp"] REG --> DEBUG["plugin.hpp"] MAIN --> CFG["Config files"] @@ -347,13 +347,13 @@ CFG --> DOCKER["Dockerfiles"] **Diagram sources** - [main.cpp:62-91](file://programs/vizd/main.cpp#L62-L91) -- [witness.hpp:34-65](file://plugins/witness/include/graphene/plugins/witness/witness.hpp#L34-L65) +- [validator.hpp:34-65](file://plugins/validator/include/graphene/plugins/validator/validator.hpp#L34-L65) - [mongo_db_plugin.hpp:14-47](file://plugins/mongo_db/include/graphene/plugins/mongo_db/mongo_db_plugin.hpp#L14-L47) - [plugin.hpp:38-108](file://plugins/debug_node/include/graphene/plugins/debug_node/plugin.hpp#L38-L108) **Section sources** - [main.cpp:62-91](file://programs/vizd/main.cpp#L62-L91) -- [witness.hpp:34-65](file://plugins/witness/include/graphene/plugins/witness/witness.hpp#L34-L65) +- [validator.hpp:34-65](file://plugins/validator/include/graphene/plugins/validator/validator.hpp#L34-L65) - [mongo_db_plugin.hpp:14-47](file://plugins/mongo_db/include/graphene/plugins/mongo_db/mongo_db_plugin.hpp#L14-L47) - [plugin.hpp:38-108](file://plugins/debug_node/include/graphene/plugins/debug_node/plugin.hpp#L38-L108) @@ -367,7 +367,7 @@ CFG --> DOCKER["Dockerfiles"] - Plugin selection: - Disable unused plugins to reduce memory and CPU overhead. - Skip virtual operations and clear old votes to improve performance on full nodes. -- Witness node specifics: +- validator node specifics: - Keep participation thresholds low for testnet; raise for production. - Ensure private key availability and secure storage. - Debug node specifics: @@ -381,7 +381,7 @@ CFG --> DOCKER["Dockerfiles"] - Enable skip-virtual-ops and clear-votes-before-block for maximum throughput. - Resource allocation recommendations: - Full node: moderate CPU, substantial RAM for shared memory, fast SSD for block log and database. - - Witness node: dedicated CPU cores, reliable network, secure key management. + - validator node: dedicated CPU cores, reliable network, secure key management. - Debug node: modest resources, local SSD, restricted network access. - Testnet node: minimal resources, ephemeral data. - MongoDB node: high IOPS storage, separate MongoDB cluster, network isolation. @@ -394,8 +394,8 @@ CFG --> DOCKER["Dockerfiles"] - Insufficient shared memory: - Increase shared-file-size and tune min-free-shared-file-size and inc-shared-file-size. - Monitor block-num-check-free-size frequency. -- Witness production issues: - - Verify witness name and private key. +- validator production issues: + - Verify validator name and private key. - Check participation thresholds and network synchronization. - Debug node anomalies: - Confirm local-only endpoints and secure access. @@ -418,13 +418,13 @@ CFG --> DOCKER["Dockerfiles"] - [config_stock_exchange.ini:22-34](file://share/vizd/config/config_stock_exchange.ini#L22-L34) ## Conclusion -Different VIZ node types serve distinct operational needs. Full nodes provide broad API coverage, witness nodes enable consensus participation, debug nodes support experimentation, testnet nodes accelerate development, MongoDB-integrated nodes enable advanced analytics, and stock exchange nodes optimize for trading infrastructure. Proper configuration, performance tuning, and monitoring are essential for each type to achieve reliable operation. The standardized P2P endpoint configuration ensures consistent network compatibility across all node types. +Different VIZ node types serve distinct operational needs. Full nodes provide broad API coverage, validator nodes enable consensus participation, debug nodes support experimentation, testnet nodes accelerate development, MongoDB-integrated nodes enable advanced analytics, and stock exchange nodes optimize for trading infrastructure. Proper configuration, performance tuning, and monitoring are essential for each type to achieve reliable operation. The standardized P2P endpoint configuration ensures consistent network compatibility across all node types. ## Appendices ### Configuration Templates and Parameters - Full node template: [config.ini:1-136](file://share/vizd/config/config.ini#L1-L136) -- Witness node template: [config_witness.ini:1-138](file://share/vizd/config/config_witness.ini#L1-L138) +- validator node template: [config_witness.ini:1-138](file://share/vizd/config/config_witness.ini#L1-L138) - Debug node template: [config_debug.ini:1-126](file://share/vizd/config/config_debug.ini#L1-L126) - MongoDB node template: [config_mongo.ini:1-135](file://share/vizd/config/config_mongo.ini#L1-L135) - Testnet template: [config_testnet.ini:1-132](file://share/vizd/config/config_testnet.ini#L1-L132) @@ -433,15 +433,15 @@ Different VIZ node types serve distinct operational needs. Full nodes provide br ### Node Type Comparison Matrix -| Feature | Full Node | Witness Node | Debug Node | Testnet Node | MongoDB Node | Stock Exchange Node | +| Feature | Full Node | validator Node | Debug Node | Testnet Node | MongoDB Node | Stock Exchange Node | |---|---|---|---|---|---|---| | P2P endpoint | 0.0.0.0:2001 | 0.0.0.0:2001 | Not set | 0.0.0.0:4243 | 0.0.0.0:4243 | 0.0.0.0:4243 | | Seed nodes | Integrated | Integrated | Not set | Integrated | Integrated | Integrated | | Webserver endpoints | Public | Local | Local | Public | Public | Local | -| Plugins | Broad | Essential + witness | Debug + test | Minimal | Mongo + essentials | Optimized trading | +| Plugins | Broad | Essential + validator | Debug + test | Minimal | Mongo + essentials | Optimized trading | | Shared memory | Large | Large | Small | Large | Large | Large | | Stale production | Off | On (configurable) | On | On | Off | Off | -| Witness participation | N/A | Configurable | N/A | Configurable | N/A | N/A | +| validator participation | N/A | Configurable | N/A | Configurable | N/A | N/A | | Private key | N/A | Required | N/A | N/A | N/A | N/A | | MongoDB integration | No | No | No | No | Yes | No | | Typical use | Production API | Consensus | Dev/Test | CI/Testing | Analytics | Trading Infrastructure | diff --git a/.qoder/repowiki/en/content/Deployment and Operations/Node Deployment/Security Hardening.md b/.qoder/repowiki/en/content/Deployment and Operations/Node Deployment/Security Hardening.md index bcd9ec2c49..80ea17902e 100644 --- a/.qoder/repowiki/en/content/Deployment and Operations/Node Deployment/Security Hardening.md +++ b/.qoder/repowiki/en/content/Deployment and Operations/Node Deployment/Security Hardening.md @@ -1,4 +1,4 @@ -# Security Hardening +# Security Hardening **Referenced Files in This Document** @@ -90,7 +90,7 @@ Key security-relevant elements: The node exposes: - P2P endpoint for peer-to-peer synchronization - HTTP and WebSocket endpoints for API access -- Optional witness production and related APIs +- Optional validator production and related APIs ```mermaid graph TB diff --git a/.qoder/repowiki/en/content/Deployment and Operations/Node Deployment/Service Integration.md b/.qoder/repowiki/en/content/Deployment and Operations/Node Deployment/Service Integration.md index b3063b5bbc..fe26eefa46 100644 --- a/.qoder/repowiki/en/content/Deployment and Operations/Node Deployment/Service Integration.md +++ b/.qoder/repowiki/en/content/Deployment and Operations/Node Deployment/Service Integration.md @@ -1,4 +1,4 @@ -# Service Integration +# Service Integration **Referenced Files in This Document** @@ -268,7 +268,7 @@ Backup and recovery: High availability: - Run multiple node instances behind a load balancer. -- Prefer witness nodes with redundant infrastructure and monitoring. +- Prefer validator nodes with redundant infrastructure and monitoring. - Use rolling updates to minimize downtime during maintenance. [No sources needed since this section provides general guidance] @@ -331,7 +331,7 @@ The VIZ CPP Node is designed for containerized deployment with robust supervisio ## Appendices - Environment variable overrides in the container script: - RPC and P2P endpoints - - Witness name and private key + - validator name and private key - Seed nodes - Extra options diff --git a/.qoder/repowiki/en/content/Development Tools/Build System/Build System.md b/.qoder/repowiki/en/content/Development Tools/Build System/Build System.md index 47b558083c..289f783151 100644 --- a/.qoder/repowiki/en/content/Development Tools/Build System/Build System.md +++ b/.qoder/repowiki/en/content/Development Tools/Build System/Build System.md @@ -1,4 +1,4 @@ -# Build System +# Build System **Referenced Files in This Document** @@ -480,7 +480,7 @@ The `build-linux.sh` script provides a comprehensive automated build solution wi # Basic build with enhanced security ./build-linux.sh -# Low memory node for witnesses +# Low memory node for validators ./build-linux.sh -l # Testnet build @@ -531,7 +531,7 @@ The `build-linux.sh` script provides a comprehensive automated build solution fo # Basic build ./build-linux.sh -# Low memory node for witnesses +# Low memory node for validators ./build-linux.sh -l # Testnet build @@ -579,7 +579,7 @@ The `build-mac.sh` script streamlines macOS development with Homebrew integratio # Basic macOS build ./build-mac.sh -# Low memory node for witness operations +# Low memory node for validator operations ./build-mac.sh -l # Testnet configuration @@ -876,7 +876,7 @@ chmod +x build-linux.sh # Basic development build (runs as user) ./build-linux.sh -# Low memory node for witness operations +# Low memory node for validator operations ./build-linux.sh -l # Testnet build diff --git a/.qoder/repowiki/en/content/Development Tools/Build System/CMake Configuration/Build Targets.md b/.qoder/repowiki/en/content/Development Tools/Build System/CMake Configuration/Build Targets.md index a5e7b61165..dfa4ce8d6d 100644 --- a/.qoder/repowiki/en/content/Development Tools/Build System/CMake Configuration/Build Targets.md +++ b/.qoder/repowiki/en/content/Development Tools/Build System/CMake Configuration/Build Targets.md @@ -1,4 +1,4 @@ -# Build Targets +# Build Targets **Referenced Files in This Document** @@ -118,7 +118,7 @@ subgraph "Plugins" PChain["graphene::chain_plugin"] PWeb["graphene::webserver_plugin"] PJson["graphene::json_rpc"] -PWitness["graphene::witness"] +PWitness["graphene::validator"] PTags["graphene::tags"] PFollow["graphene::follow"] PCommittee["graphene::committee_api"] @@ -163,7 +163,7 @@ SER --> Utilities ### vizd (Full Node Executable) - Purpose: The primary node executable with a broad set of enabled plugins. -- Linkage: Links to appbase, webserver plugin, p2p, chain plugin, network broadcast API, witness, witness API, database API, test API, social network, tags, operation history, account by key, account history, private message, auth utility, debug node, raw block, block info, JSON RPC, follow, committee API, invite API, paid subscription API, custom protocol API, and optionally MongoDB plugin. Also links to protocol, fc, and platform-specific libraries. +- Linkage: Links to appbase, webserver plugin, p2p, chain plugin, network broadcast API, validator, validator API, database API, test API, social network, tags, operation history, account by key, account history, private message, auth utility, debug node, raw block, block info, JSON RPC, follow, committee API, invite API, paid subscription API, custom protocol API, and optionally MongoDB plugin. Also links to protocol, fc, and platform-specific libraries. - Installation: Installable under bin with standard DESTINATION entries. - Platform specifics: - UNIX (non-Apple): Links to rt if found. @@ -182,7 +182,7 @@ VIZD->>Plugs : "link graphene : : webserver_plugin" VIZD->>Plugs : "link graphene : : p2p" VIZD->>Plugs : "link graphene : : chain_plugin" VIZD->>Plugs : "link graphene : : network_broadcast_api" -VIZD->>Plugs : "link graphene : : witness" +VIZD->>Plugs : "link graphene : : validator" VIZD->>Plugs : "link graphene : : witness_api" VIZD->>Plugs : "link graphene : : database_api" VIZD->>Plugs : "link graphene : : test_api_plugin" diff --git a/.qoder/repowiki/en/content/Development Tools/Build System/CMake Configuration/CMake Configuration.md b/.qoder/repowiki/en/content/Development Tools/Build System/CMake Configuration/CMake Configuration.md index 6082c4812d..fd9799f367 100644 --- a/.qoder/repowiki/en/content/Development Tools/Build System/CMake Configuration/CMake Configuration.md +++ b/.qoder/repowiki/en/content/Development Tools/Build System/CMake Configuration/CMake Configuration.md @@ -1,4 +1,4 @@ -# CMake Configuration +# CMake Configuration **Referenced Files in This Document** @@ -286,7 +286,7 @@ Vizd --> P2P["graphene::p2p"] Vizd --> Utilities["graphene_utilities"] Vizd --> ChainPlugin["graphene::chain_plugin"] Vizd --> NetworkBroadcast["graphene::network_broadcast_api"] -Vizd --> Witness["graphene::witness"] +Vizd --> validator["graphene::validator"] Vizd --> WitnessApi["graphene::witness_api"] Vizd --> DatabaseApi["graphene::database_api"] Vizd --> TestApi["graphene::test_api_plugin"] diff --git a/.qoder/repowiki/en/content/Development Tools/Build System/Docker Integration/Docker Integration.md b/.qoder/repowiki/en/content/Development Tools/Build System/Docker Integration/Docker Integration.md index 7aaa42682c..86b1ffb327 100644 --- a/.qoder/repowiki/en/content/Development Tools/Build System/Docker Integration/Docker Integration.md +++ b/.qoder/repowiki/en/content/Development Tools/Build System/Docker Integration/Docker Integration.md @@ -1,4 +1,4 @@ -# Docker Integration +# Docker Integration **Referenced Files in This Document** @@ -122,14 +122,14 @@ GHPR --> DTest - PR builds for production images with ref tagging using latest GitHub Actions versions. - Robust error handling and authentication mechanisms for reliable builds. - Runtime: - - A service wrapper script initializes configuration, applies optional seed nodes, and starts the node with environment-driven endpoints and optional witness settings. + - A service wrapper script initializes configuration, applies optional seed nodes, and starts the node with environment-driven endpoints and optional validator settings. - Configuration templates define RPC endpoints, P2P endpoints, plugin sets, logging, and optional MongoDB connection. - Snapshot assets enable fast initialization of blockchain data. Key runtime environment variables supported by the container entrypoint: - VIZD_SEED_NODES: Comma-separated list of seed nodes to connect to. -- VIZD_WITNESS_NAME: Optional witness name for block production. -- VIZD_PRIVATE_KEY: Private key for witness signing. +- VIZD_WITNESS_NAME: Optional validator name for block production. +- VIZD_PRIVATE_KEY: Private key for validator signing. - VIZD_RPC_ENDPOINT: Override RPC endpoint binding. - VIZD_P2P_ENDPOINT: Override P2P endpoint binding. - VIZD_EXTRA_OPTS: Additional arguments appended to the node command. @@ -268,14 +268,14 @@ The container entrypoint script orchestrates node startup: - Applies default seed nodes from the seednodes file if none are provided via environment. - Copies the packaged configuration into the data directory and adjusts ownership. - Optionally replays from a cached snapshot if present. -- Starts the node with configurable RPC and P2P endpoints and optional witness parameters. +- Starts the node with configurable RPC and P2P endpoints and optional validator parameters. ```mermaid sequenceDiagram participant Entrypoint as "vizd.sh" participant FS as "Mounted Volumes" participant Node as "vizd" -Entrypoint->>Entrypoint : Parse env vars (seed, witness, keys, endpoints) +Entrypoint->>Entrypoint : Parse env vars (seed, validator, keys, endpoints) Entrypoint->>FS : Copy /etc/vizd/config.ini -> /var/lib/vizd/config.ini Entrypoint->>FS : Optionally extract cached snapshot to blockchain dir Entrypoint->>Node : exec vizd with data-dir, endpoints, plugins, extra opts @@ -289,10 +289,10 @@ Node-->>Entrypoint : stdout/stderr - [vizd.sh:1-98](file://share/vizd/vizd.sh#L1-L98) ### Configuration Templates and Plugin Sets -Configuration files define RPC endpoints, plugin sets, logging, and optional MongoDB URI. The testnet configuration enables witness production and includes a default witness and private key suitable for automated testing. +Configuration files define RPC endpoints, plugin sets, logging, and optional MongoDB URI. The testnet configuration enables validator production and includes a default validator and private key suitable for automated testing. - Production: Full plugin set excluding MongoDB. -- Testnet: Includes witness plugin and default witness credentials. +- Testnet: Includes Validator Plugin and default validator credentials. - MongoDB: Adds mongo_db plugin and a MongoDB URI for external connectivity. **Section sources** @@ -359,13 +359,13 @@ For MongoDB-enabled deployments, the configuration template includes a MongoDB U -v /srv/viz/mongo-etc:/etc/vizd \ vizblockchain/vizd:mongo -- Run a witness node: +- Run a validator node: - docker run -d \ - --name viz-witness \ - -e VIZD_WITNESS_NAME="your-witness" \ + --name viz-validator \ + -e VIZD_WITNESS_NAME="your-validator" \ -e VIZD_PRIVATE_KEY="5...your-private-key" \ -p 8090:8090 -p 8091:8091 -p 2001:2001 \ - -v /srv/viz/witness-data:/var/lib/vizd \ + -v /srv/viz/validator-data:/var/lib/vizd \ vizblockchain/vizd:latest [No sources needed since this section provides practical examples without analyzing specific files] @@ -425,8 +425,8 @@ Common issues and resolutions: - Verify VIZD_SEED_NODES or rely on default seednodes; confirm firewall/NAT rules allow inbound P2P traffic on port 2001. - Slow startup due to replay: - Provide a cached snapshot or pre-seeded blockchain data in /var/lib/vizd to accelerate synchronization. -- Witness production not starting: - - Confirm VIZD_WITNESS_NAME and VIZD_PRIVATE_KEY are set and match the configured witness and private key in the configuration. +- validator production not starting: + - Confirm VIZD_WITNESS_NAME and VIZD_PRIVATE_KEY are set and match the configured validator and private key in the configuration. - MongoDB plugin errors: - Ensure the MongoDB URI is reachable from the container network and matches the configuration template. - CI/CD build failures: @@ -454,8 +454,8 @@ The Docker integration for VIZ CPP Node provides flexible, reproducible deployme ### Appendix A: Environment Variables Reference - VIZD_SEED_NODES: Comma-separated P2P endpoints to bootstrap connections. -- VIZD_WITNESS_NAME: Name of the witness to produce blocks. -- VIZD_PRIVATE_KEY: Private key for witness signing. +- VIZD_WITNESS_NAME: Name of the validator to produce blocks. +- VIZD_PRIVATE_KEY: Private key for validator signing. - VIZD_RPC_ENDPOINT: RPC endpoint binding (host:port). - VIZD_P2P_ENDPOINT: P2P endpoint binding (host:port). - VIZD_EXTRA_OPTS: Additional CLI options appended to the node process. diff --git a/.qoder/repowiki/en/content/Development Tools/Build System/Docker Integration/Production Dockerfile.md b/.qoder/repowiki/en/content/Development Tools/Build System/Docker Integration/Production Dockerfile.md index 14c7e983f8..7843edceee 100644 --- a/.qoder/repowiki/en/content/Development Tools/Build System/Docker Integration/Production Dockerfile.md +++ b/.qoder/repowiki/en/content/Development Tools/Build System/Docker Integration/Production Dockerfile.md @@ -1,4 +1,4 @@ -# Production Dockerfile +# Production Dockerfile **Referenced Files in This Document** @@ -261,7 +261,7 @@ F --> K["No MongoDB plugin"] - Set VIZD_RPC_ENDPOINT and VIZD_P2P_ENDPOINT environment variables. - Provide custom seed nodes: - Set VIZD_SEED_NODES to a whitespace-delimited list of seed nodes. -- Enable witness mode: +- Enable validator mode: - Set VIZD_WITNESS_NAME and VIZD_PRIVATE_KEY environment variables. **Section sources** @@ -326,7 +326,7 @@ RUNTIME --> SCRIPT["vizd.sh"] - Permission issues: The entry script sets ownership for /var/lib/vizd; ensure mounted volumes have correct permissions. - Configuration overrides: Place a custom config.ini in /etc/vizd to override defaults. - Seed nodes: Provide VIZD_SEED_NODES to override the default seednodes list. -- Witness mode: Set VIZD_WITNESS_NAME and VIZD_PRIVATE_KEY to enable witness participation. +- validator mode: Set VIZD_WITNESS_NAME and VIZD_PRIVATE_KEY to enable validator participation. **Section sources** - [vizd.sh](file://share/vizd/vizd.sh#L7-L53) diff --git a/.qoder/repowiki/en/content/Development Tools/Build System/Docker Integration/Testnet Dockerfile.md b/.qoder/repowiki/en/content/Development Tools/Build System/Docker Integration/Testnet Dockerfile.md index 1366dc8488..1d38025472 100644 --- a/.qoder/repowiki/en/content/Development Tools/Build System/Docker Integration/Testnet Dockerfile.md +++ b/.qoder/repowiki/en/content/Development Tools/Build System/Docker Integration/Testnet Dockerfile.md @@ -1,4 +1,4 @@ -# Testnet Dockerfile +# Testnet Dockerfile **Referenced Files in This Document** @@ -72,7 +72,7 @@ DF --> SEED ## Core Components - Testnet Dockerfile: Builds with BUILD_TESTNET enabled, installs testnet config, snapshot, and seed nodes, exposes testnet RPC and P2P ports, and declares persistent volumes for data and config. -- Testnet configuration: Defines RPC endpoints, P2P settings, plugin list, and witness production parameters tuned for testnet. +- Testnet configuration: Defines RPC endpoints, P2P settings, plugin list, and validator production parameters tuned for testnet. - Startup script: Resolves seed nodes, applies environment overrides, optionally replays from cached blockchain, and launches the node with proper data directory ownership. - Snapshot: Pre-populates test accounts for immediate testing without manual account creation. - Seed nodes: Provides initial peers for testnet connectivity. @@ -182,14 +182,14 @@ B --> |No| F["Default production config"] - P2P endpoint: Listens on a testnet-specific port. - RPC endpoints: HTTP and WebSocket endpoints configured for testnet. - Plugins: Includes chain, p2p, json_rpc, webserver, and other plugins suitable for testnet development and testing. -- Witness production: Enabled with configurable participation thresholds and witness name/key for testnet block production. +- validator production: Enabled with configurable participation thresholds and validator name/key for testnet block production. ```mermaid flowchart TD CFG["config_testnet.ini"] --> P2P["p2p-endpoint"] CFG --> RPC["webserver-http-endpoint
webserver-ws-endpoint"] CFG --> PLUGINS["plugin list"] -CFG --> WITNESS["witness and private-key"] +CFG --> validator["validator and private-key"] ``` **Diagram sources** @@ -235,7 +235,7 @@ Script->>Node : exec vizd with args and env overrides ### Testnet RPC Endpoint Setup and Environment Overrides - RPC endpoint override: The script allows overriding the RPC endpoint via an environment variable. - P2P endpoint override: Similarly supports overriding the P2P endpoint. -- Witness customization: Allows setting the witness name and private key via environment variables. +- validator customization: Allows setting the validator name and private key via environment variables. ```mermaid flowchart TD @@ -335,7 +335,7 @@ Common issues and resolutions: - No seed nodes provided: The startup script automatically loads seed nodes from the built-in seed list if none are supplied via environment variables. - RPC endpoint conflicts: Override the RPC endpoint using the environment variable to avoid port conflicts. - P2P endpoint conflicts: Override the P2P endpoint similarly. -- Witness production: Ensure the witness name and private key match the testnet configuration when attempting to produce blocks. +- validator production: Ensure the validator name and private key match the testnet configuration when attempting to produce blocks. - Snapshot not applied: Verify the presence of the snapshot file and that the node has permission to read it. - Connectivity delays: Allow time for the node to discover peers and synchronize; monitor logs for peer connection and block sync progress. diff --git a/.qoder/repowiki/en/content/Development Tools/Debugging Tools/Debug Node Plugin.md b/.qoder/repowiki/en/content/Development Tools/Debugging Tools/Debug Node Plugin.md index f180b0f4fe..86e170874b 100644 --- a/.qoder/repowiki/en/content/Development Tools/Debugging Tools/Debug Node Plugin.md +++ b/.qoder/repowiki/en/content/Development Tools/Debugging Tools/Debug Node Plugin.md @@ -1,4 +1,4 @@ -# Debug Node Plugin +# Debug Node Plugin **Referenced Files in This Document** @@ -27,7 +27,7 @@ The debug node plugin is a development and debugging tool designed to manipulate blockchain state locally for testing, reproducibility, and experimentation. It enables: - Generating blocks programmatically for rapid scenario testing - Importing existing blockchain data via binary block logs or JSON arrays -- Inspecting and modifying witness scheduling and hardfork state +- Inspecting and modifying validator scheduling and hardfork state - Applying targeted database edits during block application It integrates with the chain plugin for database access and the JSON-RPC plugin for API exposure, allowing controlled manipulation of chain state without affecting consensus on public networks. @@ -78,7 +78,7 @@ CH --> HF - Internal mechanisms - Uses database flags to skip validations for faster import - Applies targeted database edits per block head to simulate state changes - - Logs and conditionally modifies witness signing keys to enable block production + - Logs and conditionally modifies validator signing keys to enable block production Key API definitions and declarations are declared in the plugin header and implemented in the plugin source. @@ -123,14 +123,14 @@ RPC-->>Client : "JSON-RPC response" - Imports blocks from a JSON file containing an array of signed blocks. - Supports skip flags to bypass validations for faster import. - debug_generate_blocks - - Generates a given number of blocks by scheduling the current head time and modifying witness signing keys if needed. - - Accepts skip flags and optional key editing to align witness keys with the provided private key. + - Generates a given number of blocks by scheduling the current head time and modifying validator signing keys if needed. + - Accepts skip flags and optional key editing to align validator keys with the provided private key. - debug_generate_blocks_until - Generates blocks until the chain head reaches a specified absolute time, optionally skipping intermediate slots. - debug_pop_block - Returns the last block without popping it from the chain (useful for inspection). - debug_get_witness_schedule - - Retrieves the current witness schedule object for inspection. + - Retrieves the current validator schedule object for inspection. - debug_set_hardfork - Sets the active hardfork to a given ID (no-op if beyond supported range). - debug_has_hardfork @@ -159,7 +159,7 @@ NextOrDone --> |No| Done(["Return total pushed"]) - [plugin.cpp](file://plugins/debug_node/plugin.cpp#L479-L555) ### Block Generation Workflow -The block generation process selects the scheduled witness for the next slot, compares its signing key with the provided debug key, optionally edits the witness object to match, and generates a block signed by the debug key. +The block generation process selects the scheduled validator for the next slot, compares its signing key with the provided debug key, optionally edits the validator object to match, and generates a block signed by the debug key. ```mermaid flowchart TD @@ -169,11 +169,11 @@ ZeroCheck --> |No| ParseKey["Parse WIF private key"] ParseKey --> ValidKey{"Valid key?"} ValidKey --> |No| Exit0 ValidKey --> |Yes| Loop["While produced < count"] -Loop --> SlotCalc["Compute slot and scheduled witness"] +Loop --> SlotCalc["Compute slot and scheduled validator"] SlotCalc --> CompareKey{"Scheduled key == debug key?"} CompareKey --> |No| EditNeeded{"edit_if_needed?"} EditNeeded --> |No| ExitLoop["Break loop"] -EditNeeded --> |Yes| ApplyEdit["Apply debug_update to modify witness signing key"] +EditNeeded --> |Yes| ApplyEdit["Apply debug_update to modify validator signing key"] ApplyEdit --> Loop CompareKey --> |Yes| GenBlock["Generate block at scheduled time"] GenBlock --> Inc["Increment produced and slot"] @@ -188,8 +188,8 @@ ExitLoop --> Return(["Return produced count"]) **Section sources** - [plugin.cpp](file://plugins/debug_node/plugin.cpp#L222-L288) -### Witness Schedule Inspection -Retrieves the current witness schedule object from the database for inspection and debugging. +### validator Schedule Inspection +Retrieves the current validator schedule object from the database for inspection and debugging. **Section sources** - [plugin.cpp](file://plugins/debug_node/plugin.cpp#L427-L430) @@ -301,7 +301,7 @@ Common issues and resolutions: - Verify the block log path and index exist and contain sufficient blocks. - Validation failures during import - Use appropriate skip flags to bypass checks; confirm compatibility with mainnet/testnet block formats. -- Witness key mismatch +- validator key mismatch - Provide a valid WIF private key and allow key editing if needed; otherwise, block generation will halt. - Hardfork ID out of range - Set hardfork ID within supported bounds; exceeding the maximum has no effect. @@ -319,7 +319,7 @@ Operational tips: - [debug_node_plugin.md](file://documentation/debug_node_plugin.md#L50-L134) ## Conclusion -The debug node plugin provides powerful tools for blockchain state manipulation in development environments. It supports rapid block generation, efficient import of existing data, witness schedule inspection, and hardfork control. Proper use of skip flags, careful key management, and restricted API exposure ensures safe and effective debugging without compromising production integrity. +The debug node plugin provides powerful tools for blockchain state manipulation in development environments. It supports rapid block generation, efficient import of existing data, validator schedule inspection, and hardfork control. Proper use of skip flags, careful key management, and restricted API exposure ensures safe and effective debugging without compromising production integrity. [No sources needed since this section summarizes without analyzing specific files] @@ -329,9 +329,9 @@ The debug node plugin provides powerful tools for blockchain state manipulation - Creating a test environment from live chain data - Export blocks from a live node, start a new node with debug_node enabled, and import blocks via debug_push_blocks or debug_push_json_blocks. - Reproducing edge cases - - Generate blocks until a specific time, adjust hardfork state, and simulate witness key changes to reproduce timing-sensitive issues. + - Generate blocks until a specific time, adjust hardfork state, and simulate validator key changes to reproduce timing-sensitive issues. - Validating state changes - - Inspect witness schedules and hardfork properties, then generate blocks to observe behavioral differences. + - Inspect validator schedules and hardfork properties, then generate blocks to observe behavioral differences. **Section sources** - [debug_node_plugin.md](file://documentation/debug_node_plugin.md#L50-L134) diff --git a/.qoder/repowiki/en/content/Development Tools/Debugging Tools/Debugging Tools.md b/.qoder/repowiki/en/content/Development Tools/Debugging Tools/Debugging Tools.md index 9c2133285f..439138bc26 100644 --- a/.qoder/repowiki/en/content/Development Tools/Debugging Tools/Debugging Tools.md +++ b/.qoder/repowiki/en/content/Development Tools/Debugging Tools/Debugging Tools.md @@ -1,4 +1,4 @@ -# Debugging Tools +# Debugging Tools **Referenced Files in This Document** @@ -105,7 +105,7 @@ CFG --> P2P ## Core Components - Debug node plugin - - Provides APIs to push blocks from disk or JSON, generate blocks locally, pop blocks, inspect witness schedule, and control hardfork state + - Provides APIs to push blocks from disk or JSON, generate blocks locally, pop blocks, inspect validator schedule, and control hardfork state - Supports applying database updates at specific block heights and logging decisions - Exposes program options for initial database edit scripts - Transaction signing utilities @@ -116,7 +116,7 @@ CFG --> P2P - Color-coded logging improves console readability and debugging efficiency Key capabilities: -- State inspection via database access and witness schedule retrieval +- State inspection via database access and validator schedule retrieval - Transaction tracing by generating blocks and observing accepted transactions - Blockchain state visualization by replaying blocks from logs or JSON - Signing diagnostics using deterministic signing utilities @@ -164,9 +164,9 @@ Note over Client,P2P : "Enhanced P2P plugin logs with ANSI color codes for impro ### Debug Node Plugin The debug node plugin offers: - Block replay from block log and JSON arrays -- Local block generation with configurable witness key and skipping of validations +- Local block generation with configurable validator key and skipping of validations - Database update hooks applied at specific block heights -- Hardfork state control and witness schedule inspection +- Hardfork state control and validator schedule inspection ```mermaid classDiagram @@ -201,14 +201,14 @@ plugin --> plugin_impl : "owns" Key behaviors: - Block replay honors skip flags to bypass expensive validations when needed -- Local block generation modifies witness signing keys to accept self-signed blocks +- Local block generation modifies validator signing keys to accept self-signed blocks - Hardfork state can be set programmatically for testing activation logic - Logging toggles help reduce noise during automated tests Practical usage patterns: - Replay historical blocks from a block log to reproduce state - Generate blocks deterministically for consensus timing tests -- Inspect witness schedule and hardfork state during debugging sessions +- Inspect validator schedule and hardfork state during debugging sessions **Section sources** - [plugin.cpp:321-420](file://plugins/debug_node/plugin.cpp#L321-L420) @@ -304,7 +304,7 @@ Operational insights with enhanced visual distinction: - [node.cpp:5091-5108](file://libraries/network/node.cpp#L5091-L5108) ## Dependency Analysis -The debug node plugin depends on the chain plugin and JSON-RPC infrastructure. It interacts with the database to push blocks, modify witness keys, and manage hardfork state. The P2P plugin depends on the chain plugin for validation and delegates block/trx handling to it. The enhanced logging system relies on ANSI color code definitions and the underlying logging framework. +The debug node plugin depends on the chain plugin and JSON-RPC infrastructure. It interacts with the database to push blocks, modify validator keys, and manage hardfork state. The P2P plugin depends on the chain plugin for validation and delegates block/trx handling to it. The enhanced logging system relies on ANSI color code definitions and the underlying logging framework. ```mermaid graph LR @@ -380,12 +380,12 @@ E --> F["Replay blocks with skip flags to isolate issue"] ### Consensus Issues Symptoms: - Blocks not accepted or chain stalls -- Witness participation thresholds not met +- validator participation thresholds not met Workflow: - Use debug_generate_blocks to advance the chain deterministically -- Temporarily modify witness signing keys to accept self-signed blocks -- Inspect witness schedule and hardfork state via debug APIs +- Temporarily modify validator signing keys to accept self-signed blocks +- Inspect validator schedule and hardfork state via debug APIs ```mermaid sequenceDiagram @@ -393,8 +393,8 @@ participant Dev as "Developer" participant DNP as "debug_node" participant DB as "Database" Dev->>DNP : "debug_generate_blocks(key,count,skip)" -DNP->>DB : "modify witness signing_key if needed" -DNP->>DB : "generate_block(slot,witness,priv_key,skip)" +DNP->>DB : "modify validator signing_key if needed" +DNP->>DB : "generate_block(slot,validator,priv_key,skip)" DB-->>DNP : "new head block" ``` @@ -471,7 +471,7 @@ Enhanced diagnostic approach: - Unit testing: Use sign_transaction and sign_digest to validate signing logic in isolation - Integration testing: Replay blocks from JSON logs with skip flags to accelerate tests - Staging: Enable debug node plugin with restricted RPC access and targeted edit scripts -- Production troubleshooting: Temporarily enable debug APIs on loopback, replay problematic blocks, and inspect witness schedule and hardfork state +- Production troubleshooting: Temporarily enable debug APIs on loopback, replay problematic blocks, and inspect validator schedule and hardfork state - **Updated** Utilize enhanced color-coded logging for rapid identification of network issues in production environments **Section sources** @@ -492,7 +492,7 @@ The VIZ C++ Node provides robust debugging tooling centered around the debug nod - debug_generate_blocks: Produce blocks with a specified key - debug_generate_blocks_until: Advance time-based to a target head block time - debug_pop_block: Remove the head block -- debug_get_witness_schedule: Retrieve witness schedule object +- debug_get_witness_schedule: Retrieve validator schedule object - debug_set_hardfork / debug_has_hardfork: Control and query hardfork state **Section sources** diff --git a/.qoder/repowiki/en/content/Development Tools/Development Tools.md b/.qoder/repowiki/en/content/Development Tools/Development Tools.md index f711208e14..55d91c8010 100644 --- a/.qoder/repowiki/en/content/Development Tools/Development Tools.md +++ b/.qoder/repowiki/en/content/Development Tools/Development Tools.md @@ -1,4 +1,4 @@ -# Development Tools +# Development Tools **Referenced Files in This Document** @@ -287,7 +287,7 @@ Root --> PRG ## Performance Considerations - Use Release builds for performance-sensitive tasks. - Enable coverage only when profiling or auditing code coverage. -- Consider LOW_MEMORY_NODE for resource-constrained environments (e.g., witnesses). +- Consider LOW_MEMORY_NODE for resource-constrained environments (e.g., validators). - Use Docker images to standardize environments and avoid performance regressions caused by local toolchain differences. [No sources needed since this section provides general guidance] diff --git a/.qoder/repowiki/en/content/Development Tools/Development Workflow.md b/.qoder/repowiki/en/content/Development Tools/Development Workflow.md index 50667c38ad..ad630bc910 100644 --- a/.qoder/repowiki/en/content/Development Tools/Development Workflow.md +++ b/.qoder/repowiki/en/content/Development Tools/Development Workflow.md @@ -1,4 +1,4 @@ -# Development Workflow +# Development Workflow **Referenced Files in This Document** @@ -322,7 +322,7 @@ Dev --> Tests ## Performance Considerations - Build type: prefer Release for production; Debug with coverage for development and analysis. -- Low-memory nodes: use LOW_MEMORY_NODE for witness and seed-node deployments to reduce resource usage. +- Low-memory nodes: use LOW_MEMORY_NODE for validator and seed-node deployments to reduce resource usage. - Docker builds: leverage prebuilt images for faster iteration; build locally when debugging CI issues. [No sources needed since this section provides general guidance] diff --git a/.qoder/repowiki/en/content/Getting Started.md b/.qoder/repowiki/en/content/Getting Started.md index 4157f07e37..b5342c6936 100644 --- a/.qoder/repowiki/en/content/Getting Started.md +++ b/.qoder/repowiki/en/content/Getting Started.md @@ -1,4 +1,4 @@ -# Getting Started +# Getting Started **Referenced Files in This Document** @@ -33,7 +33,7 @@ This guide helps you install, configure, and run a VIZ node quickly. It covers: - Prerequisites and environment setup - Multiple installation approaches: Docker, manual compilation, and package installation - First-time setup, configuration, and initial synchronization -- Practical scenarios: full node, testnet node, and witness node +- Practical scenarios: full node, testnet node, and validator node - Security considerations, monitoring, and troubleshooting ## Project Structure @@ -68,7 +68,7 @@ C --> C4["vizd.sh"] ## Core Components - Node binary: vizd, the core blockchain node -- Configuration: config.ini for mainnet, config_testnet.ini for testnet, config_witness.ini for witness-only setups +- Configuration: config.ini for mainnet, config_testnet.ini for testnet, config_witness.ini for validator-only setups - Seed nodes: curated list to bootstrap P2P connectivity - Docker images: prebuilt or self-built images for quick deployment @@ -85,7 +85,7 @@ Key runtime entry point and plugin registration are defined in the node’s main The node exposes: - P2P endpoint for peer-to-peer communication - JSON-RPC HTTP and WebSocket endpoints for API access -- Optional witness production controls +- Optional validator production controls ```mermaid graph TB @@ -95,7 +95,7 @@ P["P2P Plugin"] W["Webserver Plugin"] C["Chain Plugin"] NA["Network Broadcast API"] -WA["Witness API"] +WA["validator API"] end subgraph "External" S["Seed Nodes"] @@ -217,7 +217,7 @@ Install --> End(["Done"]) #### Initial Configuration Files - Mainnet: config.ini - Testnet: config_testnet.ini -- Witness-only: config_witness.ini +- validator-only: config_witness.ini Key areas to review: - P2P endpoint and seed nodes @@ -277,10 +277,10 @@ F --> G["Save and start node"] - [share/vizd/docker/Dockerfile-testnet](file://share/vizd/docker/Dockerfile-testnet#L75-L77) - [documentation/testnet.md](file://documentation/testnet.md#L21-L37) -#### Witness Node +#### validator Node - Use config_witness.ini -- Enable witness and witness_api plugins -- Configure witness name and private key +- Enable validator and witness_api plugins +- Configure validator name and private key **Section sources** - [share/vizd/config/config_witness.ini](file://share/vizd/config/config_witness.ini#L68-L86) @@ -341,8 +341,8 @@ Common issues and resolutions: - Confirm volume mounts for persistent data and config - Rebuild images if dependencies change -- Witness setup - - Ensure witness name and private key are configured +- validator setup + - Ensure validator name and private key are configured - Adjust participation thresholds for testnet if needed **Section sources** @@ -355,7 +355,7 @@ Common issues and resolutions: You now have multiple paths to run a VIZ node: - Docker for quick start - Manual build for customization -- Witness configuration for block production +- validator configuration for block production Follow the configuration and troubleshooting sections to ensure a smooth setup and ongoing operation. @@ -363,7 +363,7 @@ Follow the configuration and troubleshooting sections to ensure a smooth setup a ### Security Considerations - Limit RPC exposure to trusted networks or use reverse proxies -- Use strong private keys for witness nodes +- Use strong private keys for validator nodes - Regularly update the node and monitor logs for anomalies ### Monitoring Node Health diff --git a/.qoder/repowiki/en/content/P2p Plugin.md b/.qoder/repowiki/en/content/P2p Plugin.md index 9176b5744e..8a10541c59 100644 --- a/.qoder/repowiki/en/content/P2p Plugin.md +++ b/.qoder/repowiki/en/content/P2p Plugin.md @@ -1,4 +1,4 @@ -# P2P Plugin +# P2P Plugin **Referenced Files in This Document** @@ -16,7 +16,7 @@ - [dlt_block_log.cpp](file://libraries/chain/dlt_block_log.cpp) - [database.cpp](file://libraries/chain/database.cpp) - [chainbase.hpp](file://thirdparty/chainbase/include/chainbase/chainbase.hpp) -- [witness.cpp](file://plugins/witness/witness.cpp) +- [validator.cpp](file://plugins/validator/validator.cpp) - [CMakeLists.txt](file://plugins/p2p/CMakeLists.txt) - [config.ini](file://share/vizd/config/config.ini) @@ -35,7 +35,7 @@ - Fixed atomic flag ordering in resume: `_catchup_after_pause` is set before clearing `snapshot_in_progress` to prevent stale-head fork - Refactored resume_block_processing() into two-phase design: flags set immediately (Phase 1), drain posted async without wait (Phase 2) to eliminate a fiber deadlock - Handle `shared_memory_corruption_exception` in block acceptance path: triggers auto-recovery instead of peer soft-ban -- Clear `_dlt_syncing` flag on SYNC→FORWARD transition to unblock witness production after sync completes +- Clear `_dlt_syncing` flag on SYNC→FORWARD transition to unblock validator production after sync completes - Added node uptime field (`uptime=Xh Ym Zs`) to DLT Status and DLT P2P Stats log lines ## Table of Contents @@ -330,9 +330,9 @@ flowchart TD Start([Block Received]) --> ValidateType{"Is block_post_validation_message?"} ValidateType --> |No| StandardValidation["Standard block validation
via chain.accept_block()"] ValidateType --> |Yes| ExtractParams["Extract block_id,
witness_account,
signature"] -ExtractParams --> VerifyWitness{"Verify witness exists?"} -VerifyWitness --> |No| RejectBlock["Reject block
(invalid witness)"] -VerifyWitness --> |Yes| VerifySignature["Verify signature
matches witness key"] +ExtractParams --> VerifyWitness{"Verify validator exists?"} +VerifyWitness --> |No| RejectBlock["Reject block
(invalid validator)"] +VerifyWitness --> |Yes| VerifySignature["Verify signature
matches validator key"] VerifySignature --> SignatureValid{"Signature valid?"} SignatureValid --> |No| RejectBlock SignatureValid --> |Yes| ApplyValidation["Apply block post-validation
chain.db().apply_block_post_validation()"] @@ -350,11 +350,11 @@ style BroadcastBlock fill:#ccffcc **Updated** The block validation protocol now includes enhanced concurrent access safety through operation guard protection and conditional latency logging: -The block validation process incorporates operation guards to prevent concurrent access conflicts during witness key validation and block post-validation processing. This ensures thread-safe access to shared blockchain state during high-load conditions. +The block validation process incorporates operation guards to prevent concurrent access conflicts during validator key validation and block post-validation processing. This ensures thread-safe access to shared blockchain state during high-load conditions. The enhanced validation includes: -1. **Witness Signature Verification**: Validates that the block signature matches the claimed witness's public key +1. **validator Signature Verification**: Validates that the block signature matches the claimed validator's public key 2. **Chain ID Consistency**: Ensures blocks belong to the correct blockchain instance 3. **Hard Fork Protection**: Handles different validation requirements across blockchain hard forks 4. **Post-Validation Processing**: Applies additional validation steps after initial acceptance @@ -1150,14 +1150,14 @@ flowchart TD Start([Handle Block]) --> LogGap["Log block gap:
- Block number
- Head block
- Gap size
- Gap detection context
in gray ANSI color"] LogGap --> CheckSyncMode{"Sync mode?"} CheckSyncMode --> |Yes| LogSync["Log sync block:
- Block number
- Head
- Gap
- Clamping info
in gray ANSI color"] -CheckSyncMode --> |No| LogNormal["Log normal block:
- Block number
- Transactions
- Witness
- Gap context
in gray ANSI color"] +CheckSyncMode --> |No| LogNormal["Log normal block:
- Block number
- Transactions
- validator
- Gap context
in gray ANSI color"] LogSync --> AcceptBlock["Accept block via chain.accept_block()"] LogNormal --> AcceptBlock AcceptBlock --> CheckResult{"Result successful?"} CheckResult --> |No| End([End]) CheckResult --> |Yes| CheckSyncMode2{"Sync mode?"} CheckSyncMode2 --> |Yes| End -CheckSyncMode2 --> |No| LogLatency["Log latency:
- Transaction count
- Block number
- Witness
- Latency in milliseconds
in white ANSI color"] +CheckSyncMode2 --> |No| LogLatency["Log latency:
- Transaction count
- Block number
- validator
- Latency in milliseconds
in white ANSI color"] LogLatency --> End([End]) ``` @@ -1447,7 +1447,7 @@ CheckResult --> |No| End([End - No Latency Logging]) CheckResult --> |Yes| CheckSyncMode{"Sync mode?"} CheckSyncMode --> |Yes| End([End - No Latency Logging]) CheckSyncMode --> |No| CalculateLatency["Calculate latency:
fc::time_point::now() - blk_msg.block.timestamp"] -CalculateLatency --> LogLatency["Log latency:
- Transaction count
- Block number
- Witness
- Latency in milliseconds
in white ANSI color"] +CalculateLatency --> LogLatency["Log latency:
- Transaction count
- Block number
- validator
- Latency in milliseconds
in white ANSI color"] LogLatency --> End([End - Latency Logged]) ``` @@ -1474,7 +1474,7 @@ bool result = chain.accept_block(blk_msg.block, sync_mode, ...); if (!sync_mode && result) { fc::microseconds latency = fc::time_point::now() - blk_msg.block.timestamp; ilog(CLOG_WHITE "Got ${t} transactions on block ${b} by ${w} -- latency: ${l} ms" CLOG_RESET, - ("t", blk_msg.block.transactions.size())("b", blk_msg.block.block_num())("w", blk_msg.block.witness)("l", latency.count() / 1000)); + ("t", blk_msg.block.transactions.size())("b", blk_msg.block.block_num())("w", blk_msg.block.validator)("l", latency.count() / 1000)); } ``` @@ -1602,21 +1602,21 @@ The minority fork recovery process now includes several critical enhancements wi 5. **Peer Reconnection**: Enhanced peer management with error handling and gap compatibility checking 6. **Timer Reset**: Proper timing management to prevent immediate re-trigger with gap monitoring -### Integration with Witness Plugin and Gap Detection +### Integration with Validator Plugin and Gap Detection -The minority fork recovery is triggered automatically by the witness plugin with enhanced logging and gap detection: +The minority fork recovery is triggered automatically by the Validator Plugin with enhanced logging and gap detection: ```mermaid sequenceDiagram -participant Witness as Witness Plugin +participant validator as Validator Plugin participant P2P as P2P Plugin participant Chain as Chain Database participant Network as Network Layer -Witness->>Chain : Check recent blocks -Chain-->>Witness : Block validation results -Witness->>Witness : Analyze fork scenario
with gap detection +validator->>Chain : Check recent blocks +Chain-->>validator : Block validation results +validator->>validator : Analyze fork scenario
with gap detection alt Minority fork detected -Witness->>P2P : resync_from_lib() +validator->>P2P : resync_from_lib() Note over P2P : Enhanced logging with gap context
in gray ANSI color P2P->>Chain : Pop blocks to LIB
with gap boundary awareness P2P->>Chain : Reset fork database
including gap detection @@ -1627,12 +1627,12 @@ end ``` **Diagram sources** -- [witness.cpp:540-552](file://plugins/witness/witness.cpp#L540-L552) +- [validator.cpp:540-552](file://plugins/validator/validator.cpp#L540-L552) - [p2p_plugin.cpp:992-1061](file://plugins/p2p/p2p_plugin.cpp#L992-L1061) **Section sources** - [p2p_plugin.cpp:992-1061](file://plugins/p2p/p2p_plugin.cpp#L992-L1061) -- [witness.cpp:540-552](file://plugins/witness/witness.cpp#L540-L552) +- [validator.cpp:540-552](file://plugins/validator/validator.cpp#L540-L552) ## Enhanced Block Validation @@ -1644,10 +1644,10 @@ The enhanced block validation incorporates operation guards to prevent race cond ```mermaid flowchart TD -BlockReceived([Block Received]) --> ExtractWitness["Extract witness information"] +BlockReceived([Block Received]) --> ExtractWitness["Extract validator information"] ExtractWitness --> AcquireGuard["Acquire operation guard"] -AcquireGuard --> VerifyWitness["Verify witness exists"] -VerifyWitness --> VerifySignature["Verify signature matches witness key"] +AcquireGuard --> VerifyWitness["Verify validator exists"] +VerifyWitness --> VerifySignature["Verify signature matches validator key"] VerifySignature --> ReleaseGuard["Release operation guard"] ReleaseGuard --> ApplyValidation["Apply block post-validation"] ApplyValidation --> BroadcastBlock["Broadcast block to peers"] @@ -1662,7 +1662,7 @@ The operation guard system provides several layers of protection with gap detect 1. **Resize Barrier Participation**: Operation guards participate in the shared memory resize barrier 2. **Lock Acquisition**: Automatically waits for resize operations to complete -3. **Thread Safety**: Prevents concurrent access conflicts during witness key validation +3. **Thread Safety**: Prevents concurrent access conflicts during validator key validation 4. **Resource Management**: Ensures proper cleanup and release of resources 5. **Gap Detection Integration**: Operation guards work with gap detection mechanisms @@ -1761,7 +1761,7 @@ Enhanced error handling protects against various failure scenarios with gap dete if (!sync_mode && result) { fc::microseconds latency = fc::time_point::now() - blk_msg.block.timestamp; ilog(CLOG_WHITE "Got ${t} transactions on block ${b} by ${w} -- latency: ${l} ms" CLOG_RESET, - ("t", blk_msg.block.transactions.size())("b", blk_msg.block.block_num())("w", blk_msg.block.witness)("l", latency.count() / 1000)); + ("t", blk_msg.block.transactions.size())("b", blk_msg.block.block_num())("w", blk_msg.block.validator)("l", latency.count() / 1000)); } ``` @@ -1796,7 +1796,7 @@ Three relevant flags are now atomic: - `snapshot_in_progress` (conceptually cleared by the caller via a guard) — controls whether new blocks are accepted. - `_catchup_after_pause` — tells the production gate that we just resumed and are catching up. -**Critical ordering**: `_catchup_after_pause` must be set to `true` **before** `snapshot_in_progress` is cleared. If the order were reversed, the witness plugin could observe the snapshot complete but the catchup flag unset, mis-classify the head as a stale fork, and refuse to produce. +**Critical ordering**: `_catchup_after_pause` must be set to `true` **before** `snapshot_in_progress` is cleared. If the order were reversed, the Validator Plugin could observe the snapshot complete but the catchup flag unset, mis-classify the head as a stale fork, and refuse to produce. This ordering is enforced in `p2p_plugin::resume_block_processing()` via `dlt_p2p_node::set_resume_flags()` (sets both atomics) followed by an async post for the P2P-thread-only drain. @@ -1826,7 +1826,7 @@ When `dlt_p2p_node` receives a block from the network and calls `push_block()`, ### Clear Syncing Flag on SYNC→FORWARD Transition -`dlt_p2p_node` now explicitly clears the `_dlt_syncing` flag when transitioning from SYNC state to FORWARD state. Previously the flag could remain set after the sync completed, causing the witness plugin to see `chain().is_syncing() == true` indefinitely and refuse to produce blocks. +`dlt_p2p_node` now explicitly clears the `_dlt_syncing` flag when transitioning from SYNC state to FORWARD state. Previously the flag could remain set after the sync completed, causing the Validator Plugin to see `chain().is_syncing() == true` indefinitely and refuse to produce blocks. **Section sources** - [dlt_p2p_node.cpp](file://libraries/network/dlt_p2p_node.cpp) @@ -1856,7 +1856,7 @@ P2P[p2p_plugin] Chain[chain::plugin] AppBase[appbase] Snapshot[snapshot_plugin] -Witness[witness_plugin] +validator[witness_plugin] end subgraph "Network Dependencies" Node[node.hpp] @@ -1880,7 +1880,7 @@ P2P --> Chain P2P --> Node P2P --> AppBase P2P --> Snapshot -P2P --> Witness +P2P --> validator Node --> PeerConn Node --> Messages Node --> PeerDB @@ -1906,7 +1906,7 @@ Key dependency relationships: 2. **Network Foundation**: Relies on the network library for peer communication with gap-aware protocols 3. **Application Framework**: Uses appbase for plugin lifecycle management 4. **Snapshot Coordination**: Integrates with snapshot plugin for trusted peer management with gap detection -5. **Witness Integration**: Works closely with witness plugin for fork detection and gap monitoring +5. **validator Integration**: Works closely with Validator Plugin for fork detection and gap monitoring 6. **Database Protection**: Leverages chainbase operation guards for concurrent access safety with gap detection 7. **DLT Mode Support**: Integrates with dlt_block_log for snapshot-based block serving with sophisticated gap detection 8. **Gap Detection**: Enhanced integration with gap detection mechanisms throughout the plugin stack @@ -2032,7 +2032,7 @@ The P2P plugin implements several performance optimization strategies with enhan **Updated** For minority fork scenarios with gap detection: -1. **Detection**: Monitor witness plugin logs for minority fork warnings with gap context +1. **Detection**: Monitor Validator Plugin logs for minority fork warnings with gap context 2. **Automatic Recovery**: The system automatically triggers `resync_from_lib()` with gap detection 3. **Manual Intervention**: Use RPC commands to trigger recovery if automatic detection fails 4. **Verification**: Monitor logs to confirm successful recovery and synchronization with gap awareness @@ -2175,21 +2175,21 @@ The P2P Plugin represents a sophisticated implementation of blockchain networkin **Updated** Key enhancements include: -1. **Security Focus**: Advanced block validation and witness verification mechanisms with gap detection +1. **Security Focus**: Advanced block validation and validator verification mechanisms with gap detection 2. **Performance Optimization**: Efficient synchronization and connection management with gap-aware optimizations 3. **Operational Excellence**: Comprehensive monitoring and diagnostic capabilities with gap detection 4. **Extensibility**: Clean interfaces that support future enhancements with gap detection integration 5. **Enhanced Logging**: Improved logging level consistency with reduced verbosity while maintaining operational visibility 6. **Minority Fork Recovery**: Specialized recovery mechanism for handling fork scenarios with gap awareness 7. **Concurrent Access Safety**: Enhanced protection against race conditions and data corruption with gap detection -8. **Integration Capabilities**: Seamless coordination with witness and snapshot plugins with gap detection +8. **Integration Capabilities**: Seamless coordination with validator and snapshot plugins with gap detection 9. **DLT Mode Support**: Intelligent block range management for snapshot-based nodes with sophisticated gap detection 10. **Graceful Degradation**: Robust error handling and peer interaction management with gap-aware recovery 11. **Enhanced Diagnostics**: Comprehensive logging throughout the sync process with gap detection 12. **Peer Database Analytics**: Detailed peer interaction tracking and troubleshooting with gap awareness 13. **ANSI Color Code Implementation**: Strategic use of color codes (white, cyan, gray, orange, red) for improved console readability and visual distinction 14. **Conditional Latency Logging**: Smart latency reporting that only displays successful block processing information in non-sync mode -15. **Enhanced Block Processing Visibility**: Improved visibility into block processing with detailed transaction and witness information +15. **Enhanced Block Processing Visibility**: Improved visibility into block processing with detailed transaction and validator information 16. **DLT Storage Diagnostics**: Comprehensive block storage monitoring with gap detection and coverage analysis 17. **Automatic Peer Soft-Banning**: Intelligent peer management with automatic soft-banning for sync spam and improved peer interaction 18. **DLT Integrity Verification**: Periodic verification of DLT block log integrity with gap detection and continuity scanning diff --git a/.qoder/repowiki/en/content/Plugin System/Plugin System.md b/.qoder/repowiki/en/content/Plugin System/Plugin System.md index 5588d1911a..4bfc47dd4b 100644 --- a/.qoder/repowiki/en/content/Plugin System/Plugin System.md +++ b/.qoder/repowiki/en/content/Plugin System/Plugin System.md @@ -1,4 +1,4 @@ -# Plugin System +# Plugin System **Referenced Files in This Document** @@ -441,7 +441,7 @@ The following plugins are part of the built-in set. Each plugin exposes specific - mongo_db: MongoDB integration for archival/indexing. - json_rpc: JSON-RPC dispatcher and method registry. - **snapshot**: DLT mode snapshot management and P2P synchronization. -- Additional plugins include: account_by_key, auth_util, block_info, committee_api, custom_protocol_api, debug_node, follow, invite_api, network_broadcast_api, operation_history, paid_subscription_api, private_message, raw_block, social_network, tags, test_api, witness, witness_api. +- Additional plugins include: account_by_key, auth_util, block_info, committee_api, custom_protocol_api, debug_node, follow, invite_api, network_broadcast_api, operation_history, paid_subscription_api, private_message, raw_block, social_network, tags, test_api, validator, witness_api. **Section sources** - [plugins/chain/include/graphene/plugins/chain/plugin.hpp:21-42](file://plugins/chain/include/graphene/plugins/chain/plugin.hpp#L21-L42) @@ -660,7 +660,7 @@ The P2P plugin now provides improved logging for sync block processing with bett - **Sync Mode Detection**: Distinguishes between sync mode and regular block processing - **Head Block Context**: Logs current head block number alongside block processing - **Progress Tracking**: Provides clear indication of synchronization progress -- **Performance Metrics**: Includes transaction count and witness information for non-sync blocks +- **Performance Metrics**: Includes transaction count and validator information for non-sync blocks **Logging Categories:** - **Sync Mode**: Uses `fc_ilog` with "sync" logger for synchronization operations diff --git a/.qoder/repowiki/en/content/Plugin System/Snapshot Plugin System.md b/.qoder/repowiki/en/content/Plugin System/Snapshot Plugin System.md index ee3c28a701..06cad90dd7 100644 --- a/.qoder/repowiki/en/content/Plugin System/Snapshot Plugin System.md +++ b/.qoder/repowiki/en/content/Plugin System/Snapshot Plugin System.md @@ -1,4 +1,4 @@ -# Snapshot Plugin System +# Snapshot Plugin System **Referenced Files in This Document** @@ -15,7 +15,7 @@ - [database.cpp](file://libraries/chain/database.cpp) - [database.hpp](file://libraries/chain/include/graphene/chain/database.hpp) - [dlt_block_log.cpp](file://libraries/chain/dlt_block_log.cpp) -- [witness.cpp](file://plugins/witness/witness.cpp) +- [validator.cpp](file://plugins/validator/validator.cpp) - [file_mutex.cpp](file://thirdparty/fc/src/interprocess/file_mutex.cpp) - [config.ini](file://share/vizd/config/config.ini) - [node.cpp](file://libraries/network/node.cpp) @@ -43,7 +43,7 @@ 4. [Architecture Overview](#architecture-overview) 5. [Detailed Component Analysis](#detailed-component-analysis) 6. [Asynchronous Snapshot Creation](#asynchronous-snapshot-creation) -7. [Witness-Aware Deferral Mechanism](#witness-aware-deferral-mechanism) +7. [validator-Aware Deferral Mechanism](#validator-aware-deferral-mechanism) 8. [Enhanced Error Handling](#enhanced-error-handling) 9. [Automatic Snapshot Discovery](#automatic-snapshot-discovery) 10. [Integrated Recovery Workflow](#integrated-recovery-workflow) @@ -171,7 +171,7 @@ Deep integration with the VIZ blockchain database ensures seamless state transit #### **Stale Snapshot Detection Layer** **New** Intelligent stale snapshot detection system that automatically identifies snapshots older than DLT block log start and schedules urgent fresh snapshot creation to prevent sync gaps when downloading nodes would encounter missing blocks. -**Updated** The modular architecture provides enhanced extensibility and maintainability through clear separation of concerns between interface, serialization, network, database, recovery, asynchronous execution, watchdog, **signal-based DLT block log reset handling**, **enhanced P2P integration**, **enhanced error handling**, **improved undo stack management**, and **stale snapshot detection** components. The recent additions include asynchronous snapshot creation, witness-aware deferral, watchdog mechanisms, automatic snapshot discovery, integrated recovery workflow, comprehensive error handling, **signal-based DLT block log reset handling**, **enhanced P2P integration with trusted peer support**, **dedicated threading for stalled sync detection**, **P2P stale sync detection**, **automatic gap detection for DLT block log initialization**, **enhanced error handling for snapshot download operations**, **improved undo stack management**, **enhanced exception handling**, and **stale snapshot detection**. +**Updated** The modular architecture provides enhanced extensibility and maintainability through clear separation of concerns between interface, serialization, network, database, recovery, asynchronous execution, watchdog, **signal-based DLT block log reset handling**, **enhanced P2P integration**, **enhanced error handling**, **improved undo stack management**, and **stale snapshot detection** components. The recent additions include asynchronous snapshot creation, validator-aware deferral, watchdog mechanisms, automatic snapshot discovery, integrated recovery workflow, comprehensive error handling, **signal-based DLT block log reset handling**, **enhanced P2P integration with trusted peer support**, **dedicated threading for stalled sync detection**, **P2P stale sync detection**, **automatic gap detection for DLT block log initialization**, **enhanced error handling for snapshot download operations**, **improved undo stack management**, **enhanced exception handling**, and **stale snapshot detection**. **Section sources** - [plugin.hpp:42-76](file://plugins/snapshot/include/graphene/plugins/snapshot/plugin.hpp#L42-L76) @@ -308,7 +308,7 @@ FileSys-->>Plugin : success Plugin-->>CLI : completion_status ``` -**Updated** The creation process handles over 30 different object types, from critical singleton objects to optional metadata. Each object type receives specialized treatment based on its memory layout and data structure complexity, demonstrating the modular architecture's flexibility. The recent enhancements include witness-aware deferral to prevent missed block production slots, improved anti-spam protection, integrated recovery workflow capabilities, asynchronous execution system that prevents read-lock timeouts for API and P2P threads, **signal-based DLT block log reset handling**, **dedicated threading for stalled sync detection**, **enhanced error handling for snapshot download operations**, **improved undo stack management**, **stale snapshot detection**, and **enhanced P2P integration with automatic trusted peer endpoint registration**. +**Updated** The creation process handles over 30 different object types, from critical singleton objects to optional metadata. Each object type receives specialized treatment based on its memory layout and data structure complexity, demonstrating the modular architecture's flexibility. The recent enhancements include validator-aware deferral to prevent missed block production slots, improved anti-spam protection, integrated recovery workflow capabilities, asynchronous execution system that prevents read-lock timeouts for API and P2P threads, **signal-based DLT block log reset handling**, **dedicated threading for stalled sync detection**, **enhanced error handling for snapshot download operations**, **improved undo stack management**, **stale snapshot detection**, and **enhanced P2P integration with automatic trusted peer endpoint registration**. **Diagram sources** - [plugin.cpp:885-987](file://plugins/snapshot/plugin.cpp#L885-L987) @@ -485,13 +485,13 @@ The asynchronous execution system includes several critical improvements: - [plugin.cpp:1418-1436](file://plugins/snapshot/plugin.cpp#L1418-L1436) - [plugin.cpp:737-743](file://plugins/snapshot/plugin.cpp#L737-L743) -## Witness-Aware Deferral Mechanism +## validator-Aware Deferral Mechanism -**New** The snapshot plugin now includes a sophisticated witness-aware deferral mechanism that prevents snapshot creation from interrupting witness block production, ensuring network stability and consensus participation. +**New** The snapshot plugin now includes a sophisticated validator-aware deferral mechanism that prevents snapshot creation from interrupting validator block production, ensuring network stability and consensus participation. -### Witness Detection and Deferral Logic +### validator Detection and Deferral Logic -The witness-aware deferral system provides intelligent scheduling based on witness production status: +The validator-aware deferral system provides intelligent scheduling based on validator production status: ```mermaid flowchart TD @@ -512,19 +512,19 @@ CreateDeferred --> Complete Skip --> Complete ``` -### Witness Integration Features +### validator Integration Features -The witness-aware deferral system includes several key components: +The validator-aware deferral system includes several key components: -#### Witness Production Detection -- **Plugin Integration**: Queries witness plugin for production schedule information -- **State Validation**: Checks if witness plugin is properly initialized and started -- **Graceful Degradation**: Falls back to conservative behavior if witness plugin unavailable +#### validator Production Detection +- **Plugin Integration**: Queries Validator Plugin for production schedule information +- **State Validation**: Checks if Validator Plugin is properly initialized and started +- **Graceful Degradation**: Falls back to conservative behavior if Validator Plugin unavailable #### One-Time Deferral Limit -- **Single Deferral Policy**: Allows only one deferral per witness production cycle -- **Infinite Loop Prevention**: Prevents infinite deferral loops with witness-aware checks -- **Production Priority**: Ensures witness block production takes precedence over snapshot creation +- **Single Deferral Policy**: Allows only one deferral per validator production cycle +- **Infinite Loop Prevention**: Prevents infinite deferral loops with validator-aware checks +- **Production Priority**: Ensures validator block production takes precedence over snapshot creation #### Deferred Snapshot Management - **State Persistence**: Stores snapshot path for later execution @@ -539,7 +539,7 @@ The witness-aware deferral system includes several key components: **Section sources** - [plugin.cpp:1390-1484](file://plugins/snapshot/plugin.cpp#L1390-L1484) - [plugin.cpp:1440-1449](file://plugins/snapshot/plugin.cpp#L1440-L1449) -- [witness.cpp:335-551](file://plugins/witness/witness.cpp#L335-L551) +- [validator.cpp:335-551](file://plugins/validator/validator.cpp#L335-L551) ## Enhanced Error Handling @@ -1336,7 +1336,7 @@ The snapshot plugin implements several performance optimization strategies throu ### Database Operation Optimization - Uses strong read locks during snapshot creation to ensure consistency -- Implements witness-aware deferral to prevent missed block production slots +- Implements validator-aware deferral to prevent missed block production slots - Optimized object serialization minimizes CPU overhead ### Enhanced Memory Management @@ -1403,7 +1403,7 @@ The snapshot plugin implements several performance optimization strategies throu - **Minimal Performance Impact**: Stale detection adds negligible overhead during plugin initialization - **Thread-Safe Operations**: Uses atomic flags to prevent race conditions during detection -**Updated** The modular architecture enhances performance by enabling independent optimization of each layer while maintaining system coherence. The watchdog mechanism, **signal-based DLT block log reset handling**, enhanced anti-spam protections, automatic snapshot discovery, integrated recovery workflow, asynchronous execution system, comprehensive error handling, **enhanced P2P integration with trusted peer support**, **dedicated threading for stalled sync detection**, **P2P stale sync detection**, **automatic gap detection for DLT block log initialization**, **enhanced error handling for snapshot download operations**, **improved undo stack management**, **enhanced anti-spam configuration**, **stale snapshot detection**, and **enhanced exception handling** are designed to minimize performance impact while providing comprehensive functionality. Recent improvements include dedicated server thread optimizations, DLT replay efficiency, enhanced error handling performance, witness-aware deferral optimization, **efficient dual-tier soft-ban system implementation**, **signal-based DLT block log reset handling**, **optimized P2P stale sync detection with minimal overhead**, **dedicated threading for stalled sync detection with thread isolation**, **intelligent gap detection preventing index position mismatch assertions**, **enhanced error handling for snapshot download operations**, **improved undo stack management**, **stale snapshot detection with urgent fresh snapshot creation**, and **enhanced exception handling**. +**Updated** The modular architecture enhances performance by enabling independent optimization of each layer while maintaining system coherence. The watchdog mechanism, **signal-based DLT block log reset handling**, enhanced anti-spam protections, automatic snapshot discovery, integrated recovery workflow, asynchronous execution system, comprehensive error handling, **enhanced P2P integration with trusted peer support**, **dedicated threading for stalled sync detection**, **P2P stale sync detection**, **automatic gap detection for DLT block log initialization**, **enhanced error handling for snapshot download operations**, **improved undo stack management**, **enhanced anti-spam configuration**, **stale snapshot detection**, and **enhanced exception handling** are designed to minimize performance impact while providing comprehensive functionality. Recent improvements include dedicated server thread optimizations, DLT replay efficiency, enhanced error handling performance, validator-aware deferral optimization, **efficient dual-tier soft-ban system implementation**, **signal-based DLT block log reset handling**, **optimized P2P stale sync detection with minimal overhead**, **dedicated threading for stalled sync detection with thread isolation**, **intelligent gap detection preventing index position mismatch assertions**, **enhanced error handling for snapshot download operations**, **improved undo stack management**, **stale snapshot detection with urgent fresh snapshot creation**, and **enhanced exception handling**. ### Enhanced Security Performance Considerations - Access control checks are performed efficiently using hash maps for IP lookups @@ -1429,8 +1429,8 @@ The snapshot plugin implements several performance optimization strategies throu **Snapshot Creation Failures** - **Symptom**: Snapshot creation fails with database lock errors -- **Cause**: Witness production conflicts with snapshot creation -- **Solution**: Configure witness-aware deferral or schedule snapshots during maintenance windows +- **Cause**: validator production conflicts with snapshot creation +- **Solution**: Configure validator-aware deferral or schedule snapshots during maintenance windows **Enhanced Asynchronous Execution Issues** - **Symptom**: Snapshot creation appears stuck or slow @@ -1554,8 +1554,8 @@ The snapshot plugin implements several performance optimization strategies throu **Enhanced Urgent Fresh Snapshot Creation Issues** - **Symptom**: Urgent fresh snapshots not being created when stale snapshot detected -- **Cause**: needs_fresh_snapshot flag not reset or witness-aware deferral interfering -- **Solution**: Verify needs_fresh_snapshot flag management, check witness-aware deferral logic, and ensure proper snapshot creation scheduling +- **Cause**: needs_fresh_snapshot flag not reset or validator-aware deferral interfering +- **Solution**: Verify needs_fresh_snapshot flag management, check validator-aware deferral logic, and ensure proper snapshot creation scheduling **Enhanced Diagnostic Tools** @@ -1595,8 +1595,8 @@ The Snapshot Plugin System represents a sophisticated solution for blockchain st **Updated** The recent enhancements with comprehensive snapshot plugin configuration supporting multiple trusted snapshot peers, snapshot scheduling parameters, serving options, watchdog monitoring, automatic snapshot discovery, integrated recovery workflow, enhanced anti-spam protection, **signal-based DLT block log reset handling**, **enhanced P2P integration with trusted peer support**, **enhanced error handling for snapshot download operations**, **improved undo stack management**, **stale snapshot detection**, and **enhanced exception handling** have significantly strengthened the security, reliability, and resource management capabilities of the snapshot distribution services. -Key strengths of the system include its modular architecture, extensive configuration options, built-in performance optimizations, comprehensive security features, automatic snapshot discovery, integrated recovery workflow, DLT replay integration, watchdog monitoring, asynchronous execution system, comprehensive diagnostic capabilities, **signal-based DLT block log reset handling**, **P2P stale sync detection**, **dedicated threading for stalled sync detection**, **automatic gap detection for DLT block log initialization**, **enhanced error handling for snapshot download operations**, **improved undo stack management**, **stale snapshot detection**, **automatic P2P integration with trusted peer support**, and **enhanced exception handling**. The plugin seamlessly integrates with existing VIZ infrastructure while providing powerful new capabilities for state management, peer-to-peer synchronization, automatic recovery from corrupted states, intelligent witness-aware scheduling, **efficient P2P integration with trusted peer support**, **lightweight P2P stale sync detection**, **intelligent gap detection preventing index position mismatch assertions**, **robust error handling for snapshot download operations**, **proper undo stack management during snapshot loading**, **stale snapshot detection with urgent fresh snapshot creation**, and **comprehensive exception handling mechanisms**. +Key strengths of the system include its modular architecture, extensive configuration options, built-in performance optimizations, comprehensive security features, automatic snapshot discovery, integrated recovery workflow, DLT replay integration, watchdog monitoring, asynchronous execution system, comprehensive diagnostic capabilities, **signal-based DLT block log reset handling**, **P2P stale sync detection**, **dedicated threading for stalled sync detection**, **automatic gap detection for DLT block log initialization**, **enhanced error handling for snapshot download operations**, **improved undo stack management**, **stale snapshot detection**, **automatic P2P integration with trusted peer support**, and **enhanced exception handling**. The plugin seamlessly integrates with existing VIZ infrastructure while providing powerful new capabilities for state management, peer-to-peer synchronization, automatic recovery from corrupted states, intelligent validator-aware scheduling, **efficient P2P integration with trusted peer support**, **lightweight P2P stale sync detection**, **intelligent gap detection preventing index position mismatch assertions**, **robust error handling for snapshot download operations**, **proper undo stack management during snapshot loading**, **stale snapshot detection with urgent fresh snapshot creation**, and **comprehensive exception handling mechanisms**. The implementation demonstrates best practices in blockchain plugin development, including proper resource management, error handling, user experience considerations, security through layered access control, comprehensive monitoring and recovery capabilities, asynchronous execution for improved performance, **signal-based DLT block log reset handling**, **dedicated threading for stalled sync detection**, **automatic gap detection for DLT block log initialization**, **enhanced error handling for snapshot download operations**, **improved undo stack management**, **stale snapshot detection**, and **enhanced exception handling**. The modular design enables independent development and testing of each component while maintaining system coherence, representing a significant advancement in extensibility and maintainability. -Future enhancements could focus on additional compression algorithms, enhanced security features, expanded monitoring capabilities, more sophisticated access control policies, improved recovery workflow automation, enhanced DLT replay performance optimization, advanced witness-aware scheduling algorithms, **optimized signal-based DLT block log reset handling**, **further optimization of the dual-tier soft-ban system**, **enhanced P2P stale sync detection**, **improved dedicated threading for stalled sync detection**, **intelligent gap detection preventing index position mismatch assertions**, **comprehensive error handling for all snapshot operations**, **advanced undo stack management techniques**, **stale snapshot detection optimization**, **enhanced exception handling mechanisms**, and **stale snapshot detection with urgent fresh snapshot creation**. \ No newline at end of file +Future enhancements could focus on additional compression algorithms, enhanced security features, expanded monitoring capabilities, more sophisticated access control policies, improved recovery workflow automation, enhanced DLT replay performance optimization, advanced validator-aware scheduling algorithms, **optimized signal-based DLT block log reset handling**, **further optimization of the dual-tier soft-ban system**, **enhanced P2P stale sync detection**, **improved dedicated threading for stalled sync detection**, **intelligent gap detection preventing index position mismatch assertions**, **comprehensive error handling for all snapshot operations**, **advanced undo stack management techniques**, **stale snapshot detection optimization**, **enhanced exception handling mechanisms**, and **stale snapshot detection with urgent fresh snapshot creation**. \ No newline at end of file diff --git a/.qoder/repowiki/en/content/Plugin System/Witness Guard Plugin.md b/.qoder/repowiki/en/content/Plugin System/Witness Guard Plugin.md index 73f9eb337e..d7fbbe97c7 100644 --- a/.qoder/repowiki/en/content/Plugin System/Witness Guard Plugin.md +++ b/.qoder/repowiki/en/content/Plugin System/Witness Guard Plugin.md @@ -1,4 +1,4 @@ -# Witness Guard Plugin +# validator Guard Plugin **Referenced Files in This Document** @@ -25,13 +25,13 @@ ## Introduction -The Witness Guard Plugin is a specialized plugin for the VIZ blockchain node that automatically monitors and maintains witness signing keys to prevent downtime in block production. This plugin serves as a critical safety mechanism for witness operators who want to ensure their witnesses remain productive even when encountering issues with their signing keys. +The validator Guard Plugin is a specialized plugin for the VIZ blockchain node that automatically monitors and maintains validator signing keys to prevent downtime in block production. This plugin serves as a critical safety mechanism for validator operators who want to ensure their validators remain productive even when encountering issues with their signing keys. -The plugin operates by continuously monitoring configured witnesses and automatically restoring their on-chain signing keys when they become null or invalid. It also includes intelligent auto-disable functionality to prevent excessive block production by a single witness, protecting the network from potential centralization risks. +The plugin operates by continuously monitoring configured validators and automatically restoring their on-chain signing keys when they become null or invalid. It also includes intelligent auto-disable functionality to prevent excessive block production by a single validator, protecting the network from potential centralization risks. ## Project Structure -The Witness Guard Plugin follows the standard VIZ plugin architecture pattern with a clear separation between interface and implementation: +The validator Guard Plugin follows the standard VIZ plugin architecture pattern with a clear separation between interface and implementation: ```mermaid graph TB @@ -66,7 +66,7 @@ end ## Core Components -The Witness Guard Plugin consists of several key components that work together to provide comprehensive witness monitoring and protection: +The validator Guard Plugin consists of several key components that work together to provide comprehensive validator monitoring and protection: ### Main Plugin Class The primary plugin class implements the appbase plugin interface and manages the plugin lifecycle. It requires both the chain plugin and p2p plugin to function properly. @@ -80,10 +80,10 @@ The internal implementation class contains all the core logic for: ### Data Structures The plugin maintains several critical data structures: -- **Witness Configuration Map**: Stores witness names with their associated key pairs -- **Consecutive Block Counters**: Tracks blocks produced by each witness +- **validator Configuration Map**: Stores validator names with their associated key pairs +- **Consecutive Block Counters**: Tracks blocks produced by each validator - **Pending Restoration Tracking**: Manages in-flight transactions -- **Auto-Disabled Witnesses**: Prevents automatic restoration of problematic witnesses +- **Auto-Disabled validators**: Prevents automatic restoration of problematic validators **Section sources** - [witness_guard.hpp:11-44](file://plugins/witness_guard/include/graphene/plugins/witness_guard/witness_guard.hpp#L11-L44) @@ -91,33 +91,33 @@ The plugin maintains several critical data structures: ## Architecture Overview -The Witness Guard Plugin integrates deeply with the VIZ blockchain's core infrastructure through a sophisticated event-driven architecture: +The validator Guard Plugin integrates deeply with the VIZ blockchain's core infrastructure through a sophisticated event-driven architecture: ```mermaid sequenceDiagram participant Node as "VIZ Node" participant Chain as "Chain Plugin" -participant Guard as "Witness Guard Plugin" +participant Guard as "validator Guard Plugin" participant DB as "Database" participant P2P as "P2P Network" -participant Witness as "Witness Node" -Note over Node,Witness : Startup Phase +participant validator as "validator Node" +Note over Node,validator : Startup Phase Node->>Guard : plugin_initialize() Guard->>Guard : Parse Configuration Guard->>DB : Verify Authority Keys Guard->>Guard : Setup Monitoring -Note over Node,Witness : Runtime Monitoring +Note over Node,validator : Runtime Monitoring Chain->>Guard : applied_block Signal Guard->>Guard : Check Consecutive Blocks -Guard->>DB : Query Witness Status +Guard->>DB : Query validator Status Guard->>Guard : Auto-Disable Check alt Null Signing Key Detected -Guard->>DB : Fetch Witness Object +Guard->>DB : Fetch validator Object Guard->>Guard : Build Restore Transaction Guard->>P2P : Broadcast Transaction Guard->>Guard : Track Confirmation end -Note over Node,Witness : Periodic Checks +Note over Node,validator : Periodic Checks Chain->>Guard : Block Applied Guard->>Guard : Check Restoration Status Guard->>DB : Confirm Transaction Inclusion @@ -140,14 +140,14 @@ The architecture follows a reactive pattern where the plugin listens for blockch The plugin supports extensive configuration options that allow fine-tuned control over its behavior: #### Core Configuration Options -- **witness-guard-enabled**: Enables or disables the entire plugin functionality -- **witness-guard-witness**: Configures individual witnesses with their key pairs -- **witness-guard-interval**: Sets the frequency of periodic checks in blocks -- **witness-guard-disable**: Controls auto-disable threshold for excessive block production - -#### Witness Configuration Format -Each witness configuration requires three components: -1. **Witness Name**: The account name of the witness +- **validator-guard-enabled**: Enables or disables the entire plugin functionality +- **validator-guard-validator**: Configures individual validators with their key pairs +- **validator-guard-interval**: Sets the frequency of periodic checks in blocks +- **validator-guard-disable**: Controls auto-disable threshold for excessive block production + +#### validator Configuration Format +Each validator configuration requires three components: +1. **validator Name**: The account name of the validator 2. **Signing WIF**: Private key for signing blocks 3. **Active WIF**: Private key for transaction authorization @@ -158,7 +158,7 @@ The plugin validates all configurations during initialization and performs autho ### Monitoring and Restoration Logic -The core monitoring functionality operates through a sophisticated state machine that tracks witness health and automatically restores compromised keys: +The core monitoring functionality operates through a sophisticated state machine that tracks validator health and automatically restores compromised keys: ```mermaid flowchart TD @@ -175,7 +175,7 @@ SyncOK --> |No| SkipRestore SyncOK --> |Yes| CheckLIB["Check LIB Age"] CheckLIB --> LIBOK{"LIB Recent?"} LIBOK --> |No| SkipRestore -LIBOK --> |Yes| CheckWitnesses["Iterate Configured Witnesses"] +LIBOK --> |Yes| CheckWitnesses["Iterate Configured validators"] CheckWitnesses --> NullKey{"Null Signing Key?"} NullKey --> |No| ClearState["Clear Pending State"] NullKey --> |Yes| CheckAutoDisabled{"Auto-Disabled?"} @@ -204,19 +204,19 @@ The restoration process includes comprehensive error handling and retry mechanis ### Auto-Disable Mechanism -The plugin includes an intelligent auto-disable feature designed to prevent excessive block production by a single witness: +The plugin includes an intelligent auto-disable feature designed to prevent excessive block production by a single validator: #### Consecutive Block Detection -The system tracks blocks produced by each witness and increments counters when the same witness produces consecutive blocks. When the counter reaches the configured threshold, the system automatically disables the witness by broadcasting a transaction that sets the signing key to null. +The system tracks blocks produced by each validator and increments counters when the same validator produces consecutive blocks. When the counter reaches the configured threshold, the system automatically disables the validator by broadcasting a transaction that sets the signing key to null. #### Prevention of Excessive Centralization This mechanism serves as a safeguard against: -- Single-witness dominance in block production -- Potential malicious behavior by a single witness +- Single-validator dominance in block production +- Potential malicious behavior by a single validator - Network instability caused by excessive block production #### Operator Intervention Required -When a witness is auto-disabled, the plugin prevents automatic restoration to ensure operators investigate and address underlying issues. Manual intervention is required to re-enable the witness. +When a validator is auto-disabled, the plugin prevents automatic restoration to ensure operators investigate and address underlying issues. Manual intervention is required to re-enable the validator. **Section sources** - [witness_guard.cpp:459-495](file://plugins/witness_guard/witness_guard.cpp#L459-L495) @@ -228,7 +228,7 @@ The plugin implements robust transaction management for both restoration and dis #### Transaction Construction Each operation constructs a properly formatted `witness_update` transaction with: -- Correct witness owner identification +- Correct validator owner identification - Appropriate URL preservation - Proper key updates (restore or disable) - Transaction expiration handling @@ -252,7 +252,7 @@ The plugin maintains detailed tracking of all broadcast transactions: ## Dependency Analysis -The Witness Guard Plugin has carefully managed dependencies that enable it to function effectively within the VIZ ecosystem: +The validator Guard Plugin has carefully managed dependencies that enable it to function effectively within the VIZ ecosystem: ```mermaid graph LR @@ -290,7 +290,7 @@ A --> P The plugin requires the chain plugin for: - Database access and manipulation - Block production scheduling -- Witness object management +- validator object management - Authority verification #### P2P Plugin Integration @@ -313,17 +313,17 @@ The plugin relies on protocol definitions for: ## Performance Considerations -The Witness Guard Plugin is designed with performance optimization in mind to minimize impact on node operations: +The validator Guard Plugin is designed with performance optimization in mind to minimize impact on node operations: ### Efficient Monitoring Strategy - **Event-Driven Architecture**: Uses blockchain event signals rather than polling - **Intelligent Scheduling**: Adjusts check frequency based on network conditions -- **Selective Processing**: Only processes blocks that affect monitored witnesses +- **Selective Processing**: Only processes blocks that affect monitored validators - **Memory Management**: Implements efficient data structures for tracking state ### Resource Optimization - **Minimal Memory Footprint**: Uses compact data structures for tracking -- **Efficient Key Storage**: Optimizes storage of witness configurations +- **Efficient Key Storage**: Optimizes storage of validator configurations - **Connection Management**: Properly manages database connections - **Signal Handling**: Efficient signal connection and disconnection @@ -341,18 +341,18 @@ The Witness Guard Plugin is designed with performance optimization in mind to mi **Symptoms**: Plugin fails to initialize or appears disabled **Causes**: - Missing configuration options -- Invalid witness configurations +- Invalid validator configurations - Missing required plugins (chain, p2p) - Authority verification failures **Solutions**: - Verify all configuration options are properly set -- Check witness configuration format and validity +- Check validator configuration format and validity - Ensure required plugins are enabled in config.ini -- Validate witness authority keys against blockchain state +- Validate validator authority keys against blockchain state -#### Witness Restoration Failures -**Symptoms**: Witness keys not being restored despite null signing keys +#### validator Restoration Failures +**Symptoms**: validator keys not being restored despite null signing keys **Causes**: - Active authority key mismatch - Insufficient network synchronization @@ -366,16 +366,16 @@ The Witness Guard Plugin is designed with performance optimization in mind to mi - Monitor P2P network connectivity and transaction propagation #### Auto-Disable Issues -**Symptoms**: Witnesses being auto-disabled unexpectedly or not being disabled +**Symptoms**: validators being auto-disabled unexpectedly or not being disabled **Causes**: - Incorrect disable threshold configuration - Network timing issues -- Witness scheduling conflicts +- validator scheduling conflicts - Database access problems **Solutions**: - Review and adjust disable threshold settings -- Monitor witness production patterns +- Monitor validator production patterns - Check network stability and block times - Verify database connectivity and performance @@ -385,7 +385,7 @@ The plugin performs extensive validation during initialization: #### Configuration Validation Steps 1. **Option Parsing**: Validates all command-line and config file options -2. **Witness Entry Validation**: Verifies each witness configuration triplet +2. **validator Entry Validation**: Verifies each validator configuration triplet 3. **Authority Verification**: Confirms active keys have proper authority 4. **Network Health Assessment**: Evaluates current network conditions 5. **Stale Production Detection**: Identifies stale production mode activation @@ -403,14 +403,14 @@ The plugin implements comprehensive logging for troubleshooting: ## Conclusion -The Witness Guard Plugin represents a sophisticated solution for maintaining witness reliability in the VIZ blockchain ecosystem. Its comprehensive monitoring capabilities, intelligent auto-disable mechanisms, and robust restoration processes provide essential protection against witness downtime while preventing excessive centralization risks. +The validator Guard Plugin represents a sophisticated solution for maintaining validator reliability in the VIZ blockchain ecosystem. Its comprehensive monitoring capabilities, intelligent auto-disable mechanisms, and robust restoration processes provide essential protection against validator downtime while preventing excessive centralization risks. The plugin's architecture demonstrates best practices in blockchain plugin development, including proper separation of concerns, efficient resource management, and comprehensive error handling. Its integration with the VIZ blockchain's event-driven architecture enables real-time monitoring and response to network conditions. -Key benefits of the Witness Guard Plugin include: +Key benefits of the validator Guard Plugin include: - **Automated Reliability**: Continuous monitoring reduces manual intervention requirements -- **Network Protection**: Prevents excessive witness dominance and centralization +- **Network Protection**: Prevents excessive validator dominance and centralization - **Operational Efficiency**: Intelligent scheduling minimizes performance impact - **Security Enhancement**: Comprehensive validation protects against unauthorized operations -For optimal deployment, operators should carefully configure the plugin according to their specific needs, monitor its performance regularly, and maintain awareness of network conditions that may affect its operation. The plugin's comprehensive logging and error reporting capabilities provide excellent visibility into its operations and help ensure reliable witness protection. \ No newline at end of file +For optimal deployment, operators should carefully configure the plugin according to their specific needs, monitor its performance regularly, and maintain awareness of network conditions that may affect its operation. The plugin's comprehensive logging and error reporting capabilities provide excellent visibility into its operations and help ensure reliable validator protection. \ No newline at end of file diff --git a/.qoder/repowiki/en/content/Project Overview.md b/.qoder/repowiki/en/content/Project Overview.md index da25d38e84..2435b923a3 100644 --- a/.qoder/repowiki/en/content/Project Overview.md +++ b/.qoder/repowiki/en/content/Project Overview.md @@ -1,4 +1,4 @@ -# Project Overview +# Project Overview **Referenced Files in This Document** @@ -11,7 +11,7 @@ - [vizd.sh](file://share/vizd/vizd.sh) - [database.hpp](file://libraries/chain/include/graphene/chain/database.hpp) - [operations.hpp](file://libraries/protocol/include/graphene/protocol/operations.hpp) -- [witness.hpp](file://plugins/witness/include/graphene/plugins/witness/witness.hpp) +- [validator.hpp](file://plugins/validator/include/graphene/plugins/validator/validator.hpp) - [social_network.hpp](file://plugins/social_network/include/graphene/plugins/social_network/social_network.hpp) - [global_property_object.hpp](file://libraries/chain/include/graphene/chain/global_property_object.hpp) @@ -30,18 +30,18 @@ ## Introduction VIZ is a Graphene-based blockchain implementing Fair-DPOS consensus, designed as a full consensus node for the VIZ World platform. It provides a robust, extensible foundation for decentralized applications, social networks, and financial systems with a focus on fairness, transparency, and efficient governance. The project emphasizes: -- Fair-DPOS consensus ensuring equitable witness participation and penalties for missed blocks +- Fair-DPOS consensus ensuring equitable validator participation and penalties for missed blocks - Rich social and content features integrated into the blockchain state -- A modular plugin architecture enabling flexible node configurations for different roles (full node, witness, indexer, etc.) +- A modular plugin architecture enabling flexible node configurations for different roles (full node, validator, indexer, etc.) - Strong developer tooling and operational scripts for building, running, and maintaining nodes Target audiences: -- Node operators: Run full nodes, seed nodes, and witness nodes with configurable plugins and performance tuning +- Node operators: Run full nodes, seed nodes, and validator nodes with configurable plugins and performance tuning - Application developers: Build decentralized apps leveraging JSON-RPC APIs and plugin-specific endpoints - Wallet developers: Integrate with database and chain APIs for account, transaction, and content queries Key differentiators: -- Fair-DPOS with explicit participation checks and penalties for inactive witnesses +- Fair-DPOS with explicit participation checks and penalties for inactive validators - Integrated social features (content, voting, rewards, invites, subscriptions) in the core chain - Extensive plugin ecosystem for APIs, indexing, and specialized node roles - Operational readiness with Docker images, seed nodes, and shell scripts @@ -54,7 +54,7 @@ Key differentiators: ## Project Structure At a high level, the repository is organized into: - Core libraries: Protocol definitions, chain logic, network messaging, utilities, and wallet support -- Plugins: Modular extensions exposing APIs and specialized functionality (e.g., witness, social_network, database_api) +- Plugins: Modular extensions exposing APIs and specialized functionality (e.g., validator, social_network, database_api) - Programs: Executables (vizd, cli_wallet) and utilities - Documentation: Build instructions, plugin usage, testnet setup, and API notes - Share assets: Configurations, Dockerfiles, seed nodes, and shell scripts for deployment @@ -74,7 +74,7 @@ LWallet["libraries/wallet"] end subgraph "Plugins" PChain["plugins/chain"] -PWitness["plugins/witness"] +PWitness["plugins/validator"] PDB["plugins/database_api"] PSocial["plugins/social_network"] PJSON["plugins/json_rpc"] @@ -112,29 +112,29 @@ LWallet --> LProto - [config.ini](file://share/vizd/config/config.ini#L69-L74) ## Core Components -- Chain database and consensus engine: Manages blockchain state, fork resolution, block validation, witness scheduling, and reward/cashout mechanics +- Chain database and consensus engine: Manages blockchain state, fork resolution, block validation, validator scheduling, and reward/cashout mechanics - Protocol definitions: Enumerates operations (transfers, content, governance, social features) and virtual operations for rewards and payouts - Plugin architecture: Enables modular APIs (database, social_network, committee, invite, paid_subscription, witness_api) and transport (JSON-RPC, WebServer) -- Witness plugin: Produces blocks according to Fair-DPOS participation thresholds and schedule +- Validator Plugin: Produces blocks according to Fair-DPOS participation thresholds and schedule - Social network plugin: Exposes content, votes, replies, and governance APIs tailored for VIZ World’s social features - Configuration and deployment: Comprehensive config files, Docker images, and shell scripts for production and testnet Practical examples: - Run a full node: Use the provided Docker image or build from source, configure endpoints and plugins, and start the node - Develop applications: Consume JSON-RPC endpoints exposed by database_api and social_network plugins -- Operate a witness: Enable the witness plugin, set witness name and private key, and monitor participation metrics +- Operate a validator: Enable the Validator Plugin, set validator name and private key, and monitor participation metrics **Section sources** - [database.hpp](file://libraries/chain/include/graphene/chain/database.hpp#L194-L227) - [operations.hpp](file://libraries/protocol/include/graphene/protocol/operations.hpp#L13-L102) -- [witness.hpp](file://plugins/witness/include/graphene/plugins/witness/witness.hpp#L34-L65) +- [validator.hpp](file://plugins/validator/include/graphene/plugins/validator/validator.hpp#L34-L65) - [social_network.hpp](file://plugins/social_network/include/graphene/plugins/social_network/social_network.hpp#L36-L76) - [config.ini](file://share/vizd/config/config.ini#L1-L130) - [config_testnet.ini](file://share/vizd/config/config_testnet.ini#L69-L111) - [vizd.sh](file://share/vizd/vizd.sh#L1-L82) ## Architecture Overview -The node initializes plugins, opens the chain database, and starts P2P networking and webserver services. The chain plugin coordinates block production and validation, while witness plugin enforces Fair-DPOS participation. Social and governance plugins expose APIs for content, voting, and committee operations. +The node initializes plugins, opens the chain database, and starts P2P networking and webserver services. The chain plugin coordinates block production and validation, while Validator Plugin enforces Fair-DPOS participation. Social and governance plugins expose APIs for content, voting, and committee operations. ```mermaid graph TB @@ -143,13 +143,13 @@ Main --> P2P["p2p plugin
peer connections"] Main --> Web["webserver plugin
HTTP/WebSocket"] Main --> JSON["json_rpc plugin
RPC transport"] Main --> DBAPI["database_api plugin
chain queries"] -Main --> Witness["witness plugin
block production"] +Main --> validator["Validator Plugin
block production"] Main --> Social["social_network plugin
content & votes"] Main --> Others["other plugins
committee, invite, paid_subscription"] Chain --> DB["database
blocks, txns, objects"] DBAPI --> DB Social --> DB -Witness --> DB +validator --> DB P2P --> Net["network core"] Web --> JSON ``` @@ -164,8 +164,8 @@ Web --> JSON ## Detailed Component Analysis -### Fair-DPOS Consensus and Witness Production -Fair-DPOS ensures that only participating witnesses produce blocks, with penalties for missed slots. The chain tracks participation and schedules witnesses accordingly. Operators can configure participation thresholds and enable stale production for resilience. +### Fair-DPOS Consensus and validator Production +Fair-DPOS ensures that only participating validators produce blocks, with penalties for missed slots. The chain tracks participation and schedules validators accordingly. Operators can configure participation thresholds and enable stale production for resilience. ```mermaid flowchart TD @@ -173,7 +173,7 @@ Start(["Block production cycle"]) --> CheckSync["Check sync with network"] CheckSync --> SyncOK{"Synced?"} SyncOK --> |No| WaitSync["Wait for peers"] SyncOK --> |Yes| CheckTurn["Is it my turn?"] -CheckTurn --> MyTurn{"Witness slot active?"} +CheckTurn --> MyTurn{"validator slot active?"} MyTurn --> |No| SleepSlot["Sleep until next slot"] MyTurn --> |Yes| CheckParticipation["Check participation threshold"] CheckParticipation --> PartOK{"Participation >= required?"} @@ -187,15 +187,15 @@ Broadcast --> CheckSync ``` **Diagram sources** -- [witness.hpp](file://plugins/witness/include/graphene/plugins/witness/witness.hpp#L20-L32) +- [validator.hpp](file://plugins/validator/include/graphene/plugins/validator/validator.hpp#L20-L32) - [config_testnet.ini](file://share/vizd/config/config_testnet.ini#L99-L103) **Section sources** -- [witness.hpp](file://plugins/witness/include/graphene/plugins/witness/witness.hpp#L20-L32) +- [validator.hpp](file://plugins/validator/include/graphene/plugins/validator/validator.hpp#L20-L32) - [config_testnet.ini](file://share/vizd/config/config_testnet.ini#L99-L103) ### Database and State Management -The database manages blocks, transactions, and domain objects (accounts, content, proposals, witnesses). It supports validation, push operations, fork resolution, and periodic processing (cashouts, inflation, committee actions). +The database manages blocks, transactions, and domain objects (accounts, content, proposals, validators). It supports validation, push operations, fork resolution, and periodic processing (cashouts, inflation, committee actions). ```mermaid classDiagram @@ -279,7 +279,7 @@ class Operation { - [operations.hpp](file://libraries/protocol/include/graphene/protocol/operations.hpp#L13-L102) ### Plugin APIs and Deployment -The node enables a wide array of plugins for APIs and transport. Configuration files define endpoints, plugin lists, and witness credentials. Shell scripts and Docker images streamline deployment and seeding. +The node enables a wide array of plugins for APIs and transport. Configuration files define endpoints, plugin lists, and validator credentials. Shell scripts and Docker images streamline deployment and seeding. ```mermaid sequenceDiagram @@ -317,7 +317,7 @@ The node composes multiple subsystems: graph LR Entry["programs/vizd/main.cpp"] --> Reg["register_plugins()"] Reg --> PChain["plugins/chain"] -Reg --> PWitness["plugins/witness"] +Reg --> PWitness["plugins/validator"] Reg --> PDB["plugins/database_api"] Reg --> PSocial["plugins/social_network"] Reg --> PJSON["plugins/json_rpc"] @@ -345,7 +345,7 @@ LChain --> LProto["libraries/protocol"] - Participation thresholds: Tuning participation requirements impacts block production frequency and network liveness Operational tips: -- Use LOW_MEMORY_NODE build option for consensus-only nodes (witnesses/seed nodes) +- Use LOW_MEMORY_NODE build option for consensus-only nodes (validators/seed nodes) - Monitor shared memory free space and tune increments based on workload - Adjust thread pool size for RPC clients to match CPU cores @@ -359,12 +359,12 @@ Common operational scenarios: - Node not syncing: Verify P2P endpoints and seed nodes; check logs for connection errors - RPC lock errors: Increase read/write wait retries or tune single-write-thread behavior - Memory pressure: Reduce plugin overhead, enable virtual ops skipping, and adjust shared memory parameters -- Witness production issues: Confirm participation thresholds, witness name, and private key configuration +- validator production issues: Confirm participation thresholds, validator name, and private key configuration Useful references: - Logging configuration via config sections for console and file appenders -- Testnet configuration enabling stale production and default witness credentials for development -- Shell script arguments for RPC/P2P endpoints and witness overrides +- Testnet configuration enabling stale production and default validator credentials for development +- Shell script arguments for RPC/P2P endpoints and validator overrides **Section sources** - [config.ini](file://share/vizd/config/config.ini#L112-L130) @@ -372,13 +372,13 @@ Useful references: - [vizd.sh](file://share/vizd/vizd.sh#L62-L73) ## Conclusion -VIZ delivers a Graphene-based blockchain optimized for fairness and social features, with a modular plugin architecture and strong operational tooling. Its Fair-DPOS consensus, integrated content and governance primitives, and extensive APIs make it suitable for diverse use cases—from full node operations and witness roles to application and wallet development. The combination of comprehensive documentation, Docker images, and shell scripts lowers the barrier to entry while offering deep customization for advanced users. +VIZ delivers a Graphene-based blockchain optimized for fairness and social features, with a modular plugin architecture and strong operational tooling. Its Fair-DPOS consensus, integrated content and governance primitives, and extensive APIs make it suitable for diverse use cases—from full node operations and validator roles to application and wallet development. The combination of comprehensive documentation, Docker images, and shell scripts lowers the barrier to entry while offering deep customization for advanced users. ## Appendices - Practical examples: - Full node: Use Docker image or build from source; configure endpoints and plugins; start with default or testnet config - Application development: Consume database_api and social_network endpoints over HTTP/WebSocket - - Witness operation: Enable witness plugin, set witness name and private key, monitor participation and block production + - validator operation: Enable Validator Plugin, set validator name and private key, monitor participation and block production **Section sources** - [README.md](file://README.md#L12-L29) diff --git a/.qoder/repowiki/en/content/Witness.md b/.qoder/repowiki/en/content/Validator.md similarity index 65% rename from .qoder/repowiki/en/content/Witness.md rename to .qoder/repowiki/en/content/Validator.md index 2d8896af71..fc79bf5889 100644 --- a/.qoder/repowiki/en/content/Witness.md +++ b/.qoder/repowiki/en/content/Validator.md @@ -1,9 +1,9 @@ -# Witness +# validator **Referenced Files in This Document** -- [witness.hpp](file://plugins/witness/include/graphene/plugins/witness/witness.hpp) -- [witness.cpp](file://plugins/witness/witness.cpp) +- [validator.hpp](file://plugins/validator/include/graphene/plugins/validator/validator.hpp) +- [validator.cpp](file://plugins/validator/validator.cpp) - [witness_api_plugin.hpp](file://plugins/witness_api/include/graphene/plugins/witness_api/plugin.hpp) - [witness_api_plugin.cpp](file://plugins/witness_api/plugin.cpp) - [witness_objects.hpp](file://libraries/chain/include/graphene/chain/witness_objects.hpp) @@ -29,18 +29,18 @@ ## Update Summary **Changes Made** - HF13: Implemented validator reward sharing — stakeholder reward distribution, `set_reward_sharing_operation`, `distribution_epoch_length` consensus parameter, `process_validator_epoch_distribution()`, new virtual op `stakeholder_reward_operation`; rewards accumulate as TOKEN and convert to SHARES at epoch end -- Added comprehensive witness protection and monitoring capabilities with new witness_guard plugin +- Added comprehensive validator protection and monitoring capabilities with new witness_guard plugin - Enhanced emergency recovery mechanisms with auto-disable thresholds and improved network connectivity features -- Integrated witness key auto-restore functionality with emergency consensus mode support -- Implemented consecutive block auto-disable protection to prevent witness abuse +- Integrated validator key auto-restore functionality with emergency consensus mode support +- Implemented consecutive block auto-disable protection to prevent validator abuse - Added enhanced minority fork detection with automatic recovery mechanisms - Integrated emergency consensus detection and recovery procedures - Enhanced network connectivity features with improved peer synchronization - Isolated production timer on dedicated io_service/thread, preventing P2P I/O from delaying slot callbacks - Fixed lag tight loop: tracks missed slot time and skips ahead to avoid rechecking the same slot every 250ms -- Added production watchdog: fires at 60s (emergency master) or 180s (regular witness) of no block produced -- Added slot hijack detection: counts consecutive slots where emergency master fills slots assigned to our witness -- Fixed false-positive hijack: own-witness blocks no longer trigger hijack counter +- Added production watchdog: fires at 60s (emergency master) or 180s (regular validator) of no block produced +- Added slot hijack detection: counts consecutive slots where emergency master fills slots assigned to our validator +- Fixed false-positive hijack: own-validator blocks no longer trigger hijack counter - Fixed slot index calculation (current_aslot % num_scheduled_witnesses) for correct schedule position - Added not_my_turn streak detection: warns at 500 consecutive iterations (~125s) of schedule misalignment - Added on_block_applied missed-slot diagnostic: dumps full plugin state when incoming block reveals our missed slots @@ -59,16 +59,16 @@ 10. [Conclusion](#conclusion) ## Introduction -This document explains the Witness subsystem of the VIZ node implementation. It covers how witnesses are scheduled, how blocks are produced, how witness participation is monitored, and how the witness-related APIs expose information to clients. The focus is on the witness plugin (block production), the witness API plugin (read-only queries), the witness guard plugin (protection and monitoring), and the underlying chain database that maintains witness state and schedules. +This document explains the validator subsystem of the VIZ node implementation. It covers how validators are scheduled, how blocks are produced, how validator participation is monitored, and how the validator-related APIs expose information to clients. The focus is on the Validator Plugin (block production), the validator API plugin (read-only queries), the validator guard plugin (protection and monitoring), and the underlying chain database that maintains validator state and schedules. -**Updated** Enhanced with comprehensive witness protection and monitoring capabilities, emergency recovery mechanisms, auto-disable thresholds, improved network connectivity features, witness key auto-restore functionality, consecutive block protection, enhanced minority fork detection, emergency consensus integration, and automatic recovery procedures. +**Updated** Enhanced with comprehensive validator protection and monitoring capabilities, emergency recovery mechanisms, auto-disable thresholds, improved network connectivity features, validator key auto-restore functionality, consecutive block protection, enhanced minority fork detection, emergency consensus integration, and automatic recovery procedures. ## Project Structure -The Witness functionality spans four primary areas: -- Witness plugin: Produces blocks and validates blocks posted by other witnesses with optimized timing. -- Witness Guard plugin: Provides protection and monitoring for witness keys with auto-restore and auto-disable capabilities. -- Witness API plugin: Exposes witness-related read-only queries via JSON-RPC. -- Chain database: Maintains witness objects, voting, scheduling, and participation metrics. +The validator functionality spans four primary areas: +- Validator Plugin: Produces blocks and validates blocks posted by other validators with optimized timing. +- validator Guard plugin: Provides protection and monitoring for validator keys with auto-restore and auto-disable capabilities. +- validator API plugin: Exposes validator-related read-only queries via JSON-RPC. +- Chain database: Maintains validator objects, voting, scheduling, and participation metrics. ```mermaid graph TB @@ -76,9 +76,9 @@ subgraph "Node Binary" VIZD["vizd main
registers plugins"] end subgraph "Plugins" -WITNESS["Witness Plugin
optimized block production
fork collision timeout: 21 blocks
minority fork detection
DEBUG logging: enabled"] -WGUARD["Witness Guard Plugin
key auto-restore
auto-disable protection
emergency consensus support"] -WAPI["Witness API Plugin
JSON-RPC queries"] +validator["Validator Plugin
optimized block production
fork collision timeout: 21 blocks
minority fork detection
DEBUG logging: enabled"] +WGUARD["validator Guard Plugin
key auto-restore
auto-disable protection
emergency consensus support"] +WAPI["validator API Plugin
JSON-RPC queries"] SNAPSHOT["Snapshot Plugin
coordinated operations"] P2P["P2P Plugin
broadcast
resync_from_lib()"] CHAIN["Chain Plugin
database access"] @@ -92,15 +92,15 @@ end subgraph "Time Synchronization" TIME["Time Service
NTP synchronization with 250ms ticks"] END -VIZD --> WITNESS +VIZD --> validator VIZD --> WGUARD VIZD --> WAPI VIZD --> SNAPSHOT VIZD --> P2P VIZD --> CHAIN -WITNESS --> P2P -WITNESS --> CHAIN -WITNESS --> TIME +validator --> P2P +validator --> CHAIN +validator --> TIME WGUARD --> CHAIN WGUARD --> P2P CHAIN --> DB @@ -109,13 +109,13 @@ DB --> BPV_OBJ DB --> FORK_DB WAPI --> CHAIN CHAIN --> DB -SNAPSHOT --> WITNESS +SNAPSHOT --> validator ``` **Diagram sources** - [main.cpp:63-92](file://programs/vizd/main.cpp#L63-L92) -- [witness.hpp:34-68](file://plugins/witness/include/graphene/plugins/witness/witness.hpp#L34-L68) -- [witness.cpp:59-118](file://plugins/witness/witness.cpp#L59-L118) +- [validator.hpp:34-68](file://plugins/validator/include/graphene/plugins/validator/validator.hpp#L34-L68) +- [validator.cpp:59-118](file://plugins/validator/validator.cpp#L59-L118) - [witness_guard.hpp:11-48](file://plugins/witness_guard/include/graphene/plugins/witness_guard/witness_guard.hpp#L11-L48) - [witness_guard.cpp:27-78](file://plugins/witness_guard/witness_guard.cpp#L27-L78) - [witness_api_plugin.hpp:56-98](file://plugins/witness_api/include/graphene/plugins/witness_api/plugin.hpp#L56-L98) @@ -132,54 +132,54 @@ SNAPSHOT --> WITNESS - [main.cpp:63-92](file://programs/vizd/main.cpp#L63-L92) ## Core Components -- Witness Plugin +- Validator Plugin - Provides optimized block production loop synchronized to 250ms intervals for deterministic slot time alignment. - Validates whether it is time to produce a block, checks participation thresholds, and signs blocks with configured private keys. - Broadcasts blocks and block post validations via the P2P plugin. - **Enhanced**: Implements forced NTP synchronization when timing issues are detected during block production attempts. - **Enhanced**: Implements comprehensive fork collision detection to prevent competing blocks at the same height. - - **Enhanced**: **NEW**: Implements comprehensive minority fork detection system to identify when all recent blocks were produced by local witnesses only. + - **Enhanced**: **NEW**: Implements comprehensive minority fork detection system to identify when all recent blocks were produced by local validators only. - **Enhanced**: **NEW**: Provides automatic recovery through resync_from_lib() when minority fork is detected. - **Enhanced**: **NEW**: Integrates with skip_undo_history_check flag to control production during recovery scenarios. - **Enhanced**: **NEW**: Implements comprehensive debug logging with verbose traces for block production and chain internals. - - **New**: Provides `is_witness_scheduled_soon()` method to check if any locally-controlled witnesses are scheduled to produce blocks in the upcoming 4 slots. + - **New**: Provides `is_witness_scheduled_soon()` method to check if any locally-controlled validators are scheduled to produce blocks in the upcoming 4 slots. - **New**: Implements two-level fork collision resolution system with configurable timeout blocks parameter (--fork-collision-timeout-blocks). - **New**: Integrates with enhanced fork database for automatic stale fork pruning after successful block application. - **New**: Runs production timer on a dedicated `boost::asio::io_service` and thread, isolated from P2P network I/O. - **New**: Prevents lag tight loop: records missed slot time and skips ahead to avoid rechecking the same slot every 250ms. - - **New**: Production watchdog alerts when no block is produced for 60s (emergency master) or 180s (regular witness); auto-enables verbose debug logging on first fire. - - **New**: Slot hijack detection counts consecutive emergency-master-filled slots assigned to our witness in the shuffled schedule. - - **New**: not_my_turn streak detection warns after 500 consecutive iterations (~125s) of another witness holding all slots. - - **New**: Missed-slot diagnostics via `on_block_applied` signal: detects gaps and dumps full plugin state when our witness missed a slot. -- Witness Guard Plugin - - **NEW**: Provides comprehensive witness protection and monitoring capabilities. - - **NEW**: Implements witness key auto-restore functionality to automatically restore null signing keys. - - **NEW**: Provides consecutive block auto-disable protection to prevent witness abuse by disabling witnesses after N consecutive blocks. + - **New**: Production watchdog alerts when no block is produced for 60s (emergency master) or 180s (regular validator); auto-enables verbose debug logging on first fire. + - **New**: Slot hijack detection counts consecutive emergency-master-filled slots assigned to our validator in the shuffled schedule. + - **New**: not_my_turn streak detection warns after 500 consecutive iterations (~125s) of another validator holding all slots. + - **New**: Missed-slot diagnostics via `on_block_applied` signal: detects gaps and dumps full plugin state when our validator missed a slot. +- validator Guard Plugin + - **NEW**: Provides comprehensive validator protection and monitoring capabilities. + - **NEW**: Implements validator key auto-restore functionality to automatically restore null signing keys. + - **NEW**: Provides consecutive block auto-disable protection to prevent validator abuse by disabling validators after N consecutive blocks. - **NEW**: Supports emergency consensus mode with automatic key restoration and recovery procedures. - - **NEW**: Monitors witness signing keys and automatically restores them when detected as null on-chain. + - **NEW**: Monitors validator signing keys and automatically restores them when detected as null on-chain. - **NEW**: Integrates with P2P plugin to broadcast witness_update transactions for key restoration and disabling. - **NEW**: Implements safety checks including network health monitoring and long fork detection. - **NEW**: Provides configurable check intervals and disable thresholds for flexible protection strategies. -- Witness API Plugin - - Exposes read-only queries for active witnesses, schedule, individual witnesses, and counts. - - Returns API-friendly objects derived from chain witness data. +- validator API Plugin + - Exposes read-only queries for active validators, schedule, individual validators, and counts. + - Returns API-friendly objects derived from chain validator data. - Chain Database - - Stores witness objects, schedules, participation metrics, and supports witness scheduling and participation computations. - - Manages block post validation objects and updates last irreversible block computation based on witness confirmations. + - Stores validator objects, schedules, participation metrics, and supports validator scheduling and participation computations. + - Manages block post validation objects and updates last irreversible block computation based on validator confirmations. - **Enhanced**: Provides enhanced fork database access with comprehensive querying capabilities for fork collision detection. - - **Enhanced**: Implements comprehensive witness reward creation with find_account() validation to prevent crashes from missing account objects. + - **Enhanced**: Implements comprehensive validator reward creation with find_account() validation to prevent crashes from missing account objects. - **Enhanced**: **NEW**: Integrates with skip_undo_history_check flag for controlled production during recovery scenarios. - **Enhanced**: **NEW**: Implements comprehensive debug logging system enabling verbose traces for chain internals and block processing. - **New**: Implements compare_fork_branches() function for intelligent fork weight comparison with +10% longer-chain bonus. - **New**: Provides automatic stale fork pruning mechanism to remove competing blocks from dead forks. - **New**: **Enhanced**: Supports emergency_consensus_active field for emergency consensus mode detection. -**Updated** Added comprehensive error handling and validation for witness reward creation, including find_account() checks before creating vesting rewards, crash prevention mechanisms, clear recovery procedures for database corruption scenarios, new fork collision timeout configuration, two-level fork resolution system with vote-weighted comparison and stuck-head timeout, enhanced fork database querying capabilities, automatic stale fork pruning after successful block application, **NEW**: comprehensive minority fork detection system with automatic recovery mechanisms, **NEW**: enhanced emergency consensus mode integration, **NEW**: skip_undo_history_check flag for controlled production during recovery scenarios, **NEW**: comprehensive debug logging system enabling verbose traces for block production and chain internals, **NEW**: witness protection and monitoring capabilities with auto-restore and auto-disable features, **NEW**: emergency recovery mechanisms with automatic key restoration, **NEW**: consecutive block protection to prevent witness abuse, **NEW**: enhanced network connectivity with improved peer synchronization. +**Updated** Added comprehensive error handling and validation for validator reward creation, including find_account() checks before creating vesting rewards, crash prevention mechanisms, clear recovery procedures for database corruption scenarios, new fork collision timeout configuration, two-level fork resolution system with vote-weighted comparison and stuck-head timeout, enhanced fork database querying capabilities, automatic stale fork pruning after successful block application, **NEW**: comprehensive minority fork detection system with automatic recovery mechanisms, **NEW**: enhanced emergency consensus mode integration, **NEW**: skip_undo_history_check flag for controlled production during recovery scenarios, **NEW**: comprehensive debug logging system enabling verbose traces for block production and chain internals, **NEW**: validator protection and monitoring capabilities with auto-restore and auto-disable features, **NEW**: emergency recovery mechanisms with automatic key restoration, **NEW**: consecutive block protection to prevent validator abuse, **NEW**: enhanced network connectivity with improved peer synchronization. **Section sources** -- [witness.hpp:34-68](file://plugins/witness/include/graphene/plugins/witness/witness.hpp#L34-L68) -- [witness.cpp:59-118](file://plugins/witness/witness.cpp#L59-L118) -- [witness.cpp:206-249](file://plugins/witness/witness.cpp#L206-L249) +- [validator.hpp:34-68](file://plugins/validator/include/graphene/plugins/validator/validator.hpp#L34-L68) +- [validator.cpp:59-118](file://plugins/validator/validator.cpp#L59-L118) +- [validator.cpp:206-249](file://plugins/validator/validator.cpp#L206-L249) - [witness_guard.hpp:11-48](file://plugins/witness_guard/include/graphene/plugins/witness_guard/witness_guard.hpp#L11-L48) - [witness_guard.cpp:27-78](file://plugins/witness_guard/witness_guard.cpp#L27-L78) - [witness_guard.cpp:83-191](file://plugins/witness_guard/witness_guard.cpp#L83-L191) @@ -189,14 +189,14 @@ SNAPSHOT --> WITNESS - [database.hpp:37-83](file://libraries/chain/include/graphene/chain/database.hpp#L37-L83) ## Architecture Overview -The Witness subsystem integrates tightly with the chain database and P2P layer. The witness plugin periodically evaluates conditions to produce a block using optimized 250ms interval scheduling, consults the database for witness scheduling and participation, and broadcasts the resulting block. The witness guard plugin provides continuous monitoring and protection for witness keys, automatically restoring null signing keys and preventing witness abuse through auto-disable mechanisms. The witness API plugin reads from the database to serve JSON-RPC queries. **New**: Other plugins can now coordinate with witness scheduling using the `is_witness_scheduled_soon()` method to avoid conflicts during critical operations. +The validator subsystem integrates tightly with the chain database and P2P layer. The Validator Plugin periodically evaluates conditions to produce a block using optimized 250ms interval scheduling, consults the database for validator scheduling and participation, and broadcasts the resulting block. The validator guard plugin provides continuous monitoring and protection for validator keys, automatically restoring null signing keys and preventing validator abuse through auto-disable mechanisms. The validator API plugin reads from the database to serve JSON-RPC queries. **New**: Other plugins can now coordinate with validator scheduling using the `is_witness_scheduled_soon()` method to avoid conflicts during critical operations. -**Enhanced** The architecture now includes robust NTP time synchronization with automatic fallback mechanisms, crash-safe shutdown procedures, plugin coordination capabilities through the new scheduling method, comprehensive fork collision detection system with two-level resolution, enhanced fork database querying capabilities, automatic stale fork pruning, enhanced witness reward creation with comprehensive validation and error handling, **NEW**: comprehensive minority fork detection system with automatic recovery mechanisms, **NEW**: enhanced emergency consensus mode integration, **NEW**: skip_undo_history_check flag for controlled production during recovery scenarios, **NEW**: comprehensive debug logging system enabling verbose traces for block production and chain internals, **NEW**: witness protection and monitoring capabilities with auto-restore and auto-disable features, **NEW**: emergency recovery mechanisms with automatic key restoration, **NEW**: consecutive block protection to prevent witness abuse, **NEW**: enhanced network connectivity with improved peer synchronization. +**Enhanced** The architecture now includes robust NTP time synchronization with automatic fallback mechanisms, crash-safe shutdown procedures, plugin coordination capabilities through the new scheduling method, comprehensive fork collision detection system with two-level resolution, enhanced fork database querying capabilities, automatic stale fork pruning, enhanced validator reward creation with comprehensive validation and error handling, **NEW**: comprehensive minority fork detection system with automatic recovery mechanisms, **NEW**: enhanced emergency consensus mode integration, **NEW**: skip_undo_history_check flag for controlled production during recovery scenarios, **NEW**: comprehensive debug logging system enabling verbose traces for block production and chain internals, **NEW**: validator protection and monitoring capabilities with auto-restore and auto-disable features, **NEW**: emergency recovery mechanisms with automatic key restoration, **NEW**: consecutive block protection to prevent validator abuse, **NEW**: enhanced network connectivity with improved peer synchronization. ```mermaid sequenceDiagram -participant Timer as "Witness Impl
schedule_production_loop (250ms ticks)" -participant Guard as "Witness Guard
auto-restore & protection" +participant Timer as "validator Impl
schedule_production_loop (250ms ticks)" +participant Guard as "validator Guard
auto-restore & protection" participant NTP as "NTP Service
time synchronization" participant DB as "Chain Database
DEBUG logging : enabled
emergency_consensus_active" participant ForkDB as "Fork Database
collision detection
stale pruning" @@ -254,7 +254,7 @@ end else no competing blocks alt minority fork detection Timer->>ForkDB : check last CHAIN_MAX_WITNESSES blocks -alt all from our witnesses +alt all from our validators alt emergency consensus active Timer->>Timer : skip minority fork detection else enable-stale-production enabled @@ -264,9 +264,9 @@ Timer->>P2P : resync_from_lib() Timer->>Timer : production disabled Timer->>Timer : return minority_fork else not a minority fork -alt witness reward creation +alt validator reward creation Timer->>DB : get_witness(current_witness) -Timer->>DB : find_account(witness.owner) +Timer->>DB : find_account(validator.owner) alt account exists Timer->>DB : create_vesting(account, reward) Timer->>DB : push_virtual_operation(witness_reward) @@ -285,18 +285,18 @@ else no slot Timer->>Timer : wait until next 250ms tick end Timer->>Timer : reschedule for next 250ms tick -Note over Snapshot : Check if witness scheduled soon
to coordinate operations +Note over Snapshot : Check if validator scheduled soon
to coordinate operations Snapshot->>Timer : is_witness_scheduled_soon() Timer-->>Snapshot : true/false ``` **Diagram sources** -- [witness.cpp:206-276](file://plugins/witness/witness.cpp#L206-L276) -- [witness.cpp:278-423](file://plugins/witness/witness.cpp#L278-L423) -- [witness.cpp:447-471](file://plugins/witness/witness.cpp#L447-L471) -- [witness.cpp:590-695](file://plugins/witness/witness.cpp#L590-L695) -- [witness.cpp:263-266](file://plugins/witness/witness.cpp#L263-L266) -- [witness.cpp:206-249](file://plugins/witness/witness.cpp#L206-L249) +- [validator.cpp:206-276](file://plugins/validator/validator.cpp#L206-L276) +- [validator.cpp:278-423](file://plugins/validator/validator.cpp#L278-L423) +- [validator.cpp:447-471](file://plugins/validator/validator.cpp#L447-L471) +- [validator.cpp:590-695](file://plugins/validator/validator.cpp#L590-L695) +- [validator.cpp:263-266](file://plugins/validator/validator.cpp#L263-L266) +- [validator.cpp:206-249](file://plugins/validator/validator.cpp#L206-L249) - [witness_guard.cpp:83-191](file://plugins/witness_guard/witness_guard.cpp#L83-L191) - [witness_guard.cpp:455-544](file://plugins/witness_guard/witness_guard.cpp#L455-L544) - [database.cpp:4317-4332](file://libraries/chain/database.cpp#L4317-L4332) @@ -311,7 +311,7 @@ Timer-->>Snapshot : true/false ### Parameter Types and Scaling -The witness plugin configuration parameters have been updated with improved type safety and scaling: +The Validator Plugin configuration parameters have been updated with improved type safety and scaling: - **enable-stale-production**: Boolean parameter controlling whether block production continues when the chain is stale - Type: `bool` (previously `int`) @@ -320,7 +320,7 @@ The witness plugin configuration parameters have been updated with improved type - Command line: `--enable-stale-production` - Config file: `enable-stale-production` -- **required-participation**: Integer parameter specifying minimum witness participation percentage +- **required-participation**: Integer parameter specifying minimum validator participation percentage - Type: `uint32_t` (changed from `int`) - Scale: Multiplied by `CHAIN_1_PERCENT` (100 units = 1%) - Range: 0-99% (0-9900 units) @@ -330,7 +330,7 @@ The witness plugin configuration parameters have been updated with improved type - **fork-collision-timeout-blocks**: New parameter controlling fork collision timeout behavior - Type: `uint32_t` - - Default: 21 blocks (one full witness round, 63 seconds) + - Default: 21 blocks (one full validator round, 63 seconds) - Purpose: Number of consecutive fork-collision deferrals before forcing production - Command line: `--fork-collision-timeout-blocks` - Config file: `fork-collision-timeout-blocks` @@ -338,41 +338,41 @@ The witness plugin configuration parameters have been updated with improved type - **debug-block-production**: **NEW** Boolean parameter enabling verbose debug logging for block production and chain internals - Type: `bool` - Default: `false` - - Purpose: Enables comprehensive logging with detailed traces of witness participation checks, emergency mode enforcement, block post-validation processes, and minority fork detection + - Purpose: Enables comprehensive logging with detailed traces of validator participation checks, emergency mode enforcement, block post-validation processes, and minority fork detection - Command line: `--debug-block-production` - Config file: `debug-block-production` -### Witness Guard Plugin Configuration +### validator Guard Plugin Configuration -**NEW** The witness guard plugin introduces several new configuration parameters: +**NEW** The validator guard plugin introduces several new configuration parameters: -- **witness-guard-enabled**: Boolean parameter enabling/disabling the witness protection and monitoring system +- **validator-guard-enabled**: Boolean parameter enabling/disabling the validator protection and monitoring system - Type: `bool` - Default: `true` - - Purpose: Controls whether the witness guard plugin is active - - Command line: `--witness-guard-enabled` - - Config file: `witness-guard-enabled` + - Purpose: Controls whether the validator guard plugin is active + - Command line: `--validator-guard-enabled` + - Config file: `validator-guard-enabled` -- **witness-guard-disable**: Integer parameter controlling consecutive block auto-disable threshold +- **validator-guard-disable**: Integer parameter controlling consecutive block auto-disable threshold - Type: `uint32_t` - Default: 5 blocks - - Purpose: Number of consecutive blocks produced by the same witness before automatic disabling - - Command line: `--witness-guard-disable` - - Config file: `witness-guard-disable` + - Purpose: Number of consecutive blocks produced by the same validator before automatic disabling + - Command line: `--validator-guard-disable` + - Config file: `validator-guard-disable` -- **witness-guard-interval**: Integer parameter controlling check frequency +- **validator-guard-interval**: Integer parameter controlling check frequency - Type: `uint32_t` - Default: 20 blocks (approximately 60 seconds) - - Purpose: How often to check witness signing keys in block intervals - - Command line: `--witness-guard-interval` - - Config file: `witness-guard-interval` + - Purpose: How often to check validator signing keys in block intervals + - Command line: `--validator-guard-interval` + - Config file: `validator-guard-interval` -- **witness-guard-witness**: Array parameter defining witnesses to monitor +- **validator-guard-validator**: Array parameter defining validators to monitor - Type: `std::vector` - Format: JSON triplet `["name", "signing_wif", "active_wif"]` - - Purpose: Specifies which witnesses to monitor and their key pairs - - Command line: `--witness-guard-witness` - - Config file: `witness-guard-witness` + - Purpose: Specifies which validators to monitor and their key pairs + - Command line: `--validator-guard-validator` + - Config file: `validator-guard-validator` ### Configuration Defaults @@ -380,12 +380,12 @@ The witness plugin configuration parameters have been updated with improved type - **enable-stale-production**: Now defaults to `false` to improve network stability and prevent minority fork propagation - **required-participation**: Defaults to 33% participation threshold for balanced security/performance -- **fork-collision-timeout-blocks**: Defaults to 21 blocks to match one full witness schedule round +- **fork-collision-timeout-blocks**: Defaults to 21 blocks to match one full validator schedule round - **debug-block-production**: Defaults to `false` to maintain production performance while providing debugging capability when needed -- **witness-guard-enabled**: Defaults to `true` to provide comprehensive witness protection by default -- **witness-guard-disable**: Defaults to 5 consecutive blocks to prevent witness abuse while allowing normal operation -- **witness-guard-interval**: Defaults to 20 blocks for balanced monitoring frequency -- **witness-guard-witness**: Defaults to empty (no witnesses monitored) requiring explicit configuration +- **validator-guard-enabled**: Defaults to `true` to provide comprehensive validator protection by default +- **validator-guard-disable**: Defaults to 5 consecutive blocks to prevent validator abuse while allowing normal operation +- **validator-guard-interval**: Defaults to 20 blocks for balanced monitoring frequency +- **validator-guard-validator**: Defaults to empty (no validators monitored) requiring explicit configuration ### Configuration Processing @@ -399,10 +399,10 @@ TypeCheck --> |enable-stale-production| BoolConvert["Convert to bool
Default TypeCheck --> |required-participation| IntConvert["Convert to uint32_t
Scale by CHAIN_1_PERCENT"] TypeCheck --> |fork-collision-timeout-blocks| TimeoutConvert["Convert to uint32_t
Default: 21 blocks"] TypeCheck --> |debug-block-production| DebugConvert["Convert to bool
Default: false"] -TypeCheck --> |witness-guard-enabled| GuardEnabled["Convert to bool
Default: true"] -TypeCheck --> |witness-guard-disable| DisableConvert["Convert to uint32_t
Default: 5"] -TypeCheck --> |witness-guard-interval| IntervalConvert["Convert to uint32_t
Default: 20"] -TypeCheck --> |witness-guard-witness| WitnessArray["Parse JSON triplets
Format: [name, signing_wif, active_wif]"] +TypeCheck --> |validator-guard-enabled| GuardEnabled["Convert to bool
Default: true"] +TypeCheck --> |validator-guard-disable| DisableConvert["Convert to uint32_t
Default: 5"] +TypeCheck --> |validator-guard-interval| IntervalConvert["Convert to uint32_t
Default: 20"] +TypeCheck --> |validator-guard-validator| WitnessArray["Parse JSON triplets
Format: [name, signing_wif, active_wif]"] BoolConvert --> Storage["Store in plugin state"] IntConvert --> Storage TimeoutConvert --> Storage @@ -415,19 +415,19 @@ Storage --> Runtime["Runtime Usage"] ``` **Diagram sources** -- [witness.cpp:125-133](file://plugins/witness/witness.cpp#L125-L133) -- [witness.cpp:149-155](file://plugins/witness/witness.cpp#L149-L155) -- [witness.cpp:222-224](file://plugins/witness/witness.cpp#L222-L224) -- [witness.cpp:228-233](file://plugins/witness/witness.cpp#L228-L233) +- [validator.cpp:125-133](file://plugins/validator/validator.cpp#L125-L133) +- [validator.cpp:149-155](file://plugins/validator/validator.cpp#L149-L155) +- [validator.cpp:222-224](file://plugins/validator/validator.cpp#L222-L224) +- [validator.cpp:228-233](file://plugins/validator/validator.cpp#L228-L233) - [witness_guard.cpp:301-328](file://plugins/witness_guard/witness_guard.cpp#L301-L328) - [witness_guard.cpp:330-408](file://plugins/witness_guard/witness_guard.cpp#L330-L408) - [config.hpp:57-58](file://libraries/protocol/include/graphene/protocol/config.hpp#L57-L58) **Section sources** -- [witness.cpp:125-133](file://plugins/witness/witness.cpp#L125-L133) -- [witness.cpp:149-155](file://plugins/witness/witness.cpp#L149-L155) -- [witness.cpp:222-224](file://plugins/witness/witness.cpp#L222-L224) -- [witness.cpp:228-233](file://plugins/witness/witness.cpp#L228-L233) +- [validator.cpp:125-133](file://plugins/validator/validator.cpp#L125-L133) +- [validator.cpp:149-155](file://plugins/validator/validator.cpp#L149-L155) +- [validator.cpp:222-224](file://plugins/validator/validator.cpp#L222-L224) +- [validator.cpp:228-233](file://plugins/validator/validator.cpp#L228-L233) - [witness_guard.cpp:301-328](file://plugins/witness_guard/witness_guard.cpp#L301-L328) - [witness_guard.cpp:330-408](file://plugins/witness_guard/witness_guard.cpp#L330-L408) - [config.hpp:57-58](file://libraries/protocol/include/graphene/protocol/config.hpp#L57-L58) @@ -437,15 +437,15 @@ Storage --> Runtime["Runtime Usage"] ## Detailed Component Analysis -### Witness Plugin +### Validator Plugin Responsibilities: -- Parse configuration for witness names and private keys. +- Parse configuration for validator names and private keys. - Initialize NTP time synchronization with 250ms interval optimization. - Run a production loop that: - Waits until synchronized to the next 250ms boundary for deterministic slot alignment. - Checks participation thresholds and scheduling eligibility. - **Enhanced**: Performs comprehensive fork collision detection before block generation. - - **Enhanced**: **NEW**: Implements comprehensive minority fork detection to identify when all recent blocks were produced by local witnesses only. + - **Enhanced**: **NEW**: Implements comprehensive minority fork detection to identify when all recent blocks were produced by local validators only. - **Enhanced**: **NEW**: Provides automatic recovery through resync_from_lib() when minority fork is detected. - **Enhanced**: **NEW**: Integrates with skip_undo_history_check flag for controlled production during recovery scenarios. - **Enhanced**: **NEW**: Implements comprehensive debug logging with verbose traces for block production and chain internals. @@ -456,22 +456,22 @@ Responsibilities: - **New**: Provides `is_witness_scheduled_soon()` method for external coordination. Key behaviors: -- Participation threshold enforcement via witness participation rate. +- Participation threshold enforcement via validator participation rate. - Graceful handling of missing private keys, low participation, and timing lags. - Optional allowance for stale production during initial sync. - **Enhanced**: Automatic NTP synchronization on lag detection and fork collision to prevent timing-related production failures. - **Enhanced**: Comprehensive fork collision detection prevents competing blocks at the same height. -- **Enhanced**: **NEW**: Minority fork detection identifies when all recent blocks were produced by local witnesses only. +- **Enhanced**: **NEW**: Minority fork detection identifies when all recent blocks were produced by local validators only. - **Enhanced**: **NEW**: Automatic recovery through P2P resynchronization when minority fork is detected. - **Enhanced**: **NEW**: Controlled production during recovery scenarios using skip_undo_history_check flag. - **Enhanced**: **NEW**: Comprehensive debug logging system with verbose traces for detailed visibility into block production pipeline. -- **New**: Efficient slot checking across 4 upcoming slots to detect witness scheduling conflicts. +- **New**: Efficient slot checking across 4 upcoming slots to detect validator scheduling conflicts. - **New**: Two-level fork collision resolution with vote-weighted comparison and stuck-head timeout mechanism. - **New**: Configurable fork collision timeout blocks parameter for fine-tuning fork resolution behavior. ```mermaid flowchart TD -Start(["Startup"]) --> InitKeys["Load witness names and private keys"] +Start(["Startup"]) --> InitKeys["Load validator names and private keys"] InitKeys --> InitNTP["Initialize NTP time service with 250ms ticks"] InitNTP --> InitTimeout["Initialize fork-collision-timeout-blocks (21)"] InitTimeout --> InitSkipFlag["Initialize skip_undo_history_check (false)"] @@ -479,7 +479,7 @@ InitSkipFlag --> InitDebug["Initialize debug-block-production (false)"] InitDebug --> SyncCheck["Wait until synchronized to 250ms boundary"] SyncCheck --> SlotCheck{"Slot available?"} SlotCheck --> |No| WaitNext["Sleep until next 250ms tick"] --> SyncCheck -SlotCheck --> |Yes| Scheduled["Get scheduled witness and slot time"] +SlotCheck --> |Yes| Scheduled["Get scheduled validator and slot time"] Scheduled --> Participation{"Participation >= threshold?"} Participation --> |No| LogLowParticipation["Log low participation"] --> Resched["Reschedule"] --> SyncCheck Participation --> |Yes| TimeCheck{"Within 500ms window?"} @@ -501,38 +501,38 @@ StaleProdCheck --> |Yes| ContinueProd["Continue production"] --> SignCheck StaleProdCheck --> |No| Resync["P2P resync_from_lib()"] --> DisableProd["Disable production"] --> ReturnMinority["Return minority_fork"] MinorityFork --> |Not a minority fork| SignCheck{"Private key available?"} SignCheck --> |No| LogNoKey["Log missing key"] --> Resched -SignCheck --> |Yes| RewardValidation{"Validate witness account"} +SignCheck --> |Yes| RewardValidation{"Validate validator account"} RewardValidation --> |Account exists| Produce["Generate block and broadcast"] RewardValidation --> |Account missing| CriticalError["Log critical error
Request node restart"] Produce --> Resched ``` **Diagram sources** -- [witness.cpp:206-276](file://plugins/witness/witness.cpp#L206-L276) -- [witness.cpp:278-423](file://plugins/witness/witness.cpp#L278-L423) -- [witness.cpp:447-471](file://plugins/witness/witness.cpp#L447-L471) -- [witness.cpp:590-695](file://plugins/witness/witness.cpp#L590-L695) -- [witness.cpp:263-266](file://plugins/witness/witness.cpp#L263-L266) -- [witness.cpp:509-555](file://plugins/witness/witness.cpp#L509-L555) +- [validator.cpp:206-276](file://plugins/validator/validator.cpp#L206-L276) +- [validator.cpp:278-423](file://plugins/validator/validator.cpp#L278-L423) +- [validator.cpp:447-471](file://plugins/validator/validator.cpp#L447-L471) +- [validator.cpp:590-695](file://plugins/validator/validator.cpp#L590-L695) +- [validator.cpp:263-266](file://plugins/validator/validator.cpp#L263-L266) +- [validator.cpp:509-555](file://plugins/validator/validator.cpp#L509-L555) - [p2p_plugin.hpp:50-55](file://plugins/p2p/include/graphene/plugins/p2p/p2p_plugin.hpp#L50-L55) **Section sources** -- [witness.hpp:34-68](file://plugins/witness/include/graphene/plugins/witness/witness.hpp#L34-L68) -- [witness.cpp:120-169](file://plugins/witness/witness.cpp#L120-L169) -- [witness.cpp:171-192](file://plugins/witness/witness.cpp#L171-L192) -- [witness.cpp:206-276](file://plugins/witness/witness.cpp#L206-L276) -- [witness.cpp:278-423](file://plugins/witness/witness.cpp#L278-L423) -- [witness.cpp:447-471](file://plugins/witness/witness.cpp#L447-L471) -- [witness.cpp:590-695](file://plugins/witness/witness.cpp#L590-L695) -- [witness.cpp:206-249](file://plugins/witness/witness.cpp#L206-L249) -- [witness.cpp:509-555](file://plugins/witness/witness.cpp#L509-L555) +- [validator.hpp:34-68](file://plugins/validator/include/graphene/plugins/validator/validator.hpp#L34-L68) +- [validator.cpp:120-169](file://plugins/validator/validator.cpp#L120-L169) +- [validator.cpp:171-192](file://plugins/validator/validator.cpp#L171-L192) +- [validator.cpp:206-276](file://plugins/validator/validator.cpp#L206-L276) +- [validator.cpp:278-423](file://plugins/validator/validator.cpp#L278-L423) +- [validator.cpp:447-471](file://plugins/validator/validator.cpp#L447-L471) +- [validator.cpp:590-695](file://plugins/validator/validator.cpp#L590-L695) +- [validator.cpp:206-249](file://plugins/validator/validator.cpp#L206-L249) +- [validator.cpp:509-555](file://plugins/validator/validator.cpp#L509-L555) ### New: Enhanced Minoriy Fork Detection System -The witness plugin now implements a comprehensive minority fork detection system to identify when all recent blocks were produced by local witnesses only, indicating a potential minority fork scenario. +The Validator Plugin now implements a comprehensive minority fork detection system to identify when all recent blocks were produced by local validators only, indicating a potential minority fork scenario. **Detection Logic**: - Checks the last CHAIN_MAX_WITNESSES (21) blocks in the fork database -- Verifies that all blocks were produced by configured local witnesses +- Verifies that all blocks were produced by configured local validators - Skips detection during emergency consensus mode to prevent false positives - Integrates with skip_undo_history_check flag for controlled production during recovery @@ -544,23 +544,23 @@ The witness plugin now implements a comprehensive minority fork detection system **Implementation Details**: - Uses `db.get_fork_db().head()` to access the fork database head -- Iterates backwards through CHAIN_MAX_WITNESSES blocks to verify witness ownership -- Checks `_witnesses.find(current->data.witness) == _witnesses.end()` to detect foreign witnesses +- Iterates backwards through CHAIN_MAX_WITNESSES blocks to verify validator ownership +- Checks `_witnesses.find(current->data.validator) == _witnesses.end()` to detect foreign validators - Calls `p2p().resync_from_lib()` for automatic recovery - Returns `block_production_condition::minority_fork` to signal recovery state - Integrates with emergency consensus detection via `dgp.emergency_consensus_active` **Section sources** -- [witness.cpp:509-555](file://plugins/witness/witness.cpp#L509-L555) -- [witness.hpp:31](file://plugins/witness/include/graphene/plugins/witness/witness.hpp#L31) +- [validator.cpp:509-555](file://plugins/validator/validator.cpp#L509-L555) +- [validator.hpp:31](file://plugins/validator/include/graphene/plugins/validator/validator.hpp#L31) - [database.hpp:88](file://libraries/chain/include/graphene/chain/database.hpp#L88) ### New: Enhanced Fork Collision Detection System -The witness plugin now implements a comprehensive fork collision detection system to prevent competing blocks at the same height. +The Validator Plugin now implements a comprehensive fork collision detection system to prevent competing blocks at the same height. **Detection Logic**: - Queries the fork database for all blocks at the next height level (head_block_num + 1) -- Identifies competing blocks produced by different witnesses with different parent blocks +- Identifies competing blocks produced by different validators with different parent blocks - Prevents block production when fork collision is detected - Automatically triggers NTP synchronization to resolve timing issues @@ -576,14 +576,14 @@ The witness plugin now implements a comprehensive fork collision detection syste **Implementation Details**: - Uses `db.get_fork_db().fetch_block_by_number(db.head_block_num() + 1)` to query all blocks at the target height -- Analyzes each existing block to determine if it was produced by a different witness with a different parent -- Captures detailed information including height and scheduled witness for logging +- Analyzes each existing block to determine if it was produced by a different validator with a different parent +- Captures detailed information including height and scheduled validator for logging - Returns `block_production_condition::fork_collision` to defer production - Integrates with compare_fork_branches() function for intelligent fork switching decisions **Section sources** -- [witness.cpp:447-471](file://plugins/witness/witness.cpp#L447-L471) -- [witness.cpp:590-695](file://plugins/witness/witness.cpp#L590-L695) +- [validator.cpp:447-471](file://plugins/validator/validator.cpp#L447-L471) +- [validator.cpp:590-695](file://plugins/validator/validator.cpp#L590-L695) - [fork_database.hpp:73](file://libraries/chain/include/graphene/chain/fork_database.hpp#L73) - [fork_database.cpp:151-166](file://libraries/chain/fork_database.cpp#L151-166) - [database.cpp:1223-1267](file://libraries/chain/database.cpp#L1223-L1267) @@ -618,21 +618,21 @@ The fork database now includes automatic stale fork pruning capabilities to main The database now includes a sophisticated compare_fork_branches() function that intelligently evaluates fork weight and chain length for decision-making. **Function Capabilities**: -- Calculates total vote weight for each fork branch using witness objects -- Excludes emergency witness account from weight calculations +- Calculates total vote weight for each fork branch using validator objects +- Excludes emergency validator account from weight calculations - Applies +10% bonus to longer chain to encourage network support - Handles tied scenarios and comparison impossibility gracefully - Returns 1 (branch_a heavier), -1 (branch_b heavier), or 0 (tied/impossible) **Weight Calculation Algorithm**: -- Iterates through branch items to accumulate witness vote weights -- Uses flat_set to avoid counting the same witness multiple times -- Skips emergency witness account for fairness -- Handles exceptions during witness lookup gracefully +- Iterates through branch items to accumulate validator vote weights +- Uses flat_set to avoid counting the same validator multiple times +- Skips emergency validator account for fairness +- Handles exceptions during validator lookup gracefully - Returns 0 when comparison cannot be performed **Integration with Fork Resolution**: -- Used by witness plugin for vote-weighted fork comparisons +- Used by Validator Plugin for vote-weighted fork comparisons - Supports HF12+ consensus rules with longer-chain bonus - Provides fallback for pre-HF12 longest-chain scenarios - Enables intelligent fork switching decisions @@ -645,41 +645,41 @@ The `is_witness_scheduled_soon()` method provides a crucial coordination mechani **Method Signature**: `bool is_witness_scheduled_soon() const` -**Purpose**: Checks if any locally-controlled witnesses are scheduled to produce blocks in the upcoming 4 slots, enabling other plugins to coordinate and avoid conflicts during critical operations like snapshot creation. +**Purpose**: Checks if any locally-controlled validators are scheduled to produce blocks in the upcoming 4 slots, enabling other plugins to coordinate and avoid conflicts during critical operations like snapshot creation. **Implementation Details**: -- Validates that the witness plugin has been initialized with witnesses and private keys +- Validates that the Validator Plugin has been initialized with validators and private keys - Calculates the current slot based on synchronized time plus 250ms buffer for deterministic alignment -- Iterates through slots 0-3 positions ahead to check for scheduled witnesses -- Verifies that the scheduled witness belongs to the locally-controlled set -- Confirms the witness has a valid signing key (not disabled) +- Iterates through slots 0-3 positions ahead to check for scheduled validators +- Verifies that the scheduled validator belongs to the locally-controlled set +- Confirms the validator has a valid signing key (not disabled) - Ensures the plugin has the corresponding private key for block signing -**Usage Pattern**: Other plugins can use this method to defer operations when witness production is imminent, particularly useful for snapshot creation which requires exclusive access to the blockchain state. +**Usage Pattern**: Other plugins can use this method to defer operations when validator production is imminent, particularly useful for snapshot creation which requires exclusive access to the blockchain state. **Section sources** -- [witness.cpp:206-249](file://plugins/witness/witness.cpp#L206-L249) +- [validator.cpp:206-249](file://plugins/validator/validator.cpp#L206-L249) -### Enhanced: Witness Reward Creation Process -The witness reward creation process has been significantly enhanced with comprehensive error handling and validation to prevent crashes when witness account objects are missing from the database. +### Enhanced: validator Reward Creation Process +The validator reward creation process has been significantly enhanced with comprehensive error handling and validation to prevent crashes when validator account objects are missing from the database. **Enhanced Reward Creation Logic**: -- **Pre-validation**: Uses `find_account()` to check if the witness account exists before attempting reward creation +- **Pre-validation**: Uses `find_account()` to check if the validator account exists before attempting reward creation - **Crash Prevention**: Implements comprehensive validation to prevent crashes from shared memory corruption - **Clear Recovery Guidance**: Provides explicit instructions for recovery procedures when accounts are missing - **Multi-hardfork Support**: Applies validation across all hardfork versions (HF4, HF11, and legacy models) **Implementation Details**: -- **HF11 Model**: Validates witness account before creating vesting rewards for new emission model +- **HF11 Model**: Validates validator account before creating vesting rewards for new emission model - **HF4 Model**: Comprehensive validation for consensus inflation model with detailed error logging - **Legacy Model**: Falls back to `get_account()` with clear error messaging for older models -- **Critical Error Handling**: Logs detailed witness information (signing key, missed blocks, penalties) for debugging +- **Critical Error Handling**: Logs detailed validator information (signing key, missed blocks, penalties) for debugging **Error Handling Features**: -- Detailed logging with witness metadata (signing key, missed blocks, penalties, last confirmed block) +- Detailed logging with validator metadata (signing key, missed blocks, penalties, last confirmed block) - Clear FC_ASSERT messages directing users to restart with replay - Account index size reporting for diagnostic purposes -- Prevention of crashes during witness reward distribution +- Prevention of crashes during validator reward distribution ```mermaid flowchart TD @@ -712,7 +712,7 @@ LegacyGetAccount --> LegacyCreateVesting["create_vesting(account, reward)"] - [database.cpp:1294-1311](file://libraries/chain/database.cpp#L1294-L1311) ### New: Comprehensive Debug Logging System -The witness plugin now implements a comprehensive debug logging system that provides verbose traces for block production and chain internals with detailed visibility into the entire block production pipeline. +The Validator Plugin now implements a comprehensive debug logging system that provides verbose traces for block production and chain internals with detailed visibility into the entire block production pipeline. **Debug Logging Features**: - **Block Production Loop**: Comprehensive logging of block_production_loop() entry and exit points @@ -721,19 +721,19 @@ The witness plugin now implements a comprehensive debug logging system that prov - **Emergency Mode**: Enhanced logging for emergency consensus mode enforcement - **Minority Fork Detection**: Granular visibility into minority fork detection process - **Fork Collision Resolution**: Detailed traces of fork collision detection and resolution -- **Contextual Information**: Rich contextual data including timestamps, block numbers, and witness information +- **Contextual Information**: Rich contextual data including timestamps, block numbers, and validator information **Logging Implementation**: - Uses `database()._debug_block_production` flag to control debug logging - Implements comprehensive `ilog()` statements throughout the block production pipeline -- Provides detailed traces for witness participation checks, emergency mode enforcement, block post-validation processes, and minority fork detection +- Provides detailed traces for validator participation checks, emergency mode enforcement, block post-validation processes, and minority fork detection - Includes timestamps and contextual information for better debugging and troubleshooting - Supports granular visibility into all aspects of block production with minimal performance impact **Section sources** -- [witness.cpp:228-233](file://plugins/witness/witness.cpp#L228-L233) -- [witness.cpp:338-407](file://plugins/witness/witness.cpp#L338-L407) -- [witness.cpp:411-419](file://plugins/witness/witness.cpp#L411-L419) +- [validator.cpp:228-233](file://plugins/validator/validator.cpp#L228-L233) +- [validator.cpp:338-407](file://plugins/validator/validator.cpp#L338-L407) +- [validator.cpp:411-419](file://plugins/validator/validator.cpp#L411-L419) - [database.hpp:60](file://libraries/chain/include/graphene/chain/database.hpp#L60) - [database.cpp:1890-1892](file://libraries/chain/database.cpp#L1890-L1892) - [database.cpp:4536-4573](file://libraries/chain/database.cpp#L4536-L4573) @@ -752,10 +752,10 @@ The production timer runs on its own `production_io_service_` with a dedicated O **Benefit**: The 250ms tick fires on its own OS thread with no contention from P2P fiber scheduling. Even under heavy peer churn the production callback is called at the correct wall-clock time. **Section sources** -- [witness.cpp:64-83](file://plugins/witness/witness.cpp#L64-L83) -- [witness.cpp:139-144](file://plugins/witness/witness.cpp#L139-L144) -- [witness.cpp:374-378](file://plugins/witness/witness.cpp#L374-L378) -- [witness.cpp:663-685](file://plugins/witness/witness.cpp#L663-L685) +- [validator.cpp:64-83](file://plugins/validator/validator.cpp#L64-L83) +- [validator.cpp:139-144](file://plugins/validator/validator.cpp#L139-L144) +- [validator.cpp:374-378](file://plugins/validator/validator.cpp#L374-L378) +- [validator.cpp:663-685](file://plugins/validator/validator.cpp#L663-L685) ### New: Lag Tight Loop Prevention After a `lag` production condition the current slot is already missed. Without a guard the next 250ms tick re-evaluates the same slot and returns `lag` again, spinning in a tight loop until the full 3s slot interval passes. @@ -765,21 +765,21 @@ After a `lag` production condition the current slot is already missed. Without a **State variable**: `fc::time_point_sec _last_lag_slot_time` (zero when no lag is active). **Section sources** -- [witness.cpp:182-186](file://plugins/witness/witness.cpp#L182-L186) -- [witness.cpp:911-920](file://plugins/witness/witness.cpp#L911-L920) -- [witness.cpp:945-963](file://plugins/witness/witness.cpp#L945-L963) +- [validator.cpp:182-186](file://plugins/validator/validator.cpp#L182-L186) +- [validator.cpp:911-920](file://plugins/validator/validator.cpp#L911-L920) +- [validator.cpp:945-963](file://plugins/validator/validator.cpp#L945-L963) ### New: Production Watchdog The watchdog fires when the node has produced at least one block (`_ever_produced = true`) but has gone silent while production conditions are met. This catches cases where an external factor (e.g., the emergency master blanking our key) silently stops production without returning an explicit error. **Thresholds**: - Emergency master (has `CHAIN_EMERGENCY_WITNESS_ACCOUNT` in `_witnesses`): **60 seconds**. -- Regular witness: **180 seconds**. +- Regular validator: **180 seconds**. - Re-fires every **30 seconds** after the first alert. **Actions on first fire**: 1. Sets `_watchdog_debug_enabled = true` and enables `database()._debug_block_production` to capture verbose production loop traces automatically. -2. Logs full diagnostic state: NTP drift, head block, DLT sync status, P2P catchup flag, scheduled witness, how many of our witnesses appear in the shuffled schedule, and which witnesses have blanked on-chain keys. +2. Logs full diagnostic state: NTP drift, head block, DLT sync status, P2P catchup flag, scheduled validator, how many of our validators appear in the shuffled schedule, and which validators have blanked on-chain keys. **Relevant state**: - `bool _ever_produced` — set to true on first successful production. @@ -788,18 +788,18 @@ The watchdog fires when the node has produced at least one block (`_ever_produce - `bool _watchdog_debug_enabled` — latching flag; never reset once set. **Section sources** -- [witness.cpp:174-191](file://plugins/witness/witness.cpp#L174-L191) -- [witness.cpp:965-1065](file://plugins/witness/witness.cpp#L965-L1065) +- [validator.cpp:174-191](file://plugins/validator/validator.cpp#L174-L191) +- [validator.cpp:965-1065](file://plugins/validator/validator.cpp#L965-L1065) ### New: Slot Hijack Detection -In DLT emergency consensus mode the emergency master may blank a regular witness's signing key and produce `committee` blocks in that witness's scheduled slots. The hijack counter makes this pattern visible in watchdog diagnostics. +In DLT emergency consensus mode the emergency master may blank a regular validator's signing key and produce `committee` blocks in that validator's scheduled slots. The hijack counter makes this pattern visible in watchdog diagnostics. **Detection logic** (runs inside `on_block_applied`): 1. Skip if emergency consensus is not active. 2. Compute `slot_idx = dgp.current_aslot % num_scheduled_witnesses`. -3. Look up the expected witness at `slot_idx` in the shuffled schedule. -4. If the actual block producer (`block.witness`) is `committee` (or any non-local witness) AND `slot_idx` maps to one of our witnesses → hijack detected. -5. If the actual producer IS one of our witnesses (any of them) → reset counter (false-positive guard). +3. Look up the expected validator at `slot_idx` in the shuffled schedule. +4. If the actual block producer (`block.validator`) is `committee` (or any non-local validator) AND `slot_idx` maps to one of our validators → hijack detected. +5. If the actual producer IS one of our validators (any of them) → reset counter (false-positive guard). **State variables**: - `uint32_t _slot_hijack_count` — consecutive hijacked slots since last own production. @@ -808,26 +808,26 @@ In DLT emergency consensus mode the emergency master may blank a regular witness **Logging**: First 3 hijacks are always logged; thereafter once per minute to avoid log spam. **Section sources** -- [witness.cpp:192-207](file://plugins/witness/witness.cpp#L192-L207) -- [witness.cpp:486-562](file://plugins/witness/witness.cpp#L486-L562) +- [validator.cpp:192-207](file://plugins/validator/validator.cpp#L192-L207) +- [validator.cpp:486-562](file://plugins/validator/validator.cpp#L486-L562) ### New: Missed Block Diagnostic via on_block_applied Signal -The witness plugin subscribes to the chain's `applied_block` signal via `on_block_applied()`. When an incoming block reveals that one or more slots were skipped (block number jumped by more than 1 since the last applied block), and any of the missed slots were assigned to one of our witnesses, the handler dumps the full plugin state for diagnosis. +The Validator Plugin subscribes to the chain's `applied_block` signal via `on_block_applied()`. When an incoming block reveals that one or more slots were skipped (block number jumped by more than 1 since the last applied block), and any of the missed slots were assigned to one of our validators, the handler dumps the full plugin state for diagnosis. **Triggered by**: gaps between `_last_applied_block_num` and the new block number. **State variable**: `uint64_t _last_applied_block_num` — updated on every applied block. **Section sources** -- [witness.cpp:200-207](file://plugins/witness/witness.cpp#L200-L207) -- [witness.cpp:470-562](file://plugins/witness/witness.cpp#L470-L562) +- [validator.cpp:200-207](file://plugins/validator/validator.cpp#L200-L207) +- [validator.cpp:470-562](file://plugins/validator/validator.cpp#L470-L562) ### New: not_my_turn Streak Detection -When the production loop repeatedly returns `not_my_turn` (another witness is scheduled) for an extended period while our witnesses are supposed to be in the schedule, it may indicate schedule misalignment, a forked-off chain, or a configuration error. +When the production loop repeatedly returns `not_my_turn` (another validator is scheduled) for an extended period while our validators are supposed to be in the schedule, it may indicate schedule misalignment, a forked-off chain, or a configuration error. -**Threshold**: **500 consecutive** `not_my_turn` results ≈ 125 seconds of other witnesses producing uninterrupted. +**Threshold**: **500 consecutive** `not_my_turn` results ≈ 125 seconds of other validators producing uninterrupted. -**On threshold**: Logs a warning with streak count, elapsed time, last scheduled witness name, and our configured witness set. +**On threshold**: Logs a warning with streak count, elapsed time, last scheduled validator name, and our configured validator set. **Reset**: On any `produced`, `not_synced`, or other non-`not_my_turn` result. @@ -837,38 +837,38 @@ When the production loop repeatedly returns `not_my_turn` (another witness is sc - `std::string _last_scheduled_witness` **Section sources** -- [witness.cpp:170-173](file://plugins/witness/witness.cpp#L170-L173) -- [witness.cpp:754-780](file://plugins/witness/witness.cpp#L754-L780) +- [validator.cpp:170-173](file://plugins/validator/validator.cpp#L170-L173) +- [validator.cpp:754-780](file://plugins/validator/validator.cpp#L754-L780) -### New: Witness Guard Plugin - Comprehensive Protection and Monitoring -The witness guard plugin provides comprehensive protection and monitoring capabilities for witness keys and operations. +### New: validator Guard Plugin - Comprehensive Protection and Monitoring +The validator guard plugin provides comprehensive protection and monitoring capabilities for validator keys and operations. **Core Responsibilities**: - **Auto-Key Restoration**: Automatically detects and restores null signing keys on-chain -- **Consecutive Block Protection**: Monitors witness block production and auto-disables witnesses after N consecutive blocks +- **Consecutive Block Protection**: Monitors validator block production and auto-disables validators after N consecutive blocks - **Emergency Consensus Support**: Continues monitoring and protection during emergency consensus mode - **Network Health Monitoring**: Ensures node synchronization and network health before performing actions - **Safety Checks**: Implements comprehensive safety checks to prevent malicious actions **Auto-Restore Mechanism**: -- Periodically checks configured witnesses for null signing keys +- Periodically checks configured validators for null signing keys - Broadcasts witness_update transactions to restore keys using stored key pairs - Tracks pending transactions and confirms successful restoration - Implements retry logic for failed restoration attempts - Prevents unbounded growth of pending confirmation tracking **Auto-Disable Protection**: -- Monitors consecutive block production by configured witnesses -- Auto-disables witnesses that exceed the configured threshold -- Prevents witness abuse and ensures fair network participation -- Maintains records of auto-disabled witnesses to prevent auto-restore +- Monitors consecutive block production by configured validators +- Auto-disables validators that exceed the configured threshold +- Prevents validator abuse and ensures fair network participation +- Maintains records of auto-disabled validators to prevent auto-restore - Broadcasts witness_update transactions with null signing key to disable production **Emergency Consensus Integration**: - Continues monitoring during emergency consensus mode - Adapts behavior based on emergency_consensus_active flag - Supports key restoration even when network is unstable -- Coordinates with witness plugin for seamless operation +- Coordinates with Validator Plugin for seamless operation **Safety and Validation**: - Verifies on-chain authority for configured active keys @@ -878,10 +878,10 @@ The witness guard plugin provides comprehensive protection and monitoring capabi - Provides comprehensive logging for all operations **Configuration Options**: -- **witness-guard-enabled**: Enable/disable the protection system -- **witness-guard-disable**: Set consecutive block threshold for auto-disable -- **witness-guard-interval**: Configure check frequency in blocks -- **witness-guard-witness**: Define witnesses to monitor with key pairs +- **validator-guard-enabled**: Enable/disable the protection system +- **validator-guard-disable**: Set consecutive block threshold for auto-disable +- **validator-guard-interval**: Configure check frequency in blocks +- **validator-guard-validator**: Define validators to monitor with key pairs **Section sources** - [witness_guard.hpp:11-48](file://plugins/witness_guard/include/graphene/plugins/witness_guard/witness_guard.hpp#L11-L48) @@ -892,35 +892,35 @@ The witness guard plugin provides comprehensive protection and monitoring capabi - [witness_guard.cpp:301-408](file://plugins/witness_guard/witness_guard.cpp#L301-L408) - [witness_guard.cpp:410-555](file://plugins/witness_guard/witness_guard.cpp#L410-L555) -### Witness API Plugin +### validator API Plugin Responsibilities: - Expose JSON-RPC endpoints for: - - Active witnesses in the current schedule. - - Full witness schedule object. - - Witnesses by ID, by account, by votes, by counted votes. - - Count of witnesses. - - Lookup of witness accounts by name range. + - Active validators in the current schedule. + - Full validator schedule object. + - validators by ID, by account, by votes, by counted votes. + - Count of validators. + - Lookup of validator accounts by name range. Implementation highlights: - Uses weak read locks around database queries. - Enforces limits on returned sets (e.g., max 100 for vote-based lists). -- Converts chain witness objects to API-friendly structures. +- Converts chain validator objects to API-friendly structures. ```mermaid sequenceDiagram participant Client as "Client" participant RPC as "JSON-RPC" -participant WAPI as "Witness API Plugin" +participant WAPI as "validator API Plugin" participant DB as "Chain Database" Client->>RPC : get_active_witnesses() RPC->>WAPI : dispatch WAPI->>DB : get_witness_schedule_object() DB-->>WAPI : witness_schedule_object -WAPI-->>RPC : active witness names[] +WAPI-->>RPC : active validator names[] RPC-->>Client : response Client->>RPC : get_witness_by_account(account) RPC->>WAPI : dispatch -WAPI->>DB : find witness by name +WAPI->>DB : find validator by name DB-->>WAPI : witness_object WAPI-->>RPC : witness_api_object or null RPC-->>Client : response @@ -944,11 +944,11 @@ RPC-->>Client : response ### Chain Database: Enhanced Fork Database Integration The database maintains: -- Witness objects with voting, signing keys, virtual scheduling fields, and participation counters. -- Witness schedule object with shuffled witnesses, current virtual time, and majority version. -- Block post validation objects used to coordinate cross-witness validation. +- validator objects with voting, signing keys, virtual scheduling fields, and participation counters. +- validator schedule object with shuffled validators, current virtual time, and majority version. +- Block post validation objects used to coordinate cross-validator validation. - **Enhanced**: Direct fork database access through `get_fork_db()` method for comprehensive fork collision detection. -- **Enhanced**: Comprehensive witness reward creation with find_account() validation to prevent crashes from missing account objects. +- **Enhanced**: Comprehensive validator reward creation with find_account() validation to prevent crashes from missing account objects. - **Enhanced**: **NEW**: Integration with skip_undo_history_check flag for controlled production during recovery scenarios. - **Enhanced**: **NEW**: Comprehensive debug logging system enabling verbose traces for chain internals and block processing. - **New**: Enhanced fork database with automatic stale fork pruning after successful block application. @@ -956,11 +956,11 @@ The database maintains: - **New**: **Enhanced**: Supports emergency_consensus_active field for emergency consensus mode detection. Behavior highlights: -- Computes witness participation rate and enforces minimum participation thresholds. -- Updates last irreversible block (LIB) based on witness confirmations and thresholds. -- Recomputes witness schedule and shuffles according to virtual time and votes. +- Computes validator participation rate and enforces minimum participation thresholds. +- Updates last irreversible block (LIB) based on validator confirmations and thresholds. +- Recomputes validator schedule and shuffles according to virtual time and votes. - **Enhanced**: Provides comprehensive fork database querying capabilities for fork collision detection. -- **Enhanced**: Implements comprehensive validation for witness reward creation across all hardfork versions. +- **Enhanced**: Implements comprehensive validation for validator reward creation across all hardfork versions. - **New**: Automatic stale fork pruning removes competing blocks from dead forks to maintain database efficiency. - **New**: Intelligent fork comparison with vote-weighted calculations and longer-chain bonuses. - **New**: **Enhanced**: Supports emergency consensus detection through emergency_consensus_active flag. @@ -1028,7 +1028,7 @@ class dynamic_global_property_object { +emergency_consensus_start_block : uint32_t } witness_object --> witness_schedule_object : "referenced by schedule" -block_post_validation_object --> witness_schedule_object : "mentions scheduled witnesses" +block_post_validation_object --> witness_schedule_object : "mentions scheduled validators" fork_database --> witness_schedule_object : "tracks competing blocks" dynamic_global_property_object --> fork_database : "emergency consensus state" ``` @@ -1058,7 +1058,7 @@ dynamic_global_property_object --> fork_database : "emergency consensus state" - [global_property_object.hpp:139](file://libraries/chain/include/graphene/chain/global_property_object.hpp#L139) ### Enhanced Time Synchronization Service -**New Section** The witness system now includes robust time synchronization capabilities managed through the time service layer with enhanced logging for fork collision detection and comprehensive error handling for witness reward creation. +**New Section** The validator system now includes robust time synchronization capabilities managed through the time service layer with enhanced logging for fork collision detection and comprehensive error handling for validator reward creation. Responsibilities: - Provide precise wall-clock time synchronization using NTP with 250ms interval optimization. @@ -1066,7 +1066,7 @@ Responsibilities: - Monitor and report significant time synchronization changes. - Enable forced synchronization on timing issues and fork collisions. - **Enhanced**: Comprehensive delta change monitoring with 100ms threshold detection. -- **Enhanced**: Comprehensive error handling for witness reward creation with find_account() validation. +- **Enhanced**: Comprehensive error handling for validator reward creation with find_account() validation. Key behaviors: - Thread-safe NTP service initialization and management with 250ms tick scheduling. @@ -1074,7 +1074,7 @@ Key behaviors: - Significant delta change detection (100ms threshold) for monitoring. - Graceful shutdown with proper resource cleanup. - **Enhanced**: Automatic NTP synchronization triggered by fork collision detection. -- **Enhanced**: Comprehensive validation and error handling for witness reward distribution. +- **Enhanced**: Comprehensive validation and error handling for validator reward distribution. **Section sources** - [time.cpp:13-53](file://libraries/time/time.cpp#L13-L53) @@ -1189,7 +1189,7 @@ of their stake-proportional share — economically insignificant. --- ## Dependency Analysis -- The witness plugin depends on: +- The Validator Plugin depends on: - Chain plugin for database access and block generation. - P2P plugin for broadcasting blocks and block post validations. - **Enhanced**: NTP time service for precise 250ms slot alignment and timing validation. @@ -1198,20 +1198,20 @@ of their stake-proportional share — economically insignificant. - **Enhanced**: **NEW**: Comprehensive debug logging system for verbose traces of block production and chain internals. - **New**: External plugins can depend on the `is_witness_scheduled_soon()` method for coordination. - **New**: Enhanced fork database with compare_fork_branches() function for intelligent fork switching. -- The witness guard plugin depends on: - - Chain plugin for database access and witness monitoring. +- The validator guard plugin depends on: + - Chain plugin for database access and validator monitoring. - P2P plugin for broadcasting witness_update transactions. - **New**: Emergency consensus detection through dynamic_global_property_object. - **New**: Configurable check intervals and auto-disable thresholds. - - **New**: Key pair management for witness protection. -- The witness API plugin depends on: + - **New**: Key pair management for validator protection. +- The validator API plugin depends on: - Chain plugin for read-only queries. - JSON-RPC plugin for transport. - The chain database depends on: - - Witness objects and schedule indices. - - Block post validation objects for cross-witness coordination. + - validator objects and schedule indices. + - Block post validation objects for cross-validator coordination. - **Enhanced**: Fork database for tracking competing blocks and fork resolution. - - **Enhanced**: Comprehensive validation for witness reward creation with find_account() checks. + - **Enhanced**: Comprehensive validation for validator reward creation with find_account() checks. - **Enhanced**: **NEW**: skip_undo_history_check flag for controlled production during recovery scenarios. - **Enhanced**: **NEW**: Comprehensive debug logging system enabling verbose traces for chain internals. - **New**: Automatic stale fork pruning mechanism for database efficiency. @@ -1220,16 +1220,16 @@ of their stake-proportional share — economically insignificant. ```mermaid graph LR -WITNESS["Witness Plugin"] --> CHAIN["Chain Plugin"] -WITNESS --> P2P["P2P Plugin
resync_from_lib()"] -WITNESS --> TIME["Time Service"] -WITNESS --> FORK_DB["Fork Database
enhanced with stale pruning"] -WITNESS --> DEBUG_LOG["Debug Logging
verbose traces"] -WGUARD["Witness Guard Plugin"] --> CHAIN +validator["Validator Plugin"] --> CHAIN["Chain Plugin"] +validator --> P2P["P2P Plugin
resync_from_lib()"] +validator --> TIME["Time Service"] +validator --> FORK_DB["Fork Database
enhanced with stale pruning"] +validator --> DEBUG_LOG["Debug Logging
verbose traces"] +WGUARD["validator Guard Plugin"] --> CHAIN WGUARD --> P2P WGUARD --> EMERGENCY["Emergency Consensus
emergency_consensus_active"] -WAPI["Witness API Plugin"] --> CHAIN -SNAPSHOT["Snapshot Plugin"] --> WITNESS +WAPI["validator API Plugin"] --> CHAIN +SNAPSHOT["Snapshot Plugin"] --> validator CHAIN --> DB["database.hpp/.cpp
compare_fork_branches()"] DB --> WITNESS_OBJ["witness_objects.hpp"] DB --> BPV_OBJ["chain_objects.hpp"] @@ -1242,8 +1242,8 @@ TIME --> NTP["NTP Service"] ``` **Diagram sources** -- [witness.hpp:34-68](file://plugins/witness/include/graphene/plugins/witness/witness.hpp#L34-L68) -- [witness.cpp:59-118](file://plugins/witness/witness.cpp#L59-L118) +- [validator.hpp:34-68](file://plugins/validator/include/graphene/plugins/validator/validator.hpp#L34-L68) +- [validator.cpp:59-118](file://plugins/validator/validator.cpp#L59-L118) - [witness_guard.hpp:11-48](file://plugins/witness_guard/include/graphene/plugins/witness_guard/witness_guard.hpp#L11-L48) - [witness_guard.cpp:27-78](file://plugins/witness_guard/witness_guard.cpp#L27-L78) - [witness_api_plugin.hpp:56-98](file://plugins/witness_api/include/graphene/plugins/witness_api/plugin.hpp#L56-L98) @@ -1258,7 +1258,7 @@ TIME --> NTP["NTP Service"] - [p2p_plugin.hpp:50-55](file://plugins/p2p/include/graphene/plugins/p2p/p2p_plugin.hpp#L50-L55) **Section sources** -- [witness.cpp:59-118](file://plugins/witness/witness.cpp#L59-L118) +- [validator.cpp:59-118](file://plugins/validator/validator.cpp#L59-L118) - [witness_guard.cpp:27-78](file://plugins/witness_guard/witness_guard.cpp#L27-L78) - [witness_api_plugin.cpp:13-28](file://plugins/witness_api/plugin.cpp#L13-L28) - [database.hpp:37-83](file://libraries/chain/include/graphene/chain/database.hpp#L37-L83) @@ -1266,8 +1266,8 @@ TIME --> NTP["NTP Service"] ## Performance Considerations - Production loop alignment: The loop waits until the next 250ms boundary and sleeps for at least 50ms to avoid excessive polling, reducing CPU overhead and providing deterministic slot time alignment. - Retry on block generation failures: On exceptions during block generation, pending transactions are cleared and the generation is retried once to mitigate transient issues. -- Participation threshold: Ensures sufficient witness participation before producing blocks, preventing premature production on minority forks. -- Virtual scheduling: Uses virtual time and votes to fairly distribute block production slots among witnesses, avoiding hot-spotting and ensuring proportional representation. +- Participation threshold: Ensures sufficient validator participation before producing blocks, preventing premature production on minority forks. +- Virtual scheduling: Uses virtual time and votes to fairly distribute block production slots among validators, avoiding hot-spotting and ensuring proportional representation. - **Enhanced**: Forced NTP synchronization reduces timing-related production failures and improves system reliability during clock drift scenarios. - **Enhanced**: Comprehensive fork collision detection adds minimal overhead while preventing costly fork resolution failures. - **Enhanced**: **NEW**: Minority fork detection adds minimal overhead while preventing network fragmentation and minority fork propagation. @@ -1277,44 +1277,44 @@ TIME --> NTP["NTP Service"] - **Updated**: Improved configuration parameter processing with type safety and proper scaling for better performance and reliability. - **Enhanced**: Fork database querying uses efficient multi-index containers for fast block lookup and competition detection. - **Enhanced**: find_account() validation adds minimal overhead while providing comprehensive protection against database corruption scenarios. -- **Enhanced**: Comprehensive error handling in witness reward creation prevents crashes and ensures graceful degradation during critical failures. +- **Enhanced**: Comprehensive error handling in validator reward creation prevents crashes and ensures graceful degradation during critical failures. - **New**: 250ms interval optimization provides precise timing alignment for deterministic consensus maintenance. -- **New**: Deterministic slot time calculation ensures consistent block production timing across all witness nodes. +- **New**: Deterministic slot time calculation ensures consistent block production timing across all validator nodes. - **New**: Two-level fork collision resolution system provides intelligent fork switching decisions with configurable timeout behavior. - **New**: Automatic stale fork pruning prevents database bloat and maintains fork database efficiency. - **New**: Enhanced compare_fork_branches() function provides intelligent fork weight comparison with +10% longer-chain bonus. - **New**: Configurable fork collision timeout blocks parameter allows fine-tuning of fork resolution behavior for different network conditions. - **New**: skip_undo_history_check flag provides controlled production during recovery scenarios without disrupting normal operations. - **Enhanced**: **NEW**: debug-block-production configuration option enables verbose logging for block production and chain internals with detailed traces and granular visibility. -- **Enhanced**: **NEW**: Witness guard plugin provides comprehensive protection with minimal performance impact through efficient monitoring and safety checks. -- **Enhanced**: **NEW**: Auto-disable threshold prevents witness abuse while maintaining network stability and fair participation. +- **Enhanced**: **NEW**: validator guard plugin provides comprehensive protection with minimal performance impact through efficient monitoring and safety checks. +- **Enhanced**: **NEW**: Auto-disable threshold prevents validator abuse while maintaining network stability and fair participation. - **Enhanced**: **NEW**: Emergency consensus integration ensures continuous protection and recovery during network distress scenarios. - **Enhanced**: **NEW**: Network health monitoring prevents unsafe operations during stale production override periods. - **Enhanced**: **NEW**: Pending transaction tracking prevents unbounded memory growth while maintaining reliable restoration mechanisms. -**Updated** Added performance considerations for the corrected configuration parameter types, fork collision detection system, enhanced fork database querying capabilities, comprehensive witness reward creation validation, 250ms interval optimization, deterministic slot time alignment, new fork collision timeout configuration, two-level fork resolution system with intelligent decision-making, automatic stale fork pruning, enhanced fork comparison functions, configurable timeout parameters for optimal network performance, **NEW**: minority fork detection system with minimal overhead, **NEW**: automatic recovery mechanisms through P2P resynchronization, **NEW**: skip_undo_history_check flag for controlled production during recovery scenarios, **NEW**: comprehensive debug logging system enabling verbose traces for block production and chain internals with detailed visibility, **NEW**: witness protection and monitoring capabilities with auto-restore and auto-disable features, **NEW**: emergency recovery mechanisms with automatic key restoration, **NEW**: consecutive block protection to prevent witness abuse, **NEW**: enhanced network connectivity with improved peer synchronization, **NEW**: witness guard plugin with efficient monitoring and safety checks, **NEW**: auto-disable threshold for preventing witness abuse, **NEW**: emergency consensus integration for continuous protection, **NEW**: network health monitoring for safe operations, **NEW**: pending transaction tracking for reliable restoration. +**Updated** Added performance considerations for the corrected configuration parameter types, fork collision detection system, enhanced fork database querying capabilities, comprehensive validator reward creation validation, 250ms interval optimization, deterministic slot time alignment, new fork collision timeout configuration, two-level fork resolution system with intelligent decision-making, automatic stale fork pruning, enhanced fork comparison functions, configurable timeout parameters for optimal network performance, **NEW**: minority fork detection system with minimal overhead, **NEW**: automatic recovery mechanisms through P2P resynchronization, **NEW**: skip_undo_history_check flag for controlled production during recovery scenarios, **NEW**: comprehensive debug logging system enabling verbose traces for block production and chain internals with detailed visibility, **NEW**: validator protection and monitoring capabilities with auto-restore and auto-disable features, **NEW**: emergency recovery mechanisms with automatic key restoration, **NEW**: consecutive block protection to prevent validator abuse, **NEW**: enhanced network connectivity with improved peer synchronization, **NEW**: validator guard plugin with efficient monitoring and safety checks, **NEW**: auto-disable threshold for preventing validator abuse, **NEW**: emergency consensus integration for continuous protection, **NEW**: network health monitoring for safe operations, **NEW**: pending transaction tracking for reliable restoration. ## Troubleshooting Guide Common issues and resolutions: -- No witnesses configured - - Symptom: Startup logs indicate no witnesses configured. - - Resolution: Add witness names and private keys to configuration. +- No validators configured + - Symptom: Startup logs indicate no validators configured. + - Resolution: Add validator names and private keys to configuration. - Low participation - - Symptom: Blocks not produced due to insufficient witness participation. - - Resolution: Ensure enough witnesses are online and participating per configured threshold. + - Symptom: Blocks not produced due to insufficient validator participation. + - Resolution: Ensure enough validators are online and participating per configured threshold. - Missing private key - Symptom: Logs indicate inability to sign block due to missing private key. - - Resolution: Verify private key is provided in the correct WIF format and matches the witness signing key. + - Resolution: Verify private key is provided in the correct WIF format and matches the validator signing key. - Timing lag - Symptom: Blocks not produced due to waking up outside the 500ms window. - Resolution: Improve system clock accuracy and reduce latency; consider enabling stale production only as a temporary workaround. - **Enhanced**: System automatically forces NTP synchronization when timing issues are detected. - Consecutive block production disabled - - Symptom: Blocks not produced because the last block was generated by the same witness. + - Symptom: Blocks not produced because the last block was generated by the same validator. - Resolution: Investigate connectivity issues; disable consecutive production only as a temporary workaround. - **New**: Fork collision detection issues - Symptom: Blocks not produced despite good participation and timing, frequent "deferred block production due to fork collision" messages. - - Resolution: Check network connectivity and witness coordination; verify fork database integrity; monitor NTP synchronization quality. + - Resolution: Check network connectivity and validator coordination; verify fork database integrity; monitor NTP synchronization quality. - **New**: Check fork-collision-timeout-blocks configuration (default: 21 blocks) for appropriate timeout settings. - **New**: Monitor fork collision deferral count and timeout behavior for proper fork resolution. - **New**: Two-level fork collision resolution problems @@ -1323,14 +1323,14 @@ Common issues and resolutions: - **New**: Stale fork pruning issues - Symptom: Memory usage growing or fork database becoming bloated with competing blocks. - Resolution: Verify automatic stale fork pruning is working; check fork database remove_blocks_by_number() function; ensure proper parent-child relationships in fork database. -- **New**: Witness scheduling conflicts - - Symptom: Other plugins experience conflicts with witness operations. - - Resolution: Use `is_witness_scheduled_soon()` method to coordinate operations and defer critical tasks until witness production is complete. +- **New**: validator scheduling conflicts + - Symptom: Other plugins experience conflicts with validator operations. + - Resolution: Use `is_witness_scheduled_soon()` method to coordinate operations and defer critical tasks until validator production is complete. - **New**: NTP synchronization issues - Symptom: Frequent timing-related warnings or blocks not produced despite good participation. - Resolution: Check NTP server connectivity and system clock accuracy; verify NTP service is running properly; monitor delta change logs. - **New**: Crash race conditions - - Symptom: Witness plugin fails to shut down cleanly or leaves NTP service in inconsistent state. + - Symptom: Validator Plugin fails to shut down cleanly or leaves NTP service in inconsistent state. - Resolution: Ensure proper shutdown sequence; the system now handles crash-safe NTP service cleanup. - **New**: Configuration parameter type errors - Symptom: Errors indicating incorrect parameter types or values. @@ -1339,23 +1339,23 @@ Common issues and resolutions: - `required-participation`: integer value scaled by `CHAIN_1_PERCENT` (e.g., 33 for 33%) - `fork-collision-timeout-blocks`: integer value (default: 21 blocks) - `debug-block-production`: boolean value (`true`/`false`) - **NEW** - - `witness-guard-enabled`: boolean value (`true`/`false`) - **NEW** - - `witness-guard-disable`: integer value (default: 5) - **NEW** - - `witness-guard-interval`: integer value (default: 20) - **NEW** - - `witness-guard-witness`: JSON triplet format - **NEW** + - `validator-guard-enabled`: boolean value (`true`/`false`) - **NEW** + - `validator-guard-disable`: integer value (default: 5) - **NEW** + - `validator-guard-interval`: integer value (default: 20) - **NEW** + - `validator-guard-validator`: JSON triplet format - **NEW** - Check configuration files for proper syntax and values - **Enhanced**: Fork collision detection logging - Symptom: Frequent fork collision warnings with "Collision parents at block" messages. - - Resolution: Monitor fork database for competing blocks; check witness coordination; verify network stability; ensure proper NTP synchronization. + - Resolution: Monitor fork database for competing blocks; check validator coordination; verify network stability; ensure proper NTP synchronization. - **New**: Check fork collision deferral count and timeout behavior for proper resolution. - **New**: Enhanced fork comparison failures - Symptom: compare_fork_branches() function returning 0 (cannot compare) frequently. - - Resolution: Verify both fork tips are in fork database; check witness objects availability; ensure proper fork database state. + - Resolution: Verify both fork tips are in fork database; check validator objects availability; ensure proper fork database state. - **New**: Automatic stale pruning not working - Symptom: Fork database grows with stale competing blocks. - Resolution: Verify _push_block() method is calling stale pruning; check fork database state; ensure proper parent-child relationships. - **New**: Database corruption detection - - Symptom: Multiple critical error messages during witness reward processing with account validation failures. + - Symptom: Multiple critical error messages during validator reward processing with account validation failures. - Resolution: - Immediate restart with replay to rebuild database from genesis - Check disk space and file system integrity @@ -1370,7 +1370,7 @@ Common issues and resolutions: - Resolution: Adjust fork-collision-timeout-blocks parameter based on network conditions; monitor fork collision deferral count; verify timeout logic is working correctly. - **New**: Minority fork detection false positives - Symptom: Frequent minority fork detection warnings or unexpected recovery behavior. - - Resolution: Verify emergency consensus mode is not active; check skip_undo_history_check flag state; ensure proper network connectivity; verify witness configuration is correct. + - Resolution: Verify emergency consensus mode is not active; check skip_undo_history_check flag state; ensure proper network connectivity; verify validator configuration is correct. - **New**: Recovery mechanism issues - Symptom: Automatic recovery not working or taking too long to complete. - Resolution: Check P2P plugin connectivity; verify resync_from_lib() method is functioning; monitor network synchronization progress; ensure sufficient peer connections. @@ -1382,54 +1382,54 @@ Common issues and resolutions: - Resolution: Verify debug-block-production configuration is set to `true`; check log level settings; ensure proper log file configuration; monitor performance impact; adjust debug logging scope as needed. - **New**: Comprehensive debug trace analysis - Symptom: Difficulty interpreting debug log output or missing expected trace information. - - Resolution: Review debug log entries for timestamped traces; verify debug-block-production is enabled; check for granular visibility into witness participation checks, emergency mode enforcement, block post-validation processes, and minority fork detection; ensure proper log rotation and retention policies. -- **New**: Witness guard plugin issues - - Symptom: Witness key not being restored or auto-disabled unexpectedly. - - Resolution: Check witness-guard-enabled configuration; verify witness-guard-witness entries are properly formatted JSON triplets; ensure active keys have proper authority on-chain; monitor witness guard logs for specific error messages. + - Resolution: Review debug log entries for timestamped traces; verify debug-block-production is enabled; check for granular visibility into validator participation checks, emergency mode enforcement, block post-validation processes, and minority fork detection; ensure proper log rotation and retention policies. +- **New**: validator guard plugin issues + - Symptom: validator key not being restored or auto-disabled unexpectedly. + - Resolution: Check validator-guard-enabled configuration; verify validator-guard-validator entries are properly formatted JSON triplets; ensure active keys have proper authority on-chain; monitor validator guard logs for specific error messages. - **New**: Auto-disable threshold problems - - Symptom: Witnesses being auto-disabled too frequently or not at all. - - Resolution: Adjust witness-guard-disable parameter based on network conditions; verify witness block production patterns; ensure proper key management and network connectivity. + - Symptom: validators being auto-disabled too frequently or not at all. + - Resolution: Adjust validator-guard-disable parameter based on network conditions; verify validator block production patterns; ensure proper key management and network connectivity. - **New**: Emergency consensus protection issues - - Symptom: Witness protection not working during emergency consensus mode. - - Resolution: Verify emergency_consensus_active flag is detected correctly; check witness guard plugin behavior during emergency mode; ensure proper key restoration procedures. + - Symptom: validator protection not working during emergency consensus mode. + - Resolution: Verify emergency_consensus_active flag is detected correctly; check validator guard plugin behavior during emergency mode; ensure proper key restoration procedures. - **New**: Network health monitoring failures - - Symptom: Witness guard performing operations during unhealthy network conditions. - - Resolution: Verify network health checks are working; check LIB age monitoring; ensure proper safety checks are in place; review witness guard logs for health check results. + - Symptom: validator guard performing operations during unhealthy network conditions. + - Resolution: Verify network health checks are working; check LIB age monitoring; ensure proper safety checks are in place; review validator guard logs for health check results. - **New**: Production watchdog fires unexpectedly - Symptom: `WATCHDOG:` elog appears even though production seems healthy; verbose `DEBUG_CRASH` logging auto-enabled. - - Resolution: The watchdog fires when no block has been produced for 60s (emergency master) or 180s (regular witness) while production conditions appear met. Check: is our witness in the shuffled schedule (`our_slots_in_schedule > 0`)? Is the on-chain signing key blanked (`blanked_keys` field in watchdog log)? Is P2P still syncing (`dlt_syncing`)? The watchdog log contains all these fields. + - Resolution: The watchdog fires when no block has been produced for 60s (emergency master) or 180s (regular validator) while production conditions appear met. Check: is our validator in the shuffled schedule (`our_slots_in_schedule > 0`)? Is the on-chain signing key blanked (`blanked_keys` field in watchdog log)? Is P2P still syncing (`dlt_syncing`)? The watchdog log contains all these fields. - **New**: Lag tight loop / high CPU after missed slot - Symptom: After a `lag` condition, node CPU spikes and production loop fires many times per second on the same slot. - Resolution: This was a bug fixed in commit 8fce5f1e. The `_last_lag_slot_time` guard now skips ahead to avoid rechecking the same missed slot. Upgrade to a build that includes this fix. - **New**: Slot hijack counter increments for own blocks - - Symptom: `hijack #N` messages appear in watchdog diagnostics even when one of our own witnesses produced the block. - - Resolution: Fixed in commit 7b589b71. The hijack counter now resets when any of our witnesses (not just the slot-assigned one) produced the block. Upgrade to a build containing this fix. + - Symptom: `hijack #N` messages appear in watchdog diagnostics even when one of our own validators produced the block. + - Resolution: Fixed in commit 7b589b71. The hijack counter now resets when any of our validators (not just the slot-assigned one) produced the block. Upgrade to a build containing this fix. - **New**: not_my_turn streak warning - Symptom: `NOT_MY_TURN STREAK: N consecutive slots` warning in logs. - - Resolution: Another witness has held all slots for ~125s. Check: (1) Is our witness still in the shuffled schedule? (2) Is there a chain fork where the other side has a different schedule? (3) Has our witness been replaced/disabled on-chain? The log includes `our witnesses` and `last scheduled` fields for quick diagnosis. + - Resolution: Another validator has held all slots for ~125s. Check: (1) Is our validator still in the shuffled schedule? (2) Is there a chain fork where the other side has a different schedule? (3) Has our validator been replaced/disabled on-chain? The log includes `our validators` and `last scheduled` fields for quick diagnosis. -**Updated** Added troubleshooting information for fork collision detection, witness scheduling conflicts, the new coordination mechanisms, configuration parameter type issues, comprehensive witness reward creation validation, database corruption scenarios with clear recovery procedures, 250ms interval timing optimization issues, new fork collision timeout configuration, two-level fork resolution system, automatic stale fork pruning, enhanced fork comparison functions, configurable timeout parameters for optimal network behavior, **NEW**: comprehensive minority fork detection system with automatic recovery mechanisms, **NEW**: recovery mechanism issues through P2P resynchronization, **NEW**: skip_undo_history_check flag management during recovery scenarios, **NEW**: comprehensive debug logging system enabling verbose traces for block production and chain internals with detailed visibility, **NEW**: witness protection and monitoring capabilities with auto-restore and auto-disable features, **NEW**: emergency recovery mechanisms through P2P resynchronization, **NEW**: auto-disable threshold management for preventing witness abuse, **NEW**: emergency consensus integration for continuous protection, **NEW**: network health monitoring for safe operations, **NEW**: pending transaction tracking for reliable restoration, **NEW**: witness guard plugin configuration issues, **NEW**: auto-disable threshold problems, **NEW**: emergency consensus protection failures, **NEW**: network health monitoring failures. +**Updated** Added troubleshooting information for fork collision detection, validator scheduling conflicts, the new coordination mechanisms, configuration parameter type issues, comprehensive validator reward creation validation, database corruption scenarios with clear recovery procedures, 250ms interval timing optimization issues, new fork collision timeout configuration, two-level fork resolution system, automatic stale fork pruning, enhanced fork comparison functions, configurable timeout parameters for optimal network behavior, **NEW**: comprehensive minority fork detection system with automatic recovery mechanisms, **NEW**: recovery mechanism issues through P2P resynchronization, **NEW**: skip_undo_history_check flag management during recovery scenarios, **NEW**: comprehensive debug logging system enabling verbose traces for block production and chain internals with detailed visibility, **NEW**: validator protection and monitoring capabilities with auto-restore and auto-disable features, **NEW**: emergency recovery mechanisms through P2P resynchronization, **NEW**: auto-disable threshold management for preventing validator abuse, **NEW**: emergency consensus integration for continuous protection, **NEW**: network health monitoring for safe operations, **NEW**: pending transaction tracking for reliable restoration, **NEW**: validator guard plugin configuration issues, **NEW**: auto-disable threshold problems, **NEW**: emergency consensus protection failures, **NEW**: network health monitoring failures. **Section sources** -- [witness.cpp:171-192](file://plugins/witness/witness.cpp#L171-L192) -- [witness.cpp:255-271](file://plugins/witness/witness.cpp#L255-L271) -- [witness.cpp:387-396](file://plugins/witness/witness.cpp#L387-L396) -- [witness.cpp:263-266](file://plugins/witness/witness.cpp#L263-L266) -- [witness.cpp:206-249](file://plugins/witness/witness.cpp#L206-L249) -- [witness.cpp:447-471](file://plugins/witness/witness.cpp#L447-L471) -- [witness.cpp:590-695](file://plugins/witness/witness.cpp#L590-L695) +- [validator.cpp:171-192](file://plugins/validator/validator.cpp#L171-L192) +- [validator.cpp:255-271](file://plugins/validator/validator.cpp#L255-L271) +- [validator.cpp:387-396](file://plugins/validator/validator.cpp#L387-L396) +- [validator.cpp:263-266](file://plugins/validator/validator.cpp#L263-L266) +- [validator.cpp:206-249](file://plugins/validator/validator.cpp#L206-L249) +- [validator.cpp:447-471](file://plugins/validator/validator.cpp#L447-L471) +- [validator.cpp:590-695](file://plugins/validator/validator.cpp#L590-L695) - [time.cpp:36-39](file://libraries/time/time.cpp#L36-L39) - [database.cpp:2826-2836](file://libraries/chain/database.cpp#L2826-L2836) - [database.cpp:2873-2883](file://libraries/chain/database.cpp#L2873-L2883) - [database.cpp:1456-1471](file://libraries/chain/database.cpp#L1456-L1471) - [database.cpp:1223-1267](file://libraries/chain/database.cpp#L1223-L1267) -- [witness.cpp:509-555](file://plugins/witness/witness.cpp#L509-L555) +- [validator.cpp:509-555](file://plugins/validator/validator.cpp#L509-L555) - [witness_guard.cpp:83-191](file://plugins/witness_guard/witness_guard.cpp#L83-L191) - [witness_guard.cpp:455-544](file://plugins/witness_guard/witness_guard.cpp#L455-L544) - [global_property_object.hpp:139](file://libraries/chain/include/graphene/chain/global_property_object.hpp#L139) - [p2p_plugin.hpp:50-55](file://plugins/p2p/include/graphene/plugins/p2p/p2p_plugin.hpp#L50-L55) ## Conclusion -The Witness subsystem integrates tightly with the chain database and P2P layer to ensure timely, secure, and fair block production. The witness plugin manages production loops, participation thresholds, and broadcasting, while the witness guard plugin provides comprehensive protection and monitoring capabilities. The witness API plugin exposes essential read-only data to clients. +The validator subsystem integrates tightly with the chain database and P2P layer to ensure timely, secure, and fair block production. The Validator Plugin manages production loops, participation thresholds, and broadcasting, while the validator guard plugin provides comprehensive protection and monitoring capabilities. The validator API plugin exposes essential read-only data to clients. -**Enhanced** The system now includes robust NTP time synchronization with automatic fallback mechanisms, crash-safe shutdown procedures, strengthened timing-related production failure prevention, comprehensive fork collision detection system with two-level resolution, and enhanced fork database querying capabilities. **New** The addition of the `is_witness_scheduled_soon()` method enables sophisticated plugin coordination, allowing other plugins to avoid conflicts during critical operations like snapshot creation. **Updated** The configuration parameter system has been improved with corrected defaults and proper type handling for better reliability and performance. **Enhanced** The witness reward creation process has been significantly strengthened with comprehensive error handling, find_account() validation, crash prevention mechanisms, and clear recovery procedures for database corruption scenarios. **New** The 250ms interval optimization provides precise timing alignment for deterministic consensus maintenance, while the enhanced performance characteristics ensure better system responsiveness and consensus stability. **New** The two-level fork collision resolution system with configurable timeout provides intelligent fork switching decisions, automatic stale fork pruning maintains database efficiency, enhanced fork comparison functions enable sophisticated chain selection, and configurable timeout parameters allow fine-tuning for different network conditions. **NEW** The comprehensive minority fork detection system with automatic recovery mechanisms prevents network fragmentation and ensures proper consensus maintenance, while the enhanced emergency consensus mode integration provides seamless operation during network distress scenarios. **NEW** The skip_undo_history_check flag provides controlled production during recovery scenarios, and the P2P resynchronization mechanism ensures efficient network recovery without disrupting normal operations. **NEW** The comprehensive debug logging system enables verbose traces for block production and chain internals with detailed visibility into witness participation checks, emergency mode enforcement, block post-validation processes, and minority fork detection. **NEW** The witness guard plugin provides comprehensive protection and monitoring capabilities with auto-restore functionality, consecutive block auto-disable protection, emergency consensus support, and safety checks. **NEW** The enhanced network connectivity features include improved peer synchronization and emergency recovery mechanisms. Together, they form a robust foundation for witness operations in the VIZ node, with improved time synchronization, crash handling capabilities, enhanced plugin coordination features, comprehensive fork collision detection, reliable configuration parameter processing, strengthened fork database querying for detecting competing blocks at the same height, comprehensive witness reward creation validation with crash prevention and recovery procedures, 250ms interval optimization for deterministic slot time alignment, enhanced performance characteristics for better consensus maintenance, intelligent fork resolution system, automatic stale fork pruning, configurable timeout parameters for optimal network behavior, **NEW**: comprehensive minority fork detection system with automatic recovery mechanisms, **NEW**: enhanced emergency consensus mode integration, **NEW**: controlled production during recovery scenarios, **NEW**: efficient automatic recovery through P2P resynchronization for network stability, **NEW**: comprehensive debug logging system enabling verbose traces for block production and chain internals with detailed visibility, **NEW**: witness protection and monitoring capabilities with auto-restore and auto-disable features, **NEW**: emergency recovery mechanisms with automatic key restoration, **NEW**: consecutive block protection to prevent witness abuse, **NEW**: enhanced network connectivity with improved peer synchronization, **NEW**: witness guard plugin with comprehensive protection and monitoring, **NEW**: emergency consensus integration for continuous protection, **NEW**: network health monitoring for safe operations, and **NEW**: pending transaction tracking for reliable restoration. These enhancements make the witness system more resilient to various operational challenges while providing better integration points for the broader VIZ ecosystem, comprehensive protection against shared memory corruption, robust validation mechanisms for witness reward distribution across all hardfork versions, optimized timing for improved consensus maintenance, intelligent fork resolution with configurable behavior, automatic database maintenance for optimal performance, **NEW**: comprehensive minority fork detection and recovery mechanisms for network stability, **NEW**: enhanced emergency consensus mode integration for seamless operation during network distress, **NEW**: controlled production during recovery scenarios through skip_undo_history_check flag management, **NEW**: efficient automatic recovery through P2P resynchronization for rapid network stabilization, **NEW**: comprehensive debug logging system enabling verbose traces for block production and chain internals with detailed visibility, **NEW**: witness protection and monitoring capabilities with auto-restore and auto-disable features, **NEW**: emergency recovery mechanisms with automatic key restoration, **NEW**: consecutive block protection to prevent witness abuse, **NEW**: enhanced network connectivity with improved peer synchronization, and **NEW**: witness guard plugin with comprehensive protection and monitoring for enhanced network stability and security. \ No newline at end of file +**Enhanced** The system now includes robust NTP time synchronization with automatic fallback mechanisms, crash-safe shutdown procedures, strengthened timing-related production failure prevention, comprehensive fork collision detection system with two-level resolution, and enhanced fork database querying capabilities. **New** The addition of the `is_witness_scheduled_soon()` method enables sophisticated plugin coordination, allowing other plugins to avoid conflicts during critical operations like snapshot creation. **Updated** The configuration parameter system has been improved with corrected defaults and proper type handling for better reliability and performance. **Enhanced** The validator reward creation process has been significantly strengthened with comprehensive error handling, find_account() validation, crash prevention mechanisms, and clear recovery procedures for database corruption scenarios. **New** The 250ms interval optimization provides precise timing alignment for deterministic consensus maintenance, while the enhanced performance characteristics ensure better system responsiveness and consensus stability. **New** The two-level fork collision resolution system with configurable timeout provides intelligent fork switching decisions, automatic stale fork pruning maintains database efficiency, enhanced fork comparison functions enable sophisticated chain selection, and configurable timeout parameters allow fine-tuning for different network conditions. **NEW** The comprehensive minority fork detection system with automatic recovery mechanisms prevents network fragmentation and ensures proper consensus maintenance, while the enhanced emergency consensus mode integration provides seamless operation during network distress scenarios. **NEW** The skip_undo_history_check flag provides controlled production during recovery scenarios, and the P2P resynchronization mechanism ensures efficient network recovery without disrupting normal operations. **NEW** The comprehensive debug logging system enables verbose traces for block production and chain internals with detailed visibility into validator participation checks, emergency mode enforcement, block post-validation processes, and minority fork detection. **NEW** The validator guard plugin provides comprehensive protection and monitoring capabilities with auto-restore functionality, consecutive block auto-disable protection, emergency consensus support, and safety checks. **NEW** The enhanced network connectivity features include improved peer synchronization and emergency recovery mechanisms. Together, they form a robust foundation for validator operations in the VIZ node, with improved time synchronization, crash handling capabilities, enhanced plugin coordination features, comprehensive fork collision detection, reliable configuration parameter processing, strengthened fork database querying for detecting competing blocks at the same height, comprehensive validator reward creation validation with crash prevention and recovery procedures, 250ms interval optimization for deterministic slot time alignment, enhanced performance characteristics for better consensus maintenance, intelligent fork resolution system, automatic stale fork pruning, configurable timeout parameters for optimal network behavior, **NEW**: comprehensive minority fork detection system with automatic recovery mechanisms, **NEW**: enhanced emergency consensus mode integration, **NEW**: controlled production during recovery scenarios, **NEW**: efficient automatic recovery through P2P resynchronization for network stability, **NEW**: comprehensive debug logging system enabling verbose traces for block production and chain internals with detailed visibility, **NEW**: validator protection and monitoring capabilities with auto-restore and auto-disable features, **NEW**: emergency recovery mechanisms with automatic key restoration, **NEW**: consecutive block protection to prevent validator abuse, **NEW**: enhanced network connectivity with improved peer synchronization, **NEW**: validator guard plugin with comprehensive protection and monitoring, **NEW**: emergency consensus integration for continuous protection, **NEW**: network health monitoring for safe operations, and **NEW**: pending transaction tracking for reliable restoration. These enhancements make the validator system more resilient to various operational challenges while providing better integration points for the broader VIZ ecosystem, comprehensive protection against shared memory corruption, robust validation mechanisms for validator reward distribution across all hardfork versions, optimized timing for improved consensus maintenance, intelligent fork resolution with configurable behavior, automatic database maintenance for optimal performance, **NEW**: comprehensive minority fork detection and recovery mechanisms for network stability, **NEW**: enhanced emergency consensus mode integration for seamless operation during network distress, **NEW**: controlled production during recovery scenarios through skip_undo_history_check flag management, **NEW**: efficient automatic recovery through P2P resynchronization for rapid network stabilization, **NEW**: comprehensive debug logging system enabling verbose traces for block production and chain internals with detailed visibility, **NEW**: validator protection and monitoring capabilities with auto-restore and auto-disable features, **NEW**: emergency recovery mechanisms with automatic key restoration, **NEW**: consecutive block protection to prevent validator abuse, **NEW**: enhanced network connectivity with improved peer synchronization, and **NEW**: validator guard plugin with comprehensive protection and monitoring for enhanced network stability and security. \ No newline at end of file From 5ca460e205223e859410b37140446744003366d9 Mon Sep 17 00:00:00 2001 From: Anatoly Piskunov Date: Mon, 18 May 2026 17:37:43 +0400 Subject: [PATCH 24/29] docs(cli-wallet): rename witness terms to validator in documentation - Change all instances of "witness" to "validator" to reflect updated terminology - Update command examples and descriptions accordingly - Modify headings and code snippets for commands like get_active_validators, list_validators, get_validator, update_validator, and vote_for_validator - Ensure consistency across validator operation instructions in CLI wallet docs --- .qoder/docs/cli-wallet.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.qoder/docs/cli-wallet.md b/.qoder/docs/cli-wallet.md index 0375fce954..3d1be9cf00 100644 --- a/.qoder/docs/cli-wallet.md +++ b/.qoder/docs/cli-wallet.md @@ -201,11 +201,11 @@ get_ops_in_block 1000000 false # all operations get_ops_in_block 1000000 true # only virtual operations ``` -### get_active_witnesses +### get_active_validators Returns the list of validators producing blocks in the current round (21 blocks). ```bash -get_active_witnesses +get_active_validators ``` ### get_account @@ -406,35 +406,35 @@ set_withdraw_vesting_route "alice" "charlie" 2500 true true ## validator Operations -### list_witnesses +### list_validators Lists all validators registered in the blockchain. ```bash -list_witnesses "" 100 # First 100 validators -list_witnesses "bob" 100 # 100 validators starting from "bob" +list_validators "" 100 # First 100 validators +list_validators "bob" 100 # 100 validators starting from "bob" ``` -### get_witness +### get_validator Returns information about the given validator. ```bash -get_witness "witnessname" +get_validator "validatorname" ``` -### update_witness +### update_validator Update a validator object. ```bash -update_witness "mywitness" "https://mywitness.com" "VIZsigningkey..." true +update_validator "myvalidator" "https://myvalidator.com" "VIZsigningkey..." true # Disable block production (empty key): -update_witness "mywitness" "" "" true +update_validator "myvalidator" "" "" true ``` ### update_chain_properties Vote for the chain properties. ```bash -update_chain_properties "mywitness" \ +update_chain_properties "myvalidator" \ {"account_creation_fee":"1.000 VIZ","maximum_block_size":65536,"create_account_delegation_ratio":10,...} \ true ``` @@ -443,7 +443,7 @@ update_chain_properties "mywitness" \ Vote for the versioned chain properties. ```bash -versioned_update_chain_properties "mywitness" \ +versioned_update_chain_properties "myvalidator" \ {"account_creation_fee":"1.000 VIZ","maximum_block_size":65536,...} \ true ``` @@ -457,12 +457,12 @@ set_voting_proxy "alice" "trustedvoter" true set_voting_proxy "alice" "" true ``` -### vote_for_witness +### vote_for_validator Vote for a validator to become a block producer. ```bash -vote_for_witness "alice" "mywitness" true true # Vote for -vote_for_witness "alice" "mywitness" false true # Vote against +vote_for_validator "alice" "myvalidator" true true # Vote for +vote_for_validator "alice" "myvalidator" false true # Vote against ``` --- From adf23fad77c4cf6fc2dccfdf101e1e1c748295a4 Mon Sep 17 00:00:00 2001 From: Anatoly Piskunov Date: Mon, 18 May 2026 20:58:25 +0400 Subject: [PATCH 25/29] chain: treat wrong-scheduled-validator as soft fork-resolution condition Network-latency forks at the same block height (two scheduled validators producing competing blocks for the same slot due to differing views of missed slots) previously caused minority-side nodes to: - reject the disputed block outright via FC_ASSERT in validate_block_header, - remove it from fork_db in the _push_block catch, - flag descendants of the disputed branch as DEAD_FORK and soft-ban the peer carrying the majority chain, - fail any later fork-switch attempt because the same assertion fires again during branch re-application. Effect: the minority node stays stranded on a losing fork, severs useful peers, and cannot recover even when the majority chain becomes demonstrably heavier. This change introduces a typed exception (wrong_scheduled_validator_exception) for the schedule mismatch and treats it as a soft fork-resolution condition rather than a fatal validation failure: - validate_block_header throws the typed exception instead of an untyped FC_ASSERT in the non-emergency branch. - _push_block's direct-apply catch keeps the block in fork_db (does not call _fork_db.remove) and returns false so the P2P layer reports FORK_DB_ONLY instead of REJECTED, preventing soft-bans of peers relaying the alternative branch. - _push_block's fork-switch loop retries the offending block once with skip_witness_schedule_check when compare_fork_branches has already chosen its branch as heavier, allowing the heavier chain to win and the minority node to switch back to majority consensus. Emergency-mode relaxation, signature verification, parent linkage, timestamp validation, and the generic exception recovery paths are unchanged. The behavior mirrors the existing emergency-consensus bypass: trust the chain that vote-weighted comparison preferred, because its producers' blocks already represent collective consensus. --- libraries/chain/database.cpp | 40 ++++++++++++++++++- .../graphene/chain/database_exceptions.hpp | 7 ++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/libraries/chain/database.cpp b/libraries/chain/database.cpp index 9056ad0f1b..9944561424 100644 --- a/libraries/chain/database.cpp +++ b/libraries/chain/database.cpp @@ -1890,6 +1890,22 @@ namespace graphene { namespace chain { apply_block((*ritr)->data, skip); session.push(); } + catch (const wrong_scheduled_validator_exception &e) { + // Schedule mismatch on a branch already chosen + // as heavier by vote-weighted comparison. The + // branch represents collective consensus of + // its producers — retry with the schedule + // check skipped so the heavier chain can win. + wlog("Fork switch: relaxing schedule check for block #${n} (validator=${w}); branch chosen by vote-weighted comparison: ${e}", + ("n", (*ritr)->data.block_num())("w", (*ritr)->data.validator)("e", e.to_detail_string())); + try { + auto session = start_undo_session(); + apply_block((*ritr)->data, skip | skip_witness_schedule_check); + session.push(); + } catch (const fc::exception &e2) { + except = e2; + } + } catch (const fc::exception &e) { except = e; } @@ -1988,6 +2004,18 @@ namespace graphene { namespace chain { apply_block(new_block, skip); session.push(); } + catch (const wrong_scheduled_validator_exception &e) { + // Schedule mismatch: keep the block in fork_db as a + // competing tip so descendants can link to it. When + // a heavier branch builds upon this block, fork-switch + // will apply it with skip_witness_schedule_check. + // Returning false signals "not applied" without + // triggering the P2P rejection / soft-ban path. + wlog("Block #${n} from validator ${w} kept in fork_db as competing tip (schedule mismatch): ${e}", + ("n", new_block.block_num())("w", new_block.validator) + ("e", e.to_detail_string())); + return false; + } catch (const fc::exception &e) { elog("Failed to push new block:\n${e}", ("e", e.to_detail_string())); _fork_db.remove(new_block.id()); @@ -5371,9 +5399,17 @@ namespace graphene { namespace chain { ("slot", slot_num)("num", next_block.block_num())); } } else { - FC_ASSERT(witness.owner == - scheduled_witness, "Validator produced block at wrong time", + if (witness.owner != scheduled_witness) { + // Throw a typed exception so the caller can + // distinguish a schedule mismatch from other + // validation failures. Schedule mismatch is + // a soft fork-resolution condition: the block + // is kept in fork_db, and a heavier branch can + // win via vote-weighted comparison. + FC_THROW_EXCEPTION(wrong_scheduled_validator_exception, + "Validator produced block at wrong time", ("block validator", next_block.validator)("scheduled", scheduled_witness)("slot_num", slot_num)); + } } } diff --git a/libraries/chain/include/graphene/chain/database_exceptions.hpp b/libraries/chain/include/graphene/chain/database_exceptions.hpp index b6bed015ca..6fc7878db1 100644 --- a/libraries/chain/include/graphene/chain/database_exceptions.hpp +++ b/libraries/chain/include/graphene/chain/database_exceptions.hpp @@ -70,6 +70,13 @@ namespace graphene { FC_DECLARE_DERIVED_EXCEPTION(block_validate_exception, graphene::chain::chain_exception, 4020000, "block validation exception") + // Thrown when a block's validator does not match the locally + // computed schedule for the block's slot. Treated as a soft + // condition during fork resolution: the block is kept in fork_db + // as a competing tip, and a fork-switch may apply it with + // skip_witness_schedule_check when the branch is heavier. + FC_DECLARE_DERIVED_EXCEPTION(wrong_scheduled_validator_exception, graphene::chain::block_validate_exception, 4020100, "validator does not match scheduled validator for slot") + FC_DECLARE_DERIVED_EXCEPTION(transaction_exception, graphene::chain::chain_exception, 4030000, "transaction validation exception") FC_DECLARE_DERIVED_EXCEPTION(operation_validate_exception, graphene::chain::chain_exception, 4040000, "operation validation exception") From ad900409e19cf6490cdc1c0218d817c140aad31e Mon Sep 17 00:00:00 2001 From: Anatoly Piskunov Date: Tue, 19 May 2026 11:31:33 +0400 Subject: [PATCH 26/29] =?UTF-8?q?fix(dlt-p2p):=20add=20grace=20period=20to?= =?UTF-8?q?=20prevent=20instant=20SYNC=E2=86=94FORWARD=20oscillation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When sync_stagnation_check() transitions to FORWARD, check_forward_behind() runs on the same periodic tick and immediately transitions back to SYNC because the peer is still ahead — creating an infinite oscillation loop that prevents the node from ever syncing. Add a 15-second grace period after entering FORWARD mode during which check_forward_behind() skips its peer-head comparison. This gives broadcast blocks time to arrive and gap fill a chance to work before re-evaluating whether we've fallen behind. --- libraries/network/dlt_p2p_node.cpp | 13 +++++++++++++ .../include/graphene/network/dlt_p2p_node.hpp | 2 ++ 2 files changed, 15 insertions(+) diff --git a/libraries/network/dlt_p2p_node.cpp b/libraries/network/dlt_p2p_node.cpp index e18a58f394..24faf9aaea 100644 --- a/libraries/network/dlt_p2p_node.cpp +++ b/libraries/network/dlt_p2p_node.cpp @@ -2416,6 +2416,7 @@ void dlt_p2p_node::transition_to_forward() { _sync_stagnation_retries = 0; _last_forward_head_num = 0; // P37: reset so check_forward_stagnation initializes _last_forward_progress_time = fc::time_point(); + _forward_entered_time = fc::time_point::now(); ilog(DLT_LOG_GREEN "=== DLT P2P: transitioning to FORWARD mode ===" DLT_LOG_RESET); @@ -2655,6 +2656,18 @@ void dlt_p2p_node::check_forward_behind() { if (_node_status != DLT_NODE_STATUS_FORWARD) return; if (!_delegate) return; + // Grace period: don't check for falling behind immediately after + // entering FORWARD. SYNC stagnation fires transition_to_forward() + // and then check_forward_behind() runs on the SAME periodic tick — + // instant SYNC→FORWARD→SYNC oscillation. Give FORWARD mode a + // chance to receive broadcast blocks before checking peer heads. + if (_forward_entered_time != fc::time_point()) { + auto grace = fc::time_point::now() - _forward_entered_time; + if (grace.count() < FORWARD_BEHIND_GRACE_SEC * 1000000) { + return; + } + } + uint32_t our_head = _delegate->get_head_block_num(); // Check if any active peer is significantly ahead of us. diff --git a/libraries/network/include/graphene/network/dlt_p2p_node.hpp b/libraries/network/include/graphene/network/dlt_p2p_node.hpp index c3f451f253..4a17ddd9a3 100644 --- a/libraries/network/include/graphene/network/dlt_p2p_node.hpp +++ b/libraries/network/include/graphene/network/dlt_p2p_node.hpp @@ -315,6 +315,8 @@ class dlt_p2p_node { // ── FORWARD fallbehind ────────────────────────────────────── static constexpr uint32_t FORWARD_FALLBEHIND_THRESHOLD = 2; ///< Blocks behind peer before FORWARD→SYNC + static constexpr uint32_t FORWARD_BEHIND_GRACE_SEC = 15; ///< Grace period after SYNC→FORWARD before checking peers + fc::time_point _forward_entered_time; ///< When we last entered FORWARD mode // ── Peer state ─────────────────────────────────────────────── peer_id _next_peer_id = 1; From 9fb3af29d151b8eb5ff00b320d49f56e7efc6ab8 Mon Sep 17 00:00:00 2001 From: Anatoly Piskunov Date: Tue, 19 May 2026 17:19:59 +0400 Subject: [PATCH 27/29] add new docs --- .github/workflows/docs.yml | 48 ++ @l10n/ru/docs/advanced/database-schema.md | 297 +++++++++++ @l10n/ru/docs/advanced/dlt-architecture.md | 248 ++++++++++ @l10n/ru/docs/advanced/hardfork-management.md | 199 ++++++++ @l10n/ru/docs/advanced/security.md | 162 ++++++ @l10n/ru/docs/api/cli-wallet.md | 251 ++++++++++ @l10n/ru/docs/api/client-libraries.md | 140 ++++++ @l10n/ru/docs/api/json-rpc.md | 228 +++++++++ @l10n/ru/docs/consensus/block-processing.md | 259 ++++++++++ .../ru/docs/consensus/emergency-consensus.md | 221 +++++++++ @l10n/ru/docs/consensus/fair-dpos.md | 170 +++++++ @l10n/ru/docs/consensus/fork-resolution.md | 180 +++++++ @l10n/ru/docs/consensus/hardforks.md | 171 +++++++ @l10n/ru/docs/development/building.md | 196 ++++++++ @l10n/ru/docs/development/debugging.md | 186 +++++++ .../ru/docs/development/plugin-development.md | 252 ++++++++++ @l10n/ru/docs/development/testing.md | 159 ++++++ @l10n/ru/docs/governance/chain-properties.md | 158 ++++++ @l10n/ru/docs/governance/committee.md | 116 +++++ @l10n/ru/docs/governance/staking-and-dao.md | 166 +++++++ @l10n/ru/docs/introduction/architecture.md | 177 +++++++ @l10n/ru/docs/introduction/key-concepts.md | 161 ++++++ @l10n/ru/docs/introduction/what-is-viz.md | 121 +++++ @l10n/ru/docs/node/building.md | 190 ++++++++ @l10n/ru/docs/node/configuration.md | 223 +++++++++ @l10n/ru/docs/node/docker.md | 183 +++++++ @l10n/ru/docs/node/getting-started.md | 184 +++++++ @l10n/ru/docs/node/monitoring.md | 313 ++++++++++++ @l10n/ru/docs/node/snapshot.md | 377 ++++++++++++++ @l10n/ru/docs/node/validator-guard.md | 138 ++++++ @l10n/ru/docs/node/validator-node.md | 235 +++++++++ @l10n/ru/docs/p2p/forward-mode.md | 214 ++++++++ @l10n/ru/docs/p2p/messages.md | 461 ++++++++++++++++++ @l10n/ru/docs/p2p/overview.md | 288 +++++++++++ @l10n/ru/docs/p2p/stats-reference.md | 233 +++++++++ @l10n/ru/docs/p2p/sync-scenarios.md | 215 ++++++++ @l10n/ru/docs/plugins/chain.md | 125 +++++ @l10n/ru/docs/plugins/database-api.md | 408 ++++++++++++++++ @l10n/ru/docs/plugins/overview.md | 379 ++++++++++++++ @l10n/ru/docs/plugins/snapshot.md | 347 +++++++++++++ @l10n/ru/docs/plugins/validator.md | 226 +++++++++ @l10n/ru/docs/plugins/webserver.md | 117 +++++ @l10n/ru/docs/protocol/data-types.md | 257 ++++++++++ .../protocol/operations/account-market.md | 120 +++++ @l10n/ru/docs/protocol/operations/accounts.md | 96 ++++ @l10n/ru/docs/protocol/operations/awards.md | 94 ++++ .../ru/docs/protocol/operations/committee.md | 103 ++++ @l10n/ru/docs/protocol/operations/content.md | 104 ++++ @l10n/ru/docs/protocol/operations/escrow.md | 158 ++++++ @l10n/ru/docs/protocol/operations/invites.md | 117 +++++ @l10n/ru/docs/protocol/operations/overview.md | 111 +++++ .../ru/docs/protocol/operations/proposals.md | 125 +++++ @l10n/ru/docs/protocol/operations/recovery.md | 106 ++++ .../docs/protocol/operations/subscriptions.md | 77 +++ .../ru/docs/protocol/operations/transfers.md | 136 ++++++ .../ru/docs/protocol/operations/validators.md | 166 +++++++ @l10n/ru/docs/protocol/virtual-operations.md | 354 ++++++++++++++ @l10n/ru/docs/storage/block-log.md | 166 +++++++ @l10n/ru/docs/storage/shared-memory.md | 150 ++++++ @l10n/ru/docs/storage/snapshots.md | 286 +++++++++++ @l10n/zh-CN/docs/advanced/database-schema.md | 299 ++++++++++++ @l10n/zh-CN/docs/advanced/dlt-architecture.md | 248 ++++++++++ .../docs/advanced/hardfork-management.md | 199 ++++++++ @l10n/zh-CN/docs/advanced/security.md | 162 ++++++ @l10n/zh-CN/docs/api/cli-wallet.md | 251 ++++++++++ @l10n/zh-CN/docs/api/client-libraries.md | 140 ++++++ @l10n/zh-CN/docs/api/json-rpc.md | 228 +++++++++ .../zh-CN/docs/consensus/block-processing.md | 259 ++++++++++ .../docs/consensus/emergency-consensus.md | 221 +++++++++ @l10n/zh-CN/docs/consensus/fair-dpos.md | 170 +++++++ @l10n/zh-CN/docs/consensus/fork-resolution.md | 180 +++++++ @l10n/zh-CN/docs/consensus/hardforks.md | 171 +++++++ @l10n/zh-CN/docs/development/building.md | 196 ++++++++ @l10n/zh-CN/docs/development/debugging.md | 186 +++++++ .../docs/development/plugin-development.md | 252 ++++++++++ @l10n/zh-CN/docs/development/testing.md | 159 ++++++ .../zh-CN/docs/governance/chain-properties.md | 158 ++++++ @l10n/zh-CN/docs/governance/committee.md | 116 +++++ .../zh-CN/docs/governance/staking-and-dao.md | 166 +++++++ @l10n/zh-CN/docs/introduction/architecture.md | 177 +++++++ @l10n/zh-CN/docs/introduction/key-concepts.md | 161 ++++++ @l10n/zh-CN/docs/introduction/what-is-viz.md | 121 +++++ @l10n/zh-CN/docs/node/building.md | 190 ++++++++ @l10n/zh-CN/docs/node/configuration.md | 223 +++++++++ @l10n/zh-CN/docs/node/docker.md | 183 +++++++ @l10n/zh-CN/docs/node/getting-started.md | 184 +++++++ @l10n/zh-CN/docs/node/monitoring.md | 313 ++++++++++++ @l10n/zh-CN/docs/node/snapshot.md | 377 ++++++++++++++ @l10n/zh-CN/docs/node/validator-guard.md | 138 ++++++ @l10n/zh-CN/docs/node/validator-node.md | 235 +++++++++ @l10n/zh-CN/docs/p2p/forward-mode.md | 214 ++++++++ @l10n/zh-CN/docs/p2p/messages.md | 459 +++++++++++++++++ @l10n/zh-CN/docs/p2p/overview.md | 286 +++++++++++ @l10n/zh-CN/docs/p2p/stats-reference.md | 233 +++++++++ @l10n/zh-CN/docs/p2p/sync-scenarios.md | 215 ++++++++ @l10n/zh-CN/docs/plugins/chain.md | 125 +++++ @l10n/zh-CN/docs/plugins/database-api.md | 408 ++++++++++++++++ @l10n/zh-CN/docs/plugins/overview.md | 379 ++++++++++++++ @l10n/zh-CN/docs/plugins/snapshot.md | 347 +++++++++++++ @l10n/zh-CN/docs/plugins/validator.md | 226 +++++++++ @l10n/zh-CN/docs/plugins/webserver.md | 117 +++++ @l10n/zh-CN/docs/protocol/data-types.md | 257 ++++++++++ .../protocol/operations/account-market.md | 120 +++++ .../docs/protocol/operations/accounts.md | 96 ++++ .../zh-CN/docs/protocol/operations/awards.md | 94 ++++ .../docs/protocol/operations/committee.md | 103 ++++ .../zh-CN/docs/protocol/operations/content.md | 104 ++++ .../zh-CN/docs/protocol/operations/escrow.md | 158 ++++++ .../zh-CN/docs/protocol/operations/invites.md | 117 +++++ .../docs/protocol/operations/overview.md | 111 +++++ .../docs/protocol/operations/proposals.md | 125 +++++ .../docs/protocol/operations/recovery.md | 106 ++++ .../docs/protocol/operations/subscriptions.md | 77 +++ .../docs/protocol/operations/transfers.md | 136 ++++++ .../docs/protocol/operations/validators.md | 166 +++++++ .../zh-CN/docs/protocol/virtual-operations.md | 354 ++++++++++++++ @l10n/zh-CN/docs/storage/block-log.md | 166 +++++++ @l10n/zh-CN/docs/storage/shared-memory.md | 150 ++++++ @l10n/zh-CN/docs/storage/snapshots.md | 286 +++++++++++ docs/PLAN.md | 285 +++++++++++ docs/advanced/database-schema.md | 299 ++++++++++++ docs/advanced/dlt-architecture.md | 248 ++++++++++ docs/advanced/hardfork-management.md | 199 ++++++++ docs/advanced/security.md | 162 ++++++ docs/api/cli-wallet.md | 251 ++++++++++ docs/api/client-libraries.md | 140 ++++++ docs/api/json-rpc.md | 228 +++++++++ docs/consensus/block-processing.md | 259 ++++++++++ docs/consensus/emergency-consensus.md | 221 +++++++++ docs/consensus/fair-dpos.md | 170 +++++++ docs/consensus/fork-resolution.md | 180 +++++++ docs/consensus/hardforks.md | 171 +++++++ docs/development/building.md | 196 ++++++++ docs/development/debugging.md | 186 +++++++ docs/development/plugin-development.md | 252 ++++++++++ docs/development/testing.md | 159 ++++++ docs/governance/chain-properties.md | 158 ++++++ docs/governance/committee.md | 116 +++++ docs/governance/staking-and-dao.md | 166 +++++++ docs/introduction/architecture.md | 177 +++++++ docs/introduction/key-concepts.md | 161 ++++++ docs/introduction/what-is-viz.md | 121 +++++ docs/node/building.md | 190 ++++++++ docs/node/configuration.md | 223 +++++++++ docs/node/docker.md | 183 +++++++ docs/node/getting-started.md | 184 +++++++ docs/node/monitoring.md | 313 ++++++++++++ docs/node/snapshot.md | 377 ++++++++++++++ docs/node/validator-guard.md | 138 ++++++ docs/node/validator-node.md | 235 +++++++++ docs/p2p/forward-mode.md | 214 ++++++++ docs/p2p/messages.md | 459 +++++++++++++++++ docs/p2p/overview.md | 287 +++++++++++ docs/p2p/stats-reference.md | 234 +++++++++ docs/p2p/sync-scenarios.md | 218 +++++++++ docs/plugins/chain.md | 125 +++++ docs/plugins/database-api.md | 408 ++++++++++++++++ docs/plugins/overview.md | 379 ++++++++++++++ docs/plugins/snapshot.md | 347 +++++++++++++ docs/plugins/validator.md | 226 +++++++++ docs/plugins/webserver.md | 117 +++++ docs/protocol/data-types.md | 257 ++++++++++ docs/protocol/operations/account-market.md | 120 +++++ docs/protocol/operations/accounts.md | 96 ++++ docs/protocol/operations/awards.md | 94 ++++ docs/protocol/operations/committee.md | 103 ++++ docs/protocol/operations/content.md | 104 ++++ docs/protocol/operations/escrow.md | 158 ++++++ docs/protocol/operations/invites.md | 117 +++++ docs/protocol/operations/overview.md | 111 +++++ docs/protocol/operations/proposals.md | 125 +++++ docs/protocol/operations/recovery.md | 106 ++++ docs/protocol/operations/subscriptions.md | 77 +++ docs/protocol/operations/transfers.md | 136 ++++++ docs/protocol/operations/validators.md | 166 +++++++ docs/protocol/virtual-operations.md | 354 ++++++++++++++ docs/storage/block-log.md | 166 +++++++ docs/storage/shared-memory.md | 150 ++++++ docs/storage/snapshots.md | 286 +++++++++++ package.json | 13 + redocly.yaml | 189 +++++++ 181 files changed, 35936 insertions(+) create mode 100644 .github/workflows/docs.yml create mode 100644 @l10n/ru/docs/advanced/database-schema.md create mode 100644 @l10n/ru/docs/advanced/dlt-architecture.md create mode 100644 @l10n/ru/docs/advanced/hardfork-management.md create mode 100644 @l10n/ru/docs/advanced/security.md create mode 100644 @l10n/ru/docs/api/cli-wallet.md create mode 100644 @l10n/ru/docs/api/client-libraries.md create mode 100644 @l10n/ru/docs/api/json-rpc.md create mode 100644 @l10n/ru/docs/consensus/block-processing.md create mode 100644 @l10n/ru/docs/consensus/emergency-consensus.md create mode 100644 @l10n/ru/docs/consensus/fair-dpos.md create mode 100644 @l10n/ru/docs/consensus/fork-resolution.md create mode 100644 @l10n/ru/docs/consensus/hardforks.md create mode 100644 @l10n/ru/docs/development/building.md create mode 100644 @l10n/ru/docs/development/debugging.md create mode 100644 @l10n/ru/docs/development/plugin-development.md create mode 100644 @l10n/ru/docs/development/testing.md create mode 100644 @l10n/ru/docs/governance/chain-properties.md create mode 100644 @l10n/ru/docs/governance/committee.md create mode 100644 @l10n/ru/docs/governance/staking-and-dao.md create mode 100644 @l10n/ru/docs/introduction/architecture.md create mode 100644 @l10n/ru/docs/introduction/key-concepts.md create mode 100644 @l10n/ru/docs/introduction/what-is-viz.md create mode 100644 @l10n/ru/docs/node/building.md create mode 100644 @l10n/ru/docs/node/configuration.md create mode 100644 @l10n/ru/docs/node/docker.md create mode 100644 @l10n/ru/docs/node/getting-started.md create mode 100644 @l10n/ru/docs/node/monitoring.md create mode 100644 @l10n/ru/docs/node/snapshot.md create mode 100644 @l10n/ru/docs/node/validator-guard.md create mode 100644 @l10n/ru/docs/node/validator-node.md create mode 100644 @l10n/ru/docs/p2p/forward-mode.md create mode 100644 @l10n/ru/docs/p2p/messages.md create mode 100644 @l10n/ru/docs/p2p/overview.md create mode 100644 @l10n/ru/docs/p2p/stats-reference.md create mode 100644 @l10n/ru/docs/p2p/sync-scenarios.md create mode 100644 @l10n/ru/docs/plugins/chain.md create mode 100644 @l10n/ru/docs/plugins/database-api.md create mode 100644 @l10n/ru/docs/plugins/overview.md create mode 100644 @l10n/ru/docs/plugins/snapshot.md create mode 100644 @l10n/ru/docs/plugins/validator.md create mode 100644 @l10n/ru/docs/plugins/webserver.md create mode 100644 @l10n/ru/docs/protocol/data-types.md create mode 100644 @l10n/ru/docs/protocol/operations/account-market.md create mode 100644 @l10n/ru/docs/protocol/operations/accounts.md create mode 100644 @l10n/ru/docs/protocol/operations/awards.md create mode 100644 @l10n/ru/docs/protocol/operations/committee.md create mode 100644 @l10n/ru/docs/protocol/operations/content.md create mode 100644 @l10n/ru/docs/protocol/operations/escrow.md create mode 100644 @l10n/ru/docs/protocol/operations/invites.md create mode 100644 @l10n/ru/docs/protocol/operations/overview.md create mode 100644 @l10n/ru/docs/protocol/operations/proposals.md create mode 100644 @l10n/ru/docs/protocol/operations/recovery.md create mode 100644 @l10n/ru/docs/protocol/operations/subscriptions.md create mode 100644 @l10n/ru/docs/protocol/operations/transfers.md create mode 100644 @l10n/ru/docs/protocol/operations/validators.md create mode 100644 @l10n/ru/docs/protocol/virtual-operations.md create mode 100644 @l10n/ru/docs/storage/block-log.md create mode 100644 @l10n/ru/docs/storage/shared-memory.md create mode 100644 @l10n/ru/docs/storage/snapshots.md create mode 100644 @l10n/zh-CN/docs/advanced/database-schema.md create mode 100644 @l10n/zh-CN/docs/advanced/dlt-architecture.md create mode 100644 @l10n/zh-CN/docs/advanced/hardfork-management.md create mode 100644 @l10n/zh-CN/docs/advanced/security.md create mode 100644 @l10n/zh-CN/docs/api/cli-wallet.md create mode 100644 @l10n/zh-CN/docs/api/client-libraries.md create mode 100644 @l10n/zh-CN/docs/api/json-rpc.md create mode 100644 @l10n/zh-CN/docs/consensus/block-processing.md create mode 100644 @l10n/zh-CN/docs/consensus/emergency-consensus.md create mode 100644 @l10n/zh-CN/docs/consensus/fair-dpos.md create mode 100644 @l10n/zh-CN/docs/consensus/fork-resolution.md create mode 100644 @l10n/zh-CN/docs/consensus/hardforks.md create mode 100644 @l10n/zh-CN/docs/development/building.md create mode 100644 @l10n/zh-CN/docs/development/debugging.md create mode 100644 @l10n/zh-CN/docs/development/plugin-development.md create mode 100644 @l10n/zh-CN/docs/development/testing.md create mode 100644 @l10n/zh-CN/docs/governance/chain-properties.md create mode 100644 @l10n/zh-CN/docs/governance/committee.md create mode 100644 @l10n/zh-CN/docs/governance/staking-and-dao.md create mode 100644 @l10n/zh-CN/docs/introduction/architecture.md create mode 100644 @l10n/zh-CN/docs/introduction/key-concepts.md create mode 100644 @l10n/zh-CN/docs/introduction/what-is-viz.md create mode 100644 @l10n/zh-CN/docs/node/building.md create mode 100644 @l10n/zh-CN/docs/node/configuration.md create mode 100644 @l10n/zh-CN/docs/node/docker.md create mode 100644 @l10n/zh-CN/docs/node/getting-started.md create mode 100644 @l10n/zh-CN/docs/node/monitoring.md create mode 100644 @l10n/zh-CN/docs/node/snapshot.md create mode 100644 @l10n/zh-CN/docs/node/validator-guard.md create mode 100644 @l10n/zh-CN/docs/node/validator-node.md create mode 100644 @l10n/zh-CN/docs/p2p/forward-mode.md create mode 100644 @l10n/zh-CN/docs/p2p/messages.md create mode 100644 @l10n/zh-CN/docs/p2p/overview.md create mode 100644 @l10n/zh-CN/docs/p2p/stats-reference.md create mode 100644 @l10n/zh-CN/docs/p2p/sync-scenarios.md create mode 100644 @l10n/zh-CN/docs/plugins/chain.md create mode 100644 @l10n/zh-CN/docs/plugins/database-api.md create mode 100644 @l10n/zh-CN/docs/plugins/overview.md create mode 100644 @l10n/zh-CN/docs/plugins/snapshot.md create mode 100644 @l10n/zh-CN/docs/plugins/validator.md create mode 100644 @l10n/zh-CN/docs/plugins/webserver.md create mode 100644 @l10n/zh-CN/docs/protocol/data-types.md create mode 100644 @l10n/zh-CN/docs/protocol/operations/account-market.md create mode 100644 @l10n/zh-CN/docs/protocol/operations/accounts.md create mode 100644 @l10n/zh-CN/docs/protocol/operations/awards.md create mode 100644 @l10n/zh-CN/docs/protocol/operations/committee.md create mode 100644 @l10n/zh-CN/docs/protocol/operations/content.md create mode 100644 @l10n/zh-CN/docs/protocol/operations/escrow.md create mode 100644 @l10n/zh-CN/docs/protocol/operations/invites.md create mode 100644 @l10n/zh-CN/docs/protocol/operations/overview.md create mode 100644 @l10n/zh-CN/docs/protocol/operations/proposals.md create mode 100644 @l10n/zh-CN/docs/protocol/operations/recovery.md create mode 100644 @l10n/zh-CN/docs/protocol/operations/subscriptions.md create mode 100644 @l10n/zh-CN/docs/protocol/operations/transfers.md create mode 100644 @l10n/zh-CN/docs/protocol/operations/validators.md create mode 100644 @l10n/zh-CN/docs/protocol/virtual-operations.md create mode 100644 @l10n/zh-CN/docs/storage/block-log.md create mode 100644 @l10n/zh-CN/docs/storage/shared-memory.md create mode 100644 @l10n/zh-CN/docs/storage/snapshots.md create mode 100644 docs/PLAN.md create mode 100644 docs/advanced/database-schema.md create mode 100644 docs/advanced/dlt-architecture.md create mode 100644 docs/advanced/hardfork-management.md create mode 100644 docs/advanced/security.md create mode 100644 docs/api/cli-wallet.md create mode 100644 docs/api/client-libraries.md create mode 100644 docs/api/json-rpc.md create mode 100644 docs/consensus/block-processing.md create mode 100644 docs/consensus/emergency-consensus.md create mode 100644 docs/consensus/fair-dpos.md create mode 100644 docs/consensus/fork-resolution.md create mode 100644 docs/consensus/hardforks.md create mode 100644 docs/development/building.md create mode 100644 docs/development/debugging.md create mode 100644 docs/development/plugin-development.md create mode 100644 docs/development/testing.md create mode 100644 docs/governance/chain-properties.md create mode 100644 docs/governance/committee.md create mode 100644 docs/governance/staking-and-dao.md create mode 100644 docs/introduction/architecture.md create mode 100644 docs/introduction/key-concepts.md create mode 100644 docs/introduction/what-is-viz.md create mode 100644 docs/node/building.md create mode 100644 docs/node/configuration.md create mode 100644 docs/node/docker.md create mode 100644 docs/node/getting-started.md create mode 100644 docs/node/monitoring.md create mode 100644 docs/node/snapshot.md create mode 100644 docs/node/validator-guard.md create mode 100644 docs/node/validator-node.md create mode 100644 docs/p2p/forward-mode.md create mode 100644 docs/p2p/messages.md create mode 100644 docs/p2p/overview.md create mode 100644 docs/p2p/stats-reference.md create mode 100644 docs/p2p/sync-scenarios.md create mode 100644 docs/plugins/chain.md create mode 100644 docs/plugins/database-api.md create mode 100644 docs/plugins/overview.md create mode 100644 docs/plugins/snapshot.md create mode 100644 docs/plugins/validator.md create mode 100644 docs/plugins/webserver.md create mode 100644 docs/protocol/data-types.md create mode 100644 docs/protocol/operations/account-market.md create mode 100644 docs/protocol/operations/accounts.md create mode 100644 docs/protocol/operations/awards.md create mode 100644 docs/protocol/operations/committee.md create mode 100644 docs/protocol/operations/content.md create mode 100644 docs/protocol/operations/escrow.md create mode 100644 docs/protocol/operations/invites.md create mode 100644 docs/protocol/operations/overview.md create mode 100644 docs/protocol/operations/proposals.md create mode 100644 docs/protocol/operations/recovery.md create mode 100644 docs/protocol/operations/subscriptions.md create mode 100644 docs/protocol/operations/transfers.md create mode 100644 docs/protocol/operations/validators.md create mode 100644 docs/protocol/virtual-operations.md create mode 100644 docs/storage/block-log.md create mode 100644 docs/storage/shared-memory.md create mode 100644 docs/storage/snapshots.md create mode 100644 package.json create mode 100644 redocly.yaml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000000..b23389acd9 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,48 @@ +name: Deploy Docs + +on: + push: + branches: [master] + paths: + - 'docs/**' + - '@l10n/**' + - 'redocly.yaml' + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - run: npm ci + + - run: npm run build + + - uses: actions/upload-pages-artifact@v3 + with: + path: dist + + deploy: + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - uses: actions/deploy-pages@v4 + id: deployment diff --git a/@l10n/ru/docs/advanced/database-schema.md b/@l10n/ru/docs/advanced/database-schema.md new file mode 100644 index 0000000000..e3fd80fa25 --- /dev/null +++ b/@l10n/ru/docs/advanced/database-schema.md @@ -0,0 +1,297 @@ +# Схема базы данных + +VIZ Ledger использует ChainBase — персистентное хранилище с отображением в память и несколькими индексами, построенное на Boost.Interprocess. Всё состояние цепочки находится в `shared_memory.bin`. Каждый тип объекта ассоциирован с контейнером Boost.MultiIndex, определяющим его первичные и вторичные индексы. + +--- + +## Реестр типов объектов + +Каждый персистентный объект имеет уникальный числовой type ID, объявленный в `chain_object_types.hpp`. Полный набор отслеживаемых типов объектов: + +| Объект | Примечания | +|--------|-----------| +| `dynamic_global_property` | Синглтон: текущее состояние цепочки, head-блок, LIB, инфляция | +| `account` | Все зарегистрированные аккаунты | +| `account_authority` | Наборы authority master/active/regular | +| `witness` (валидатор) | Регистрации валидаторов, ключи подписи, счётчики голосов | +| `transaction` | Ожидающие/недавние транзакции (окно TAPOS) | +| `block_summary` | 65536-слотовый TAPOS-буфер ID блоков | +| `witness_schedule` | Синглтон: расписание активных валидаторов | +| `content` | Посты и комментарии (устарело) | +| `content_type` | Метаданные заголовка/тела контента | +| `content_vote` | Голоса за контент | +| `witness_vote` | Голоса за валидаторов от аккаунтов | +| `hardfork_property` | Синглтон: отслеживание текущего/следующего хардфорка | +| `withdraw_vesting_route` | Правила маршрутизации вывода | +| `master_authority_history` | История изменений master-ключей | +| `account_recovery_request` | Ожидающие запросы восстановления аккаунта | +| `change_recovery_account_request` | Ожидающие изменения аккаунта восстановления | +| `escrow` | Эскроу-переводы | +| `vesting_delegation` | Активные делегирования SHARES | +| `fix_vesting_delegation` | Записи исправления делегирований | +| `vesting_delegation_expiration` | Делегирования в окне возврата | +| `account_metadata` | JSON-метаданные аккаунта | +| `proposal` | Управленческие предложения | +| `required_approval` | Требования одобрения предложений | +| `committee_request` | Запросы на финансирование комитета | +| `committee_vote` | Голоса комитета | +| `invite` | Инвайты аккаунтов | +| `award_shares_expire` | Истекающие наградные SHARES | +| `paid_subscription` | Предложения подписок | +| `paid_subscribe` | Активные подписки | +| `witness_penalty_expire` | Истечения штрафов за пропуски валидатора | +| `block_post_validation` | Записи поствалидации блоков | + +--- + +## Объект аккаунта + +Аккаунты хранят балансы, состояние вестинга, метрики делегирования, пропускную способность, флаги аукциона/продажи и участие в управлении. + +**Ключевые поля:** `name`, `balance` (VIZ), `vesting_shares`, `delegated_vesting_shares`, `received_vesting_shares`, `energy`, `next_vesting_withdrawal`, `witnesses_voted_for`, `recovery_account`. + +**Индексы:** + +| Тег | Ключ | Тип | +|-----|------|-----| +| `by_id` | `id` | уникальный | +| `by_name` | `name` | уникальный | +| `by_account_on_sale` | флаг продажи | неуникальный | +| `by_account_on_auction` | флаг аукциона | неуникальный | +| `by_account_on_sale_start_time` | время начала продажи | неуникальный | +| `by_subaccount_on_sale` | флаг продажи субаккаунта | неуникальный | +| `by_next_vesting_withdrawal` | `(next_vesting_withdrawal, id)` | составной | + +Составной индекс `by_next_vesting_withdrawal` обеспечивает пакетную обработку предстоящих выплат вывода за O(log N). + +--- + +## Объект контента + +Объекты контента представляют посты и комментарии с метаданными голосования, выплат и вложенности. **Эти объекты устарели** — новые приложения должны использовать `custom_operation`. + +**Индексы на `content`:** + +| Тег | Ключ | +|-----|------| +| `by_id` | `id` | +| `by_cashout_time` | `(cashout_time, id)` | +| `by_permlink` | `(author, permlink)` | +| `by_root` | `(root_content, id)` | +| `by_parent` | `(parent_author, parent_permlink, id)` | +| `by_last_update` | `(parent_author, last_update, id)` — нагружает API | +| `by_author_last_update` | `(author, last_update, id)` — нагружает API | + +**Индексы на `content_vote`:** + +| Тег | Ключ | +|-----|------| +| `by_id` | `id` | +| `by_content_voter` | `(content, voter)` — уникальный | +| `by_voter_content` | `(voter, content)` — уникальный | +| `by_voter_last_update` | `(voter, last_update, content)` | +| `by_content_weight_voter` | `(content, weight, voter)` — для лидербордов | + +--- + +## Объекты валидатора (Witness) + +**Индексы `witness_object`:** + +| Тег | Ключ | +|-----|------| +| `by_id` | `id` | +| `by_name` | `owner` — уникальный | +| `by_vote_name` | `(votes, owner)` | +| `by_counted_vote_name` | `(counted_votes, owner)` | +| `by_schedule_time` | `(virtual_scheduled_time, id)` — O(log N) планирование слотов | + +**Индексы `witness_vote_object`:** + +| Тег | Ключ | +|-----|------| +| `by_id` | `id` | +| `by_account_witness` | `(account, validator)` — уникальный | +| `by_witness_account` | `(validator, account)` — уникальный | + +Индекс `by_schedule_time` используется планировщиком производства блоков для выбора следующего валидатора за O(log N). + +--- + +## Объекты предложений и требуемых одобрений + +**Индексы `proposal_object`:** + +| Тег | Ключ | +|-----|------| +| `by_id` | `id` | +| `by_account` | `(author, title)` — уникальный | +| `by_expiration` | `expiration` — неуникальный | + +**Индексы `required_approval_object`:** + +| Тег | Ключ | +|-----|------| +| `by_id` | `id` | +| `by_account` | `(account, proposal)` | + +--- + +## Объект инвайта + +| Тег | Ключ | +|-----|------| +| `by_id` | `id` | +| `by_invite_key` | публичный ключ — неуникальный | +| `by_status` | статус — неуникальный | +| `by_creator` | создатель — неуникальный | +| `by_receiver` | получатель — неуникальный | + +--- + +## Вспомогательные объекты + +**`withdraw_vesting_route`:** + +| Тег | Ключ | +|-----|------| +| `by_withdraw_route` | `(from_account, to_account)` — уникальный | +| `by_destination` | `(to_account, id)` | + +**`escrow`:** + +| Тег | Ключ | +|-----|------| +| `by_from_id` | `(from, escrow_id)` — уникальный | +| `by_to` | `(to, id)` | +| `by_agent` | `(agent, id)` | +| `by_ratification_deadline` | `(is_approved, ratification_deadline, id)` | + +**`vesting_delegation`:** + +| Тег | Ключ | +|-----|------| +| `by_delegation` | `(delegator, delegatee)` — уникальный | + +**`vesting_delegation_expiration`:** + +| Тег | Ключ | +|-----|------| +| `by_expiration` | `expiration` — неуникальный | +| `by_account_expiration` | `(delegator, expiration)` | + +--- + +## Fork-база данных + +Fork-база данных (`fork_database`) поддерживает дерево блоков в памяти для управления форками цепочки. Работает отдельно от персистентного хранилища chainbase. + +**Связанный индекс** — блоки канонической цепочки, индексированные по ID и номеру блока. +**Несвязанный индекс** — осиротевшие или неупорядоченные блоки, родитель которых ещё не известен. + +``` +Добавление блока + ├── Родитель известен в связанном индексе? + │ ДА → связать блок, вставить в связанный индекс, обновить head + │ НЕТ → вставить в несвязанный индекс + └── Попытаться связать ожидающие несвязанные блоки +``` + +Когда поступает новый блок, ID которого совпадает с родителем несвязанного блока, `_push_next()` каскадно обходит несвязанный индекс и продвигает эти блоки в связанную цепочку. + +**Операции с ветками:** +- `fetch_branch_from(first, second)` — обходит обе ветки для поиска общего предка. Возвращает `(first_branch, second_branch)` для переключения форков. +- `set_max_size(n)` — усекает блоки старше n, ограничивает потребление памяти. +- `walk_main_branch_to_num(n)` — итерирует главную цепочку до определённого номера блока. + +**Валидность блока:** Блоки, помеченные как невалидные, никогда не продвигаются. Добавление блока за пределами максимального окна переупорядочивания вызывает assert. + +--- + +## Управление индексами + +Основные индексы регистрируются в `database::initialize_indexes()`. Плагины регистрируют дополнительные индексы через `add_plugin_index()` в `plugin_startup()`. + +```cpp +// Регистрация основных индексов (database.cpp) +add_core_index(); +add_core_index(); +// ... + +// Регистрация индексов плагинов (запуск плагина) +db.add_plugin_index(); +``` + +--- + +## Связи объектов + +``` +account ──(author)──► content ──► content_vote ◄──(voter)── account +account ──(delegator)──► vesting_delegation ──► account (delegatee) +account ──(account)──► witness_vote ──► witness (validator) +account ──(author)──► proposal ──► required_approval ◄──(account)── account +account ──(creator/receiver)──► invite +escrow: from + to + agent → escrow_object +``` + +--- + +## Руководство по оптимизации запросов + +**Быстрые поиски:** +- Аккаунт по имени → `by_name` (уникальный, O(log N)) +- Расписание валидаторов → `by_schedule_time` (упорядочен по виртуальному времени) +- Контент по author+permlink → `by_permlink` (уникальный составной) +- Голоса по content+weight → `by_content_weight_voter` (лидерборды) + +**Пакетная обработка:** +- Выводы вестинга → итерировать `by_next_vesting_withdrawal` вперёд +- Истекающие делегирования → итерировать `by_expiration` вперёд +- Истекающие предложения → итерировать `by_expiration` вперёд + +**Избегайте полного сканирования:** всегда используйте тег с индексом. Составные индексы упорядочены прежде всего по крайнему левому ключу — ставьте наиболее селективное или часто фильтруемое поле первым. + +--- + +## Расширение схемы для плагинов + +Для добавления пользовательского типа объекта: + +1. Определить класс объекта, наследующий от `chainbase::object`. +2. Объявить `chainbase::shared_multi_index_container` с нужными индексами. +3. Зарегистрировать через `db.add_plugin_index()` в `plugin_startup()`. +4. Добавить макросы `FC_REFLECT` для сериализации. + +```cpp +class my_object : public chainbase::object { + id_type id; + account_name_type account; + uint64_t value; +}; + +using my_index = chainbase::shared_multi_index_container< + my_object, + indexed_by< + ordered_unique, + member>, + ordered_unique, + member> + > +>; +``` + +--- + +## Эволюция схемы + +Новый хардфорк → новые поля или объекты. Руководящие принципы: + +- Сохранять семантику первичных ключей стабильной между хардфорками. +- Добавлять новые поля как опциональные или с умолчаниями; никогда не менять существующий порядок полей. +- Ограждать использование новых индексов проверками `has_hardfork()` при реплее. +- Добавлять новые теги MultiIndex рядом с существующими — никогда не удалять тег, который могут запрашивать реплеирующие узлы. + +--- + +См. также: [Разработка плагинов](../development/plugin-development.md), [Виртуальные операции](../protocol/virtual-operations.md), [Управление хардфорками](./hardfork-management.md). diff --git a/@l10n/ru/docs/advanced/dlt-architecture.md b/@l10n/ru/docs/advanced/dlt-architecture.md new file mode 100644 index 0000000000..0bf6c03673 --- /dev/null +++ b/@l10n/ru/docs/advanced/dlt-architecture.md @@ -0,0 +1,248 @@ +# Архитектура DLT P2P + +P2P-слой VIZ Ledger был переработан с устаревшего протокола Graphene на основе synopsis (`node.cpp`) на специализированный DLT-нативный протокол (`dlt_p2p_node.cpp`). Публичный API плагина остался неизменным — заменена только внутренняя реализация. + +--- + +## Обзор + +``` +До: p2p_plugin → graphene::network::node (node.cpp, 6978 строк, STCP, gossip-инвентарь) +После: p2p_plugin → dlt_p2p_node (dlt_p2p_node.cpp, 2627 строк, raw TCP, диапазонная синхронизация) +``` + +Замена выполнена **на месте**: то же имя плагина `"p2p"`, тот же порт (2001/4243), тот же публичный API. Все зависимые плагины (валидатор, snapshot и т.п.) не потребовали изменений. + +--- + +## Проводной протокол + +Чистый TCP — без уровня шифрования STCP. Каждое сообщение на канале: + +``` +[4 байта: размер данных (uint32_t)] [4 байта: msg_type (uint32_t)] [N байт: fc::raw::pack(T)] +``` + +### Типы сообщений (5100–5116) + +| Тип | ID | Описание | +|-----|----|---------| +| `dlt_hello_message` | 5100 | Рукопожатие: версия протокола, head/LIB, DLT-диапазон, статус узла/форка | +| `dlt_hello_reply_message` | 5101 | Ответ на рукопожатие: exchange_enabled, fork_alignment | +| `dlt_range_request_message` | 5102 | Запрос диапазона ID блоков | +| `dlt_range_reply_message` | 5103 | Ответ с доступным диапазоном блоков | +| `dlt_get_block_range_message` | 5104 | Получить блоки start..end с проверкой prev_block_id | +| `dlt_block_range_reply_message` | 5105 | Ответ: вектор блоков + флаг is_last | +| `dlt_get_block_message` | 5106 | Получить один блок по ID | +| `dlt_block_reply_message` | 5107 | Ответ: блок + next_available + is_last | +| `dlt_not_available_message` | 5108 | Блок недоступен | +| `dlt_fork_status_message` | 5109 | Трансляция текущего статуса форка/узла пирам | +| `dlt_peer_exchange_request` | 5110 | Запрос списка известных пиров | +| `dlt_peer_exchange_reply` | 5111 | Ответ со списком пиров | +| `dlt_peer_exchange_rate_limited` | 5112 | Уведомление об ограничении скорости: ожидать N секунд | +| `dlt_transaction_message` | 5113 | Трансляция подписанной транзакции | +| `dlt_soft_ban_message` | 5114 | Уведомление перед отключением забаненного пира | +| `dlt_gap_fill_request` | 5115 | Запрос конкретных номеров блоков для заполнения пробела | +| `dlt_gap_fill_reply` | 5116 | Ответ с запрошенными блоками | + +--- + +## Fiber-архитектура + +Весь ввод/вывод выполняется в одном `fc::thread` с использованием кооперативных файберов — мьютексы для разделяемого состояния не нужны: + +| Файбер | Роль | +|--------|------| +| Цикл принятия | Ожидает входящие соединения; отклоняет дублирующиеся IP | +| Цикл чтения (на пира) | Читает сообщения; диспетчеризует в `on_message()` | +| Периодическая задача | Переподключение, проверка стагнации, статистика пиров, очистка mempool | + +Файберы уступают управление на блокирующем вводе/выводе (`readsome()`, `writesome()`), позволяя работать с несколькими пирами в одном потоке без конкуренции. + +--- + +## Статусы узла и жизненный цикл пира + +**Статусы узла:** `SYNC` (догоняет) / `FORWARD` (живой, обменивается блоками) + +**Состояния жизненного цикла пира:** +``` +CONNECTING → HANDSHAKING → SYNCING → ACTIVE → DISCONNECTED → BANNED +``` + +Таймауты: connecting=5с, handshaking=10с. Откат переподключения: 30с → 60с → … → 3600с с джиттером ±25%, сброс после 5 минут стабильного аптайма. Пиры удаляются после 8 часов без ответа. + +--- + +## Синхронизация блоков: режим SYNC + +Узел в режиме SYNC последовательно получает блоки от пира с более высоким head: + +1. `request_blocks_from_peer()` — отправляет `dlt_get_block_range_message` для до 200 блоков после нашего head. +2. `on_dlt_block_range_reply()` — валидирует хеш-цепочку `prev_block_id`, применяет каждый блок. +3. `check_sync_catchup()` — сравнивает наш head с head'ами всех пиров; переходит в FORWARD после синхронизации. +4. `sync_stagnation_check()` — после 30с без нового блока повторяет до 3 раз, затем переходит в FORWARD с предупреждением. + +### Заполнение пробелов + +Когда между нашим head и ранним доступным блоком синхронизирующегося пира существует непрерывный пробел, `request_gap_fill()` отправляет `dlt_gap_fill_request` (до 100 блоков на запрос) любому пиру, чей DLT-диапазон покрывает пробел. Заполнение пробелов работает как в SYNC, так и в FORWARD режиме: + +- Запускается из `on_dlt_block_reply()` (обнаружен блок не в порядке) и `periodic_task()` (каждые 5с). +- Откат от пиров с включённым обменом к любому активному пиру с более высоким head. +- Откат в режим SYNC, если ни один пир не имеет нужных блоков. +- Большие пробелы обрабатываются кусками по 100 блоков с задержкой 5с между запросами. + +--- + +## Обмен блоками: режим FORWARD + +В режиме FORWARD пиры обмениваются живыми блоками и транзакциями: + +- Флаг `exchange_enabled` контролирует, получает ли пир от нас новые блоки. +- При переходе в FORWARD, `dlt_fork_status_message` отправляется **всем** пирам (не только с включённым обменом), уведомляя их о готовности. +- `on_dlt_fork_status()` пересчитывает `exchange_enabled` при переходе пира из SYNC в FORWARD. +- `check_forward_stagnation()` — если head не продвигался 30с И хотя бы один пир опережает, переходит в SYNC. + +--- + +## Выравнивание форков и право на обмен + +В процессе рукопожатия `check_fork_alignment()` выполняет многоуровневое сопоставление ID блоков для определения, находятся ли пиры на одном форке: + +| Проверка | Условие | +|---------|---------| +| Пустой пир | `head_block_num == 0` → выровнен (новый узел) | +| Перекрытие диапазонов | Наш DLT-лог покрывает head пира → `is_block_known(head_id)` | +| Граничная связь | `peer_head + 1 == our_earliest` → проверить `previous` нашего раннего блока == `peer_head_id` | +| Откат к LIB | Всегда проверять `is_block_known(lib_id)` | + +Эта многоуровневая проверка предотвращает ложные отключения "разные форки" в DLT-режиме, где старые блоки обрезаны, и старая проверка по одному ID не прошла бы для пиров на одной цепочке. + +--- + +## Разрешение форков + +Подсистема разрешения форков отслеживает конкурирующие верхушки цепочки: + +- **Порог:** 42 блока расхождения запускают `resolve_fork()` (= `CHAIN_MAX_WITNESSES × 2`, один полный оборот расписания). +- **Выбор:** Наиболее тяжёлая ветка по весу голосов. +- **Гистерезис:** 6 последовательных блоков как победитель перед переключением (`CONFIRMATION_BLOCKS`). +- **Статус:** `_fork_status` открыт через `is_on_majority_fork()` для плагина валидатора для проверки перед производством блоков. + +--- + +## Антиспам + +| Механизм | Описание | +|---------|---------| +| Счётчик `spam_strikes` | Единый счётчик на пира; сбрасывается на хорошем пакете; мягкий бан при пороге=10 | +| Мягкий бан | Устанавливает состояние BANNED на 3600с; отправляет `dlt_soft_ban_message` перед закрытием | +| Дедупликация по IP | Отклоняет дублирующиеся соединения с одного IP (как входящие, так и исходящие) | +| Дедупликация трансляций | `send_to_all_our_fork_peers()` отслеживает `std::set` для пропуска дублирующихся IP | + +Дублирующиеся блоки и блоки не в порядке из ответов на диапазонные запросы молча пропускаются — не считаются спамом. Ошибки десериализации не увеличивают счётчик spam strikes. + +--- + +## P2P Mempool + +Отдельный внутрипроцессный mempool (отличный от `_pending_tx` цепочки) обеспечивает раннюю фильтрацию транзакций до приёма цепочкой: + +- **Дедупликация** по `tx_id`. +- **Вытеснение** по старейшему истечению при достижении лимитов. +- **Лимиты** (настраиваемые): максимум 10 000 записей, 100 МБ всего, 64 КБ на транзакцию. +- **Предварительные записи** помечаются в режиме SYNC; повторно валидируются при переходе в FORWARD. +- **Очистка** при получении блока (`remove_transactions_in_block`) и переключении форка (`prune_mempool_on_fork_switch`). + +--- + +## Обмен пирами + +Ограниченное по скорости обнаружение пиров: + +- Максимум 3 запроса за 5-минутное окно на пира. +- Фильтр разнообразия подсетей: максимум 2 пира на префикс `/24` в каждом ответе. +- Обмениваются только пиры с аптаймом ≥600с. +- Входящие пиры (эфемерные порты) исключаются из ответов на обмен. + +--- + +## Механизмы восстановления + +### Изоляция пиров (P53) + +Когда в течение 60 секунд нет активных пиров, `emergency_peer_reset()`: +- Очищает все мягкие баны (BANNED → DISCONNECTED, сбрасывает spam strikes). +- Сбрасывает откаты всех отключённых пиров до минимума с немедленным переподключением. + +### Пауза/возобновление обработки блоков + +`pause_block_processing()` / `resume_block_processing()` позволяют плагину snapshot приостановить приём P2P-блоков во время сериализации состояния. Периодическая задача пропускает операции, обращающиеся к БД, в режиме паузы. + +### Начальный льготный период (P22) + +В течение первых 60 секунд после запуска блоки в пределах 10 от head обрабатываются как `FORK_DB_ONLY` вместо `DEAD_FORK` — предотвращая каскадные отклонения пока fork_db восстанавливается из block log. + +--- + +## Результаты приёма блоков + +Перечисление `dlt_block_accept_result` заменяет старый булев возврат: + +| Значение | Смысл | +|---------|------| +| `ACCEPTED` | Блок применён к цепочке (стал новым head) | +| `FORK_DB_ONLY` | Хранится в fork_db, но не применён (несвязываемый, конкурирующий форк) | +| `DEAD_FORK` | Блок на/ниже head из мёртвого форка — пир получает мягкий бан | +| `ALREADY_KNOWN` | Уже есть этот блок (дубликаты, `block_too_old_exception`) | +| `REJECTED` | Провал полной валидации | + +--- + +## Справочник конфигурации + +| Параметр | По умолчанию | Описание | +|---------|-------------|---------| +| `dlt-block-log-max-blocks` | 100 000 | Максимальное число блоков в скользящем DLT block log | +| `dlt-peer-max-disconnect-hours` | 8 | Удалить пира после N часов без ответа | +| `dlt-mempool-max-tx` | 10 000 | Жёсткий лимит записей mempool | +| `dlt-mempool-max-bytes` | 100 МБ | Жёсткий лимит памяти mempool | +| `dlt-mempool-max-tx-size` | 64 КБ | Отклонять слишком большие транзакции | +| `dlt-mempool-max-expiration-hours` | 24 | Отклонять транзакции с далёким будущим истечением | +| `dlt-peer-exchange-max-per-reply` | 10 | Максимум пиров в ответе на обмен | +| `dlt-peer-exchange-max-per-subnet` | 2 | Антисибил: максимум 2 пира на /24 | +| `dlt-peer-exchange-min-uptime-sec` | 600 | Минимальный аптайм перед обменом пиром | +| `dlt-stats-interval-sec` | 300 | Интервал лога статистики пиров (мин. 30) | + +--- + +## Цветовое логирование + +| Цвет | Значение | +|------|---------| +| Зелёный | Прогресс синхронизации и производство блоков | +| Белый | Нормальный обмен блоками | +| Красный | События форка | +| Тёмно-серый | Обработка транзакций | +| Оранжевый | Предупреждения (мягкие баны, стагнация, пробелы) | +| Голубой | Вывод статистики пиров | + +--- + +## Паттерн делегата + +Сетевая библиотека ссылается только на `fc` и `graphene_protocol` — не на `graphene_chain`. Абстрактный интерфейс `dlt_p2p_delegate` устраняет этот разрыв: + +``` +dlt_p2p_node (сетевая библиотека) ←→ dlt_p2p_delegate (интерфейс) ←→ dlt_delegate (p2p_plugin) +``` + +`dlt_delegate` в `p2p_plugin.cpp` реализует: +- `read_block_by_num()` — проверяет dlt_block_log, затем fork_db. +- `accept_block()` — вызывает `push_block()`; перехватывает `unlinkable_block_exception` → хранит в fork_db. +- `get_fork_branch_tips()` — получает из fork_db вокруг текущего head. +- `is_tapos_block_known()` — делегирует `db.is_known_block()`. + +--- + +См. также: [Обзор P2P](../p2p/overview.md), [Сценарии синхронизации](../p2p/sync-scenarios.md), [Плагин snapshot](../storage/snapshots.md), [Block log](../storage/block-log.md). diff --git a/@l10n/ru/docs/advanced/hardfork-management.md b/@l10n/ru/docs/advanced/hardfork-management.md new file mode 100644 index 0000000000..65ffb42999 --- /dev/null +++ b/@l10n/ru/docs/advanced/hardfork-management.md @@ -0,0 +1,199 @@ +# Управление хардфорками + +VIZ Ledger координирует обновления протокола через детерминированную систему хардфорков. Хардфорки определяются во время компиляции, активируются консенсусом валидаторов и применяются автоматически в процессе обработки блоков — без перезапуска узла. + +--- + +## Как это работает + +### 1. Файлы определений + +Каждый хардфорк имеет выделенный файл `*.hf` в `libraries/chain/hardfork.d/`, определяющий константы времени компиляции: + +```cpp +// Пример: 9.hf +#define CHAIN_HARDFORK_9 9 +#define CHAIN_HARDFORK_9_VERSION version(1, 0, 9) +#define CHAIN_HARDFORK_9_TIME (fc::time_point_sec(1650000000)) +``` + +Файл преамбулы (`0-preamble.hf`) объявляет общее количество и схему объекта hardfork_property. Текущее значение: `CHAIN_NUM_HARDFORKS = 12`. + +### 2. Консенсус валидаторов + +Валидаторы публикуют предпочтительную версию следующего хардфорка через `versioned_chain_properties_update_operation`. При каждом обновлении расписания валидаторов узел: + +1. Собирает версию хардфорка, которую поддерживает каждый активный валидатор. +2. Если большинство согласно на версию ≥ следующей запланированной, устанавливает `next_hardfork` и `next_hardfork_time`. + +### 3. Активация во время обработки блоков + +Когда время head-блока превышает `next_hardfork_time` и достаточное количество валидаторов поддерживает версию, узел вызывает `apply_hardfork(N)`. Все последующие изменения поведения управляются проверками `has_hardfork(N)` в эвалуаторах, логике инфляции и свойствах цепочки. + +--- + +## История хардфорков + +| HF | Ключевые изменения | +|----|-----------------| +| 1 | Исправление вычисления медианы | +| 2 | Исправление порога одобрения комитета | +| 3 | Незначительные исправления протокола | +| 4 | Операции наград, последовательности custom-операций | +| 5 | Исправления пропускной способности и authority | +| 6 | Штрафы за пропуски валидатора, подсчёт голосов | +| 7 | Корректировки социального/контентного слоя | +| 8 | Очистка протокола | +| 9 | Система инвайтов, платные подписки, комиссии валидатора, withdraw_intervals | +| 10 | Модель инфляции | +| 11 | Изменения модели эмиссии | +| 12 | Аварийное восстановление консенсуса (см. ниже) | + +--- + +## HF12: Аварийное восстановление консенсуса + +HF12 вводит автоматическое восстановление сети при остановке производства блоков. + +### Активация + +Если метка времени последнего необратимого блока (LIB) отстаёт от системных часов более чем на `CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC` (1 час), аварийный режим активируется автоматически. Создаётся аварийный аккаунт валидатора (`CHAIN_EMERGENCY_WITNESS_ACCOUNT = "committee"`) с известным публичным ключом (`CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY`) и вставляется в расписание производства блоков. + +### Трёхуровневое обеспечение безопасности + +| Состояние сети | Условие | Поведение | +|--------------|---------|----------| +| Здоровое | Участие ≥ 33% | Принудительно применяет безопасные умолчания; переопределяет ручную конфигурацию | +| Критическое | Участие < 33% | Уважает переопределения ручной конфигурации для восстановления оператором | +| Аварийное | Аварийный режим активен | Автоматически обходит проверки устаревания и участия | + +### Расширенное планирование валидаторов + +- **Гибридное расписание**: Аварийный валидатор заполняет недоступные слоты, сохраняя позиции реальных валидаторов. +- **Переключение форков с взвешиванием голосов**: В качестве основного критерия сравнения форков используется сумма сырых голосов от уникальных не-committee валидаторов. +- **Исключение из медианы**: Голоса аварийного валидатора за свойства исключаются из вычисления медианных параметров цепочки. + +--- + +## Объект hardfork_property + +Персистентный `hardfork_property_object` (синглтон в chainbase) отслеживает: + +| Поле | Описание | +|------|---------| +| `processed_hardforks` | Вектор времён применённых хардфорков | +| `last_hardfork` | ID последнего применённого хардфорка | +| `current_hardfork_version` | Текущая применяемая версия протокола | +| `next_hardfork` | Версия следующего запланированного хардфорка | +| `next_hardfork_time` | Когда активируется `next_hardfork` | + +Для HF12+ дополнительные поля отслеживают состояние аварийного консенсуса. + +--- + +## Жизненный цикл базы данных и хардфорки + +### При открытии + +``` +database::open() + → init_schema(), initialize_indexes(), initialize_evaluators() + → загрузить hardfork_property из chainbase + → init_hardforks() ← заполнить массивы _hardfork_times[] и _hardfork_versions[] + → assert: ревизия chainbase == head_block_num +``` + +### При переиндексации + +Реплей всех блоков из block_log с флагами пропуска (без проверки подписей, без merkle) для ускорения. `apply_hardfork()` срабатывает на каждой границе хардфорка во время реплея, обеспечивая детерминированную реконструкцию состояния. + +### При применении блока + +``` +process_hardforks() + → проверить, превышено ли next_hardfork_time + → проверить, поддерживает ли консенсус валидаторов версию + → если да: apply_hardfork(N) + → выполнить специфичные для версии миграции состояния + → обновить current_hardfork_version +``` + +--- + +## Откат и переключение форков + +База данных использует undo-сессии для атомарного применения блоков — частичные сбои откатываются чисто. + +При переключении форков `fetch_branch_from()` возвращает обе ветки к общему предку, откатывает текущую ветку и заново применяет новую. HF12 добавляет взвешенное по голосам сравнение цепочек в этом процессе. + +При сбое применения блока запись в fork-базе данных удаляется и выбрасывается исключение. P2P-слой обрабатывает исключение, соответствующим образом помечая отправившего пира. + +--- + +## Добавление нового хардфорка + +1. **Создать `N.hf`** в `libraries/chain/hardfork.d/`: + ```cpp + #define CHAIN_HARDFORK_N N + #define CHAIN_HARDFORK_N_VERSION version(1, 0, N) + #define CHAIN_HARDFORK_N_TIME (fc::time_point_sec(UNIX_TIMESTAMP)) + ``` + +2. **Увеличить `CHAIN_NUM_HARDFORKS`** в `0-preamble.hf` до N. + +3. **Управлять новым поведением** в эвалуаторах и логике времени выполнения: + ```cpp + if (db.has_hardfork(CHAIN_HARDFORK_N)) { + // новое поведение + } else { + // устаревшее поведение + } + ``` + +4. **Добавить миграции состояния** в `apply_hardfork(N)` в `database.cpp`: + ```cpp + case CHAIN_HARDFORK_N: + // однократный код миграции + break; + ``` + +5. **Учесть аварийный режим**: Если хардфорк изменяет планирование валидаторов или параметры цепочки, убедитесь, что аварийный валидатор исключён из затронутых вычислений. + +6. **Проверить с переиндексацией**: Выполнить полную переиндексацию на данных mainnet-блоков для подтверждения детерминированного реплея, воспроизводящего идентичное состояние. + +--- + +## Устранение неполадок + +| Симптом | Вероятная причина | Решение | +|---------|----------------|---------| +| Хардфорк не срабатывает | Консенсус валидаторов не достигнут | Убедиться, что валидаторы публикуют целевую версию; проверить API `get_next_scheduled_hardfork()` | +| Несоответствие ревизии при открытии | Ревизия chainbase ≠ head block num | Переиндексировать из block log или восстановить из снапшота | +| Исчерпание памяти при переиндексации | Разделяемая память слишком мала | Увеличить `shared-file-size`; включить автоизменение размера | +| Аварийный режим не активируется | HF12 ещё не применён | Убедиться, что `current_hardfork_version` ≥ 1.0.12 | +| Несоответствие состояния после переиндексации | Недетерминированная ветка has_hardfork() | Проверить `apply_hardfork()` на побочные эффекты | + +**Диагностика:** + +```json +{ "method": "database_api.get_hardfork_version", "params": [] } +{ "method": "database_api.get_next_scheduled_hardfork", "params": [] } +``` + +--- + +## Контрольный список обновления + +- [ ] Определить `N.hf` с реалистичной меткой времени (согласовать с валидаторами) +- [ ] Увеличить `CHAIN_NUM_HARDFORKS` в `0-preamble.hf` +- [ ] Реализовать миграции `apply_hardfork(N)` +- [ ] Управлять изменениями поведения проверками `has_hardfork()` +- [ ] Сделать резервную копию базы данных и block log перед развёртыванием +- [ ] Запустить узел в режиме только-чтение для проверки совместимости +- [ ] Мониторить логи на события активации хардфорка +- [ ] Согласовать с валидаторами публикацию новой версии +- [ ] Подтвердить, что `get_next_scheduled_hardfork()` показывает ожидаемую версию/время + +--- + +См. также: [Свойства цепочки](../governance/chain-properties.md), [Валидаторы](../protocol/operations/validators.md), [Схема базы данных](./database-schema.md), [Database API](../plugins/database-api.md). diff --git a/@l10n/ru/docs/advanced/security.md b/@l10n/ru/docs/advanced/security.md new file mode 100644 index 0000000000..7d8b61f41a --- /dev/null +++ b/@l10n/ru/docs/advanced/security.md @@ -0,0 +1,162 @@ +# Реализация безопасности + +Модель безопасности VIZ Ledger опирается на три столпа: верификация authority на основе порогов, детерминированная валидация подписей и зашифрованный транспорт между пирами. На этой странице описывается каждая подсистема и даются рекомендации для операторов и разработчиков плагинов. + +--- + +## Модель authority + +У каждого аккаунта есть три уровня authority — master, active и regular — каждый из которых представлен взвешенным набором ключей и/или ссылок на аккаунты с пороговым весом. + +``` +Authority { + weight_threshold: uint32 + key_auths: { PublicKey → weight } + account_auths: { AccountName → weight } +} +``` + +Операция авторизована, когда сумма весов предоставленных подписей (с рекурсивным разрешением authority аккаунтов) достигает или превышает `weight_threshold`. + +**Глубина рекурсии** ограничена во избежание бесконечных циклов в цепочках вложенных authority аккаунтов. + +Та же структура authority хранится в разделяемой памяти как `SharedAuthority` с использованием межпроцессных аллокаторов, совместимых с отображаемыми файлами Boost.Interprocess. + +--- + +## Валидация подписей + +Валидация подписей транзакций использует детерминированную ECDSA secp256k1: + +1. **Дайджест**: `sha256(chain_id || serialized_transaction)` +2. **Восстановление**: `secp256k1_recover(signature, digest)` → публичный ключ +3. **Проверка authority**: `sign_state.check_authority(account, level)` обходит дерево authority и проверяет, что восстановленный набор ключей удовлетворяет порогу. + +Движок `sign_state`: +- Поддерживает набор предоставленных подписей и их восстановленных ключей. +- Рекурсивно разрешает authority аккаунтов до максимальной глубины. +- Фильтрует неиспользуемые подписи после верификации. + +**Для разработчиков плагинов:** Используйте API `check_authority_signature` плагина `auth_util` для проверки подписей перед обработкой чувствительных операций: + +```json +{ + "method": "auth_util.check_authority_signature", + "params": ["alice", "regular", "", ["", ""]] +} +``` + +Возвращает набор верифицированных ключей подписи при удовлетворении authority или ошибку в противном случае. + +--- + +## Шифрование транспорта пиров + +Все peer-to-peer соединения используют обмен ключами ECDH + потоковое шифрование AES: + +1. Каждая сторона генерирует эфемерную пару ключей при установлении соединения. +2. ECDH формирует общий секрет из эфемерных ключей. +3. Потоковые кодировщики/декодировщики AES инициализируются с общим секретом. +4. Все последующие сообщения шифруются. + +Это предотвращает пассивное прослушивание. Каждое соединение использует свежий эфемерный ключ, поэтому компрометация одной сессии не влияет на другие. + +Реализация находится в `stcp_socket` (`libraries/network/stcp_socket.cpp`). + +--- + +## Открытие API + +Плагин `webserver` предоставляет JSON-RPC через HTTP и WebSocket. Конфигурация безопасности: + +```ini +# Привязать к loopback только для внутреннего доступа +webserver-http-endpoint = 127.0.0.1:8090 +webserver-ws-endpoint = 127.0.0.1:8091 + +# Использовать обратный прокси (nginx, caddy) для публичного доступа с TLS +``` + +**Размер пула потоков:** Веб-сервер работает с настраиваемым числом потоков. Установите `webserver-thread-pool-size` в соответствии с ожидаемой нагрузкой одновременных запросов. Недостаточный размер вызывает очередь запросов; избыточный — расход ресурсов. + +```ini +webserver-thread-pool-size = 4 +``` + +--- + +## Сетевые меры безопасности + +- **Зашифрованные каналы**: Все соединения пиров зашифрованы (ECDH + AES). Пассивное прослушивание невозможно. +- **База данных пиров**: P2P-узел ведёт базу данных пиров и метаданные времени распространения. +- **Мягкие баны**: Пиры, ведущие себя некорректно (отправляющие невалидные блоки, только данные форков без прогресса), получают временные мягкие баны вместо постоянного отключения. +- **Ограничения пропускной способности**: Настраиваются через `max-send-buffer-size` и связанные P2P-параметры. + +--- + +## Оценка уязвимостей + +**Типичные риски:** + +| Риск | Мера противодействия | +|------|---------------------| +| Обход authority через некорректные подписи | Ограниченная глубина рекурсии; строгая проверка весовых порогов | +| Слабая случайность в эфемерных ключах | Использует детерминированную генерацию ключей secp256k1 | +| MitM на незашифрованном RPC | Привязка веб-сервера к loopback; TLS-прокси для публичных эндпоинтов | +| DoS через большие полезные нагрузки | Ограничения размера JSON-RPC; пул потоков веб-сервера контролирует параллелизм | +| Исчерпание вложенных authority | Максимальная глубина рекурсии принудительно применяется в `sign_state` | + +**Контрольный список тестирования на проникновение:** +- Отправлять некорректные или неполные подписи для проверки защиты от обхода authority. +- Тестировать ограничения глубины рекурсии с глубоко вложенными цепочками authority аккаунтов. +- Проверять шифрование транспорта с помощью захвата сети (на канале не должно быть открытого текста). +- Нагружать эндпоинт веб-сервера для проверки исчерпания памяти и насыщения очереди. + +--- + +## Лучшие практики безопасности для разработки плагинов + +**Валидация входных данных:** +- Отклонять некорректные или слишком большие JSON-RPC полезные нагрузки на границах плагина. +- Валидировать все параметры по ожидаемым типам и диапазонам перед обработкой. + +**Аутентификация:** +- Всегда использовать `auth_util.check_authority_signature` перед применением изменений состояния, требующих авторизации. +- Никогда не доверять именам аккаунтов или ссылкам на ключи без проверки подписей. + +**Сравнения в постоянное время:** +- Использовать `fc::crypto::secure_compare` или аналог для сравнений секретов во избежание тайминговых атак по сторонним каналам. + +**Никаких учётных данных в открытом виде:** +- Никогда не хранить приватные ключи в состоянии плагина или логах. +- Генерировать эфемерные ключи на каждую сессию; никогда не повторно использовать. + +**Модель угроз по уровням authority:** +- `regular` authority: социальные/контентные операции — минимальные привилегии. +- `active` authority: средства, стейкинг, голосование — средние привилегии. +- `master` authority: ротация ключей, восстановление — максимальные привилегии. Требовать явного подтверждения пользователя в любом UI. + +--- + +## Мониторинг и реагирование на инциденты + +**Метрики для мониторинга:** +- Количество соединений пиров и частота смены. +- Глубина очереди пула потоков веб-сервера и задержка ответа. +- Частота сбоев валидации подписей (видна в логах на уровне `warn`). +- Пропускная способность на соединение пира. + +**Реагирование на инциденты:** +1. Изолировать затронутый эндпоинт (ограничить `webserver-http-endpoint` до loopback). +2. Ротировать ключи подписи через master authority при компрометации ключа валидатора. +3. Повторно валидировать authority аккаунтов после ротации ключей. +4. Проверить логи вокруг сбоев `sign_state` и необычных цепочек authority. + +**Ротация ключей:** +- Ключ подписи валидатора: операция `update_validator` с новым ключом подписи. +- Ключи аккаунта: `update_account` с новыми ключами master/active/regular. +- Все изменения ключей вступают в силу немедленно со следующего блока. + +--- + +См. также: [Разработка плагинов](../development/plugin-development.md), [Типы данных](../protocol/data-types.md), [Валидаторы](../protocol/operations/validators.md), [Плагин webserver](../plugins/webserver.md). diff --git a/@l10n/ru/docs/api/cli-wallet.md b/@l10n/ru/docs/api/cli-wallet.md new file mode 100644 index 0000000000..ecb999e107 --- /dev/null +++ b/@l10n/ru/docs/api/cli-wallet.md @@ -0,0 +1,251 @@ +# CLI-кошелёк + +Исполняемый файл `cli_wallet` предоставляет интерактивный интерфейс командной строки для управления аккаунтами, подписи и трансляции транзакций, а также запросов к блокчейну. + +--- + +## Подключение + +```bash +cli_wallet --server-rpc-endpoint="ws://127.0.0.1:8091" +``` + +При первом запуске установите пароль: +``` +new >>> set_password "yourpassword" +``` + +Затем разблокируйте: +``` +locked >>> unlock "yourpassword" +``` + +--- + +## Управление кошельком + +| Команда | Описание | +|---------|---------| +| `is_new` | Возвращает `true`, если пароль ещё не установлен | +| `is_locked` | Возвращает `true`, если кошелёк заблокирован | +| `lock` | Заблокировать кошелёк | +| `unlock "password"` | Разблокировать кошелёк | +| `set_password "password"` | Установить или изменить пароль | +| `load_wallet_file "file.json"` | Загрузить файл кошелька (`""` = перезагрузить текущий) | +| `save_wallet_file "file.json"` | Сохранить кошелёк в файл | +| `set_transaction_expiration 60` | Установить TTL транзакции в секундах | +| `quit` | Выйти из кошелька | +| `help` | Список всех команд | +| `gethelp "command"` | Подробная справка по одной команде | + +--- + +## Управление ключами + +| Команда | Описание | +|---------|---------| +| `import_key "5K..."` | Импортировать приватный ключ WIF | +| `suggest_brain_key` | Сгенерировать мнемонический ключ с публичной/приватной парой | +| `list_keys` | Список всех приватных ключей (WIF) в кошельке | +| `get_private_key "VIZpubkey..."` | Получить WIF для известного публичного ключа | +| `get_private_key_from_password "account" "role" "password"` | Получить ключ из учётных данных | +| `normalize_brain_key "words..."` | Нормализовать строку мнемонического ключа | + +--- + +## Запросы + +| Команда | Описание | +|---------|---------| +| `info` | Текущее состояние блокчейна | +| `database_info` | Статистика объектов базы данных | +| `get_block 1000000` | Данные блока | +| `get_ops_in_block 1000000 false` | Операции в блоке (`true` = только виртуальные) | +| `get_active_validators` | Активный набор валидаторов | +| `get_account "alice"` | Объект аккаунта | +| `list_accounts "" 100` | Постраничный список аккаунтов | +| `list_my_accounts` | Аккаунты с ключами в кошельке | +| `get_account_history "alice" -1 100` | Последние 100 операций alice | +| `get_transaction "txid..."` | Транзакция по ID | +| `get_master_history "alice"` | История смены мастер-ключей | +| `get_withdraw_routes "alice" "all"` | Маршруты вывода (`"incoming"`, `"outgoing"`, `"all"`) | +| `get_proposed_transactions "alice" 0 100` | Предложения, требующие одобрения alice | + +--- + +## Операции с аккаунтами + +| Команда | Описание | +|---------|---------| +| `create_account "creator" "1.000 VIZ" "10.000000 SHARES" "newaccount" "{}" true` | Создать аккаунт с автогенерацией ключей | +| `create_account_with_keys "creator" "1.000 VIZ" "10.000000 SHARES" "newaccount" "{}" "VIZmaster..." "VIZactive..." "VIZregular..." "VIZmemo..." true` | Создать аккаунт с заданными ключами | +| `update_account "account" "{}" "VIZm..." "VIZa..." "VIZr..." "VIZmemo..." true` | Обновить все ключи и метаданные | +| `update_account_auth_key "account" "active" "VIZnewkey..." 1 true` | Добавить ключ к авторизации (вес 0 = удалить) | +| `update_account_auth_account "account" "active" "guardian" 1 true` | Добавить аккаунт к авторизации | +| `update_account_auth_threshold "account" "active" 2 true` | Установить порог веса авторизации | +| `update_account_meta "account" "{\"key\":\"value\"}" true` | Обновить JSON-метаданные (regular-авторизация) | +| `update_account_memo_key "account" "VIZnewmemo..." true` | Обновить ключ memo | +| `delegate_vesting_shares "alice" "bob" "100.000000 SHARES" true` | Делегировать SHARES (0 = отменить) | + +--- + +## Перевод и вестинг + +| Команда | Описание | +|---------|---------| +| `transfer "alice" "bob" "10.000 VIZ" "memo" true` | Перевести VIZ (префикс `#` = зашифрованное memo) | +| `transfer_to_vesting "alice" "alice" "100.000 VIZ" true` | Застейкать VIZ как SHARES | +| `withdraw_vesting "alice" "100.000000 SHARES" true` | Начать анстейкинг (0 = отменить) | +| `set_withdraw_vesting_route "alice" "bob" 5000 false true` | Направить 50% выводов к bob как VIZ | + +--- + +## Операции с валидаторами + +| Команда | Описание | +|---------|---------| +| `list_validators "" 100` | Список валидаторов | +| `get_validator "validatorname"` | Объект валидатора | +| `update_validator "myvalidator" "https://url" "VIZsigningkey..." true` | Зарегистрировать/обновить валидатора | +| `update_chain_properties "myvalidator" {...} true` | Проголосовать за параметры цепочки (формат init) | +| `versioned_update_chain_properties "myvalidator" {...} true` | Проголосовать за версионированные параметры (формат hf9) | +| `vote_for_validator "alice" "myvalidator" true true` | Проголосовать за валидатора (`false` = снять голос) | +| `set_voting_proxy "alice" "proxy" true` | Установить прокси голосования (`""` = удалить) | + +--- + +## Операции эскроу + +| Команда | Описание | +|---------|---------| +| `escrow_transfer "alice" "bob" "agent" 1 "100.000 VIZ" "1.000 VIZ" "2024-06-01T00:00:00" "2024-07-01T00:00:00" "{}" true` | Создать эскроу | +| `escrow_approve "alice" "bob" "agent" "bob" 1 true true` | Одобрить эскроу (who = `"bob"` или `"agent"`) | +| `escrow_dispute "alice" "bob" "agent" "alice" 1 true` | Открыть спор (who = `"alice"` или `"bob"`) | +| `escrow_release "alice" "bob" "agent" "agent" "bob" 1 "100.000 VIZ" true` | Выпустить средства | + +--- + +## Операции восстановления + +| Команда | Описание | +|---------|---------| +| `request_account_recovery "recovery" "victim" {"weight_threshold":1,...} true` | Запросить восстановление как аккаунт восстановления | +| `recover_account "victim" {"recent_master_auth"} {"new_master_auth"} true` | Подтвердить восстановление | +| `change_recovery_account "account" "new_recovery" true` | Изменить аккаунт восстановления (задержка 30 дней) | + +--- + +## Операции комитета + +| Команда | Описание | +|---------|---------| +| `committee_worker_create_request "creator" "https://url" "worker" "100.000 VIZ" "500.000 VIZ" 604800 true` | Создать заявку на финансирование | +| `committee_worker_cancel_request "creator" 123 true` | Отменить заявку | +| `committee_vote_request "voter" 123 10000 true` | Голосовать (+10000 = полная поддержка, -10000 = полное несогласие, 0 = снять) | + +--- + +## Операции инвайтов + +| Команда | Описание | +|---------|---------| +| `create_invite "creator" "10.000 VIZ" "VIZinvitekey..." true` | Создать инвайт | +| `claim_invite_balance "initiator" "receiver" "5Kinvitesecret..." true` | Получить баланс инвайта | +| `invite_registration "initiator" "newaccount" "5Kinvitesecret..." "VIZnewaccountkey..." true` | Создать аккаунт из инвайта | +| `use_invite_balance "initiator" "receiver" "5Kinvitesecret..." true` | Использовать инвайт (может перевести в SHARES) | + +--- + +## Операции наград + +| Команда | Описание | +|---------|---------| +| `award "alice" "bob" 1000 0 "Great work!" [] true` | Наградить с вознаграждением на основе энергии | +| `fixed_award "alice" "bob" "10.000000 SHARES" 5000 0 "Reward" [] true` | Наградить фиксированной суммой SHARES | + +Формат бенефициара: `[{"account":"charlie","weight":2000}]` + +--- + +## Операции подписок + +| Команда | Описание | +|---------|---------| +| `set_paid_subscription "account" "https://url" 3 "10.000 VIZ" 30 true` | Создать подписку (3 уровня, 10 VIZ/период, 30-дневный период) | +| `paid_subscribe "subscriber" "account" 2 "20.000 VIZ" 1 true true` | Подписаться на уровень 2 | + +--- + +## Рынок аккаунтов + +| Команда | Описание | +|---------|---------| +| `set_account_price "account" "account" "100.000 VIZ" true true` | Выставить на продажу | +| `set_subaccount_price "account" "account" "50.000 VIZ" true true` | Выставить создание субаккаунта на продажу | +| `buy_account "buyer" "account" "100.000 VIZ" "VIZnewkey..." "0.000 VIZ" true` | Купить аккаунт | +| `target_account_sale "account" "account" "targetbuyer" "100.000 VIZ" true true` | Целевая продажа | + +--- + +## Пользовательская операция + +```bash +custom [] ["alice"] "my_app" "{\"action\":\"follow\",\"target\":\"bob\"}" true +``` + +Параметры: `required_active_auths` `required_regular_auths` `id` `json` `broadcast` + +--- + +## Конструктор транзакций + +Создание и подпись пользовательских транзакций с несколькими операциями: + +```bash +begin_builder_transaction # Возвращает дескриптор (например, 0) +add_operation_to_builder_transaction 0 [2,{"from":"alice","to":"bob","amount":"10.000 VIZ","memo":""}] +sign_builder_transaction 0 true # Подписать и транслировать +``` + +| Команда | Описание | +|---------|---------| +| `begin_builder_transaction` | Начать новую транзакцию (возвращает дескриптор) | +| `add_operation_to_builder_transaction handle [type_id, op]` | Добавить операцию | +| `replace_operation_in_builder_transaction handle idx [type_id, op]` | Заменить операцию | +| `preview_builder_transaction handle` | Предварительный просмотр JSON транзакции | +| `sign_builder_transaction handle broadcast` | Подписать (и опционально транслировать) | +| `propose_builder_transaction handle author title memo expiry review broadcast` | Обернуть в предложение | +| `remove_builder_transaction handle` | Отменить | +| `get_prototype_operation "transfer_operation"` | Получить пустой шаблон операции | +| `serialize_transaction {trx}` | Получить hex-сериализацию | +| `sign_transaction {trx} broadcast` | Подписать произвольную транзакцию | + +--- + +## DNS-помощники NS + +Хранить DNS-записи в метаданных аккаунта: + +```bash +ns_set_records "myaccount" {"a_records":["188.120.231.153"],"ssl_hash":"4a4613...","ttl":28800} true +ns_get_summary "myaccount" +ns_extract_a_records "myaccount" +ns_remove_records "myaccount" true +``` + +Хелперы валидации: `ns_validate_ipv4`, `ns_validate_sha256_hash`, `ns_validate_ttl`, `ns_validate_ssl_txt_record`, `ns_validate_metadata`, `ns_create_metadata`. + +--- + +## Личные сообщения + +```bash +get_encrypted_memo "alice" "bob" "#secret message" +decrypt_memo "#encrypteddata..." +get_inbox "myaccount" "2024-01-15T00:00:00" 100 0 +get_outbox "myaccount" "2024-01-15T00:00:00" 100 0 +``` + +--- + +См. также: [JSON-RPC API](./json-rpc.md), [Обзор операций](../protocol/operations/overview.md), [Типы данных](../protocol/data-types.md). diff --git a/@l10n/ru/docs/api/client-libraries.md b/@l10n/ru/docs/api/client-libraries.md new file mode 100644 index 0000000000..7f2d4b3097 --- /dev/null +++ b/@l10n/ru/docs/api/client-libraries.md @@ -0,0 +1,140 @@ +# Клиентские библиотеки + +Официальные клиентские библиотеки доступны для Python, PHP и JavaScript. Все библиотеки взаимодействуют с узлом VIZ через JSON-RPC API и выполняют подписание транзакций локально. + +--- + +## Python — viz-python-lib + +**Репозиторий:** https://github.com/VIZ-Blockchain/viz-python-lib + +### Установка + +```bash +pip install viz-python-lib +``` + +### Быстрый старт + +```python +from viz import Client + +viz = Client( + node="wss://node.viz.cx/ws", + keys=["5...private_key..."] +) + +# Наградить аккаунт энергией +viz.award("receiver_account", 10.5, "with love", None, "your_account") +``` + +### Возможности + +- Транспорт WebSocket (`wss://`) и HTTP (`https://`) +- Полная трансляция транзакций: все операции протокола +- Локальное управление ключами — приватные ключи не покидают клиент +- Автоматические повторные попытки и переключение между несколькими узлами +- Совместимость с Python 3 + +--- + +## PHP — viz-php-lib + +**Репозиторий:** https://github.com/VIZ-Blockchain/viz-php-lib + +### Установка + +Библиотека использует автозагрузку PSR-4 и не требует Composer. Склонируйте или скачайте репозиторий и подключите автозагрузчик: + +```php +require_once '/path/to/viz-php-lib/autoload.php'; +``` + +**Требования к PHP-расширениям:** `gmp` (предпочтительно) или `bcmath` для работы с большими числами. + +### Быстрый старт + +```php +$private_key = '5...your_private_key...'; +$tx = new VIZ\Transaction('https://node.viz.plus/', $private_key); + +// Сформировать и транслировать транзакцию награды +$tx_data = $tx->award($account, 'committee', 1000, 0, 'memo'); +$tx_status = $tx->execute($tx_data['json']); +``` + +### Возможности + +- Поддержка всех 40 операций протокола +- Шифрование/расшифровка мемо по алгоритму AES-256-CBC +- Без внешних зависимостей — только стандартные PHP-расширения +- Совместимость с PHP 7.x и 8.x + +--- + +## JavaScript — viz-js-lib + +**Репозиторий:** https://github.com/VIZ-Blockchain/viz-js-lib +**npm:** https://www.npmjs.com/package/viz-js-lib + +### Установка + +```bash +npm install viz-js-lib --save +``` + +### CDN (браузер) + +```html + + + + + +``` + +### Быстрый старт + +```js +const viz = require('viz-js-lib'); + +// Получить WIF-ключ из учётных данных +var wif = viz.auth.toWif(username, password, 'regular'); + +// Транслировать голос +viz.broadcast.vote(wif, voter, author, permlink, weight, function(err, result) { + console.log(err, result); +}); +``` + +### Настройка узла + +```js +viz.config.set('websocket', 'wss://node.viz.cx/ws'); +// или HTTP: +viz.config.set('websocket', 'https://node.viz.cx/'); +``` + +### Возможности + +- Транспорт WebSocket (`ws://` / `wss://`) и HTTP (`http://` / `https://`) +- Работает в Node.js и современных браузерах +- Утилиты для ключей: `toWif`, `toPublic`, `isWif`, `signTransaction` +- Полный broadcast API для всех операций протокола +- Лицензия MIT + +--- + +## Выбор библиотеки + +| Критерий | Python | PHP | JavaScript | +|----------|--------|-----|-----------| +| Установка | `pip` | Ручная PSR-4 | `npm` | +| Транспорт | WS / HTTP | HTTP | WS / HTTP | +| Среда | Python 3 | PHP 7+ | Node.js / браузер | +| Зависимости | минимальные | GMP или BCMath | нет (bundled) | +| Лицензия | MIT | MIT | MIT | + +--- + +См. также: [JSON-RPC API](./json-rpc.md), [CLI Wallet](./cli-wallet.md). diff --git a/@l10n/ru/docs/api/json-rpc.md b/@l10n/ru/docs/api/json-rpc.md new file mode 100644 index 0000000000..3335c76be7 --- /dev/null +++ b/@l10n/ru/docs/api/json-rpc.md @@ -0,0 +1,228 @@ +# JSON-RPC API + +Все API узлов VIZ используют JSON-RPC 2.0 через HTTP POST или WebSocket. + +--- + +## Формат запроса + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "method": "api_name.method_name", + "params": [arg1, arg2] +} +``` + +- `id` может быть любым числом или строкой; передаётся в ответе без изменений. +- `params` может быть массивом или объектом в зависимости от метода. +- Поддерживаются HTTP POST и WebSocket. Подписки требуют WebSocket. + +--- + +## Формат ответа + +**Успех:** +```json +{ "jsonrpc": "2.0", "id": 1, "result": { ... } } +``` + +**Ошибка:** +```json +{ "jsonrpc": "2.0", "id": 1, "error": { "code": -32602, "message": "Invalid params" } } +``` + +### Коды ошибок + +| Код | Значение | +|-----|---------| +| `-32700` | Ошибка разбора — некорректный JSON | +| `-32600` | Некорректный запрос | +| `-32601` | Метод не найден | +| `-32602` | Некорректные параметры | +| `-32603` | Внутренняя ошибка | +| от `-32099` до `-32000` | Ошибка сервера (исключение из обработчика) | + +--- + +## Пространства имён плагинов + +| Пространство имён | Статус | Описание | +|-----------------|-------|---------| +| `database_api` | Активен | Запросы блоков, аккаунтов, состояния цепочки | +| `network_broadcast_api` | Активен | Трансляция транзакций и блоков | +| `witness_api` | Активен | Запросы валидаторов | +| `account_by_key` | Активен | Обратный поиск по ключу | +| `account_history` | Активен | История операций аккаунта | +| `operation_history` | Активен | Запросы операций блока | +| `committee_api` | Активен | Запросы заявок комитета | +| `invite_api` | Активен | Запросы инвайтов | +| `paid_subscription_api` | Активен | Запросы подписок | +| `custom_protocol_api` | Активен | Метаданные пользовательского протокола | +| `auth_util` | Активен | Проверка авторизации | +| `block_info` | Активен | Расширенная информация о блоке | +| `raw_block` | Активен | Экспорт сырых блоков | +| `follow` | Устарел | Индексы подписок и ленты | +| `tags` | Устарел | Поиск контента по тегу | +| `social_network` | Устарел | Высокоуровневые запросы контента | +| `private_message` | Устарел | Индекс зашифрованных сообщений | +| `debug_node` | Только dev | Тестовые/отладочные операции | + +--- + +## Методы `database_api` + +| Метод | Описание | +|-------|---------| +| `get_block_header(block_num)` | Заголовок блока для данной высоты | +| `get_block(block_num)` | Полный подписанный блок | +| `get_irreversible_block_header(block_num)` | Заголовок блока, если он необратимый | +| `get_irreversible_block(block_num)` | Полный блок, если он необратимый | +| `set_block_applied_callback(callback)` | WebSocket: подписка на новые блоки | +| `get_config()` | Компайл-тайм константы цепочки | +| `get_dynamic_global_properties()` | Текущее состояние цепочки | +| `get_chain_properties()` | Медианные параметры цепочки валидаторов | +| `get_hardfork_version()` | Строка текущей версии хардфорка | +| `get_next_scheduled_hardfork()` | Информация о следующем запланированном хардфорке | +| `get_accounts(names[])` | Полные объекты аккаунтов | +| `lookup_account_names(names[])` | Аналог get_accounts, но с поддержкой null | +| `lookup_accounts(lower_bound, limit)` | Постраничный список имён аккаунтов | +| `get_account_count()` | Общее количество зарегистрированных аккаунтов | +| `get_master_history(account)` | История смены мастер-ключей | +| `get_recovery_request(account)` | Ожидающий запрос восстановления аккаунта | +| `get_escrow(from, escrow_id)` | Объект эскроу | +| `get_withdraw_routes(account, type)` | Маршруты вывода из вестинга (`"incoming"` / `"outgoing"` / `"all"`) | +| `get_vesting_delegations(account, from, limit, type)` | Делегирования (`"delegated"` / `"received"`) | +| `get_expiring_vesting_delegations(account, from, limit)` | Делегирования в окне возврата | +| `get_transaction_hex(trx)` | Hex-кодированная сериализованная транзакция | +| `get_required_signatures(trx, available_keys[])` | Минимальный набор ключей для подписания | +| `get_potential_signatures(trx)` | Все ключи, которые могут подписать | +| `verify_authority(trx)` | `true`, если полностью подписана | +| `verify_account_authority(name, keys[])` | `true`, если ключи удовлетворяют авторизации | +| `get_database_info()` | Статистика использования памяти chainbase | +| `get_proposed_transactions(account, from, limit)` | Предложения, требующие одобрения аккаунта | +| `get_accounts_on_sale(from, limit)` | Аккаунты, выставленные на прямую продажу | +| `get_accounts_on_auction(from, limit)` | Аккаунты, выставленные на аукцион | +| `get_subaccounts_on_sale(from, limit)` | Права создания субаккаунтов на продажу | + +--- + +## Методы `network_broadcast_api` + +| Метод | Описание | +|-------|---------| +| `broadcast_transaction(trx)` | Трансляция (асинхронная) | +| `broadcast_transaction_synchronous(trx)` | Трансляция с ожиданием включения в блок | +| `broadcast_transaction_with_callback(callback, trx)` | Трансляция с обратным вызовом WebSocket | +| `broadcast_block(block)` | Трансляция подписанного блока (валидаторы) | + +--- + +## Методы `witness_api` + +| Метод | Описание | +|-------|---------| +| `get_active_witnesses()` | Текущий активный набор валидаторов (21 аккаунт) | +| `get_witness_schedule()` | Полный объект расписания валидаторов | +| `get_witnesses(ids[])` | Валидаторы по внутренним ID | +| `get_witness_by_account(account)` | Объект валидатора для аккаунта | +| `get_witnesses_by_vote(lower_bound, limit)` | Валидаторы по убыванию веса голосов | +| `get_witnesses_by_counted_vote(lower_bound, limit)` | Валидаторы по числу голосов | +| `get_witness_count()` | Общее количество зарегистрированных валидаторов | +| `lookup_witness_accounts(lower_bound, limit)` | Список имён аккаунтов валидаторов | + +--- + +## Методы `account_history` + +### `get_account_history(account, from, limit)` + +Возвращает операции, связанные с `account`. `from = -1` начинает с самой последней. + +```json +{ + "method": "account_history.get_account_history", + "params": ["alice", -1, 100] +} +``` + +Возвращает словарь `{ sequence: { trx_id, block, op: [type_id, data] } }`. + +--- + +## Методы `operation_history` + +| Метод | Описание | +|-------|---------| +| `get_ops_in_block(block_num, only_virtual)` | Операции в блоке | +| `get_transaction(trx_id)` | Транзакция по ID | + +--- + +## Методы `committee_api` + +| Метод | Описание | +|-------|---------| +| `get_committee_request(request_id)` | Детали и статус заявки | +| `get_committee_request_votes(request_id)` | Голоса по заявке | +| `get_committee_requests_list(from, limit)` | Список ID заявок | + +--- + +## Методы `invite_api` + +| Метод | Описание | +|-------|---------| +| `get_invites_list(from, limit)` | Все активные ID инвайтов | +| `get_invite_by_id(id)` | Инвайт по внутреннему ID | +| `get_invite_by_key(public_key)` | Инвайт по публичному ключу | + +--- + +## Методы `paid_subscription_api` + +| Метод | Описание | +|-------|---------| +| `get_paid_subscriptions(from, limit)` | Все предложения подписок | +| `get_paid_subscription_options(account)` | Конфигурация подписок для аккаунта | +| `get_paid_subscription_status(subscriber, account)` | Статус подписки | +| `get_active_paid_subscriptions(subscriber, from, limit)` | Активные подписки | +| `get_inactive_paid_subscriptions(subscriber, from, limit)` | Истёкшие подписки | + +--- + +## WebSocket-подписки + +Доступны только через постоянное WebSocket-соединение. + +| Метод | Данные обратного вызова | +|-------|------------------------| +| `database_api.set_block_applied_callback` | Заголовок блока при каждом применённом блоке | +| `database_api.set_pending_transaction_callback` | Транзакция при входе в пул ожидания | +| `database_api.cancel_all_subscriptions` | Отписаться от всего | + +--- + +## Рекомендуемые наборы плагинов + +**Минимальный API-узел:** +```ini +plugin = chain json_rpc webserver p2p +plugin = database_api network_broadcast_api +``` + +**Полный API-узел (добавить):** +```ini +plugin = witness_api account_by_key account_history operation_history +plugin = committee_api invite_api paid_subscription_api +``` + +**Узел-валидатор (добавить):** +```ini +plugin = validator snapshot +``` + +--- + +См. также: [Database API](../plugins/database-api.md), [Плагин веб-сервера](../plugins/webserver.md), [Обзор операций](../protocol/operations/overview.md). diff --git a/@l10n/ru/docs/consensus/block-processing.md b/@l10n/ru/docs/consensus/block-processing.md new file mode 100644 index 0000000000..06d43c1718 --- /dev/null +++ b/@l10n/ru/docs/consensus/block-processing.md @@ -0,0 +1,259 @@ +# Обработка блоков + +Внутренняя механика применения блоков, управления ожидающими транзакциями и переключения форков. + +--- + +## Обзор + +Когда узел получает новый блок через P2P, плагин chain вызывает `database::push_block()`. Последовательность: + +1. Временно удалить ожидающие (mempool) транзакции из базы данных. +2. Применить входящий блок. +3. Повторно применить ожидающие транзакции, не включённые в блок. + +Это управляется вспомогательным классом `without_pending_transactions` в `db_with.hpp`. + +--- + +## Ключевые структуры данных + +| Структура | Тип | Назначение | +|-----------|-----|-----------| +| `_pending_tx` | `vector` | Mempool: полученные транзакции, ожидающие включения в блок | +| `_popped_tx` | `deque` | Транзакции из извлечённого блока (при переключении форка); повторно применяются после переключения | +| `_pending_tx_session` | `optional` | Сессия отмены, охватывающая все изменения состояния ожидающих транзакций | + +--- + +## Поток применения блока + +``` +push_block(new_block) + └─ without_pending_transactions(db, skip, _pending_tx, callback) + ├─ pending_transactions_restorer ctor: clear_pending() + ├─ callback: _push_block(new_block) ← применить входящий блок + └─ ~pending_transactions_restorer() ← восстановить ожидающие транзакции +``` + +### Пошагово внутри `_push_block()` + +1. **Ранние проверки отклонения** (см. ниже). +2. Добавить блок в `fork_db`. +3. Если новая голова форка непосредственно продолжает текущую голову (`new_block.previous == head_block_id()`): + - Пропустить логику переключения форка, перейти к `apply_block()`. +4. Если новая голова выше и расходится с текущей головой: + - **Сравнение форков с весом голосов** (HF12) — см. [Разрешение форков](./fork-resolution.md). + - Извлекать блоки старого форка до общего предка. + - Применять блоки нового форка по порядку. +5. `apply_block()` запускает evaluator-ы транзакций, обновляет динамические глобальные свойства, обрабатывает виртуальные операции. +6. `update_last_irreversible_block()` — продвигает LIB, если ≥14 валидаторов подтвердили. + +--- + +## Восстановление ожидающих транзакций + +Деструктор `~pending_transactions_restorer()` обрабатывает два списка по порядку после применения нового блока. + +### Шаг 1: Повторное применение `_popped_tx` (из переключения форка) + +``` +для каждой tx в _popped_tx: + если time_elapsed > 200ms → отложить (добавить обратно в _pending_tx) + иначе если is_known_transaction(tx) → пропустить (уже в цепочке) + иначе → _push_transaction(tx) → applied_txs++ +``` + +### Шаг 2: Повторное применение `_pending_transactions` (исходный mempool) + +``` +для каждой tx в _pending_transactions: + если time_elapsed > 200ms → отложить + иначе если is_known_transaction(tx) → пропустить + иначе → _push_transaction(tx) → applied_txs++ + при transaction_exception → отбросить (недействительная) + при fc::exception → тихо отбросить +``` + +### Шаг 3: Сводка в лог + +Если какие-либо транзакции были отложены: +``` +Postponed N pending transactions. M were applied. +``` + +--- + +## Временной лимит + +**`CHAIN_PENDING_TRANSACTION_EXECUTION_LIMIT` = 200 мс** + +После истечения этого времени с начала восстановления все оставшиеся транзакции помещаются обратно в `_pending_tx` без применения. Это предотвращает блокировку узла на большом mempool. + +Лимит срабатывает при: +- Высокопроизводительных блоках с многочисленными ожидающими транзакциями +- CPU-интенсивных операциях +- Системе под нагрузкой + +--- + +## Ограничение размера блока при генерации + +**`CHAIN_BLOCK_GENERATION_POSTPONED_TX_LIMIT` = 5** + +Во время `_generate_block()` транзакции, которые превысят `maximum_block_size`, пропускаются. После 5 последовательных превышающих транзакций цикл генерации прерывается. Они остаются в `_pending_tx` для следующего блока. + +Лог: +``` +Postponed N transactions due to block size limit +``` + +--- + +## Посев головы fork DB + +Перед добавлением блока `_push_block()` убеждается, что текущий головной блок базы данных присутствует в `fork_db`: + +``` +если new_block.previous == head_block_id() + И head_block_id() НЕ в fork_db: + получить головной блок из block log (или dlt_block_log в DLT-режиме) + fork_db.start_block(head_block) +``` + +Без этого посева допустимые следующие блоки выбрасывали бы `unlinkable_block_exception`, поскольку их `previous` отсутствует в `fork_db`. Это также исправляет узлы-валидаторы, генерирующие собственные блоки — `generate_block()` устанавливает `pending_block.previous = head_block_id()`. + +--- + +## Обход прямого продолжения + +После добавления блока в `fork_db`, если блок непосредственно продолжает голову базы данных: + +``` +если new_block.previous == head_block_id(): + → пропустить переключение форка, перейти к apply_block() +``` + +Это обрабатывает случай, когда `fork_db._head` указывает на устаревший более высокий блок из предыдущего неудачного цикла синхронизации. Без этого обхода устаревшая голова запускала бы логику переключения форка, которая тихо отбрасывает допустимый следующий блок. + +--- + +## Раннее отклонение блока + +`_push_block()` применяет несколько ранних проверок отклонения во избежание ненужной работы и предотвращения бесконечных циклов синхронизации: + +| Проверка | Условие | Действие | +|----------|---------|---------| +| Уже применён | `block.num ≤ head` и ID совпадает с существующим блоком | Тихо игнорировать (дубликат) | +| Другой форк | `block.num ≤ head`, другой ID, родитель не в fork_db | Тихо отклонить | +| Далеко впереди, разрыв > 100 | `block.num > head`, родитель неизвестен, разрыв > 100 блоков | Тихо отклонить (защита памяти) | +| Далеко впереди, разрыв ≤ 100 | `block.num > head`, родитель неизвестен, разрыв ≤ 100 | Разрешить в fork_db (кешируется в unlinked index) | +| Следующий блок напрямую | `block.previous == head_block_id()` | Всегда разрешено | + +Порог разрыва в 100 блоков предотвращает разбухание памяти от мёртвых форков, допуская нормальную обработку блоков вне порядка при P2P-синхронизации. + +--- + +## Переключение форка + +Когда узел переключается на другой форк: + +1. `pop_block()` удаляет текущий головной блок; его транзакции перемещаются в `_popped_tx`. +2. Повторять до достижения общего предка. +3. Применять блоки нового форка по порядку от общего предка до новой головы. +4. `~pending_transactions_restorer()` сначала повторно применяет `_popped_tx`, затем исходный mempool. + +Транзакции, уже находящиеся в новой цепочке, тихо пропускаются через `is_known_transaction()`. + +### Линейное продолжение vs. реальный форк + +`_push_next()` в `fork_db` может автоматически связывать несколько блоков-сирот при прибытии их родителя, вызывая прыжок `fork_db._head` на несколько блоков впереди головы базы данных за один вызов `push_block()`. Код различает: + +- **Линейное продолжение** (`branches.second.size() == 1` и общий предок == текущая голова): операции извлечения не нужны; блоки применяются напрямую. +- **Реальное переключение форка** (расходящиеся ветви): полная последовательность извлечения и повторного применения. + +Это различие критично в DLT-режиме, где LIB == head и сессии отмены зафиксированы — цикл извлечения при линейном продолжении был бы бесконечным. + +--- + +## Обработка блоков-сирот (Unlinked Index) + +Когда прибывает блок, родитель которого неизвестен, `fork_db` сохраняет его в `_unlinked_index`. Когда недостающий родитель наконец прибывает: + +1. `_push_block(parent)` связывает родителя с цепочкой. +2. `_push_next(parent)` итерирует `_unlinked_index` в поиске дочерних блоков `parent`. +3. Дочерние блоки перемещаются в `_index` и рекурсивно связываются. +4. `fork_db._head` может продвинуться на несколько блоков за один вызов (запускает путь линейного продолжения). + +--- + +## Мягкий бан пиров на основе страйков + +Пиры не получают немедленный бан за отправку не связываемых блоков. Счётчик накапливается: + +| Путь | Порог | Условие сброса | +|------|-------|---------------| +| Нормальная работа: не связываемый блок на уровне головы или ниже | 20 страйков | Допустимый блок от того же пира | +| Путь синхронизации: общее отклонение блока | 20 страйков | Допустимый блок от того же пира | +| Мёртвый форк / блок слишком старый | Немедленный бан | — | + +Честные пиры могут восстановиться после временных ошибок (перезагрузка снимка, гонки таймингов, кратковременные micro-fork). + +--- + +## Тайминг производства блока валидатором + +Плагин validator использует таймер с интервалом 250мс и упреждением 250мс: + +1. Таймер срабатывает каждые **250мс** (выровнен по 250мс границам системных часов, минимальный сон 50мс). +2. `maybe_produce_block()` вычисляет `now = NTP_time + 250ms`. +3. `get_slot_at_time(now)` находит текущий слот. +4. Если слот принадлежит настроенному валидатору и `|scheduled_time - now| ≤ 500ms`, производит блок с детерминированным `scheduled_time` в качестве временной метки. + +``` +Слот на T=6.000, тик на T=5.750: + now = 5.750 + 0.250 = 6.000 → слот совпал → производить +``` + +Это даёт 500мс запас безопасности против порога задержки. + +### Условия производства (проверяются по порядку) + +| Условие | Результат при сбое | +|---------|-------------------| +| Цепочка синхронизирована (или `enable-stale-production`) | `not_synced` | +| `get_slot_at_time(now) > 0` | `not_time_yet` | +| Запланированный валидатор в нашем настроенном наборе | `not_my_turn` | +| Ненулевой ключ подписи on-chain | `not_my_turn` | +| Приватный ключ для ключа подписи в памяти | `no_private_key` | +| Участие в сети ≥ порога (до HF12) | `low_participation` | +| `|scheduled_time - now| ≤ 500ms` | `lag` | +| Нет конкурирующего блока на той же высоте в fork_db | `fork_collision` | +| Последние 21 блок НЕ все от наших валидаторов | `minority_fork` | + +--- + +## Константы конфигурации + +| Константа | Значение | Описание | +|-----------|---------|----------| +| `CHAIN_PENDING_TRANSACTION_EXECUTION_LIMIT` | 200 мс | Максимальное время повторного применения ожидающих транзакций после добавления блока | +| `CHAIN_BLOCK_GENERATION_POSTPONED_TX_LIMIT` | 5 | Максимум последовательных превышающих транзакций, пропускаемых при генерации | +| `CHAIN_BLOCK_SIZE` | 65536 байт | Жёсткий лимит размера блока | +| `maximum_block_size` | Динамический (медиана валидаторов) | Мягкий лимит размера блока | +| `CHAIN_BLOCK_INTERVAL` | 3 с | Интервал производства блоков | + +--- + +## Префиксы отладочных логов + +| Префикс | Значение | +|---------|---------| +| `FORK-SWITCH-POP: popping head #H` | Нормальное переключение форка — извлечение блока старого форка | +| `FORK-RECOVER-POP: popping head #H` | Восстановление после ошибки — откат неудачного переключения форка | +| `POP_BLOCK: db_head=#X fork_db_head=#Y` | Состояние перед каждым вызовом `pop_block()` | +| `Fork switch: new_head=#X branches.first=N branches.second=M` | Ветви перед переключением форка; `M=0` означает линейное продолжение | + +--- + +См. также: [Fair-DPOS](./fair-dpos.md), [Разрешение форков](./fork-resolution.md), [Узел-валидатор](../node/validator-node.md). diff --git a/@l10n/ru/docs/consensus/emergency-consensus.md b/@l10n/ru/docs/consensus/emergency-consensus.md new file mode 100644 index 0000000000..7d824e890a --- /dev/null +++ b/@l10n/ru/docs/consensus/emergency-consensus.md @@ -0,0 +1,221 @@ +# Режим экстренного консенсуса + +Режим экстренного консенсуса (введён в HF12) активируется автоматически, когда сеть простаивает в течение 1 часа. Специальный валидатор "committee" берёт на себя производство блоков для поддержания непрерывности цепочки до тех пор, пока реальные валидаторы не восстановят свои ключи подписи. + +--- + +## Ключевые константы + +| Константа | Значение | Значение | +|-----------|---------|---------| +| `CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC` | 3600 с | Время простоя до активации | +| `CHAIN_EMERGENCY_WITNESS_ACCOUNT` | `"committee"` | Аккаунт экстренного производителя блоков | +| `CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY` | `VIZ75CR...` | Детерминированный ключ подписи для экстренного режима | +| `CHAIN_EMERGENCY_EXIT_NORMAL_BLOCKS` | 21 | Последовательные блоки реальных валидаторов для выхода | +| `CHAIN_IRREVERSIBLE_THRESHOLD` | 75% | Доля слотов расписания для выхода | +| `CHAIN_MAX_WITNESSES` | 21 | Максимальное количество слотов валидаторов | + +### Поля состояния в `dynamic_global_property_object` + +| Поле | По умолчанию | Значение | +|------|--------------|---------| +| `emergency_consensus_active` | `false` | Активен экстренный режим | +| `emergency_consensus_start_block` | `0` | Номер блока при активации | + +--- + +## Активация + +`update_global_dynamic_data()` выполняется при каждом применённом блоке и проверяет: + +1. **Ворота HF12** — пропустить, если харфорк ещё не активирован. +2. **Уже активен** — пропустить, если экстренный режим уже включён. +3. **LIB-блок доступен в block log** — пропустить, если LIB-блок отсутствует в block log (DLT-узлы после восстановления из снимка имеют пустой block log; отсутствие LIB-блока вычислило бы миллионы устаревших секунд и запустило бы ложный дедлок активации). +4. **Таймаут** — вычислить `seconds_since_lib = current_block.timestamp − lib_block.timestamp`. Если `< 3600`, пропустить. + +Все проверки используют только временны́е метки, встроенные в блоки — никаких системных часов, никаких флагов пропуска. Это гарантирует идентичную детерминированную активацию на каждом узле, включая воспроизведения. + +### Последовательность активации + +Когда пересекается порог таймаута: + +1. Установить `dgp.emergency_consensus_active = true` и `dgp.emergency_consensus_start_block = block_num`. +2. Создать или обновить объект валидатора "committee": + - `signing_key = CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY` + - `props = current_median_props` + - Голоса за харфорки установлены на текущую применённую версию (нейтральный голосователь). +3. Отключить ВСЕХ реальных валидаторов: установить `signing_key = zero`, сбросить `penalty_percent = 0`, `current_run = 0`. +4. Удалить все объекты `witness_penalty_expire`. +5. Переопределить расписание валидаторов: все `CHAIN_MAX_WITNESSES` слотов → "committee". +6. Уведомить fork DB: `_fork_db.set_emergency_mode(true)` (включает детерминированное разрешение хэш-ничьих). +7. Лог: `"EMERGENCY CONSENSUS MODE activated at block #N. No blocks for X seconds since LIB Y."` + +--- + +## Экстренная работа + +### Производство блоков — мастер vs. последователь + +Узлы с настроенным `emergency-private-key` в `config.ini` являются **экстренными мастерами**; все остальные узлы — **последователи**. + +```ini +# Только для экстренного мастер-узла +emergency-private-key = 5Jzzz... # приватный ключ CHAIN_EMERGENCY_WITNESS_ACCOUNT +``` + +| Роль | Проверка DLT-синхронизации | Проверка minority fork | Производство | +|------|---------------------------|----------------------|-------------| +| Мастер | Обходится (привело бы к дедлоку) | Пропускается (ожидается, что все блоки будут "нашими") | Производит блоки для всех слотов | +| Последователь | Нормальная | 21-блочная проверка (1 полный раунд) | Стандартное производство для собственных запланированных слотов | + +### Детерминированное разрешение ничьих в fork DB + +В экстренном режиме несколько мастеров (географическая избыточность) могут производить конкурирующие блоки на одной высоте с одним ключом. Порядок прибытия варьируется в зависимости от P2P-топологии. + +`fork_database::_push_block()` разрешает ничьи: +``` +item->num == _head->num И _emergency_consensus_active: + item->id < _head->id → _head = item (побеждает меньший хэш блока) + иначе → оставить текущую _head +``` + +Все узлы сходятся на одной вершине цепочки независимо от того, какой блок они увидели первым. + +### Продвижение LIB + +`update_last_irreversible_block()` вычисляет LIB нормально, но **ограничивает его значением HEAD − 1** в экстренном режиме. Без этого ограничения, поскольку все 21 слот принадлежат "committee" и `committee.last_supported_block_num == HEAD`, вычисление `nth_element` возвращает HEAD — что зафиксировало бы сессию отмены текущего блока в середине применения, вызывая необратимое повреждение. + +После 3 блоков committee (`CHAIN_IRREVERSIBLE_SUPPORT_MIN_RUN`) LIB продвигается с каждым блоком, сохраняя окно fork DB небольшим. + +### Гибридное расписание валидаторов + +`update_witness_schedule()` строит гибридное расписание в каждом раунде: + +- Слоты, где `signing_key` реального валидатора ненулевой: сохранить реального валидатора. +- Пустые или нулевые слоты: заполнить "committee". + +Это позволяет реальным валидаторам возвращаться постепенно. Каждый раз, когда реальный валидатор восстанавливает свой ключ подписи через `validator_update_operation`, следующее перестроение расписания включает его. + +Committee исключается из подсчёта версий харфорков и вычисления медианных свойств цепочки (он копирует текущую медиану, поэтому учёт его на каждый слот исказил бы медиану). + +--- + +## Деактивация + +Каждое перестроение расписания проверяет условие выхода: + +``` +real_witness_slots >= CHAIN_MAX_WITNESSES × 75% +``` + +При 21 валидаторе: `21 × 0.75 = 15.75 → 15` слотов реальных валидаторов требуется. + +При выполнении условия: +1. `dgp.emergency_consensus_active = false`. +2. `_fork_db.set_emergency_mode(false)`. +3. Лог: `"EMERGENCY CONSENSUS MODE deactivated at block #N. R real validators active."`. + +Сеть возобновляет нормальную работу на следующем цикле расписания. + +--- + +## Восстановление при запуске + +`database::open()` проверяет расписание валидаторов после воспроизведения. Если какой-либо слот пустой (строка `""`), экстренный режим был активен при остановке узла: + +1. Если `emergency_consensus_active` ещё не установлен → установить и установить флаг экстренного режима fork DB. +2. Заполнить все слоты "committee". +3. Лог: `"schedule repaired, all N slots set to committee"`. + +Это гарантирует, что расписание всегда согласовано после некорректного завершения во время экстренного режима. + +--- + +## Интеграция с Validator Guard + +Плагин `witness_guard` продолжает работу во время экстренного режима и фактически становится более критичным: + +- Реальные валидаторы отключаются (ключ подписи обнуляется) при активации. +- Защита валидатора автоматически транслирует `validator_update_operation` для восстановления ключа подписи каждого валидатора, как только на цепочке обнаруживается нулевой ключ. +- Защита `enable-stale-production` в validator guard **не блокирует** восстановление ключей в экстренном режиме ("Экстренный консенсус обрабатывает собственное восстановление и восстановление ключей может по-прежнему требоваться"). +- Как только 15 валидаторов восстанавливают свои ключи, срабатывает условие выхода. + +См. [Защита валидатора](../node/validator-guard.md). + +--- + +## Защиты P2P-взаимодействия + +Несколько P2P-защитных механизмов знают об экстренном режиме: + +| Механизм | Поведение в экстренном режиме | +|----------|-------------------------------| +| `resync_from_lib()` | **Полностью пропускается** — извлечение блоков вблизи LIB в экстренном режиме приведёт к сбою | +| `stale_sync_check_task()` | Если голова мастера продвигается → сбросить таймер, пропустить восстановление; если голова последователя застряла → разрешить восстановление | +| `handle_block()` (DLT, режим синхронизации, разрыв 0–2) | Обрабатывается как нормальный (не синхронизация) для предотвращения нарушения цикла производства | +| Обнаружение зависшей синхронизации снимков | Та же логика, что и проверка зависшей синхронизации | + +Защита `resync_from_lib()` наиболее критична: в экстренном режиме LIB близок к HEAD. Извлечение блоков до LIB и сброс fork DB привели бы к тому, что блоки пиров из реальной сети связываются с повторно заполненным LIB, запуская переключение форка, извлечение ниже зафиксированного LIB и либо сбой, либо повреждение состояния. + +--- + +## Валидация блоков — ослабленное соответствие слот-валидатор + +`verify_signing_witness()` обычно утверждает, что производитель блока точно совпадает с запланированным валидатором. В экстренном режиме: + +``` +Если block.validator != scheduled_witness: + dlog("Emergency mode: accepting block from BW at slot scheduled for SW") + → принять в любом случае (подпись по-прежнему проверяется по signing_key block.validator) +``` + +Это позволяет экстренным мастерам производить блоки, даже если несколько слотов в ожидающем расписании всё ещё назначены реальным валидаторам. + +--- + +## Совместимость со снимками + +Поля экстренного состояния включены в снимки с совместимыми по умолчанию значениями: + +- `emergency_consensus_active` отсутствует в снимке → по умолчанию `false`. +- `emergency_consensus_start_block` отсутствует → по умолчанию `0`. + +Снимки, созданные во время активного экстренного режима, корректно сохраняют состояние; снимки, созданные до HF12, импортируются как неэкстренные. + +--- + +## Сводка защитных механизмов + +| № | Расположение | Механизм | +|---|------------|---------| +| 1 | `update_global_dynamic_data` | Активировать только при HF12 + не активен + LIB-блок доступен | +| 2 | `update_witness_schedule` | Гибридное переопределение + проверка выхода при ≥75% реальных валидаторов | +| 3 | `update_last_irreversible_block` | Ограничить LIB значением HEAD − 1 в экстренном режиме | +| 4 | `verify_signing_witness` | Ослабить соответствие слот-валидатор | +| 5 | `fork_db._push_block` | Детерминированное разрешение хэш-ничьих | +| 6 | `maybe_produce_block` (мастер) | Обходить синхронизацию, устаревание, участие; пропускать minority fork | +| 7 | `maybe_produce_block` (последователь) | Сначала синхронизироваться; 21-блочная проверка изоляции | +| 8 | `resync_from_lib` | **Полностью пропускать** в экстренном режиме | +| 9 | `stale_sync_check_task` | Пропустить если голова мастера продвигается; разрешить если последователь застрял | +| 10 | `handle_block` | Почти догнавшие блоки обрабатываются как нормальные в DLT-экстренном режиме | +| 11 | `database::open` | Исправление расписания при запуске | +| 12 | `witness_guard` | Не подавлять восстановление ключей в экстренном режиме | +| 13 | `snapshot import` | Совместимая обработка полей | +| 14 | `update_witness_schedule` | Исключать committee из подсчёта версий харфорков | +| 15 | `update_median_witness_props` | Исключать committee из вычисления медианы | + +--- + +## Ключевые инварианты + +1. **Детерминированная активация** — использует только временны́е метки в блоках; идентична на каждом узле и при каждом воспроизведении. +2. **Безопасность DLT-снимка** — пропускает активацию если LIB-блок отсутствует в block log. +3. **Неизменность экстренного форка** — `resync_from_lib()` отказывается выполняться в экстренном режиме. +4. **Различие мастер/последователь** — только узлы с `--emergency-private-key` являются мастерами. +5. **Сходимость fork DB** — детерминированное разрешение хэш-ничьих гарантирует, что все узлы выбирают один блок. +6. **Безопасность LIB** — ограничен значением HEAD − 1 для сохранения защиты отмены. +7. **Нейтральное голосование committee** — committee голосует за текущую применённую версию харфорка, копирует медианные props. + +--- + +См. также: [Fair-DPOS](./fair-dpos.md), [Разрешение форков](./fork-resolution.md), [Узел-валидатор](../node/validator-node.md), [Защита валидатора](../node/validator-guard.md). diff --git a/@l10n/ru/docs/consensus/fair-dpos.md b/@l10n/ru/docs/consensus/fair-dpos.md new file mode 100644 index 0000000000..b44c3af2e2 --- /dev/null +++ b/@l10n/ru/docs/consensus/fair-dpos.md @@ -0,0 +1,170 @@ +# Консенсус Fair-DPOS + +VIZ Ledger использует **Fair Delegated Proof of Stake (Fair-DPOS)** — расширение классического алгоритма DPoS, добавляющее контроль участия и штрафы за пропуски блоков для предотвращения получения вознаграждений без реального производства. + +--- + +## Как работает классический DPoS + +В стандартном DPoS: +- Держатели токенов голосуют за аккаунты-валидаторы (с весом, пропорциональным SHARES). +- Валидаторы с наибольшим числом голосов планируются для производства блоков в порядке round-robin. +- Каждые 3 секунды срабатывает один слот; запланированный валидатор либо производит блок, либо пропускает слот. + +VIZ работает с **21 активным валидатором** в каждом раунде расписания. + +--- + +## Расширение «Fair» + +В классическом DPoS валидатор может бесконечно пропускать блоки и при этом продолжать получать голоса (и иногда вознаграждения). Fair-DPOS добавляет: + +1. **Отслеживание участия** — 128-битная битовая маска отслеживает последние 128 слотов. Каждый слот помечается 1 (произведён) или 0 (пропущен). +2. **Порог участия** — узел не производит блоки, если менее `required-participation`% последних слотов было заполнено любым валидатором (по умолчанию 33%). Это защита от сценария minority fork. +3. **Штрафы за пропуски** — валидаторы, пропускающие блоки, накапливают счётчик пропусков. При каждой оценке хардфорка наихудшие исполнители могут быть исключены из активного набора. +4. **Распределение вознаграждений** (HF13) — блочные вознаграждения валидаторов частично перераспределяются их голосующим, согласовывая стимулы делегаторов с производительностью валидаторов. + +--- + +## Расписание валидаторов + +### Построение расписания + +Каждые `CHAIN_WITNESS_SCHEDULE_BLOCK_NUM` блоков (21) цепочка пересчитывает активный набор валидаторов: + +1. Выбираются 21 валидатор с наибольшим суммарным весом голосов (SHARES, делегированные им). +2. Добавляются **валидаторы с долей времени** — ротируемый слот для нижестоящих валидаторов, чтобы они могли периодически участвовать и предотвращать полную концентрацию. +3. Результирующие 21 слот перемешиваются с использованием идентификатора головного блока в качестве источника энтропии (детерминированное перемешивание = одинаковый результат на всех узлах). + +Полученный упорядоченный список становится **текущим расписанием**. Каждая позиция соответствует 3-секундному слоту. + +### Назначение слота + +При текущем времени `T`: + +``` +slot_num = (T - genesis_time) / CHAIN_BLOCK_INTERVAL +scheduled = schedule[slot_num % num_scheduled_validators] +``` + +Временны́е метки блоков всегда являются **детерминированным временем слота**, а не реальными часами: +``` +block_time = genesis_time + slot_num × 3s +``` + +### Пропущенные слоты + +Когда валидатор пропускает свой слот, `update_global_dynamic_data()` увеличивает `current_aslot` и помечает слот как пропущенный в битовой маске участия. Другие валидаторы не заполняют пропущенный слот — 3-секундный ритм продолжается со следующего слота независимо от этого. + +--- + +## Коэффициент участия + +Коэффициент участия вычисляется как: + +``` +participation = popcount(recent_slots_filled) / 128 +``` + +где `recent_slots_filled` — 128-битное скользящее окно результатов слотов. + +**Производство валидаторов блокируется при падении участия ниже `required-participation`** (по умолчанию 33%). Это предотвращает продолжение производства блоков узлом на minority fork, когда большая часть сети недоступна. + +Параметр конфигурации: +```ini +required-participation = 33 # минимум %, 0–99 +``` + +--- + +## Последний необратимый блок (LIB) + +Блок становится необратимым, когда более 2/3 активных валидаторов построили на его основе. Цепочка отслеживает это в `last_irreversible_block_num`. + +``` +irreversibility_threshold = ceil(num_scheduled_validators * 2 / 3) +``` + +При 21 валидаторе: `ceil(21 × 2/3) = 14` подтверждений. Как только 14 валидаторов произвели блоки, являющиеся потомками блока N, блок N становится LIB. + +Продвижение LIB **приостанавливается в режиме экстренного консенсуса** (см. [Экстренный консенсус](./emergency-consensus.md)). + +--- + +## Голосование по хардфоркам + +Валидаторы участвуют в активации хардфорков, устанавливая свой `hardfork_version_vote` через `validator_update_operation`. Хардфорк N активируется, когда: + +1. Супербольшинство (>80%) текущего набора валидаторов подало сигнал поддержки. +2. Запланированная метка времени активации хардфорка наступила. + +Оба условия должны выполняться одновременно. Это позволяет операторам сети блокировать нежелательные хардфорки, воздерживаясь от голосования даже после наступления запланированного времени. + +--- + +## Защита от minority fork + +Если последние 21 последовательный блок произведены только валидаторами, входящими в настроенный набор данного узла, плагин валидатора заключает, что узел изолирован, и автоматически откатывается к LIB. Это и есть **защита от minority fork**. + +Проверка пропускается при: +- `enable-stale-production = true` (разработка/тестовая сеть) +- Активном режиме экстренного консенсуса + +--- + +## Режим экстренного консенсуса + +Если в течение `CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC` (по умолчанию 1 час) не было произведено ни одного блока, цепочка переходит в **режим чрезвычайной ситуации**: + +- Все 21 слот валидаторов назначаются `CHAIN_EMERGENCY_WITNESS_ACCOUNT` («committee»). +- Экстренный валидатор подписывает блоки с использованием `CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY`. +- Все штрафы валидаторов сбрасываются; отключённые валидаторы повторно включаются. +- Продвижение LIB приостанавливается в период чрезвычайной ситуации. +- Режим чрезвычайной ситуации завершается после того, как `CHAIN_EMERGENCY_EXIT_NORMAL_BLOCKS` (21) последовательных обычных блоков продвигают LIB выше начального блока чрезвычайной ситуации. + +Полная информация — в разделе [Экстренный консенсус](./emergency-consensus.md). + +--- + +## HF12: сравнение форков по весу голосов + +Начиная с хардфорка 12, при наличии двух конкурирующих форков на одной высоте блока цепочка использует **сравнение форков по весу голосов** вместо простой длиннейшей цепочки: + +1. Для каждой ветви форка суммируются SHARES, делегированные каждому уникальному валидатору, произведшему блок в этой ветви. +2. Применяется **бонус +10%** к более длинной цепочке для разрешения ничьих в пользу непрерывности производства. +3. Побеждает форк с большим суммарным весом голосов. +4. При равенстве текущий форк сохраняется до срабатывания таймаута разрешателя коллизий (21 отсрочка). + +Полный алгоритм коллизии форков — в разделе [Разрешение форков](./fork-resolution.md). + +--- + +## Сводка по конфигурации + +| Параметр | По умолчанию | Описание | +|---------|-------------|---------| +| `required-participation` | `33` (33%) | Минимальное участие для производства блоков | +| `enable-stale-production` | `false` | Обход проверки участия (только для тестовой сети) | +| `emergency-private-key` | — | Опциональный ключ подписи для экстренного консенсуса | +| Активные валидаторы | 21 | Задано в `CHAIN_MAX_WITNESSES` | +| Интервал блока | 3 с | `CHAIN_BLOCK_INTERVAL` | +| Порог LIB | ⌈21 × 2/3⌉ = 14 | Блоки, подтверждающие необратимость | +| Таймаут чрезвычайной ситуации | 3600 с | `CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC` | + +--- + +## Расположение ключевых источников + +| Компонент | Файл | +|----------|------| +| Построение расписания валидаторов | `libraries/chain/database.cpp` — `update_witness_schedule()` | +| Обновление битовой маски участия | `libraries/chain/database.cpp` — `update_global_dynamic_data()` | +| Продвижение LIB | `libraries/chain/database.cpp` — `update_last_irreversible_block()` | +| Голосование по хардфоркам | `libraries/chain/database.cpp` — `process_hardforks()` | +| Цикл производства | `plugins/validator/validator.cpp` — `maybe_produce_block()` | +| Активация режима чрезвычайной ситуации | `libraries/chain/database.cpp` — `check_emergency_consensus()` | +| Сравнение форков HF12 | `libraries/chain/database.cpp` — `compare_fork_branches()` | + +--- + +См. также: [Обработка блоков](./block-processing.md), [Разрешение форков](./fork-resolution.md), [Экстренный консенсус](./emergency-consensus.md), [Узел-валидатор](../node/validator-node.md). diff --git a/@l10n/ru/docs/consensus/fork-resolution.md b/@l10n/ru/docs/consensus/fork-resolution.md new file mode 100644 index 0000000000..4e8121e44a --- /dev/null +++ b/@l10n/ru/docs/consensus/fork-resolution.md @@ -0,0 +1,180 @@ +# Разрешение форков + +На этой странице описывается, как VIZ Ledger обнаруживает, выбирает и разрешает конкурирующие форки — от базовой базы данных форков до решателя коллизий HF12 с весом голосов. + +--- + +## База данных форков + +**База данных форков** (`fork_database`) — это хранящееся в памяти дерево кандидатных вершин цепочки. Каждый блок, полученный по P2P, вставляется сюда до применения к состоянию цепочки. + +Ключевые операции: +- `push_block(b)` — связать `b` с его родителем; если родитель неизвестен, кешировать в `_unlinked_index` +- `_push_next(item)` — когда родитель прибывает, итеративно связывать все кешированные дочерние блоки +- `fetch_branch_from(a, b)` — пройти обе ветви до общего предка +- `set_max_size(n)` — обрезать старые блоки из обоих linked и unlinked индексов + +### Обнаружение дубликатов + +Перед вставкой fork DB проверяет, существует ли уже блок с тем же ID. Если да — тихо игнорируется. Это предотвращает избыточную обработку блоков, повторно транслируемых при P2P-синхронизации. + +### Unlinked index + +Блоки, родитель которых ещё не в fork DB, хранятся в `_unlinked_index`. Когда родитель наконец прибывает: +1. `_push_block(parent)` связывает родителя. +2. `_push_next(parent)` итерирует `_unlinked_index` в поиске дочерних блоков родителя. +3. Дочерние блоки перемещаются в `_index` и рекурсивно связываются. +4. Голова fork DB может прыгнуть на несколько блоков за один вызов. + +Порог разрыва (100 блоков) предотвращает разбухание памяти: блоки более чем на 100 опережающие голову базы данных с неизвестным родителем тихо отклоняются до попадания в fork DB. + +--- + +## Выбор форка: правило самой длинной цепочки + +После вставки блока fork DB возвращает новую голову. Если новая голова выше головы базы данных и расходится с ней, предпринимается **переключение форка**. + +**До HF12:** Простое правило самой длинной цепочки — побеждает форк с наибольшим номером блока. + +--- + +## HF12: Сравнение форков с весом голосов + +Начиная с харфорка 12, при двух конкурирующих форках на одной высоте блока вместо простого правила длинной цепочки используется `compare_fork_branches()`: + +### Алгоритм + +1. **Получить ветви** через `fetch_branch_from(fork_a_tip, fork_b_tip)` до общего предка. +2. **Суммировать вес голосов по валидатору** для каждой ветви — только уникальные аккаунты валидаторов учитываются один раз на ветвь. Аккаунт экстренного валидатора (`"committee"`) исключается. +3. **Применить бонус +10%** к более длинной цепочке. +4. **Вернуть**: `+1` если ветвь A сильнее, `-1` если B сильнее, `0` если равенство. + +### Обработка коллизий форков + +Когда `compare_fork_branches()` вызывается из плагина validator: +- Если один форк явно сильнее → производить на этом форке. +- Если равенство или неопределённость → отложить (инкрементировать `fork_collision_defer_count_`). +- После **21 последовательного откладывания** (один полный раунд валидаторов) → таймаут: вызвать `remove_blocks_by_number(height)` для очистки устаревших конкурирующих блоков, затем производить на канонической цепочке. + +| Условие | Флаг `peer_needs_sync_items_from_us` | +|---------|-------------------------------------| +| Ответ пустой | `false` — наша цепочка пуста | +| Ответ = 1 элемент в synopsis | `false` — пир обновлён | +| Ответ >1 элемента, `remaining == 0` | `false` — пир почти обновлён (переключиться на инвентарь) | +| Ответ >1 элемента, `remaining > 0` | `true` — пир далеко позади (оставаться в режиме синхронизации) | + +--- + +## Процесс переключения форка + +Когда узел переключается на лучший форк: + +``` +1. fetch_branch_from(new_head, current_head) + → branches.first = [new_tip, ..., common_ancestor] + → branches.second = [current_tip, ..., common_ancestor] + +2. Проверка линейного продолжения: + branches.second.size() == 1 И common_ancestor == head + → Пропустить цикл извлечения; применить branches.first напрямую. + +3. Реальное переключение форка: + для каждого блока в branches.second (в обратном порядке): + FORK-SWITCH-POP: pop_block() ← сохранить txs в _popped_tx + для каждого блока в branches.first (в обратном порядке): + FORK-SWITCH-APPLY: apply_block() + +4. При исключении: + для каждого применённого выше блока: + FORK-RECOVER-POP: pop_block() ← отменить частичное применение + Инвалидировать неудавшийся форк. + Повторно бросить исключение. +``` + +Различие **линейного продолжения** критично в DLT-режиме, где LIB == head: цикл извлечения был бы бесконечным, поскольку сессии отмены уже зафиксированы. + +--- + +## Определение необратимого блока + +После каждого применения блока `update_last_irreversible_block()` продвигает Last Irreversible Block (LIB): + +1. Собирает `last_supported_block_num` для каждого из 21 запланированных валидаторов. +2. Сортирует и берёт `⌈21 × 25%⌉ = 5`-й снизу (т.е. значение, на уровне которого или выше находятся 75% валидаторов). +3. Полученный номер блока становится новым LIB. + +После того как блок становится LIB, он записывается в `block_log` (или `dlt_block_log` в DLT-режиме), а его сессия отмены фиксируется. + +**LIB ограничен значением HEAD − 1 в режиме экстренного консенсуса** для предотвращения фиксации сессии отмены применяемого в данный момент блока. + +--- + +## Обрезка устаревших форков + +Два механизма предотвращают накопление устаревших данных: + +1. **`remove_blocks_by_number(num)`** — удаляет все блоки на конкретной высоте. Вызывается решателем коллизий форков после таймаута 21 откладывания. +2. **`set_max_size(n)`** — обрезает старые блоки из `_index` и `_unlinked_index`, когда fork DB превышает `n` записей. + +--- + +## Защита от minority fork + +Перед каждым производством блока плагин validator проверяет последние 21 блок в fork DB: + +- Если все 21 были произведены собственными настроенными валидаторами этого узла → узел изолирован на minority fork. +- Действие (`enable-stale-production = false`): вызвать `resync_from_lib()` — извлечь до LIB, сбросить fork DB, переинициировать P2P-синхронизацию, переподключить сид-узлы. +- Действие (`enable-stale-production = true`): зафиксировать предупреждение, продолжить производство. +- Активен экстренный консенсус → пропустить проверку (ожидается, что все слоты будут "нашими" для экстренного мастера). + +--- + +## Метрики коллизий форков (HF12) + +HF12 добавил два поля в `dynamic_global_property_object` для on-chain мониторинга: + +| Поле | Тип | Описание | +|------|-----|----------| +| `fork_collision_count` | `uint32_t` | Накопленное количество коллизий форков с genesis | +| `last_fork_collision_block_num` | `uint32_t` | Номер блока последней коллизии | + +Читается через `get_dynamic_global_properties`. + +--- + +## Диагностика fork DB + +Fork DB предоставляет O(1) аксессоры для мониторинга: + +| Метод | Возвращает | +|-------|-----------| +| `linked_size()` | Количество блоков в linked index | +| `unlinked_size()` | Количество блоков в unlinked index | +| `linked_min_block_num()` | Наименьший номер блока в linked index | +| `linked_max_block_num()` | Наибольший номер блока в linked index | +| `unlinked_min_block_num()` | Наименьший номер блока в unlinked index | +| `unlinked_max_block_num()` | Наибольший номер блока в unlinked index | + +Задача P2P-статистики записывает их каждые 5 минут: + +``` +Block storage | dlt_log: [79174319..79274318] | dlt_resizes: 412 | fork_db: linked=18 unlinked=0 +``` + +Растущий `unlinked_size`, который не уменьшается, указывает на постоянный разрыв в получаемом потоке блоков (проблема P2P-подключения или узел на изолированном форке). + +--- + +## Устранение неполадок + +| Симптом | Диагноз | +|---------|---------| +| Результат производства `fork_collision` | Конкурирующий блок на целевой высоте; дождитесь таймаута 21 откладывания или разрешения по весу голосов | +| Результат производства `minority_fork` | Узел изолирован; проверьте P2P-пиры и подключение к сидам | +| `unlinked_size` растёт неограниченно | Родительские блоки не поступают; проверьте P2P-подключение | +| Повторяющиеся переключения форков в логах | Сетевой раздел между двумя подмножествами валидаторов; изучите подключение между ними | +| Голова не продвигается в DLT-режиме | Путаница с линейным продолжением vs. переключением форка; проверьте логи `FORK-SWITCH-POP` | + +--- + +См. также: [Fair-DPOS](./fair-dpos.md), [Обработка блоков](./block-processing.md), [Экстренный консенсус](./emergency-consensus.md). diff --git a/@l10n/ru/docs/consensus/hardforks.md b/@l10n/ru/docs/consensus/hardforks.md new file mode 100644 index 0000000000..b1a1ca4271 --- /dev/null +++ b/@l10n/ru/docs/consensus/hardforks.md @@ -0,0 +1,171 @@ +# Харфорки + +Харфорк — это обновление сети, изменяющее правила консенсуса. Все узлы должны обновиться до запланированной временной метки активации; узлы, работающие на старом программном обеспечении, разойдутся с сетью после активации. + +--- + +## Механизм активации + +Каждый харфорк имеет: +- Уникальный **номер** (N). +- **Unix-временную метку** — самое раннее время по системным часам, когда харфорк может активироваться. +- **Суперквалифицированное большинство голосов валидаторов** — >80% текущего набора валидаторов должны подать сигнал новой версии харфорка через `validator_update_operation`. + +Оба условия должны выполняться одновременно. Валидаторы могут заблокировать нежелательный харфорк, удерживая свой голос за версию даже после наступления запланированной временной метки. + +--- + +## История харфорков + +| # | Версия | Ключевые изменения | +|---|---------|-------------------| +| 1–10 | 1.x – 2.x | Фундамент, социальный граф, система энергии, комитет, подписки | +| 11 | 3.0.0 | — | +| 12 | 3.1.0 | Метрики коллизий форков, сравнение форков с весом голосов, режим экстренного консенсуса, улучшения NTP | +| 13 | 3.2.0 | Совместное использование наград валидаторов с распределением пропорционально голосам | + +--- + +## Краткое описание HF12 + +HF12 (версия 3.1.0) ввёл: + +1. **Счётчик коллизий форков** — `fork_collision_count` и `last_fork_collision_block_num` добавлены в `dynamic_global_property_object`. Доступно через `get_dynamic_global_properties`. +2. **Сравнение форков с весом голосов** (`compare_fork_branches()`) — выбор форка использует общий делегированный SHARES по ветви валидатора + 10% бонус за более длинную цепочку. +3. **Режим экстренного консенсуса** — активируется автоматически через 1 час без блоков; аккаунт "committee" занимает все 21 слот. См. [Экстренный консенсус](./emergency-consensus.md). +4. **Авто-ресинхронизация minority fork** — плагин validator обнаруживает изоляцию узла (21 последовательный собственный блок) и откатывается до LIB. +5. **Улучшения NTP** — выделенный NTP-клиент с настраиваемыми серверами, интервалом и порогом времени туда-обратно. + +--- + +## Краткое описание HF13 + +HF13 (версия 3.2.0) ввёл: + +**Совместное использование наград валидаторов**: часть награды валидатора каждого блока перераспределяется пропорционально аккаунтам, проголосовавшим за этого валидатора (по их весу голосов в SHARES). + +- Новое поле в `witness_object`: `reward_percent` — доля награды за блок, делимая с голосующими (0–10000 базисных пунктов). +- Новая виртуальная операция: `validator_reward_virtual_operation` — срабатывает один раз при каждом распределении наград. +- Устанавливается через `validator_update_operation`. + +--- + +## Реализация нового харфорка + +### Шаг 1: Создать файл определения харфорка + +`libraries/chain/hardfork.d/N.hf`: + +```cpp +#ifndef CHAIN_HARDFORK_N +#define CHAIN_HARDFORK_N N +#define CHAIN_HARDFORK_N_TIME 1234567890 // Unix-временная метка — должна быть в будущем +#define CHAIN_HARDFORK_N_VERSION hardfork_version(3, N, 0) +#endif +``` + +### Шаг 2: Увеличить константы + +`libraries/chain/hardfork.d/0-preamble.hf`: +```cpp +#define CHAIN_NUM_HARDFORKS N +``` + +`libraries/protocol/include/graphene/protocol/config.hpp` (если видимо протоколу): +```cpp +#define CHAIN_VERSION (version(3, N, 0)) +``` + +### Шаг 3: Версия схемы + +Если изменяется какая-либо разметка объекта chainbase (новые поля, удалённые поля, изменённые типы), **увеличьте `CHAIN_SCHEMA_VERSION`** в `config.hpp`: + +```cpp +#define CHAIN_SCHEMA_VERSION uint32_t(N) +``` + +Плагин chain проверяет это при запуске. Несоответствие очищает `shared_memory.bin` перед открытием, предотвращая некорректное чтение из старых разметок. + +Новые поля всегда должны иметь **нулевые значения по умолчанию**, чтобы избежать кода миграции: +```cpp +uint16_t my_new_field = 0; +``` + +### Шаг 4: Подключить в database.cpp + +`init_hardforks()`: +```cpp +FC_ASSERT(CHAIN_HARDFORK_N == N); +_hardfork_times[N] = fc::time_point_sec(CHAIN_HARDFORK_N_TIME); +_hardfork_versions[N] = hardfork_version(CHAIN_HARDFORK_N_VERSION); +``` + +Случай в `apply_hardfork()`: +```cpp +case CHAIN_HARDFORK_N: { + // Миграция при необходимости. Оставить пустым с комментарием, если нулевые значения по умолчанию покрывают это. + break; +} +``` + +### Шаг 5: Операция и evaluator (при наличии новой операции) + +1. Добавить структуру в `chain_operations.hpp` с `validate()` и геттерами авторизации. +2. Добавить в `static_variant` в `operations.hpp`. +3. Объявить `DEFINE_EVALUATOR(my_new_op)` в `chain_evaluator.hpp`. +4. Реализовать `do_apply()` в `.cpp` файле evaluator — всегда сначала проверять `ASSERT_REQ_HF(CHAIN_HARDFORK_N, ...)`. +5. Зарегистрировать в `initialize_evaluators()` в `database.cpp`. + +### Шаг 6: Обновление плагинов + +| Плагин | Что обновить | +|--------|-------------| +| `account_history` | Добавить экстрактор воздействия для любой новой виртуальной операции | +| `witness_api` | Добавить новые поля из `witness_object` в `witness_api_object` | +| `snapshot` | Добавить новые объекты chainbase в `serialize_state` / `load_snapshot` | + +--- + +## Жизненный цикл версии схемы + +``` +Новый узел (без существующих данных): + stored = 0, compiled = N → несоответствие + очистить shared_memory (нет операций при отсутствии) + записать schema_version = N + genesis → нормальный запуск + +Обновление (старый бинарный файл имел версию M < N): + stored = M, compiled = N → несоответствие + очистить shared_memory.bin + записать schema_version = N + db.open() → исключение несоответствия ревизии + → авто-восстановление: импорт снимка + воспроизведение dlt_block_log + +Нормальный перезапуск: + stored = N, compiled = N → совпадение + db.open() продолжается нормально +``` + +**Ключевые файлы:** +- `config.hpp` — `CHAIN_SCHEMA_VERSION` +- `plugins/chain/plugin.cpp` — логика проверки схемы и очистки +- `/schema_version` — текстовый файл с текущей версией + +--- + +## Чеклист развёртывания + +- [ ] `CHAIN_NUM_HARDFORKS` увеличен +- [ ] `CHAIN_VERSION` обновлён (если видимо протоколу) +- [ ] `CHAIN_SCHEMA_VERSION` увеличен (если изменилась разметка объекта chainbase) +- [ ] Файл харфорка `.hf` создан с будущей временной меткой активации +- [ ] Все новые поля имеют нулевые значения по умолчанию; комментарий в `apply_hardfork` объясняет, почему миграция не нужна +- [ ] Новый evaluator зарегистрирован в `initialize_evaluators()` +- [ ] Новая виртуальная операция зарегистрирована в плагине `account_history` +- [ ] `witness_api_object` обновлён если изменился `witness_object` +- [ ] Плагин snapshot обновлён если добавлены новые объекты chainbase + +--- + +См. также: [Fair-DPOS](./fair-dpos.md), [Экстренный консенсус](./emergency-consensus.md), [Снимки](../node/snapshot.md). diff --git a/@l10n/ru/docs/development/building.md b/@l10n/ru/docs/development/building.md new file mode 100644 index 0000000000..491b11a837 --- /dev/null +++ b/@l10n/ru/docs/development/building.md @@ -0,0 +1,196 @@ +# Сборка + +Узел VIZ Ledger использует CMake 3.16+ и требует Boost 1.71+ с компонентом coroutine. Поддерживаемые платформы: Ubuntu 24.04+, macOS (Homebrew), Windows (MSVC или MinGW). + +--- + +## Linux (Ubuntu/Debian) + +### Шаг 1: Установка зависимостей (требуется root) + +```bash +chmod +x install-deps-linux.sh +sudo ./install-deps-linux.sh +``` + +Устанавливает: CMake, GCC/G++, Git, Make, ccache, OpenSSL, Boost 1.71 (все необходимые компоненты, включая coroutine/context), readline и библиотеки сжатия. + +### Шаг 2: Сборка (выполнять от обычного пользователя, не root) + +```bash +chmod +x build-linux.sh +./build-linux.sh +``` + +**Распространённые параметры:** + +```bash +./build-linux.sh # Release-сборка (по умолчанию) +./build-linux.sh -l # LOW_MEMORY_NODE (узлы-валидаторы) +./build-linux.sh -n # Testnet-сборка +./build-linux.sh -t Debug -j4 # Debug-сборка с 4 параллельными задачами +./build-linux.sh --skip-deps # Пропустить установку зависимостей +./build-linux.sh --install # Установить в систему после сборки + +# Пользовательские пути к зависимостям +./build-linux.sh --boost-root /opt/boost_1_74_0 --openssl-root /opt/openssl +``` + +### Fedora/RHEL + +Те же скрипты автоматически определяют `dnf`. Устанавливаемые пакеты: `cmake`, `gcc-c++`, `git`, `ccache`, `boost-devel`, `openssl-devel`, `bzip2-devel`, `zstd-devel`. + +--- + +## macOS + +```bash +chmod +x build-mac.sh +./build-mac.sh +``` + +Требует Xcode Command Line Tools и Homebrew. Скрипт устанавливает: `boost`, `cmake`, `git`, `autoconf`, `automake`, `libtool`, `openssl`, `readline`. + +**Параметры:** + +```bash +./build-mac.sh -l # Low-memory узел +./build-mac.sh -n # Testnet +./build-mac.sh --skip-deps # Пропустить установки Homebrew +./build-mac.sh --boost-root /opt/boost_1_74_0 +``` + +--- + +## Windows (MinGW) + +Установите необходимые переменные окружения, затем запустите batch-скрипт: + +```cmd +set BOOST_ROOT=C:\Boost +set OPENSSL_ROOT_DIR=C:\OpenSSL-Win64 +build-mingw.bat +``` + +**Опциональные переменные:** + +| Переменная | По умолчанию | Описание | +|-----------|-------------|---------| +| `VIZ_BUILD_TYPE` | Release | Release или Debug | +| `VIZ_LOW_MEMORY` | OFF | Включить low-memory узел | +| `VIZ_BUILD_TESTNET` | OFF | Testnet-сборка | +| `VIZ_FULL_STATIC` | OFF | Полностью статический бинарник | +| `VIZ_CMAKE_EXTRA` | — | Дополнительные флаги CMake | + +**Требования:** MinGW-w64 с C++11 и SSE4.2, CMake 3.16+, Boost 1.71+ (статический, `link=static threading=multi runtime-link=shared`), OpenSSL для Windows. + +--- + +## Windows (MSVC) + +```cmd +set BOOST_ROOT=C:\Boost +set OPENSSL_ROOT_DIR=C:\OpenSSL-Win64 +build-msvc.bat +``` + +**Опциональные переменные:** + +| Переменная | По умолчанию | Описание | +|-----------|-------------|---------| +| `VIZ_VS_VERSION` | "Visual Studio 17 2022" | Генератор Visual Studio | +| `VIZ_BUILD_TYPE` | Release | Тип сборки | +| `VIZ_LOW_MEMORY` | OFF | Low-memory узел | +| `VIZ_BUILD_TESTNET` | OFF | Testnet-сборка | + +**Требования:** Visual Studio 2019+ с нагрузкой "Desktop development with C++", CMake 3.16+. + +--- + +## Docker + +В репозитории поставляются Dockerfile для нескольких конфигураций: + +| Dockerfile | Описание | +|-----------|---------| +| `Dockerfile-production` | Полный узел, Release, без MongoDB | +| `Dockerfile-lowmem` | То же, но с `LOW_MEMORY_NODE=ON` | +| `Dockerfile-mongo` | Включён плагин MongoDB | +| `Dockerfile-testnet` | Testnet (`BUILD_TESTNET=ON`) | + +Все Dockerfile используют двухэтапную сборку для минимизации размера образа и пакеты Boost 1.71 (`libboost-coroutine-dev`, `libboost-context-dev`). + +--- + +## Параметры CMake + +| Параметр | По умолчанию | Описание | +|---------|-------------|---------| +| `BUILD_TESTNET` | OFF | Сборка для testnet | +| `LOW_MEMORY_NODE` | OFF | Исключить неконсенсусные данные (уменьшает RAM) | +| `CHAINBASE_CHECK_LOCKING` | OFF | Включить проверку блокировок (только для разработки) | +| `ENABLE_MONGO_PLUGIN` | OFF | Включить плагин MongoDB | +| `BUILD_SHARED_LIBRARIES` | OFF | Собирать разделяемые библиотеки | +| `USE_PCH` | OFF | Включить предкомпилированные заголовки (ускоряет пересборку) | + +--- + +## Расширенный вариант: `configure_build.py` + +Обёртка над CMake с разумными значениями по умолчанию и поддержкой кросс-компиляции: + +```bash +# Release-сборка +python3 programs/build_helpers/configure_build.py --release --src ../.. + +# Debug с low-memory +python3 programs/build_helpers/configure_build.py --debug --low-memory + +# Кросс-компиляция для Windows с MinGW +python3 programs/build_helpers/configure_build.py --win --release + +# Пользовательские пути к зависимостям +python3 programs/build_helpers/configure_build.py \ + --boost-dir /opt/boost_1_74_0 \ + --openssl-dir /opt/openssl \ + --release +``` + +--- + +## Создание каркаса нового плагина + +```bash +python3 programs/util/newplugin.py graphene myplugin +``` + +Генерирует: `CMakeLists.txt`, заголовок/реализацию плагина, заголовок/реализацию API в `libraries/plugins/myplugin/`. + +--- + +## Цели сборки + +| Бинарник | Описание | +|---------|---------| +| `vizd` | Основной демон узла | +| `cli_wallet` | Кошелёк командной строки | +| `js_operation_serializer` | Сериализатор операций для JavaScript | +| `size_checker` | Утилита анализа размеров | + +--- + +## Устранение неполадок + +**Версия Boost ниже 1.71:** Установите Boost 1.71+ из пакетного менеджера (Ubuntu 24.04 поставляет 1.74). На macOS `brew install boost` предоставляет актуальную версию. На Windows соберите из исходников с компонентом coroutine. + +**Ошибка `Do not run this script as root`:** Используйте `sudo ./install-deps-linux.sh` для зависимостей, затем запускайте `./build-linux.sh` от обычного пользователя. + +**Отсутствует компонент coroutine:** Убедитесь, что на Ubuntu/Debian установлены `libboost-coroutine-dev` и `libboost-context-dev`. + +**macOS: OpenSSL не найден:** Задайте `OPENSSL_ROOT_DIR` вручную: `export OPENSSL_ROOT_DIR=$(brew --prefix openssl)`. + +**Windows MinGW: отсутствуют переменные:** Перед запуском `build-mingw.bat` должны быть заданы `BOOST_ROOT` и `OPENSSL_ROOT_DIR`. + +--- + +См. также: [Разработка плагинов](./plugin-development.md), [Тестирование](./testing.md), [Обзор плагинов](../plugins/overview.md). diff --git a/@l10n/ru/docs/development/debugging.md b/@l10n/ru/docs/development/debugging.md new file mode 100644 index 0000000000..74ac249d76 --- /dev/null +++ b/@l10n/ru/docs/development/debugging.md @@ -0,0 +1,186 @@ +# Отладка + +Узел VIZ Ledger предоставляет несколько инструментов отладки: плагин `debug_node` для манипулирования состоянием и реплея, утилиты подписания транзакций для криптографической диагностики, а также логирование P2P-плагина с цветовыми кодами ANSI для анализа сети. + +--- + +## Плагин debug_node + +Плагин `debug_node` предоставляет JSON-RPC API для: +- Реплея блоков из block log или JSON-массива +- Локальной генерации блоков с настраиваемым ключом подписи +- Откатки блоков (pop) для отмены состояния +- Просмотра расписания валидаторов и состояния хардфорков +- Применения изменений БД на определённых высотах блоков + +**Включение с ограниченным RPC (только localhost):** + +```ini +plugin = debug_node +webserver-http-endpoint = 127.0.0.1:8090 +``` + +### Справочник API + +| Метод | Описание | +|-------|---------| +| `debug_push_blocks(src, count)` | Загрузить блоки из каталога block log | +| `debug_push_json_blocks(file, count, skip)` | Загрузить блоки из JSON-файла с массивом | +| `debug_generate_blocks(key, count, skip, miss, edit)` | Генерировать блоки с заданным ключом подписи | +| `debug_generate_blocks_until(key, time, sparse, skip)` | Продвинуть цепочку до целевого времени | +| `debug_pop_block()` | Удалить head-блок, вернув его | +| `debug_get_witness_schedule()` | Получить текущий объект расписания валидаторов | +| `debug_set_hardfork(id)` | Программно задать состояние хардфорка | +| `debug_has_hardfork(id)` | Проверить, применён ли хардфорк | + +### Примеры использования + +```json +// Реплей 100 блоков из block log +{"method":"debug_node.debug_push_blocks","params":["/data/blockchain",100]} + +// Генерация 10 блоков с ключом подписи (пропустить валидацию) +{"method":"debug_node.debug_generate_blocks","params":["5K...",10,2,0,{}]} + +// Просмотр расписания валидаторов +{"method":"debug_node.debug_get_witness_schedule","params":[]} + +// Активация хардфорка 9 для тестирования +{"method":"debug_node.debug_set_hardfork","params":[9]} +``` + +**Генерация блоков** временно изменяет ключ подписи активного валидатора для приёма самоподписанных блоков, затем восстанавливает исходный ключ. + +**Хуки обновления БД** позволяют инъецировать изменения состояния на определённых высотах блоков: + +```cpp +// Из кода плагина +debug_plugin.debug_update([&](database& db) { + // Изменить состояние db здесь +}, skip_flags); +``` + +--- + +## Утилиты подписания транзакций + +### sign_transaction + +Читает JSON-запросы на подписание из stdin (по одному в строку), вычисляет дайджест транзакции и подпись, и выводит результаты: + +```bash +echo '{"ref_block_num":1234,"ref_block_prefix":5678,...}' | ./sign_transaction +``` + +Вывод включает: `digest`, `sig_digest`, `key` (публичный ключ) и `signature`. + +**Диагностика сбоев подписания:** +1. Вычислите `sig_digest` с помощью `sign_transaction`. +2. Сравните с `sig_digest(chain_id)` из кошелька. +3. Убедитесь, что WIF-ключ соответствует заявленному ключу подписи. + +### sign_digest + +Подписывает сырой SHA-256 дайджест с помощью WIF-ключа: + +```bash +echo '{"digest":"abc123...","wif":"5K..."}' | ./sign_digest +``` + +Полезна для подтверждения корректности chain ID и изоляции проблем пластичности подписи. + +--- + +## Отладка сети (P2P-логи) + +P2P-плагин использует ANSI-цветовые коды для визуального разграничения в консольном выводе: + +| Цвет | ANSI-код | Содержимое | +|------|---------|-----------| +| Белый | `\033[97m` | Обработка блоков: количество транзакций, задержка | +| Голубой | `\033[96m` | Статистика пиров: количество соединений, байты, RTT | +| Серый | `\033[90m` | Детальный контекст отладки: DLT-режим, состояние синхронизации | +| Оранжевый | — | Предупреждения о соединениях и уведомления об отключении | +| Красный | — | Критические события завершения соединения | + +**Чтение P2P-логов:** +- **Белый**: Быстрая оценка активности обработки блоков и объёма транзакций. +- **Голубой**: Мониторинг количества пиров и состояния соединений в реальном времени. +- **Серый**: Исследование DLT-режима и деталей протокола синхронизации. +- **Оранжевый/Красный**: Выявление сбоев соединений и событий блокировки пиров. + +### Отдельный логгер сети + +Сообщения согласования синхронизации идут через логгер `"sync"`. Включение в `config.ini`: + +```ini +[logger.sync] +level = info +appenders = stderr +``` + +Сообщения P2P-узла используют логгер `"p2p"` (не логгер по умолчанию): + +```ini +[logger.p2p] +level = info +appenders = stderr +``` + +--- + +## Конфигурация отладки + +`share/vizd/config/config_debug.ini` — шаблон конфигурации, оптимизированный для отладки: + +- Увеличенные размеры разделяемой памяти и пороги роста для длительных реплеев. +- Единственный поток записи для детерминированной генерации блоков. +- Настроенные счётчики повторов блокировок чтения/записи. + +Ключевые настройки: + +```ini +shared-file-size = 12G +shared-file-full-threshold = 97 +shared-file-scale-rate = 3 +chainbase-check-locking = 0 +``` + +--- + +## Рабочие процессы отладки + +### Сбой валидации транзакции + +1. Запустите `sign_transaction` на JSON проблемной транзакции. +2. Сравните вычисленный `sig_digest` со значением из кошелька. +3. Убедитесь, что WIF-ключ соответствует authority аккаунта. +4. Воспроизведите блоки, содержащие транзакцию, с помощью `debug_push_blocks` и изучите логи. + +### Стопор консенсуса + +1. Используйте `debug_generate_blocks` для детерминированного продвижения цепочки. +2. Осмотрите расписание валидаторов с помощью `debug_get_witness_schedule`. +3. При необходимости задайте состояние хардфорка через `debug_set_hardfork` для тестирования логики активации. + +### Проблемы с сетевым подключением + +1. Проверьте **голубые логи** по количеству пиров и состоянию соединений. +2. Проверьте **белые логи** по задержке приёма блоков и пропускам. +3. Проверьте **серые логи** по состоянию DLT-синхронизации при синхронизации снапшотов. +4. Проверьте **оранжевые/красные логи** по событиям завершения соединений и блокировке пиров. +5. Сопоставьте исключения при применении блоков с конкретными номерами блоков в логах. + +### Ускорение интеграционного тестирования + +Реплей блоков из JSON-лога с флагами пропуска для обхода дорогостоящей валидации: + +```json +{"method":"debug_node.debug_push_json_blocks","params":["/tmp/blocks.json",100,2]} +``` + +Флаги пропуска: `1` = пропустить undo-сессию, `2` = пропустить подпись валидатора, `4` = пропустить проверку merkle. + +--- + +См. также: [Сборка](./building.md), [Тестирование](./testing.md), [Обзор P2P](../p2p/overview.md), [Обзор плагинов](../plugins/overview.md). diff --git a/@l10n/ru/docs/development/plugin-development.md b/@l10n/ru/docs/development/plugin-development.md new file mode 100644 index 0000000000..1b3da6588e --- /dev/null +++ b/@l10n/ru/docs/development/plugin-development.md @@ -0,0 +1,252 @@ +# Разработка плагинов + +Система плагинов VIZ Ledger построена на AppBase. Каждый плагин следует единому жизненному циклу, регистрирует свой API в JSON-RPC слое и подписывается на сигналы базы данных цепочки. + +--- + +## Структура плагина + +Плагин состоит из: + +- **Заголовка** (`include/graphene/plugins//plugin.hpp`) — объявляет класс плагина и его API. +- **Реализации** (`plugin.cpp`) — хуки жизненного цикла, подписки на сигналы, тела API-методов. +- **CMakeLists.txt** — объявляет цель и связывает зависимости. + +### Создание каркаса нового плагина + +```bash +python3 programs/util/newplugin.py graphene myplugin +``` + +Генерирует шаблонный код в `plugins/myplugin/`: +- `CMakeLists.txt` +- `include/graphene/plugins/myplugin/plugin.hpp` +- `plugin.cpp` +- Заголовок и реализация API + +--- + +## Жизненный цикл + +``` +plugin_initialize(options) + └── Зарегистрировать фабрику API + └── Разобрать опции + +plugin_startup() + └── Подключиться к сигналам БД + └── Запустить фоновые потоки + +plugin_shutdown() + └── Отключить сигналы + └── Остановить фоновые потоки +``` + +Все три метода вызываются AppBase в порядке зависимостей. Никогда не вызывайте `plugin_startup()` напрямую. + +--- + +## Регистрация JSON-RPC API + +Плагины регистрируют методы в плагине `json_rpc` с помощью макросного посетителя: + +```cpp +// В plugin.hpp — объявить API +DECLARE_API( + (get_account_history) + (get_ops_in_block) +) + +// В plugin.cpp — при запуске +plugin_startup() { + auto& json_rpc = appbase::app().get_plugin(); + json_rpc.add_api( + MAKE_API(this, get_account_history) + MAKE_API(this, get_ops_in_block) + ); +} +``` + +Каждый API-метод принимает одну структуру аргументов и возвращает одну структуру результата. Void-методы используют специальный пустой тип результата. + +**Именование методов:** JSON-RPC имя метода — `<пространство_имён_плагина>.<имя_метода>`. Например, `account_history.get_account_history`. + +--- + +## Сигналы базы данных + +База данных цепочки излучает сигналы, на которые подписываются плагины: + +| Сигнал | Триггер | +|--------|---------| +| `applied_block` | После применения блока (постсостояние) | +| `pre_apply_operation` | Перед применением каждой операции | +| `on_applied_transaction` | После применения транзакции | +| `post_apply_operation` | После применения каждой операции | + +```cpp +// Подключение в plugin_startup() +auto& db = appbase::app().get_plugin().db(); + +db.applied_block.connect([this](const signed_block& b) { + on_applied_block(b); +}); + +db.pre_apply_operation.connect([this](const operation_notification& note) { + on_pre_apply_operation(note); +}); +``` + +**Важно:** Обработчики сигналов выполняются синхронно в процессе обработки блоков. Не выполняйте тяжёлую работу внутри них — ставьте задачи в очередь фонового потока. + +--- + +## Доступ к базе данных + +### Чтение (из API-методов) + +Используйте слабую блокировку чтения для минимизации конкуренции: + +```cpp +auto& db = appbase::app().get_plugin().db(); +// В API-обработчиках db автоматически блокируется для чтения +auto account = db.get_account("alice"); +``` + +### Запись (из обработчиков сигналов или эвалуаторов) + +Пишите только внутри обработчиков сигналов или эвалуаторов — никогда из API-методов. + +```cpp +// Внутри обработчика applied_block +db.modify(db.get_account("alice"), [](account_object& a) { + a.some_field = new_value; +}); +``` + +--- + +## Пользовательские индексы базы данных + +Плагины могут добавлять собственные индексы в БД: + +```cpp +// В plugin_startup(), после инициализации цепочки +auto& db = appbase::app().get_plugin().db(); +db.add_plugin_index(); +``` + +Определяйте объект и индекс в заголовках по образцу существующих объектов: + +```cpp +// Определение объекта +class my_object : public chainbase::object { + id_type id; + account_name_type account; + uint64_t some_field; +}; + +// Контейнер MultiIndex +using my_index = chainbase::shared_multi_index_container< + my_object, + indexed_by< + ordered_unique, member>, + ordered_unique, member> + > +>; +``` + +--- + +## Пользовательские эвалуаторы операций + +Для обработки новых типов операций: + +```cpp +// Определить операцию в протокольном слое и зарегистрировать эвалуатор +class my_operation_evaluator : public evaluator { +public: + void do_apply(const my_operation& op) { + // Валидация и применение изменений состояния + auto& db = this->db(); + // ... + } +}; + +// Регистрация при инициализации БД +db.register_evaluator(); +``` + +Используйте проверки `has_hardfork(CHAIN_HARDFORK_N)` для управления изменениями поведения с целью обратной совместимости. + +--- + +## WebSocket события реального времени + +Для отправки уведомлений в реальном времени: + +```cpp +// При plugin_startup(), зарегистрировать callback блока в веб-сервере +auto& ws = appbase::app().get_plugin(); +ws.add_handler("my_stream", [this](const fc::variant& params, fc::variant& result) { + // Обработчик потока +}); +``` + +Плагин веб-сервера работает в собственном потоке `io_service` — постите callback'и из любого потока с помощью `ws.post([]{...})`. + +--- + +## Объявление зависимостей + +Объявляйте зависимости в `plugin_requires()` вашего плагина: + +```cpp +static std::vector plugin_requires() { + return { &appbase::app().get_plugin(), + &appbase::app().get_plugin() }; +} +``` + +AppBase автоматически разрешает порядок инициализации. + +--- + +## Руководящие принципы производительности + +- **API-методы**: Используйте индексированные поиски, а не полное сканирование. Добавляйте индексы плагинов для горячих паттернов доступа. +- **Обработчики сигналов**: Возвращайте управление быстро. Ставьте тяжёлую обработку в очередь выделенного `fc::thread`. +- **Кэширование**: Кэшируйте результаты горячих путей в памяти; инвалидируйте при `applied_block`. +- **Пагинация**: Всегда разбивайте на страницы большие наборы результатов вместо возврата неограниченных коллекций. + +--- + +## Тестирование плагинов + +Используйте плагин `debug_node` для симуляции условий в цепочке: + +```json +{"method":"debug_node.debug_generate_blocks","params":["5K...",10,0,0,{}]} +``` + +Пишите модульные тесты с помощью Boost.Test и существующей тестовой оснастки. Добавляйте тесты в соответствующий категорийный набор (`operation_tests`, `block_tests` и т.п.). + +Для интеграционных тестов загружайте плагин вместе с цепочкой и воспроизводите известную последовательность блоков с помощью `debug_push_blocks`. + +--- + +## Развёртывание + +Включайте плагины в `config.ini`: + +```ini +plugin = myplugin +``` + +Некоторые плагины требуют полного реиндексирования при включении на существующей цепочке (особенно те, которые отслеживают историю операций). Документируйте это требование явно. + +Для внешних (сторонних) плагинов помещайте их в `plugins/external/` — CMake обнаруживает их автоматически. + +--- + +См. также: [Обзор плагинов](../plugins/overview.md), [Database API](../plugins/database-api.md), [Сборка](./building.md), [Отладка](./debugging.md). diff --git a/@l10n/ru/docs/development/testing.md b/@l10n/ru/docs/development/testing.md new file mode 100644 index 0000000000..f05c628a67 --- /dev/null +++ b/@l10n/ru/docs/development/testing.md @@ -0,0 +1,159 @@ +# Тестирование + +VIZ Ledger использует Boost.Test для модульных тестов и предоставляет вспомогательные программы для интеграционного тестирования. + +--- + +## Модульные тесты + +Бинарник модульных тестов собирается через CMake в рамках цели `libraries/chain`. + +### Категории тестов + +| Набор | Описание | +|-------|---------| +| `basic_tests` | Проверка основной функциональности | +| `block_tests` | Логика, специфичная для блокчейна | +| `live_tests` | Проверка сценариев прошлых хардфорков | +| `operation_tests` | Проверка операций | +| `operation_time_tests` | Временно-зависимые операции (вывод вестинга и т.п.) | +| `serialization_tests` | Проверки цикличной сериализации | + +### Запуск тестов + +```bash +# Запустить все тесты +./tests/chain_test + +# Фильтр по набору +./tests/chain_test --run_test=basic_tests + +# Фильтр по конкретному тест-кейсу +./tests/chain_test --run_test=basic_tests/my_test_case + +# Настройка детализации +./tests/chain_test --log_level=all --report_level=detailed +``` + +### Параметры времени выполнения Boost.Test + +| Параметр | Значения | Описание | +|---------|---------|---------| +| `--log_level` | all, success, test_suite, message, warning, error, cpp_exception, system_error, fatal_error, nothing | Детализация лога | +| `--report_level` | no, confirm, short, detailed | Детализация отчёта | +| `--run_test` | `` или `/` | Фильтр запускаемых тестов | + +--- + +## Покрытие кода + +Включите покрытие в Debug-сборке: + +```bash +cmake -DCMAKE_BUILD_TYPE=Debug -DCOVERAGE=ON .. +make chain_test + +# Захватить базовую линию +lcov --capture --initial --directory . --output-file base.info + +# Запустить тесты +./tests/chain_test + +# Захватить трассировку тестов +lcov --capture --directory . --output-file test.info + +# Объединить и очистить +lcov --add-tracefile base.info --add-tracefile test.info --output-file merged.info +lcov --remove merged.info '/usr/*' '*/tests/*' --output-file coverage.info + +# Сгенерировать HTML-отчёт +genhtml coverage.info --output-directory coverage_report +``` + +--- + +## Интеграционные утилиты + +### Утилита тестирования block log + +Утилита `test_block_log` проверяет хранение и извлечение блоков: + +```bash +# Собирается в programs/util/test_block_log +./test_block_log /tmp/test_block_log_dir +``` + +Открывает block log, добавляет подписанные блоки, сбрасывает на диск и читает обратно. Полезна для проверки логики хранения блоков. + +### Утилиты подписания транзакций + +```bash +# Подписать транзакцию (JSON-вход построчно) +echo '{"ref_block_num":...}' | ./sign_transaction + +# Подписать сырой дайджест +echo '{"digest":"...","wif":"5K..."}' | ./sign_digest +``` + +Обе утилиты выводят вычисленные `digest`, `sig_digest`, ключ подписи и подпись. Полезны для диагностики сбоев подписания путём сравнения sig_digest с подписями из кошелька. + +--- + +## Тестовый API-плагин + +Плагин `test_api` предоставляет два JSON-RPC API (`test_api_a` и `test_api_b`) для интеграционных тестов. Регистрируется в `programs/vizd/main.cpp` и загружается процессом узла. + +--- + +## Тестовая сеть + +Для изолированного тестирования используйте конфигурацию testnet: + +```bash +# Запустить узел testnet +vizd --config share/vizd/config/config_testnet.ini + +# Или собрать Docker-образ testnet +docker build -f share/vizd/docker/Dockerfile-testnet -t viz-testnet . +``` + +Снапшот `share/vizd/snapshot-testnet.json` доступен для быстрой инициализации testnet. + +--- + +## Непрерывная интеграция + +CI-матрица собирает Docker-образы для нескольких вариантов: + +| Вариант | Dockerfile | +|---------|-----------| +| Стандартный | `Dockerfile-production` | +| Low-memory | `Dockerfile-lowmem` | +| MongoDB | `Dockerfile-mongo` | +| Testnet | `Dockerfile-testnet` | + +Сборки запускаются для каждой ветки и тега, с публикацией артефактов при наличии учётных данных. + +--- + +## Написание новых тестов + +Добавляйте новые наборы тестов к существующей цели с помощью макросов Boost.Test: + +```cpp +#include + +BOOST_AUTO_TEST_SUITE(my_feature_tests) + +BOOST_AUTO_TEST_CASE(basic_case) { + BOOST_CHECK_EQUAL(1 + 1, 2); +} + +BOOST_AUTO_TEST_SUITE_END() +``` + +Группируйте тесты в соответствующий категорийный набор. Отдавайте предпочтение интеграционным тестам, работающим с реальным состоянием цепочки, а не моковым — для выявления расхождений между моком и продакшен-поведением. + +--- + +См. также: [Сборка](./building.md), [Отладка](./debugging.md), [Разработка плагинов](./plugin-development.md). diff --git a/@l10n/ru/docs/governance/chain-properties.md b/@l10n/ru/docs/governance/chain-properties.md new file mode 100644 index 0000000000..2e59ed2281 --- /dev/null +++ b/@l10n/ru/docs/governance/chain-properties.md @@ -0,0 +1,158 @@ +# Параметры цепочки + +Параметры цепочки — это управляемые параметры сети: комиссии, размер блока, уровни инфляции, правила штрафов и другое. Центрального органа, устанавливающего их, нет — каждый активный валидатор публикует свои предпочтительные значения, а блокчейн применяет **медиану** по всем активным валидаторам. + +--- + +## Как это работает + +### 1. Валидаторы публикуют предпочтения + +Каждый валидатор отправляет предпочтительные параметры через `versioned_chain_properties_update_operation`: + +```json +[46, { + "owner": "alice", + "props": [3, { + "account_creation_fee": "1.000 VIZ", + "maximum_block_size": 131072, + ... + }] +}] +``` + +`[3, {...}]` указывает версию 3 (`chain_properties_hf9`, текущий формат). + +### 2. Расчёт медианы + +При каждом обновлении расписания валидаторов блокчейн вызывает `update_median_witness_props()`. Для **каждого параметра независимо** он: +1. Собирает значения от каждого активного валидатора. +2. Сортирует их. +3. Выбирает **медиану** (индекс `active.size() / 2`). + +``` +Пример — 5 валидаторов голосуют за account_creation_fee: + 0.5, 1.0, 1.0, 2.0, 5.0 VIZ + ↑ + медиана = 1.0 VIZ +``` + +Медиана устойчива к экстремумам: один валидатор не может вызвать внезапный крупный сдвиг; для значительного изменения любого параметра необходимо согласие большинства. + +### 3. Применение + +Результирующий объект `median_props` сохраняется в `witness_schedule_object` и применяется при обработке всех блоков. + +--- + +## Все управляемые параметры + +### Аккаунт и делегирование + +| Параметр | Тип | По умолчанию | Описание | +|---------|-----|------------|---------| +| `account_creation_fee` | asset (VIZ) | 1.000 VIZ | Минимальная комиссия за создание нового аккаунта | +| `create_account_delegation_ratio` | uint32 | 10 | Необходимое делегирование = ratio × fee | +| `create_account_delegation_time` | uint32 (с) | 2592000 (30д) | Время блокировки делегирования при создании | +| `min_delegation` | asset (VIZ) | 1.000 VIZ | Минимальная сумма для любого делегирования SHARES | + +### Размер блока и пропускная способность + +| Параметр | Тип | По умолчанию | Описание | +|---------|-----|------------|---------| +| `maximum_block_size` | uint32 (байт) | 131072 | Максимальный размер блока; управляет пропускной способностью | +| `bandwidth_reserve_percent` | uint16 (bp) | 1000 (10%) | Дополнительная пропускная способность для малых аккаунтов | +| `bandwidth_reserve_below` | asset (SHARES) | 500.000000 | Порог для получения резерва пропускной способности | +| `data_operations_cost_additional_bandwidth` | uint32 (%) | 0 | Множитель дополнительной пропускной способности для операций с данными (custom_operation) | + +### Инфляция и экономика + +| Параметр | Тип | По умолчанию | Описание | +|---------|-----|------------|---------| +| `inflation_witness_percent` | uint16 (bp) | 2000 (20%) | Доля валидаторов от блочной инфляции | +| `inflation_ratio_committee_vs_reward_fund` | uint16 (bp) | 5000 (50%) | Разделение оставшейся инфляции: фонд комитета vs фонд вознаграждений | +| `inflation_recalc_period` | uint32 (блоки) | 806400 (~28д) | Как часто пересчитывается инфляция | + +Поток инфляции: `block_reward × inflation_witness_percent` → валидатор. Остаток делится: `inflation_ratio_committee_vs_reward_fund` → фонд комитета; остальное → фонд вознаграждений за награды. + +### Система вознаграждений + +| Параметр | Тип | По умолчанию | Описание | +|---------|-----|------------|---------| +| `min_curation_percent` | uint16 (bp) | 500 (5%) | Минимальная доля кураторского вознаграждения из выплат за контент | +| `max_curation_percent` | uint16 (bp) | 500 (5%) | Максимальная доля кураторского вознаграждения | +| `vote_accounting_min_rshares` | uint32 | 5000000 | Минимальные rshares для ненулевого вознаграждения за награду | +| `flag_energy_additional_cost` | uint16 (bp) | 0 | Дополнительная стоимость энергии для даунвотов/флагов | + +### Ответственность валидаторов + +| Параметр | Тип | По умолчанию | Описание | +|---------|-----|------------|---------| +| `witness_miss_penalty_percent` | uint16 (bp) | 100 (1%) | Снижение веса голосов при пропуске блока | +| `witness_miss_penalty_duration` | uint32 (с) | 86400 (1д) | Продолжительность штрафа за пропуск | + +### Комиссии + +Все комиссии поступают в фонд комитета (казна DAO). + +| Параметр | Тип | По умолчанию | Описание | +|---------|-----|------------|---------| +| `committee_create_request_fee` | asset (VIZ) | 100.000 VIZ | Комиссия за создание заявки на финансирование комитета | +| `create_paid_subscription_fee` | asset (VIZ) | 100.000 VIZ | Комиссия за создание платной подписки | +| `account_on_sale_fee` | asset (VIZ) | 10.000 VIZ | Комиссия за выставление аккаунта на продажу | +| `subaccount_on_sale_fee` | asset (VIZ) | 100.000 VIZ | Комиссия за выставление прав создания субаккаунта на продажу | +| `witness_declaration_fee` | asset (VIZ) | 10.000 VIZ | Единовременная комиссия за регистрацию валидатора | +| `create_invite_min_balance` | asset (VIZ) | 10.000 VIZ | Минимальный баланс инвайта | + +### Вывод из вестинга + +| Параметр | Тип | По умолчанию | Описание | +|---------|-----|------------|---------| +| `withdraw_intervals` | uint16 | 28 | Количество ежедневных платежей при анстейкинге SHARES | + +--- + +## Версии параметров + +Параметры вводились поэтапно при хардфорках: + +| Версия | Индекс | Хардфорк | Добавленные поля | +|--------|--------|---------|----------------| +| `chain_properties_init` | 0 | Генезис | account_creation_fee, maximum_block_size, параметры делегирования, курация, пропускная способность, стоимость флага, минимальные rshares, порог комитета | +| `chain_properties_hf4` | 1 | HF4 | inflation_witness_percent, inflation_ratio_committee_vs_reward_fund, inflation_recalc_period | +| `chain_properties_hf6` | 2 | HF6 | data_operations_cost_additional_bandwidth, witness_miss_penalty_percent, witness_miss_penalty_duration | +| `chain_properties_hf9` | 3 | HF9 | create_invite_min_balance, committee_create_request_fee, create_paid_subscription_fee, account_on_sale_fee, subaccount_on_sale_fee, witness_declaration_fee, withdraw_intervals | + +Для всех новых публикаций параметров валидатора используйте индекс версии 3 (`chain_properties_hf9`). + +--- + +## Цикл управления + +``` +Держатели SHARES → голосуют за валидаторов +Валидаторы → публикуют предпочтительные значения параметров +Блокчейн → берёт медиану активного набора +Медиана → применяется как живые правила сети +``` + +Изменение параметра требует, чтобы **большинство активных валидаторов** опубликовали новое значение. Процесс: +1. Сообщество обсуждает желаемое изменение (например, снижение комиссий). +2. Валидаторы обновляют публикуемые параметры. +3. Пользователи переносят голоса к валидаторам, публикующим желаемые значения. +4. Как только большинство активных валидаторов публикует новое значение, медиана смещается. +5. Новое значение вступает в силу автоматически — хардфорк или голосование за управление не требуются. + +--- + +## Чтение текущих параметров + +```json +{ "method": "database_api.get_chain_properties", "params": [] } +``` + +Возвращает текущие действующие медианные параметры. См. [Database API](../plugins/database-api.md#get_chain_properties). + +--- + +См. также: [Валидаторы](../protocol/operations/validators.md), [Database API](../plugins/database-api.md), [Стейкинг и DAO](./staking-and-dao.md). diff --git a/@l10n/ru/docs/governance/committee.md b/@l10n/ru/docs/governance/committee.md new file mode 100644 index 0000000000..071ca7f6a1 --- /dev/null +++ b/@l10n/ru/docs/governance/committee.md @@ -0,0 +1,116 @@ +# Committee DAO + +Комитет — децентрализованный механизм финансирования VIZ Ledger. Любой аккаунт может создать заявку на финансирование; все держатели SHARES голосуют за одобрение или отклонение с помощью двухполярного голосования, взвешенного по доле. + +Фонд комитета пополняется из комиссий за создание заявок и из доли инфляции блоков, выделяемой ему (регулируется параметром цепочки `inflation_ratio_committee_vs_reward_fund`). + +--- + +## Операции + +| Операция | ID | Авт. | Описание | +|---------|----|----|---------| +| `committee_worker_create_request_operation` | 35 | regular `creator` | Создать заявку на финансирование | +| `committee_worker_cancel_request_operation` | 36 | regular `creator` | Отменить собственную активную заявку | +| `committee_vote_request_operation` | 37 | regular `voter` | Проголосовать по заявке | + +Полное описание полей — в разделе [Операции комитета](../protocol/operations/committee.md). + +--- + +## Механика голосования + +Каждый держатель SHARES может проголосовать с `vote_percent` в диапазоне от **−10000 до +10000** (базисные пункты): + +- Положительный голос: поддерживает заявку. +- Отрицательный голос: возражает против заявки. +- Ноль: снимает голос. + +**Вес голоса** = `effective_vesting_shares × vote_percent / 10000`. + +Голоса можно менять в любое время, пока заявка активна. + +--- + +## Расчёт одобрения + +По наступлении `end_time` заявки: + +``` +max_rshares = СУММА(voter.effective_vesting_shares для всех голосующих) +actual_rshares = СУММА(voter.effective_vesting_shares × vote_percent / 10000) +``` + +**Три условия для одобрения:** + +1. **Участие:** `max_rshares ≥ total_vesting_shares × committee_request_approve_min_percent / 10000` + (если не выполнено → отклонено, недостаточное участие) + +2. **Консенсус:** `actual_rshares > 0` + (если отрицательный → отклонено, сообщество против) + +3. **Минимальная выплата:** + ``` + payout = required_amount_max × (actual_rshares / max_rshares) + ``` + (если `payout < required_amount_min` → отклонено) + +Если все условия выполнены → **одобрено**, выплата = вычисленное значение. + +--- + +## Жизненный цикл заявки + +``` +[Создана, статус=0] + └── период голосования (5–30 дней) + ├── Недостаточное участие → [Отклонена, статус=2] + ├── Чистый отрицательный / ниже минимума → [Истекла, статус=3] + ├── Создатель отменяет → [Отменена, статус=1] + └── Одобрена → [Одобрена, статус=4] + └── выплаты (каждые 200 блоков) + └── [Завершена, статус=5] +``` + +--- + +## Обработка выплат + +Одобренные заявки (статус 4) получают выплаты из фонда комитета каждые 200 блоков (~10 минут). Фонд делится поровну между всеми одобренными заявками: + +``` +payment = min(committee_fund / count_approved_requests, remain_payout_amount) +``` + +Виртуальные операции в жизненном цикле: + +| Виртуальная операция | Триггер | +|--------------------|--------| +| `committee_cancel_request_operation` (ID 38) | Заявка истекает без одобрения | +| `committee_approve_request_operation` (ID 39) | Достигнут порог одобрения | +| `committee_payout_request_operation` (ID 40) | Выплата обработана | +| `committee_pay_request_operation` (ID 41) | Работник получает оплату | + +--- + +## Запрос состояния комитета + +```json +{ "method": "committee_api.get_committee_request", "params": [42] } +{ "method": "committee_api.get_committee_request_votes", "params": [42] } +{ "method": "committee_api.get_committee_requests_list", "params": [0, 100] } +``` + +--- + +## Ключевые свойства + +- **Двухполярность**: каждый голосующий выражает поддержку или несогласие с точной интенсивностью. +- **Взвешенность по доле**: голоса взвешиваются заблокированными токенами, делая манипуляцию дорогостоящей. +- **Пропорциональная выплата**: чем сильнее консенсус — тем выше выплата (до `required_amount_max`). +- **Самофинансирование**: комиссии за создание поступают в фонд комитета. +- **Некастодиальность**: нет доверенного посредника; протокол автоматически применяет все правила. + +--- + +См. также: [Операции комитета](../protocol/operations/committee.md), [Стейкинг и DAO](./staking-and-dao.md), [Параметры цепочки](./chain-properties.md), [Виртуальные операции](../protocol/virtual-operations.md). diff --git a/@l10n/ru/docs/governance/staking-and-dao.md b/@l10n/ru/docs/governance/staking-and-dao.md new file mode 100644 index 0000000000..f61b0083fc --- /dev/null +++ b/@l10n/ru/docs/governance/staking-and-dao.md @@ -0,0 +1,166 @@ +# Стейкинг и управление DAO + +## Стейкинг (SHARES) + +Стейкинг конвертирует ликвидные токены VIZ в **SHARES** (вестинговые доли). Застейканные токены заблокированы и не могут быть переданы напрямую, но дают право на управление пропорционально застейканной сумме. + +У каждого аккаунта есть три поля вестинга: + +| Поле | Значение | +|------|---------| +| `vesting_shares` | SHARES, принадлежащие аккаунту | +| `delegated_vesting_shares` | SHARES, делегированные другим (уменьшают мощь) | +| `received_vesting_shares` | SHARES, полученные через делегирование (увеличивают мощь) | + +**Эффективные вестинговые доли** — сила управления, используемая во всех взвешенных операциях: + +``` +effective_vesting_shares = vesting_shares − delegated_vesting_shares + received_vesting_shares +``` + +--- + +## Операции стейкинга + +### Стейкинг: `transfer_to_vesting_operation` (ID 3) + +Конвертирует ликвидный VIZ в SHARES. Можно переводить на баланс другого аккаунта. + +```json +[3, {"from": "alice", "to": "alice", "amount": "1000.000 VIZ"}] +``` + +### Анстейкинг: `withdraw_vesting_operation` (ID 4) + +Инициирует постепенный вывод за `withdraw_intervals` ежедневных платежей (регулируется цепочкой, по умолчанию 28 дней). Установите `"0.000000 SHARES"` для отмены. + +```json +[4, {"account": "alice", "vesting_shares": "1000.000000 SHARES"}] +``` + +Виртуальная `fill_vesting_withdraw_operation` срабатывает один раз за интервал по мере освобождения токенов. + +### Маршруты вывода: `set_withdraw_vesting_route_operation` (ID 11) + +Направляет процент выводов на другой аккаунт, опционально с немедленным повторным вестингом. + +```json +[11, {"from_account": "alice", "to_account": "bob", "percent": 5000, "auto_vest": true}] +``` + +До 10 маршрутов на аккаунт; суммарный процент по всем маршрутам не должен превышать 10000. + +### Делегирование: `delegate_vesting_shares_operation` (ID 19) + +Передаёт силу управления (не владение) другому аккаунту. Установите `"0.000000 SHARES"` для отмены. + +```json +[19, {"delegator": "alice", "delegatee": "bob", "vesting_shares": "500.000000 SHARES"}] +``` + +При отмене делегирования SHARES входят в 7-дневное окно возврата. По его истечении срабатывает виртуальная `return_vesting_delegation_operation`. + +--- + +## Где используются SHARES + +SHARES — это универсальный токен управления. Каждое значимое действие взвешивается по `effective_vesting_shares`. + +### 1. Голосование за валидаторов (Fair-DPOS) + +```json +[7, {"account": "alice", "witness": "bob", "approve": true}] +``` + +Вес голоса **делится поровну** между всеми валидаторами, за которых голосует аккаунт: + +``` +fair_weight = effective_vesting_shares / witnesses_voted_for +``` + +Это предотвращает концентрацию — голосование за 10 валидаторов даёт каждому 1/10 вашего веса. Аккаунты также могут установить доверенного представителя (`account_witness_proxy_operation`), делегировав все голоса за валидаторов другому аккаунту. + +### 2. Голосование в Committee DAO + +```json +[37, {"voter": "alice", "request_id": 42, "vote_percent": 7500}] +``` + +Вес голоса: `effective_vesting_shares × vote_percent / 10000`. +Диапазон: от −10000 (сильное несогласие) до +10000 (сильная поддержка). + +### 3. Награды (распределение социальных вознаграждений) + +```json +[47, {"initiator": "alice", "receiver": "bob", "energy": 1000, ...}] +``` + +Размер вознаграждения пропорционален: +``` +rshares = effective_vesting_shares × energy / 10000 +``` + +Аккаунт с в 10 раз большим количеством SHARES создаёт в 10 раз большее вознаграждение при той же энергии. + +### 4. Управление параметрами цепочки + +Валидаторы публикуют предпочтительные параметры цепочки; блокчейн применяет медиану. Поскольку валидаторы избираются голосами, взвешенными по доле, все параметры цепочки косвенно управляются держателями SHARES. + +### 5. Пропускная способность транзакций + +Пропускная способность сети распределяется пропорционально `effective_vesting_shares`. Аккаунты с менее чем 500 SHARES получают дополнительный резерв пропускной способности 10%. + +### 6. Создание аккаунта через делегирование + +Новые аккаунты можно создать, делегировав им SHARES в соотношении 10× (заблокированы на 30 дней), что делает создание аккаунта доступным без ликвидных токенов. + +--- + +## VIZ как DAO + +| Традиционный DAO | Блокчейн VIZ | +|-----------------|-------------| +| Казна DAO | Фонд комитета + фонд вознаграждений | +| Токены управления | SHARES | +| Голосование за предложения | Заявки Committee worker | +| Совет директоров | Избранные валидаторы | +| Выборы директоров | Голосование за валидаторов (Fair-DPOS) | +| Распределение дивидендов | Механизм наград (фонд вознаграждений) | +| Устав / правила | Параметры цепочки (медианное управление) | +| Голосование по доверенности | `delegate_vesting_shares` + прокси валидатора | + +### Свойства управления + +1. **Пропорциональное представительство**: 1 SHARES = 1 единица влияния везде. +2. **Двухполярное голосование**: отрицательные голоса активно противодействуют, а не просто воздерживаются. +3. **Непрерывное управление**: нет фиксированных сезонов голосования — голоса можно изменять в любое время. +4. **Кожа в игре**: SHARES заблокированы; выход занимает 28 дней. Долгосрочное согласование. +5. **Нет доверенных посредников**: все правила применяются кодом протокола. +6. **Делегирование без хранения**: сила управления может быть одолжена и отозвана в любое время. + +### Цикл управления + +``` +Стейкинг VIZ → Получение SHARES → Сила управления + ├── Голосование за валидаторов → Производство блоков и параметры цепочки + ├── Голосование в комитете → Расходование казны + ├── Награды другим аккаунтам → Распределение ценности из фонда вознаграждений + └── Делегирование другим → Усиление силы управления союзников +``` + +--- + +## Ключевые константы + +| Константа | Значение | Описание | +|---------|---------|---------| +| `CHAIN_VESTING_WITHDRAW_INTERVALS` | 28 | Ежедневные платежи при выводе | +| `CHAIN_VESTING_WITHDRAW_INTERVAL_SECONDS` | 86400 (1д) | Время между платежами | +| `CHAIN_MAX_WITHDRAW_ROUTES` | 10 | Максимум маршрутов вывода на аккаунт | +| `CHAIN_ENERGY_REGENERATION_SECONDS` | 432000 (5д) | Полное восстановление энергии | +| `CHAIN_100_PERCENT` | 10000 | Знаменатель в базисных пунктах | +| `CHAIN_MAX_ACCOUNT_WITNESS_VOTES` | 100 | Максимум валидаторов на аккаунт | + +--- + +См. также: [Параметры цепочки](./chain-properties.md), [Committee DAO](./committee.md), [Награды](../protocol/operations/awards.md), [Переводы](../protocol/operations/transfers.md). diff --git a/@l10n/ru/docs/introduction/architecture.md b/@l10n/ru/docs/introduction/architecture.md new file mode 100644 index 0000000000..65385414b6 --- /dev/null +++ b/@l10n/ru/docs/introduction/architecture.md @@ -0,0 +1,177 @@ +# Обзор архитектуры + +VIZ Ledger реализован как модульный C++-демон (`vizd`), состоящий из слоёв библиотек и системы плагинов. На этой странице описаны структурные слои, паттерны проектирования и взаимодействие компонентов. + +--- + +## Слоистая структура + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Программы │ +│ vizd (демон узла) cli_wallet (CLI-кошелёк) │ +├─────────────────────────────────────────────────────────────────┤ +│ Плагины │ +│ chain │ validator │ p2p │ webserver │ json_rpc │ database_api │ +│ social_network │ snapshot │ committee_api │ invite_api │ ... │ +├─────────────────────────────────────────────────────────────────┤ +│ Основные библиотеки │ +│ libraries/chain — машина состояний блокчейна, fork db │ +│ libraries/protocol — типы операций, транзакции │ +│ libraries/network — P2P-обмен сообщениями │ +│ libraries/api — общие типы свойств API │ +│ libraries/wallet — вспомогательные инструменты сборки транзакций │ +│ libraries/time — утилиты времени с синхронизацией NTP │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### Библиотеки + +| Библиотека | Ключевой файл | Назначение | +|-----------|--------------|-----------| +| `libraries/chain` | `database.hpp` | Состояние блокчейна: счета, блоки, объекты, fork DB, разделяемая память | +| `libraries/protocol` | `operations.hpp` | Объединение `static_variant` всех 64+ типов операций | +| `libraries/network` | `node.hpp` | P2P-движок: соединения с пирами, синхронизация, распространение сообщений | +| `libraries/api` | `chain_api_properties.hpp` | Общие типы, возвращаемые плагинами API | +| `libraries/wallet` | `wallet.hpp` | Вызовы API удалённого узла, построение транзакций | +| `libraries/time` | `time.hpp` | Синхронизация NTP для тайминга блоковых слотов | + +### Плагины + +Плагины регистрируются во фреймворке `AppBase` при запуске и реализуют хуки жизненного цикла (`plugin_initialize`, `plugin_startup`, `plugin_shutdown`). + +| Плагин | Роль | +|--------|------| +| `chain` | Открывает базу данных, валидирует и применяет блоки/транзакции | +| `validator` | Производит блоки по расписанию (Fair-DPOS), управляет NTP и watchdog | +| `p2p` | Управляет соединениями с пирами, синхронизирует блоки, распространяет транзакции | +| `webserver` | HTTP и WebSocket сервер для доступа к API | +| `json_rpc` | Маршрутизирует JSON-RPC-запросы к зарегистрированным плагинам API | +| `database_api` | Запросы на чтение: счета, блоки, транзакции, глобальные данные | +| `social_network` | Индексирует и запрашивает контент, голоса, ответы | +| `snapshot` | Создаёт и восстанавливает снапшоты состояния | +| `committee_api` | Запросы по заявкам committee worker | +| `invite_api` | Запросы объектов инвайтов | +| `paid_subscription_api` | Запросы платных подписок | +| `account_history` | Индекс истории операций по счету | +| `account_by_key` | Поиск счетов по публичному ключу | +| `follow` | Индекс отношений подписок/игнорирования | +| `tags` | Индексирование контента по тегам | +| `witness_api` | Запросы расписания валидаторов и ключей подписи | +| `debug_node` | Тестовые утилиты: внедрение блоков, установка времени | + +--- + +## Паттерны проектирования + +### Наблюдатель на основе событий (Сигналы) + +`Database` цепочки генерирует события `fc::signal` в ключевых точках. Плагины подписываются на эти сигналы для реализации индексирования, истории и уведомлений без привязки к ядру цепочки. + +``` +push_block() / push_transaction() + │ + ├── pre_apply_operation ──► плагины-подписчики (пре-хуки) + ├── [evaluator применяет изменение состояния] + ├── post_apply_operation ──► плагины-подписчики (пост-хуки) + └── applied_block ──► плагины-подписчики (блок финализирован) +``` + +### Фабрика + Стратегия (Реестр Evaluator'ов) + +Каждый тип операции имеет выделенный класс **evaluator**. `EvaluatorRegistry` сопоставляет идентификаторы типов операций с экземплярами evaluator'ов. При применении транзакции: + +1. `Database` извлекает тег типа операции из `static_variant`. +2. Реестр возвращает зарегистрированный evaluator. +3. Метод evaluator'а `do_apply(op)` изменяет состояние базы данных. + +Добавление новой операции требует только регистрации нового evaluator'а — без изменений в цикле диспетчеризации. + +### Архитектура на основе плагинов (AppBase) + +`vizd/main.cpp` регистрирует все плагины в `AppBase` перед вызовом `app().exec()`. Каждый плагин объявляет свои параметры и зависимости; AppBase управляет порядком и жизненным циклом. + +``` +main() ──► register_plugin() + ──► register_plugin() + ──► register_plugin() + ──► ... + ──► app().initialize(argc, argv) + ──► app().startup() + ──► app().exec() ← цикл событий до SIGINT/SIGTERM +``` + +### Разделение MVC + +| Слой | Компонент | Ответственность | +|------|-----------|----------------| +| Данные | `libraries/chain/database` | Постоянство состояния, валидация, сигналы | +| Управление | Плагины (`chain`, `validator`, `p2p`) | Жизненный цикл, приём блоков/транзакций, координация | +| Представление | Плагины API (`database_api`, `social_network`, …) | Конечные точки запросов только на чтение | + +--- + +## Поток данных: входящий блок + +``` +Пир (P2P) ──► p2p_plugin::handle_block() + ──► chain_plugin::accept_block() + ──► database::push_block() + ├── валидация заголовка и подписи блока + ├── для каждой транзакции: + │ ├── валидация авторитетов + │ └── evaluator->do_apply(operation) + ├── обработка виртуальных операций (вознаграждения, кэшауты) + ├── сигнал applied_block + └── обновление fork DB / LIB +``` + +## Поток данных: API-запрос + +``` +Клиент (HTTP/WS) ──► webserver_plugin + ──► json_rpc_plugin::call() + ──► registry.find_api_method(api, method) + ──► database_api / social_network / ... + ──► database::get_*(...) + ──► возврат JSON-результата +``` + +--- + +## Модель параллелизма + +| Задача | Подход | +|--------|--------| +| Операции записи | Единственный поток записи (опциональный параметр `single-write-thread`) | +| Операции чтения | Несколько параллельных читателей через разделяемую память `chainbase` | +| P2P-ввод/вывод | Выделенный пул потоков `boost::asio::io_service` | +| Таймер производства блоков | Изолированные `io_service` + поток в плагине валидатора для предотвращения задержек P2P | +| Обслуживание RPC | Настраиваемый пул потоков (`rpc-endpoint-thread-pool-size`) | + +Важнейший инвариант: **только один поток может удерживать блокировку записи на базе данных в любой момент времени.** Весь код evaluator'ов и обработки блоков выполняется под этой блокировкой. + +--- + +## База данных в разделяемой памяти + +Состояние хранится в memory-mapped файле (`shared_memory.bin`), управляемом `chainbase`. Ключевые свойства: + +- Все объектные индексы (счета, блоки, контент, валидаторы, …) находятся в этом файле. +- Файл увеличивается инкрементально, когда свободное место падает ниже порога. +- При чистом завершении файл согласован; сбой может потребовать воспроизведения с лога блоков. +- Узлы могут экспортировать **снапшот** состояния разделяемой памяти на границе блока — см. [Снапшоты](../storage/snapshots.md). + +--- + +## Карта исходников + +| Файл | Роль в архитектуре | +|------|--------------------| +| `programs/vizd/main.cpp` | Регистрация плагинов и запуск | +| `libraries/chain/include/graphene/chain/database.hpp` | Интерфейс основной базы данных и сигналы | +| `libraries/chain/include/graphene/chain/evaluator_registry.hpp` | Фабрика evaluator'ов операций | +| `libraries/network/include/graphene/network/node.hpp` | Интерфейс делегата P2P-узла | +| `libraries/protocol/include/graphene/protocol/operations.hpp` | Объединение типов операций | +| `plugins/chain/plugin.cpp` | Плагин цепочки: приём блоков/транзакций | +| `plugins/json_rpc/plugin.cpp` | Диспетчеризация JSON-RPC | diff --git a/@l10n/ru/docs/introduction/key-concepts.md b/@l10n/ru/docs/introduction/key-concepts.md new file mode 100644 index 0000000000..46643387de --- /dev/null +++ b/@l10n/ru/docs/introduction/key-concepts.md @@ -0,0 +1,161 @@ +# Ключевые концепции + +На этой странице описаны фундаментальные концепции, необходимые для работы с VIZ Ledger — разработчику, оператору узла или создателю приложений. + +--- + +## Аккаунты + +Каждый участник VIZ Ledger — это **аккаунт**. Аккаунты хранят балансы, создают контент, голосуют за валидаторов и взаимодействуют со всеми возможностями протокола. + +### Правила именования аккаунтов + +- Длина: от 3 до 16 символов +- Метки, разделённые точками: каждая метка ≥ 3 символов +- Каждая метка начинается с буквы, оканчивается буквой или цифрой +- Допустимы только строчные латинские буквы (`a-z`), цифры (`0-9`), дефисы (`-`) +- Примеры корректных имён: `alice`, `alice.bob`, `viz-user1` + +### Уровни авторизации + +У каждого аккаунта три уровня авторизации, каждый из которых содержит набор ключей или делегированных аккаунтов: + +| Уровень | Используется для | Ключ | +|---------|-----------------|------| +| `master` | Смена ключей, восстановление аккаунта, операции высокой безопасности | Мастер-ключ (хранить офлайн) | +| `active` | Переводы токенов, вестинг, голосование за валидаторов | Активный ключ | +| `regular` | Контент, награды, голосование в комитете, социальные операции | Регулярный ключ | + +Авторизация — это мультиподписная структура: `{ weight_threshold, account_auths[], key_auths[] }`. Транзакция авторизована, когда сумма весов предоставленных подписей достигает `weight_threshold` или превышает его. + +--- + +## Токены + +### VIZ — ликвидный токен + +- 3 знака после запятой: `"10.000 VIZ"` +- Используется для переводов, комиссий и операций финансирования +- Конвертируется в SHARES через `transfer_to_vesting_operation` + +### SHARES — застейканный токен + +- 6 знаков после запятой: `"10.000000 SHARES"` +- Представляет голосующую силу и ёмкость энергии +- Создаётся при стейкинге VIZ; выводится обратно в VIZ за 28 интервалов (≈28 дней) +- Не переводится напрямую; может быть делегирован другим аккаунтам + +--- + +## Система энергии + +Энергия — ресурс, определяющий влияние социальных действий (наград, голосов) относительно доли аккаунта в SHARES. + +| Свойство | Значение | +|----------|---------| +| Единица | Базисные пункты: `0` = 0%, `10000` = 100% | +| Скорость восстановления | Полное восполнение за 24 часа (86 400 секунд) | +| Формула восстановления | `current_energy = min(10000, last_energy + elapsed_sec * 10000 / 86400)` | + +Когда аккаунт выдаёт награду с `energy = 500` (5%), эта доля его SHARES используется для определения распределения из пула вознаграждений. Трата энергии не «уничтожает» токены — она определяет вес в пуле вознаграждений. + +--- + +## Валидаторы (производители блоков) + +**Валидаторы** (ранее называвшиеся «witnesses») — аккаунты, производящие блоки и поддерживающие работу сети. + +- Любой аккаунт может зарегистрироваться как валидатор через `validator_update_operation`. +- Держатели токенов голосуют за валидаторов, используя вес своих SHARES. +- Ведущие по голосам валидаторы получают расписание блоков в порядке round-robin. +- Каждый слот блока — ровно 3 секунды. +- Раунд из 21 валидатора = 21 блок = 63 секунды. + +### Участие в Fair-DPOS + +В отличие от стандартного DPOS, VIZ Ledger штрафует бездействие: +- У каждого валидатора есть **оценка участия** на основе недавнего производства блоков. +- Если участие по всей сети опускается ниже `required-participation` (по умолчанию 33%), производство блоков приостанавливается. +- Валидаторы, пропускающие слишком много блоков, получают штраф к голосам, действующий в течение `witness_miss_penalty_duration` секунд. + +--- + +## Блоки и транзакции + +### Блок + +Подписанный набор транзакций, произведённый валидатором в его запланированном слоте. Содержит: +- `previous`: хэш предыдущего блока (связь цепочки) +- `timestamp`: точное время слота +- `witness`: имя производящего валидатора +- `transactions[]`: список подписанных транзакций +- `witness_signature`: подпись валидатора + +### Транзакция + +Одна или несколько операций, сгруппированных и подписанных. Свойства: +- `ref_block_num = head_block_number & 0xFFFF` +- `ref_block_prefix` = байты 4–7 идентификатора референсного блока (little-endian uint32) +- `expiration`: должен быть в пределах 60 секунд от текущего времени (рекомендуется) +- `operations[]`: 1 или более операций +- `signatures[]`: подписи ECDSA, удовлетворяющие всем необходимым авторизациям + +### Операция + +Атомарная единица изменения состояния. Сериализуется как `[type_id, operation_object]` внутри транзакции. Существует 64+ типов операций, охватывающих переводы, социальные действия, управление и другое — см. [Обзор операций](../protocol/operations/overview.md). + +--- + +## Пул вознаграждений + +Инфляция непрерывно добавляется в пул вознаграждений. Валидаторы и создатели контента получают средства из этого пула: + +| Получатель | Источник | +|-----------|---------| +| Валидаторы | `inflation_witness_percent` от блочного вознаграждения | +| Комитет | Доля `inflation_ratio_committee_vs_reward_fund` | +| Фонд вознаграждений | Остаток — распределяется через награды и голоса за контент | + +Точные проценты устанавливаются консенсусом валидаторов через `versioned_chain_properties_update_operation` и принимаются голосованием ведущих валидаторов. + +--- + +## Fork и LIB + +**Fork database** (`fork_db`): дерево в памяти из недавно полученных блоков, которые ещё могут не входить в канонич цепочку. Узел отслеживает все форк-кандидаты и всегда продолжает наиболее тяжёлый (наиболее одобрённый) форк. + +**LIB (Last Irreversible Block)**: последний блок, подтверждённый более чем 2/3 валидаторов. Блоки на уровне LIB и ниже не могут быть реорганизованы. Как только блок опускается ниже LIB, он записывается в постоянный лог блоков. + +--- + +## Снапшот + +Снапшот — бинарный дамп всего состояния базы данных на определённом номере блока. Позволяет новому узлу: +1. Скачать файл снапшота +2. Загрузить его за секунды (вместо воспроизведения всей истории блоков) +3. Продолжить синхронизацию с высоты блока снапшота + +Снапшоты создаются плагином `snapshot` и не влияют на канонич цепочку — это исключительно операционный инструмент. + +--- + +## Параметры цепочки (параметры управления) + +Параметры консенсуса на блокчейне контролируются валидаторами через `versioned_chain_properties_update_operation`. Активные параметры являются **медианой** значений, опубликованных ведущими 21 валидаторами. + +Ключевые параметры: +- `account_creation_fee` — стоимость создания нового аккаунта +- `maximum_block_size` — максимальный размер блока в байтах +- `inflation_witness_percent` — доля валидаторов от блочного вознаграждения +- `witness_miss_penalty_percent` / `witness_miss_penalty_duration` — штраф за пропуск блоков +- `withdraw_intervals` — количество интервалов вывода из вестинга + +См. [Управление параметрами цепочки](../governance/chain-properties.md) для полного списка параметров. + +--- + +## Хардфорки + +Обновления протокола развёртываются как **хардфорки** — запланированные активации на определённом номере блока. Как только ≥17 из 21 валидаторов сигнализируют поддержку хардфорка, он активируется при следующем запланированном блоке. Хардфорки могут добавлять новые типы операций, изменять правила консенсуса или вводить новые параметры цепочки. + +См. [Хардфорки](../consensus/hardforks.md) для истории и процесса обновления. diff --git a/@l10n/ru/docs/introduction/what-is-viz.md b/@l10n/ru/docs/introduction/what-is-viz.md new file mode 100644 index 0000000000..1175c1fa43 --- /dev/null +++ b/@l10n/ru/docs/introduction/what-is-viz.md @@ -0,0 +1,121 @@ +# Что такое VIZ Ledger? + +VIZ Ledger — это технология распределённого реестра (DLT), построенная на алгоритме консенсуса **Fair-DPOS** (Fair Delegated Proof of Stake). Платформа создана для децентрализованных социальных, финансовых и управленческих приложений, где критически важны справедливость, прозрачность и эффективность. + +> *VIZ Ledger использует консенсусный механизм на основе блокчейна со вспомогательным хранением состояния через снапшоты, что делает её гибридной DLT-системой.* + +--- + +## VIZ Ledger vs. традиционный блокчейн + +Традиционные блокчейны требуют от каждого полного узла хранить полную историю всех транзакций начиная с генезис-блока. VIZ Ledger предлагает иной подход: + +| Свойство | Традиционный блокчейн | VIZ Ledger | +|----------|-----------------------|------------| +| Хранение состояния | Полная история на каждом узле | Последние блоки + периодические снапшоты | +| Метод синхронизации | Воспроизведение всех блоков с генезиса | Загрузка снапшота + воспроизведение последних блоков | +| Требования к хранилищу | Растут неограниченно | Ограничены интервалом снапшота | +| Модель безопасности | Полная верификация цепочки | Верификация снапшота + консенсус | +| Консенсус | Различный | Fair-DPOS | + +Эта архитектура ближе к тому, что в отрасли называют **DLT** в широком смысле — аналогично Hedera Hashgraph или Corda — а не к классическому блокчейну, где каждый узел хранит полную историю реестра. + +### Почему «VIZ Ledger»? + +Название следует той же логике, что и [XRP Ledger](https://xrpl.org): +- Оно нейтрально относительно механизма хранения данных. +- Точно отражает основную функцию: ведение распределённого реестра счетов, транзакций и состояния. +- Оставляет пространство для архитектурной эволюции без переименования. + +В технической документации полное описание используется там, где это необходимо: *«VIZ Ledger — это Fair-DPOS-реестр с вспомогательным хранением состояния через снапшоты.»* + +--- + +## Основные свойства + +### Консенсус Fair-DPOS + +VIZ Ledger использует **Fair Delegated Proof of Stake** — эволюцию стандартного DPOS: + +- Держатели токенов голосуют за **валидаторов** (производителей блоков) с использованием застейканных SHARES. +- Ведущие валидаторы по весу голосов получают расписание производства блоков в порядке round-robin. +- **Обеспечение справедливости**: валидатор, пропускающий блоки, получает снижение оценки участия. Если участие падает ниже требуемого порога — производство блоков приостанавливается. +- Нет неограниченных вознаграждений для неактивных валидаторов — производство требует реального участия. + +### Хранение состояния через снапшоты + +- Узлы хранят текущее состояние (счета, балансы, контент, голоса) в разделяемой памяти. +- Периодические снапшоты фиксируют полное состояние при конкретном номере блока. +- Новые узлы могут быстро синхронизироваться, загрузив последний снапшот и воспроизведя только блоки после него — вместо всей истории цепочки. +- Лог блоков (бинарный формат) хранит все блоки для узлов, которым необходим исторический доступ. + +### Социальные и управленческие примитивы + +VIZ Ledger встраивает социальные и управленческие возможности непосредственно в протокол — они не являются прикладным слоем: + +- **Система энергии**: у счетов есть пул энергии (0–100%), который восстанавливается за 24 часа. Энергия тратится на социальные действия (награды, голоса) пропорционально доле счёта. +- **Награды**: любой счёт может наградить другой счёт, используя энергию и распределяя токены из пула вознаграждений. +- **Committee DAO**: запросы финансирования, предложения и голосование на блокчейне. +- **Инвайты**: механизм приглашений на блокчейне для создания новых счетов. +- **Платные подписки**: контракты подписок между счетами на блокчейне. + +--- + +## Архитектура в общих чертах + +``` +┌─────────────────────────────────────────────────────────────┐ +│ процесс vizd │ +│ │ +│ ┌──────────┐ ┌──────────┐ ┌────────────┐ ┌─────────┐ │ +│ │ chain │ │validator │ │database_api│ │ p2p │ │ +│ │ plugin │ │ plugin │ │ plugin │ │ plugin │ │ +│ └────┬─────┘ └────┬─────┘ └─────┬──────┘ └────┬────┘ │ +│ │ │ │ │ │ +│ ┌────▼──────────────▼──────────────▼───────────────▼────┐ │ +│ │ libraries/chain (база данных) │ │ +│ └────────────────────────────────────────────────────────┘ │ +│ ┌────────────────────┐ ┌──────────────────────────────┐ │ +│ │ libraries/network │ │ libraries/protocol │ │ +│ └────────────────────┘ └──────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ + │ │ + Узлы сети Кошельки / Приложения + (P2P-порт 2001) (HTTP/WS-порты 8090/8091) +``` + +Ключевые компоненты: + +| Компонент | Роль | +|-----------|------| +| `chain plugin` | Открывает базу данных, координирует обработку блоков и транзакций | +| `validator plugin` | Производит блоки по расписанию на основе правил Fair-DPOS | +| `database_api plugin` | Предоставляет JSON-RPC-запросы на чтение для кошельков и приложений | +| `p2p plugin` | Управляет соединениями с пирами, распространением блоков и транзакций | +| `webserver plugin` | HTTP и WebSocket транспорт для JSON-RPC | +| `snapshot plugin` | Создаёт и загружает снапшоты состояния | + +--- + +## Система токенов + +VIZ Ledger имеет два нативных токена: + +| Токен | Назначение | Знаков после запятой | +|-------|-----------|---------------------| +| `VIZ` | Ликвидный токен для переводов и оплаты комиссий | 3 (`10.000 VIZ`) | +| `SHARES` | Застейканный токен, представляющий голосующую силу и ёмкость энергии | 6 (`10.000000 SHARES`) | + +VIZ можно конвертировать в SHARES через `transfer_to_vesting_operation`. SHARES можно вывести обратно в VIZ за 28 интервалов вывода. + +--- + +## Для кого эта документация? + +| Аудитория | Начните здесь | +|-----------|--------------| +| Операторы узлов | [Начало работы](../node/getting-started.md) | +| Операторы валидаторов | [Запуск узла-валидатора](../node/validator-node.md) | +| Разработчики приложений | [JSON-RPC API](../api/json-rpc.md) | +| Разработчики кошельков и библиотек | [Типы данных](../protocol/data-types.md) · [Операции](../protocol/operations/overview.md) | +| Контрибьюторы протокола | [Архитектура](./architecture.md) · [Консенсус](../consensus/fair-dpos.md) | diff --git a/@l10n/ru/docs/node/building.md b/@l10n/ru/docs/node/building.md new file mode 100644 index 0000000000..8ac23adeb0 --- /dev/null +++ b/@l10n/ru/docs/node/building.md @@ -0,0 +1,190 @@ +# Сборка из исходного кода + +VIZ Ledger использует систему сборки на основе CMake с отдельными скриптами для каждой платформы. Двухэтапный процесс для Linux (`install-deps-linux.sh` + `build-linux.sh`) разделяет установку зависимостей (требует root) и саму сборку (выполняется от обычного пользователя). + +--- + +## Требования + +| Компонент | Версия | +|-----------|---------| +| CMake | 3.16+ | +| GCC | 4.8+ | +| Clang | 3.3+ | +| Boost | 1.71+ (с компонентом `coroutine`) | +| OpenSSL | Любая актуальная версия | + +--- + +## Linux (Ubuntu 20.04 / 22.04 / 24.04) + +### Шаг 1 — Клонирование репозитория + +```bash +git clone --recursive https://github.com/VIZ-Blockchain/viz-cpp-node +cd viz-cpp-node +``` + +### Шаг 2 — Установка зависимостей (от root) + +```bash +chmod +x install-deps-linux.sh +sudo ./install-deps-linux.sh +``` + +Устанавливает: cmake, gcc/g++, git, boost (все компоненты включая coroutine/context), openssl, readline, ccache и библиотеки сжатия. + +### Шаг 3 — Сборка (от обычного пользователя) + +```bash +chmod +x build-linux.sh +./build-linux.sh +``` + +Выходной бинарный файл: `build/programs/vizd/vizd` + +### Основные флаги сборки + +```bash +# Низкопамятный узел (для валидаторов/сид-узлов — без плагинов индексирования истории) +./build-linux.sh -l + +# Сборка для тестнета +./build-linux.sh -n + +# Debug-сборка +./build-linux.sh -t Debug + +# Параллельные задания +./build-linux.sh -j 8 + +# Пропустить установку зависимостей (уже установлены) +./build-linux.sh --skip-deps + +# Пользовательские пути к Boost / OpenSSL +./build-linux.sh --boost-root /opt/boost_1_74_0 --openssl-root /opt/openssl +``` + +--- + +## macOS + +```bash +chmod +x build-mac.sh +./build-mac.sh +``` + +Скрипт автоматически устанавливает Xcode Command Line Tools (при необходимости) и зависимости Homebrew, затем конфигурирует и собирает проект. + +```bash +# С указанием пути к Boost +./build-mac.sh --boost-root /opt/homebrew/opt/boost + +# Пропустить установку зависимостей +./build-mac.sh --skip-deps +``` + +--- + +## Windows (MinGW) + +```cmd +set BOOST_ROOT=C:\Boost +set OPENSSL_ROOT_DIR=C:\OpenSSL-Win64 +build-mingw.bat +``` + +Опциональные переменные окружения: + +| Переменная | По умолчанию | Описание | +|------------|--------------|----------| +| `VIZ_BUILD_TYPE` | `Release` | `Release` или `Debug` | +| `VIZ_LOW_MEMORY` | `OFF` | `ON` для низкопамятного узла | +| `VIZ_BUILD_TESTNET` | `OFF` | `ON` для сборки тестнета | +| `VIZ_FULL_STATIC` | `OFF` | `ON` для полностью статического бинарного файла | + +--- + +## Windows (MSVC) + +Требуется Visual Studio 2019+ с нагрузкой «Разработка классических приложений на C++»: + +```cmd +set BOOST_ROOT=C:\Boost +set OPENSSL_ROOT_DIR=C:\OpenSSL-Win64 +build-msvc.bat +``` + +--- + +## Опции CMake + +Для прямого использования CMake (продвинутый режим): + +| Опция | По умолчанию | Описание | +|-------|--------------|----------| +| `BUILD_TESTNET` | `OFF` | Включить код для тестнета | +| `LOW_MEMORY_NODE` | `OFF` | Исключить плагины истории/индексирования | +| `CHAINBASE_CHECK_LOCKING` | `OFF` | Включить проверки блокировок (debug) | +| `ENABLE_MONGO_PLUGIN` | `OFF` | Собрать плагин MongoDB | +| `BUILD_SHARED_LIBRARIES` | `OFF` | Собрать разделяемые библиотеки | +| `USE_PCH` | `OFF` | Включить предкомпилированные заголовки (ускоряет пересборку) | + +Пример: + +```bash +mkdir build && cd build +cmake -DCMAKE_BUILD_TYPE=Release \ + -DLOW_MEMORY_NODE=ON \ + -DCMAKE_INSTALL_PREFIX=/usr/local \ + .. +make -j$(nproc) +``` + +Или с помощью вспомогательного скрипта: + +```bash +python3 programs/build_helpers/configure_build.py --release +``` + +--- + +## Цели сборки + +| Цель | Бинарный файл | Описание | +|------|---------------|----------| +| `vizd` | `programs/vizd/vizd` | Основной демон узла | +| `cli_wallet` | `programs/cli_wallet/cli_wallet` | Кошелёк командной строки | + +--- + +## Docker-сборки + +В репозитории поставляются четыре Dockerfile: + +| Файл | Назначение | +|------|-----------| +| `Dockerfile-production` | Полный узел мейннета (Release) | +| `Dockerfile-lowmem` | Низкопамятный узел (`LOW_MEMORY_NODE=ON`) | +| `Dockerfile-mongo` | Узел с плагином MongoDB | +| `Dockerfile-testnet` | Узел тестнета (`BUILD_TESTNET=ON`) | + +Пример сборки: + +```bash +docker build -f share/vizd/docker/Dockerfile-production -t vizd:local . +``` + +Полная настройка Docker для продакшена описана в разделе [Docker](./docker.md). + +--- + +## Устранение неполадок + +| Проблема | Решение | +|----------|---------| +| `boost/coroutine.hpp` не найден | Установите `libboost-coroutine-dev` (Ubuntu) или Boost 1.71+ | +| CMake < 3.16 | Установите новый CMake с `cmake.org` или Kitware PPA | +| Ошибка `do not run as root` | Запустите `build-linux.sh` от обычного пользователя, не `sudo` | +| Ошибка линковки на macOS (OpenSSL) | `export OPENSSL_ROOT_DIR=$(brew --prefix openssl)` | +| Нехватка памяти при компиляции | Используйте `-j 2` для уменьшения числа параллельных заданий | diff --git a/@l10n/ru/docs/node/configuration.md b/@l10n/ru/docs/node/configuration.md new file mode 100644 index 0000000000..70681b9414 --- /dev/null +++ b/@l10n/ru/docs/node/configuration.md @@ -0,0 +1,223 @@ +# Конфигурация узла + +Узлы VIZ Ledger настраиваются через INI-файл. В репозитории поставляется несколько шаблонов в `share/vizd/config/`: + +| Шаблон | Назначение | +|--------|-----------| +| `config.ini` | Полный узел основной сети с публичным RPC | +| `config_witness.ini` | Узел-валидатор (RPC на localhost, производство блоков) | +| `config_testnet.ini` | Тестовая сеть / разработка | +| `config_mongo.ini` | Узел с бэкендом истории MongoDB | +| `config_lowmem.ini` | Маломощный консенсусный/сид-узел | +| `config_stock_exchange.ini` | Потребитель рыночных данных (минимум плагинов) | +| `config_debug.ini` | Режим отладки | + +--- + +## Сеть и P2P + +```ini +# Адрес прослушивания P2P-соединений (стандартный порт 2001) +p2p-endpoint = 0.0.0.0:2001 + +# Максимальное количество подключений к пирам (без ограничений, если не задано) +p2p-max-connections = 200 + +# Начальные узлы для установки соединений (повторяемый параметр) +p2p-seed-node = seed1.viz.media:2001 +p2p-seed-node = seed2.viz.media:2001 + +# Контрольные точки: доверенные пары (block_num, block_id) (повторяемый параметр) +# checkpoint = [12345,"0003039..." ] +``` + +--- + +## Веб-сервер и RPC + +```ini +# HTTP JSON-RPC эндпоинт +webserver-http-endpoint = 0.0.0.0:8090 + +# WebSocket JSON-RPC эндпоинт +webserver-ws-endpoint = 0.0.0.0:8091 + +# Размер пула потоков RPC +webserver-thread-pool-size = 2 +``` + +> **Примечание по безопасности:** Для узлов-валидаторов привяжите к `127.0.0.1`, чтобы заблокировать внешний доступ: +> ```ini +> webserver-http-endpoint = 127.0.0.1:8090 +> webserver-ws-endpoint = 127.0.0.1:8091 +> ``` + +--- + +## Блокировки и параллелизм + +```ini +# Время ожидания блокировки чтения в микросекундах перед повторной попыткой +read-wait-micro = 500000 + +# Максимальное количество повторных попыток блокировки чтения +max-read-wait-retries = 2 + +# Время ожидания блокировки записи в микросекундах перед повторной попыткой +write-wait-micro = 500000 + +# Максимальное количество повторных попыток блокировки записи +max-write-wait-retries = 3 + +# Сериализовать все операции записи в одном потоке (рекомендуется) +single-write-thread = true + +# Запускать уведомления плагинов при push_transaction (увеличивает задержку; по умолчанию false) +enable-plugins-on-push-transaction = false +``` + +--- + +## Разделяемая память (база данных) + +Состояние блокчейна хранится в файле с отображением в память (`shared_memory.bin`). + +```ini +# Начальный размер файла разделяемой памяти +shared-file-size = 4G + +# Минимальный свободный объём перед изменением размера +min-free-shared-file-size = 500M + +# Объём увеличения файла при изменении размера +inc-shared-file-size = 2G + +# Проверять свободное место каждые N блоков +block-num-check-free-size = 1000 +``` + +Подбирайте `shared-file-size` в зависимости от размера цепочки. Для основной сети начните с `4G` и следите за ростом. + +--- + +## Активация плагинов + +```ini +# Каждая строка 'plugin' добавляет плагин (повторяемый параметр) +# Минимальный набор для полного API-узла: +plugin = chain p2p webserver json_rpc database_api network_broadcast_api + +# Дополнительные плагины индексирования (отключить на маломощных узлах): +plugin = social_network tags follow account_history account_by_key +plugin = committee_api invite_api paid_subscription_api custom_protocol_api + +# Только для узлов-валидаторов: +plugin = validator witness_api +``` + +### Наборы плагинов по типу узла + +| Тип узла | Плагины | +|---------|--------| +| Полный узел | Все перечисленные выше | +| Валидатор | `chain p2p webserver json_rpc database_api network_broadcast_api validator witness_api` | +| Маломощный сид | `chain p2p` | +| Биржа | `chain p2p webserver json_rpc database_api network_broadcast_api account_history` | + +--- + +## История и отслеживание + +```ini +# Удалять объекты голосов до данного блока (экономит память; 0 = хранить всё) +clear-votes-before-block = 0 + +# Пропускать индексирование виртуальных операций (экономит память у валидаторов) +skip-virtual-ops = false + +# Индексировать историю аккаунтов только для диапазона (опционально) +# track-account-range = ["alice","alice.zzz"] + +# Белый/чёрный список типов операций для истории +# history-whitelist-ops = transfer_operation +# history-blacklist-ops = custom_operation + +# Начать индексирование истории с данного номера блока +# history-start-block = 1000000 + +# Максимальное количество записей в ленте аккаунта (плагин follow) +follow-max-feed-size = 500 + +# Диапазон отслеживания личных сообщений (опционально) +# pm-account-range = ["alice","alice.zzz"] +``` + +--- + +## Валидатор (производство блоков) + +На не-валидаторных узлах эти параметры не задаются. + +```ini +# Разрешить производство при устаревшей цепочке (только для разработки/тестовой сети) +enable-stale-production = false + +# Минимальный процент участия для производства блоков (0–99) +required-participation = 33 + +# Имя аккаунта валидатора (повторяемый параметр для нескольких валидаторов на одном узле) +validator = alice + +# WIF-ключ подписи валидатора +private-key = 5JxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxWIF + +# Приватный ключ экстренного консенсуса (опционально) +# emergency-private-key = 5Jxxx... +``` + +Полная конфигурация валидатора — в разделе [Узел-валидатор](./validator-node.md). + +--- + +## Логирование + +```ini +# Консольный логгер (вывод в stderr) +log.console_appender.stderr.stream = std_error + +# Файловый логгер для P2P-сообщений +log.file_appender.p2p.filename = logs/p2p/p2p.log + +# Уровень логирования: all, debug, info, warn, error, off +logger.default.level = warn +logger.default.appenders = stderr + +logger.p2p.level = warn +logger.p2p.appenders = p2p +``` + +--- + +## MongoDB (опционально) + +Применимо только при сборке с `ENABLE_MONGO_PLUGIN=ON`: + +```ini +plugin = mongo_db +mongodb-uri = mongodb://localhost:27017/vizd +``` + +--- + +## Полный справочник + +Все параметры по исходным файлам: + +| Источник | Параметры | +|---------|----------| +| `plugins/chain/plugin.hpp` | `shared-file-size`, `min-free-shared-file-size`, `inc-shared-file-size`, `block-num-check-free-size`, `single-write-thread`, `enable-plugins-on-push-transaction`, `read-wait-micro`, `max-read-wait-retries`, `write-wait-micro`, `max-write-wait-retries`, `skip-virtual-ops`, `clear-votes-before-block`, `track-account-range`, `history-whitelist-ops`, `history-blacklist-ops`, `history-start-block` | +| `plugins/p2p/p2p_plugin.hpp` | `p2p-endpoint`, `p2p-max-connections`, `p2p-seed-node`, `checkpoint` | +| `plugins/webserver/webserver_plugin.hpp` | `webserver-http-endpoint`, `webserver-ws-endpoint`, `webserver-thread-pool-size` | +| `plugins/validator/validator.hpp` | `enable-stale-production`, `required-participation`, `validator`, `private-key`, `emergency-private-key`, `fork-collision-timeout-blocks`, `ntp-server`, `ntp-request-interval`, `debug-block-production` | +| `plugins/follow/` | `follow-max-feed-size` | +| `plugins/private_message/` | `pm-account-range` | diff --git a/@l10n/ru/docs/node/docker.md b/@l10n/ru/docs/node/docker.md new file mode 100644 index 0000000000..3c125ad769 --- /dev/null +++ b/@l10n/ru/docs/node/docker.md @@ -0,0 +1,183 @@ +# Развёртывание через Docker + +VIZ Ledger поставляется с четырьмя Docker-образами для различных профилей развёртывания. Все используют двухэтапную сборку: этап builder компилирует бинарный файл; этап runtime содержит только бинарный файл и конфигурацию. + +--- + +## Доступные образы + +| Dockerfile | Тег | Описание | +|-----------|-----|----------| +| `Dockerfile-production` | `latest` | Полный узел мейннета (Release, все плагины) | +| `Dockerfile-lowmem` | `lowmem` | Низкопамятный узел (`LOW_MEMORY_NODE=ON`, без индексов истории) | +| `Dockerfile-mongo` | `mongo` | Полный узел с плагином истории MongoDB | +| `Dockerfile-testnet` | `testnet` | Узел тестнета (`BUILD_TESTNET=ON`) | + +--- + +## Быстрый старт + +```bash +docker run -d \ + --name vizd \ + --restart unless-stopped \ + -p 2001:2001 \ + -p 8090:8090 \ + -p 8091:8091 \ + -v /data/vizd:/var/lib/vizd \ + vizblockchain/vizd:latest +``` + +Просмотр логов: + +```bash +docker logs -f vizd +``` + +--- + +## Тома + +| Путь в контейнере | Назначение | +|-------------------|-----------| +| `/var/lib/vizd` | Данные блокчейна, разделяемая память, block log | +| `/etc/vizd` | Файлы конфигурации и список сид-узлов | + +Всегда монтируйте `/var/lib/vizd` для сохранения состояния между перезапусками контейнера. + +Использование пользовательской конфигурации: + +```bash +docker run -d \ + -v /data/vizd:/var/lib/vizd \ + -v /my/config.ini:/etc/vizd/config.ini \ + vizblockchain/vizd:latest +``` + +--- + +## Переменные окружения + +Скрипт входа (`vizd.sh`) считывает следующие переменные окружения: + +| Переменная | Описание | Пример | +|------------|----------|--------| +| `VIZD_SEED_NODES` | Список сид-узлов через пробел (переопределяет `/etc/vizd/seednodes`) | `seed1.viz.media:2001 seed2.viz.media:2001` | +| `VIZD_RPC_ENDPOINT` | Переопределить HTTP RPC endpoint | `0.0.0.0:8090` | +| `VIZD_P2P_ENDPOINT` | Переопределить P2P endpoint | `0.0.0.0:2001` | +| `VIZD_WITNESS` | Имя аккаунта валидатора (включает производство блоков) | `alice` | +| `VIZD_PRIVATE_KEY` | Подписывающий ключ валидатора в формате WIF | `5J...` | + +--- + +## Порты + +| Порт | Протокол | Назначение | +|------|----------|-----------| +| 2001 | TCP | P2P-соединения с пирами | +| 8090 | TCP | HTTP JSON-RPC | +| 8091 | TCP | WebSocket JSON-RPC | + +--- + +## Узел-валидатор (Docker) + +```bash +docker run -d \ + --name vizd-validator \ + --restart unless-stopped \ + -p 2001:2001 \ + -v /data/vizd:/var/lib/vizd \ + -e VIZD_WITNESS=myvalidator \ + -e VIZD_PRIVATE_KEY=5Jxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx \ + vizblockchain/vizd:latest +``` + +Для валидаторов **не открывайте** порты 8090/8091 публично — привязывайте только к localhost: + +```bash +-e VIZD_RPC_ENDPOINT=127.0.0.1:8090 +``` + +--- + +## Узел тестнета + +```bash +docker run -d \ + --name vizd-testnet \ + -p 2001:2001 \ + -p 8090:8090 \ + -v /data/vizd-testnet:/var/lib/vizd \ + vizblockchain/vizd:testnet +``` + +--- + +## Локальная сборка образов + +```bash +# Production +docker build \ + -f share/vizd/docker/Dockerfile-production \ + -t vizd:local \ + . + +# Low-memory +docker build \ + -f share/vizd/docker/Dockerfile-lowmem \ + -t vizd:lowmem \ + . +``` + +### CMake-флаги для каждого образа + +| Образ | `LOW_MEMORY_NODE` | `ENABLE_MONGO_PLUGIN` | `BUILD_TESTNET` | +|-------|:-----------------:|:---------------------:|:---------------:| +| production | OFF | OFF | OFF | +| lowmem | ON | OFF | OFF | +| mongo | OFF | ON | OFF | +| testnet | OFF | OFF | ON | + +--- + +## CI/CD (GitHub Actions) + +В репозитории поставляется `.github/workflows/docker-main.yml`, который собирает и публикует production-образ с тегом `latest` при каждом push в `master`. + +```yaml +- name: Build and push + uses: docker/build-push-action@v2 + with: + file: share/vizd/docker/Dockerfile-production + tags: vizblockchain/vizd:latest + push: true +``` + +--- + +## Требования к ресурсам + +| Тип узла | RAM | Диск | +|----------|-----|------| +| Полный узел (мейннет) | 8 ГБ+ | 50 ГБ+ | +| Низкопамятный / валидатор | 4 ГБ | 20 ГБ | +| Тестнет | 4 ГБ | 10 ГБ | + +Начинайте с размера разделяемой памяти, удобно помещающегося в RAM. В `config.ini`: + +```ini +shared-file-size = 4G +``` + +--- + +## Устранение неполадок + +| Симптом | Причина | Решение | +|---------|---------|---------| +| Контейнер сразу завершается | Плохая конфигурация или отсутствующий том | `docker logs vizd` — проверьте ошибки запуска | +| Порт 8090 недоступен | RPC привязан к localhost | Уберите префикс `127.0.0.1:` или используйте reverse proxy | +| Нет пиров | Файрвол блокирует порт 2001 | Откройте порт 2001 TCP входящий | +| Медленная синхронизация | Снимок не загружен | Предоставьте снимок в томе перед первым запуском | +| `Permission denied` на `/var/lib/vizd` | Несоответствие владельца тома | `chown -R 1000:1000 /data/vizd` | diff --git a/@l10n/ru/docs/node/getting-started.md b/@l10n/ru/docs/node/getting-started.md new file mode 100644 index 0000000000..93dcef54df --- /dev/null +++ b/@l10n/ru/docs/node/getting-started.md @@ -0,0 +1,184 @@ +# Начало работы + +В этом руководстве описано всё необходимое для запуска узла VIZ Ledger — от установки зависимостей до начальной синхронизации. + +--- + +## Требования + +| Требование | Минимум | Рекомендуется | +|-----------|---------|--------------| +| ОС | Ubuntu 20.04 LTS | Ubuntu 24.04 LTS | +| ОЗУ | 4 ГБ | 8 ГБ+ | +| Диск | 20 ГБ | 50 ГБ+ SSD | +| CPU | 2 ядра | 4+ ядра | +| Сеть | Публичный IP, открытый порт 2001 | Стабильное соединение | + +**Используемые порты:** + +| Порт | Протокол | Назначение | +|------|---------|-----------| +| 2001 | TCP | P2P соединения с пирами | +| 8090 | TCP | HTTP JSON-RPC | +| 8091 | TCP | WebSocket JSON-RPC | + +--- + +## Вариант А: Docker (рекомендуется для быстрого старта) + +### 1. Загрузка образа + +```bash +docker pull vizblockchain/vizd:latest +``` + +### 2. Запуск узла + +```bash +docker run -d \ + --name vizd \ + -p 2001:2001 \ + -p 8090:8090 \ + -p 8091:8091 \ + -v /data/vizd:/var/lib/vizd \ + vizblockchain/vizd:latest +``` + +### 3. Просмотр логов + +```bash +docker logs -f vizd +``` + +Через несколько минут вы должны увидеть подключения к пирам и прогресс синхронизации блоков. + +### Переменные окружения (Docker) + +| Переменная | Назначение | Пример | +|-----------|-----------|--------| +| `VIZD_SEED_NODES` | Переопределить начальные узлы | `node1.viz.media:2001` | +| `VIZD_WITNESS` | Имя валидатора (для узла-валидатора) | `alice` | +| `VIZD_PRIVATE_KEY` | Ключ подписи валидатора (WIF) | `5J...` | + +--- + +## Вариант Б: Сборка из исходников + +### 1. Установка зависимостей (Linux) + +```bash +git clone --recursive https://github.com/VIZ-Blockchain/viz-cpp-node +cd viz-cpp-node +chmod +x install-deps-linux.sh +sudo ./install-deps-linux.sh +``` + +### 2. Сборка + +```bash +chmod +x build-linux.sh +./build-linux.sh +``` + +Для сборки с малым объёмом памяти (валидаторы и сид-узлы — без плагинов индексирования): + +```bash +./build-linux.sh -l +``` + +Исполняемый файл помещается в `build/programs/vizd/vizd`. + +### 3. macOS + +```bash +chmod +x build-mac.sh +./build-mac.sh +``` + +### 4. Windows (MinGW) + +```cmd +set BOOST_ROOT=C:\Boost +set OPENSSL_ROOT_DIR=C:\OpenSSL-Win64 +build-mingw.bat +``` + +Подробные инструкции по платформам и параметры CMake — в разделе [Сборка](./building.md). + +--- + +## Начальная настройка + +Скопируйте шаблон конфигурации для основной сети: + +```bash +cp share/vizd/config/config.ini /data/vizd/config.ini +``` + +Минимальные правки для публичного узла: + +```ini +# P2P +p2p-endpoint = 0.0.0.0:2001 +p2p-seed-node = seed1.viz.media:2001 +p2p-seed-node = seed2.viz.media:2001 + +# RPC +webserver-http-endpoint = 0.0.0.0:8090 +webserver-ws-endpoint = 0.0.0.0:8091 + +# Разделяемая память — подберите по доступному диску +shared-file-size = 4G + +# Плагины (полный узел) +plugin = chain p2p webserver json_rpc database_api network_broadcast_api +plugin = social_network tags follow account_history +``` + +Для узла-валидатора см. [Узел-валидатор](./validator-node.md). + +--- + +## Запуск узла + +```bash +./vizd --config-file /data/vizd/config.ini --data-dir /data/vizd +``` + +В Docker передайте каталог данных как том (см. Вариант А выше). + +--- + +## Проверка синхронизации + +Запросите узел через HTTP RPC: + +```bash +curl -s -X POST http://localhost:8090 \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"call","params":["database_api","get_dynamic_global_properties",[]],"id":1}' \ + | python3 -m json.tool +``` + +Проверьте `head_block_number` — после синхронизации он должен увеличиваться каждые 3 секунды. + +--- + +## Типы узлов + +| Тип | Шаблон конфигурации | Описание | +|-----|-------------------|---------| +| Полный узел | `config.ini` | Все плагины, публичные RPC-эндпоинты | +| Валидатор | `config_witness.ini` | Производство блоков, RPC только на localhost | +| Тестовая сеть | `config_testnet.ini` | Разработка и тестирование | +| Малая память | `config.ini` + флаг сборки `LOW_MEMORY_NODE` | Только консенсус, без индексов истории | +| MongoDB | `config_mongo.ini` | Полная история в MongoDB | + +--- + +## Дальнейшие шаги + +- [Справочник конфигурации](./configuration.md) — описание всех параметров +- [Развёртывание Docker](./docker.md) — продакшн-настройка Docker +- [Узел-валидатор](./validator-node.md) — запуск блок-продюсера +- [Снапшоты](./snapshot.md) — быстрая синхронизация через снапшоты состояния diff --git a/@l10n/ru/docs/node/monitoring.md b/@l10n/ru/docs/node/monitoring.md new file mode 100644 index 0000000000..66f16ce5e7 --- /dev/null +++ b/@l10n/ru/docs/node/monitoring.md @@ -0,0 +1,313 @@ +# Мониторинг + +На этой странице описаны проверки работоспособности, паттерны логов, статистика P2P-пиров и интеграция с внешними стеками мониторинга для узлов VIZ Ledger. + +--- + +## Проверка работоспособности: синхронизация узла + +Запросите динамические глобальные свойства узла, чтобы убедиться в его работе и синхронизации: + +```bash +curl -s -X POST http://localhost:8090 \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"call","params":["database_api","get_dynamic_global_properties",[]],"id":1}' \ + | python3 -m json.tool +``` + +Проверьте `head_block_number` — он должен увеличиваться каждые 3 секунды при синхронизации. Проверьте `time` — оно должно быть в пределах нескольких секунд от системных часов. + +Простой скрипт проверки доступности: + +```bash +#!/bin/bash +RESPONSE=$(curl -sf -X POST http://localhost:8090 \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"call","params":["database_api","get_dynamic_global_properties",[]],"id":1}') +if [ $? -ne 0 ]; then echo "CRIT: RPC unreachable"; exit 2; fi + +HEAD=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['result']['head_block_number'])") +echo "OK: head_block_number=$HEAD" +``` + +--- + +## Паттерны логов + +### Производство блоков (узлы-валидаторы) + +``` +# Хорошо: слот произведён +produced block #123456 @2025-01-01T00:00:03 validator=alice sz=2048 + +# Пропущенный слот +MISSED-SLOT-OUR-validator: alice missed slot at 2025-01-01T00:00:06 + +# Обнаружен minority fork +MINORITY FORK DETECTED: rolling back to LIB #123400 + +# Сработал watchdog +WATCHDOG: no production for 180s, clearing flags +``` + +### P2P-подключение + +``` +# Новый пир подключён +New peer is connected (203.0.113.10:2001), now 8 active peers + +# Мягкий бан пира +soft-banning peer 203.0.113.10:2001 for 300s: reason=only_fork_db_blocks_no_progress + +# Синхронизация завершена +Sync: peer 203.0.113.10 says we're up-to-date +``` + +### Снимок и восстановление + +``` +# Снимок создан +Snapshot created at block 5000000 in 14.2s: /data/snapshots/snapshot-block-5000000.json + +# Запущено автовосстановление +shared_memory_corruption_exception detected — starting auto-recovery +Recovery complete. Resumed from block 4999500 +``` + +### Sync-логгер (DLT-режим) + +Включите логгер `sync` для просмотра деталей переговоров о синхронизации: + +```ini +[logger.sync] +level = info +appenders = stderr +``` + +Ключевые сообщения: +- `Starting sync with peer ...` — синхронизация начата +- `on_blockchain_item_ids_inventory: ...` — получена партия ID блоков +- `Sync: peer X says we're up-to-date` — синхронизация с этим пиром завершена +- `DEFERRED_RESIZE: sync block #N deferred` — синхронизация блока отложена из-за изменения размера разделяемой памяти +- `auto-clearing stuck peer_needs_sync_items_from_us` — 30-секундная защита сняла зависший флаг + +--- + +## Конфигурация логов + +Логи настраиваются в `config.ini`: + +```ini +# Вывод в консоль +log.console_appender.stderr.stream = std_error + +# P2P лог-файл +log.file_appender.p2p.filename = logs/p2p/p2p.log + +# Уровни логирования: all, debug, info, warn, error, off +logger.default.level = warn +logger.default.appenders = stderr + +logger.p2p.level = warn +logger.p2p.appenders = p2p +``` + +> **Примечание:** `node.cpp` направляет все свои вызовы `ilog`/`wlog` в логгер `p2p`. Для просмотра P2P-сообщений настройте уровень логгера `p2p` на `info`. + +Ротация логов через `logrotate` (пример `/etc/logrotate.d/vizd`): + +``` +/data/vizd/logs/p2p/p2p.log { + daily + rotate 14 + compress + missingok + copytruncate +} +``` + +--- + +## Статистика P2P-пиров + +Плагин P2P записывает метрики работоспособности пиров каждые 5 минут (настраивается). Включите в `config.ini`: + +```ini +p2p-stats-enabled = true +p2p-stats-interval = 300 # секунды +``` + +Пример вывода в лог: + +``` +P2P peer | ip: 203.0.113.10 | port: 2001 | latency: 45ms | bytes_in: 12345 | blocked: false | reason: +P2P peer | ip: 198.51.100.5 | port: 2001 | latency: 120ms | bytes_in: 8765 | blocked: true | reason: soft_ban +Block storage | dlt_log: [79174319..79274318] | dlt_resizes: 412 | fork_db: linked=18 unlinked=0 +``` + +Поля: +- `latency` — задержка туда-обратно в мс +- `bytes_in` — дельта байт, полученных с последнего измерения +- `blocked` / `reason` — статус мягкого бана или ограничения и причина +- `Block storage` — диапазон DLT block log, счётчик изменений размера, состояние fork_db + +Высокое значение `dlt_resizes` в сочетании с уменьшающимся диапазоном `dlt_log` может указывать на срабатывание самовосстановления файла отображения. Пир с `reason: soft_ban` может находиться на форке или отправлять только устаревшие данные. + +--- + +## Prometheus и Grafana + +Узел не предоставляет нативный endpoint Prometheus. Используйте [Node Exporter](https://github.com/prometheus/node_exporter) для метрик уровня ОС и опрашивайте JSON-RPC endpoint с помощью пользовательского экспортёра: + +```python +# минимальный пример: опрос head_block_number +import requests, time +from prometheus_client import Gauge, start_http_server + +g = Gauge('viz_head_block_number', 'Current head block') + +def collect(): + r = requests.post('http://localhost:8090', json={ + "jsonrpc": "2.0", "method": "call", + "params": ["database_api", "get_dynamic_global_properties", []], + "id": 1 + }, timeout=5) + g.set(r.json()['result']['head_block_number']) + +start_http_server(9100) +while True: + collect() + time.sleep(3) +``` + +**Рекомендуемые панели дашборда:** + +| Панель | Метрика / Источник | +|--------|-------------------| +| Головной блок | `viz_head_block_number` (увеличивается каждые 3 с при синхронизации) | +| Отставание блока | `time() - viz_head_block_time` (секунды отставания от системных часов) | +| Количество пиров | Из лога P2P-статистики | +| Задержка пиров | Лог P2P-статистики по IP пира | +| Свободная разделяемая память | `viz_shared_memory_free_mb` из пользовательского экспортёра | +| CPU / RAM | Стандартные метрики Node Exporter | +| Дисковый I/O | `node_disk_*` Node Exporter | + +--- + +## ELK / Централизованное логирование + +Перенаправляйте логи узла в центральный коллектор. Пример с Filebeat: + +```yaml +# filebeat.yml +filebeat.inputs: + - type: log + paths: + - /data/vizd/logs/p2p/p2p.log + fields: + service: vizd + node: validator-1 + +output.logstash: + hosts: ["logstash:5044"] +``` + +Разбор ключевых полей (grok Logstash или ingest pipeline Elasticsearch): + +``` +MISSED-SLOT-OUR-validator: %{WORD:validator} missed slot at %{TIMESTAMP_ISO8601:slot_time} +produced block #%{NUMBER:block_num} @%{TIMESTAMP_ISO8601:block_time} validator=%{WORD:producer} +``` + +--- + +## Мониторинг, специфичный для валидатора + +### Ключевые метрики для оповещений + +| Условие | Серьёзность | Действие | +|---------|-------------|---------| +| `MISSED-SLOT-OUR-validator` в логах | Предупреждение | Проверить NTP, задержку сети, нагрузку CPU | +| `MINORITY FORK DETECTED` | Критическое | Проверить P2P-подключение к сид-узлам | +| `WATCHDOG: no production for 180s` | Критическое | Проверить ключ валидатора и работоспособность узла | +| Код результата `no_private_key` | Критическое | Несоответствие ключа подписи — проверить конфигурацию | +| Код результата `low_participation` | Предупреждение | Деградация работоспособности сети | +| Головной блок перестал увеличиваться | Критическое | Узел может быть заблокирован | +| Количество пиров упало до 0 | Критическое | Сетевой раздел или проблема с файрволом | + +### Проверка NTP + +```bash +chronyc tracking | grep "System time" +# или +timedatectl | grep "NTP synchronized" +``` + +Плагин validator использует собственный NTP-клиент (настраивается через `ntp-server` в config), но синхронизация системных часов также важна. Дрейф >200мс может вызвать пропуск слотов. + +--- + +## Обслуживание базы данных + +### Размер разделяемой памяти + +Следите за предупреждениями о нехватке места в логах: + +``` +chainbase: shared memory low — resizing from 4G to 6G +``` + +Превентивно настройте параметры роста в `config.ini`: + +```ini +shared-file-size = 4G +min-free-shared-file-size = 500M +inc-shared-file-size = 2G +block-num-check-free-size = 1000 +``` + +### Проверка резервных копий снимков + +После создания снимка проверьте его корректную загрузку на тестовом узле: + +```bash +vizd --create-snapshot /tmp/verify-snap.json --plugin snapshot +# Ожидается: завершается корректно с "Snapshot created at block N" +``` + +Периодически тестируйте восстановление после сбоя: + +```bash +vizd --replay-from-snapshot --snapshot-auto-latest --plugin snapshot +# Ожидается: импортирует снимок, воспроизводит dlt_block_log, выдаёт "Recovery complete" +``` + +--- + +## Чеклист реагирования на инциденты + +**Узел не синхронизируется:** +1. Проверьте количество пиров (логи `p2p-stats-enabled` или RPC `get_info`). +2. Убедитесь, что файрвол разрешает TCP-порт 2001 входящий. +3. Проверьте настройки `p2p-seed-node` — попробуйте альтернативные сиды. +4. Ищите записи `soft_ban` в P2P-статистике — узел может находиться на форке. + +**Валидатор не производит блоки:** +1. Убедитесь, что `validator` и `private-key` в `config.ini` соответствуют on-chain ключу подписи. +2. Проверьте, не является ли причиной `low_participation` (работоспособность сети). +3. Проверьте синхронизацию NTP. +4. Ищите `MINORITY FORK DETECTED` — узлу может потребоваться ресинхронизация. + +**Узел завис / разделяемая память повреждена:** +1. Если `--auto-recover-from-snapshot` включён (по умолчанию) и снимки существуют, узел восстанавливается автоматически — проверьте логи. +2. Ручное восстановление: `vizd --replay-from-snapshot --snapshot-auto-latest --plugin snapshot`. +3. Если снимков нет: `vizd --replay-blockchain` (требует полный block log; недоступно в DLT-режиме). + +**RPC недоступен:** +1. Проверьте привязку `webserver-http-endpoint` — валидаторы по умолчанию используют `127.0.0.1:8090`. +2. Проверьте конфигурацию файрвола или reverse proxy. +3. Убедитесь, что список плагинов включает `webserver json_rpc database_api`. + +--- + +См. также: [Узел-валидатор](./validator-node.md), [Защита валидатора](./validator-guard.md), [Снимки](./snapshot.md), [Конфигурация](./configuration.md). diff --git a/@l10n/ru/docs/node/snapshot.md b/@l10n/ru/docs/node/snapshot.md new file mode 100644 index 0000000000..f9c5f11a84 --- /dev/null +++ b/@l10n/ru/docs/node/snapshot.md @@ -0,0 +1,377 @@ +# Снимки (Snapshots) + +Плагин snapshot обеспечивает почти мгновенный старт узла путём сериализации полного состояния блокчейна в JSON-файл. Вместо воспроизведения миллионов блоков из block log узел загружает снимок и синхронизирует только блоки, произведённые после его создания. + +Узлы, загруженные из снимка, работают в **DLT-режиме**: основной block log остаётся пустым, а компактный **скользящий DLT block log** (`dlt_block_log`) хранит последние необратимые блоки. + +--- + +## Краткий справочник + +| Задача | Команда / Конфигурация | +|--------|----------------------| +| Создать снимок один раз (остановить узел) | `vizd --create-snapshot /path/snap.json --plugin snapshot` | +| Создать снимок на блоке N | `snapshot-at-block = N` + `snapshot-dir = /path` | +| Создавать снимок каждые N блоков | `snapshot-every-n-blocks = N` + `snapshot-dir = /path` | +| Развернуть новый узел из файла | `vizd --snapshot /path/snap.json --plugin snapshot` | +| Восстановление после сбоя | `vizd --replay-from-snapshot --snapshot-auto-latest --plugin snapshot` | +| Автоматическое развёртывание через P2P | `sync-snapshot-from-trusted-peer = true` + `trusted-snapshot-peer = host:8092` | + +--- + +## Подключение плагина + +```ini +plugin = snapshot +``` + +--- + +## Справочник по конфигурации + +### Флаги только для CLI + +| Флаг | Описание | +|------|----------| +| `--snapshot ` | Развернуть из файла снимка (DLT-режим). Пропускается, если `shared_memory.bin` уже существует или файл уже был импортирован (переименован в `.used`). | +| `--snapshot-auto-latest` | Автоматически определить последний снимок в `snapshot-dir` по номерам блоков в именах файлов. Используется с `--replay-from-snapshot`. Игнорируется при наличии `--snapshot`. | +| `--replay-from-snapshot` | Восстановление после сбоя: всегда очищает разделяемую память, импортирует снимок, воспроизводит `dlt_block_log`. Не переименовывает файл снимка. Требует `--snapshot` или `--snapshot-auto-latest`. | +| `--auto-recover-from-snapshot` | (по умолчанию: `true`) Автоматическое восстановление при обнаружении повреждения разделяемой памяти — без перезапуска. Отключается через `--no-auto-recover-from-snapshot`. | +| `--create-snapshot ` | Создать снимок по указанному пути из текущей базы данных, затем выйти. | +| `--sync-snapshot-from-trusted-peer` | (по умолчанию: `false`) Загрузить снимок от доверенных пиров при пустом состоянии. Требует явного включения. | + +### Опции конфигурационного файла + +| Опция | По умолчанию | Описание | +|-------|--------------|----------| +| `snapshot-at-block` | `0` | Создать снимок при достижении указанного номера блока (0 = отключено). | +| `snapshot-every-n-blocks` | `0` | Создавать снимок каждые N блоков (0 = отключено). Срабатывает только на живых блоках — пропускается при P2P-синхронизации. | +| `snapshot-dir` | — | Каталог для автоматически генерируемых снимков. Создаётся автоматически при отсутствии. | +| `snapshot-max-age-days` | `90` | Удалять снимки старше N дней после создания нового (0 = отключено). | +| `allow-snapshot-serving` | `false` | Раздавать снимки другим узлам по TCP. | +| `allow-snapshot-serving-only-trusted` | `false` | Ограничить раздачу только доверенными пирами. | +| `snapshot-serve-endpoint` | `0.0.0.0:8092` | TCP-endpoint для сервера снимков. | +| `trusted-snapshot-peer` | — | Адрес доверенного пира для P2P-синхронизации снимков (повторяемый). | +| `dlt-block-log-max-blocks` | `100000` | Размер скользящего block log в DLT-режиме (опция плагина chain). 0 = отключено. | + +--- + +## Создание снимков + +### Метод 1: Одноразовый (остановить узел, создать файл, выйти) + +Сначала остановите работающий узел, затем: + +```bash +vizd --create-snapshot /data/snapshots/viz-snapshot.json --plugin snapshot +``` + +Узел открывает существующую базу данных, при необходимости выполняет воспроизведение, записывает снимок и завершается до активации P2P или валидаторных плагинов. + +### Метод 2: Снимок на конкретном блоке (без простоя) + +```ini +plugin = snapshot +snapshot-at-block = 5000000 +snapshot-dir = /data/snapshots +``` + +При применении блока 5 000 000 снимок записывается в `/data/snapshots/snapshot-block-5000000.json` без остановки узла. + +### Метод 3: Периодические снимки (рекомендуется для продакшена) + +```ini +plugin = snapshot +snapshot-every-n-blocks = 28800 # ~24 часа при 3 с/блок +snapshot-dir = /data/snapshots +snapshot-max-age-days = 90 +``` + +Файлы автоматически именуются `snapshot-block-.json`. Создание снимка асинхронно: + +- **Фаза 1** (read lock, ~1 с): сериализация всех объектов базы данных в память. +- **Фаза 2** (без блокировки, ~2 с): сжатие, контрольная сумма, запись на диск. + +Обработка блоков приостанавливается только во время Фазы 1; API и P2P-чтения не затрагиваются на всём протяжении. + +**Рекомендуемые интервалы:** + +| Частота | Блоки | Примерное время | +|---------|-------|----------------| +| Частые | 10 000 | ~8 часов | +| Ежедневно | 28 800 | ~24 часа | +| Еженедельно | 100 000 | ~3,5 дня | + +### Метод 4: Совмещение at-block и периодических + +Оба параметра могут быть активны одновременно: + +```ini +snapshot-at-block = 5000000 +snapshot-every-n-blocks = 100000 +snapshot-dir = /data/snapshots +``` + +--- + +## Развёртывание: загрузка из снимка (DLT-режим) + +Перенесите файл снимка на новый узел, затем: + +```bash +vizd \ + --snapshot /data/snapshots/viz-snapshot.json \ + --plugin snapshot \ + --plugin p2p \ + --p2p-seed-node seed1.viz.media:2001 +``` + +Узел загружает состояние за секунды и начинает P2P-синхронизацию с высоты блока снимка. + +### Что происходит при загрузке + +1. `chain::plugin_startup()` обнаруживает `--snapshot`. +2. Три проверки безопасности (по порядку): разделяемая память уже существует → пропустить; файл не найден (уже `.used`) → пропустить; иначе продолжить. +3. База данных открывается через `open_from_snapshot()` (очищает и переинициализирует chainbase). +4. Снимок JSON проверяется (версия формата, chain ID, SHA-256 контрольная сумма), все 32 типа объектов импортируются. +5. Файл снимка переименовывается в `.used` для предотвращения повторного импорта при перезапуске. +6. LIB продвигается до `head_block_num`, чтобы P2P-synopsis начинался с головы снимка. +7. Fork database заполняется головным блоком снимка. +8. Плагин P2P запускается и синхронизирует блоки начиная с `LIB + 1`. + +### Безопасность перезапуска + +| Сценарий | Результат | +|----------|-----------| +| Первый запуск (нет `shared_memory.bin`, файл присутствует) | Импортирует снимок, переименовывает в `.used` | +| Перезапуск (shared_memory существует) | Пропускает импорт — использует существующее состояние | +| Перезапуск (shared_memory очищена, файл уже `.used`) | Пропускает импорт — файл не найден | +| Принудительный повторный импорт | `--resync-blockchain` + новый файл снимка | + +Нет необходимости убирать `--snapshot` из командной строки или Docker `VIZD_EXTRA_OPTS` после первоначального импорта. + +--- + +## Формат файла снимка + +Снимок — это единый JSON-файл: + +```json +{ + "header": { + "version": 1, + "chain_id": "...", + "snapshot_block_num": 12345678, + "snapshot_block_id": "...", + "snapshot_block_time": "2025-01-01T00:00:00", + "last_irreversible_block_num": 12345660, + "payload_checksum": "sha256...", + "object_counts": { "account": 50000, ... } + }, + "state": { + "dynamic_global_property": [ ... ], + "account": [ ... ], + ... + } +} +``` + +### 32 включённых типа объектов + +**Критические (11)** — обязательные для консенсуса: +`dynamic_global_property`, `witness_schedule`, `hardfork_property`, `account`, `account_authority`, `validator`, `witness_vote`, `block_summary`, `content`, `content_vote`, `block_post_validation` + +**Важные (15)** — необходимые для полноценной работы: +`transaction`, `vesting_delegation`, `vesting_delegation_expiration`, `fix_vesting_delegation`, `withdraw_vesting_route`, `escrow`, `proposal`, `required_approval`, `committee_request`, `committee_vote`, `invite`, `award_shares_expire`, `paid_subscription`, `paid_subscribe`, `witness_penalty_expire` + +**Дополнительные (5)** — метаданные и восстановление: +`content_type`, `account_metadata`, `master_authority_history`, `account_recovery_request`, `change_recovery_account_request` + +--- + +## DLT-скользящий block log + +В DLT-режиме основной `block_log` остаётся пустым. `dlt_block_log` (файлы `dlt_block_log.log` + `dlt_block_log.index`) хранит последние необратимые блоки для: + +- **P2P-раздачи блоков** — пиры могут запрашивать последние блоки для разрешения fork. +- **API-доступа** — `get_block` работает для блоков в скользящем окне. + +```ini +dlt-block-log-max-blocks = 100000 # хранить последние ~3,5 дня блоков +``` + +Когда лог превышает этот размер, старые блоки удаляются с начала. Реализация отслеживает логические размеры файлов независимо от файла, отображаемого в память, для предотвращения ошибок с устаревшим размером после тысяч циклов изменения размера. + +--- + +## Восстановление после сбоя: `--replay-from-snapshot` + +Используйте этот режим, когда `shared_memory.bin` повреждён (некорректное завершение, диск заполнен, аппаратный сбой). Обычный `--replay-blockchain` недоступен в DLT-режиме, поскольку `block_log` пуст. + +```bash +# Указать путь к снимку явно +vizd --replay-from-snapshot --snapshot /data/snapshots/snapshot-block-79273800.json --plugin snapshot + +# Или автоматически определить последний снимок +vizd --replay-from-snapshot --snapshot-auto-latest --plugin snapshot +``` + +Шаги восстановления: +1. Всегда очищает `shared_memory.bin` (предполагает повреждение). +2. Импортирует состояние снимка. +3. Воспроизводит `dlt_block_log` начиная с `snapshot_head + 1`. +4. Генерирует `on_sync` — P2P заполняет оставшийся разрыв до головы живой цепочки. + +Файл снимка **не** переименовывается в `.used` (может потребоваться снова). + +### Пример сценария восстановления + +DLT-узел падает на блоке 79 274 318 при `snapshot-every-n-blocks = 100000` и `dlt-block-log-max-blocks = 100000`: + +``` +/data/viz-snapshots/snapshot-block-79273800.json ← последний снимок +/blockchain/dlt_block_log.* ← содержит блоки 79174319..79274318 +/blockchain/shared_memory.bin ← ПОВРЕЖДЁН +``` + +```bash +vizd --replay-from-snapshot --snapshot-auto-latest --plugin snapshot +``` + +``` +Loading state from snapshot: .../snapshot-block-79273800.json (12.3 с) +Replaying dlt_block_log from block 79273801 to 79274318... + 100% 518 of 518 (block 79274318, elapsed 7.2 с) +Recovery complete. Started on blockchain with 79274318 blocks. +``` + +Узел теперь на блоке 79 274 318; P2P-синхронизация доставляет остальное. + +--- + +## Автоматическое восстановление в реальном времени: `--auto-recover-from-snapshot` + +Включено по умолчанию (`true`). При обнаружении повреждения во время обработки или генерации блоков в реальном времени узел: + +1. Находит последний снимок в `snapshot-dir`. +2. Закрывает базу данных. +3. Очищает и повторно импортирует по тому же пути, что и `--replay-from-snapshot`. +4. Возобновляет P2P-синхронизацию — перезапуск не требуется. + +**Предварительные условия:** плагин `snapshot` включён и снимки присутствуют в `snapshot-dir`. + +Для отключения (при отладке): + +```bash +vizd --no-auto-recover-from-snapshot +``` + +--- + +## P2P-синхронизация снимков + +Узлы могут загружать снимки от доверенных пиров по пользовательскому TCP-протоколу, исключая ручную передачу файлов. + +### Сервер снимков + +```ini +plugin = snapshot +allow-snapshot-serving = true +snapshot-serve-endpoint = 0.0.0.0:8092 +snapshot-every-n-blocks = 28800 +snapshot-dir = /data/viz-snapshots +``` + +### Развёртывание нового узла + +```ini +plugin = snapshot +trusted-snapshot-peer = seed1.viz.media:8092 +trusted-snapshot-peer = seed2.viz.media:8092 +sync-snapshot-from-trusted-peer = true +``` + +Когда узел стартует с 0 блоков и `sync-snapshot-from-trusted-peer = true`, он запрашивает все доверенные пиры, выбирает пир с наибольшим снимком, загружает его блоками по 1 МБ, проверяет SHA-256 контрольную сумму и импортирует — всё до активации P2P или валидаторных плагинов. + +### Безопасность + +- Загрузки свыше 2 ГБ отклоняются. +- Контрольная сумма проверяется через потоковый SHA-256 (никогда не загружается полностью в память). +- Ограничение скорости, максимум 5 одновременных соединений, 60-секундный дедлайн на соединение. +- `allow-snapshot-serving-only-trusted = true` ограничивает доступ списком `trusted-snapshot-peer`. + +--- + +## Docker + +Используйте `VIZD_EXTRA_OPTS` для передачи флагов снимков: + +```bash +# Развернуть из снимка +docker run -e VIZD_EXTRA_OPTS="--snapshot /var/lib/vizd/snapshots/snap.json --plugin snapshot" ... + +# Восстановление после сбоя +docker run -e VIZD_EXTRA_OPTS="--replay-from-snapshot --snapshot-auto-latest --plugin snapshot" ... +``` + +Периодические снимки через `config.ini` (без `VIZD_EXTRA_OPTS`): + +```ini +plugin = snapshot +snapshot-every-n-blocks = 28800 +snapshot-dir = /var/lib/vizd/snapshots +``` + +Файлы снимков доступны на хосте по пути смонтированного тома. + +| Задача | Метод | +|--------|-------| +| Периодические снимки | `snapshot-every-n-blocks` в config | +| Одноразовый снимок | `--create-snapshot` через `VIZD_EXTRA_OPTS` | +| Развёртывание нового узла | `--snapshot` через `VIZD_EXTRA_OPTS` | +| Восстановление после сбоя | `--replay-from-snapshot --snapshot-auto-latest` через `VIZD_EXTRA_OPTS` | +| Авто-восстановление | По умолчанию — убедитесь, что `plugin = snapshot` и `snapshot-dir` заданы | +| P2P-авторазвёртывание | `sync-snapshot-from-trusted-peer = true` + `trusted-snapshot-peer` в config | + +--- + +## Рекомендуемая конфигурация для продакшена + +```ini +plugin = snapshot + +# Снимок каждые ~24 часа +snapshot-every-n-blocks = 28800 +snapshot-dir = /data/viz-snapshots +snapshot-max-age-days = 90 + +# DLT-скользящий block log: хранить последние ~3,5 дня +dlt-block-log-max-blocks = 100000 + +shared-file-size = 4G +plugin = p2p +p2p-seed-node = seed1.viz.media:2001 +``` + +--- + +## Обнаружение устаревшего снимка + +Если последний снимок раздающего узла старше начального блока `dlt_block_log`, новые узлы, загружающие снимок, не смогут синхронизировать недостающие блоки. При запуске плагин обнаруживает это и автоматически создаёт свежий снимок на следующем живом блоке — ручного вмешательства не требуется. + +--- + +## Устранение неполадок + +| Проблема | Проверьте | +|----------|-----------| +| Узел повторно импортирует снимок при каждом перезапуске | Файл снимка не переименовывается в `.used` — проверьте права записи в каталог снимков | +| `item_not_available` от пиров | DLT block log может не охватывать рекламируемые блоки — убедитесь, что `dlt-block-log-max-blocks` достаточно велик | +| P2P-синхронизация зависает после загрузки снимка | Проверьте `[logger.sync]` в config; убедитесь, что LIB был продвинут до головы после импорта | +| Создание снимка не удаётся | Проверьте место на диске в `snapshot-dir`; при сбое узел продолжает работу | +| Авто-восстановление срабатывает неожиданно | Проверьте ошибки диска; изучите логи на наличие `shared_memory_corruption_exception` | +| P2P-загрузка отклонена (>2 ГБ) | Снимок слишком большой — увеличьте `dlt-block-log-max-blocks` на раздающем узле для уменьшения размера снимка | + +--- + +См. также: [Плагин Snapshot](../plugins/snapshot.md) — полная справка по реализации; [P2P-обзор](../p2p/overview.md) — детали DLT-протокола синхронизации. diff --git a/@l10n/ru/docs/node/validator-guard.md b/@l10n/ru/docs/node/validator-guard.md new file mode 100644 index 0000000000..c3373ce1c2 --- /dev/null +++ b/@l10n/ru/docs/node/validator-guard.md @@ -0,0 +1,138 @@ +# Защита валидатора (Validator Guard) + +Плагин `witness_guard` автоматизирует восстановление ключа подписи для аккаунтов валидаторов. Когда ключ подписи валидатора сбрасывается в null (отключая производство блоков), плагин обнаруживает изменение и транслирует `witness_update_operation` для восстановления ключа — без ручного вмешательства. + +--- + +## Когда использовать этот плагин + +- Ключ подписи вашего аккаунта валидатора может быть обнулён мастером экстренного консенсуса, протоколом безопасности или вручную. +- Без этого плагина вам нужно вручную отслеживать ключ в сети и восстанавливать его до наступления вашего запланированного слота. +- С этим плагином узел проверяет наличие нулевых ключей каждые N блоков и восстанавливает их автоматически. + +--- + +## Подключение плагина + +```ini +plugin = witness_guard +``` + +--- + +## Конфигурация + +| Опция | По умолчанию | Описание | +|-------|--------------|----------| +| `validator-guard-enabled` | `true` | Глобально включить или отключить плагин. | +| `validator-guard-interval` | `20` | Интервал проверки в блоках (~60 с при 3 с/блок). | +| `validator-guard-validator` | — | JSON-тройка `[name, signing_wif, active_wif]`. Повторяемый. | +| `validator-guard-disable` | `5` | Число последовательных блоков от одного валидатора перед его автоотключением. `0` = отключено. | + +Плагин также считывает `enable-stale-production` из конфигурации плагина validator. + +### Пример + +```ini +plugin = witness_guard + +# Мониторинг одного валидатора +validator-guard-validator = ["alice", "5K_SIGNING_WIF", "5K_ACTIVE_WIF"] + +# Мониторинг второго валидатора +validator-guard-validator = ["alice.backup", "5J_SIGNING_WIF", "5J_ACTIVE_WIF"] + +# Проверка каждые 10 блоков +validator-guard-interval = 10 +``` + +> **Безопасность:** Активный приватный ключ хранится в открытом виде в `config.ini`. Ограничьте права доступа к файлу (`chmod 600 config.ini`) и избегайте его доступности недоверенным процессам. + +--- + +## Принцип работы + +### Запуск + +1. Разбирает и проверяет все настроенные WIF-ключи. +2. Если `enable-stale-production = true`, автовосстановление начинается отключённым (см. Защитные механизмы). +3. После открытия базы данных цепочки проверяет каждый настроенный активный ключ по on-chain авторизации. Валидаторы, аккаунты которых не найдены или чьи ключи не совпадают, удаляются из мониторинга с предупреждением. +4. Выполняет немедленную проверку; кэширует результат для синхронизации с периодическим расписанием. + +### Обработчик на каждый блок + +При каждом блоке: + +1. **Автоотключение при последовательных блоках**: Если отслеживаемый валидатор произвёл `validator-guard-disable` последовательных блоков, транслируется `witness_update_operation` с нулевым ключом для его отключения, и валидатор помечается как автоотключённый. Любой блок от *другого* валидатора сбрасывает все счётчики последовательных блоков. +2. **Подтверждение транзакции**: Сканирует ожидающие ID транзакций восстановления в блоке. При совпадении помечает восстановление как подтверждённое и очищает состояние отслеживания. +3. **Упреждающее планирование**: Если какой-либо отслеживаемый валидатор запланирован в следующие 3 слота, запускает немедленную проверку, чтобы ключ можно было восстановить до прихода слота. +4. **Периодическая проверка**: В противном случае основная проверка выполняется каждые `validator-guard-interval` блоков. Пока узел всё ещё догоняет после запуска, проверки выполняются каждые 10 блоков. + +### Основная проверка + +Каждая проверка (по порядку): + +1. **Защита от устаревшего производства**: Если активен `enable-stale-production` и участие в сети < 33%, пропускает все восстановления. Автоматически снимается, когда участие достигает ≥ 33%. +2. **Проверка синхронизации**: Пропускает, если время головного блока отстаёт от системных часов более чем на 2 интервала блока. +3. **Безопасность при длинном форке**: Пропускает, если LIB старше 200 секунд. +4. **Очистка просроченных**: Просрочивает устаревшие незавершённые попытки восстановления, чтобы их можно было повторить. +5. **Проверка ключа для каждого валидатора**: Считывает on-chain ключ подписи. + - Ключ присутствует → очищает состояние ожидающего восстановления и флаг автоотключения. + - Ключ null + валидатор был автоотключён → пропускает автовосстановление (оператор должен разобраться). + - Ключ null + нет незавершённого восстановления → вызывает `send_witness_update`. + +### Транзакция восстановления + +1. Строит `witness_update_operation`, сохраняя текущий on-chain URL и устанавливая ключ подписи на настроенный публичный ключ. +2. Оборачивает в `signed_transaction` с 30-секундным сроком действия и ссылкой на текущий головной блок. +3. Подписывает настроенным активным приватным ключом. +4. Транслирует через P2P. +5. Отслеживает ID транзакции в `_pending_confirmations` для предотвращения дублирующих отправок. + +--- + +## Защитные механизмы + +| Механизм | Поведение | +|----------|-----------| +| **Устаревшее производство** | При `enable-stale-production = true` автовосстановление отключено во избежание трансляции на minority fork. Автоматически снимается при участии ≥ 33%. | +| **Экстренный режим** | Во время экстренного консенсуса защита от устаревшего производства обходится — восстановление ключа может понадобиться для восстановления. | +| **Проверка синхронизации** | Выполняется только при синхронизации узла. | +| **Обнаружение длинного форка** | Пропускает, если LIB старше 200 секунд. | +| **Проверка авторизации** | Активные ключи проверяются по on-chain авторизации при запуске. | +| **Автоотключение при последовательных блоках** | Автоматически обнуляет ключ подписи после N последовательных блоков от одного валидатора. Автовосстановление подавляется до ручного исправления ключа оператором. | +| **Предотвращение дублирования** | Незавершённые восстановления отслеживаются со сроком действия; дублирующие транзакции не отправляются. | + +--- + +## Сообщения журнала + +| Сообщение | Значение | +|-----------|----------| +| `monitoring validator 'alice' (signing key: VIZ...)` | Плагин запущен для этого валидатора | +| `enable-stale-production detected — auto-restore is DISABLED` | Активен режим устаревшего производства; восстановление подавлено | +| `network is healthy (XX%), auto-clearing stale production override` | Защита от устаревшего производства снята | +| `'alice' has null signing key on-chain — initiating restore` | Обнаружен нулевой ключ, скоро будет выполнена трансляция | +| `broadcasting witness_update [ID: ...] for 'alice' — restoring key to VIZ...` | Транзакция восстановления отправлена | +| `CONFIRMED restoration for 'alice' in block #N` | Восстановление подтверждено on-chain | +| `POTENTIAL LONG FORK DETECTED! LIB #N is Xs old. Skipping restoration.` | Восстановление пропущено из-за устаревшего LIB | +| `validator 'alice' produced N consecutive blocks — auto-disabling` | Достигнут порог последовательных блоков | +| `'alice' was auto-disabled (consecutive block limit), skipping auto-restore` | Автовосстановление подавлено после автоотключения | +| `witness_update FAILED for 'alice': [error]` | Трансляция не удалась | + +--- + +## Устранение неполадок + +| Проблема | Проверьте | +|----------|-----------| +| Восстановление не срабатывает | Убедитесь, что `validator-guard-enabled = true`; узел синхронизирован; аккаунт зарегистрирован как валидатор | +| Отключено при `enable-stale-production = true` | Ожидаемо — ждёт участия в сети ≥ 33% | +| Транзакция не прошла | Убедитесь, что `active_wif` соответствует активной авторизации аккаунта. Проверьте предупреждение при запуске о несовпадении ключей | +| Ошибка разбора конфигурации | Каждая запись должна быть допустимым 3-элементным JSON-массивом: `["name", "signing_wif", "active_wif"]` | +| Валидатор автоотключён и не восстанавливается | Был достигнут порог последовательных блоков. Выясните причину, вручную восстановите ключ подписи on-chain; флаг автоотключения сбрасывается, как только ключ обнаруживается как ненулевой | +| Предупреждение об авторизации при запуске | `WARNING: Configured active key ... does NOT have authority on-chain` — обновите ключ в конфигурации | + +--- + +См. также: [Узел-валидатор](./validator-node.md) — настройка ключа подписи; [Плагин Validator](../plugins/validator.md) — внутреннее устройство цикла производства. diff --git a/@l10n/ru/docs/node/validator-node.md b/@l10n/ru/docs/node/validator-node.md new file mode 100644 index 0000000000..43b920ddd4 --- /dev/null +++ b/@l10n/ru/docs/node/validator-node.md @@ -0,0 +1,235 @@ +# Запуск узла-валидатора + +Валидаторы (производители блоков) — аккаунты, которые алгоритм Fair-DPOS планирует для производства блоков каждые 3 секунды. Для запуска узла-валидатора необходим зарегистрированный аккаунт, ключ подписи и правильно настроенный узел. + +--- + +## Требования + +1. Аккаунт VIZ Ledger, зарегистрированный как валидатор через `validator_update_operation`. +2. WIF-приватный ключ, соответствующий ключу подписи, зарегистрированному в блокчейне. +3. Синхронизированный полный узел (плагин валидатора требует плагины chain + p2p + snapshot). + +--- + +## Конфигурация + +Используйте `share/vizd/config/config_witness.ini` в качестве базового шаблона. + +Ключевые настройки: + +```ini +# P2P — разрешить публичные входящие соединения для распространения блоков +p2p-endpoint = 0.0.0.0:2001 +p2p-seed-node = seed1.viz.media:2001 + +# RPC — привязать к localhost для безопасности (валидаторам публичный API не нужен) +webserver-http-endpoint = 127.0.0.1:8090 +webserver-ws-endpoint = 127.0.0.1:8091 + +# Обязательные плагины для валидатора +plugin = chain p2p webserver json_rpc database_api network_broadcast_api validator witness_api + +# Пропустить индексирование виртуальных операций для экономии памяти +skip-virtual-ops = true + +# Разделяемая память — 2G достаточно для маломощных сборок валидатора +shared-file-size = 2G + +# ─── Идентификатор валидатора ───────────────────────────────── +# Имя вашего аккаунта-валидатора +validator = myvalidator + +# Ваш ключ подписи в формате WIF +private-key = 5JxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxWIF + +# Порог участия — остановить производство при падении сети ниже этого значения +required-participation = 33 + +# НЕ включать на основной сети (только для тестовой сети/бутстрапа) +# enable-stale-production = false +``` + +--- + +## Синхронизация NTP + +Точное время критически важно для производства блоков. Плагин валидатора поддерживает собственный NTP-клиент: + +```ini +# NTP-серверы (по умолчанию: pool.ntp.org, time.google.com, time.cloudflare.com) +ntp-server = pool.ntp.org +ntp-server = time.cloudflare.com + +# Проверять NTP каждые 15 минут +ntp-request-interval = 900 + +# Отбрасывать ответы NTP с временем roundtrip > 150 мс +ntp-round-trip-threshold = 150 +``` + +Убедитесь, что системные часы сервера также синхронизированы (через `chrony` или `systemd-timesyncd`). + +--- + +## Запуск узла + +```bash +./vizd --config-file /data/vizd/config.ini --data-dir /data/vizd +``` + +### Docker + +```bash +docker run -d \ + --name vizd-validator \ + --restart unless-stopped \ + -p 2001:2001 \ + -v /data/vizd:/var/lib/vizd \ + -e VIZD_WITNESS=myvalidator \ + -e VIZD_PRIVATE_KEY=5Jxxx... \ + vizblockchain/vizd:lowmem +``` + +Для валидаторов используйте образ `lowmem` — он не включает ненужные плагины индексирования. + +--- + +## Регистрация / обновление валидатора + +Используйте `cli_wallet` или совместимый кошелёк для трансляции `validator_update_operation`: + +```json +{ + "type": "validator_update_operation", + "value": { + "owner": "myvalidator", + "url": "https://mysite.example/validator", + "block_signing_key": "VIZ5hqSa...", + "props": [3, { + "account_creation_fee": "1.000 VIZ", + "maximum_block_size": 65536 + }] + } +} +``` + +Значение `block_signing_key` должно совпадать с `private-key` в конфигурации узла. + +Чтобы отключить валидатора (убрать из расписания), установите нулевой ключ: + +```json +"block_signing_key": "VIZ1111111111111111111111111111111114T1Anm" +``` + +--- + +## Цикл производства: что делает узел + +Плагин валидатора запускает таймер производства 250 мс в **выделенном потоке** (изолированном от P2P ввода-вывода). При каждом тике вызывается `maybe_produce_block()`, который проверяет (по порядку): + +1. **Проверка синхронизации** (DLT-режим): не производить, если узел нагоняет пиров. +2. **Проверка снапшота**: не производить, если идёт создание снапшота. +3. **Проверка участия**: сеть должна иметь ≥33% участия валидаторов. +4. **Назначение слота**: запланирован ли валидатор этого узла для текущего слота? +5. **Проверка ключа**: есть ли у узла правильный приватный ключ? +6. **Обнаружение minority fork**: если последние 21 блок произведены только валидаторами этого узла — откат и повторная синхронизация. +7. **Разрешение коллизии форков**: если на той же высоте существует другой блок — применяется сравнение весов голосов. +8. **Проверка задержки**: если узел опоздал более чем на 500 мс — пропустить слот. +9. **Генерация и трансляция** блока. + +Полный граф выполнения — в разделе [Плагин валидатора](../plugins/validator.md). + +--- + +## Результаты производства (сообщения в логах) + +| Результат | Значение | +|----------|---------| +| `produced` | Блок успешно произведён и транслирован | +| `not_synced` | Узел нагоняет или выполняется снапшот | +| `not_time_yet` | Нет доступного слота или дрейф NTP | +| `not_my_turn` | Для этого слота запланирован другой валидатор | +| `no_private_key` | Запланированный валидатор настроен, но приватный ключ отсутствует | +| `low_participation` | Участие сети ниже порога | +| `lag` | Пробуждение >500 мс после слота — слот пропущен | +| `fork_collision` | Конкурирующий блок на следующей высоте — ожидание | +| `minority_fork` | Узел на изолированном форке — откат | + +--- + +## Механизмы безопасности + +### Защита от сетевого раздела +Если менее 33% валидаторов участвуют в производстве, оно останавливается для предотвращения split-brain ситуаций. Переопределите с `enable-stale-production = true` только для бутстрапа/тестовой сети. + +### Обнаружение minority fork +Если форк-база данных узла показывает 21+ последовательных блоков только от собственных валидаторов, узел автоматически откатывается к LIB и выполняет повторную синхронизацию. Это позволяет обнаружить сетевую изоляцию. + +### Watchdog производства +Если в течение 180 секунд (60 с для экстренного мастера) при активном `should_be_producing` не был произведён ни один блок, watchdog автоматически сбрасывает зависшие флаги (`minority_fork_recovering`, нагон P2P, синхронизация цепочки) и пытается возобновить производство. + +### Безопасность снапшотов +Производство блоков приостанавливается во время создания снапшота во избежание конфликтов блокировок записи. + +--- + +## Мониторинг + +Следите за этими паттернами в логах: + +``` +# Хорошо: блок произведён +produced block #123456 ... validator=myvalidator + +# Предупреждение: пропущен слот +MISSED-SLOT-OUR-validator: ... + +# Предупреждение: minority fork +MINORITY FORK DETECTED: rolling back to LIB + +# Предупреждение: сработал watchdog +WATCHDOG: no production for 180s, clearing flags +``` + +Также см. [Мониторинг](./monitoring.md) и [Validator Guard](./validator-guard.md) для автоматических оповещений. + +--- + +## Несколько валидаторов на одном узле + +Параметры `validator` и `private-key` можно указывать несколько раз: + +```ini +validator = alice +validator = alice.backup +private-key = 5Jxxx... # Ключ Alice +private-key = 5Jyyy... # Ключ Alice.backup +``` + +Узел будет производить блоки для любого из настроенных валидаторов по расписанию. + +--- + +## Ключ экстренного консенсуса + +Для узлов, участвующих в восстановлении экстренного консенсуса: + +```ini +emergency-private-key = 5Jzzz... # Ключ экстренного комитета +``` + +При его наличии узел автоматически добавляет `CHAIN_EMERGENCY_WITNESS_ACCOUNT` в свой набор валидаторов и участвует в производстве экстренных блоков. См. [Экстренный консенсус](../consensus/emergency-consensus.md). + +--- + +## Устранение неполадок + +| Проблема | Что проверить | +|---------|--------------| +| Не производит блоки | Проверьте `validator` и `private-key` в конфиге; убедитесь, что ключ подписи в блокчейне совпадает с конфигом | +| `no_private_key` в логах | Ключ подписи в блокчейне не совпадает ни с одним `private-key` в конфиге | +| `low_participation` | Проблема работоспособности сети — проверьте количество пиров и статус других валидаторов | +| `minority_fork` | Сетевая изоляция — проверьте подключение к начальным узлам | +| Предупреждения о зависании NTP | Проверьте синхронизацию системного NTP: `chronyc tracking` или `timedatectl` | +| Перехваты слотов | Ключ подписи мог быть обнулён экстренным мастером; восстановите через `validator_update_operation` | diff --git a/@l10n/ru/docs/p2p/forward-mode.md b/@l10n/ru/docs/p2p/forward-mode.md new file mode 100644 index 0000000000..b46c31d885 --- /dev/null +++ b/@l10n/ru/docs/p2p/forward-mode.md @@ -0,0 +1,214 @@ +# Режим Forward — обмен блоками и транзакциями + +Режим forward (`DLT_NODE_STATUS_FORWARD`) — это нормальное рабочее состояние после того, как узел догнал сеть. Вместо вытягивания диапазонов блоков от пиров, узел **толкает** новые блоки и транзакции всем выравненным по форку пирам по мере их поступления. + +--- + +## Шлюз доставки: `exchange_enabled` + +Весь трафик в режиме forward фильтруется двумя флагами для каждого пира: + +| Флаг | Значение | +|------|---------| +| `exchange_enabled == true` | Пир выровнен по форку — его head блок известен нам (или наш ему) | +| `lifecycle_state == ACTIVE` | Пир завершил рукопожатие | + +Оба должны быть истинными, чтобы пир получал трансляции блоков и транзакций. + +Центральная функция трансляции `send_to_all_our_fork_peers()` итерирует все подключённые пиры и пропускает тех, кто не проходит ни одну из проверок. + +--- + +## Установка `exchange_enabled` + +### Начальная установка (рукопожатие hello) + +Во время hello acceptor вызывает `check_fork_alignment()` — многоуровневую проверку с учётом DLT-диапазона: + +| Случай | Проверка | +|--------|---------| +| Пир без блоков (`head_num == 0`) | → выровнен | +| Head пира в нашем DLT-диапазоне | `is_block_known(peer.head_id)` | +| Head пира + 1 == наш DLT earliest | Читаем наш самый ранний блок; проверяем `previous == peer.head_id` | +| Резерв LIB | `is_block_known(peer.lib_id)` | + +Если хотя бы одна проверка прошла → `exchange_enabled = true`. + +### OR-комбинирование + +Обе стороны отправляют сообщения hello друг другу и каждая независимо вычисляет `exchange_enabled`. Финальное значение для пира — это **логическое ИЛИ** определений обеих сторон. Если любая из сторон распознаёт цепочку другой, обмен включается. + +Слейв, чей head ниже DLT-диапазона мастера, не проходит свою `check_fork_alignment` (он ещё не применил блоки мастера), но проверка мастера проходит (он знает head слейва). OR гарантирует включение обмена даже в этом асимметричном случае. + +### Триггеры повторной оценки + +`exchange_enabled` переоценивается при изменении знаний узла о блоках пира: + +| Триггер | Когда | +|---------|-------| +| `transition_to_forward()` | Для каждого пира с `exchange_enabled=false`; повторная проверка `is_block_known(peer_head_id)` | +| `on_dlt_fork_status()` | Пир переходит SYNC → FORWARD; повторная проверка выравнивания форка | +| Блок принят от пира | Если блок применяется к нашей цепочке, немедленно включает обмен для этого пира | + +--- + +## Трансляция блоков + +### Блоки собственного производства + +Когда валидатор производит блок: + +``` +validator.cpp → p2p_plugin.broadcast_block(block) + → dlt_p2p_node.broadcast_block(block) + → send_to_all_our_fork_peers(dlt_block_reply_message, exclude=none, block_id=block.id()) +``` + +Блок отправляется **всем** ACTIVE пирам с включённым обменом. Подавление эха предотвращает повторную отправку пирам, которые уже имеют блок. + +### Ретрансляция полученных блоков + +Когда блок поступает от пира X: + +1. Записать, что X имеет этот блок (`state.record_known_block(block.id())`). +2. Применить блок к цепочке. +3. `send_to_all_our_fork_peers(block_reply, exclude=X, block_id=block.id())` — отправить всем другим ACTIVE пирам с включённым обменом. + +--- + +## Подавление эха блоков + +Без подавления блоки возвращаются к производителю через цепочки ретрансляции: + +``` +A производит блок N → отправляет B, C +B ретранслирует N в A, C +C ретранслирует N в A, B +A получает собственный блок N обратно от B и C — впустую потраченная пропускная способность +``` + +Каждое состояние пира ведёт **кольцевой буфер из 20 последних ID блоков** (`known_blocks`). Перед отправкой блока пиру узел проверяет `peer.has_block(block_id)`. Если уже известен, отправка пропускается. + +Пир записывается как "имеющий" блок в двух случаях: +- **Мы только что отправили его** — записывается в `send_to_all_our_fork_peers` после отправки. +- **Они отправили его нам** — записывается в `on_dlt_block_reply` при получении. + +В журнале ретрансляции показаны счётчики фильтрации эха: +``` +Relay block_reply to 3 peers (0 skipped: no_exchange, 0 skipped: not_active, 1 skipped: echo) +``` + +--- + +## Трансляция транзакций + +### Самостоятельно (через API) + +Транзакция, отправленная через `network_broadcast_api` → добавляется в P2P-мемпул → `dlt_transaction_message` отправляется всем ACTIVE пирам с включённым обменом. + +### Ретрансляция полученных транзакций + +Транзакция поступает от пира X → добавляется в мемпул → ретранслируется всем ACTIVE пирам с включённым обменом **кроме X**. + +### Предварительная фильтрация мемпула + +Перед принятием транзакции в мемпул или её пересылкой она должна пройти: + +| Проверка | Неудача | +|---------|---------| +| Дубликат (`trx_id` уже в мемпуле) | Молча пропустить | +| Истекшая (`expiration < now`) | Отклонить; увеличить spam-удар, если от пира | +| Срок действия слишком далеко (>24 ч в будущем) | Отклонить; увеличить spam-удар | +| Слишком большая (>`dlt-mempool-max-tx-size`, по умолч. 64 КБ) | Отклонить; увеличить spam-удар | +| TaPoS недействителен (ссылочный блок неизвестен) | Отклонить; увеличить spam-удар | +| Мемпул полон | Вытеснить запись с наиболее ранним сроком, затем добавить | + +**Временные записи:** Транзакции, полученные в режиме SYNC, помечаются `is_provisional = true` — хранятся локально, но не пересылаются пирам. При переходе в FORWARD временные записи повторно проверяются против текущего head, недействительные удаляются. + +--- + +## Переход SYNC → FORWARD + +### Триггеры + +| Триггер | Условие | +|---------|---------| +| Ответ на диапазон блоков с `is_last=true` | И применён хотя бы один блок (не все dead-fork) | +| `check_sync_catchup()` | `our_head >= все активные heads пиров` И хотя бы один активный пир | +| Таймаут застоя | 30 с без блока, 3 повтора исчерпаны | + +`check_sync_catchup()` запускается после каждого принятия блока и каждые 5 секунд из периодической задачи. + +**Защита от изоляции:** `check_sync_catchup()` НЕ заявляет о догоне при нулевом количестве активных пиров. Вместо этого запускается 60-секундный таймер изоляции; после истечения срабатывает `emergency_peer_reset()` (см. ниже). + +### Действия при переходе + +1. Уведомить все подключённые пиры: транслировать `dlt_fork_status_message` с `node_status=FORWARD` каждому активному/синхронизирующемуся пиру (не только с включённым обменом). Это позволяет пирам немедленно переоценить `exchange_enabled` для нас. +2. Переоценить `exchange_enabled` для всех пиров. +3. Повторно проверить и очистить недействительные временные записи мемпула. +4. Сбросить `_sync_stagnation_retries = 0`. +5. Сбросить `_last_block_received_time = now`, чтобы таймер застоя forward начинал заново. + +--- + +## Откат FORWARD → SYNC + +Если блоки перестают поступать в режиме FORWARD, узел откатывается в SYNC: + +| Триггер | Условие | +|---------|---------| +| Ответ hello показывает, что пир далеко впереди | `peer_head_num > our_head + 2` при получении hello_reply | +| Периодическая проверка | `check_forward_behind()`: любой активный пир имеет `peer_head_num > our_head + 2` | +| Застой | `check_forward_stagnation()`: head не двигается 30 с И хотя бы один пир впереди | + +**Бездействие при отсутствии опережающих пиров:** `check_forward_stagnation()` НЕ переходит в SYNC, когда все подключённые пиры имеют тот же head. Синхронизировать нечего; переход просто вызвал бы осцилляцию. Таймер застоя сбрасывается, и узел остаётся в FORWARD. + +При переходе в SYNC `_last_block_received_time` сбрасывается до `now`, чтобы таймер застоя синхронизации начинал заново (не унаследовав время из фазы FORWARD). + +--- + +## Восстановление изоляции пира + +Когда все пиры отключены или заблокированы (например, после паузы снимка), нормальные переходы режима SYNC/FORWARD крутятся бессмысленно. После **60 секунд** с нулём активных соединений: + +`emergency_peer_reset()`: +1. Переводит все BANNED пиры обратно в состояние DISCONNECTED; очищает `spam_strikes`. +2. Сбрасывает backoff всех DISCONNECTED пиров до 30 с (`INITIAL_RECONNECT_BACKOFF_SEC`) с `next_reconnect_attempt = now`. +3. Очищает счётчики повторов застоя. +4. На следующем тике периодической задачи (~5 с) `periodic_reconnect_check()` немедленно переподключается. + +--- + +## Что не пересылается + +| Сценарий | Трафик | +|---------|-------| +| Пир имеет `exchange_enabled=false` | Нет блоков, нет транзакций | +| Узел в режиме SYNC | Нет трансляций; только запросы диапазона и запросы gap fill | +| Обработка блоков приостановлена (`_block_processing_paused=true`) | Блоки принимаются и ставятся в очередь, но периодические задачи с доступом к БД пропускаются | + +--- + +## Сводка доставки + +| Событие | Получатели | Исключены | Фильтрация эха | +|---------|-----------|---------|--------------| +| Узел производит блок | Все ACTIVE пиры с `exchange_enabled=true` | (нет) | Пиры с блоком в `known_blocks` | +| Узел получает блок от X | Все ACTIVE пиры с `exchange_enabled=true` | X | Пиры с блоком в `known_blocks` | +| Узел создаёт транзакцию | Все ACTIVE пиры с `exchange_enabled=true` | (нет) | (нет) | +| Узел получает транзакцию от X | Все ACTIVE пиры с `exchange_enabled=true` | X | (нет) | + +--- + +## `peer_head_num` — устаревший снимок + +`peer_head_num`, показанный в [статистике](./stats-reference.md), обновляется из: +- Рукопожатия hello +- Обменов сообщениями `dlt_fork_status_message` +- Ретрансляции блоков (получение блока N означает `peer_head_num ≥ N`) + +Между этими событиями реальный head цепочки пира может быть значительно выше. Не следует воспринимать `peer_head_num` как актуальное значение. + +--- + +См. также: [Обзор P2P](./overview.md), [Сценарии синхронизации](./sync-scenarios.md), [Справочник статистики](./stats-reference.md), [Сообщения](./messages.md). diff --git a/@l10n/ru/docs/p2p/messages.md b/@l10n/ru/docs/p2p/messages.md new file mode 100644 index 0000000000..4a816aaa6c --- /dev/null +++ b/@l10n/ru/docs/p2p/messages.md @@ -0,0 +1,461 @@ +# Справочник P2P-сообщений + +DLT P2P использует бинарный протокол через raw TCP. Каждое сообщение оформляется 4-байтовым заголовком с порядком байт little-endian, содержащим ID типа сообщения, за которым следует полезная нагрузка с префиксом длины, сериализованная с помощью FC reflection. + +**Заголовочный файл:** [libraries/network/include/graphene/network/dlt_p2p_messages.hpp](../../libraries/network/include/graphene/network/dlt_p2p_messages.hpp) + +--- + +## Сводка типов сообщений + +| ID типа | Имя | Направление | Назначение | +|---------|-----|------------|-----------| +| 5100 | `dlt_hello_message` | инициатор → принимающий | Начальное рукопожатие — состояние цепочки и возможности | +| 5101 | `dlt_hello_reply_message` | принимающий → инициатор | Ответ на рукопожатие — выравнивание форков, статус обмена | +| 5102 | `dlt_range_request_message` | любое | Запросить у пира наличие конкретного блока | +| 5103 | `dlt_range_reply_message` | любое | Ответ: доступный диапазон блоков | +| 5104 | `dlt_get_block_range_message` | синхр. → пир | Запрос диапазона блоков (массовая синхронизация) | +| 5105 | `dlt_block_range_reply_message` | пир → синхр. | Массовая доставка блоков | +| 5106 | `dlt_get_block_message` | любое | Запрос одного блока | +| 5107 | `dlt_block_reply_message` | любое | Доставка одного блока | +| 5108 | `dlt_not_available_message` | любое | Запрошенный блок недоступен | +| 5109 | `dlt_fork_status_message` | любое | Обновление состояния живой цепочки (голова, LIB, форк, DLT-диапазон) | +| 5110 | `dlt_peer_exchange_request` | любое | Запросить список адресов пиров | +| 5111 | `dlt_peer_exchange_reply` | любое | Ответ со списком адресов пиров | +| 5112 | `dlt_peer_exchange_rate_limited` | любое | Ответ с ограничением скорости на запрос обмена пирами | +| 5113 | `dlt_transaction_message` | любое | Трансляция транзакции | +| 5114 | `dlt_soft_ban_message` | любое → забаненный | Уведомление о мягком бане перед отключением | +| 5115 | `dlt_gap_fill_request` | любое | Запрос конкретных блоков для заполнения разрыва | +| 5116 | `dlt_gap_fill_reply` | любое | Доставка блоков для заполнения разрыва | + +--- + +## Перечисления + +### `dlt_node_status` + +| Значение | Значение | +|---------|---------| +| `DLT_NODE_STATUS_SYNC` (0) | Узел отстаёт; активно вытягивает блоки от пиров | +| `DLT_NODE_STATUS_FORWARD` (1) | Узел обновлён; обменивается блоками через трансляцию | + +### `dlt_fork_status` + +| Значение | Значение | +|---------|---------| +| `DLT_FORK_STATUS_NORMAL` (0) | На форке большинства | +| `DLT_FORK_STATUS_LOOKING_RESOLUTION` (1) | Форк обнаружен; выполняется алгоритм разрешения | +| `DLT_FORK_STATUS_MINORITY` (2) | Подтверждён на minority fork | + +### `dlt_peer_lifecycle_state` + +| Значение | Значение | +|---------|---------| +| `DLT_PEER_LIFECYCLE_CONNECTING` (0) | TCP-соединение в процессе (таймаут 5 с) | +| `DLT_PEER_LIFECYCLE_HANDSHAKING` (1) | Обмен hello в процессе (таймаут 10 с) | +| `DLT_PEER_LIFECYCLE_SYNCING` (2) | Обмен блоками синхронизации (`we_need_sync_items` или пир нуждается в блоках от нас) | +| `DLT_PEER_LIFECYCLE_ACTIVE` (3) | Полностью синхронизирован; нормальный обмен блоками/транзакциями | +| `DLT_PEER_LIFECYCLE_DISCONNECTED` (4) | Не подключён; допустим для переподключения после отката | +| `DLT_PEER_LIFECYCLE_BANNED` (5) | Мягко забанен; нет переподключения до истечения бана | + +--- + +## Подробный справочник сообщений + +### 5100 — `dlt_hello_message` + +Отправляется немедленно после установки TCP-соединения. Содержит полное состояние цепочки и возможности инициирующего узла. + +```cpp +struct dlt_hello_message { + uint16_t protocol_version; // в настоящее время 1 + block_id_type head_block_id; + uint32_t head_block_num; + block_id_type lib_block_id; + uint32_t lib_block_num; + uint32_t dlt_earliest_block; // старейший блок в нашем скользящем DLT log (0 если нет) + uint32_t dlt_latest_block; // последний блок в нашем DLT log + bool emergency_active; // экстренный консенсус активен в настоящее время + bool has_emergency_key; // мы держим приватный ключ экстренного committee + uint8_t fork_status; // перечисление dlt_fork_status + uint8_t node_status; // перечисление dlt_node_status (SYNC или FORWARD) +}; +``` + +**Примечания:** +- `dlt_earliest_block` критически важен для выравнивания форков с учётом DLT-диапазона. Принимающий использует его во избежание запроса блоков, которых больше нет в скользящем окне инициатора. +- `has_emergency_key` идентифицирует экстренный мастер-узел. Другие узлы могут приоритизировать синхронизацию от этого пира во время режима экстренного консенсуса. + +--- + +### 5101 — `dlt_hello_reply_message` + +Отправляется принимающим в ответ на 5100. Завершает рукопожатие. + +```cpp +struct dlt_hello_reply_message { + bool exchange_enabled; // true если мы считаем инициатора обновлённым + bool fork_alignment; // true если инициатор на том же форке + block_id_type initiator_head_seen; // эхо: head_block_id инициатора как мы его видим + block_id_type initiator_lib_seen; // эхо: lib_block_id инициатора как мы его видим + uint32_t our_dlt_earliest; // наш ранний DLT-блок + uint32_t our_dlt_latest; // наш последний DLT-блок + uint8_t our_fork_status; // перечисление dlt_fork_status + uint8_t our_node_status; // перечисление dlt_node_status +}; +``` + +**Проверка выравнивания форков многоуровневая** для обработки обрезанных DLT-диапазонов: + +| Случай | Выполняемая проверка | +|--------|---------------------| +| Пир не имеет блоков (`head_num == 0`) | → выровнен | +| Голова пира в нашем DLT-диапазоне | `is_block_known(peer.head_block_id)` | +| head пира + 1 == наш ранний | Читаем `our_earliest_block.previous == peer.head_block_id` | +| Запасной вариант | `is_block_known(peer.lib_block_id)` | + +**`exchange_enabled`** равно `true`, когда fork_db принимающего содержит головной блок инициатора (т.е. инициатор находится в окне обмена и на том же форке). Только exchange-enabled пиры получают трансляции блоков и транзакций. + +--- + +### 5102 — `dlt_range_request_message` + +Спрашивает пира, есть ли у него конкретный блок по номеру и/или ID. + +```cpp +struct dlt_range_request_message { + uint32_t block_num; + block_id_type block_id; // хэш запрашиваемого блока +}; +``` + +--- + +### 5103 — `dlt_range_reply_message` + +Ответ на 5102. Возвращает доступный диапазон обслуживания от пира. + +```cpp +struct dlt_range_reply_message { + uint32_t range_start; // ранний блок, который пир может обслужить + uint32_t range_end; // последний блок, который пир может обслужить + bool has_blocks; // false если у пира нет блоков вообще +}; +``` + +--- + +### 5104 — `dlt_get_block_range_message` + +Запрашивает непрерывный диапазон блоков в режиме SYNC. Максимум 200 блоков на запрос. + +```cpp +struct dlt_get_block_range_message { + uint32_t start_block_num; + uint32_t end_block_num; + block_id_type prev_block_id; // хэш блока (start_block_num - 1); используется для проверки непрерывности цепочки +}; +``` + +**Примечания:** +- Обслуживающий пир проверяет, что `blocks[0].previous == prev_block_id` перед отправкой. +- Разрыв между `start_block_num` и `dlt_earliest_block` обслуживающего пира может потребовать промежуточного пира. + +--- + +### 5105 — `dlt_block_range_reply_message` + +Ответ на 5104. Содержит до 200 блоков. + +```cpp +struct dlt_block_range_reply_message { + std::vector blocks; + uint32_t last_block_next_available; // следующий доступный блок после этой партии + bool is_last; // true если на этом пире больше нет блоков +}; +``` + +**`is_last = true`** запускает `transition_to_forward()` на принимающей стороне, если узел обновлён. + +--- + +### 5106 — `dlt_get_block_message` + +Запрашивает один блок по номеру. + +```cpp +struct dlt_get_block_message { + uint32_t block_num; + block_id_type prev_block_id; // хэш (block_num - 1) для проверки связи цепочки +}; +``` + +--- + +### 5107 — `dlt_block_reply_message` + +Ответ на 5106. + +```cpp +struct dlt_block_reply_message { + signed_block block; + uint32_t next_available; // следующий номер блока, который пир может обслужить (0 если на голове) + bool is_last; // true если это головной блок пира +}; +``` + +--- + +### 5108 — `dlt_not_available_message` + +Отправляется, когда пир не может обслужить запрошенный блок (блок вне диапазона DLT log или блок неизвестен). + +```cpp +struct dlt_not_available_message { + uint32_t block_num; +}; +``` + +Запрашивающий узел должен найти другой пир с блоком в диапазоне или запустить заполнение разрыва / SYNC-режим. + +--- + +### 5109 — `dlt_fork_status_message` + +Обновление состояния живой цепочки. Отправляется при изменении головы, LIB, DLT-окна или статуса форка узла, и при переходе SYNC → FORWARD. + +```cpp +struct dlt_fork_status_message { + uint8_t fork_status; // перечисление dlt_fork_status + block_id_type head_block_id; + uint32_t head_block_num; + block_id_type lib_block_id; + uint32_t lib_block_num; + uint32_t dlt_earliest_block; + uint32_t dlt_latest_block; + uint8_t node_status; // перечисление dlt_node_status +}; +``` + +**Ключевой сценарий использования:** При переходе узла SYNC → FORWARD он транслирует это сообщение всем подключённым пирам, чтобы они могли немедленно переоценить `exchange_enabled` для этого узла, не ожидая следующего цикла hello. + +Принимающий узел обновляет своё локальное `dlt_peer_state` для отправителя и повторно проверяет выравнивание форков + право на обмен. + +--- + +### 5110 — `dlt_peer_exchange_request` + +Пустое сообщение, запрашивающее список известных пиров. + +```cpp +struct dlt_peer_exchange_request { + // пусто +}; +``` + +Ограничение скорости: **3 запроса за 5-минутное скользящее окно** на пир. Нарушители получают `dlt_peer_exchange_rate_limited` (5112). + +--- + +### 5111 — `dlt_peer_exchange_reply` + +Ответ на 5110. Содержит информацию о конечных точках известных пиров. + +```cpp +struct dlt_peer_endpoint_info { + fc::ip::endpoint endpoint; + node_id_t node_id; +}; + +struct dlt_peer_exchange_reply { + std::vector peers; +}; +``` + +**Фильтры, применяемые перед включением в ответ:** +- Время работы пира ≥ `dlt-peer-exchange-min-uptime-sec` (по умолчанию 600 с) +- Максимум `dlt-peer-exchange-max-per-subnet` (по умолчанию 2) пиров на /24-подсеть +- Пиры с `is_incoming` исключены (эфемерные исходные порты) +- Максимум `dlt-peer-exchange-max-per-reply` (по умолчанию 10) пиров всего + +--- + +### 5112 — `dlt_peer_exchange_rate_limited` + +Отправляется вместо 5111 при превышении лимита скорости запросов. + +```cpp +struct dlt_peer_exchange_rate_limited { + uint32_t wait_seconds; // сколько времени запрашивающий должен ждать перед повторной попыткой +}; +``` + +--- + +### 5113 — `dlt_transaction_message` + +Переносит подписанную транзакцию для распространения через mempool. + +```cpp +struct dlt_transaction_message { + signed_transaction trx; +}; +``` + +Полученные транзакции добавляются в P2P mempool (фильтруются по сроку, TaPoS, размеру) перед отправкой в цепочечный `_pending_tx`. Успешно принятые транзакции пересылаются всем exchange-enabled пирам. + +--- + +### 5114 — `dlt_soft_ban_message` + +Отправляется пиру непосредственно перед закрытием соединения из-за спама или нарушения протокола. Принимающий пир входит в состояние BANNED на `ban_duration_sec` и не будет пытаться переподключиться до истечения бана. + +```cpp +struct dlt_soft_ban_message { + uint32_t ban_duration_sec; // длительность бана в секундах + std::string reason; // причина в понятном человеку формате +}; +``` + +Общие причины в логах: +- `"spam_strikes_exceeded"` — 10 страйков за недействительные пакеты +- `"dead_fork_blocks"` — пир неоднократно присылал блоки с мёртвого форка +- `"protocol_violation"` — неожиданный тип сообщения или некорректные данные + +--- + +### 5115 — `dlt_gap_fill_request` + +Запрашивает конкретные блоки для заполнения обнаруженного разрыва в потоке блоков узла. Работает в обоих режимах SYNC и FORWARD. + +```cpp +struct dlt_gap_fill_request { + std::vector block_nums; // конкретные запрашиваемые номера блоков +}; +``` + +**Ограничения:** +- Максимум **100 блоков** на запрос (`GAP_FILL_MAX_BLOCKS`). +- **5-секундное охлаждение** между запросами от одного узла (`GAP_FILL_COOLDOWN_SEC`). +- Более крупные разрывы запрашиваются кусками по 100 блоков; последующие куски запускаются в следующем цикле периодической задачи. +- Обслуживающий пир читает блоки из своего `dlt_block_log`; номера блоков вне диапазона обслуживания дают 5108. +- Обслуживание принимает запросы от любого активного пира (не только exchange-enabled). + +Запускается из трёх мест: +1. `on_dlt_block_reply()` — обнаружен внеочерёдной блок +2. `periodic_task()` — проактивная проверка разрывов каждые 5 с +3. `resume_block_processing()` — после завершения паузы снимка + +--- + +### 5116 — `dlt_gap_fill_reply` + +Ответ на 5115. Содержит запрошенные блоки (может быть подмножеством, если некоторые недоступны). + +```cpp +struct dlt_gap_fill_reply { + std::vector blocks; // запрошенные блоки; может быть частичным +}; +``` + +--- + +## Поток рукопожатия + +``` +Инициатор Принимающий + │ │ + │──TCP connect──────────────────────►│ + │ │ + │ 5100 dlt_hello_message │ + │──────────────────────────────────►│ + │ (head/lib, DLT-диапазон, │ + │ emergency_active, node_status) │ + │ │ проверка выравнивания форков + │ │ установка exchange_enabled + │ 5101 dlt_hello_reply_message │ + │◄──────────────────────────────────│ + │ (exchange_enabled, fork_alignment, │ + │ наш DLT-диапазон, наш node_status)│ + │ │ + ├── exchange_enabled = true ──────────┼── начинается обмен FORWARD или синхронизация + └── exchange_enabled = false ─────────┴── инициатор входит в SYNC-режим +``` + +--- + +## Поток SYNC-режима + +``` +Синхр. узел Обслуживающий пир + │ │ + │ 5104 dlt_get_block_range │ + │ (start=our_head+1, end=+200, │ + │ prev_block_id=our_head_id) │ + │──────────────────────────────────►│ + │ │ читает dlt_block_log + │ 5105 dlt_block_range_reply │ + │◄──────────────────────────────────│ + │ (blocks=[N+1..N+200], is_last) │ + │ │ + │ применяет каждый блок │ + │ если is_last → переход в forward │ + │ иначе → запрашивает следующую │ + │ партию │ +``` + +Если разрыв существует между `our_head + 1` и `dlt_earliest_block` обслуживающего пира, узел ищет промежуточный пир перед запросом. + +--- + +## Трансляция в FORWARD-режиме + +``` +производитель блока Пир A Пир B (exchange-enabled) + │ │ │ + │ произвести блок │ │ + │ ─5109 fork_status─────►│ │ + │ (через уведомление │ │ + │ о переходе) │ │ + │ │ │ + │ ─трансляция блока─────►│ (exchange-enabled) │ + │ │ ─трансляция блока─────►│ + │ │ │ push_block() + │ │ │ ─5109 fork_status─► пиры +``` + +Только exchange-enabled пиры в `DLT_FORK_STATUS_NORMAL` или `DLT_FORK_STATUS_LOOKING_RESOLUTION` получают трансляции блоков. + +--- + +## Поток заполнения разрывов + +``` +Узел (FORWARD, разрыв обнаружен) Пир (имеет блоки разрыва) + │ │ + │ 5115 dlt_gap_fill_request │ + │ (block_nums=[N+1, N+2, N+3]) │ + │──────────────────────────────────►│ + │ │ читает dlt_block_log + │ 5116 dlt_gap_fill_reply │ + │◄──────────────────────────────────│ + │ (blocks=[N+1, N+2, N+3]) │ + │ │ + │ применяет блоки → голова продвиг. │ +``` + +--- + +## Формат провода + +Каждое сообщение записывается как: + +``` +[ 4 байта: ID типа (uint32 LE) ][ 4 байта: длина полезной нагрузки (uint32 LE) ][ N байт: FC-сериализованная полезная нагрузка ] +``` + +Чтение использует `fc::tcp_socket::readsome()` / `writesome()` (неблокирующий, с уступкой волокна). Слоя шифрования нет — все пиры в одной цепочке разделяют общую сетевую идентичность и сообщения не подписываются пер-сообщение. + +--- + +См. также: [Обзор P2P](./overview.md), [Сценарии синхронизации](./sync-scenarios.md), [Справочник статистики](./stats-reference.md). diff --git a/@l10n/ru/docs/p2p/overview.md b/@l10n/ru/docs/p2p/overview.md new file mode 100644 index 0000000000..24bf1ff297 --- /dev/null +++ b/@l10n/ru/docs/p2p/overview.md @@ -0,0 +1,288 @@ +# Обзор P2P-сети + +VIZ Ledger использует пользовательский DLT P2P-протокол, который заменил устаревший сетевой уровень graphene на основе synopsis. Новая конструкция оптимизирована для DLT-режима (узлы на основе снимков со скользящими block log) и устраняет сложный synopsis предков graphene в пользу более простого обмена блоками на основе диапазонов. + +--- + +## Архитектура + +``` +┌─────────────────────────────────────────────────────────┐ +│ p2p_plugin (плагин AppBase) │ +│ └─ dlt_delegate (реализует dlt_p2p_delegate) │ +│ └─ мост к состоянию цепочки: db(), fork_db, │ +│ block_log │ +├─────────────────────────────────────────────────────────┤ +│ dlt_p2p_node │ +│ ├─ цикл принятия (входящие TCP-соединения) │ +│ ├─ периодическая задача (тик 5с: переподключение, │ +│ │ статистика, разрывы) │ +│ └─ dlt_peer_state × N (по одному на подключённый пир) │ +├─────────────────────────────────────────────────────────┤ +│ Формат провода: raw TCP, заголовок (тип + длина) + данные│ +│ Модель волокон: весь I/O в одном fc::thread, кооп. │ +└─────────────────────────────────────────────────────────┘ +``` + +### Решения по проектированию + +| Решение | Обоснование | +|---------|------------| +| Паттерн делегата | `dlt_p2p_node` связывает только `fc` + `graphene_protocol`. Прямой доступ к цепочке предоставляется через `dlt_p2p_delegate` во избежание циклических зависимостей. | +| Raw TCP (без STCP-шифрования) | DLT-экстренный режим переключает всех валидаторов одновременно — совместимое шифрование не требуется. Более простой протокол провода. | +| Кооперативные волокна (fc::thread) | Весь I/O использует `readsome()`/`writesome()`, которые уступают волокно. Несколько пиров в одном потоке без мьютексов. | +| Отдельный P2P mempool | Цепочечный `_pending_tx` вступает в силу только после принятия. P2P mempool фильтрует по сроку действия, TaPoS и размеру до отправки в цепочку, снижая нагрузку на evaluator. | +| Встроенная замена плагина | Имя плагина по-прежнему `"p2p"`, порт по-прежнему `2001`/`4243`, публичный API не изменился. Старый и новый протоколы несовместимы; двойной режим создаёт изолированные подсети. | + +--- + +## Жизненный цикл пира + +Каждое соединение пира проходит через следующие состояния: + +``` +CONNECTING ──(TCP установлен)──► HANDSHAKING + таймаут 5с ↓ ↓ таймаут 10с + DISCONNECTED hello/hello_reply + ▲ ↓ + │ SYNCING ──(догнал)──► ACTIVE + │ │ + └──(отключение/ошибка)───────────────┘ + │ + BANNED ◄──(spam_strikes ≥ 10)────────┘ +``` + +**Значения таймаутов:** +- Connecting → DISCONNECTED: **5 секунд** +- Handshaking → DISCONNECTED: **10 секунд** + +**Откат переподключения:** 30 с → 60 с → … → 3600 с с ±25% джиттером. Откат сбрасывается после стабильного соединения >5 минут. Пиры без ответа в течение 8 часов удаляются навсегда. + +**Экстренный сброс пиров:** Если все пиры изолированы (ноль активных соединений) в течение 60 секунд, `emergency_peer_reset()` очищает все мягкие баны и сбрасывает все откаты до начального значения с немедленными попытками переподключения. + +--- + +## Рукопожатие Hello + +При соединении инициирующий пир отправляет `dlt_hello_message`, содержащий: + +- `head_block_num` / `head_block_id` +- `lib_block_num` / `lib_block_id` +- `dlt_earliest_block_num` — старейший блок, доступный в скользящем DLT block log пира +- `node_status` — SYNC или FORWARD + +Принимающий пир отвечает `dlt_hello_reply_message`, содержащим: + +- `fork_alignment` — перекрываются ли блоки на одном форке +- `exchange_enabled` — считает ли отвечающий пир отправителя обновлённым + +### Проверка выравнивания форков (с учётом DLT-диапазона) + +Поскольку DLT-узлы обрезают старые блоки, наивное сравнение head-ID ложно помечало бы однофорковые пиры как "другой форк". Проверка многоуровневая: + +| Случай | Проверка | +|--------|---------| +| Пир не имеет блоков (`head_num == 0`) | Выровнен | +| Голова пира в нашем DLT-диапазоне | `is_block_known(peer.head_id)` | +| head пира + 1 == наш ранний блок | Читаем наш ранний блок, проверяем `previous == peer.head_id` | +| Запасной вариант | `is_block_known(peer.lib_id)` | + +--- + +## Режимы синхронизации + +Каждый узел в любой момент находится в одном из двух режимов: + +### SYNC-режим (вытягивание) + +Используется, когда узел отстаёт от сети. Узел запрашивает блоки диапазонами до **200 блоков** от пира: + +``` +мы пир + │──dlt_get_block_range──►│ + │◄──dlt_block_range_reply─│ + │ (до 200 блоков) │ + │──применить каждый блок──►chain│ + │ │ + │ (когда is_last=true) │ + │──переход в forward │ +``` + +**Обнаружение разрывов:** Если `our_head + 1 < peer.dlt_earliest` (недостающие блоки больше не в скользящем логе пира), узел ищет другой пир, способный устранить разрыв. Если ни один пир не может обслужить разрыв, рекомендуется импорт снимка. + +**Защита от стагнации:** Если в течение 30 секунд не получено ни одного блока, узел повторяет попытки до 3 раз, затем переходит в FORWARD-режим с предупреждением. + +### FORWARD-режим (проталкивание) + +Используется, когда узел обновлён. Блоки рассылаются через `dlt_block_message`. Каждый блок транслируется всем **exchange-enabled** пирам, разделяющим тот же форк. + +**Откат FORWARD → SYNC:** Если голова узла не продвигается **30 секунд** (`check_forward_stagnation`) и хотя бы один пир опережает, узел повторно входит в SYNC-режим. + +### Переходы SYNC ↔ FORWARD + +| Переход | Триггер | +|---------|--------| +| SYNC → FORWARD | Ответ диапазона блоков с `is_last=true` | +| SYNC → FORWARD | `check_sync_catchup()`: наша голова ≥ все пиры | +| SYNC → FORWARD | Стагнация после 3 попыток | +| FORWARD → SYNC | `check_forward_stagnation()`: голова застряла 30с и пир опережает | +| FORWARD → SYNC | Заполнение разрыва не удалось и нет доступного пира | + +При переходе SYNC → FORWARD узел транслирует `dlt_fork_status_message` со статусом `node_status=FORWARD` всем подключённым пирам, позволяя им переоценить `exchange_enabled` для этого узла. + +--- + +## Заполнение разрывов + +Заполнение разрывов — лёгкий механизм для получения небольшого числа конкретных блоков без входа в полный SYNC-режим. Использует два специальных типа сообщений (`dlt_gap_fill_request` / `dlt_gap_fill_reply`) и срабатывает в трёх местах: + +1. Когда прибывает блок вне порядка (`on_dlt_block_reply`) +2. Каждые 5 секунд из `periodic_task()` +3. После завершения паузы снимка (`resume_block_processing()`) + +**Правила:** +- Максимум **100 блоков на запрос** (`GAP_FILL_MAX_BLOCKS`); более крупные разрывы используют разбитые на части запросы. +- **5-секундное охлаждение** между запросами заполнения разрывов. +- Запрашивающий пир выбирает активный пир с наибольшим номером головного блока. +- Обслуживающий пир читает блоки из своего DLT block log; запросы вне диапазона лога отклоняются. +- Пиры жизненного цикла SYNCING являются допустимыми кандидатами (не только ACTIVE). +- Если подходящий пир не найден, узел немедленно переходит в SYNC-режим. + +--- + +## Mempool + +DLT P2P уровень поддерживает собственный mempool, отдельный от цепочечного `_pending_tx`. Это позволяет ранней фильтрации до отправки транзакций в chain evaluator. + +**Проверки при приёме:** +- Дубликат по `tx_id` — дедупликация при получении +- Истечение срока — отклонять уже истёкшие +- TaPoS (`tapos_block_num`) — отклонять если блок-ссылка неизвестен +- Размер — отклонять если `tx.size > dlt-mempool-max-tx-size` (по умолчанию 64 КБ) +- Горизонт истечения — отклонять если срок действия более чем `dlt-mempool-max-expiration-hours` (по умолчанию 24 ч) в будущем + +**Вытеснение:** Когда mempool превышает `dlt-mempool-max-tx` (по умолчанию 10 000) или `dlt-mempool-max-bytes` (по умолчанию 100 МБ), первым вытесняется запись с ближайшим сроком истечения. + +**Жизненный цикл:** +- Транзакции, полученные во время SYNC, помечаются как **provisional** и повторно проверяются при переходе в FORWARD (блоки TaPoS теперь могут быть известны). +- При применении блока включённые транзакции обрезаются (`remove_transactions_in_block`). +- При переключении форка записи с недействительным TaPoS обрезаются (`prune_mempool_on_fork_switch`). +- `periodic_mempool_cleanup()` удаляет истёкшие и TaPoS-недействительные записи в каждом цикле. + +--- + +## Разрешение форков + +DLT P2P уровень отслеживает состояние форка с **порогом 42 блока** (2 полных раунда валидаторов = `CHAIN_MAX_WITNESSES × 2`). + +`track_fork_state()` вызывается после каждого применения блока. При обнаружении конкурирующего форка, удерживаемого ≥ 42 блока, `resolve_fork()` вычисляет **самую весомую ветвь** по общему весу голосов. Ветвь-кандидат должна набрать **6 последовательных блоков подтверждения** (`dlt_fork_resolution_state::CONFIRMATION_BLOCKS`) до переключения узла на неё (гистерезис). + +Текущий статус форка предоставляется через `is_on_majority_fork()`, который плагин Validator использует для решения о производстве блоков. + +--- + +## Защита от спама + +Каждый пир имеет единый счётчик **`spam_strikes`**: + +- Увеличивается при: недействительном блоке, недействительной транзакции, нарушении протокола +- Сбрасывается при: любом допустимом пакете +- Порог мягкого бана: **10 страйков** + +Мягко забаненный пир получает `dlt_soft_ban_message` (содержащий `ban_duration_sec` и понятную человеку причину) до закрытия соединения. Забаненный пир входит в состояние BANNED на указанную длительность и не будет переподключаться до её истечения. + +**Дедупликация соединений по IP** предотвращает множественные соединения от одного узла: +- `accept_loop()` отклоняет входящие соединения от IP с существующей активной записью. +- `connect_to_peer()` пропускает исходящие соединения если целевой IP уже имеет активную запись. +- Трансляция (`send_to_all_our_fork_peers`) отслеживает `set` и пропускает IP, уже отправленные в этой трансляции. + +**Толерантность к дублирующимся / внеочерёдным блокам:** +- Уже применённые блоки тихо пропускаются (не считаются спамом). +- Внеочерёдные блоки в ответах диапазонов попадают в `fork_db` вместо запуска мягкого бана. +- Ошибки десериализации не увеличивают spam strikes. +- Слишком крупные сообщения от пиров старого протокола запускают отключение без увеличения откатного времени. + +--- + +## Обмен пирами + +Узлы обмениваются адресами пиров для помощи в обнаружении. + +**Ограничение скорости:** **3 запроса за 5-минутное окно** на пир. + +**Фильтры, применяемые перед разделением адреса пира:** +- Минимальное время работы: **600 секунд** +- Разнообразие подсетей: максимум **2 пира на /24**-подсеть +- Исключение эфемерных портов: пиры с `is_incoming` никогда не разделяются (их порт временный) + +**Лимиты на ответ:** `dlt-peer-exchange-max-per-reply` (по умолчанию 10). + +--- + +## Пауза/возобновление обработки блоков + +Плагин snapshot (и другие плагины, требующие эксклюзивного доступа) может приостановить приём P2P-блоков через `pause_block_processing()`. Во время паузы: + +- `periodic_task()` пропускает операции, требующие read lock базы данных: `sync_stagnation_check()`, `periodic_peer_exchange()`, `log_peer_stats()`. +- Таймеры зависшей синхронизации и форвард-стагнации сбрасываются, чтобы узел не входил в ненужные переходы режима. +- Продолжается ведение хозяйства без DB: переподключение, управление жизненным циклом, очистка mempool, разбанивание пиров. + +При `resume_block_processing()` узел сначала пытается заполнить разрыв, прежде чем откатиться к SYNC-режиму. + +--- + +## Конфигурация + +| Опция | По умолчанию | Описание | +|-------|--------------|----------| +| `p2p-endpoint` | `0.0.0.0:2001` | Адрес и порт прослушивания | +| `seed-node` | — | Адрес(а) статических сид-пиров | +| `p2p-max-connections` | — | Максимальное количество одновременных соединений пиров | +| `dlt-block-log-max-blocks` | 100000 | Ёмкость скользящего DLT block log | +| `dlt-peer-max-disconnect-hours` | 8 | Удалять не отвечающий пир через N часов | +| `dlt-mempool-max-tx` | 10000 | Жёсткий лимит на количество записей в mempool | +| `dlt-mempool-max-bytes` | 104857600 | Жёсткий лимит на общую память mempool (100 МБ) | +| `dlt-mempool-max-tx-size` | 65536 | Отклонять транзакции крупнее этого (64 КБ) | +| `dlt-mempool-max-expiration-hours` | 24 | Отклонять транзакции истекающие более чем через N часов | +| `dlt-peer-exchange-max-per-reply` | 10 | Максимум адресов на ответ обмена пирами | +| `dlt-peer-exchange-max-per-subnet` | 2 | Максимум пиров, разделяемых на /24-подсеть | +| `dlt-peer-exchange-min-uptime-sec` | 600 | Минимальное время работы пира перед разделением адреса | +| `dlt-stats-interval-sec` | 300 | Интервал между выводом статистики пиров в лог (мин. 30 с) | + +--- + +## Лог статистики пиров + +Каждые `dlt-stats-interval-sec` (по умолчанию 5 минут) узел выводит сводку статистики пиров: + +``` +[DLT-P2P] node=FORWARD head=#79274318 lib=#79274297 fork=MAJORITY + peer 192.168.1.10:2001 ACTIVE head=#79274318 exch=YES dlt=[79174319..79274318] strikes=0 + peer 192.168.1.11:2001 SYNCING head=#79274100 exch=no dlt=[79174319..79274100] strikes=0 + peer 192.168.1.12:2001 BANNED ban_remaining=3540s +``` + +Поля: +- `exch=YES/no` — включён ли обмен блоками/транзакциями с этим пиром +- `dlt=[min..max]` — диапазон DLT block log, который пир может обслуживать +- `strikes` — текущее количество spam strikes (сбрасывается при любом допустимом пакете) +- `ban_remaining` — секунды до истечения мягкого бана + +Интервал статистики может обновляться во время работы через `set_stats_log_interval()`. + +--- + +## Диагностическая сводка + +| Симптом | Вероятная причина | +|---------|-----------------| +| Узел застрял в SYNC, голова не продвигается | Разрыв между нашей головой и DLT-диапазоном пира — пир не может устранить; рассмотрите импорт снимка | +| Быстрые SYNC ↔ FORWARD колебания | Нет пира опережающего, или все пиры изолированы — проверьте записи журнала `emergency_peer_reset` | +| Все пиры показывают `exch=no` | Переход в FORWARD не уведомил пиров; должно самоисправиться в следующем цикле `broadcast_chain_status` | +| `spam_strikes` растёт на всех пирах | Вероятно расхождение форка — проверьте выравнивание форков в логах hello | +| `unlinked_size` растёт в fork_db | Родительские блоки не поступают; заполнение разрыва должно восстановиться в течение 5с | +| `peer_head_num` выглядит устаревшим в статистике | Ожидаемо — `peer_head_num` является снимком из последнего обмена hello/fork_status, не реального времени | + +--- + +См. также: [Сообщения](./messages.md), [Сценарии синхронизации](./sync-scenarios.md), [Форвард-режим](./forward-mode.md), [Справочник статистики](./stats-reference.md), [Снимок](../node/snapshot.md), [Разрешение форков](../consensus/fork-resolution.md). diff --git a/@l10n/ru/docs/p2p/stats-reference.md b/@l10n/ru/docs/p2p/stats-reference.md new file mode 100644 index 0000000000..85ac9d8c63 --- /dev/null +++ b/@l10n/ru/docs/p2p/stats-reference.md @@ -0,0 +1,233 @@ +# Справочник статистики P2P + +Уровень DLT P2P выводит две периодические строки журнала для мониторинга: + +| Префикс журнала | Интервал по умолчанию | Назначение | +|----------------|----------------------|-----------| +| `DLT Status \|` | ~30 с | Компактная однострочная запись для мониторинга через tail/grep | +| `=== DLT P2P Stats \|` | ~120 с (настраивается через `dlt-stats-interval-sec`) | Полная детализация по каждому пиру | + +--- + +## Компактная строка статуса + +``` +DLT Status | FORWARD | head=#79881136 lib=#79881130 | dlt_range=79000000-79881136 | peers=6active/8conn | uptime=2h15m43s | flags=... +``` + +| Поле | Пример | Значение | +|------|--------|---------| +| Режим | `FORWARD` | Рабочий режим узла (`SYNC` или `FORWARD`) | +| `head=#N` | `head=#79881136` | Номер текущего head блока | +| `lib=#N` | `lib=#79881130` | Номер последнего необратимого блока | +| `dlt_range=A-B` | `dlt_range=79000000-79881136` | Диапазон блоков в скользящем DLT block log | +| `peers=Xactive/Yconn` | `peers=6active/8conn` | Пиры с включённым обменом / всего TCP-соединений | +| `uptime` | `2h15m43s` | Время с момента запуска узла | +| `flags` | различные | Активные флаги (snapshot, paused, catchup и т.д.) | + +--- + +## Полная статистика — Строка заголовка + +``` +=== DLT P2P Stats | status=FWD fork=NORMAL head=79881136 lib=79881130 peers=6 conn=4 paused=no uptime=0h20m30s === +``` + +### `status` — Рабочий режим узла + +| Значение | Значение | +|----------|---------| +| `SYNC` | Догоняет — тянет блоки от пиров; не транслирует транзакции | +| `FWD` | Догнал — производит и ретранслирует блоки и транзакции в реальном времени | + +**Причины `SYNC`:** Узел только что запущен; отстал во время простоя; обнаружил minority fork и пересинхронизируется; head стагнировал более 30 секунд при наличии опережающего пира. + +**Причины `FWD`:** Узел догнал head сети; все блоки поступают через трансляцию в реальном времени. + +### `fork` — Статус форка + +| Значение | Значение | +|----------|---------| +| `NORMAL` | На мажоритарном форке — нет конфликта | +| `LOOKING` | Обнаружены конкурирующие tips; сравниваются ветви (порог: 42 блока = 2 полных раунда) | +| `MINORITY` | Подтверждено нахождение на minority fork; ожидается переключение | + +**Причины отличия от `NORMAL`:** Два валидатора произвели блок в одном слоте; сетевой раздел разделил валидаторов между tips; получен блок альтернативного форка. + +### `head` и `lib` + +- **`head`** — номер блока текущего tip цепочки +- **`lib`** — последний необратимый блок; блоки на этом уровне и ниже финализированы + +Разрыв head-to-lib обычно составляет 1–10 блоков в нормальной работе DLT. + +### `peers` и `conn` + +- **`peers`** — всего записей пиров в таблице (активные + подключающиеся + отключённые, отслеживаемые для переподключения) +- **`conn`** — текущие живые TCP-соединения + +Когда `peers` значительно превышает `conn`, узел имеет отключённые пиры в очередях ожидания. + +### `paused` + +| Значение | Значение | +|----------|---------| +| `no` | Обработка блоков активна | +| `YES` | Приём блоков временно остановлен (идёт экспорт снимка) | + +В режиме паузы P2P-соединения продолжают работать нормально. Полученные блоки ставятся в очередь в fork DB и применяются при возобновлении. Таймеры stale-sync и forward-stagnation сбрасываются, чтобы во время паузы не происходило ложных переходов режима. + +--- + +## Полная статистика — Строки по каждому пиру + +### Активный пир + +``` +62.109.17.82:2001 | ACTIVE | exch=YES | head=79881136 lib=79880729 | range=79869724-79880729 | peer_fork=NORM peer_node=FWD | spam=0 | +align+emrg+sync +``` + +#### Состояние жизненного цикла + +| Метка | Значение | +|-------|---------| +| `CONNECT` | TCP-подключение в процессе (таймаут 5 с) | +| `HANDSHAKE` | Обмен hello/hello-reply в процессе (таймаут 10 с) | +| `SYNCING` | Загружает диапазон блоков от этого пира | +| `ACTIVE` | Полностью работоспособен; обмен установлен | +| `DISC` | Отключён; переподключится после ожидания | +| `BANNED` | Мягко заблокирован; переподключения нет до истечения бана | + +#### `exch` — Статус обмена + +| Значение | Значение | +|----------|---------| +| `YES` | Обмен блоками и транзакциями включён; обе стороны на одном форке | +| `no` | Обмен отключён; выравнивание форка не подтверждено | + +**Причины `no`:** Рукопожатие только что завершено, выравнивание форка ещё не проверено; head/LIB пира не в нашем fork DB; пир сообщил о несовпадении форка; переход SYNC → FORWARD ещё не распространился. + +**Как обмен становится `YES`:** +1. Рукопожатие hello: acceptor вызывает `is_block_known(peer.head_block_id)` — при совпадении устанавливает `exchange_enabled=true` в ответе hello. +2. Принятие блока: когда блок от этого пира применяется к нашей цепочке, обмен включается. +3. Переход FORWARD: пир транслирует `dlt_fork_status_message` с `node_status=FORWARD`, вызывая повторную проверку выравнивания форка. + +#### `head` / `lib` (значения пира) + +Последние сообщённые пиром номера head и LIB блоков — **снимок** из последнего hello, сообщения fork_status или ретрансляции блока. Реальная цепочка пира может опережать эти значения, особенно в режиме FORWARD при быстром производстве блоков. + +#### `range` — Диапазон DLT block log + +`earliest-latest`: номера блоков, доступных в скользящем DLT block log пира. + +Если блоки, необходимые для gap fill или начальной синхронизации, ниже `earliest`, этот пир не может их обслуживать. Узел ищет другого пира, чей диапазон покрывает недостающие блоки. + +#### `peer_fork` — Самосообщаемый статус форка пира + +| Метка | Значение | +|-------|---------| +| `NORM` | Пир сообщает о нахождении на мажоритарном форке | +| `LOOK` | Пир разрешает конфликт форка | +| `MINO` | Пир сообщает о нахождении на minority fork | + +Пиры, сообщающие `MINO`, вероятно, в процессе переключения форков и могут скоро изменить свой head. Блок от них не следует считать каноническим. + +#### `peer_node` — Рабочий режим пира + +| Метка | Значение | +|-------|---------| +| `SYNC` | Пир догоняет; не будет транслировать транзакции | +| `FWD` | Пир догнал и ретранслирует блоки в реальном времени | + +#### `spam` — Счётчик ударов анти-спама + +Удары, накопленные с момента последнего корректного пакета. Мягкая блокировка срабатывает при **10 ударах**. Сбрасывается при любом корректном пакете, успешном переподключении или истечении бана. + +**Триггеры ударов:** Ошибка десериализации; нарушение протокола (неожиданное сообщение в текущем состоянии); блоки dead-fork (после льготного периода). + +**Примечание:** Дублирующиеся блоки и блоки не по порядку внутри range reply **не** увеличивают счётчик. + +#### Флаги + +| Флаг | Значение | +|------|---------| +| `+align` | Выравнивание форка подтверждено — блоки от этого пира применяются к нашей цепочке | +| `+emrg` | Пир сообщает об активном экстренном консенсусе | +| `+ekey` | Пир владеет приватным ключом экстренного комитета (кандидат в экстренные мастер-узлы) | +| `+sync` | Синхронизация диапазона блоков ожидает или выполняется с этим пиром | + +--- + +### Отключённый пир + +``` +138.201.117.201:2001 | DISC | disconnected=74s | backoff=480s | reconnect_in=502s | spam=0 +``` + +| Поле | Значение | +|------|---------| +| `disconnected` | Секунды с момента потери соединения | +| `backoff` | Текущий интервал переподключения; удваивается при каждой неудаче: 30 → 60 → 120 → … → 3600 с | +| `reconnect_in` | Секунды до следующей попытки переподключения | +| `spam` | Остаточный счётчик ударов из предыдущей сессии | + +Backoff сбрасывается до начального значения (30 с), когда соединение остаётся стабильным более 5 минут. + +--- + +### Заблокированный пир + +``` +1.2.3.4:2001 | BANNED | ban_remaining=1800s | reason=spam strike threshold exceeded +``` + +| Поле | Значение | +|------|---------| +| `ban_remaining` | Секунды до истечения бана (бан по умолчанию: 3600 с) | +| `reason` | Человекочитаемая причина бана, отправленная в `dlt_soft_ban_message` | + +После истечения запись возвращается в состояние DISCONNECTED и возобновляется обычное переподключение с backoff. + +--- + +## Интерпретация сценариев + +| Симптом | Вероятная причина | Действие | +|---------|------------------|---------| +| Все пиры `exch=no` | Рукопожатие только что завершено; несовпадение форка; узел в SYNC с нераспознанными пирами | Дождитесь перехода FORWARD для повторной оценки; проверьте статус `fork` | +| `status=SYNC` не продвигается | Разрыв до DLT-диапазона пира; нет доступного пира-мостика | Проверьте `range` на пирах; возможно, требуется импорт снимка | +| `peer_fork=MINO` на нескольких пирах | Раздел форка в масштабе сети | Наблюдайте; протокол сходится автоматически | +| Высокий `backoff` на отключённых пирах | Повторные сбои соединения; нестабильность сети | Проверьте подключение на порту 2001; высокий backoff ожидаем и сбрасывается при успехе | +| `paused=YES` неожиданно | Снимок завис или упал во время экспорта | Проверьте журналы плагина snapshot | +| `fork=LOOKING` не разрешается | Форк сохраняется > 42 блоков без чёткого большинства | Проверьте подключение валидаторов; проверьте цепочку на обоих tips | +| `spam` растёт на одном пире | Несовпадение протокола; пир на несовместимом форке | Автоматический бан при 10 ударах; проверьте версию ПО пира | +| Быстрые колебания SYNC ↔ FWD | Нет опережающего пира; все пиры на одном head | `emergency_peer_reset()` срабатывает через 60 с изоляции; также проверьте исправление P53 в журналах | + +--- + +## Константы протокола + +| Константа | Значение | Описание | +|-----------|---------|---------| +| `SPAM_STRIKE_THRESHOLD` | 10 | Удары до мягкой блокировки | +| `BAN_DURATION_SEC` | 3600 | Длительность мягкой блокировки по умолчанию (1 ч) | +| `INITIAL_RECONNECT_BACKOFF_SEC` | 30 | Первая задержка переподключения | +| `MAX_RECONNECT_BACKOFF_SEC` | 3600 | Максимальная задержка переподключения (1 ч) | +| `STABLE_CONNECTION_RESET_SEC` | 300 | Продолжительность соединения до сброса backoff (5 мин) | +| `PEER_EXCHANGE_MAX_REQUESTS` | 3 | Макс. запросов обмена пирами за скользящее окно | +| `PEER_EXCHANGE_WINDOW_SEC` | 300 | Окно ограничения скорости обмена пирами (5 мин) | +| `CONNECTING_TIMEOUT` | 5 с | Таймаут TCP-подключения | +| `HANDSHAKING_TIMEOUT` | 10 с | Таймаут обмена hello | +| `PEER_REMOVAL_HOURS` | 8 ч | Удалить неотвечающего пира через это время | +| `ISOLATION_RESET_SEC` | 60 | Секунды с нулём активных пиров до `emergency_peer_reset()` | +| `GAP_FILL_MAX_BLOCKS` | 100 | Макс. блоков за запрос gap fill | +| `GAP_FILL_COOLDOWN_SEC` | 5 | Минимальный интервал между запросами gap fill | +| `GAP_FILL_TIMEOUT_SEC` | 15 | Таймаут флага gap fill в процессе | +| `FORWARD_STAGNATION_SEC` | 30 | Порог непродвижения head в режиме FORWARD | +| `SYNC_STAGNATION_SEC` | 30 | Порог отсутствия блоков в режиме SYNC | +| `FORK_RESOLUTION_BLOCK_THRESHOLD` | 42 | Блоки до срабатывания разрешения форка (2 × CHAIN_MAX_WITNESSES) | +| `FORK_RESOLUTION_CONFIRMATION_BLOCKS` | 6 | Последовательных блоков для подтверждения разрешения форка | + +--- + +См. также: [Обзор P2P](./overview.md), [Сообщения](./messages.md), [Сценарии синхронизации](./sync-scenarios.md). diff --git a/@l10n/ru/docs/p2p/sync-scenarios.md b/@l10n/ru/docs/p2p/sync-scenarios.md new file mode 100644 index 0000000000..f076f6e365 --- /dev/null +++ b/@l10n/ru/docs/p2p/sync-scenarios.md @@ -0,0 +1,215 @@ +# Сценарии синхронизации P2P + +Эта страница описывает, как уровень DLT P2P обрабатывает типичные ситуации синхронизации: первый запуск, догон после простоя, разрывы DLT-диапазона, восстановление после форка и экстренный консенсус. + +--- + +## Классификация узлов + +В сценариях ниже используется эталонная конфигурация из 4 узлов: + +| Роль | Описание | +|------|---------| +| **Master** | Режим FORWARD; DLT block log `[A..B]`; держит экстренный приватный ключ | +| **Slave (NEAR)** | Head на `A-1` (точно примыкает к DLT-диапазону мастера) | +| **Slave (FAR)** | Head значительно ниже `A` (не в DLT-диапазоне мастера) | +| **Fresh node** | Нет блоков; только состояние genesis | + +--- + +## Сценарий 1: NEAR Slave (head примыкает к DLT-диапазону мастера) + +**Настройка:** DLT-диапазон мастера `[1000-2000]`. Head слейва = 999. + +### Рукопожатие hello + +1. Слейв отправляет hello: `head_num=999, head_id=H999`. +2. Проверка `check_fork_alignment` мастера — многоуровневая проверка: + - `head_num=999` ниже `dlt_earliest=1000` — не в диапазоне. + - `head_num + 1 == dlt_earliest (1000)` → **проверка граничной ссылки**: читает блок 1000, проверяет `block_1000.previous == H999`. + - Совпадение → `fork_alignment = true`, `exchange_enabled = true`. +3. Мастер отвечает: `exchange_enabled=true, fork_alignment=true`. +4. Слейв переходит в состояние жизненного цикла **SYNCING** на мастере. + +### Синхронизация блоков + +Слейв запрашивает `dlt_get_block_range(start=1000, end=1199, prev=H999)`. Мастер отвечает блоками 1000–1199 из своего DLT-журнала. Слейв применяет каждый блок. Это повторяется батчами по 200 блоков, пока слейв не достигнет блока 2000 и `is_last=true` не вызовет `transition_to_forward()`. + +**Результат:** Чистая P2P-синхронизация без загрузки снимка. Без штрафных задержек. + +--- + +## Сценарий 2: FAR Slave (head намного ниже DLT-диапазона мастера) + +**Настройка:** DLT-диапазон мастера `[1000-2000]`. Head слейва = 800. + +### Рукопожатие hello + +1. Слейв отправляет hello: `head_num=800, head_id=H800`. +2. Проверка выравнивания форка мастера: `800 < 1000`, проверка граничной ссылки не проходит (`800 + 1 ≠ 1000`), резервный вариант по LIB тоже не проходит (LIB ID обрезан). +3. `fork_alignment = false`, но `exchange_enabled = false`. +4. Мастер **не отключает** слейв, так как `hello.node_status == SYNC` — SYNC-пиры всегда переходят в состояние жизненного цикла ACTIVE. + +### Попытка синхронизации + +Слейв переходит в состояние жизненного цикла ACTIVE на мастере. Поскольку `exchange_enabled = false`, мастер не отправляет forward-блоки. Слейв пытается запросить диапазон блоков: `request_blocks_from_peer` обнаруживает `our_head+1 (801) < peer_dlt_earliest (1000)` — **разрыв обнаружен**. + +Узел ищет среди всех подключённых пиров того, чей DLT-диапазон покрывает блок 801. Если найден, этот пир используется как источник синхронизации-мостика. Если ни один пир не может перекрыть разрыв: + +``` +[P2P] Gap detected: our_head=800, nearest_peer_dlt_earliest=1000 + No peer can serve blocks 801-999. Snapshot import may be required. +``` + +Примерно через 90 секунд без прогресса head детектор застывшей синхронизации плагина snapshot срабатывает и инициирует загрузку снимка с доверенных пиров (настраивается через `trusted-peer-for-snapshot`). После импорта снимка на блоке 1500 слейв снова входит в режим SYNC и нормально догоняет сеть. + +--- + +## Сценарий 3: Fresh Node (нет блоков) + +**Настройка:** У узла нет блоков; `head_num=0, head_id=zero_id`. + +### Рукопожатие hello + +1. Новый узел отправляет hello: `head_num=0`. +2. Проверка форка мастера: `head_num == 0` → **пустой пир** → `fork_alignment = true` (обрабатывается как "новый узел, ещё не на каком-либо форке"). +3. `exchange_enabled = true` (мастер будет принимать блоки от этого узла). +4. Новый узел переходит в состояние жизненного цикла ACTIVE на мастере. + +### Попытка синхронизации + +В `request_blocks_from_peer`, `our_head=0` и `peer_dlt_latest=2000`. Однако `peer_dlt_earliest=1000`, поэтому самый ранний доступный блок — 1000. Запрос начинается с `max(our_head+1, peer_dlt_earliest) = 1000`. Узел получает блоки от 1000, но не может применить их, так как база данных цепочки не имеет состояния до блока 1000. + +Плагин snapshot обнаруживает застой и загружает снимок (например, на блоке 1500). После импорта новый узел нормально догоняет от блока 1500 → 2000. + +--- + +## Сценарий 4: Перезапуск после сбоя + +**Настройка:** Узел был на head 1912, DLT-диапазон `[1750-1912]`. После перезапуска пиры на блоке 2000. + +### Восстановление при запуске + +1. `database::open()` проверяет консистентность DLT block log: если head журнала совпадает с head базы данных → консистентен; иначе сбрасывает журнал. +2. Последние **100 блоков** из DLT block log засеваются в `fork_db` (блоки 1813–1912). Это даёт вновь поступающим блокам окно родителя в 100 блоков без необходимости их предварительной загрузки. +3. Применяется **60-секундный льготный период**: в первые 60 секунд после запуска блоки в пределах 10 от head обрабатываются как `FORK_DB_ONLY` вместо `DEAD_FORK`. Это предотвращает "каскад отклонений", когда пиры воспроизводят блоки вблизи head, о которых только что заполненный fork_db ещё не знает. + +### Догон + +Узел снова входит в режим SYNC и запрашивает блоки начиная с 1913. Пиры с DLT-диапазоном `[1800-2000]` могут обслуживать все нужные блоки. Узел догоняет до 2000 и переходит в FORWARD. + +--- + +## Сценарий 5: Переключение форка + +**Настройка:** Узел на head `H` на форке A. Пир имеет head форка B `H'`, где `H' > H` и форк B имеет больший вес голосов. + +### Обнаружение форка + +1. Блок с форка B поступает через трансляцию. Fork DB привязывает его к родительской цепочке. +2. `track_fork_state()` вызывается после каждого блока. Когда форк B удерживает преимущество на протяжении **42 блоков** (2 полных раунда валидаторов), запускается `resolve_fork()`. +3. `resolve_fork()` вычисляет суммарный вес голосов (делегированные SHARES) валидаторов на каждой ветви. Форк B должен удерживать 6 последовательных подтверждающих блоков до фиксации переключения. + +### Выполнение переключения форка + +1. `pop_block()` удаляет блоки форка A обратно до общего предка. Выброшенные транзакции попадают в `_popped_tx`. +2. Блоки форка B применяются от общего предка до нового head. +3. `_popped_tx` и `_pending_tx` повторно применяются; транзакции, уже в цепочке форка B, молча пропускаются. + +**Статус форка в статистике:** переходы `NORMAL → LOOKING → NORMAL` (или `MINORITY`, если этот узел на проигрывающей ветви). + +--- + +## Сценарий 6: Синхронизация при экстренном консенсусе + +**Настройка:** Сеть застала более 3600 секунд. Экстренный консенсус активен. + +### Работа мастера + +Экстренный мастер (узел с `emergency-private-key` в конфиге) производит все 21 блок в раунде, используя ключ подписи "committee". В статистике: `+emrg +ekey`. + +### Синхронизация слейва при экстренном режиме + +1. Слейв подключается к мастеру. Hello мастера включает `emergency_active=true, has_emergency_key=true`. +2. Проверка выравнивания форка слейва выполняется нормально — блоки committee являются обычными подписанными блоками с точки зрения P2P-уровня. +3. Слейв входит в режим SYNC и запрашивает блоки, произведённые committee, от мастера. +4. Валидация блоков: `verify_signing_witness()` смягчает проверку соответствия производитель-слот во время экстренного режима — если производитель блока не совпадает с точным запланированным слотом, он принимается, пока подпись проходит проверку против `signing_key` производителя. + +### Восстановление ключа валидатора + +Когда реальные валидаторы восстанавливают свои ключи подписи (через `validator_update_operation`), перестройка расписания включает их в гибридное расписание. Как только **15 из 21** слота валидаторов стали реальными (не committee), экстренный режим деактивируется. Последующие блоки производятся реальными валидаторами и синхронизируются нормально. + +--- + +## Сценарий 7: Восстановление застоя синхронизации + +**Условие:** Режим SYNC, блоки не получены в течение 30 секунд. + +1. Срабатывает `sync_stagnation_check()`: попытка 1 из 3 — повторный запрос блоков от всех активных пиров с включённым обменом. +2. Через 30 секунд: попытка 2 из 3. +3. Через 30 секунд: попытка 3 из 3. +4. После третьей попытки: `transition_to_forward()` с предупреждением о застое. + +Если узел всё ещё отставал при переходе в FORWARD, `check_forward_stagnation()` обнаружит отсутствие прогресса head через 30 секунд и вернётся в режим SYNC, начиная новый цикл. + +--- + +## Сценарий 8: Заполнение пробелов (Gap Fill) + +**Условие:** Режим FORWARD; 1–100 блоков отсутствуют в потоке блоков. + +Gap fill запускается автоматически при: +- Получении блока не по порядку (блок N+2 поступает до N+1). +- `periodic_task()` обнаруживает `highest_seen_block_num > our_head + 1`. +- Вызов `resume_block_processing()` после паузы снимка. + +**Протокол:** +1. Выбрать пир с наибольшим `peer_head_num` среди активных пиров. +2. Отправить `dlt_gap_fill_request(block_nums=[N+1, N+2, ...])` (макс. 100 блоков). +3. Ждать ответа до **15 секунд**. +4. При получении применить вернувшиеся блоки. Если блоки всё ещё отсутствуют, запустить ещё один gap fill на следующем периодическом цикле. + +**Если ни один пир не может обслужить пробел** (нет exchange-enabled или SYNCING-пира с более высоким head), узел немедленно переходит в режим SYNC. + +--- + +## Сценарий 9: Предотвращение осцилляции SYNC ↔ FORWARD + +**Корневая причина осцилляции:** После перехода FORWARD→SYNC таймер застоя синхронизации наследует устаревшую временную метку, немедленно срабатывает, и `check_sync_catchup` видит ноль пиров впереди → переходит обратно в FORWARD. Цикл продолжается. + +**Применённые исправления:** +- `transition_to_sync()` сбрасывает `_last_block_received_time` до `now`, чтобы таймеры застоя начинали заново. +- `check_forward_stagnation()` НЕ переходит в SYNC, когда все подключённые пиры имеют тот же head, что и наш узел — нет смысла синхронизироваться, когда никто не впереди. +- `check_sync_catchup()` НЕ заявляет "догнал", когда нет активных пиров; вместо этого запускается 60-секундный таймер изоляции. +- После 60 секунд изоляции `emergency_peer_reset()` очищает все мягкие баны и задержки, принуждая к немедленному переподключению ко всем известным пирам. + +--- + +## Сценарий 10: Блоки dead fork + +**Условие:** Пир отправляет блоки из цепочки, которая разошлась до окна fork DB узла. `push_block()` выбрасывает `unlinkable_block_exception`, и номер блока ≤ `head_block_num`. + +**Поведение:** +1. `dlt_delegate::accept_block()` возвращает `DEAD_FORK`. +2. Блок НЕ сохраняется в `fork_db._unlinked_index` (предотвращает рост памяти). +3. Пир накапливает spam-удар за каждый dead-fork блок. +4. После 10 ударов пир получает мягкий бан на 3600 с. +5. Цикл синхронизации прерывается — дальнейшие блоки от этого пира не обрабатываются в текущем батче. + +**Льготный период (исправление P22):** В первые 60 секунд после запуска узла блоки в пределах 10 от текущего head, дающие сбой с `unlinkable_block_exception`, возвращаются как `FORK_DB_ONLY` (не `DEAD_FORK`). Это предотвращает ложную блокировку легитимных пиров, отправляющих блоки вблизи head до полного заполнения fork_db из последних 100 блоков. + +--- + +## Конфигурация, влияющая на синхронизацию + +| Настройка | По умолчанию | Эффект | +|-----------|-------------|-------| +| `seed-node` | — | Статические пиры; переподключаются после `emergency_peer_reset()` | +| `dlt-block-log-max-blocks` | 100000 | Ёмкость DLT-журнала; влияет на то, как далеко назад пиры могут перекрывать пробелы | +| `trusted-peer-for-snapshot` | — | Пиры, от которых принимается загрузка снимка | +| `stalled-sync-timeout-minutes` | 2 | Минуты до запуска восстановления плагином snapshot | +| `enable-stale-production` | false | Разрешить валидатору производить блоки без синхронизации (только для разработки) | + +--- + +См. также: [Обзор P2P](./overview.md), [Режим Forward](./forward-mode.md), [Экстренный консенсус](../consensus/emergency-consensus.md), [Снимки](../node/snapshot.md). diff --git a/@l10n/ru/docs/plugins/chain.md b/@l10n/ru/docs/plugins/chain.md new file mode 100644 index 0000000000..84ed51bc2e --- /dev/null +++ b/@l10n/ru/docs/plugins/chain.md @@ -0,0 +1,125 @@ +# Плагин Chain + +Плагин chain — это ключевой компонент каждого узла VIZ Ledger. Он управляет разделяемой памятью базы данных chainbase, принимает и проверяет блоки и транзакции, поддерживает состояние fork database и block log, координирует запуск с плагинами snapshot и P2P. Все остальные плагины зависят от него. + +**Исходник:** [plugins/chain/plugin.cpp](../../plugins/chain/plugin.cpp) + +--- + +## Зависимости + +``` +json_rpc::plugin +``` + +Плагин chain должен быть первым инициализируемым доменным плагином; `json_rpc` — его единственная формальная зависимость и загружается первым фреймворком AppBase. + +--- + +## Конфигурация + +### Только CLI-флаги + +Это одноразовые операции восстановления или обслуживания; они заставляют узел выполнить конкретное действие при запуске и не могут быть заданы в `config.ini`. + +| Флаг | Описание | +|------|---------| +| `--replay-blockchain` | Очистить chainbase shared memory и воспроизвести полный block log с блока 1. | +| `--force-replay-blockchain` | То же, что `--replay-blockchain`, но пропускает проверку повреждений. Используется, когда block log цел, но chainbase нечитаем. | +| `--replay-from-snapshot ` | Восстановление после сбоя для DLT-узлов: очистить shared memory, импортировать снимок, затем воспроизвести скользящий DLT block log. См. [Плагин Snapshot](./snapshot.md). | +| `--snapshot-auto-latest` | С `--replay-from-snapshot`: автоматически обнаружить последний снимок в `snapshot-dir` вместо ручного указания пути. | +| `--auto-recover-from-snapshot` | По умолчанию `true`. Автоматически восстанавливаться во время работы при обнаружении повреждения shared memory во время обработки или генерации блоков, без перезапуска. Отключить через `--no-auto-recover-from-snapshot`. | +| `--resync-blockchain` | Очистить и shared memory, и block log; начать с genesis или из снимка. Деструктивно — использовать только при восстановлении после полной потери данных. | +| `--check-locks` | Проверить порядок блокировок (только для разработки). | +| `--validate-database-invariants` | Выполнять проверки консистентности базы данных на каждом блоке (очень медленно; только для разработки). | + +### Опции файла конфигурации + +#### Разделяемая память + +| Опция | По умолчанию | Описание | +|-------|-------------|---------| +| `shared-file-dir` | `state` | Каталог для файла разделяемой памяти (абсолютный путь или относительный к каталогу данных). | +| `shared-file-size` | `2G` | Начальный размер разделяемой памяти. Используйте `4G`–`16G` для производственных узлов в зависимости от возраста цепочки и количества объектов. | +| `inc-shared-file-size` | `2G` | Шаг роста при падении свободного места ниже минимального порога. | +| `min-free-shared-file-size` | `500M` | Автоматически расширяться, когда свободная разделяемая память падает ниже этого значения. | +| `block-num-check-free-size` | `1000` | Проверять свободное место каждые N блоков. | +| `flush-state-interval` | `10000` | Принудительный полный сброс на диск каждые N блоков. Более высокие значения улучшают пропускную способность ценой большего объёма данных для воспроизведения после нечистого завершения работы. | + +#### Block log и DLT + +| Опция | По умолчанию | Описание | +|-------|-------------|---------| +| `dlt-block-log-max-blocks` | `100000` | Количество последних блоков для хранения в скользящем DLT block log (`dlt_block_log.log`). Активно только в DLT-режиме (после импорта снимка). Установить в `0` для отключения. | +| `checkpoint` | — | Пары номер-блока/ID-блока, которые должны совпадать при воспроизведении; можно указывать несколько раз. | + +#### Производительность + +| Опция | По умолчанию | Описание | +|-------|-------------|---------| +| `single-write-thread` | `false` | Маршрутизировать все операции записи через выделенный поток io_service. Улучшает консистентность при высоком параллелизме; незначительная потеря пропускной способности. | +| `skip-virtual-ops` | `false` | Пропустить обработку виртуальных операций. Уменьшает использование памяти; ломает плагины, индексирующие виртуальные операции (`account_history`, `operation_history`). | +| `enable-plugins-on-push-transaction` | `false` | Уведомлять плагины-наблюдатели, когда транзакции поступают в пул ожидания (до применения блока). | +| `read-wait-micro` | *(по умолч. db)* | Таймаут блокировки чтения в микросекундах. | +| `max-read-wait-retries` | *(по умолч. db)* | Количество попыток до того, как таймаут блокировки чтения становится фатальным. | +| `write-wait-micro` | *(по умолч. db)* | Таймаут блокировки записи в микросекундах. | +| `max-write-wait-retries` | *(по умолч. db)* | Количество попыток до того, как таймаут блокировки записи становится фатальным. | + +--- + +## Последовательность запуска + +``` +plugin_initialize() ← разобрать CLI и опции конфигурации; проверить путь к снимку +plugin_startup() ← открыть или создать базу данных + ├─ --resync → очистить shared memory + block log; инициализировать genesis + ├─ --replay → очистить shared memory; воспроизвести из block log + ├─ --snapshot → импортировать снимок; запустить DLT-режим + ├─ --replay-from-snapshot → импортировать снимок; воспроизвести dlt_block_log + └─ нормальный перезапуск → открыть существующую shared memory; воспроизвести при несовпадении ревизии +emit on_sync() ← активируются плагины P2P и валидатора +``` + +Вся загрузка снимков происходит внутри `plugin_startup()`, до того как P2P или валидатор видят базу данных. + +--- + +## Принятие блоков + +`chain::plugin::accept_block()` является точкой входа для всех входящих блоков (от P2P и от валидатора). Он: + +1. Проверяет, что временная метка блока не слишком далека в будущем. +2. Под блокировкой записи вызывает `database::push_block()`. +3. Обновляет fork database и block log. +4. Испускает сигнал `applied_block` всем плагинам-подписчикам. +5. При `shared_memory_corruption_exception` вызывает `attempt_auto_recovery()`, если автовосстановление включено. + +Принятие транзакций (`accept_transaction()`) следует тому же пути через `database::push_transaction()`. + +--- + +## Разделяемая память + +База данных chainbase живёт в одном файле с отображением в память (`shared_memory.bin`) в `shared-file-dir`. Ключевые рекомендации по размеру: + +- Начните с `shared-file-size = 4G` для узла, загружающегося из недавнего снимка. +- База данных автоматически увеличивается на `inc-shared-file-size`, когда свободное место падает ниже `min-free-shared-file-size`. +- После чистого завершения работы файл уменьшается до фактически используемого размера. +- После сбоя запустите с `--replay-blockchain` или `--replay-from-snapshot` для восстановления консистентного состояния. + +--- + +## Устранение неполадок + +| Симптом | Действие | +|---------|---------| +| `FC_ASSERT` или `database_revision_exception` при запуске | Несовпадение ревизии — запустить `--replay-blockchain` | +| Открытие chainbase завершается ошибкой повреждения | Запустить `--replay-from-snapshot --snapshot-auto-latest` (DLT-узлы) или `--replay-blockchain` (полные узлы) | +| Узел застрял на genesis после `--resync-blockchain` | Block log также был очищен; предоставьте `--snapshot` для загрузки состояния из снимка | +| Разделяемая память растёт без ограничений | Проверить настройки `inc-shared-file-size` и `min-free-shared-file-size`; убедиться, что цепочка применяет блоки нормально | +| Ошибки `write lock timeout` | Другой процесс удерживает блокировку записи; проверить наличие устаревших процессов `vizd` | +| Автовосстановление срабатывает неоднократно | Возможные аппаратные неисправности хранилища; проверить здоровье диска; также убедиться, что `snapshot-every-n-blocks` настроен, чтобы существовали свежие снимки | + +--- + +См. также: [Плагин Snapshot](./snapshot.md), [Плагин валидатора](./validator.md), [Обзор P2P](../p2p/overview.md), [Обработка блоков](../consensus/block-processing.md). diff --git a/@l10n/ru/docs/plugins/database-api.md b/@l10n/ru/docs/plugins/database-api.md new file mode 100644 index 0000000000..61c10cbf6b --- /dev/null +++ b/@l10n/ru/docs/plugins/database-api.md @@ -0,0 +1,408 @@ +# Database API + +Плагин `database_api` предоставляет JSON-RPC методы только для чтения, позволяющие запрашивать состояние блокчейна: блоки, аккаунты, свойства цепочки, делегирование, проверку авторизации и управление. + +**Исходник:** [plugins/database_api/](../../plugins/database_api/) + +--- + +## Зависимости + +``` +json_rpc::plugin, chain::plugin +``` + +--- + +## Формат запроса + +Все методы используют JSON-RPC 2.0 через HTTP POST или WebSocket: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "method": "database_api.", + "params": [, , ...] +} +``` + +--- + +## Блоки и транзакции + +### `get_block_header(block_num)` + +Возвращает заголовок блока для указанного номера блока или `null`, если не найден. + +```json +{ "method": "database_api.get_block_header", "params": [12345678] } +``` + +**Возвращает:** `block_header` — `previous`, `timestamp`, `witness`, `transaction_merkle_root`, `extensions`. + +--- + +### `get_block(block_num)` + +Возвращает полный подписанный блок, включая все транзакции. + +```json +{ "method": "database_api.get_block", "params": [12345678] } +``` + +**Возвращает:** `signed_block` — все поля заголовка плюс `transactions[]` с полными данными операций, а также `block_id`, `signing_key`, `transaction_ids[]`. + +--- + +### `get_irreversible_block_header(block_num)` + +То же, что `get_block_header`, но возвращает блок только если он достиг LIB (необратим). + +--- + +### `get_irreversible_block(block_num)` + +То же, что `get_block`, но возвращает блок только если он достиг LIB. + +--- + +### `set_block_applied_callback(callback)` + +Зарегистрировать WebSocket-колбек для уведомления о каждом применённом блоке. Колбек получает заголовок блока в виде JSON-варианта. + +**Только WebSocket.** Отписаться через `cancel_all_subscriptions`. + +--- + +## Глобальные параметры цепочки + +### `get_config()` + +Возвращает константы времени компиляции: chain ID, символы токенов, интервал блоков, максимальный размер блока, периоды голосования и все константы `CHAIN_*`. + +```json +{ "method": "database_api.get_config", "params": [] } +``` + +--- + +### `get_dynamic_global_properties()` + +Возвращает текущее состояние цепочки: номер и ID текущего head блока, время, текущий валидатор, суммарные vesting shares, уровень участия, балансы фондов DPO и прочее. + +```json +{ "method": "database_api.get_dynamic_global_properties", "params": [] } +``` + +Ключевые поля: `head_block_number`, `head_block_id`, `time`, `current_witness`, `total_vesting_shares`, `total_vesting_fund_viz`, `committee_fund`, `last_irreversible_block_num`, `participation_count`. + +--- + +### `get_chain_properties()` + +Возвращает текущие параметры управления on-chain (задаются через `chain_properties_update_operation`): минимальную комиссию за создание аккаунта, максимальный размер блока, комиссию за создание аккаунта в VIZ, процент резерва пропускной способности и параметры вознаграждений. + +```json +{ "method": "database_api.get_chain_properties", "params": [] } +``` + +--- + +### `get_hardfork_version()` + +Возвращает строку версии текущего активного харфорка (например, `"1.0.0"`). + +```json +{ "method": "database_api.get_hardfork_version", "params": [] } +``` + +--- + +### `get_next_scheduled_hardfork()` + +Возвращает версию и запланированное время активации следующего ожидающего харфорка. + +```json +{ "method": "database_api.get_next_scheduled_hardfork", "params": [] } +``` + +**Возвращает:** `{ "hf_version": "1.0.0", "live_time": "2025-01-01T00:00:00" }` + +--- + +## Аккаунты + +### `get_accounts(account_names)` + +Возвращает полные объекты аккаунтов для заданного списка имён аккаунтов. + +```json +{ "method": "database_api.get_accounts", "params": [["alice", "bob"]] } +``` + +**Возвращает:** Массив `account_api_object` — имя, баланс, vesting shares, полученные vesting, делегированные vesting, ключи, аккаунт восстановления, дата создания, количество постов, голосовая сила и прочее. + +--- + +### `lookup_account_names(account_names)` + +То же, что `get_accounts`, но возвращает `null` для аккаунтов, которые не существуют. + +```json +{ "method": "database_api.lookup_account_names", "params": [["alice", "nonexistent"]] } +``` + +**Возвращает:** Массив `optional` — `null` для отсутствующих аккаунтов. + +--- + +### `lookup_accounts(lower_bound_name, limit)` + +Возвращает набор имён аккаунтов начиная с `lower_bound_name`, до `limit` результатов (макс. 1000). Полезно для постраничного перечисления аккаунтов. + +```json +{ "method": "database_api.lookup_accounts", "params": ["alice", 100] } +``` + +**Возвращает:** Набор строк с именами аккаунтов. + +--- + +### `get_account_count()` + +Возвращает общее количество зарегистрированных аккаунтов. + +```json +{ "method": "database_api.get_account_count", "params": [] } +``` + +--- + +## Состояние аккаунта + +### `get_master_history(account)` + +Возвращает историю изменений master authority для указанного аккаунта. + +```json +{ "method": "database_api.get_master_history", "params": ["alice"] } +``` + +**Возвращает:** Массив `master_authority_history_api_object` — `account`, `previous_master_authority`, `last_valid_time`. + +--- + +### `get_recovery_request(account)` + +Возвращает ожидающий запрос восстановления аккаунта для указанного аккаунта, если он есть. + +```json +{ "method": "database_api.get_recovery_request", "params": ["alice"] } +``` + +**Возвращает:** `optional` — `account_to_recover`, `new_master_authority`, `expires`. + +--- + +### `get_escrow(from, escrow_id)` + +Возвращает объект эскроу-перевода для указанного отправителя и ID эскроу. + +```json +{ "method": "database_api.get_escrow", "params": ["alice", 1] } +``` + +**Возвращает:** `optional` — все поля эскроу, включая `from`, `to`, `agent`, `ratification_deadline`, `escrow_expiration`, суммы и статус подтверждения. + +--- + +### `get_withdraw_routes(account, type)` + +Возвращает маршруты вывода vesting power для аккаунта. `type` — одно из `"incoming"`, `"outgoing"` или `"all"`. + +```json +{ "method": "database_api.get_withdraw_routes", "params": ["alice", "outgoing"] } +``` + +**Возвращает:** Массив `{ "from_account", "to_account", "percent", "auto_vest" }`. + +--- + +### `get_vesting_delegations(account, from, limit, type)` + +Возвращает vesting-делегирования для аккаунта. `type` — `"delegated"` (делегирования этого аккаунта) или `"received"` (полученные делегирования). + +```json +{ "method": "database_api.get_vesting_delegations", "params": ["alice", "", 100, "delegated"] } +``` + +**Возвращает:** Массив `vesting_delegation_api_object` — `delegator`, `delegatee`, `vesting_shares`, `min_delegation_time`. + +--- + +### `get_expiring_vesting_delegations(account, from, limit)` + +Возвращает записи об истечении vesting-делегирований для аккаунта — делегирования, которые были отозваны и ожидают возврата. + +```json +{ "method": "database_api.get_expiring_vesting_delegations", "params": ["alice", "1970-01-01T00:00:00", 100] } +``` + +**Возвращает:** Массив `vesting_delegation_expiration_api_object` — `delegator`, `vesting_shares`, `expiration`. + +--- + +## Авторизация и проверка транзакций + +### `get_transaction_hex(trx)` + +Возвращает hex-encoded сериализованную двоичную форму транзакции. Полезно для подписи и трансляции. + +```json +{ "method": "database_api.get_transaction_hex", "params": [{ ...объект транзакции... }] } +``` + +**Возвращает:** Hex-строку. + +--- + +### `get_required_signatures(trx, available_keys)` + +По частично подписанной транзакции и набору доступных подписывающему публичных ключей возвращает минимальное подмножество ключей, которые должны подписать для авторизации транзакции. + +```json +{ + "method": "database_api.get_required_signatures", + "params": [{ ...trx... }, ["VIZ5...", "VIZ6..."]] +} +``` + +**Возвращает:** Набор строк публичных ключей. + +--- + +### `get_potential_signatures(trx)` + +Возвращает все публичные ключи, которые потенциально могут подписать транзакцию (по всем задействованным аккаунтам и уровням authority). Используйте это для предварительной фильтрации набора ключей кошелька перед вызовом `get_required_signatures`. + +```json +{ "method": "database_api.get_potential_signatures", "params": [{ ...trx... }] } +``` + +**Возвращает:** Набор строк публичных ключей. + +--- + +### `verify_authority(trx)` + +Возвращает `true`, если транзакция имеет все необходимые подписи; иначе выбрасывает ошибку с описанием того, чего не хватает. + +```json +{ "method": "database_api.verify_authority", "params": [{ ...signed_trx... }] } +``` + +--- + +### `verify_account_authority(name, signers)` + +Возвращает `true`, если заданный набор публичных ключей имеет достаточные полномочия для действий от имени `name`. + +```json +{ "method": "database_api.verify_account_authority", "params": ["alice", ["VIZ5..."]] } +``` + +--- + +## Информация о базе данных + +### `get_database_info()` + +Возвращает статистику использования разделяемой памяти chainbase. + +```json +{ "method": "database_api.get_database_info", "params": [] } +``` + +**Возвращает:** +```json +{ + "total_size": 4294967296, + "free_size": 1073741824, + "reserved_size": 0, + "used_size": 3221225472, + "index_list": [ + { "name": "account_object", "record_count": 52341 }, + ... + ] +} +``` + +--- + +## Управление + +### `get_proposed_transactions(account, from, limit)` + +Возвращает предложения по управлению, требующие подтверждения от `account`. + +```json +{ "method": "database_api.get_proposed_transactions", "params": ["alice", 0, 100] } +``` + +**Возвращает:** Массив `proposal_api_object` — полные детали предложения, включая необходимые подтверждения, срок действия и список операций. + +--- + +## Рынок аккаунтов + +### `get_accounts_on_sale(from, limit)` + +Возвращает аккаунты, выставленные на продажу (прямая продажа, не аукцион). + +```json +{ "method": "database_api.get_accounts_on_sale", "params": [0, 100] } +``` + +**Возвращает:** Массив `account_on_sale_api_object` — `account`, `account_seller`, `account_offer_price`, `account_on_sale_start_time`, `target_buyer`. + +--- + +### `get_accounts_on_auction(from, limit)` + +Возвращает аккаунты, выставленные на аукцион. + +```json +{ "method": "database_api.get_accounts_on_auction", "params": [0, 100] } +``` + +**Возвращает:** Массив `account_on_sale_api_object` — то же плюс `current_bid`, `current_bidder`, `current_bidder_key`, `last_bid`. + +--- + +### `get_subaccounts_on_sale(from, limit)` + +Возвращает регистрации пространства имён аккаунтов, доступные для продажи (права на создание субаккаунтов). + +```json +{ "method": "database_api.get_subaccounts_on_sale", "params": [0, 100] } +``` + +**Возвращает:** Массив `subaccount_on_sale_api_object` — `account`, `subaccount_seller`, `subaccount_offer_price`. + +--- + +## Коды ошибок + +| Код | Значение | +|-----|---------| +| `-32700` | Ошибка разбора — недействительный JSON | +| `-32600` | Недействительный запрос — отсутствуют обязательные поля | +| `-32601` | Метод не найден | +| `-32602` | Недействительные параметры | +| `-32603` | Внутренняя ошибка | +| `-32099` – `-32000` | Ошибка сервера (исключение из обработчика метода) | + +--- + +См. также: [Обзор плагинов](./overview.md), [Методы witness_api](./overview.md#witness_api), [JSON-RPC API](../api/json-rpc.md). diff --git a/@l10n/ru/docs/plugins/overview.md b/@l10n/ru/docs/plugins/overview.md new file mode 100644 index 0000000000..c6a236ef06 --- /dev/null +++ b/@l10n/ru/docs/plugins/overview.md @@ -0,0 +1,379 @@ +# Обзор плагинов + +VIZ Ledger использует фреймворк плагинов **AppBase**. Каждый плагин имеет жизненный цикл (`plugin_initialize` → `plugin_startup` → `plugin_shutdown`), может регистрировать методы JSON-RPC, может хранить данные в базе данных chainbase и подписываться на сигналы цепочки (например, `applied_block`). + +--- + +## Категории плагинов + +| Категория | Описание | +|---------|---------| +| **Основные** | Необходимы для любого узла | +| **Инфраструктурные** | Сетевое взаимодействие, веб-сервер, снапшоты | +| **API** | Предоставляют JSON-RPC эндпоинты для клиентов | +| **Индексирование** | Индексируют данные цепочки в chainbase для быстрых запросов | +| **Производство** | Подписание и производство блоков | +| **Внешние** | Интеграция с внешними системами (MongoDB) | +| **Отладка/Тестирование** | Только для разработки; не для продакшна | + +--- + +## Инвентарь плагинов + +### Основные + +| Плагин | Статус | Зависимости | JSON-RPC | +|--------|--------|------------|---------| +| `chain` | Обязательный | `json_rpc` | — | +| `json_rpc` | Обязательный | — | — | + +### Инфраструктурные + +| Плагин | Статус | Зависимости | JSON-RPC | +|--------|--------|------------|---------| +| `webserver` | Нужен для API | `json_rpc` | — | +| `p2p` | Нужен для сети | `chain` | — | +| `snapshot` | Рекомендуется | `chain` | — | +| `witness_guard` | Рекомендуется для валидаторов | `chain`, `p2p` | — | + +### API + +| Плагин | Статус | Зависимости | JSON-RPC | +|--------|--------|------------|---------| +| `database_api` | Активен | `json_rpc`, `chain` | Да | +| `network_broadcast_api` | Активен | `json_rpc`, `chain`, `p2p` | Да | +| `witness_api` | Активен | `json_rpc`, `chain` | Да | +| `account_by_key` | Активен | `json_rpc`, `chain` | Да | +| `account_history` | Активен | `json_rpc`, `chain`, `operation_history` | Да | +| `operation_history` | Активен | `json_rpc`, `chain` | Да | +| `committee_api` | Активен | `json_rpc`, `chain` | Да | +| `invite_api` | Активен | `json_rpc`, `chain` | Да | +| `paid_subscription_api` | Активен | `json_rpc`, `chain` | Да | +| `custom_protocol_api` | Активен | `json_rpc`, `chain` | Да | +| `auth_util` | Активен | `json_rpc`, `chain` | Да | +| `block_info` | Активен | `json_rpc`, `chain` | Да | +| `raw_block` | Активен | `json_rpc`, `chain` | Да | +| `follow` | Устарел | `json_rpc`, `chain` | Да | +| `tags` | Устарел | `json_rpc`, `chain`, `follow` | Да | +| `social_network` | Устарел | `json_rpc`, `chain` | Да | +| `private_message` | Устарел | `json_rpc`, `chain` | Да | + +### Производство + +| Плагин | Статус | Зависимости | JSON-RPC | +|--------|--------|------------|---------| +| `validator` | Активен | `chain`, `p2p` | — | + +### Внешние + +| Плагин | Статус | Зависимости | JSON-RPC | +|--------|--------|------------|---------| +| `mongo_db` | Активен | `chain` | — | + +### Отладка / Тестирование + +| Плагин | Статус | Зависимости | JSON-RPC | +|--------|--------|------------|---------| +| `debug_node` | Только для разработки | `chain` | Да | +| `test_api` | Только для тестов | `json_rpc` | — | + +--- + +## Основные плагины + +### `chain` + +Управляет базой данных chainbase, применяет блоки и транзакции, и генерирует сигналы для всех остальных плагинов. + +**Ключевые параметры конфигурации:** + +| Параметр | По умолчанию | Описание | +|---------|------------|---------| +| `shared-file-size` | `2G` | Начальный размер файла разделяемой памяти | +| `shared-file-dir` | `state` | Каталог для файлов разделяемой памяти | +| `inc-shared-file-size` | `2G` | Шаг роста при нехватке места | +| `min-free-shared-file-size` | `500M` | Порог автоматического расширения | +| `flush-state-interval` | `10000` | Принудительный сброс на диск каждые N блоков | +| `skip-virtual-ops` | `false` | Пропускать виртуальные операции (уменьшает память) | +| `dlt-block-log-max-blocks` | `100000` | Ёмкость скользящего DLT-лога блоков | + +**Ключевые флаги CLI:** + +| Флаг | Описание | +|------|---------| +| `--replay-blockchain` | Очистить chainbase и повторить из лога блоков | +| `--force-replay-blockchain` | То же, но игнорирует проверку корruptor | +| `--replay-from-snapshot` | Импортировать снапшот, затем повторить DLT-лог блоков (восстановление после сбоя) | +| `--auto-recover-from-snapshot` | Автоматическое восстановление при повреждении разделяемой памяти | +| `--resync-blockchain` | Очистить chainbase и лог блоков; начать с генезиса или снапшота | + +--- + +### `json_rpc` + +Фреймворк-плагин; без конфигурации. Регистрирует и диспетчеризирует все методы JSON-RPC. Должен загружаться первым. + +Все JSON-RPC запросы используют формат 2.0: +```json +{ + "jsonrpc": "2.0", + "method": "api_name.method_name", + "params": {}, + "id": 1 +} +``` + +--- + +## Инфраструктурные плагины + +### `webserver` + +HTTP и WebSocket сервер, перенаправляющий запросы к `json_rpc`. Включает кэш ответов только для чтения. + +**Ключевые параметры конфигурации:** + +| Параметр | По умолчанию | Описание | +|---------|------------|---------| +| `webserver-http-endpoint` | — | Адрес прослушивания HTTP (например, `0.0.0.0:8090`) | +| `webserver-ws-endpoint` | — | Адрес прослушивания WebSocket (например, `0.0.0.0:8091`) | +| `webserver-thread-pool-size` | `32` | Рабочие потоки для обработки HTTP/WS | +| `webserver-cache-enabled` | `true` | Включить кэширование ответов | +| `webserver-cache-size` | `10000` | Максимальное количество кэшированных записей | + +Ключи кэша формируются из `method + params` (не `id`), предотвращая обход путём ротации `id` запроса. Мутирующие методы (`network_broadcast_api.*`, `debug_node.*`) никогда не кэшируются. Кэш очищается при каждом новом применённом блоке. + +Подробности — в разделе [Веб-сервер](./webserver.md). + +--- + +### `p2p` + +DLT P2P-сетевое взаимодействие — распространение блоков и транзакций, управление пирами, восстановление после minority fork. + +**Ключевые параметры конфигурации:** + +| Параметр | По умолчанию | Описание | +|---------|------------|---------| +| `p2p-endpoint` | — | Адрес прослушивания (например, `0.0.0.0:2001`) | +| `seed-node` | — | Статические начальные пиры | +| `p2p-max-connections` | — | Максимальное число одновременных соединений | +| `dlt-block-log-max-blocks` | `100000` | Ёмкость скользящего DLT-лога | +| `dlt-stats-interval-sec` | `300` | Интервал логирования статистики пиров | + +Полная архитектура P2P — в разделе [Обзор P2P](../p2p/overview.md). + +--- + +### `snapshot` + +Создание снапшотов, загрузка и P2P-синхронизация снапшотов для быстрой начальной загрузки и восстановления после сбоев. + +Подробности — в разделах [Снапшот](../node/snapshot.md) и [Плагин: Снапшот](./snapshot.md). + +--- + +## API-плагины + +### `database_api` + +Основной API чтения. Запросы блоков, транзакций, аккаунтов, состояния цепочки, версии хардфорка, делегирований, предложений. + +Полный справочник методов — в разделе [Database API](./database-api.md). + +--- + +### `network_broadcast_api` + +Отправка и трансляция подписанных транзакций и блоков. + +| Метод | Описание | +|-------|---------| +| `broadcast_transaction` | Отправить транзакцию (асинхронно) | +| `broadcast_transaction_synchronous` | Отправить и ждать включения в блок | +| `broadcast_transaction_with_callback` | Отправить с обратным вызовом при включении или истечении | +| `broadcast_block` | Отправить подписанный блок (только валидаторы) | + +--- + +### `witness_api` + +Запросы состояния валидаторов: активный набор, расписание, отдельные валидаторы, рейтинги голосов. + +| Метод | Описание | +|-------|---------| +| `get_active_witnesses` | Текущий активный набор из 21 валидатора | +| `get_witness_schedule` | Полный объект расписания | +| `get_witnesses` | Валидаторы по ID в базе данных | +| `get_witness_by_account` | Один валидатор по имени аккаунта | +| `get_witnesses_by_vote` | Валидаторы, отсортированные по суммарному весу голосов | +| `get_witnesses_by_counted_vote` | Валидаторы по числу голосов | +| `get_witness_count` | Общее количество зарегистрированных валидаторов | +| `lookup_witness_accounts` | Список имён аккаунтов валидаторов по префиксу | + +--- + +### `account_by_key` + +Обратный поиск аккаунтов по публичному ключу. + +| Метод | Описание | +|-------|---------| +| `get_key_references` | Получить имена аккаунтов, использующих данные публичные ключи | + +--- + +### `account_history` + +История операций аккаунта с постраничным выводом. + +| Метод | Описание | +|-------|---------| +| `get_account_history(account, from, limit)` | Получить операции; `from=-1` возвращает самые новые; максимум 1000 за вызов | + +**Параметры конфигурации:** +- `track-account-range` — диапазон имён аккаунтов для индексирования (по умолчанию: все аккаунты) +- `history-count-blocks` — сохранять историю за N блоков + +--- + +### `operation_history` + +Индекс всех операций для запросов на уровне блока и транзакции. + +| Метод | Описание | +|-------|---------| +| `get_ops_in_block(block_num, virtual_ops)` | Операции в блоке; `virtual_ops=true` включает виртуальные | +| `get_transaction(tx_id)` | Транзакция по ID | + +**Параметры конфигурации:** +- `history-whitelist-ops` / `history-blacklist-ops` — фильтрация хранимых типов операций +- `history-start-block` — начать индексирование с этого номера блока +- `history-count-blocks` — сохранять историю за N блоков + +--- + +### `committee_api` + +Запросы заявок комитета и голосований. + +| Метод | Описание | +|-------|---------| +| `get_committee_request(id)` | Заявка по ID | +| `get_committee_request_votes(id)` | Голоса по заявке | +| `get_committee_requests_list(from, limit, status)` | Постраничный список заявок | + +--- + +### `invite_api` + +Запросы активных кодов инвайтов. + +| Метод | Описание | +|-------|---------| +| `get_invites_list` | Все ID инвайтов | +| `get_invite_by_id(id)` | Инвайт по ID в базе данных | +| `get_invite_by_key(pub_key)` | Инвайт по публичному ключу | + +--- + +### `paid_subscription_api` + +Запросы предложений подписок и статуса подписчиков. + +| Метод | Описание | +|-------|---------| +| `get_paid_subscriptions` | Все активные предложения подписок | +| `get_paid_subscription_options(account)` | Конфигурация подписок для аккаунта | +| `get_paid_subscription_status(subscriber, account)` | Статус конкретной подписки | +| `get_active_paid_subscriptions(subscriber)` | Активные подписки для подписчика | +| `get_inactive_paid_subscriptions(subscriber)` | Истёкшие подписки | + +--- + +### Устаревшие API-плагины + +| Плагин | Методы | Примечание | +|--------|--------|----------| +| `follow` | Подписчики/подписки, ленты, блоги, репосты | Работает, но не рекомендуется для новых интеграций | +| `tags` | Трендовый/горячий/новый контент по тегу | Работает, но не рекомендуется для новых интеграций | +| `social_network` | Контент, голоса, ответы | Обёртка над запросами комитета/инвайтов; работает | +| `private_message` | Входящие/исходящие для зашифрованных сообщений | На основе `custom_operation`; работает | + +--- + +## Плагин производства + +### `validator` + +Подписание и производство блоков. Запускает цикл таймера 250 мс; производит, когда следующий слот назначен настроенному аккаунту валидатора. + +**Ключевые параметры конфигурации:** + +| Параметр | По умолчанию | Описание | +|---------|------------|---------| +| `validator` | — | Имя/имена аккаунтов валидаторов | +| `private-key` | — | WIF-ключ(и) для подписи | +| `emergency-private-key` | — | Ключ подписи для экстренного консенсуса | +| `enable-stale-production` | `false` | Производить, даже если цепочка устарела (только для тестовой сети) | +| `required-participation` | `3300` | Минимальное участие в базисных пунктах (3300 = 33%) | +| `fork-collision-timeout-blocks` | `21` | Отсрочек перед принудительным производством при коллизии | + +`required-participation` всегда в **базисных пунктах** (0–10000 = 0%–100%). + +Подробности — в разделе [Плагин валидатора](./validator.md). + +--- + +## Рекомендуемые наборы плагинов + +### Минимальный API-узел + +```ini +plugin = chain +plugin = json_rpc +plugin = webserver +plugin = p2p +plugin = database_api +plugin = network_broadcast_api +``` + +### Полный API-узел + +```ini +plugin = chain +plugin = json_rpc +plugin = webserver +plugin = p2p +plugin = database_api +plugin = network_broadcast_api +plugin = witness_api +plugin = account_by_key +plugin = account_history +plugin = operation_history +plugin = committee_api +plugin = invite_api +plugin = paid_subscription_api +``` + +### Узел-валидатор + +```ini +plugin = chain +plugin = p2p +plugin = validator +plugin = json_rpc +plugin = webserver +plugin = database_api +plugin = network_broadcast_api +plugin = witness_api +plugin = snapshot + +snapshot-every-n-blocks = 28800 +snapshot-dir = /data/snapshots +dlt-block-log-max-blocks = 100000 +``` + +--- + +См. также: [Плагин chain](./chain.md), [Плагин валидатора](./validator.md), [Плагин snapshot](./snapshot.md), [Обзор P2P](../p2p/overview.md), [JSON-RPC API](../api/json-rpc.md). diff --git a/@l10n/ru/docs/plugins/snapshot.md b/@l10n/ru/docs/plugins/snapshot.md new file mode 100644 index 0000000000..2164104519 --- /dev/null +++ b/@l10n/ru/docs/plugins/snapshot.md @@ -0,0 +1,347 @@ +# Плагин Snapshot + +Плагин snapshot обеспечивает почти мгновенный запуск узла путём сериализации и восстановления полного состояния блокчейна в виде JSON-файла. Вместо воспроизведения миллионов блоков из block log, узел загружает предварительно подготовленный снимок и начинает синхронизацию с высоты блока снимка через P2P. + +**Исходник:** [plugins/snapshot/snapshot.cpp](../../plugins/snapshot/snapshot.cpp) + +--- + +## Зависимости + +``` +chain::plugin +``` + +--- + +## Конфигурация + +### Только CLI-опции + +| Опция | По умолчанию | Описание | +|-------|-------------|---------| +| `--snapshot ` | — | Загрузить состояние из файла снимка (DLT-режим). Пропускает импорт, если `shared_memory.bin` уже существует; переименовывает файл в `.used` после успешного импорта. | +| `--snapshot-auto-latest` | `false` | Автоматически обнаружить последний снимок в `snapshot-dir` по номеру блока из имени файла. Игнорируется, если также указан `--snapshot`. | +| `--replay-from-snapshot` | `false` | Восстановление после сбоя: импортировать снимок, затем воспроизвести блоки из скользящего DLT block log. Всегда очищает shared memory; НЕ переименовывает файл снимка в `.used`. Требует `--snapshot` или `--snapshot-auto-latest`. | +| `--auto-recover-from-snapshot` | `true` | Автоматическое восстановление во время работы при повреждении shared memory — перезапуск не требуется. Требует `plugin = snapshot` и наличия снимков в `snapshot-dir`. Отключить через `--no-auto-recover-from-snapshot`. | +| `--create-snapshot ` | — | Создать снимок по указанному пути, используя текущее состояние базы данных, затем выйти. Запускается до активации P2P или валидатора. | +| `--sync-snapshot-from-trusted-peer` | `false` | Загрузить и применить снимок с доверенных пиров, когда состояние пустое. Требует `trusted-snapshot-peer`. Явно включается, чтобы предотвратить случайное стирание состояния. | + +### Опции файла конфигурации + +| Опция | По умолчанию | Описание | +|-------|-------------|---------| +| `snapshot-at-block` | `0` | Создать снимок при достижении указанного номера блока (0 = отключено). | +| `snapshot-every-n-blocks` | `0` | Создавать снимок каждые N блоков (0 = отключено). Срабатывает только на живых блоках — пропускается во время начальной P2P-синхронизации. | +| `snapshot-dir` | — | Каталог для автоматически создаваемых файлов снимков. Создаётся автоматически при отсутствии. | +| `snapshot-max-age-days` | `90` | Удалять снимки старше N дней после создания нового (0 = отключено). | +| `allow-snapshot-serving` | `false` | Включить обслуживание снимков по TCP для других узлов. | +| `allow-snapshot-serving-only-trusted` | `false` | Ограничить обслуживание снимков только настроенными доверенными пирами. | +| `snapshot-serve-endpoint` | `0.0.0.0:8092` | TCP-точка для слушателя обслуживания снимков. | +| `trusted-snapshot-peer` | — | Конечная точка доверенного пира для P2P-синхронизации снимков (`IP:port`); может повторяться. | + +Опция `dlt-block-log-max-blocks` (в разделе конфигурации плагина `chain`) управляет размером скользящего DLT block log и тесно связана с работой снимков — см. [Скользящий DLT block log](#скользящий-dlt-block-log) ниже. + +--- + +## Создание снимков + +### Метод 1: Единовременно (узел остановлен) + +Остановите узел, затем перезапустите с `--create-snapshot`. Узел открывает существующую базу данных (воспроизводя из block log при необходимости), записывает снимок и выходит до активации P2P или валидатора: + +```bash +vizd --create-snapshot /data/snapshots/viz-snapshot.json --plugin snapshot +``` + +### Метод 2: На конкретном блоке (без простоя) + +```ini +plugin = snapshot +snapshot-at-block = 5000000 +snapshot-dir = /data/snapshots +``` + +Когда применяется блок 5 000 000, узел записывает `/data/snapshots/snapshot-block-5000000.json` без прерывания работы. + +### Метод 3: Периодически (рекомендуется) + +```ini +plugin = snapshot +snapshot-every-n-blocks = 28800 +snapshot-dir = /data/snapshots +snapshot-max-age-days = 90 +``` + +Файлы называются `snapshot-block-.json`. Рекомендуемые интервалы: + +| Интервал | Блоков | Время (при 3 с/блок) | +|---------|--------|---------------------| +| Часто | 10 000 | ~8 ч | +| Ежедневно | 28 800 | ~24 ч | +| Еженедельно | 100 000 | ~3,5 суток | + +### Метод 4: Комбинирование at-block и периодического + +Оба параметра можно задать вместе; `snapshot-at-block` срабатывает один раз, а `snapshot-every-n-blocks` — многократно. + +### Как работает создание снимка + +Создание снимка асинхронно и разделено на две фазы для минимизации влияния: + +- **Фаза 1 (блокировка на чтение, ~1 с):** Все 32 отслеживаемых типа объектов сериализуются в памяти. Обработка блоков ожидает во время этой фазы; операции чтения API и P2P выполняются параллельно. +- **Фаза 2 (без блокировки, ~2 с):** Сжатие, вычисление контрольной суммы SHA-256 и запись в файл. Нормальная работа узла возобновляется. + +При сбое создания (например, диск переполнен) ошибка записывается в журнал, и узел продолжает работу. + +--- + +## Загрузка из снимка (DLT-режим) + +### Первый запуск + +```bash +vizd --snapshot /path/to/snapshot.json --plugin snapshot +``` + +При первой загрузке: +1. Плагин snapshot проверяет заголовок файла (версию формата, chain ID, контрольную сумму SHA-256). +2. Shared memory очищается и переинициализируется из состояния снимка. +3. Все 32 типа объектов импортируются. +4. LIB повышается до `head_block_num`, чтобы P2P-синхронизация начиналась с точки снимка. +5. Файл снимка переименовывается в `.used` для предотвращения повторного импорта при перезапуске. +6. Плагин P2P запускается и синхронизируется вперёд от head снимка. + +### Безопасность перезапуска + +Узел безопасно перезапускать с `--snapshot` всё ещё в командной строке (например, через `VIZD_EXTRA_OPTS`): + +| Сценарий | Поведение | +|---------|---------| +| Первый запуск (нет shared memory, файл существует) | Импортирует снимок; переименовывает в `.used` | +| Перезапуск (shared memory существует) | Пропускает импорт — использует существующее состояние | +| Перезапуск (shared memory очищена, файл уже `.used`) | Пропускает импорт — "snapshot file not found" | +| Принудительный повторный импорт | Используйте `--resync-blockchain` + предоставьте свежий файл снимка | + +### DLT-режим + +После импорта снимка узел работает в **DLT-режиме**: основной `block_log` пуст. Отдельный [скользящий DLT block log](#скользящий-dlt-block-log) хранит последние блоки для P2P-обслуживания и локальных запросов блоков. Режим определяется автоматически при последующих перезапусках (пустой `block_log` + существующее состояние chainbase). + +--- + +## Формат файла снимка + +Каждый снимок — один JSON-файл: + +```json +{ + "header": { + "version": 1, + "chain_id": "...", + "snapshot_block_num": 12345678, + "snapshot_block_id": "...", + "snapshot_block_time": "2025-01-01T00:00:00", + "last_irreversible_block_num": 12345660, + "payload_checksum": "sha256...", + "object_counts": { "account": 50000, ... } + }, + "state": { + "dynamic_global_property": [...], + "account": [...], + ... + } +} +``` + +### Включённые типы объектов (32 всего) + +**Критические (11)** — необходимы для консенсуса: +`dynamic_global_property`, `witness_schedule`, `hardfork_property`, `account`, `account_authority`, `validator`, `witness_vote`, `block_summary`, `content`, `content_vote`, `block_post_validation` + +**Важные (15)** — необходимы для полной работы: +`transaction`, `vesting_delegation`, `vesting_delegation_expiration`, `fix_vesting_delegation`, `withdraw_vesting_route`, `escrow`, `proposal`, `required_approval`, `committee_request`, `committee_vote`, `invite`, `award_shares_expire`, `paid_subscription`, `paid_subscribe`, `witness_penalty_expire` + +**Опциональные (5)** — метаданные и восстановление: +`content_type`, `account_metadata`, `master_authority_history`, `account_recovery_request`, `change_recovery_account_request` + +--- + +## Скользящий DLT block log + +В DLT-режиме основной `block_log` пуст. **Скользящий DLT block log** (`dlt_block_log.log` + `dlt_block_log.index`) хранит самые последние необратимые блоки. + +Это обеспечивает: +- **P2P-обслуживание блоков** — пиры, запрашивающие последние блоки для разрешения форков и догона при синхронизации. +- **Локальные запросы блоков** — API-вызовы вроде `get_block` работают для хранимого диапазона. + +### Конфигурация + +```ini +# Хранить последние 100 000 блоков (по умолчанию) +dlt-block-log-max-blocks = 100000 + +# Полностью отключить DLT block log +# dlt-block-log-max-blocks = 0 +``` + +Журнал использует скользящее окно: когда он превышает `dlt-block-log-max-blocks`, старые блоки обрезаются с начала. При перезапуске журнал сохраняется, и новые блоки добавляются с того места, где остановились. + +### Обнаружение устаревшего снимка + +Если номер блока последнего снимка меньше начального блока DLT-журнала, скачивающие узлы не могут перекрыть разрыв (снимок на блоке N, DLT-журнал начинается с M > N, блоки N+1..M-1 отсутствуют). Плагин обнаруживает это при запуске и создаёт срочный свежий снимок на первом живом блоке после завершения синхронизации. + +--- + +## Восстановление после сбоя: `--replay-from-snapshot` + +Когда `shared_memory.bin` повреждён и узел не может запуститься нормально, `--replay-from-snapshot` комбинирует импорт снимка с воспроизведением DLT block log: + +```bash +# Указать снимок явно +vizd --replay-from-snapshot --snapshot /data/snapshots/snapshot-block-79273800.vizjson --plugin snapshot + +# Или автоматически обнаружить последний снимок +vizd --replay-from-snapshot --snapshot-auto-latest --plugin snapshot +``` + +**Шаги восстановления:** +1. Очищает shared memory (всегда — предполагается повреждение). +2. Импортирует снимок. +3. Воспроизводит блоки из `dlt_block_log` после head снимка, восстанавливая до последнего доступного состояния. +4. Возобновляет P2P-синхронизацию от воспроизведённого head блока. + +### Сравнение: `--snapshot` и `--replay-from-snapshot` + +| Аспект | `--snapshot` | `--replay-from-snapshot` | +|--------|-------------|--------------------------| +| Назначение | Первоначальная настройка узла | Восстановление после повреждения | +| Проверка shared memory | Пропускает, если уже существует | Всегда очищает и повторно импортирует | +| Переименование файла снимка | Переименовывает в `.used` | НЕ переименовывает | +| Воспроизведение DLT block log | Нет | Да | +| Типичное использование | Первоначальная настройка | Восстановление после сбоя | + +### Пример восстановления + +DLT-узел падает на блоке 79 274 318. Состояние: +``` +snapshots/snapshot-block-79273800.vizjson ← на 518 блоков позади точки сбоя +blockchain/dlt_block_log.log ← содержит блоки 79174319..79274318 +blockchain/shared_memory.bin ← ПОВРЕЖДЁН +``` + +Команда восстановления: `vizd --replay-from-snapshot --snapshot-auto-latest --plugin snapshot` + +``` +Loading state from snapshot: snapshot-block-79273800.vizjson +Snapshot loaded at block 79273800, elapsed time 12.3 sec +Replaying dlt_block_log from block 79273801 to 79274318 (518 blocks)... +Done replaying, head_block=79274318, elapsed time: 7.3 sec +``` + +P2P-синхронизация заполняет разрыв до живого head цепочки. + +--- + +## Автоматическое восстановление во время работы: `--auto-recover-from-snapshot` + +Включено по умолчанию. При обнаружении повреждения shared memory во время обработки или генерации блоков узел: + +1. Закрывает базу данных. +2. Находит последний снимок в `snapshot-dir`. +3. Очищает shared memory, импортирует снимок, воспроизводит `dlt_block_log`. +4. Возобновляет P2P-синхронизацию — перезапуск не требуется. + +**Предварительные условия:** +- `plugin = snapshot` должен быть включён. +- В `snapshot-dir` должны существовать снимки. Используйте `snapshot-every-n-blocks` для их автоматического создания. +- `dlt_block_log` должен покрывать блоки после снимка для минимальной потери данных. + +При сбое восстановления (снимок не найден, ошибка импорта) узел записывает ошибку в журнал и чисто завершает работу. + +Для отключения: `--no-auto-recover-from-snapshot` + +--- + +## P2P-синхронизация снимков + +Узлы могут обслуживать и загружать снимки по TCP, обеспечивая полностью автоматизированную начальную настройку без ручной передачи файлов. + +### Конфигурация сервера + +```ini +plugin = snapshot +allow-snapshot-serving = true +snapshot-serve-endpoint = 0.0.0.0:8092 +snapshot-every-n-blocks = 28800 +snapshot-dir = /data/snapshots + +# Необязательно: ограничить только доверенными пирами +# allow-snapshot-serving-only-trusted = true +# trusted-snapshot-peer = 1.2.3.4:8092 +``` + +### Конфигурация клиента + +```ini +plugin = snapshot +trusted-snapshot-peer = seed1.viz.world:8092 +trusted-snapshot-peer = seed2.viz.world:8092 +sync-snapshot-from-trusted-peer = true +``` + +`sync-snapshot-from-trusted-peer` по умолчанию `false` — должен быть явно включён, чтобы предотвратить случайное стирание состояния. + +### Как это работает + +1. **Запрос:** Подключается к каждому доверенному пиру, отправляет `snapshot_info_request`, собирает метаданные (номер блока, контрольную сумму, размер). +2. **Выбор:** Выбирает пира с наибольшим номером блока. +3. **Загрузка:** Загружается кусками по 1 МБ; прогресс записывается в консоль каждые 5%. +4. **Проверка:** Контрольная сумма SHA-256 проверяется потоково (без полной загрузки файла в память). +5. **Импорт:** Очищает состояние, загружает проверенный снимок, инициализирует харфорки. + +Все операции происходят внутри `chain::plugin_startup()`, до активации P2P и валидатора. Узел полностью заблокирован до завершения импорта. + +### Безопасность + +- Максимальный размер загрузки: 2 ГБ. +- Максимум 5 одновременных подключений на сервер, каждое с 60-секундным дедлайном подключения. +- Ограничение скорости по IP. +- Управляющие сообщения ограничены 64 КБ; только ответы с данными допускают до 64 МБ. +- TCP-сервер работает в выделенном потоке, независимо от основного цикла ввода/вывода. + +--- + +## Рекомендуемая производственная конфигурация + +```ini +plugin = snapshot + +# Ежедневные снимки (~24 ч при 3 с/блок) +snapshot-every-n-blocks = 28800 +snapshot-dir = /data/viz-snapshots + +# Хранить снимки 90 дней (по умолчанию) +snapshot-max-age-days = 90 + +# Скользящий DLT block log: последние 100 тыс. блоков (по умолчанию) +dlt-block-log-max-blocks = 100000 +``` + +--- + +## Устранение неполадок + +| Симптом | Проверка | +|---------|---------| +| Узел повторно импортирует снимок при каждом перезапуске | `shared_memory.bin` удаляется между перезапусками; или файл снимка так и не был переименован в `.used` | +| `Snapshot file not found` при запуске | Файл уже переименован в `.used` при предыдущем успешном импорте; или неверный путь | +| `Chain ID mismatch` при загрузке снимка | Снимок создан из другой цепочки; невозможно импортировать | +| `Checksum mismatch` | Файл снимка повреждён или передан неполностью | +| Создание снимка никогда не срабатывает | `snapshot-dir` не задан, или узел всё ещё в P2P-синхронизации (периодические снимки пропускают режим синхронизации) | +| Предупреждение об устаревшем снимке при запуске | Последний снимок старше начала `dlt_block_log`; узел создаст свежий снимок после следующего живого блока | +| Автовосстановление срабатывает, но даёт сбой | Нет снимков в `snapshot-dir`; проверьте, настроен ли `snapshot-every-n-blocks` | +| Загрузка снимка по P2P не удаётся | Проверьте доступность `trusted-snapshot-peer` на порту 8092; проверьте `allow-snapshot-serving = true` на сервере | + +--- + +См. также: [Снимки](../node/snapshot.md), [Плагин валидатора](./validator.md), [Плагин chain](./chain.md), [Обзор P2P](../p2p/overview.md). diff --git a/@l10n/ru/docs/plugins/validator.md b/@l10n/ru/docs/plugins/validator.md new file mode 100644 index 0000000000..ea33a9b892 --- /dev/null +++ b/@l10n/ru/docs/plugins/validator.md @@ -0,0 +1,226 @@ +# Плагин валидатора + +Плагин валидатора отвечает за подписание и производство блоков. Он запускает выделенный цикл таймера с интервалом 250 мс в собственном потоке ОС, выполняет ряд проверок безопасности при каждом тике и вызывает `database::generate_block()` при выполнении всех условий. + +**Источник:** [plugins/validator/validator.cpp](../../plugins/validator/validator.cpp) + +--- + +## Зависимости + +``` +chain::plugin, p2p::p2p_plugin, snapshot::snapshot_plugin +``` + +--- + +## Конфигурация + +### Производство блоков + +| Параметр | По умолчанию | Описание | +|---------|------------|---------| +| `validator` / `-w` | — | Имя/имена аккаунтов-валидаторов; может повторяться | +| `private-key` | — | WIF-ключ(и) для подписи; может повторяться | +| `emergency-private-key` | — | WIF-ключ для экстренного консенсуса; автоматически добавляет `CHAIN_EMERGENCY_WITNESS_ACCOUNT` в набор валидаторов | +| `enable-stale-production` | `false` | Обход проверок участия и синхронизации (только для тестовой сети / восстановления сети) | +| `required-participation` | `3300` | Минимальное участие валидаторов в **базисных пунктах** (3300 = 33%) | +| `fork-collision-timeout-blocks` | `21` | Количество последовательных отсрочек при коллизии форков перед принудительным производством (один полный раунд валидаторов) | + +### Синхронизация NTP + +| Параметр | По умолчанию | Описание | +|---------|------------|---------| +| `ntp-server` | `pool.ntp.org`, `time.google.com`, `time.cloudflare.com` | NTP-серверы; может повторяться | +| `ntp-request-interval` | `900` | Нормальный интервал синхронизации в секундах | +| `ntp-retry-interval` | `300` | Интервал повторной попытки при отсутствии ответа NTP | +| `ntp-round-trip-threshold` | `150` | Отбрасывать ответы NTP с временем roundtrip > N мс | +| `ntp-history-size` | `5` | Окно скользящего среднего для сглаживания дельты NTP | + +### Отладка + +| Параметр | По умолчанию | Описание | +|---------|------------|---------| +| `debug-block-production` | `false` | Включить подробное отладочное логирование в базе данных цепочки | + +--- + +## Таймер производства + +Цикл производства работает на **выделенном `production_io_service_`** и собственном потоке ОС — полностью отдельно от общего io_service AppBase/P2P. Это предотвращает задержку 250-мс обратного вызова таймера из-за P2P-активности (отключение пиров, TLS-рукопожатия, опустошение очереди отправки). + +**Выравнивание тика:** +``` +тик таймера каждые 250 мс по границам 250 мс настенных часов +минимальная задержка: 50 мс (для поглощения джиттера ОС) +``` + +**Упреждение:** `now = ntp_time + 250 мс` — сдвигает решение о производстве вперёд так, чтобы тик на `T_slot - 250 мс` совпадал точно с границей слота: +``` +Слот в T=6.000 с: + Тик в T=5.750 → now=6.000 → слот совпал → производство при lag=0 мс + Тик в T=6.000 → now=6.250 → lag=250 мс → ещё в пределах порога 500 мс +``` + +**Пропуск при задержке:** После результата `lag` тот же слот повторно срабатывал бы при каждом тике в оставшееся время 3-секундного интервала. Защита пропускает до следующей границы слота, чтобы цикл освобождал CPU вместо холостого вращения. + +--- + +## `maybe_produce_block()` — последовательность проверок безопасности + +Следующие проверки выполняются **по порядку** при каждом тике, где `slot > 0`. + +| № | Проверка | Результат при отказе | +|---|---------|---------------------| +| 1 | Шлюз синхронизации DLT (только DLT-режим): `chain().is_syncing()` = false, или узел является экстренным мастером | `not_synced` | +| 2 | Шлюз паузы снапшота: `snapshot().is_snapshot_in_progress()` = false | `not_synced` | +| 3 | Шлюз нагона P2P: `p2p().is_catching_up_after_pause()` = false | `not_synced` | +| 4 | Трёхсостояние безопасности HF12 (см. ниже) | `not_synced` / `low_participation` | +| 5 | `slot = db.get_slot_at_time(now) > 0` | `not_time_yet` | +| 6 | Запланированный валидатор входит в наш настроенный набор | `not_my_turn` | +| 7 | Слот ещё не заполнен (`scheduled_time > head_block_time`) | `not_time_yet` | +| 8 | `signing_key` валидатора в блокчейне ненулевой | `not_my_turn` | +| 9 | Приватный ключ для `signing_key` загружен | `no_private_key` | +| 10 | До HF12: участие ≥ порога | `low_participation` | +| 11 | `|scheduled_time - now| ≤ 500 мс` | `lag` | +| 12 | Проверка коллизии форков (см. ниже) | `fork_collision` | +| 13 | Вторая проверка паузы снапшота (окно гонки) | `not_time_yet` | +| 14 | `db.generate_block()` + `p2p().broadcast_block()` | `produced` | + +### Трёхсостояние безопасности HF12 (проверка №4) + +**Активен экстренный консенсус:** +- Экстренный мастер (имеет `emergency-private-key` + «committee» в расписании): продолжает безусловно. +- Ведомый: требует `get_slot_time(1) >= now` (цепочка не устарела) перед производством. + +**Нормальный режим (HF12+):** +- Участие ≥ 33%: здоровая сеть; проверка синхронизации через `get_slot_time(1)`. +- Участие < 33%: ослабленная сеть; применяется порог участия vs `required-participation`. +- `enable-stale-production=true`: обходит обе проверки участия и синхронизации. + +**До HF12:** Простая проверка синхронизации через `get_slot_time(1)`. + +### Разрешение коллизии форков (проверка №12) + +При наличии конкурирующего блока на `head_block_num + 1`: + +1. **Сравнение по весу голосов (HF12+):** `compare_fork_branches()` вычисляет суммарные SHARES, делегированные каждой ветви. Если наша ветвь тяжелее — продолжить и удалить конкурирующий блок. При равенстве или меньшем весе — отложить. +2. **Таймаут зависшей головы:** После `fork-collision-timeout-blocks` последовательных отсрочек (по умолчанию 21 = 63 секунды) конкурирующий блок удаляется и производство возобновляется. Это обрабатывает «мёртвые» блоки форка от отключённых пиров. + +**Экстренный режим:** Любой конкурирующий блок вызывает отсрочку; путь взвешивания голосов не используется. + +--- + +## Обнаружение minority fork + +Перед каждой попыткой производства (после проверок безопасности HF12) плагин просматривает последние 21 блок в `fork_db`. Если все 21 были произведены собственными настроенными валидаторами узла — узел изолирован на minority fork. + +- **Действие по умолчанию:** Вызов `p2p().resync_from_lib()` — откат блоков к LIB, сброс fork DB, повторная инициация синхронизации P2P, переподключение к начальным узлам. Возвращает `minority_fork`. +- **С `enable-stale-production=true`:** Запись предупреждения, продолжение производства. +- **Пропускается при:** Активном экстренном консенсусе (блоки комитета всегда соответствовали бы нашему настроенному набору). В экстренном режиме вместо него используется специфичная для DLT проверка изоляции ведомого. + +--- + +## Обнаружение остановки NTP + +Если `get_slot_at_time(now)` возвращает 0 (NTP отстаёт от времени цепочки), счётчик `_slot_zero_streak` увеличивается: + +| Серия | Время | Действие | +|-------|-------|---------| +| 3 | ~750 мс | Предупреждение | +| 10 | ~2,5 с | Принудительная повторная синхронизация NTP | +| 60 | ~15 с | Предупреждение о длительной остановке | +| 120 | ~30 с | Критическая ошибка | + +Счётчик сбрасывается при любом ненулевом результате слота. + +--- + +## Watchdog производства + +Если узел хотя бы раз производил блок и `should_be_producing` = true (определяется из активного состояния цепочки: участие ≥ 33% или активен экстренный консенсус с нашим ключом), но ни одного блока не было произведено в течение: +- Экстренный мастер: **60 секунд** +- Обычный валидатор: **180 секунд** + +Watchdog срабатывает каждые 30 секунд и записывает диагностику. При выполнении условий восстановления (голова продвигалась в последние 30 с, не синхронизируется, есть соединения с пирами, ненулевые ключи подписи в блокчейне) он принудительно сбрасывает блокирующие условия: + +1. Сбрасывает флаг `_minority_fork_recovering`. +2. Вызывает `p2p().clear_catchup_flag()` — сбрасывает флаг нагона P2P после паузы. +3. Вызывает `chain().clear_syncing()` — сбрасывает флаг синхронизации цепочки. + +Производство автоматически возобновляется при следующем тике. + +--- + +## `on_block_applied()` — обработчик сигнала + +Подключён к `database::applied_block`. Запускается для каждого входящего блока. + +### Обнаружение пропущенного слота + +Когда `block_num > prev_num + 1` (разрыв в потоке блоков), обработчик определяет, был ли наш валидатор запланирован для любого из пропущенных слотов, и записывает полное диагностическое состояние (флаги производства, смещение NTP, статус синхронизации, статус ключа подписи, время следующего слота). + +### Обнаружение перехвата слота (DLT-режим экстренного консенсуса) + +При активном экстренном консенсусе экстренный мастер может обнулить ключ подписи нашего валидатора и производить блоки комитета в наших запланированных слотах. Обработчик отслеживает это через `_slot_hijack_count`. Сбрасывается, когда один из наших валидаторов производит блок. + +--- + +## Публичный API + +### `is_witness_scheduled_soon()` + +Возвращает `true`, если локально управляемый валидатор запланирован для производства в следующих 4 слотах (~12 секунд). Плагин снапшота вызывает это перед планированием снапшота, чтобы отложить его при неминуемом производстве. + +### `is_emergency_master()` + +Возвращает `true`, когда: +1. Настроен `emergency-private-key` (`CHAIN_EMERGENCY_WITNESS_ACCOUNT` в `_witnesses`). +2. Аккаунт «committee» находится в текущем расписании валидаторов. + +Только узлы, где выполняются оба условия, должны производить блоки в одиночку в экстренном режиме; остальные являются ведомыми и должны сначала синхронизироваться. + +### `is_emergency_key_configured()` + +Возвращает `true`, если настроен `emergency-private-key`, независимо от текущего расписания. Используется в сообщениях приветствия P2P (поле `has_emergency_key`). + +### `get_production_diagnostics()` + +Возвращает компактную диагностическую строку: +``` +validator[skip_flags=0x0 catching_up=0 head=#79881136 last_prod=45s_ago minority_rcv=0 slot_hijacks=0] +``` +Включается в логи стагнации P2P FORWARD, когда узел завис без пиров впереди. + +--- + +## Ключевые инварианты + +1. **Никогда не производить в DLT-режиме при синхронизации** — создаёт блоки на устаревшей голове, вызывая осцилляцию форков. +2. **Никогда не производить во время создания снапшота** — взаимная блокировка записи. +3. **Никогда не производить, если слот уже заполнен** — создаёт мини-форк. +4. **Экстренный мастер должен всегда производить** — он единственный производитель блоков; ожидание вызовет дедлок. +5. **Ведомые должны синхронизироваться перед производством в экстренном режиме** — производство на устаревшей голове = minority fork. +6. **Участие < 33% останавливает производство** — защита от сетевого раздела (переопределяемая). +7. **21 последовательный блок от собственных валидаторов → откат к LIB** — восстановление после minority fork. +8. **Все чтения базы данных свежие** — кэширование состояния отсутствует; экстренный режим может активироваться/деактивироваться на каждом блоке. + +--- + +## Устранение неполадок + +| Симптом | Что проверить | +|---------|--------------| +| Логи `not_synced` | Активна синхронизация DLT или создание снапшота — ждать; watchdog сбросит автоматически при зависании | +| Повторное `not_time_yet` | NTP отстаёт от времени цепочки; проверьте предупреждения `_slot_zero_streak` и смещение NTP | +| `not_my_turn` на нашем слоте | Ключ подписи обнулён в блокчейне; отправьте `validator_update_operation` для восстановления | +| `no_private_key` | В конфиге отсутствует `private-key` для ключа подписи, зарегистрированного в блокчейне | +| `low_participation` | Участие сети < 33%; проверьте подключение к пирам или установите `enable-stale-production=true` | +| `fork_collision` | Конкурирующий блок на следующей высоте; ждать разрешения по весу голосов или таймаута 21 отсрочки | +| `minority_fork` | Изолирован; плагин автоматически пересинхронизируется с LIB | +| Watchdog срабатывает повторно | Флаг синхронизации или нагона завис; watchdog сбросит автоматически при продвижении головы | +| Логи `SLOT-HIJACK` | Экстренный мастер обнулил наш ключ; восстановите через `validator_update_operation` | + +--- + +См. также: [Validator Guard](../node/validator-guard.md), [Fair-DPOS](../consensus/fair-dpos.md), [Экстренный консенсус](../consensus/emergency-consensus.md), [Обработка блоков](../consensus/block-processing.md). diff --git a/@l10n/ru/docs/plugins/webserver.md b/@l10n/ru/docs/plugins/webserver.md new file mode 100644 index 0000000000..7a7b596ea5 --- /dev/null +++ b/@l10n/ru/docs/plugins/webserver.md @@ -0,0 +1,117 @@ +# Плагин Webserver + +Плагин webserver предоставляет HTTP и WebSocket конечные точки, перенаправляющие JSON-RPC запросы в плагин `json_rpc`. Включает кеш ответов, ключом которого служит `method + params` (не `id`), инвалидируемый при каждом применённом блоке, и пул потоков для параллельной обработки запросов. + +**Исходник:** [plugins/webserver/webserver_plugin.cpp](../../plugins/webserver/webserver_plugin.cpp) + +--- + +## Зависимости + +``` +json_rpc::plugin +``` + +--- + +## Конфигурация + +| Опция | По умолчанию | Описание | +|-------|-------------|---------| +| `webserver-http-endpoint` | — | Адрес прослушивания HTTP, например `0.0.0.0:8090` | +| `webserver-ws-endpoint` | — | Адрес прослушивания WebSocket, например `0.0.0.0:8091` | +| `webserver-thread-pool-size` | `256` | Рабочие потоки для обработки HTTP и WebSocket запросов. | +| `webserver-cache-enabled` | `true` | Включить кеш ответов. | +| `webserver-cache-size` | `10000` | Максимальное количество кешированных ответов. Кеш полностью вытесняется при достижении этого лимита. | + +Для работы плагина должно быть задано хотя бы одно из `webserver-http-endpoint` или `webserver-ws-endpoint`. Оба могут быть включены одновременно. + +--- + +## Минимальный config.ini + +```ini +plugin = webserver + +webserver-http-endpoint = 0.0.0.0:8090 +webserver-ws-endpoint = 0.0.0.0:8091 +``` + +--- + +## Кеш ответов + +### Что кешируется + +Каждый ответ JSON-RPC только для чтения подходит для кеширования. Ключ кеша — это SHA-256 хеш `method + params` — намеренно **исключая `id`**, чтобы ротация `id` запроса не могла обойти кеш. + +### Что никогда не кешируется + +| Пространство имён | Причина | +|------------------|---------| +| `network_broadcast_api.*` | Изменяющий состояние (трансляция транзакций/блоков) | +| `debug_node.*` | Изменяющие состояние операции отладки | +| Некорректные/нераспознаваемые запросы | Невозможно надёжно вычислить ключ | + +Пакетные запросы (JSON-массив) обрабатываются как единственная атомарная запись кеша с ключом на основе хеша полного массива. + +### Инвалидация + +Кеш очищается при каждом применённом блоке. Ответы никогда не обслуживаются устаревшими дольше одного интервала блока (3 секунды). + +### Отключение кеша + +```ini +webserver-cache-enabled = false +``` + +Отключите для узлов, обслуживающих клиентов с высокими требованиями к задержке или клиентов реального времени, для которых окно кеша в 3 секунды неприемлемо. + +--- + +## Пул потоков + +Серверы HTTP и WebSocket каждый работают на выделенном экземпляре `io_service`. Входящие запросы направляются в общий пул потоков из `webserver-thread-pool-size` рабочих. + +**Рекомендации по размеру:** +- Публичный API-узел со смешанным трафиком чтения/записи: `256` (по умолч.) достаточно для большинства нагрузок. +- Высокопроизводительные узлы: увеличьте до `512` или более. Следите за насыщением CPU — больше потоков, чем ядер CPU, не помогает для операций, ограниченных CPU. +- Разработка/локальный узел: достаточно `4`–`8`. + +--- + +## WebSocket-подписки + +WebSocket-клиенты могут регистрировать колбеки: + +| Метод | Описание | +|-------|---------| +| `database_api.set_block_applied_callback` | Вызывается при каждом применённом блоке с заголовком блока | +| `database_api.set_pending_transaction_callback` | Вызывается, когда транзакция поступает в пул ожидания | +| `database_api.cancel_all_subscriptions` | Отписаться от всех колбеков | + +Подписки требуют постоянного WebSocket-соединения. Они недоступны по обычному HTTP. + +--- + +## Безопасность + +- **Привяжите к localhost** (`127.0.0.1`) и используйте обратный прокси (nginx/Caddy) для публичного доступа. Привязка к `0.0.0.0` открывает RPC напрямую в сеть. +- Плагин не имеет встроенной аутентификации или ограничения скорости. Применяйте их на уровне обратного прокси. +- Мутирующие методы (`network_broadcast_api`, `debug_node`) защищены от отравления кеша по замыслу, но они остаются доступными для вызова с любого подключённого клиента — при необходимости ограничьте доступ на сетевом уровне. + +--- + +## Устранение неполадок + +| Симптом | Проверка | +|---------|---------| +| Порт уже занят при запуске | Другой процесс привязан к настроенному порту; измените порт или завершите конфликтующий процесс | +| Высокое использование памяти | Уменьшите `webserver-cache-size` или отключите кеширование | +| Медленные ответы под нагрузкой | Увеличьте `webserver-thread-pool-size`; проверьте насыщение CPU | +| WebSocket-подписки не срабатывают | Подписки требуют WebSocket-соединения, а не HTTP | +| Устаревшие ответы | Если `webserver-cache-enabled = true`, ответы актуальны в пределах одного интервала блока (~3 с); для использования в реальном времени отключите кеш | + +--- + +См. также: [Обзор плагинов](./overview.md), [Database API](./database-api.md), [JSON-RPC API](../api/json-rpc.md). diff --git a/@l10n/ru/docs/protocol/data-types.md b/@l10n/ru/docs/protocol/data-types.md new file mode 100644 index 0000000000..a05a60a979 --- /dev/null +++ b/@l10n/ru/docs/protocol/data-types.md @@ -0,0 +1,257 @@ +# Общие типы данных + +Все общие типы данных, используемые в операциях и виртуальных операциях протокола VIZ Ledger. + +--- + +## Примитивные типы + +| Тип C++ | JSON-представление | Описание | +|---------|-------------------|---------| +| `string` | `string` | UTF-8 строка | +| `bool` | `boolean` | `true` / `false` | +| `uint8_t` | `integer` | Беззнаковое 8-битное целое | +| `uint16_t` | `integer` | Беззнаковое 16-битное целое (0–65535) | +| `int16_t` | `integer` | Знаковое 16-битное целое (−32768–32767) | +| `uint32_t` | `integer` | Беззнаковое 32-битное целое | +| `int32_t` | `integer` | Знаковое 32-битное целое | +| `uint64_t` | `string` или `integer` | Беззнаковое 64-битное целое — в JavaScript используйте строку во избежание переполнения | +| `int64_t` | `string` или `integer` | Знаковое 64-битное целое | +| `share_type` | `integer` | Псевдоним для `safe` — количество токенов в единицах сатоши | +| `time_point_sec` | `string` | Дата и время UTC в формате ISO 8601: `"2024-01-15T12:00:00"` (без суффикса часового пояса) | + +--- + +## `account_name_type` + +Строка фиксированной длины (максимум 16 байт), идентифицирующая аккаунт. Правила: + +- Метки, разделённые точками; каждая метка — не менее 3 символов. +- Начинается с буквы, оканчивается буквой или цифрой. +- Только строчные латинские буквы (`a`–`z`), цифры (`0`–`9`), дефисы (`-`). +- Минимальная длина: 2 символа (`CHAIN_MIN_ACCOUNT_NAME_LENGTH`). +- Максимальная длина: 16 символов (`CHAIN_MAX_ACCOUNT_NAME_LENGTH`). + +**JSON:** обычная строка — `"alice"`, `"alice.bob"` + +--- + +## `public_key_type` + +Сжатый публичный ключ secp256k1, закодированный в base58check с префиксом `VIZ`. + +**JSON:** строка — `"VIZ5hqSa4NkEZGAMUpoH5EaEr64mBJuMcPpGjvk8qb7hcPFTbXSQ9"` + +- Префикс должен быть `VIZ` (не `STM`, `GLS` или любой другой). +- Кодируется из 33-байтного сжатого публичного ключа + 4-байтной контрольной суммы = 37 байт всего, затем base58-кодирование. + +--- + +## `asset` + +Представляет количество токенов с символом. В ответах JSON API и параметрах операций сериализуется в виде человекочитаемой строки: + +``` +"10.000 VIZ" +"5.000000 SHARES" +``` + +### Символы токенов + +| Символ | Строка | Знаки после запятой | Описание | +|--------|-------|---------------------|---------| +| `TOKEN_SYMBOL` | `VIZ` | 3 | Основной ликвидный токен | +| `SHARES_SYMBOL` | `SHARES` | 6 | Вестинговые доли (застейканный VIZ) | + +При построении операций всегда используйте строковый формат. Разбирайте разделением по пробелу: слева — количество, справа — символ. VIZ использует 3 знака после запятой; SHARES — 6. + +--- + +## `authority` + +Структура мультиподписной авторизации, управляющая уровнем разрешений аккаунта. + +```json +{ + "weight_threshold": 1, + "account_auths": [ + ["alice", 1] + ], + "key_auths": [ + ["VIZ5hqSa4NkEZGAMUpoH5EaEr64mBJuMcPpGjvk8qb7hcPFTbXSQ9", 1] + ] +} +``` + +| Поле | Тип | Описание | +|------|-----|---------| +| `weight_threshold` | `uint32_t` | Минимальный суммарный вес для выполнения авторизации | +| `account_auths` | `[[account_name, weight], ...]` | Подписывающие на основе аккаунта | +| `key_auths` | `[[public_key, weight], ...]` | Подписывающие на основе ключа | + +Сумма весов выполненных подписей должна быть ≥ `weight_threshold`. Пустая авторизация: `{ "weight_threshold": 0, "account_auths": [], "key_auths": [] }`. + +### Уровни авторизации + +| Уровень | Используется для | +|---------|-----------------| +| `master` | Наивысшая безопасность — смена ключей, восстановление аккаунта | +| `active` | Операции с токенами — перевод, вестинг, голосование за валидаторов | +| `regular` | Социальные операции — контент, награды, голосование в комитете | + +--- + +## `beneficiary_route_type` + +Указывает бенефициара и его долю вознаграждения при выплатах за контент. + +```json +{ "account": "alice", "weight": 2500 } +``` + +| Поле | Тип | Описание | +|------|-----|---------| +| `account` | `account_name_type` | Аккаунт бенефициара | +| `weight` | `uint16_t` | Доля в базисных пунктах (10000 = 100%) | + +- Сумма весов всех бенефициаров не должна превышать 10000. +- Бенефициары должны быть отсортированы по имени аккаунта (по возрастанию) в массиве. +- Каждый аккаунт бенефициара должен существовать в блокчейне. + +--- + +## `extensions_type` + +В настоящее время не используется — всегда сериализуется как пустой массив. + +```json +"extensions": [] +``` + +--- + +## `versioned_chain_properties` + +Статический вариант, содержащий одну из версий параметров цепочки. Сериализуется как 2-элементный массив `[type_index, object]`. + +| Индекс | Тип | +|--------|-----| +| 0 | `chain_properties_init` | +| 1 | `chain_properties_hf4` | +| 2 | `chain_properties_hf6` | +| 3 | `chain_properties_hf9` (текущая) | + +Полный справочник полей по версиям — в разделе [Параметры цепочки](../governance/chain-properties.md). + +--- + +## `operation` (статический вариант) + +Каждая операция сериализуется как 2-элементный массив: `[type_id, operation_object]`. + +### Обычные операции (транслируемые пользователями) + +| ID | Операция | +|----|---------| +| 0 | `vote_operation` *(устарела)* | +| 1 | `content_operation` *(устарела)* | +| 2 | `transfer_operation` | +| 3 | `transfer_to_vesting_operation` | +| 4 | `withdraw_vesting_operation` | +| 5 | `account_update_operation` | +| 6 | `witness_update_operation` | +| 7 | `account_witness_vote_operation` | +| 8 | `account_witness_proxy_operation` | +| 9 | `delete_content_operation` *(устарела)* | +| 10 | `custom_operation` | +| 11 | `set_withdraw_vesting_route_operation` | +| 12 | `request_account_recovery_operation` | +| 13 | `recover_account_operation` | +| 14 | `change_recovery_account_operation` | +| 15 | `escrow_transfer_operation` | +| 16 | `escrow_dispute_operation` | +| 17 | `escrow_release_operation` | +| 18 | `escrow_approve_operation` | +| 19 | `delegate_vesting_shares_operation` | +| 20 | `account_create_operation` | +| 21 | `account_metadata_operation` | +| 22 | `proposal_create_operation` | +| 23 | `proposal_update_operation` | +| 24 | `proposal_delete_operation` | +| 25 | `chain_properties_update_operation` | +| 35 | `committee_worker_create_request_operation` | +| 36 | `committee_worker_cancel_request_operation` | +| 37 | `committee_vote_request_operation` | +| 43 | `create_invite_operation` | +| 44 | `claim_invite_balance_operation` | +| 45 | `invite_registration_operation` | +| 46 | `versioned_chain_properties_update_operation` | +| 47 | `award_operation` | +| 50 | `set_paid_subscription_operation` | +| 51 | `paid_subscribe_operation` | +| 54 | `set_account_price_operation` | +| 55 | `set_subaccount_price_operation` | +| 56 | `buy_account_operation` | +| 58 | `use_invite_balance_operation` | +| 60 | `fixed_award_operation` | +| 61 | `target_account_sale_operation` | + +### Виртуальные операции (генерируются блокчейном, не транслируются) + +| ID | Операция | +|----|---------| +| 26 | `author_reward_operation` | +| 27 | `curation_reward_operation` | +| 28 | `content_reward_operation` | +| 29 | `fill_vesting_withdraw_operation` | +| 30 | `shutdown_witness_operation` | +| 31 | `hardfork_operation` | +| 32 | `content_payout_update_operation` | +| 33 | `content_benefactor_reward_operation` | +| 34 | `return_vesting_delegation_operation` | +| 38 | `committee_cancel_request_operation` | +| 39 | `committee_approve_request_operation` | +| 40 | `committee_payout_request_operation` | +| 41 | `committee_pay_request_operation` | +| 42 | `witness_reward_operation` | +| 48 | `receive_award_operation` | +| 49 | `benefactor_award_operation` | +| 52 | `paid_subscription_action_operation` | +| 53 | `cancel_paid_subscription_operation` | +| 57 | `account_sale_operation` | +| 59 | `expire_escrow_ratification_operation` | +| 62 | `bid_operation` | +| 63 | `outbid_operation` | + +--- + +## Построение транзакции + +Подписанная транзакция содержит: + +| Поле | Значение | +|------|---------| +| `ref_block_num` | `head_block_number & 0xFFFF` | +| `ref_block_prefix` | байты 4–7 `block_id` в виде little-endian `uint32` | +| `expiration` | UTC время в виде строки; рекомендуется не более ~60 с от времени трансляции | +| `operations` | Массив пар `[type_id, object]` | +| `extensions` | Всегда `[]` | +| `signatures` | Массив компактных подписей ECDSA в hex-кодировке | + +**Подпись:** `sha256(chain_id + serialized_transaction_body)` → компактная ECDSA-подпись над secp256k1. + +**Приватные ключи:** формат WIF (base58check, байт версии `0x80`). + +--- + +## Система энергии + +Энергия используется операциями типа «награда». + +- Хранится в базисных пунктах: 0–10000 (0%–100%). +- Восстанавливается со скоростью 100% за 24 часа (`CHAIN_ENERGY_REGENERATION_SECONDS = 86400`). +- Текущая энергия: `min(10000, last_energy + elapsed_seconds × 10000 / 86400)`. + +--- + +См. также: [Обзор операций](./operations/overview.md), [Виртуальные операции](./virtual-operations.md), [Параметры цепочки](../governance/chain-properties.md). diff --git a/@l10n/ru/docs/protocol/operations/account-market.md b/@l10n/ru/docs/protocol/operations/account-market.md new file mode 100644 index 0000000000..38c723d534 --- /dev/null +++ b/@l10n/ru/docs/protocol/operations/account-market.md @@ -0,0 +1,120 @@ +# Операции рынка аккаунтов + +Рынок аккаунтов позволяет выставлять аккаунты и пространства субаккаунтов на продажу и покупать их в блокчейне. + +--- + +## `set_account_price_operation` (ID 54) + +**Авторизация:** `master` `account` + +Выставляет аккаунт на публичную продажу или обновляет объявление. При листинге взимается `account_on_sale_fee`. + +| Поле | Тип | Описание | +|------|-----|---------| +| `account` | `account_name_type` | Выставляемый аккаунт | +| `account_seller` | `account_name_type` | Аккаунт, получающий оплату (может отличаться от `account`) | +| `account_offer_price` | `asset` (VIZ) | Запрашиваемая цена | +| `account_on_sale` | `bool` | `true` — выставить; `false` — снять с продажи | + +```json +[54, { + "account": "alice", + "account_seller": "alice", + "account_offer_price": "1000.000 VIZ", + "account_on_sale": true +}] +``` + +- `account_on_sale: false` снимает аккаунт с продажи без возврата комиссии. +- `account_seller` может быть любым аккаунтом — полезно при брокерских продажах. + +--- + +## `set_subaccount_price_operation` (ID 55) + +**Авторизация:** `master` `account` + +Выставляет на продажу право создания субаккаунтов (например, `account.childname`). При листинге взимается `subaccount_on_sale_fee`. + +| Поле | Тип | Описание | +|------|-----|---------| +| `account` | `account_name_type` | Родительский аккаунт | +| `subaccount_seller` | `account_name_type` | Аккаунт, получающий оплату | +| `subaccount_offer_price` | `asset` (VIZ) | Цена за право создания одного субаккаунта | +| `subaccount_on_sale` | `bool` | `true` — выставить; `false` — снять с продажи | + +```json +[55, { + "account": "alice", + "subaccount_seller": "alice", + "subaccount_offer_price": "50.000 VIZ", + "subaccount_on_sale": true +}] +``` + +- Покупатели приобретают право создать один субаккаунт в пространстве имён `account` за транзакцию. + +--- + +## `buy_account_operation` (ID 56) + +**Авторизация:** `active` `buyer` + +Покупает аккаунт, выставленный на продажу. Все права передаются покупателю. + +| Поле | Тип | Описание | +|------|-----|---------| +| `buyer` | `account_name_type` | Покупающий аккаунт | +| `account` | `account_name_type` | Покупаемый аккаунт | +| `account_offer_price` | `asset` (VIZ) | Цена покупки (должна точно совпадать с объявлением) | +| `account_authorities_key` | `public_key_type` | Новый ключ, устанавливаемый как master, active, regular и memo купленного аккаунта | +| `tokens_to_shares` | `asset` (VIZ) | Дополнительный VIZ для конвертации в SHARES для купленного аккаунта (может быть `"0.000 VIZ"`) | + +```json +[56, { + "buyer": "bob", + "account": "alice", + "account_offer_price": "1000.000 VIZ", + "account_authorities_key": "VIZ5newowner...", + "tokens_to_shares": "0.000 VIZ" +}] +``` + +- `account_offer_price` должна точно совпадать с ценой в `set_account_price_operation`. +- `account_authorities_key` применяется ко всем четырём слотам authority одновременно. +- Оплата отправляется `account_seller`, указанному в объявлении. +- При успешной покупке срабатывает виртуальная операция `account_sale_operation`. + +--- + +## `target_account_sale_operation` (ID 61) + +**Авторизация:** `master` `account` + +Выставляет аккаунт на приватную (адресную) продажу конкретному покупателю. Купить это объявление может только `target_buyer`. + +| Поле | Тип | Описание | +|------|-----|---------| +| `account` | `account_name_type` | Выставляемый аккаунт | +| `account_seller` | `account_name_type` | Аккаунт, получающий оплату | +| `target_buyer` | `account_name_type` | Единственный допустимый покупатель | +| `account_offer_price` | `asset` (VIZ) | Запрашиваемая цена | +| `account_on_sale` | `bool` | `true` — выставить; `false` — снять с продажи | + +```json +[61, { + "account": "alice", + "account_seller": "alice", + "target_buyer": "charlie", + "account_offer_price": "500.000 VIZ", + "account_on_sale": true +}] +``` + +- `account_on_sale: false` отменяет адресное объявление. +- Покупатель использует стандартную `buy_account_operation` для завершения покупки. + +--- + +См. также: [Типы данных](../data-types.md), [Обзор операций](./overview.md), [Виртуальные операции](../virtual-operations.md), [Database API — рынок аккаунтов](../../plugins/database-api.md#account-market). diff --git a/@l10n/ru/docs/protocol/operations/accounts.md b/@l10n/ru/docs/protocol/operations/accounts.md new file mode 100644 index 0000000000..87607c966c --- /dev/null +++ b/@l10n/ru/docs/protocol/operations/accounts.md @@ -0,0 +1,96 @@ +# Операции с аккаунтами + +--- + +## `account_create_operation` (ID 20) + +**Авторизация:** `active` `creator` + +Создаёт новый аккаунт в блокчейне. Комиссия конвертируется в SHARES для нового аккаунта. + +| Поле | Тип | Описание | +|------|-----|---------| +| `fee` | `asset` (VIZ) | Комиссия за создание ≥ `account_creation_fee` цепочки | +| `delegation` | `asset` (SHARES) | Начальное делегирование SHARES новому аккаунту | +| `creator` | `account_name_type` | Аккаунт, оплачивающий комиссию | +| `new_account_name` | `account_name_type` | Имя нового аккаунта | +| `master` | `authority` | Master authority | +| `active` | `authority` | Active authority | +| `regular` | `authority` | Regular authority | +| `memo_key` | `public_key_type` | Публичный ключ memo | +| `json_metadata` | `string` | JSON-метаданные (может быть `""`) | +| `referrer` | `account_name_type` | Аккаунт-реферер (может быть `""`) | +| `extensions` | `extensions_type` | Всегда `[]` | + +```json +[20, { + "fee": "1.000 VIZ", + "delegation": "10.000000 SHARES", + "creator": "alice", + "new_account_name": "bob", + "master": { "weight_threshold": 1, "account_auths": [], "key_auths": [["VIZ5...", 1]] }, + "active": { "weight_threshold": 1, "account_auths": [], "key_auths": [["VIZ5...", 1]] }, + "regular": { "weight_threshold": 1, "account_auths": [], "key_auths": [["VIZ5...", 1]] }, + "memo_key": "VIZ5...", + "json_metadata": "", + "referrer": "", + "extensions": [] +}] +``` + +- Все три authority обязательны (даже если используются одинаковые ключи). +- `fee.symbol` должен быть `VIZ`; `delegation.symbol` должен быть `SHARES`. + +--- + +## `account_update_operation` (ID 5) + +**Авторизация:** `master` `account` (если присутствует поле `master`), иначе `active` + +Обновляет ключи и метаданные аккаунта. + +| Поле | Тип | Описание | +|------|-----|---------| +| `account` | `account_name_type` | Аккаунт для обновления | +| `master` | `optional` | Новый master authority (опустить, если не меняется) | +| `active` | `optional` | Новый active authority | +| `regular` | `optional` | Новый regular authority | +| `memo_key` | `public_key_type` | Новый ключ memo (обязателен, даже если не меняется) | +| `json_metadata` | `string` | Новые JSON-метаданные | + +```json +[5, { + "account": "alice", + "active": { "weight_threshold": 1, "account_auths": [], "key_auths": [["VIZ5new...", 1]] }, + "memo_key": "VIZ5new...", + "json_metadata": "{\"profile\":\"updated\"}" +}] +``` + +- Если присутствует `master` → подписать текущим ключом **master**. +- Если `master` отсутствует → подписать текущим ключом **active**. +- `memo_key` всегда обязателен. + +--- + +## `account_metadata_operation` (ID 21) + +**Авторизация:** `regular` `account` + +Обновляет только JSON-метаданные аккаунта. Меньшая стоимость пропускной способности, чем у `account_update`. + +| Поле | Тип | Описание | +|------|-----|---------| +| `account` | `account_name_type` | Аккаунт для обновления | +| `json_metadata` | `string` | Новая строка JSON-метаданных | + +```json +[21, { + "account": "alice", + "json_metadata": "{\"name\":\"Alice\",\"about\":\"Hello!\"}" +}] +``` + +--- + +См. также: [Типы данных](../data-types.md), [Обзор операций](./overview.md). diff --git a/@l10n/ru/docs/protocol/operations/awards.md b/@l10n/ru/docs/protocol/operations/awards.md new file mode 100644 index 0000000000..24f923e01c --- /dev/null +++ b/@l10n/ru/docs/protocol/operations/awards.md @@ -0,0 +1,94 @@ +# Операции наград + +Награды — основной механизм социального вознаграждения. Аккаунт тратит *энергию*, чтобы наградить SHARES другого аккаунта из пула вознаграждений. Размер награды пропорционален расходу энергии инициатора и его стейку SHARES. + +**Энергия:** хранится в базисных пунктах 0–10000; восстанавливается до 100% за 24 часа (`CHAIN_ENERGY_REGENERATION_SECONDS = 86400`). + +--- + +## `award_operation` (ID 47) + +**Авторизация:** `regular` `initiator` + +Награждает `receiver` SHARES, тратя указанный процент энергии. Фактический объём SHARES определяется стейком инициатора и глубиной пула. + +| Поле | Тип | Описание | +|------|-----|---------| +| `initiator` | `account_name_type` | Аккаунт, дающий награду | +| `receiver` | `account_name_type` | Аккаунт, получающий награду | +| `energy` | `uint16_t` | Расходуемая энергия в базисных пунктах (1–10000) | +| `custom_sequence` | `uint64_t` | Порядковый номер, задаваемый приложением (может быть 0) | +| `memo` | `string` | Опциональное сообщение или причина | +| `beneficiaries` | `vector` | Опциональные бенефициары, получающие долю награды | + +```json +[47, { + "initiator": "alice", + "receiver": "bob", + "energy": 1000, + "custom_sequence": 0, + "memo": "great article!", + "beneficiaries": [] +}] +``` + +С бенефициарами: +```json +[47, { + "initiator": "alice", + "receiver": "bob", + "energy": 1000, + "custom_sequence": 1, + "memo": "", + "beneficiaries": [ + {"account": "charlie", "weight": 2000} + ] +}] +``` + +- `energy` = 10000 расходует 100% текущей энергии. +- При наличии бенефициаров `receiver` получает `(10000 − сумма_весов) / 10000` от награды. +- Сумма весов бенефициаров должна быть ≤ 10000; бенефициары должны быть отсортированы по имени аккаунта по возрастанию. +- Виртуальная операция `receive_award_operation` срабатывает для `receiver`. +- Виртуальная операция `benefactor_award_operation` срабатывает для каждого бенефициара. + +--- + +## `fixed_award_operation` (ID 60) + +**Авторизация:** `regular` `initiator` + +Награждает `receiver` **фиксированным количеством** SHARES. Энергия расходуется пропорционально желаемому размеру награды; `max_energy` ограничивает расход. + +| Поле | Тип | Описание | +|------|-----|---------| +| `initiator` | `account_name_type` | Аккаунт, дающий награду | +| `receiver` | `account_name_type` | Аккаунт, получающий награду | +| `reward_amount` | `asset` (SHARES) | Фиксированное количество SHARES для награждения | +| `max_energy` | `uint16_t` | Максимально расходуемая энергия (базисные пункты; 0 = без ограничения) | +| `custom_sequence` | `uint64_t` | Порядковый номер, задаваемый приложением | +| `memo` | `string` | Опциональное сообщение или причина | +| `beneficiaries` | `vector` | Опциональные бенефициары | + +```json +[60, { + "initiator": "alice", + "receiver": "bob", + "reward_amount": "10.000000 SHARES", + "max_energy": 5000, + "custom_sequence": 1, + "memo": "fixed reward", + "beneficiaries": [ + {"account": "charlie", "weight": 1000} + ] +}] +``` + +- `reward_amount.symbol` должен быть `SHARES`. +- `max_energy = 0` означает отсутствие ограничения на энергию — операция расходует столько энергии, сколько необходимо. +- Фактический расход энергии зависит от стейка инициатора и текущей глубины пула вознаграждений. +- Правила для бенефициаров идентичны `award_operation`. + +--- + +См. также: [Типы данных](../data-types.md), [Обзор операций](./overview.md), [Виртуальные операции](../virtual-operations.md). diff --git a/@l10n/ru/docs/protocol/operations/committee.md b/@l10n/ru/docs/protocol/operations/committee.md new file mode 100644 index 0000000000..3664fdc424 --- /dev/null +++ b/@l10n/ru/docs/protocol/operations/committee.md @@ -0,0 +1,103 @@ +# Операции комитета + +Система комитета (рабочие предложения) позволяет членам сообщества запрашивать финансирование из фонда комитета. Держатели SHARES голосуют за одобрение или отклонение запросов; одобренные запросы получают выплату из фонда. + +--- + +## `committee_worker_create_request_operation` (ID 35) + +**Авторизация:** `regular` `creator` + +Создаёт новый запрос на финансирование. При подаче с создателя взимается `committee_create_request_fee`. + +| Поле | Тип | Описание | +|------|-----|---------| +| `creator` | `account_name_type` | Аккаунт, создающий запрос | +| `url` | `string` | URL с описанием предложения (непустой, макс. 255 байт) | +| `worker` | `account_name_type` | Аккаунт, который получит выплату | +| `required_amount_min` | `asset` (VIZ) | Минимально приемлемая выплата | +| `required_amount_max` | `asset` (VIZ) | Максимально приемлемая выплата | +| `duration` | `uint32_t` | Длительность запроса в секундах | + +```json +[35, { + "creator": "alice", + "url": "https://alice.example.com/proposal", + "worker": "alice", + "required_amount_min": "100.000 VIZ", + "required_amount_max": "500.000 VIZ", + "duration": 604800 +}] +``` + +**Ограничения:** + +| Параметр | Значение | +|---------|---------| +| Минимальная длительность | 5 дней (432000 с) | +| Максимальная длительность | 30 дней (2592000 с) | +| `required_amount_max` | Должен быть > `required_amount_min` | + +- `required_amount_min` ≥ 0; `required_amount_max` > `required_amount_min`. +- `worker` может отличаться от `creator`. + +--- + +## `committee_worker_cancel_request_operation` (ID 36) + +**Авторизация:** `regular` `creator` + +Отменяет существующий запрос на финансирование до его истечения. + +| Поле | Тип | Описание | +|------|-----|---------| +| `creator` | `account_name_type` | Создатель запроса | +| `request_id` | `uint32_t` | ID запроса для отмены | + +```json +[36, { + "creator": "alice", + "request_id": 42 +}] +``` + +- Только `creator` запроса может его отменить. +- `request_id` должен ссылаться на существующий активный запрос. + +--- + +## `committee_vote_request_operation` (ID 37) + +**Авторизация:** `regular` `voter` + +Голосует за запрос на финансирование. Голосовая сила пропорциональна стейку SHARES голосующего. + +| Поле | Тип | Описание | +|------|-----|---------| +| `voter` | `account_name_type` | Аккаунт, отдающий голос | +| `request_id` | `uint32_t` | ID запроса | +| `vote_percent` | `int16_t` | Вес голоса в базисных пунктах (−10000 до 10000) | + +```json +[37, { + "voter": "bob", + "request_id": 42, + "vote_percent": 10000 +}] +``` + +- `vote_percent` > 0 → поддержка; `vote_percent` < 0 → возражение; `vote_percent` = 0 → снять голос. +- Запрос одобряется, когда взвешенный нетто-процент голосов ≥ свойству цепочки `committee_request_approve_min_percent`. + +**Виртуальные операции, вызываемые жизненным циклом комитета:** + +| Виртуальная операция | Триггер | +|--------------------|---------| +| `committee_cancel_request_operation` | Запрос истекает без одобрения | +| `committee_approve_request_operation` | Запрос достигает порога одобрения | +| `committee_payout_request_operation` | Обрабатывается выплата | +| `committee_pay_request_operation` | Работник получает оплату | + +--- + +См. также: [Типы данных](../data-types.md), [Обзор операций](./overview.md), [Виртуальные операции](../virtual-operations.md), [Управление комитетом](../../governance/committee.md). diff --git a/@l10n/ru/docs/protocol/operations/content.md b/@l10n/ru/docs/protocol/operations/content.md new file mode 100644 index 0000000000..031e1a8920 --- /dev/null +++ b/@l10n/ru/docs/protocol/operations/content.md @@ -0,0 +1,104 @@ +# Операции с контентом + +> **Уведомление об устаревании:** `vote_operation` (ID 0), `content_operation` (ID 1) и `delete_content_operation` (ID 9) **устарели**. Они остаются в варианте операций для исторической совместимости и совместимости с архивами, но не должны использоваться в новом коде. `custom_operation` (ID 10) — активный универсальный канал данных. + +--- + +## `content_operation` *(устарело)* (ID 1) + +**Авторизация:** `regular` `author` + +Создаёт или обновляет объект контента (пост или комментарий). Контент адресуется по `author` + `permlink`. + +| Поле | Тип | Описание | +|------|-----|---------| +| `parent_author` | `account_name_type` | Автор родительского контента; `""` для корневого поста | +| `parent_permlink` | `string` | Permlink родителя; служит тегом категории для корневых постов | +| `author` | `account_name_type` | Автор контента | +| `permlink` | `string` | Уникальный идентификатор в пространстве имён автора | +| `title` | `string` | Заголовок поста | +| `body` | `string` | Тело поста (Markdown) | +| `curation_percent` | `int16_t` | Доля вознаграждения за курацию в базисных пунктах (0–10000) | +| `json_metadata` | `string` | Строка JSON-метаданных | +| `extensions` | `content_extensions_type` | Опциональный список бенефициаров | + +Формат расширения бенефициаров (внутри `extensions`): +```json +[[0, { + "beneficiaries": [ + {"account": "bob", "weight": 2500} + ] +}]] +``` + +- `parent_author == ""` → корневой пост; иначе комментарий. +- `permlink` должен быть уникальным для каждого автора. +- `curation_percent` должен быть в диапазоне `[min_curation_percent, max_curation_percent]` цепочки. +- Веса бенефициаров должны суммироваться до ≤ 10000 и быть отсортированы по имени аккаунта по возрастанию. + +--- + +## `vote_operation` *(устарело)* (ID 0) + +**Авторизация:** `regular` `voter` + +Голосует с весом за единицу контента. + +| Поле | Тип | Описание | +|------|-----|---------| +| `voter` | `account_name_type` | Голосующий аккаунт | +| `author` | `account_name_type` | Автор контента | +| `permlink` | `string` | Permlink контента | +| `weight` | `int16_t` | Вес голоса: отрицательный = флаг, положительный = апвоут, 0 = снять голос | + +- Диапазон `weight`: −10000 до 10000. +- Голоса с флагом могут повлечь дополнительную стоимость энергии (`flag_energy_additional_cost` — свойство цепочки). + +--- + +## `delete_content_operation` *(устарело)* (ID 9) + +**Авторизация:** `regular` `author` + +Удаляет объект контента. + +| Поле | Тип | Описание | +|------|-----|---------| +| `author` | `account_name_type` | Автор контента | +| `permlink` | `string` | Permlink контента для удаления | + +- Контент с ожидающей выплатой не может быть удалён. + +--- + +## `custom_operation` (ID 10) + +**Авторизация:** `active` или `regular` подписантов (хотя бы один) + +Публикует произвольные JSON-данные в блокчейне. Используется приложениями для построения пользовательских on-chain протоколов. + +| Поле | Тип | Описание | +|------|-----|---------| +| `required_active_auths` | `flat_set` | Аккаунты, требующие подписи active-ключом | +| `required_regular_auths` | `flat_set` | Аккаунты, требующие подписи regular-ключом | +| `id` | `string` | Идентификатор пространства имён, определённый приложением (макс. 32 символа) | +| `json` | `string` | Корректная UTF-8 JSON-нагрузка | + +```json +[10, { + "required_active_auths": [], + "required_regular_auths": ["alice"], + "id": "my_app", + "json": "{\"action\":\"follow\",\"target\":\"bob\"}" +}] +``` + +- Хотя бы одно из `required_active_auths` или `required_regular_auths` должно быть непустым. +- Аккаунты в `required_active_auths` должны подписывать своим ключом **active**. +- Аккаунты в `required_regular_auths` должны подписывать своим ключом **regular**. +- Оба набора могут быть заполнены одновременно для операций с несколькими полномочиями. +- Поле `json` считается операцией с данными — может повлечь дополнительную стоимость пропускной способности (свойство цепочки `data_operations_cost_additional_bandwidth`). + +--- + +См. также: [Типы данных](../data-types.md), [Обзор операций](./overview.md), [Награды](./awards.md). diff --git a/@l10n/ru/docs/protocol/operations/escrow.md b/@l10n/ru/docs/protocol/operations/escrow.md new file mode 100644 index 0000000000..2e53b6c3f6 --- /dev/null +++ b/@l10n/ru/docs/protocol/operations/escrow.md @@ -0,0 +1,158 @@ +# Операции эскроу + +Эскроу удерживает токены VIZ в условном переводе: средства высвобождаются только после одобрения как получателем, так и нейтральным агентом, либо арбитрируются агентом в случае спора. + +**Процесс эскроу:** +``` +escrow_transfer → escrow_approve (обоими "to" и "agent") + → escrow_release (от "from" или "to") + → [escrow_dispute] → escrow_release (только от "agent") + ↓ + (срок подтверждения пропущен) + → expire_escrow_ratification_operation [виртуальная — средства возвращаются] +``` + +--- + +## `escrow_transfer_operation` (ID 15) + +**Авторизация:** `active` `from` + +Создаёт эскроу. Средства немедленно уходят от `from` на баланс эскроу; для высвобождения необходимо одобрение и `agent`, и `to`. + +| Поле | Тип | Описание | +|------|-----|---------| +| `from` | `account_name_type` | Отправитель | +| `to` | `account_name_type` | Предполагаемый получатель | +| `agent` | `account_name_type` | Нейтральный агент эскроу (арбитр) | +| `escrow_id` | `uint32_t` | Уникальный ID, выбранный отправителем (по умолч. 30) | +| `token_amount` | `asset` (VIZ) | Сумма для удержания в эскроу | +| `fee` | `asset` (VIZ) | Комиссия агента — выплачивается агенту при одобрении | +| `ratification_deadline` | `time_point_sec` | Срок для одобрения обеими сторонами | +| `escrow_expiration` | `time_point_sec` | Истечение, если эскроу так и не будет высвобождено | +| `json_metadata` | `string` | Опциональные условия / метаданные | + +```json +[15, { + "from": "alice", + "to": "bob", + "agent": "charlie", + "escrow_id": 1001, + "token_amount": "100.000 VIZ", + "fee": "1.000 VIZ", + "ratification_deadline": "2024-06-01T00:00:00", + "escrow_expiration": "2024-07-01T00:00:00", + "json_metadata": "{\"description\":\"payment for work\"}" +}] +``` + +- `ratification_deadline` должен быть раньше `escrow_expiration`. +- Обе временные метки должны быть в будущем на момент трансляции. +- `escrow_id` должен быть уникальным для аккаунта `from`. +- Если не одобрено до `ratification_deadline`, срабатывает виртуальная `expire_escrow_ratification_operation`, и средства возвращаются `from`. + +--- + +## `escrow_approve_operation` (ID 18) + +**Авторизация:** `active` `who` + +Одобряет или отклоняет эскроу. Для активации эскроу необходимо одобрение и `to`, и `agent`. + +| Поле | Тип | Описание | +|------|-----|---------| +| `from` | `account_name_type` | Исходный отправитель эскроу | +| `to` | `account_name_type` | Исходный получатель эскроу | +| `agent` | `account_name_type` | Агент эскроу | +| `who` | `account_name_type` | Кто одобряет: должен быть `to` или `agent` | +| `escrow_id` | `uint32_t` | ID эскроу | +| `approve` | `bool` | `true` для одобрения, `false` для отклонения | + +```json +[18, { + "from": "alice", + "to": "bob", + "agent": "charlie", + "who": "bob", + "escrow_id": 1001, + "approve": true +}] +``` + +- `who` должен быть либо `to`, либо `agent`. +- После одобрения не может быть отозвано. +- Если `approve: false` — эскроу отменяется и средства возвращаются `from`. +- Должно быть транслировано до `ratification_deadline`. + +--- + +## `escrow_dispute_operation` (ID 16) + +**Авторизация:** `active` `who` + +Открывает спор по одобренному эскроу. После спора только `agent` может высвобождать средства. + +| Поле | Тип | Описание | +|------|-----|---------| +| `from` | `account_name_type` | Исходный отправитель эскроу | +| `to` | `account_name_type` | Исходный получатель эскроу | +| `agent` | `account_name_type` | Агент эскроу | +| `who` | `account_name_type` | Кто открывает спор: должен быть `from` или `to` | +| `escrow_id` | `uint32_t` | ID эскроу | + +```json +[16, { + "from": "alice", + "to": "bob", + "agent": "charlie", + "who": "alice", + "escrow_id": 1001 +}] +``` + +- Может быть открыт только по **одобренному** эскроу (оба `to` и `agent` одобрили). +- Должен быть открыт до `escrow_expiration`. + +--- + +## `escrow_release_operation` (ID 17) + +**Авторизация:** `active` `who` + +Высвобождает средства эскроу `receiver`. Частичные высвобождения допускаются. + +| Поле | Тип | Описание | +|------|-----|---------| +| `from` | `account_name_type` | Исходный отправитель эскроу | +| `to` | `account_name_type` | Исходный получатель эскроу | +| `agent` | `account_name_type` | Агент эскроу | +| `who` | `account_name_type` | Аккаунт, высвобождающий средства | +| `receiver` | `account_name_type` | Аккаунт, получающий средства (должен быть `from` или `to`) | +| `escrow_id` | `uint32_t` | ID эскроу | +| `token_amount` | `asset` (VIZ) | Сумма для высвобождения (может быть частичной) | + +```json +[17, { + "from": "alice", + "to": "bob", + "agent": "charlie", + "who": "alice", + "receiver": "bob", + "escrow_id": 1001, + "token_amount": "100.000 VIZ" +}] +``` + +**Правила разрешения на высвобождение:** + +| Состояние | Кто может высвобождать | Кому | +|----------|----------------------|------| +| Без спора, до истечения | `from` или `to` | Другой стороне | +| Без спора, после истечения | `from` или `to` | Любой стороне | +| В споре | Только `agent` | Любой стороне | + +- Частичные высвобождения допускаются; остаток остаётся в эскроу. + +--- + +См. также: [Типы данных](../data-types.md), [Обзор операций](./overview.md), [Виртуальные операции](../virtual-operations.md). diff --git a/@l10n/ru/docs/protocol/operations/invites.md b/@l10n/ru/docs/protocol/operations/invites.md new file mode 100644 index 0000000000..02b3b0c5b3 --- /dev/null +++ b/@l10n/ru/docs/protocol/operations/invites.md @@ -0,0 +1,117 @@ +# Операции инвайтов + +Инвайты позволяют существующим пользователям VIZ привлекать новые аккаунты без необходимости иметь предварительно существующий аккаунт у получателя. Инвайт — это одноразовая пара ключей, финансируемая токенами VIZ; приватный ключ служит секретом инвайта. + +**Процесс инвайта:** +``` +create_invite → invite_registration_operation (создать новый аккаунт) + → claim_invite_balance_operation (зачислить баланс существующему аккаунту) + → use_invite_balance_operation (альтернативное получение) +``` + +--- + +## `create_invite_operation` (ID 43) + +**Авторизация:** `active` `creator` + +Создаёт инвайт, генерируя ключ и блокируя токены VIZ в нём. + +| Поле | Тип | Описание | +|------|-----|---------| +| `creator` | `account_name_type` | Аккаунт, создающий инвайт | +| `balance` | `asset` (VIZ) | VIZ для блокировки в инвайте | +| `invite_key` | `public_key_type` | Публичный ключ пары ключей инвайта | + +```json +[43, { + "creator": "alice", + "balance": "5.000 VIZ", + "invite_key": "VIZ5invite..." +}] +``` + +- Сгенерируйте случайную пару ключей secp256k1. **Публичный ключ** идёт в `invite_key`; **приватный ключ** (WIF) становится секретом инвайта для передачи. +- `balance` должен быть ≥ свойству цепочки `create_invite_min_balance`. + +--- + +## `claim_invite_balance_operation` (ID 44) + +**Авторизация:** `active` `initiator` + +Получает баланс VIZ из инвайта, переводя его `receiver`. Инвайт используется и не может быть применён повторно. + +| Поле | Тип | Описание | +|------|-----|---------| +| `initiator` | `account_name_type` | Существующий аккаунт, получающий инвайт | +| `receiver` | `account_name_type` | Аккаунт, получающий баланс VIZ | +| `invite_secret` | `string` | WIF-приватный ключ инвайта | + +```json +[44, { + "initiator": "bob", + "receiver": "bob", + "invite_secret": "5Ky1MXn..." +}] +``` + +- `receiver` может отличаться от `initiator` — баланс может быть перенаправлен. +- `invite_secret` — приватный ключ пары ключей инвайта в кодировке WIF. + +--- + +## `invite_registration_operation` (ID 45) + +**Авторизация:** `active` `initiator` + +Использует инвайт для создания нового аккаунта в блокчейне. Баланс инвайта конвертируется в SHARES и назначается новому аккаунту. + +| Поле | Тип | Описание | +|------|-----|---------| +| `initiator` | `account_name_type` | Существующий аккаунт, инициирующий регистрацию | +| `new_account_name` | `account_name_type` | Имя нового аккаунта | +| `invite_secret` | `string` | WIF-приватный ключ инвайта | +| `new_account_key` | `public_key_type` | Ключ, устанавливаемый как master, active, regular и memo для нового аккаунта | + +```json +[45, { + "initiator": "bob", + "new_account_name": "carol", + "invite_secret": "5Ky1MXn...", + "new_account_key": "VIZ5newacct..." +}] +``` + +- `new_account_key` применяется ко всем четырём слотам authority (master, active, regular, memo). +- Баланс инвайта конвертируется в SHARES (не ликвидные VIZ) для нового аккаунта. +- Инвайт используется после применения. + +--- + +## `use_invite_balance_operation` (ID 58) + +**Авторизация:** `active` `initiator` + +Альтернативное получение инвайта, которое может конвертировать баланс в SHARES для получателя вместо ликвидных VIZ. + +| Поле | Тип | Описание | +|------|-----|---------| +| `initiator` | `account_name_type` | Аккаунт, использующий инвайт | +| `receiver` | `account_name_type` | Существующий аккаунт, получающий баланс | +| `invite_secret` | `string` | WIF-приватный ключ инвайта | + +```json +[58, { + "initiator": "bob", + "receiver": "bob", + "invite_secret": "5Ky1MXn..." +}] +``` + +- `receiver` должен быть существующим аккаунтом. +- Инвайт используется после применения. + +--- + +См. также: [Типы данных](../data-types.md), [Обзор операций](./overview.md), [Аккаунты](./accounts.md). diff --git a/@l10n/ru/docs/protocol/operations/overview.md b/@l10n/ru/docs/protocol/operations/overview.md new file mode 100644 index 0000000000..2379bdcd86 --- /dev/null +++ b/@l10n/ru/docs/protocol/operations/overview.md @@ -0,0 +1,111 @@ +# Обзор операций + +Операции VIZ Ledger — это атомарные действия изменения состояния, включаемые в транзакции. Каждая операция сериализуется как 2-элементный массив `[type_id, object]` внутри подписанной транзакции. + +--- + +## Обычные операции + +Это инициируемые пользователями операции, которые могут быть транслированы в сеть. + +| ID | Операция | Уровень авторизации | Справочник | +|----|---------|---------------------|----------| +| 0 | `vote_operation` *(устарела)* | regular | [Контент](./content.md) | +| 1 | `content_operation` *(устарела)* | regular | [Контент](./content.md) | +| 2 | `transfer_operation` | active (VIZ) / master (SHARES) | [Переводы](./transfers.md) | +| 3 | `transfer_to_vesting_operation` | active | [Переводы](./transfers.md) | +| 4 | `withdraw_vesting_operation` | active | [Переводы](./transfers.md) | +| 5 | `account_update_operation` | master / active | [Аккаунты](./accounts.md) | +| 6 | `witness_update_operation` | active | [Валидаторы](./validators.md) | +| 7 | `account_witness_vote_operation` | active | [Валидаторы](./validators.md) | +| 8 | `account_witness_proxy_operation` | active | [Валидаторы](./validators.md) | +| 9 | `delete_content_operation` *(устарела)* | regular | [Контент](./content.md) | +| 10 | `custom_operation` | active / regular | [Контент](./content.md) | +| 11 | `set_withdraw_vesting_route_operation` | active | [Переводы](./transfers.md) | +| 12 | `request_account_recovery_operation` | active | [Восстановление](./recovery.md) | +| 13 | `recover_account_operation` | master (×2) | [Восстановление](./recovery.md) | +| 14 | `change_recovery_account_operation` | master | [Восстановление](./recovery.md) | +| 15 | `escrow_transfer_operation` | active | [Эскроу](./escrow.md) | +| 16 | `escrow_dispute_operation` | active | [Эскроу](./escrow.md) | +| 17 | `escrow_release_operation` | active | [Эскроу](./escrow.md) | +| 18 | `escrow_approve_operation` | active | [Эскроу](./escrow.md) | +| 19 | `delegate_vesting_shares_operation` | active | [Переводы](./transfers.md) | +| 20 | `account_create_operation` | active | [Аккаунты](./accounts.md) | +| 21 | `account_metadata_operation` | regular | [Аккаунты](./accounts.md) | +| 22 | `proposal_create_operation` | active | [Предложения](./proposals.md) | +| 23 | `proposal_update_operation` | varies | [Предложения](./proposals.md) | +| 24 | `proposal_delete_operation` | active | [Предложения](./proposals.md) | +| 25 | `chain_properties_update_operation` | active | [Валидаторы](./validators.md) | +| 35 | `committee_worker_create_request_operation` | regular | [Комитет](./committee.md) | +| 36 | `committee_worker_cancel_request_operation` | regular | [Комитет](./committee.md) | +| 37 | `committee_vote_request_operation` | regular | [Комитет](./committee.md) | +| 43 | `create_invite_operation` | active | [Инвайты](./invites.md) | +| 44 | `claim_invite_balance_operation` | active | [Инвайты](./invites.md) | +| 45 | `invite_registration_operation` | active | [Инвайты](./invites.md) | +| 46 | `versioned_chain_properties_update_operation` | active | [Валидаторы](./validators.md) | +| 47 | `award_operation` | regular | [Награды](./awards.md) | +| 50 | `set_paid_subscription_operation` | active | [Подписки](./subscriptions.md) | +| 51 | `paid_subscribe_operation` | active | [Подписки](./subscriptions.md) | +| 54 | `set_account_price_operation` | master | [Рынок аккаунтов](./account-market.md) | +| 55 | `set_subaccount_price_operation` | master | [Рынок аккаунтов](./account-market.md) | +| 56 | `buy_account_operation` | active | [Рынок аккаунтов](./account-market.md) | +| 58 | `use_invite_balance_operation` | active | [Инвайты](./invites.md) | +| 60 | `fixed_award_operation` | regular | [Награды](./awards.md) | +| 61 | `target_account_sale_operation` | master | [Рынок аккаунтов](./account-market.md) | + +--- + +## Виртуальные операции + +Виртуальные операции генерируются самим блокчейном во время обработки блоков. Они никогда не транслируются пользователями — они появляются в истории аккаунта и данных блоков исключительно в информационных целях. + +| ID | Операция | Триггер | Справочник | +|----|---------|--------|----------| +| 26 | `author_reward_operation` | Выплата за контент | [Виртуальные операции](../virtual-operations.md) | +| 27 | `curation_reward_operation` | Выплата за контент | [Виртуальные операции](../virtual-operations.md) | +| 28 | `content_reward_operation` | Выплата за контент | [Виртуальные операции](../virtual-operations.md) | +| 29 | `fill_vesting_withdraw_operation` | Срабатывание интервала вывода | [Виртуальные операции](../virtual-operations.md) | +| 30 | `shutdown_witness_operation` | Деактивация валидатора | [Виртуальные операции](../virtual-operations.md) | +| 31 | `hardfork_operation` | Активация хардфорка | [Виртуальные операции](../virtual-operations.md) | +| 32 | `content_payout_update_operation` | Обновление выплаты за контент | [Виртуальные операции](../virtual-operations.md) | +| 33 | `content_benefactor_reward_operation` | Выплата за контент | [Виртуальные операции](../virtual-operations.md) | +| 34 | `return_vesting_delegation_operation` | Завершение периода возврата делегирования | [Виртуальные операции](../virtual-operations.md) | +| 38 | `committee_cancel_request_operation` | Истечение заявки комитета | [Виртуальные операции](../virtual-operations.md) | +| 39 | `committee_approve_request_operation` | Одобрение заявки комитета | [Виртуальные операции](../virtual-operations.md) | +| 40 | `committee_payout_request_operation` | Обработка выплаты комитета | [Виртуальные операции](../virtual-operations.md) | +| 41 | `committee_pay_request_operation` | Оплата работнику комитета | [Виртуальные операции](../virtual-operations.md) | +| 42 | `witness_reward_operation` | Произведён блок | [Виртуальные операции](../virtual-operations.md) | +| 48 | `receive_award_operation` | Получена награда | [Виртуальные операции](../virtual-operations.md) | +| 49 | `benefactor_award_operation` | Награда с бенефициаром | [Виртуальные операции](../virtual-operations.md) | +| 52 | `paid_subscription_action_operation` | Оплата подписки | [Виртуальные операции](../virtual-operations.md) | +| 53 | `cancel_paid_subscription_operation` | Отмена/истечение подписки | [Виртуальные операции](../virtual-operations.md) | +| 57 | `account_sale_operation` | Продан аккаунт | [Виртуальные операции](../virtual-operations.md) | +| 59 | `expire_escrow_ratification_operation` | Истёк дедлайн эскроу | [Виртуальные операции](../virtual-operations.md) | +| 62 | `bid_operation` | Сделана ставка на аукционе | [Виртуальные операции](../virtual-operations.md) | +| 63 | `outbid_operation` | Перебитая ставка на аукционе | [Виртуальные операции](../virtual-operations.md) | + +--- + +## Построение транзакции + +```json +{ + "ref_block_num": 12345, + "ref_block_prefix": 678901234, + "expiration": "2024-01-15T12:01:00", + "operations": [ + [2, { "from": "alice", "to": "bob", "amount": "1.000 VIZ", "memo": "" }] + ], + "extensions": [], + "signatures": ["1f2a3b..."] +} +``` + +- `ref_block_num` = `head_block_number & 0xFFFF` +- `ref_block_prefix` = байты 4–7 `block_id` в виде little-endian `uint32` +- `expiration` = текущее UTC-время + TTL (рекомендуется не более 60 секунд) +- Подпись: `sha256(chain_id || serialized_tx)` → компактная ECDSA-подпись secp256k1 + +--- + +См. также: [Типы данных](../data-types.md), [Виртуальные операции](../virtual-operations.md), [JSON-RPC API](../../api/json-rpc.md). diff --git a/@l10n/ru/docs/protocol/operations/proposals.md b/@l10n/ru/docs/protocol/operations/proposals.md new file mode 100644 index 0000000000..be1e1fd4da --- /dev/null +++ b/@l10n/ru/docs/protocol/operations/proposals.md @@ -0,0 +1,125 @@ +# Операции предложений + +Предложения обеспечивают мультиподписное управление: один аккаунт создаёт набор операций, требующий одобрения от заданного набора подписантов перед исполнением. Предложение исполняется автоматически при получении достаточного числа одобрений. + +--- + +## `proposal_create_operation` (ID 22) + +**Авторизация:** `active` `author` + +Создаёт транзакционное предложение. Предложение идентифицируется парой `author` + `title`. + +| Поле | Тип | Описание | +|------|-----|---------| +| `author` | `account_name_type` | Аккаунт, создающий предложение | +| `title` | `string` | Уникальное название для данного автора (используется как ID предложения) | +| `memo` | `string` | Описание в читаемом виде | +| `expiration_time` | `time_point_sec` | Время истечения предложения | +| `proposed_operations` | `vector` | Операции для исполнения при одобрении | +| `review_period_time` | `optional` | Опционально: новые одобрения не принимаются после этого времени | +| `extensions` | `extensions_type` | Всегда `[]` | + +Каждый элемент `proposed_operations` — это `operation_wrapper`: +```json +{"op": [type_id, operation_object]} +``` + +```json +[22, { + "author": "alice", + "title": "transfer-proposal-001", + "memo": "Joint transfer to shared fund", + "expiration_time": "2024-12-31T23:59:59", + "proposed_operations": [ + { + "op": [2, { + "from": "multisig-wallet", + "to": "fund", + "amount": "1000.000 VIZ", + "memo": "" + }] + } + ], + "review_period_time": null, + "extensions": [] +}] +``` + +- `title` должен быть уникальным для каждого `author`. +- `expiration_time` должен быть в будущем. +- Если задан `review_period_time`, он должен быть раньше `expiration_time`; новые одобрения после этого момента не принимаются. +- `proposed_operations` может содержать несколько операций любого типа. + +--- + +## `proposal_update_operation` (ID 23) + +**Авторизация:** Зависит от изменяемых наборов одобрений + +Добавляет или удаляет одобрения. Предложение исполняется автоматически при наборе достаточного количества одобрений. + +| Поле | Тип | Описание | +|------|-----|---------| +| `author` | `account_name_type` | Автор предложения | +| `title` | `string` | Название предложения | +| `active_approvals_to_add` | `flat_set` | Аккаунты, дающие active-одобрение | +| `active_approvals_to_remove` | `flat_set` | Аккаунты, отзывающие active-одобрение | +| `master_approvals_to_add` | `flat_set` | Аккаунты, дающие master-одобрение | +| `master_approvals_to_remove` | `flat_set` | Аккаунты, отзывающие master-одобрение | +| `regular_approvals_to_add` | `flat_set` | Аккаунты, дающие regular-одобрение | +| `regular_approvals_to_remove` | `flat_set` | Аккаунты, отзывающие regular-одобрение | +| `key_approvals_to_add` | `flat_set` | Публичные ключи, дающие одобрение | +| `key_approvals_to_remove` | `flat_set` | Публичные ключи, отзывающие одобрение | +| `extensions` | `extensions_type` | Всегда `[]` | + +```json +[23, { + "author": "alice", + "title": "transfer-proposal-001", + "active_approvals_to_add": ["bob"], + "active_approvals_to_remove": [], + "master_approvals_to_add": [], + "master_approvals_to_remove": [], + "regular_approvals_to_add": [], + "regular_approvals_to_remove": [], + "key_approvals_to_add": [], + "key_approvals_to_remove": [], + "extensions": [] +}] +``` + +- Транзакция должна быть подписана ключами, соответствующими добавляемым или удаляемым одобрениям. +- Все поля `*_to_add` и `*_to_remove` по умолчанию равны `[]`, если не нужны. +- После исполнения предложение считается завершённым; дальнейшие обновления отклоняются. + +--- + +## `proposal_delete_operation` (ID 24) + +**Авторизация:** `active` `requester` + +Безвозвратно удаляет (накладывает вето на) предложение. Может быть вызвана любым требуемым authority в данном предложении. + +| Поле | Тип | Описание | +|------|-----|---------| +| `author` | `account_name_type` | Автор предложения | +| `title` | `string` | Название предложения | +| `requester` | `account_name_type` | Аккаунт, запрашивающий удаление | +| `extensions` | `extensions_type` | Всегда `[]` | + +```json +[24, { + "author": "alice", + "title": "transfer-proposal-001", + "requester": "bob", + "extensions": [] +}] +``` + +- `requester` должен быть требуемым authority в данном предложении. +- Удаление необратимо. + +--- + +См. также: [Типы данных](../data-types.md), [Обзор операций](./overview.md), [Database API — get_proposed_transactions](../../plugins/database-api.md#get_proposed_transactionsaccount-from-limit). diff --git a/@l10n/ru/docs/protocol/operations/recovery.md b/@l10n/ru/docs/protocol/operations/recovery.md new file mode 100644 index 0000000000..8ac0ef460d --- /dev/null +++ b/@l10n/ru/docs/protocol/operations/recovery.md @@ -0,0 +1,106 @@ +# Операции восстановления аккаунта + +Механизм восстановления позволяет заранее назначенному доверенному аккаунту (*аккаунту восстановления*) помочь восстановить доступ к скомпрометированному аккаунту, используя недавнюю действительную master authority. + +**Процесс восстановления:** +``` +request_account_recovery → recover_account (в течение 24 часов) +change_recovery_account (задержка 30 дней перед вступлением в силу) +``` + +--- + +## `request_account_recovery_operation` (ID 12) + +**Авторизация:** `active` `recovery_account` + +Инициирует запрос восстановления аккаунта. Аккаунт восстановления предлагает новую master authority для скомпрометированного аккаунта; у владельца аккаунта есть 24 часа для подтверждения через `recover_account_operation`. + +| Поле | Тип | Описание | +|------|-----|---------| +| `recovery_account` | `account_name_type` | Доверенный аккаунт восстановления | +| `account_to_recover` | `account_name_type` | Скомпрометированный аккаунт для восстановления | +| `new_master_authority` | `authority` | Новая master authority для назначения после подтверждения | +| `extensions` | `extensions_type` | Всегда `[]` | + +```json +[12, { + "recovery_account": "recover-service", + "account_to_recover": "alice", + "new_master_authority": { + "weight_threshold": 1, + "account_auths": [], + "key_auths": [["VIZ5newkey...", 1]] + }, + "extensions": [] +}] +``` + +- Только назначенный аккаунт восстановления `account_to_recover` может отправить это. +- Разрешён только один активный запрос на восстановление на аккаунт; повторная отправка обновляет запрос и сбрасывает 24-часовое окно. +- Для отмены: установите `new_master_authority.weight_threshold` в `0`. + +--- + +## `recover_account_operation` (ID 13) + +**Авторизация:** Подписи, удовлетворяющие **и** `new_master_authority`, **и** `recent_master_authority` + +Подтверждает восстановление, доказывая предыдущее владение. Должна быть транслирована в течение 24 часов после запроса восстановления. + +| Поле | Тип | Описание | +|------|-----|---------| +| `account_to_recover` | `account_name_type` | Восстанавливаемый аккаунт | +| `new_master_authority` | `authority` | Новая master authority (должна точно совпадать с запросом на восстановление) | +| `recent_master_authority` | `authority` | Master authority, действовавшая в течение последних 30 дней | +| `extensions` | `extensions_type` | Всегда `[]` | + +```json +[13, { + "account_to_recover": "alice", + "new_master_authority": { + "weight_threshold": 1, + "account_auths": [], + "key_auths": [["VIZ5newkey...", 1]] + }, + "recent_master_authority": { + "weight_threshold": 1, + "account_auths": [], + "key_auths": [["VIZ5oldkey...", 1]] + }, + "extensions": [] +}] +``` + +- Транзакция должна быть подписана ключами, одновременно удовлетворяющими **обеим** authority — новой и недавней. +- `new_master_authority` должна точно совпадать с указанной в ожидающем запросе на восстановление. +- После восстановления старый master ключ становится недействительным. + +--- + +## `change_recovery_account_operation` (ID 14) + +**Авторизация:** `master` `account_to_recover` + +Изменяет аккаунт восстановления. Изменение вступает в силу после **30-дневной задержки**, чтобы предотвратить подмену аккаунта восстановления злоумышленником во время активной атаки. + +| Поле | Тип | Описание | +|------|-----|---------| +| `account_to_recover` | `account_name_type` | Аккаунт, меняющий свой аккаунт восстановления | +| `new_recovery_account` | `account_name_type` | Новое имя аккаунта восстановления | +| `extensions` | `extensions_type` | Всегда `[]` | + +```json +[14, { + "account_to_recover": "alice", + "new_recovery_account": "new-recovery-service", + "extensions": [] +}] +``` + +- `new_recovery_account` должен быть существующим аккаунтом. +- Если `new_recovery_account` равен `""`, аккаунтом восстановления становится валидатор с наибольшим количеством голосов. + +--- + +См. также: [Типы данных](../data-types.md), [Обзор операций](./overview.md), [Аккаунты](./accounts.md). diff --git a/@l10n/ru/docs/protocol/operations/subscriptions.md b/@l10n/ru/docs/protocol/operations/subscriptions.md new file mode 100644 index 0000000000..9de11c3829 --- /dev/null +++ b/@l10n/ru/docs/protocol/operations/subscriptions.md @@ -0,0 +1,77 @@ +# Операции платных подписок + +Платные подписки позволяют аккаунтам предлагать многоуровневые периодические услуги с оплатой в токенах VIZ и опциональным автопродлением. + +--- + +## `set_paid_subscription_operation` (ID 50) + +**Авторизация:** `active` `account` + +Создаёт или обновляет предложение подписки. При первом создании взимается `create_paid_subscription_fee`. + +| Поле | Тип | Описание | +|------|-----|---------| +| `account` | `account_name_type` | Аккаунт, предлагающий подписку | +| `url` | `string` | URL с деталями подписки | +| `levels` | `uint16_t` | Количество уровней подписки (≥ 1) | +| `amount` | `asset` (VIZ) | Базовая цена за период на единицу уровня | +| `period` | `uint16_t` | Период подписки в днях (≥ 1) | + +```json +[50, { + "account": "alice", + "url": "https://alice.example.com/subscribe", + "levels": 3, + "amount": "10.000 VIZ", + "period": 30 +}] +``` + +- Фактическая стоимость для подписчика = `amount × level`. +- `levels = 3` при `amount = "10.000 VIZ"` → уровень 1 стоит 10 VIZ, уровень 2 — 20 VIZ, уровень 3 — 30 VIZ за период. +- Обновление операции изменяет параметры для будущих подписок; существующие активные подписки продолжаются на прежних условиях до продления. + +--- + +## `paid_subscribe_operation` (ID 51) + +**Авторизация:** `active` `subscriber` + +Оформляет или продлевает платную подписку. Токены немедленно переводятся от `subscriber` к `account`. + +| Поле | Тип | Описание | +|------|-----|---------| +| `subscriber` | `account_name_type` | Подписывающийся аккаунт | +| `account` | `account_name_type` | Аккаунт, предлагающий подписку | +| `level` | `uint16_t` | Уровень подписки (1 – `levels`) | +| `amount` | `asset` (VIZ) | Сумма платежа | +| `period` | `uint16_t` | Количество оплачиваемых периодов | +| `auto_renewal` | `bool` | Включить автоматическое продление каждый период | + +```json +[51, { + "subscriber": "bob", + "account": "alice", + "level": 2, + "amount": "20.000 VIZ", + "period": 1, + "auto_renewal": true +}] +``` + +- `amount` должна точно совпадать с `subscription.amount × level × period`. +- `level` должен быть в диапазоне [1, `subscription.levels`]. +- `auto_renewal: true` — токены автоматически списываются каждый период при наличии достаточного баланса. +- `auto_renewal: false` — разовая подписка; истекает после оплаченного периода. + +**Виртуальные операции:** + +| Виртуальная операция | Триггер | +|--------------------|---------| +| `paid_subscription_action_operation` | Платёж обработан | +| `cancel_paid_subscription_operation` | Подписка истекла или отменена | + +--- + +См. также: [Типы данных](../data-types.md), [Обзор операций](./overview.md), [Виртуальные операции](../virtual-operations.md). diff --git a/@l10n/ru/docs/protocol/operations/transfers.md b/@l10n/ru/docs/protocol/operations/transfers.md new file mode 100644 index 0000000000..1bc045ed3a --- /dev/null +++ b/@l10n/ru/docs/protocol/operations/transfers.md @@ -0,0 +1,136 @@ +# Операции переводов + +--- + +## `transfer_operation` (ID 2) + +**Авторизация:** `active` `from` (VIZ) / `master` `from` (SHARES) + +Переводит токены VIZ или SHARES между аккаунтами. + +| Поле | Тип | Описание | +|------|-----|---------| +| `from` | `account_name_type` | Отправляющий аккаунт | +| `to` | `account_name_type` | Получающий аккаунт | +| `amount` | `asset` | Сумма перевода (VIZ или SHARES) | +| `memo` | `string` | Текст memo (открытый или зашифрованный; может быть `""`) | + +```json +[2, { + "from": "alice", + "to": "bob", + "amount": "10.000 VIZ", + "memo": "payment for services" +}] +``` + +- `amount.symbol` должен быть `VIZ` или `SHARES`. +- Переводы VIZ требуют полномочия **active**; переводы SHARES требуют полномочия **master**. +- Формат зашифрованного memo: `#`, за которым следует ciphertext в кодировке base58. + +--- + +## `transfer_to_vesting_operation` (ID 3) + +**Авторизация:** `active` `from` + +Конвертирует ликвидные VIZ в SHARES (стейкинг). SHARES могут быть начислены другому аккаунту. + +| Поле | Тип | Описание | +|------|-----|---------| +| `from` | `account_name_type` | Аккаунт, предоставляющий VIZ | +| `to` | `account_name_type` | Аккаунт, получающий SHARES (может совпадать с `from`) | +| `amount` | `asset` (VIZ) | Количество VIZ для стейкинга | + +```json +[3, { + "from": "alice", + "to": "alice", + "amount": "100.000 VIZ" +}] +``` + +- `amount.symbol` должен быть `VIZ`. +- `to` может быть любым существующим аккаунтом — полезно для подарка застейканных SHARES. + +--- + +## `withdraw_vesting_operation` (ID 4) + +**Авторизация:** `active` `account` + +Инициирует постепенный вывод SHARES обратно в ликвидные VIZ через несколько интервалов. + +| Поле | Тип | Описание | +|------|-----|---------| +| `account` | `account_name_type` | Аккаунт, инициирующий вывод | +| `vesting_shares` | `asset` (SHARES) | Суммарные SHARES для вывода; `0.000000 SHARES` отменяет | + +```json +[4, { + "account": "alice", + "vesting_shares": "1000.000000 SHARES" +}] +``` + +- Вывод распределяется на `withdraw_intervals` интервалов (свойство цепочки, по умолч. 28). +- Каждый интервал: конвертируется `vesting_shares / withdraw_intervals` SHARES. +- Установите `"0.000000 SHARES"` для отмены активного вывода. + +--- + +## `set_withdraw_vesting_route_operation` (ID 11) + +**Авторизация:** `active` `from_account` + +Направляет процент выводимых vesting средств на указанный аккаунт с опциональным повторным вестингом направленной части. + +| Поле | Тип | Описание | +|------|-----|---------| +| `from_account` | `account_name_type` | Аккаунт, выводы которого перенаправляются | +| `to_account` | `account_name_type` | Аккаунт-получатель | +| `percent` | `uint16_t` | Процент для маршрутизации (0–10000 базисных пунктов) | +| `auto_vest` | `bool` | Если `true`, направленные токены немедленно снова вестируются в `to_account` | + +```json +[11, { + "from_account": "alice", + "to_account": "bob", + "percent": 5000, + "auto_vest": false +}] +``` + +- `percent` = 0 удаляет этот маршрут к `to_account`. +- Сумма всех маршрутов от `from_account` не должна превышать 10000. +- Допускаются несколько маршрутов к разным аккаунтам. + +--- + +## `delegate_vesting_shares_operation` (ID 19) + +**Авторизация:** `active` `delegator` + +Делегирует SHARES другому аккаунту. Делегат получает пропускную способность и голосовую силу; владение остаётся у делегирующего. + +| Поле | Тип | Описание | +|------|-----|---------| +| `delegator` | `account_name_type` | Аккаунт, делегирующий SHARES | +| `delegatee` | `account_name_type` | Аккаунт, получающий делегирование | +| `vesting_shares` | `asset` (SHARES) | Сумма делегирования; `0.000000 SHARES` удаляет делегирование | + +```json +[19, { + "delegator": "alice", + "delegatee": "bob", + "vesting_shares": "500.000000 SHARES" +}] +``` + +- `vesting_shares` должен быть ≥ свойства цепочки `min_delegation`, или ровно `0.000000 SHARES` для удаления. +- При удалении делегирования SHARES входят в 7-дневное окно возврата перед зачислением обратно. +- Виртуальная `return_vesting_delegation_operation` срабатывает по окончании окна возврата. + +--- + +См. также: [Типы данных](../data-types.md), [Обзор операций](./overview.md). diff --git a/@l10n/ru/docs/protocol/operations/validators.md b/@l10n/ru/docs/protocol/operations/validators.md new file mode 100644 index 0000000000..b8b41cb2c2 --- /dev/null +++ b/@l10n/ru/docs/protocol/operations/validators.md @@ -0,0 +1,166 @@ +# Операции валидаторов + +--- + +## `witness_update_operation` (ID 6) + +**Авторизация:** `active` `owner` + +Регистрирует или обновляет узел валидатора. Установка `block_signing_key` в нулевой ключ удаляет валидатора из производства блоков. + +| Поле | Тип | Описание | +|------|-----|---------| +| `owner` | `account_name_type` | Имя аккаунта валидатора | +| `url` | `string` | Сайт или информационный URL валидатора (непустой, макс. 256 байт) | +| `block_signing_key` | `public_key_type` | Ключ, используемый для подписи произведённых блоков | + +```json +[6, { + "owner": "alice", + "url": "https://alice.example.com", + "block_signing_key": "VIZ5hqSa4NkEZGAMUpoH5EaEr64mBJuMcPpGjvk8qb7hcPFTbXSQ9" +}] +``` + +- **Нулевой ключ** (деактивация): `"VIZ1111111111111111111111111111111114T1Anm"` — удаляет из производства блоков без удаления записи валидатора. +- Трансляция этой операции требует `witness_declaration_fee` (выплачивается в фонд комитета). + +--- + +## `chain_properties_update_operation` (ID 25) + +**Авторизация:** `active` `owner` + +Голосует за базовые свойства цепочки (формат `chain_properties_init`). Значение on-chain — медиана среди всех активных валидаторов. + +| Поле | Тип | Описание | +|------|-----|---------| +| `owner` | `account_name_type` | Валидатор, отдающий голос | +| `props` | `chain_properties_init` | Предлагаемые параметры цепочки | + +```json +[25, { + "owner": "alice", + "props": { + "account_creation_fee": "1.000 VIZ", + "maximum_block_size": 65536, + "create_account_delegation_ratio": 10, + "create_account_delegation_time": 2592000, + "min_delegation": "1.000 VIZ", + "min_curation_percent": 0, + "max_curation_percent": 10000, + "bandwidth_reserve_percent": 1000, + "bandwidth_reserve_below": "1.000000 SHARES", + "flag_energy_additional_cost": 1000, + "vote_accounting_min_rshares": 0, + "committee_request_approve_min_percent": 1000 + } +}] +``` + +- Все процентные поля в базисных пунктах (0–10000). +- `min_curation_percent` должен быть ≤ `max_curation_percent`. +- Используйте `versioned_chain_properties_update_operation` (ID 46) для расширенных свойств HF9+. + +--- + +## `versioned_chain_properties_update_operation` (ID 46) + +**Авторизация:** `active` `owner` + +Голосует за версионированные свойства цепочки, поддерживающие все расширения харфорков. Предпочтительнее `chain_properties_update_operation` для текущих узлов. + +| Поле | Тип | Описание | +|------|-----|---------| +| `owner` | `account_name_type` | Валидатор, отдающий голос | +| `props` | `versioned_chain_properties` | Версионированные параметры, сериализованные как `[index, object]` | + +```json +[46, { + "owner": "alice", + "props": [3, { + "account_creation_fee": "1.000 VIZ", + "maximum_block_size": 65536, + "create_account_delegation_ratio": 10, + "create_account_delegation_time": 2592000, + "min_delegation": "1.000 VIZ", + "min_curation_percent": 0, + "max_curation_percent": 10000, + "bandwidth_reserve_percent": 1000, + "bandwidth_reserve_below": "1.000000 SHARES", + "flag_energy_additional_cost": 1000, + "vote_accounting_min_rshares": 0, + "committee_request_approve_min_percent": 1000, + "inflation_witness_percent": 2000, + "inflation_ratio_committee_vs_reward_fund": 1000, + "inflation_recalc_period": 28800, + "data_operations_cost_additional_bandwidth": 0, + "witness_miss_penalty_percent": 100, + "witness_miss_penalty_duration": 86400, + "create_invite_min_balance": "1.000 VIZ", + "committee_create_request_fee": "1.000 VIZ", + "create_paid_subscription_fee": "1.000 VIZ", + "account_on_sale_fee": "10.000 VIZ", + "subaccount_on_sale_fee": "1.000 VIZ", + "witness_declaration_fee": "1.000 VIZ", + "withdraw_intervals": 28 + }] +}] +``` + +- `props` — статический вариант: используйте индекс `3` для `chain_properties_hf9` (текущий). +- Полный список полей по индексу версии см. в [Типах данных](../data-types.md#versioned_chain_properties). + +--- + +## `account_witness_vote_operation` (ID 7) + +**Авторизация:** `active` `account` + +Голосует за валидатора или снимает голос. Топ-21 валидаторов по кумулятивному весу голосов производят блоки. + +| Поле | Тип | Описание | +|------|-----|---------| +| `account` | `account_name_type` | Голосующий аккаунт | +| `witness` | `account_name_type` | Валидатор, за которого голосуют | +| `approve` | `bool` | `true` для добавления голоса, `false` для его снятия | + +```json +[7, { + "account": "alice", + "witness": "bob", + "approve": true +}] +``` + +- Вес голоса пропорционален стейку SHARES голосующего. +- `approve: false` снимает ранее поданный голос. + +--- + +## `account_witness_proxy_operation` (ID 8) + +**Авторизация:** `active` `account` + +Делегирует все голоса за валидаторов аккаунту-прокси. Все существующие прямые голоса удаляются при установке прокси. + +| Поле | Тип | Описание | +|------|-----|---------| +| `account` | `account_name_type` | Аккаунт, устанавливающий прокси | +| `proxy` | `account_name_type` | Аккаунт-прокси; `""` удаляет прокси | + +```json +[8, { + "account": "alice", + "proxy": "bob" +}] +``` + +- `proxy: ""` (пустая строка) удаляет прокси и восстанавливает прямое голосование. +- Нельзя установить прокси на себя. +- Цепочки прокси разрешаются транзитивно (A→B→C); максимальная глубина цепочки ограничена. +- Установка прокси удаляет все прямые голоса за валидаторов. + +--- + +См. также: [Типы данных](../data-types.md), [Обзор операций](./overview.md), [Свойства цепочки](../../governance/chain-properties.md). diff --git a/@l10n/ru/docs/protocol/virtual-operations.md b/@l10n/ru/docs/protocol/virtual-operations.md new file mode 100644 index 0000000000..fe1b2790a7 --- /dev/null +++ b/@l10n/ru/docs/protocol/virtual-operations.md @@ -0,0 +1,354 @@ +# Виртуальные операции + +Виртуальные операции генерируются самим блокчейном в процессе обработки блоков. Они **никогда не транслируются пользователями** — они появляются только в истории аккаунта и данных блоков в информационных целях. + +Виртуальные операции используют тот же вариант операций, что и обычные, но имеют более высокие type ID. Их можно наблюдать через API `account_history` или потоковую трансляцию блоков. + +--- + +## Выплаты за контент *(устарело)* + +### `author_reward_operation` (ID 26) + +**Триггер:** Выплата за публикацию контента + +Срабатывает, когда автор получает свою долю вознаграждения из выплаты за контент. + +| Поле | Тип | Описание | +|------|-----|---------| +| `author` | `account_name_type` | Автор контента | +| `permlink` | `string` | Permlink контента | +| `token_payout` | `asset` (VIZ) | Ликвидная часть VIZ | +| `vesting_payout` | `asset` (SHARES) | Вестинговая часть | + +--- + +### `curation_reward_operation` (ID 27) + +**Триггер:** Выплата за публикацию контента + +Срабатывает, когда куратор получает вознаграждение за кураторство. + +| Поле | Тип | Описание | +|------|-----|---------| +| `curator` | `account_name_type` | Аккаунт куратора | +| `reward` | `asset` (SHARES) | Вознаграждение куратора | +| `content_author` | `account_name_type` | Автор куриуемого контента | +| `content_permlink` | `string` | Permlink куриуемого контента | + +--- + +### `content_reward_operation` (ID 28) + +**Триггер:** Выплата за публикацию контента + +Срабатывает, когда пост достигает времени выплаты. + +| Поле | Тип | Описание | +|------|-----|---------| +| `author` | `account_name_type` | Автор контента | +| `permlink` | `string` | Permlink контента | +| `payout` | `asset` | Общая сумма выплаты | + +--- + +### `content_payout_update_operation` (ID 32) + +**Триггер:** Пересчёт выплаты за контент (например, после изменений голосов) + +| Поле | Тип | Описание | +|------|-----|---------| +| `author` | `account_name_type` | Автор контента | +| `permlink` | `string` | Permlink контента | + +--- + +### `content_benefactor_reward_operation` (ID 33) + +**Триггер:** Выплата за публикацию контента — срабатывает для каждого бенефициара + +| Поле | Тип | Описание | +|------|-----|---------| +| `benefactor` | `account_name_type` | Аккаунт бенефициара | +| `author` | `account_name_type` | Автор контента | +| `permlink` | `string` | Permlink контента | +| `reward` | `asset` | Доля вознаграждения бенефициара | + +--- + +## Вывод вестинга + +### `fill_vesting_withdraw_operation` (ID 29) + +**Триггер:** Каждый интервал вывода вестинга + +Срабатывает один раз за интервал для каждого активного маршрута вывода. + +| Поле | Тип | Описание | +|------|-----|---------| +| `from_account` | `account_name_type` | Аккаунт, выводящий средства | +| `to_account` | `account_name_type` | Целевой аккаунт (может отличаться через маршрут вывода) | +| `withdrawn` | `asset` (SHARES) | Выведенное количество SHARES за интервал | +| `deposited` | `asset` | Зачислено на `to_account` (VIZ или SHARES при `auto_vest = true`) | + +```json +[29, { + "from_account": "alice", + "to_account": "alice", + "withdrawn": "35.714285 SHARES", + "deposited": "10.000 VIZ" +}] +``` + +--- + +### `return_vesting_delegation_operation` (ID 34) + +**Триггер:** Окончание 7-дневного периода возврата после `delegate_vesting_shares_operation` с нулевой суммой + +| Поле | Тип | Описание | +|------|-----|---------| +| `account` | `account_name_type` | Аккаунт, получающий возвращённые SHARES | +| `vesting_shares` | `asset` (SHARES) | SHARES, возвращённые из лимба | + +--- + +## Операции валидатора + +### `shutdown_witness_operation` (ID 30) + +**Триггер:** Валидатор деактивирован из-за недостаточного веса голосов + +| Поле | Тип | Описание | +|------|-----|---------| +| `owner` | `account_name_type` | Деактивированный валидатор | + +--- + +### `witness_reward_operation` (ID 42) + +**Триггер:** Произведён блок — валидатор получает вознаграждение за блок + +| Поле | Тип | Описание | +|------|-----|---------| +| `witness` | `account_name_type` | Аккаунт валидатора | +| `shares` | `asset` (SHARES) | Вознаграждение за блок | + +```json +[42, { + "witness": "alice", + "shares": "1.234567 SHARES" +}] +``` + +--- + +## Сетевые события + +### `hardfork_operation` (ID 31) + +**Триггер:** Активация хардфорка сети + +| Поле | Тип | Описание | +|------|-----|---------| +| `hardfork_id` | `uint32_t` | Номер хардфорка | + +--- + +## Награды + +### `receive_award_operation` (ID 48) + +**Триггер:** `award_operation` или `fixed_award_operation` + +Срабатывает для основного получателя награды. + +| Поле | Тип | Описание | +|------|-----|---------| +| `initiator` | `account_name_type` | Аккаунт, выдавший награду | +| `receiver` | `account_name_type` | Аккаунт, получивший награду | +| `custom_sequence` | `uint64_t` | Порядковый номер приложения из операции награды | +| `memo` | `string` | Memo из операции награды | +| `shares` | `asset` (SHARES) | Полученные SHARES | + +```json +[48, { + "initiator": "alice", + "receiver": "bob", + "custom_sequence": 0, + "memo": "great article!", + "shares": "5.000000 SHARES" +}] +``` + +--- + +### `benefactor_award_operation` (ID 49) + +**Триггер:** `award_operation` или `fixed_award_operation` с бенефициарами + +Срабатывает по одному разу для каждого бенефициара. + +| Поле | Тип | Описание | +|------|-----|---------| +| `initiator` | `account_name_type` | Аккаунт, выдавший награду | +| `benefactor` | `account_name_type` | Аккаунт бенефициара | +| `receiver` | `account_name_type` | Основной получатель награды | +| `custom_sequence` | `uint64_t` | Порядковый номер приложения | +| `memo` | `string` | Memo из операции награды | +| `shares` | `asset` (SHARES) | SHARES, полученные бенефициаром | + +--- + +## Комитет + +### `committee_cancel_request_operation` (ID 38) + +**Триггер:** Запрос на финансирование комитета истекает, не набрав порог одобрения + +| Поле | Тип | Описание | +|------|-----|---------| +| `request_id` | `uint32_t` | ID отменённого запроса | + +--- + +### `committee_approve_request_operation` (ID 39) + +**Триггер:** Запрос на финансирование комитета достигает требуемого порога одобрения + +| Поле | Тип | Описание | +|------|-----|---------| +| `request_id` | `uint32_t` | ID одобренного запроса | + +--- + +### `committee_payout_request_operation` (ID 40) + +**Триггер:** Обработка выплаты по запросу комитета + +| Поле | Тип | Описание | +|------|-----|---------| +| `request_id` | `uint32_t` | ID выплаченного запроса | + +--- + +### `committee_pay_request_operation` (ID 41) + +**Триггер:** Работник получает выплату из фонда комитета + +| Поле | Тип | Описание | +|------|-----|---------| +| `worker` | `account_name_type` | Аккаунт работника | +| `request_id` | `uint32_t` | ID запроса комитета | +| `tokens` | `asset` (VIZ) | Выплаченная сумма | + +```json +[41, { + "worker": "alice", + "request_id": 42, + "tokens": "250.000 VIZ" +}] +``` + +--- + +## Платные подписки + +### `paid_subscription_action_operation` (ID 52) + +**Триггер:** Исполнена `paid_subscribe_operation` или обработан платёж автопродления + +| Поле | Тип | Описание | +|------|-----|---------| +| `subscriber` | `account_name_type` | Аккаунт подписчика | +| `account` | `account_name_type` | Провайдер подписки | +| `level` | `uint16_t` | Уровень подписки | +| `amount` | `asset` (VIZ) | Сумма платежа | +| `period` | `uint16_t` | Количество периодов | +| `summary_duration_sec` | `uint64_t` | Суммарная длительность подписки (секунды) | +| `summary_amount` | `asset` (VIZ) | Общая выплаченная сумма | + +--- + +### `cancel_paid_subscription_operation` (ID 53) + +**Триггер:** Подписка истекла или недостаточно баланса для автопродления + +| Поле | Тип | Описание | +|------|-----|---------| +| `subscriber` | `account_name_type` | Аккаунт подписчика | +| `account` | `account_name_type` | Провайдер подписки | + +--- + +## Рынок аккаунтов + +### `account_sale_operation` (ID 57) + +**Триггер:** `buy_account_operation` успешно завершена + +| Поле | Тип | Описание | +|------|-----|---------| +| `account` | `account_name_type` | Проданный аккаунт | +| `price` | `asset` (VIZ) | Цена продажи | +| `buyer` | `account_name_type` | Покупатель | +| `seller` | `account_name_type` | Продавец (получатель платежа) | + +```json +[57, { + "account": "alice", + "price": "1000.000 VIZ", + "buyer": "bob", + "seller": "alice" +}] +``` + +--- + +### `bid_operation` (ID 62) + +**Триггер:** Новая ставка на аккаунт, выставленный на аукцион + +| Поле | Тип | Описание | +|------|-----|---------| +| `account` | `account_name_type` | Аккаунт, на который делается ставка | +| `bidder` | `account_name_type` | Аккаунт, делающий ставку | +| `bid` | `asset` (VIZ) | Сумма ставки | + +--- + +### `outbid_operation` (ID 63) + +**Триггер:** Предыдущая ставка перебита более высокой + +Срабатывает для перебитого аккаунта; предыдущая сумма ставки возвращается. + +| Поле | Тип | Описание | +|------|-----|---------| +| `account` | `account_name_type` | Аккаунт, на который делается ставка | +| `bidder` | `account_name_type` | Перебитый аккаунт | +| `bid` | `asset` (VIZ) | Возвращённая сумма ставки | + +--- + +##托管 (Escrow) + +### `expire_escrow_ratification_operation` (ID 59) + +**Триггер:** Пропущен дедлайн `ratification_deadline` — ни `to`, ни `agent` не дали одобрения вовремя + +Все заблокированные средства возвращаются `from`. + +| Поле | Тип | Описание | +|------|-----|---------| +| `from` | `account_name_type` | Исходный отправитель эскроу | +| `to` | `account_name_type` | Предполагаемый получатель | +| `agent` | `account_name_type` | Агент эскроу | +| `escrow_id` | `uint32_t` | ID эскроу | +| `token_amount` | `asset` (VIZ) | Возвращённая сумма токенов | +| `fee` | `asset` (VIZ) | Возвращённая комиссия (агент не получает оплату, поскольку эскроу не был ратифицирован) | +| `ratification_deadline` | `time_point_sec` | Пропущенный дедлайн | + +--- + +См. также: [Обзор операций](./operations/overview.md), [Награды](./operations/awards.md), [Комитет](./operations/committee.md). diff --git a/@l10n/ru/docs/storage/block-log.md b/@l10n/ru/docs/storage/block-log.md new file mode 100644 index 0000000000..5fdd02dd98 --- /dev/null +++ b/@l10n/ru/docs/storage/block-log.md @@ -0,0 +1,166 @@ +# Block Log + +VIZ хранит блоки в бинарных лог-файлах. Существуют два варианта: + +| Вариант | Файлы | Назначение | +|---------|-------|-----------| +| `block_log` | `block_log` + `block_log.index` | Полная история (архивные узлы) | +| `dlt_block_log` | `dlt_block_log` + `dlt_block_log.index` | Скользящее окно (DLT/snapshot-узлы) | + +Оба используют одинаковый формат файла данных; формат индекса незначительно отличается. + +--- + +## Бинарная сериализация (`fc::raw`) + +Все данные используют кодировку little-endian. + +| Тип | Формат | +|-----|--------| +| `uint8_t` – `uint64_t` | Фиксированная ширина, little-endian | +| `fc::unsigned_int` | Переменная длина (varint): 7 бит данных + 1 бит продолжения на байт | +| `string` | `[varint: длина][байты UTF-8]` | +| `vector` | `[varint: количество][элементы...]` | +| `optional` | `[uint8: 0 или 1][значение если 1]` | +| `static_variant` | `[varint: индекс типа][сериализованное значение]` | + +--- + +## Формат файла данных + +`block_log` и `dlt_block_log` используют одинаковый формат: + +``` +[бинарный блок 1][uint64 LE: позиция блока 1] +[бинарный блок 2][uint64 LE: позиция блока 2] +... +``` + +Каждая запись = сериализованный `signed_block`, за которым следует его начальное смещение в виде `uint64_t`. + +**Чтение head-блока:** перейти к последним 8 байтам, прочитать смещение, перейти туда, десериализовать. + +--- + +## Индексные файлы + +### `block_log.index` + +Каждая запись — 8-байтовое смещение `uint64_t` в `block_log`. + +``` +offset = 8 × (block_num − 1) +``` + +### `dlt_block_log.index` + +Начинается с 8-байтового заголовка: + +``` +[uint64 LE: start_block_num][uint64 LE: смещение start_block_num][...] +``` + +``` +offset = 8 + 8 × (block_num − start_block_num) +``` + +--- + +## Структура `signed_block` + +``` +block_header: + [20 байт: ID предыдущего блока (ripemd160)] + [4 байта: метка времени (uint32 Unix seconds)] + [varint + string: имя аккаунта валидатора] + [20 байт: transaction_merkle_root (ripemd160)] + [varint + vector: extensions] + +signed_block_header (добавляется): + [65 байт: witness_signature (1 байт восстановления + 32 r + 32 s)] + +signed_block (добавляется): + [varint + vector: транзакции] +``` + +Номер блока **не хранится напрямую**. Вычисляется как: +``` +block_num = num_from_id(previous) + 1 +num_from_id = первые 4 байта block_id как uint32_t LE +``` +(Genesis: `previous` — все нули → `block_num = 1`.) + +--- + +## Структура `signed_transaction` + +``` +transaction: + [2 байта: ref_block_num (uint16 LE)] + [4 байта: ref_block_prefix (uint32 LE)] + [4 байта: expiration (uint32 Unix seconds)] + [varint + vector: операции] + [varint + extensions_type: extensions] + +signed_transaction (добавляется): + [varint + vector: подписи] +``` + +--- + +## Сериализация операций + +Каждая операция в векторе `operations` — статический вариант: + +``` +[varint: type_id][поля, специфичные для операции...] +``` + +Type ID: см. [Обзор операций](../protocol/operations/overview.md). + +**Формат ассета на уровне протокола:** +``` +[int64: amount][uint64: symbol] +``` +Symbol — упакованный `uint64`: байт 0 = знаков после запятой, байты 1–6 = ASCII-имя, байт 7 = 0x00. + +| Символ | Hex (LE) | +|--------|----------| +| VIZ (3 знака) | `03 56 49 5A 00 00 00 00` | +| SHARES (6 знаков) | `06 53 48 41 52 45 53 00` | + +**Формат публичного ключа на уровне протокола:** 33 сырых байта (сжатый secp256k1): `[0x02 или 0x03][32 байта x]`. + +--- + +## Расширения заголовка блока + +| Индекс | Тип | Данные | +|--------|-----|--------| +| 0 | `void_t` | (нет) | +| 1 | `version` | `uint32_t` номер версии (major 8 \| hf 8 \| release 16 бит) | +| 2 | `hardfork_version_vote` | `uint32_t` hf_version + `uint32_t` hf_time | + +--- + +## Скользящее окно `dlt_block_log` + +DLT-лог хранит только недавнее окно блоков; более старые блоки обрезаются. Начинается с `start_block_num > 1`. Узлы, использующие снапшоты, используют этот файл для восстановления после сбоя (реплей из снапшота + dlt_block_log). + +--- + +## Просмотрщик block log + +В инструментарии включён терминальный просмотрщик block log (`block-log-viewer.js`): + +``` +node block-log-viewer.js [--dlt] +``` + +Основные команды: `f` — первый, `l` — последний, `n`/`p` — следующий/предыдущий, `g ` — перейти к блоку N, `o` — показать операции, `s ` — поиск по типу операции, `S ` — поиск по содержимому, `scan` — построить битовую маску для быстрой навигации. + +Команда `scan` создаёт файл битовой маски (`block_log.bitmask`), отмечающий блоки с непустыми операциями, что позволяет мгновенно перемещаться по `N`/`P`. + +--- + +См. также: [Разделяемая память](./shared-memory.md), [Снапшоты](./snapshots.md), [Плагин chain](../plugins/chain.md). diff --git a/@l10n/ru/docs/storage/shared-memory.md b/@l10n/ru/docs/storage/shared-memory.md new file mode 100644 index 0000000000..148b9ae678 --- /dev/null +++ b/@l10n/ru/docs/storage/shared-memory.md @@ -0,0 +1,150 @@ +# Разделяемая память + +Всё состояние блокчейна в узле VIZ хранится в едином файле с отображением в память (`shared_memory.bin`), управляемом библиотекой **chainbase**. Узел не может работать без этого файла. + +--- + +## Архитектура + +``` +процесс vizd +├── block_log / dlt_block_log — сырые байты блоков (диск) +└── shared_memory.bin (mmap) — всё состояние цепочки (chainbase) + ├── account_index + ├── witness_index + ├── transaction_index + └── ... (все остальные индексы объектов) +``` + +API-потоки (пул потоков веб-сервера) захватывают **разделяемые блокировки чтения**; применение блока удерживает **эксклюзивную блокировку записи**. Несколько читателей могут работать одновременно; запись блокирует всех читателей. + +--- + +## Конфигурация + +Все параметры указываются в `config.ini`. + +### Параметры размера + +| Параметр | По умолчанию | Описание | +|---------|-------------|---------| +| `shared-file-dir` | `state` | Каталог для `shared_memory.bin` (относительно каталога данных или абсолютный) | +| `shared-file-size` | `2G` | Начальный объём. Если файл существует и значение больше — файл вырастает. Реплей не запускается. | +| `inc-shared-file-size` | `2G` | Шаг автоматического увеличения при падении свободного места ниже порога | +| `min-free-shared-file-size` | `500M` | Порог свободного места, при котором запускается автоувеличение | + +**Правило:** `min-free-shared-file-size` должен быть меньше `inc-shared-file-size`, иначе возникнут каскадные изменения размера. + +### Параметры таймаута блокировок + +| Параметр | По умолчанию | Описание | +|---------|-------------|---------| +| `read-wait-micro` | `500000` (500 мс) | Таймаут одной попытки блокировки чтения | +| `max-read-wait-retries` | `3` | Максимальное количество попыток чтения перед ошибкой | +| `write-wait-micro` | `500000` (500 мс) | Таймаут одной попытки блокировки записи | +| `max-write-wait-retries` | `3` | Максимальное количество попыток записи перед ошибкой | + +### Параметры производительности + +| Параметр | По умолчанию | Описание | +|---------|-------------|---------| +| `single-write-thread` | `false` | Сериализовать все операции применения блоков/транзакций. **Рекомендуется для продакшена.** | +| `block-num-check-free-size` | `1000` | Проверять свободное место каждые N блоков | +| `flush-state-interval` | — | Сбрасывать разделяемую память на диск каждые N блоков | +| `clear-votes-before-block` | `0` | Удалять голоса старше этого блока (0 = хранить все). Уменьшает потребление памяти. | +| `skip-virtual-ops` | `false` | Пропускать уведомления о виртуальных операциях. Экономит CPU при реплее. | + +--- + +## Рекомендуемые конфигурации + +**Узел-валидатор (продакшен):** +```ini +shared-file-size = 4G +inc-shared-file-size = 2G +min-free-shared-file-size = 500M +single-write-thread = true +``` + +**API-узел (высокая пропускная способность чтения):** +```ini +shared-file-size = 8G +inc-shared-file-size = 2G +min-free-shared-file-size = 500M +single-write-thread = true +read-wait-micro = 1000000 +max-read-wait-retries = 10 +webserver-thread-pool-size = 256 +``` + +**Реплей / первоначальная синхронизация:** +```ini +shared-file-size = 8G +inc-shared-file-size = 4G +min-free-shared-file-size = 500M +block-num-check-free-size = 10 +skip-virtual-ops = true +``` + +--- + +## Автоизменение размера + +База данных автоматически увеличивается, когда свободное место падает ниже `min-free-shared-file-size`. При каждом изменении размера: + +1. Приостанавливаются все операции (включая производство блоков и API-запросы). +2. Уничтожается текущее отображение памяти. +3. Файл увеличивается на `inc-shared-file-size`. +4. Файл заново отображается, пересчитываются все указатели индексов. + +Выделяйте `shared-file-size` с запасом, чтобы минимизировать частоту изменений размера. Каждое изменение вызывает скачок задержки. + +--- + +## Планирование размера + +Ориентировочное использование для полного узла VIZ mainnet: + +| Компонент | Ориентировочный размер | +|-----------|----------------------| +| Индекс аккаунтов (~14 тыс. аккаунтов) | ~50 МБ | +| Индекс валидаторов | ~5 МБ | +| История операций (плагин operation_history) | 200–500 МБ | +| История аккаунтов (плагин account_history) | 100–300 МБ | +| Остальные индексы | 100–200 МБ | +| **Рекомендуемый начальный размер** | **4–8 ГБ** | + +--- + +## Последовательность запуска + +``` +1. Открыть shared_memory.bin (увеличить, если shared-file-size больше) +2. Захватить эксклюзивную блокировку файла +3. Инициализировать индексы +4. Если отсутствует genesis → init_genesis() +5. Открыть block_log или dlt_block_log +6. undo_all() → откатиться к последнему необратимому блоку +7. Проверить совпадение head блока с block log +``` + +--- + +## Восстановление + +| Симптом | Действие | +|---------|---------| +| `CRITICAL: validator X account object MISSING` | Повреждение — использовать `--replay-blockchain` | +| `Could not modify object, uniqueness constraint violated` | Повреждение — реплей или ресинхронизация | +| `Unable to acquire READ lock` | Конкуренция за блокировку — увеличить `read-wait-micro` / включить `single-write-thread` | +| Узел зацикливается при запуске | Повреждённый файл — `--replay-blockchain` или `--snapshot` | + +Варианты восстановления: + +- `--replay-blockchain` — удалить разделяемую память, выполнить реплей из block log. +- `--resync-blockchain` — удалить разделяемую память и block log, синхронизироваться от пиров. +- `--snapshot ` — загрузить из снапшота, выполнить реплей dlt_block_log поверх. + +--- + +См. также: [Плагин chain](../plugins/chain.md), [Плагин snapshot](../plugins/snapshot.md), [Block log](./block-log.md). diff --git a/@l10n/ru/docs/storage/snapshots.md b/@l10n/ru/docs/storage/snapshots.md new file mode 100644 index 0000000000..cbe54db3c0 --- /dev/null +++ b/@l10n/ru/docs/storage/snapshots.md @@ -0,0 +1,286 @@ +# Плагин snapshot + +Плагин snapshot обеспечивает почти мгновенный запуск узла путём сериализации и восстановления полного состояния блокчейна в виде JSON-файла снапшота. Вместо реплея миллионов блоков из block log узел может загрузить заранее подготовленный снапшот и начать синхронизацию с этой точки через P2P. + +--- + +## Включение + +```ini +plugin = snapshot +``` + +Или из командной строки: + +```bash +vizd --plugin snapshot +``` + +--- + +## Справочник конфигурации + +### Параметры только командной строки + +| Параметр | Тип | Описание | +|---------|-----|---------| +| `--snapshot ` | string | Загрузить состояние из файла снапшота (DLT-режим). Пропускает импорт, если `shared_memory.bin` уже существует; переименовывает файл в `.used` после успешного импорта. | +| `--snapshot-auto-latest` | bool | Автоматически найти последний снапшот в `snapshot-dir` по номеру блока в имени файла. Игнорируется, если задан `--snapshot`. | +| `--replay-from-snapshot` | bool | Восстановление после сбоя: импортировать снапшот, затем воспроизвести `dlt_block_log` для достижения последнего доступного состояния. Всегда очищает разделяемую память; не переименовывает снапшот. | +| `--auto-recover-from-snapshot` | bool (по умолчанию: `true`) | Автоматическое восстановление при обнаружении повреждения разделяемой памяти. Закрывает БД, находит последний снапшот, очищает разделяемую память, импортирует и реплеит — без перезапуска. | +| `--create-snapshot ` | string | Создать снапшот из текущего состояния БД, затем завершить работу. | +| `--sync-snapshot-from-trusted-peer` | bool (по умолчанию: `false`) | Загрузить и применить снапшот от доверенных пиров при пустом состоянии. Требует явного включения. | + +### Параметры конфигурационного файла + +| Параметр | Тип | По умолчанию | Описание | +|---------|-----|-------------|---------| +| `snapshot-at-block` | uint32 | `0` | Создать снапшот при достижении этого номера блока | +| `snapshot-every-n-blocks` | uint32 | `0` | Создавать снапшот каждые N блоков (0 = отключено) | +| `snapshot-dir` | string | `""` | Каталог для автоматически генерируемых файлов снапшотов | +| `snapshot-max-age-days` | uint32 | `90` | Удалять снапшоты старше N дней (0 = отключено) | +| `allow-snapshot-serving` | bool | `false` | Включить раздачу снапшотов по TCP другим узлам | +| `allow-snapshot-serving-only-trusted` | bool | `false` | Ограничить раздачу только доверенными пирами | +| `snapshot-serve-endpoint` | string | `0.0.0.0:8092` | TCP-адрес для раздачи снапшотов | +| `trusted-snapshot-peer` | string (multi) | — | Доверенный пир для синхронизации снапшотов (`IP:port`). Повторяемый. | +| `dlt-block-log-max-blocks` | uint32 | `100000` | Количество последних блоков в скользящем DLT block log (плагин chain) | + +--- + +## Создание снапшотов + +### Метод 1: Разовый (узел останавливается и завершает работу) + +```bash +vizd --create-snapshot /data/snapshots/viz-snapshot.json --plugin snapshot +``` + +Узел открывает существующую БД, при необходимости выполняет реплей, создаёт снапшот и завершает работу до активации P2P. + +### Метод 2: В определённом блоке (без простоя) + +```ini +plugin = snapshot +snapshot-at-block = 5000000 +snapshot-dir = /data/snapshots +``` + +Когда применяется блок 5 000 000, снапшот записывается в `/data/snapshots/snapshot-block-5000000.json` в фоновом потоке — обработка блоков приостанавливается лишь на короткое время. + +### Метод 3: Периодические автоматические снапшоты (рекомендуется) + +```ini +plugin = snapshot +snapshot-every-n-blocks = 100000 +snapshot-dir = /data/snapshots +``` + +Снапшоты создаются каждые 100 000 блоков (~3,5 суток). Во время синхронизации снапшоты пропускаются — они срабатывают только на живых блоках. + +**Рекомендуемые интервалы:** + +| Интервал | Блоков | Приблизительное время | +|---------|--------|----------------------| +| Частый | 10 000 | ~8 часов | +| Ежедневный | 28 800 | ~24 часа | +| Еженедельный | 100 000 | ~3,5 суток | +| Редкий | 1 000 000 | ~35 суток | + +### Ротация снапшотов + +Старые снапшоты автоматически удаляются после создания каждого нового снапшота, если они старше `snapshot-max-age-days` (по умолчанию 90). Для отключения: + +```ini +snapshot-max-age-days = 0 +``` + +--- + +## Загрузка из снапшота (DLT-режим) + +```bash +vizd --snapshot /path/to/snapshot.json --plugin snapshot +``` + +Что происходит при загрузке: + +1. Плагин snapshot регистрирует callback загрузки в плагине chain. +2. Плагин chain проверяет: если `shared_memory.bin` уже существует → пропускает импорт (защита от дублирования). Если файл снапшота не найден → пропускает импорт. +3. БД открывается через `open_from_snapshot()` — разделяемая память очищается, chainbase инициализируется. +4. Снапшот валидируется (версия формата, chain ID, контрольная сумма SHA-256) и все 32 типа объектов импортируются. +5. Файл снапшота переименовывается в `.used`. +6. LIB продвигается до `head_block_num`, чтобы P2P-синопсис начинался с head снапшота. +7. Fork-БД инициализируется head-блоком снапшота. +8. P2P начинает синхронизацию с LIB + 1. + +### Защита от дублирования при перезапуске + +| Сценарий перезапуска | Действие | +|--------------------|---------| +| 1-й запуск (нет shared_memory, файл существует) | Импортирует снапшот, переименовывает в `.used` | +| Перезапуск (shared_memory существует) | Пропускает импорт | +| Перезапуск (shared_memory удалена, файл `.used`) | Пропускает импорт | +| Принудительный повторный импорт | `--resync-blockchain` + новый файл снапшота | + +Флаг `--snapshot` безопасно оставлять в командной строке постоянно. + +--- + +## Скользящий DLT block log + +При работе в DLT-режиме (загружено из снапшота) основной `block_log` пуст. Отдельный **скользящий DLT block log** (`dlt_block_log`) хранит последние необратимые блоки. + +- Позволяет раздавать блоки через P2P (пиры могут запрашивать недавние блоки для разрешения форков). +- Позволяет выполнять API-вызовы типа `get_block` для недавних блоков. +- Хранится в `dlt_block_log.log` и `dlt_block_log.index` в каталоге данных блокчейна. +- Скользящее окно: когда лог превышает `dlt-block-log-max-blocks`, старые блоки усекаются с начала. + +```ini +dlt-block-log-max-blocks = 100000 +``` + +--- + +## Восстановление после сбоя: `--replay-from-snapshot` + +Когда `shared_memory.bin` повреждён после некорректного завершения, используйте этот режим: + +```bash +# Явно указать путь к снапшоту +vizd --replay-from-snapshot --snapshot /data/snapshots/snapshot-block-79273800.vizjson --plugin snapshot + +# Автоматически найти последний снапшот +vizd --replay-from-snapshot --snapshot-auto-latest --plugin snapshot +``` + +Шаги восстановления: +1. Очищает `shared_memory.bin` (всегда — предполагается повреждение). +2. Импортирует состояние из снапшота. +3. Реплеит блоки `dlt_block_log` за head снапшота. +4. Возобновляет P2P-синхронизацию от реплеенного head. + +| Аспект | `--snapshot` | `--replay-from-snapshot` | +|--------|-------------|--------------------------| +| Проверка разделяемой памяти | Пропускает если существует | Всегда очищает | +| Переименование снапшота | Переименовывает в `.used` | Не переименовывает | +| Реплей DLT block log | Нет | Да | +| Применение | Первоначальная загрузка | Восстановление после сбоя | + +--- + +## Автоматическое восстановление: `--auto-recover-from-snapshot` + +Включено по умолчанию (`true`). При обнаружении повреждения разделяемой памяти в процессе обработки или генерации блоков узел: + +1. Закрывает БД. +2. Находит последний снапшот в `snapshot-dir`. +3. Очищает разделяемую память, импортирует снапшот, реплеит `dlt_block_log`. +4. Возобновляет P2P-синхронизацию — **без перезапуска**. + +Предварительные условия: +- `plugin = snapshot` должен быть включён. +- В `snapshot-dir` должны существовать снапшоты. + +Для отключения (например, для отладки): + +```bash +vizd --no-auto-recover-from-snapshot +``` + +--- + +## P2P-синхронизация снапшотов + +Узлы могут загружать снапшоты напрямую от доверенных пиров по TCP. + +### Конфигурация сервера + +```ini +plugin = snapshot +allow-snapshot-serving = true +snapshot-serve-endpoint = 0.0.0.0:8092 +snapshot-every-n-blocks = 28800 +snapshot-dir = /data/viz-snapshots +``` + +### Конфигурация клиента + +```ini +trusted-snapshot-peer = seed1.viz.world:8092 +trusted-snapshot-peer = seed2.viz.world:8092 +sync-snapshot-from-trusted-peer = true +``` + +Клиент подключается к каждому доверенному пиру, выбирает тот, у которого наибольший номер блока, загружает снапшот частями по 1 МБ, проверяет контрольную сумму SHA-256 и импортирует его. + +Меры безопасности: максимальный размер снапшота 2 ГБ, список доверенных пиров, ограничение скорости, 60-секундный дедлайн соединения, потоковая проверка контрольной суммы. + +--- + +## Формат файла снапшота + +```json +{ + "header": { + "version": 1, + "chain_id": "...", + "snapshot_block_num": 12345678, + "snapshot_block_id": "...", + "snapshot_block_time": "2025-01-01T00:00:00", + "last_irreversible_block_num": 12345660, + "payload_checksum": "sha256...", + "object_counts": { "account": 50000, ... } + }, + "state": { + "dynamic_global_property": [ ... ], + "account": [ ... ], + ... + } +} +``` + +### Включённые типы объектов (32 всего) + +**Критические (11):** `dynamic_global_property`, `witness_schedule`, `hardfork_property`, `account`, `account_authority`, `validator`, `witness_vote`, `block_summary`, `content`, `content_vote`, `block_post_validation` + +**Важные (15):** `transaction`, `vesting_delegation`, `vesting_delegation_expiration`, `fix_vesting_delegation`, `withdraw_vesting_route`, `escrow`, `proposal`, `required_approval`, `committee_request`, `committee_vote`, `invite`, `award_shares_expire`, `paid_subscription`, `paid_subscribe`, `witness_penalty_expire` + +**Опциональные (5):** `content_type`, `account_metadata`, `master_authority_history`, `account_recovery_request`, `change_recovery_account_request` + +--- + +## Рекомендуемая продакшен-конфигурация + +```ini +plugin = snapshot + +# Создавать снапшот каждые ~24 часа +snapshot-every-n-blocks = 28800 +snapshot-dir = /data/viz-snapshots + +# Автоматически удалять снапшоты старше 90 дней +snapshot-max-age-days = 90 + +# DLT rolling block log: хранить последние 100k блоков +dlt-block-log-max-blocks = 100000 + +shared-file-size = 4G +plugin = p2p +p2p-seed-node = seed1.viz.world:2001 +``` + +--- + +## Краткий справочник Docker + +| Задача | Команда | +|-------|---------| +| Запуск с периодическими снапшотами | Добавить `snapshot-every-n-blocks` в конфиг, перезапустить контейнер | +| Разовый снапшот | `VIZD_EXTRA_OPTS="--create-snapshot /var/lib/vizd/snapshots/snap.json --plugin snapshot"` | +| Загрузка из снапшота | `VIZD_EXTRA_OPTS="--snapshot /var/lib/vizd/snapshots/snap.json --plugin snapshot"` | +| Восстановление после сбоя | `VIZD_EXTRA_OPTS="--replay-from-snapshot --snapshot-auto-latest --plugin snapshot"` | +| Авто-восстановление (по умолчанию) | Включено по умолчанию; убедитесь, что `plugin = snapshot` и `snapshot-every-n-blocks` заданы | + +--- + +См. также: [Плагин chain](../plugins/chain.md), [Разделяемая память](./shared-memory.md), [Block log](./block-log.md), [Сценарии синхронизации P2P](../p2p/sync-scenarios.md). diff --git a/@l10n/zh-CN/docs/advanced/database-schema.md b/@l10n/zh-CN/docs/advanced/database-schema.md new file mode 100644 index 0000000000..7d460724da --- /dev/null +++ b/@l10n/zh-CN/docs/advanced/database-schema.md @@ -0,0 +1,299 @@ +# 数据库架构 + +VIZ Ledger 使用 ChainBase——一个基于 Boost.Interprocess 构建的内存映射、多索引持久化存储。所有链状态都在 `shared_memory.bin` 中。每种对象类型都与定义其主索引和次级索引的 Boost.MultiIndex 容器关联。 + +--- + +## 对象类型注册表 + +每个持久化对象在 `chain_object_types.hpp` 中声明了唯一的数字类型 ID。所有跟踪的对象类型完整集合: + +| 对象 | 备注 | +|------|------| +| `dynamic_global_property` | 单例:当前链状态、头区块、LIB、通胀 | +| `account` | 所有已注册账户 | +| `account_authority` | master/active/regular 权限集 | +| `witness`(验证者) | 验证者注册信息、签名密钥、票数 | +| `transaction` | 待处理/最近的交易(TAPOS 窗口) | +| `block_summary` | 65536 槽的 TAPOS 区块 ID 缓冲区 | +| `witness_schedule` | 单例:活跃验证者调度 | +| `content` | 帖子和评论(已弃用) | +| `content_type` | 内容标题/正文元数据 | +| `content_vote` | 内容投票 | +| `witness_vote` | 账户对验证者的投票 | +| `hardfork_property` | 单例:当前/下一个硬分叉跟踪 | +| `withdraw_vesting_route` | 提取路由规则 | +| `master_authority_history` | Master 密钥变更历史 | +| `account_recovery_request` | 待处理的账户恢复请求 | +| `change_recovery_account_request` | 待处理的恢复账户变更 | +| `escrow` | 托管转账 | +| `vesting_delegation` | 活跃的 SHARES 委托 | +| `fix_vesting_delegation` | 委托修复记录 | +| `vesting_delegation_expiration` | 处于返还窗口的委托 | +| `account_metadata` | 账户 JSON 元数据 | +| `proposal` | 治理提案 | +| `required_approval` | 提案批准要求 | +| `committee_request` | 委员会资金请求 | +| `committee_vote` | 委员会投票 | +| `invite` | 账户邀请 | +| `award_shares_expire` | 过期的奖励 SHARES | +| `paid_subscription` | 订阅服务 | +| `paid_subscribe` | 活跃订阅 | +| `witness_penalty_expire` | 验证者缺席惩罚到期 | +| `block_post_validation` | 区块后验证记录 | + +--- + +## 账户对象 + +账户存储余额、锁仓状态、委托指标、带宽、拍卖/出售标志和治理参与情况。 + +**关键字段:** `name`、`balance`(VIZ)、`vesting_shares`、`delegated_vesting_shares`、`received_vesting_shares`、`energy`、`next_vesting_withdrawal`、`witnesses_voted_for`、`recovery_account`。 + +**索引:** + +| 标签 | 键 | 类型 | +|------|-----|------| +| `by_id` | `id` | 唯一 | +| `by_name` | `name` | 唯一 | +| `by_account_on_sale` | 出售标志 | 非唯一 | +| `by_account_on_auction` | 拍卖标志 | 非唯一 | +| `by_account_on_sale_start_time` | 出售开始时间 | 非唯一 | +| `by_subaccount_on_sale` | 子账户出售标志 | 非唯一 | +| `by_next_vesting_withdrawal` | `(next_vesting_withdrawal, id)` | 复合 | + +`by_next_vesting_withdrawal` 复合索引支持以 O(log N) 批量处理即将到来的提取分期付款。 + +--- + +## 内容对象 + +内容对象表示带有投票、结算和嵌套元数据的帖子和评论。**这些对象已弃用**——新应用应改用 `custom_operation`。 + +**`content` 的索引:** + +| 标签 | 键 | +|------|-----| +| `by_id` | `id` | +| `by_cashout_time` | `(cashout_time, id)` | +| `by_permlink` | `(author, permlink)` | +| `by_root` | `(root_content, id)` | +| `by_parent` | `(parent_author, parent_permlink, id)` | +| `by_last_update` | `(parent_author, last_update, id)` — API 密集 | +| `by_author_last_update` | `(author, last_update, id)` — API 密集 | + +**`content_vote` 的索引:** + +| 标签 | 键 | +|------|-----| +| `by_id` | `id` | +| `by_content_voter` | `(content, voter)` — 唯一 | +| `by_voter_content` | `(voter, content)` — 唯一 | +| `by_voter_last_update` | `(voter, last_update, content)` | +| `by_content_weight_voter` | `(content, weight, voter)` — 用于排行榜 | + +--- + +## 验证者(Witness)对象 + +**`witness_object` 的索引:** + +| 标签 | 键 | +|------|-----| +| `by_id` | `id` | +| `by_name` | `owner` — 唯一 | +| `by_vote_name` | `(votes, owner)` | +| `by_counted_vote_name` | `(counted_votes, owner)` | +| `by_schedule_time` | `(virtual_scheduled_time, id)` — O(log N) 槽位调度 | + +**`witness_vote_object` 的索引:** + +| 标签 | 键 | +|------|-----| +| `by_id` | `id` | +| `by_account_witness` | `(account, validator)` — 唯一 | +| `by_witness_account` | `(validator, account)` — 唯一 | + +`by_schedule_time` 索引是区块生产调度器以 O(log N) 时间选择下一个验证者的方式。 + +--- + +## 提案和必需批准对象 + +**`proposal_object` 的索引:** + +| 标签 | 键 | +|------|-----| +| `by_id` | `id` | +| `by_account` | `(author, title)` — 唯一 | +| `by_expiration` | `expiration` — 非唯一 | + +**`required_approval_object` 的索引:** + +| 标签 | 键 | +|------|-----| +| `by_id` | `id` | +| `by_account` | `(account, proposal)` | + +--- + +## 邀请对象 + +| 标签 | 键 | +|------|-----| +| `by_id` | `id` | +| `by_invite_key` | 公钥 — 非唯一 | +| `by_status` | 状态 — 非唯一 | +| `by_creator` | 创建者 — 非唯一 | +| `by_receiver` | 接收者 — 非唯一 | + +--- + +## 辅助对象 + +**`withdraw_vesting_route`:** + +| 标签 | 键 | +|------|-----| +| `by_withdraw_route` | `(from_account, to_account)` — 唯一 | +| `by_destination` | `(to_account, id)` | + +**`escrow`:** + +| 标签 | 键 | +|------|-----| +| `by_from_id` | `(from, escrow_id)` — 唯一 | +| `by_to` | `(to, id)` | +| `by_agent` | `(agent, id)` | +| `by_ratification_deadline` | `(is_approved, ratification_deadline, id)` | + +**`vesting_delegation`:** + +| 标签 | 键 | +|------|-----| +| `by_delegation` | `(delegator, delegatee)` — 唯一 | + +**`vesting_delegation_expiration`:** + +| 标签 | 键 | +|------|-----| +| `by_expiration` | `expiration` — 非唯一 | +| `by_account_expiration` | `(delegator, expiration)` | + +--- + +## Fork 数据库 + +Fork 数据库(`fork_database`)维护一个区块内存树,用于管理链分叉。它独立于持久化的 chainbase 存储运行。 + +**已链接索引** — 规范链区块,按区块 ID 和区块号索引。 +**未链接索引** — 孤立的或无序的区块,其父区块尚未知晓。 + +``` +推送区块 + ├── 父区块在已链接索引中已知? + │ 是 → 链接区块,插入已链接索引,更新头部 + │ 否 → 插入未链接索引 + └── 尝试链接待处理的未链接区块 +``` + +当新区块到达且其 ID 与未链接区块的父区块匹配时,`_push_next()` 级联遍历未链接索引,将这些区块提升到已链接链中。 + +**分支操作:** +- `fetch_branch_from(first, second)` — 遍历两个分支找到共同祖先。返回 `(first_branch, second_branch)` 用于切换分叉。 +- `set_max_size(n)` — 修剪 n 个以前的区块,限制内存使用。 +- `walk_main_branch_to_num(n)` — 迭代主链到特定区块号。 + +**区块有效性:** 标记为无效的区块永远不会被提升。推送超出最大重排窗口的区块会触发断言。 + +--- + +## 索引管理 + +核心索引在 `database::initialize_indexes()` 期间注册。插件通过 `plugin_startup()` 中的 `add_plugin_index()` 注册额外索引。 + +```cpp +// 核心索引注册(database.cpp) +add_core_index(); +add_core_index(); +// ... + +// 插件索引注册(插件启动) +db.add_plugin_index(); +``` + +--- + +## 对象关系 + +``` +account ──(author)──► content ──► content_vote ◄──(voter)── account +account ──(delegator)──► vesting_delegation ──► account (delegatee) +account ──(account)──► witness_vote ──► witness (validator) +account ──(author)──► proposal ──► required_approval ◄──(account)── account +account ──(creator/receiver)──► invite +escrow: from + to + agent → escrow_object +``` + +--- + +## 查询优化指南 + +**快速查找:** +- 按名称查找账户 → `by_name`(唯一,O(log N)) +- 验证者调度 → `by_schedule_time`(按虚拟时间排序) +- 按 author+permlink 查找内容 → `by_permlink`(唯一复合) +- 按 content+weight 查找投票 → `by_content_weight_voter`(排行榜) + +**批量处理:** +- 锁仓提取 → 向前迭代 `by_next_vesting_withdrawal` +- 过期委托 → 向前迭代 `by_expiration` +- 过期提案 → 向前迭代 `by_expiration` + +**避免全扫描:** 始终使用带索引的标签。复合索引首先按最左边的键排序——将选择性最强或过滤最频繁的字段放在第一位。 + +--- + +## 插件的架构扩展 + +添加自定义对象类型: + +1. 定义继承自 `chainbase::object` 的对象类。 +2. 声明带有所需索引的 `chainbase::shared_multi_index_container`。 +3. 在 `plugin_startup()` 中通过 `db.add_plugin_index()` 注册。 +4. 添加 `FC_REFLECT` 宏用于序列化。 + +```cpp +class my_object : public chainbase::object { + id_type id; + account_name_type account; + uint64_t value; +}; + +using my_index = chainbase::shared_multi_index_container< + my_object, + indexed_by< + ordered_unique, + member>, + ordered_unique, + member> + > +>; +``` + +--- + +## 架构演进 + +新硬分叉 → 新字段或对象。指导原则: + +- 在硬分叉间保持现有主键语义稳定。 +- 将新字段添加为可选或有默认值;永远不要更改现有字段布局。 +- 在重放期间用 `has_hardfork()` 检查控制新索引使用。 +- 在现有标签旁添加新的 MultiIndex 标签——永远不要删除正在重放的节点可能查询的标签。 + +另见:[硬分叉管理](./hardfork-management.md)、[共享内存](../storage/shared-memory.md)。 + +--- + +参见:[插件开发](../development/plugin-development.md)、[虚拟操作](../protocol/virtual-operations.md)、[硬分叉管理](./hardfork-management.md)。 diff --git a/@l10n/zh-CN/docs/advanced/dlt-architecture.md b/@l10n/zh-CN/docs/advanced/dlt-architecture.md new file mode 100644 index 0000000000..d00701855b --- /dev/null +++ b/@l10n/zh-CN/docs/advanced/dlt-architecture.md @@ -0,0 +1,248 @@ +# DLT P2P 架构 + +VIZ Ledger 的 P2P 层已从基于 Graphene 摘要的旧协议(`node.cpp`)重新设计为专用的 DLT 原生协议(`dlt_p2p_node.cpp`)。公共插件 API 保持不变——只替换了内部实现。 + +--- + +## 概述 + +``` +之前: p2p_plugin → graphene::network::node (node.cpp, 6978 行, STCP, 库存 gossip) +之后: p2p_plugin → dlt_p2p_node (dlt_p2p_node.cpp, 2627 行, 原始 TCP, 范围式同步) +``` + +替换**就地**完成:相同的插件名称 `"p2p"`、相同端口(2001/4243)、相同的公共 API。所有依赖插件(验证者、snapshot 等)无需任何更改。 + +--- + +## 线格协议 + +原始 TCP——无 STCP 加密层。线格上的每条消息: + +``` +[4 字节: 数据大小 (uint32_t)] [4 字节: msg_type (uint32_t)] [N 字节: fc::raw::pack(T)] +``` + +### 消息类型(5100–5116) + +| 类型 | ID | 描述 | +|------|----|------| +| `dlt_hello_message` | 5100 | 握手:协议版本、head/LIB、DLT 范围、节点/分叉状态 | +| `dlt_hello_reply_message` | 5101 | 握手回复:exchange_enabled、fork_alignment | +| `dlt_range_request_message` | 5102 | 请求区块 ID 范围 | +| `dlt_range_reply_message` | 5103 | 回复可用区块范围 | +| `dlt_get_block_range_message` | 5104 | 获取 start..end 的区块,带 prev_block_id 检查 | +| `dlt_block_range_reply_message` | 5105 | 回复:区块向量 + is_last 标志 | +| `dlt_get_block_message` | 5106 | 按 ID 获取单个区块 | +| `dlt_block_reply_message` | 5107 | 回复:区块 + next_available + is_last | +| `dlt_not_available_message` | 5108 | 区块不可用 | +| `dlt_fork_status_message` | 5109 | 向对等节点广播当前分叉/节点状态 | +| `dlt_peer_exchange_request` | 5110 | 请求已知对等节点列表 | +| `dlt_peer_exchange_reply` | 5111 | 回复对等节点列表 | +| `dlt_peer_exchange_rate_limited` | 5112 | 速率限制通知:等待 N 秒 | +| `dlt_transaction_message` | 5113 | 广播已签名交易 | +| `dlt_soft_ban_message` | 5114 | 断开封禁对等节点前的通知 | +| `dlt_gap_fill_request` | 5115 | 请求特定区块号以填补空缺 | +| `dlt_gap_fill_reply` | 5116 | 回复请求的区块 | + +--- + +## Fiber 架构 + +所有 I/O 在单个 `fc::thread` 上使用协作式 fiber 运行——共享状态无需互斥锁: + +| Fiber | 角色 | +|-------|------| +| 接受循环 | 等待传入连接;拒绝重复 IP | +| 读取循环(每对等节点) | 读取消息;分发到 `on_message()` | +| 周期性任务 | 重新连接、检查停滞、对等节点统计、mempool 清理 | + +Fiber 在阻塞 I/O(`readsome()`、`writesome()`)时让出控制权,允许在单线程上处理多个对等节点而无需争用。 + +--- + +## 节点状态和对等节点生命周期 + +**节点状态:** `SYNC`(追赶中)/ `FORWARD`(实时,交换区块) + +**对等节点生命周期状态:** +``` +CONNECTING → HANDSHAKING → SYNCING → ACTIVE → DISCONNECTED → BANNED +``` + +超时:connecting=5s,handshaking=10s。重连退避:30s → 60s → … → 3600s,带 ±25% 抖动,稳定运行 5 分钟后重置。8 小时无响应后移除对等节点。 + +--- + +## 区块同步:SYNC 模式 + +处于 SYNC 模式的节点从 head 更高的对等节点顺序获取区块: + +1. `request_blocks_from_peer()` — 发送 `dlt_get_block_range_message`,请求我们 head 之后最多 200 个区块。 +2. `on_dlt_block_range_reply()` — 验证 `prev_block_id` 哈希链,应用每个区块。 +3. `check_sync_catchup()` — 将我们的 head 与所有对等节点的 head 比较;赶上后切换到 FORWARD。 +4. `sync_stagnation_check()` — 30s 无新区块后最多重试 3 次,然后切换到 FORWARD 并发出警告。 + +### 空缺填补 + +当我们的 head 与同步对等节点最早可用区块之间存在连续空缺时,`request_gap_fill()` 向 DLT 范围覆盖该空缺的任意对等节点发送 `dlt_gap_fill_request`(每次请求最多 100 个区块)。空缺填补在 SYNC 和 FORWARD 模式下均可工作: + +- 由 `on_dlt_block_reply()`(检测到乱序区块)和 `periodic_task()`(每 5s)触发。 +- 从启用交换的对等节点回退到任何 head 更高的活跃对等节点。 +- 如果没有对等节点拥有所需区块,回退到 SYNC 模式。 +- 大空缺以 100 个区块为块处理,请求间有 5s 冷却时间。 + +--- + +## 区块交换:FORWARD 模式 + +在 FORWARD 模式下,对等节点交换实时区块和交易: + +- `exchange_enabled` 标志控制对等节点是否从我们这里接收新区块。 +- 切换到 FORWARD 时,`dlt_fork_status_message` 发送到**所有**对等节点(不仅仅是启用交换的),通知它们我们已就绪。 +- `on_dlt_fork_status()` 在对等节点从 SYNC 切换到 FORWARD 时重新评估 `exchange_enabled`。 +- `check_forward_stagnation()` — 如果 head 30s 未推进且至少一个对等节点领先,切换到 SYNC。 + +--- + +## 分叉对齐和交换资格 + +在握手期间,`check_fork_alignment()` 执行多层区块 ID 匹配以确定对等节点是否在同一分叉: + +| 检查 | 条件 | +|------|------| +| 空对等节点 | `head_block_num == 0` → 已对齐(新节点) | +| 范围重叠 | 我们的 DLT 日志覆盖对等节点的 head → `is_block_known(head_id)` | +| 边界链接 | `peer_head + 1 == our_earliest` → 检查我们最早区块的 `previous == peer_head_id` | +| LIB 回退 | 始终检查 `is_block_known(lib_id)` | + +这种多层检查防止了 DLT 模式下的误"不同分叉"断连,在 DLT 模式中旧区块已被修剪,旧的单 ID 检查对同一链上的对等节点会失败。 + +--- + +## 分叉解决 + +分叉解决子系统跟踪竞争链的顶端: + +- **阈值:** 42 个区块的分歧触发 `resolve_fork()`(= `CHAIN_MAX_WITNESSES × 2`,一个完整的调度轮次)。 +- **选择:** 按投票权重最重的分支。 +- **迟滞:** 切换前需要连续 6 个区块作为赢家(`CONFIRMATION_BLOCKS`)。 +- **状态:** `_fork_status` 通过 `is_on_majority_fork()` 暴露,供验证者插件在生产区块前检查。 + +--- + +## 反垃圾 + +| 机制 | 描述 | +|------|------| +| `spam_strikes` 计数器 | 每个对等节点一个计数器;好数据包时重置;阈值=10 时软封禁 | +| 软封禁 | 设置 BANNED 状态持续 3600s;关闭前发送 `dlt_soft_ban_message` | +| 按 IP 去重 | 拒绝来自同一 IP 的重复连接(入站和出站均适用) | +| 广播去重 | `send_to_all_our_fork_peers()` 跟踪 `std::set` 跳过重复 IP | + +来自范围回复的重复区块和乱序区块会被静默跳过——不计为垃圾。反序列化错误不增加 spam strikes 计数。 + +--- + +## P2P Mempool + +独立的进程内 mempool(区别于链的 `_pending_tx`),在链接受前提供早期交易过滤: + +- 按 `tx_id` **去重**。 +- 达到限制时按最旧到期**驱逐**。 +- **限制**(可配置):最多 10,000 条、总计 100 MB、每笔交易 64 KB。 +- **临时条目**在 SYNC 模式下标记;切换到 FORWARD 时重新验证。 +- 收到区块时(`remove_transactions_in_block`)和分叉切换时(`prune_mempool_on_fork_switch`)**清理**。 + +--- + +## 对等节点交换 + +速率受限的对等节点发现: + +- 每个对等节点每 5 分钟窗口最多 3 次请求。 +- 子网多样性过滤器:每个回复中每个 `/24` 前缀最多 2 个对等节点。 +- 只共享运行时间 ≥600s 的对等节点。 +- 入站对等节点(临时端口)从交换回复中排除。 + +--- + +## 恢复机制 + +### 对等节点隔离(P53) + +当 60 秒内没有活跃对等节点时,`emergency_peer_reset()`: +- 清除所有软封禁(BANNED → DISCONNECTED,重置 spam strikes)。 +- 将所有断开连接的对等节点退避重置为最小值,立即重连。 + +### 区块处理暂停/恢复 + +`pause_block_processing()` / `resume_block_processing()` 允许 snapshot 插件在状态序列化期间暂停 P2P 区块接收。周期性任务在暂停时跳过访问数据库的操作。 + +### 启动宽限期(P22) + +启动后前 60 秒内,head 10 个区块以内的区块被视为 `FORK_DB_ONLY` 而非 `DEAD_FORK`——防止 fork_db 从区块日志重建时的级联拒绝。 + +--- + +## 区块接受结果 + +`dlt_block_accept_result` 枚举替代旧的布尔返回: + +| 值 | 含义 | +|----|------| +| `ACCEPTED` | 区块已应用到链(成为新 head) | +| `FORK_DB_ONLY` | 存储在 fork_db 但未应用(无法链接、竞争分叉) | +| `DEAD_FORK` | 死分叉中处于/低于 head 的区块——对等节点被软封禁 | +| `ALREADY_KNOWN` | 已有此区块(重复、`block_too_old_exception`) | +| `REJECTED` | 验证完全失败 | + +--- + +## 配置参考 + +| 选项 | 默认值 | 描述 | +|------|--------|------| +| `dlt-block-log-max-blocks` | 100,000 | DLT 滚动区块日志中的最大区块数 | +| `dlt-peer-max-disconnect-hours` | 8 | 无响应后多少小时移除对等节点 | +| `dlt-mempool-max-tx` | 10,000 | Mempool 条目硬限制 | +| `dlt-mempool-max-bytes` | 100 MB | Mempool 总内存硬限制 | +| `dlt-mempool-max-tx-size` | 64 KB | 拒绝超大交易 | +| `dlt-mempool-max-expiration-hours` | 24 | 拒绝远期到期交易 | +| `dlt-peer-exchange-max-per-reply` | 10 | 每次交换回复的最大对等节点数 | +| `dlt-peer-exchange-max-per-subnet` | 2 | 反 Sybil:每 /24 最多 2 个对等节点 | +| `dlt-peer-exchange-min-uptime-sec` | 600 | 共享对等节点前的最小运行时间 | +| `dlt-stats-interval-sec` | 300 | 对等节点统计日志间隔(最小 30) | + +--- + +## 彩色日志 + +| 颜色 | 含义 | +|------|------| +| 绿色 | 同步进度和区块生产 | +| 白色 | 正常区块交换 | +| 红色 | 分叉事件 | +| 深灰色 | 交易处理 | +| 橙色 | 警告(软封禁、停滞、空缺) | +| 青色 | 对等节点统计输出 | + +--- + +## 委托模式 + +网络库只链接 `fc` 和 `graphene_protocol`——不链接 `graphene_chain`。`dlt_p2p_delegate` 抽象接口弥补了这一差距: + +``` +dlt_p2p_node(网络库) ←→ dlt_p2p_delegate(接口) ←→ dlt_delegate(p2p_plugin) +``` + +`p2p_plugin.cpp` 中的 `dlt_delegate` 实现: +- `read_block_by_num()` — 检查 dlt_block_log,然后检查 fork_db。 +- `accept_block()` — 调用 `push_block()`;捕获 `unlinkable_block_exception` → 存储到 fork_db。 +- `get_fork_branch_tips()` — 从当前 head 周围的 fork_db 获取。 +- `is_tapos_block_known()` — 委托给 `db.is_known_block()`。 + +--- + +参见:[P2P 概述](../p2p/overview.md)、[同步场景](../p2p/sync-scenarios.md)、[Snapshot 插件](../storage/snapshots.md)、[区块日志](../storage/block-log.md)。 diff --git a/@l10n/zh-CN/docs/advanced/hardfork-management.md b/@l10n/zh-CN/docs/advanced/hardfork-management.md new file mode 100644 index 0000000000..02f1cc0a59 --- /dev/null +++ b/@l10n/zh-CN/docs/advanced/hardfork-management.md @@ -0,0 +1,199 @@ +# 硬分叉管理 + +VIZ Ledger 通过确定性硬分叉系统协调协议升级。硬分叉在编译时定义,由验证者共识激活,并在区块处理期间自动应用——无需节点重启。 + +--- + +## 工作原理 + +### 1. 定义文件 + +每个硬分叉在 `libraries/chain/hardfork.d/` 中有一个专用的 `*.hf` 文件,定义编译时常量: + +```cpp +// 示例:9.hf +#define CHAIN_HARDFORK_9 9 +#define CHAIN_HARDFORK_9_VERSION version(1, 0, 9) +#define CHAIN_HARDFORK_9_TIME (fc::time_point_sec(1650000000)) +``` + +前言文件(`0-preamble.hf`)声明总数和 hardfork_property 对象架构。当前:`CHAIN_NUM_HARDFORKS = 12`。 + +### 2. 验证者共识 + +验证者通过 `versioned_chain_properties_update_operation` 发布其首选的下一个硬分叉版本。在每次验证者调度更新期间,节点: + +1. 收集每个活跃验证者支持的硬分叉版本。 +2. 如果多数同意的版本 ≥ 下一个计划版本,设置 `next_hardfork` 和 `next_hardfork_time`。 + +### 3. 区块处理期间的激活 + +当头区块时间超过 `next_hardfork_time` 且足够多的验证者支持该版本时,节点调用 `apply_hardfork(N)`。所有后续行为变化都通过评估器、通胀逻辑和链属性中的 `has_hardfork(N)` 检查来控制。 + +--- + +## 硬分叉历史 + +| HF | 主要变更 | +|----|---------| +| 1 | 中值计算修复 | +| 2 | 委员会批准阈值修复 | +| 3 | 小型协议更正 | +| 4 | 奖励操作、自定义操作序列 | +| 5 | 带宽和权限修复 | +| 6 | 验证者缺席惩罚、投票计数 | +| 7 | 社交/内容调整 | +| 8 | 协议清理 | +| 9 | 邀请系统、付费订阅、验证者费用、withdraw_intervals | +| 10 | 通胀模型 | +| 11 | 发行模型变更 | +| 12 | 紧急共识恢复(见下文) | + +--- + +## HF12:紧急共识恢复 + +HF12 引入了区块生产停滞时的自动网络恢复。 + +### 激活 + +如果最后一个不可逆区块(LIB)的时间戳比挂钟时间滞后超过 `CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC`(1 小时),紧急模式自动激活。创建一个具有已知公钥(`CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY`)的紧急验证者账户(`CHAIN_EMERGENCY_WITNESS_ACCOUNT = "committee"`),并插入区块生产调度中。 + +### 三态安全执行 + +| 网络状态 | 条件 | 行为 | +|---------|------|------| +| 健康 | 参与率 ≥ 33% | 强制执行安全默认值;覆盖手动配置 | +| 困难 | 参与率 < 33% | 遵守手动配置覆盖,以便运营者恢复 | +| 紧急 | 紧急模式激活 | 自动绕过过期和参与率检查 | + +### 增强的验证者调度 + +- **混合调度**:紧急验证者填充不可用的槽位,同时保持真实验证者的位置。 +- **投票加权分叉切换**:使用来自唯一非 committee 验证者的原始票数之和作为主要分叉比较标准。 +- **中值排除**:紧急验证者的属性投票从链参数中值计算中排除。 + +--- + +## Hardfork Property 对象 + +持久化的 `hardfork_property_object`(chainbase 中的单例)跟踪: + +| 字段 | 描述 | +|------|------| +| `processed_hardforks` | 已应用硬分叉时间的向量 | +| `last_hardfork` | 最后一个已应用硬分叉的 ID | +| `current_hardfork_version` | 当前强制执行的协议版本 | +| `next_hardfork` | 下一个计划的硬分叉版本 | +| `next_hardfork_time` | `next_hardfork` 何时激活 | + +对于 HF12+,额外字段跟踪紧急共识状态。 + +--- + +## 数据库生命周期与硬分叉 + +### 打开时 + +``` +database::open() + → init_schema(), initialize_indexes(), initialize_evaluators() + → 从 chainbase 加载 hardfork_property + → init_hardforks() ← 填充 _hardfork_times[] 和 _hardfork_versions[] 数组 + → assert: chainbase 修订版本 == head_block_num +``` + +### 重新索引时 + +从 block_log 重放所有区块,带跳过标志(无签名检查、无 merkle 检查)以提高速度。`apply_hardfork()` 在重放期间的每个硬分叉边界触发,确保确定性的状态重建。 + +### 应用区块时 + +``` +process_hardforks() + → 检查 next_hardfork_time 是否已过 + → 检查验证者共识是否支持该版本 + → 若是: apply_hardfork(N) + → 运行版本特定的状态迁移 + → 更新 current_hardfork_version +``` + +--- + +## 回滚和分叉切换 + +数据库使用撤销会话进行原子性区块应用——部分失败会干净地回滚。 + +对于分叉切换,`fetch_branch_from()` 将两个分支返回到它们的共同祖先,弹出当前分支,然后重新应用新分支。HF12 在此过程中添加了投票加权链比较。 + +如果区块应用失败,fork 数据库条目被删除并抛出异常。P2P 层通过适当标记发送对等节点来处理该异常。 + +--- + +## 添加新硬分叉 + +1. **在 `libraries/chain/hardfork.d/` 中创建 `N.hf`:** + ```cpp + #define CHAIN_HARDFORK_N N + #define CHAIN_HARDFORK_N_VERSION version(1, 0, N) + #define CHAIN_HARDFORK_N_TIME (fc::time_point_sec(UNIX_TIMESTAMP)) + ``` + +2. **在 `0-preamble.hf` 中将 `CHAIN_NUM_HARDFORKS` 递增到 N。** + +3. **在评估器和运行时逻辑中控制新行为:** + ```cpp + if (db.has_hardfork(CHAIN_HARDFORK_N)) { + // 新行为 + } else { + // 旧行为 + } + ``` + +4. **在 `database.cpp` 的 `apply_hardfork(N)` 中添加状态迁移:** + ```cpp + case CHAIN_HARDFORK_N: + // 一次性迁移代码 + break; + ``` + +5. **考虑紧急模式**:如果硬分叉修改了验证者调度或链参数,确保紧急验证者从受影响的计算中排除。 + +6. **使用重新索引测试**:对主网区块数据运行完整重新索引,以确认确定性重放产生相同状态。 + +--- + +## 故障排查 + +| 症状 | 可能原因 | 解决方案 | +|------|----------|---------| +| 硬分叉未触发 | 未达到验证者共识 | 验证验证者是否发布目标版本;检查 `get_next_scheduled_hardfork()` API | +| 打开时修订版本不匹配 | chainbase 修订版本 ≠ head block num | 从区块日志重新索引或从快照恢复 | +| 重新索引期间内存耗尽 | 共享内存太小 | 增加 `shared-file-size`;启用自动调整大小 | +| 紧急模式未激活 | HF12 尚未应用 | 验证 `current_hardfork_version` ≥ 1.0.12 | +| 重新索引后状态不匹配 | 非确定性的 has_hardfork() 分支 | 审计 `apply_hardfork()` 的副作用 | + +**诊断:** + +```json +{ "method": "database_api.get_hardfork_version", "params": [] } +{ "method": "database_api.get_next_scheduled_hardfork", "params": [] } +``` + +--- + +## 升级检查表 + +- [ ] 定义 `N.hf`,时间戳合理(与验证者协调) +- [ ] 在 `0-preamble.hf` 中递增 `CHAIN_NUM_HARDFORKS` +- [ ] 实现 `apply_hardfork(N)` 迁移 +- [ ] 用 `has_hardfork()` 检查控制行为变化 +- [ ] 部署前备份数据库和区块日志 +- [ ] 以只读模式启动节点验证兼容性 +- [ ] 监控日志中的硬分叉激活事件 +- [ ] 与验证者协调发布新版本 +- [ ] 确认 `get_next_scheduled_hardfork()` 显示预期的版本/时间 + +--- + +参见:[链属性](../governance/chain-properties.md)、[验证者](../protocol/operations/validators.md)、[数据库架构](./database-schema.md)、[Database API](../plugins/database-api.md)。 diff --git a/@l10n/zh-CN/docs/advanced/security.md b/@l10n/zh-CN/docs/advanced/security.md new file mode 100644 index 0000000000..c26d74d5ba --- /dev/null +++ b/@l10n/zh-CN/docs/advanced/security.md @@ -0,0 +1,162 @@ +# 安全实现 + +VIZ Ledger 的安全模型基于三个支柱:基于阈值的权限验证、确定性签名验证和加密的对等节点传输。本页介绍每个子系统,并为运营者和插件开发者提供指导。 + +--- + +## 权限模型 + +每个账户有三个权限级别——master、active 和 regular——每个级别表示为带有权重阈值的密钥和/或账户引用的加权集合。 + +``` +Authority { + weight_threshold: uint32 + key_auths: { PublicKey → weight } + account_auths: { AccountName → weight } +} +``` + +当提供的签名权重之和(以及递归解析的账户权限)达到或超过 `weight_threshold` 时,操作即被授权。 + +**递归深度**受限,以防止嵌套账户权限链中的无限循环。 + +相同的权限结构作为 `SharedAuthority` 存储在共享内存中,使用与 Boost.Interprocess 映射文件兼容的进程间分配器。 + +--- + +## 签名验证 + +交易签名验证使用确定性的 secp256k1 ECDSA: + +1. **摘要**:`sha256(chain_id || serialized_transaction)` +2. **恢复**:`secp256k1_recover(signature, digest)` → 公钥 +3. **权限检查**:`sign_state.check_authority(account, level)` 遍历权限树,验证恢复的密钥集满足阈值。 + +`sign_state` 引擎: +- 维护提供的签名集及其恢复的密钥。 +- 递归解析账户权限,直到最大深度。 +- 验证后过滤未使用的签名。 + +**对于插件开发者:** 在处理敏感操作前,使用 `auth_util` 插件的 `check_authority_signature` API 验证签名: + +```json +{ + "method": "auth_util.check_authority_signature", + "params": ["alice", "regular", "", ["", ""]] +} +``` + +若权限满足则返回已验证签名密钥的集合,否则返回错误。 + +--- + +## 对等节点传输加密 + +所有点对点连接使用 ECDH 密钥交换 + AES 流加密: + +1. 每一方在连接时生成临时密钥对。 +2. ECDH 从临时密钥生成共享密钥。 +3. 使用共享密钥初始化 AES 编码器/解码器流。 +4. 所有后续消息均被加密。 + +这防止了被动窃听。每个连接使用新鲜的临时密钥,因此一个会话的泄露不影响其他会话。 + +实现位于 `stcp_socket`(`libraries/network/stcp_socket.cpp`)。 + +--- + +## API 暴露 + +`webserver` 插件通过 HTTP 和 WebSocket 提供 JSON-RPC 服务。安全配置: + +```ini +# 绑定到 loopback 仅供内部访问 +webserver-http-endpoint = 127.0.0.1:8090 +webserver-ws-endpoint = 127.0.0.1:8091 + +# 使用反向代理(nginx、caddy)通过 TLS 进行公共访问 +``` + +**线程池大小:** Webserver 运行可配置数量的线程。将 `webserver-thread-pool-size` 设置为与预期并发请求负载匹配。大小不足会导致请求排队;过大会浪费资源。 + +```ini +webserver-thread-pool-size = 4 +``` + +--- + +## 网络安全措施 + +- **加密通道**:所有对等节点连接均已加密(ECDH + AES)。被动窃听不可能实现。 +- **对等节点数据库**:P2P 节点维护对等节点数据库和传播时间元数据。 +- **软封禁**:行为不正确的对等节点(发送无效区块、仅分叉数据无进展)接受临时软封禁,而非永久断开连接。 +- **带宽限制**:可通过 `max-send-buffer-size` 和相关 P2P 选项配置。 + +--- + +## 漏洞评估 + +**常见风险:** + +| 风险 | 缓解措施 | +|------|----------| +| 通过畸形签名绕过权限 | 限制递归深度;严格的权重阈值检查 | +| 临时密钥的弱随机性 | 使用 secp256k1 的确定性密钥生成 | +| 未加密 RPC 上的中间人攻击 | 将 webserver 绑定到 loopback;对公共端点使用 TLS 反向代理 | +| 通过超大载荷的 DoS | JSON-RPC 载荷大小限制;webserver 线程池控制并发 | +| 嵌套权限耗尽 | 在 `sign_state` 中强制执行最大递归深度 | + +**渗透测试检查表:** +- 提交畸形或不完整的签名以验证权限绕过保护。 +- 使用深度嵌套的账户权限链测试递归深度限制。 +- 通过网络抓包验证传输加密(线路上不应出现明文)。 +- 对 webserver 端点进行压力测试,检测内存耗尽和队列饱和。 + +--- + +## 插件开发安全最佳实践 + +**输入验证:** +- 在插件边界拒绝畸形或超大的 JSON-RPC 载荷。 +- 在处理前针对预期类型和范围验证所有参数。 + +**认证:** +- 在应用需要授权的状态变更前,始终使用 `auth_util.check_authority_signature`。 +- 未验证签名前,永远不要信任账户名或密钥引用。 + +**恒定时间比较:** +- 对秘密比较使用 `fc::crypto::secure_compare` 或等效工具,以防止定时侧信道攻击。 + +**不存储明文凭据:** +- 永远不要在插件状态或日志中存储私钥。 +- 每个会话生成临时密钥;永不复用。 + +**每个权限级别的威胁模型:** +- `regular` 权限:社交/内容操作——最低权限。 +- `active` 权限:资金、质押、投票——中等权限。 +- `master` 权限:密钥轮换、恢复——最高权限。在任何 UI 中需要明确的用户确认。 + +--- + +## 监控和事件响应 + +**需要监控的指标:** +- 对等节点连接数及流失率。 +- Webserver 线程池队列深度和响应延迟。 +- 签名验证失败率(在日志 `warn` 级别可见)。 +- 每个对等节点连接的带宽。 + +**事件响应:** +1. 隔离受影响的端点(将 `webserver-http-endpoint` 限制为 loopback)。 +2. 若验证者密钥泄露,通过 master 权限轮换签名密钥。 +3. 密钥轮换后重新验证账户权限。 +4. 检查 `sign_state` 失败和异常权限链周围的日志。 + +**密钥轮换:** +- 验证者签名密钥:使用新签名密钥执行 `update_validator` 操作。 +- 账户密钥:使用新 master/active/regular 密钥执行 `update_account`。 +- 所有密钥变更在下一个区块后立即生效。 + +--- + +参见:[插件开发](../development/plugin-development.md)、[数据类型](../protocol/data-types.md)、[验证者](../protocol/operations/validators.md)、[Webserver 插件](../plugins/webserver.md)。 diff --git a/@l10n/zh-CN/docs/api/cli-wallet.md b/@l10n/zh-CN/docs/api/cli-wallet.md new file mode 100644 index 0000000000..34ea70520c --- /dev/null +++ b/@l10n/zh-CN/docs/api/cli-wallet.md @@ -0,0 +1,251 @@ +# CLI 钱包 + +`cli_wallet` 可执行文件提供交互式命令行界面,用于管理账户、签名和广播交易以及查询区块链。 + +--- + +## 连接 + +```bash +cli_wallet --server-rpc-endpoint="ws://127.0.0.1:8091" +``` + +首次运行时设置密码: +``` +new >>> set_password "yourpassword" +``` + +然后解锁: +``` +locked >>> unlock "yourpassword" +``` + +--- + +## 钱包管理 + +| 命令 | 描述 | +|------|------| +| `is_new` | 如果尚未设置密码则返回 `true` | +| `is_locked` | 如果钱包已锁定则返回 `true` | +| `lock` | 锁定钱包 | +| `unlock "password"` | 解锁钱包 | +| `set_password "password"` | 设置或更改密码 | +| `load_wallet_file "file.json"` | 加载钱包文件(`""` = 重新加载当前) | +| `save_wallet_file "file.json"` | 保存钱包到文件 | +| `set_transaction_expiration 60` | 设置交易 TTL(秒) | +| `quit` | 退出钱包 | +| `help` | 列出所有命令 | +| `gethelp "command"` | 某个命令的详细帮助 | + +--- + +## 密钥管理 + +| 命令 | 描述 | +|------|------| +| `import_key "5K..."` | 导入 WIF 私钥 | +| `suggest_brain_key` | 生成带公私钥对的助记密钥 | +| `list_keys` | 列出钱包中的所有私钥(WIF) | +| `get_private_key "VIZpubkey..."` | 获取已知公钥的 WIF | +| `get_private_key_from_password "account" "role" "password"` | 从凭据派生密钥 | +| `normalize_brain_key "words..."` | 规范化助记密钥字符串 | + +--- + +## 查询 + +| 命令 | 描述 | +|------|------| +| `info` | 当前区块链状态 | +| `database_info` | 数据库对象统计 | +| `get_block 1000000` | 区块数据 | +| `get_ops_in_block 1000000 false` | 区块中的操作(`true` = 仅虚拟操作) | +| `get_active_validators` | 活跃验证者集合 | +| `get_account "alice"` | 账户对象 | +| `list_accounts "" 100` | 分页账户列表 | +| `list_my_accounts` | 钱包中有密钥的账户 | +| `get_account_history "alice" -1 100` | alice 的最后 100 个操作 | +| `get_transaction "txid..."` | 按 ID 查询交易 | +| `get_master_history "alice"` | 主密钥更改历史 | +| `get_withdraw_routes "alice" "all"` | 提取路由(`"incoming"`、`"outgoing"`、`"all"`) | +| `get_proposed_transactions "alice" 0 100` | 需要 alice 批准的提案 | + +--- + +## 账户操作 + +| 命令 | 描述 | +|------|------| +| `create_account "creator" "1.000 VIZ" "10.000000 SHARES" "newaccount" "{}" true` | 使用自动生成的密钥创建账户 | +| `create_account_with_keys "creator" "1.000 VIZ" "10.000000 SHARES" "newaccount" "{}" "VIZmaster..." "VIZactive..." "VIZregular..." "VIZmemo..." true` | 使用指定密钥创建账户 | +| `update_account "account" "{}" "VIZm..." "VIZa..." "VIZr..." "VIZmemo..." true` | 更新所有密钥和元数据 | +| `update_account_auth_key "account" "active" "VIZnewkey..." 1 true` | 向授权添加密钥(权重 0 = 删除) | +| `update_account_auth_account "account" "active" "guardian" 1 true` | 向授权添加账户 | +| `update_account_auth_threshold "account" "active" 2 true` | 设置授权权重阈值 | +| `update_account_meta "account" "{\"key\":\"value\"}" true` | 更新 JSON 元数据(regular 授权) | +| `update_account_memo_key "account" "VIZnewmemo..." true` | 更新 memo 密钥 | +| `delegate_vesting_shares "alice" "bob" "100.000000 SHARES" true` | 委托 SHARES(0 = 移除) | + +--- + +## 转账和质押 + +| 命令 | 描述 | +|------|------| +| `transfer "alice" "bob" "10.000 VIZ" "memo" true` | 转账 VIZ(memo 前缀 `#` = 加密) | +| `transfer_to_vesting "alice" "alice" "100.000 VIZ" true` | 将 VIZ 质押为 SHARES | +| `withdraw_vesting "alice" "100.000000 SHARES" true` | 开始解质押(0 = 取消) | +| `set_withdraw_vesting_route "alice" "bob" 5000 false true` | 将 50% 提取路由到 bob 作为 VIZ | + +--- + +## 验证者操作 + +| 命令 | 描述 | +|------|------| +| `list_validators "" 100` | 列出验证者 | +| `get_validator "validatorname"` | 验证者对象 | +| `update_validator "myvalidator" "https://url" "VIZsigningkey..." true` | 注册/更新验证者 | +| `update_chain_properties "myvalidator" {...} true` | 对链属性投票(init 格式) | +| `versioned_update_chain_properties "myvalidator" {...} true` | 对版本化链属性投票(hf9 格式) | +| `vote_for_validator "alice" "myvalidator" true true` | 为验证者投票(`false` = 撤销投票) | +| `set_voting_proxy "alice" "proxy" true` | 设置投票代理(`""` = 移除) | + +--- + +## 托管操作 + +| 命令 | 描述 | +|------|------| +| `escrow_transfer "alice" "bob" "agent" 1 "100.000 VIZ" "1.000 VIZ" "2024-06-01T00:00:00" "2024-07-01T00:00:00" "{}" true` | 创建托管 | +| `escrow_approve "alice" "bob" "agent" "bob" 1 true true` | 批准托管(who = `"bob"` 或 `"agent"`) | +| `escrow_dispute "alice" "bob" "agent" "alice" 1 true` | 发起争议(who = `"alice"` 或 `"bob"`) | +| `escrow_release "alice" "bob" "agent" "agent" "bob" 1 "100.000 VIZ" true` | 释放资金 | + +--- + +## 恢复操作 + +| 命令 | 描述 | +|------|------| +| `request_account_recovery "recovery" "victim" {"weight_threshold":1,...} true` | 以恢复账户身份请求恢复 | +| `recover_account "victim" {"recent_master_auth"} {"new_master_auth"} true` | 确认恢复 | +| `change_recovery_account "account" "new_recovery" true` | 更改恢复账户(30 天延迟) | + +--- + +## 委员会操作 + +| 命令 | 描述 | +|------|------| +| `committee_worker_create_request "creator" "https://url" "worker" "100.000 VIZ" "500.000 VIZ" 604800 true` | 创建资金请求 | +| `committee_worker_cancel_request "creator" 123 true` | 取消请求 | +| `committee_vote_request "voter" 123 10000 true` | 投票(+10000 = 完全支持,-10000 = 完全反对,0 = 撤销) | + +--- + +## 邀请操作 + +| 命令 | 描述 | +|------|------| +| `create_invite "creator" "10.000 VIZ" "VIZinvitekey..." true` | 创建邀请 | +| `claim_invite_balance "initiator" "receiver" "5Kinvitesecret..." true` | 领取邀请余额 | +| `invite_registration "initiator" "newaccount" "5Kinvitesecret..." "VIZnewaccountkey..." true` | 从邀请创建账户 | +| `use_invite_balance "initiator" "receiver" "5Kinvitesecret..." true` | 使用邀请余额(可能归属为 SHARES) | + +--- + +## 奖励操作 + +| 命令 | 描述 | +|------|------| +| `award "alice" "bob" 1000 0 "Great work!" [] true` | 基于能量的奖励 | +| `fixed_award "alice" "bob" "10.000000 SHARES" 5000 0 "Reward" [] true` | 固定 SHARES 数量奖励 | + +受益人格式:`[{"account":"charlie","weight":2000}]` + +--- + +## 订阅操作 + +| 命令 | 描述 | +|------|------| +| `set_paid_subscription "account" "https://url" 3 "10.000 VIZ" 30 true` | 创建订阅(3 级,10 VIZ/期,30 天周期) | +| `paid_subscribe "subscriber" "account" 2 "20.000 VIZ" 1 true true` | 订阅级别 2 | + +--- + +## 账户市场 + +| 命令 | 描述 | +|------|------| +| `set_account_price "account" "account" "100.000 VIZ" true true` | 挂牌出售 | +| `set_subaccount_price "account" "account" "50.000 VIZ" true true` | 挂牌出售子账户创建权 | +| `buy_account "buyer" "account" "100.000 VIZ" "VIZnewkey..." "0.000 VIZ" true` | 购买账户 | +| `target_account_sale "account" "account" "targetbuyer" "100.000 VIZ" true true` | 定向销售 | + +--- + +## 自定义操作 + +```bash +custom [] ["alice"] "my_app" "{\"action\":\"follow\",\"target\":\"bob\"}" true +``` + +参数:`required_active_auths` `required_regular_auths` `id` `json` `broadcast` + +--- + +## 交易构建器 + +构建并签名包含多个操作的自定义交易: + +```bash +begin_builder_transaction # 返回句柄(例如 0) +add_operation_to_builder_transaction 0 [2,{"from":"alice","to":"bob","amount":"10.000 VIZ","memo":""}] +sign_builder_transaction 0 true # 签名并广播 +``` + +| 命令 | 描述 | +|------|------| +| `begin_builder_transaction` | 开始新交易(返回句柄) | +| `add_operation_to_builder_transaction handle [type_id, op]` | 添加操作 | +| `replace_operation_in_builder_transaction handle idx [type_id, op]` | 替换操作 | +| `preview_builder_transaction handle` | 预览交易 JSON | +| `sign_builder_transaction handle broadcast` | 签名(并可选广播) | +| `propose_builder_transaction handle author title memo expiry review broadcast` | 包装为提案 | +| `remove_builder_transaction handle` | 丢弃 | +| `get_prototype_operation "transfer_operation"` | 获取空操作模板 | +| `serialize_transaction {trx}` | 获取十六进制序列化 | +| `sign_transaction {trx} broadcast` | 签名任意交易 | + +--- + +## NS DNS 助手 + +在账户元数据中存储 DNS 记录: + +```bash +ns_set_records "myaccount" {"a_records":["188.120.231.153"],"ssl_hash":"4a4613...","ttl":28800} true +ns_get_summary "myaccount" +ns_extract_a_records "myaccount" +ns_remove_records "myaccount" true +``` + +验证助手:`ns_validate_ipv4`、`ns_validate_sha256_hash`、`ns_validate_ttl`、`ns_validate_ssl_txt_record`、`ns_validate_metadata`、`ns_create_metadata`。 + +--- + +## 私信 + +```bash +get_encrypted_memo "alice" "bob" "#secret message" +decrypt_memo "#encrypteddata..." +get_inbox "myaccount" "2024-01-15T00:00:00" 100 0 +get_outbox "myaccount" "2024-01-15T00:00:00" 100 0 +``` + +--- + +参见:[JSON-RPC API](./json-rpc.md)、[操作概述](../protocol/operations/overview.md)、[数据类型](../protocol/data-types.md)。 diff --git a/@l10n/zh-CN/docs/api/client-libraries.md b/@l10n/zh-CN/docs/api/client-libraries.md new file mode 100644 index 0000000000..71076a0f17 --- /dev/null +++ b/@l10n/zh-CN/docs/api/client-libraries.md @@ -0,0 +1,140 @@ +# 客户端库 + +Python、PHP 和 JavaScript 均有官方客户端库。所有库通过 JSON-RPC API 与 VIZ 节点通信,并在本地完成交易签名。 + +--- + +## Python — viz-python-lib + +**仓库:** https://github.com/VIZ-Blockchain/viz-python-lib + +### 安装 + +```bash +pip install viz-python-lib +``` + +### 快速开始 + +```python +from viz import Client + +viz = Client( + node="wss://node.viz.cx/ws", + keys=["5...private_key..."] +) + +# 向账户奖励能量 +viz.award("receiver_account", 10.5, "with love", None, "your_account") +``` + +### 功能特性 + +- 支持 WebSocket(`wss://`)和 HTTP(`https://`)传输 +- 完整的交易广播:所有协议操作 +- 本地密钥管理——私钥不离开客户端 +- 多节点自动重试和故障转移 +- 兼容 Python 3 + +--- + +## PHP — viz-php-lib + +**仓库:** https://github.com/VIZ-Blockchain/viz-php-lib + +### 安装 + +该库使用 PSR-4 自动加载,无需 Composer。克隆或下载仓库后引入自动加载文件: + +```php +require_once '/path/to/viz-php-lib/autoload.php'; +``` + +**PHP 扩展要求:** `gmp`(首选)或 `bcmath`,用于大整数运算。 + +### 快速开始 + +```php +$private_key = '5...your_private_key...'; +$tx = new VIZ\Transaction('https://node.viz.plus/', $private_key); + +// 构建并广播奖励交易 +$tx_data = $tx->award($account, 'committee', 1000, 0, 'memo'); +$tx_status = $tx->execute($tx_data['json']); +``` + +### 功能特性 + +- 支持全部 40 个协议操作 +- AES-256-CBC 备注加密/解密 +- 无外部依赖——仅使用标准 PHP 扩展 +- 兼容 PHP 7.x 和 8.x + +--- + +## JavaScript — viz-js-lib + +**仓库:** https://github.com/VIZ-Blockchain/viz-js-lib +**npm:** https://www.npmjs.com/package/viz-js-lib + +### 安装 + +```bash +npm install viz-js-lib --save +``` + +### CDN(浏览器) + +```html + + + + + +``` + +### 快速开始 + +```js +const viz = require('viz-js-lib'); + +// 从凭据派生 WIF 密钥 +var wif = viz.auth.toWif(username, password, 'regular'); + +// 广播投票 +viz.broadcast.vote(wif, voter, author, permlink, weight, function(err, result) { + console.log(err, result); +}); +``` + +### 节点配置 + +```js +viz.config.set('websocket', 'wss://node.viz.cx/ws'); +// 或 HTTP: +viz.config.set('websocket', 'https://node.viz.cx/'); +``` + +### 功能特性 + +- 支持 WebSocket(`ws://` / `wss://`)和 HTTP(`http://` / `https://`)传输 +- 可在 Node.js 和现代浏览器中使用 +- 密钥工具:`toWif`、`toPublic`、`isWif`、`signTransaction` +- 覆盖所有协议操作的完整广播 API +- MIT 许可证 + +--- + +## 如何选择库 + +| 标准 | Python | PHP | JavaScript | +|------|--------|-----|-----------| +| 安装方式 | `pip` | 手动 PSR-4 | `npm` | +| 传输协议 | WS / HTTP | HTTP | WS / HTTP | +| 运行环境 | Python 3 | PHP 7+ | Node.js / 浏览器 | +| 依赖项 | 极少 | GMP 或 BCMath | 无(已打包) | +| 许可证 | MIT | MIT | MIT | + +--- + +参见:[JSON-RPC API](./json-rpc.md)、[CLI Wallet](./cli-wallet.md)。 diff --git a/@l10n/zh-CN/docs/api/json-rpc.md b/@l10n/zh-CN/docs/api/json-rpc.md new file mode 100644 index 0000000000..761e53aa66 --- /dev/null +++ b/@l10n/zh-CN/docs/api/json-rpc.md @@ -0,0 +1,228 @@ +# JSON-RPC API + +所有 VIZ 节点 API 使用 JSON-RPC 2.0,通过 HTTP POST 或 WebSocket 访问。 + +--- + +## 请求格式 + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "method": "api_name.method_name", + "params": [arg1, arg2] +} +``` + +- `id` 可以是任意数字或字符串;响应中会原样返回。 +- `params` 可以是数组或对象,取决于方法。 +- 支持 HTTP POST 和 WebSocket。订阅需要 WebSocket。 + +--- + +## 响应格式 + +**成功:** +```json +{ "jsonrpc": "2.0", "id": 1, "result": { ... } } +``` + +**错误:** +```json +{ "jsonrpc": "2.0", "id": 1, "error": { "code": -32602, "message": "Invalid params" } } +``` + +### 错误代码 + +| 代码 | 含义 | +|------|------| +| `-32700` | 解析错误——无效的 JSON | +| `-32600` | 无效请求 | +| `-32601` | 方法未找到 | +| `-32602` | 无效参数 | +| `-32603` | 内部错误 | +| `-32099` 到 `-32000` | 服务器错误(处理器抛出的异常) | + +--- + +## 插件命名空间 + +| 命名空间 | 状态 | 描述 | +|---------|------|------| +| `database_api` | 活跃 | 区块、账户、链状态查询 | +| `network_broadcast_api` | 活跃 | 交易和区块广播 | +| `witness_api` | 活跃 | 验证者查询 | +| `account_by_key` | 活跃 | 反向密钥查找 | +| `account_history` | 活跃 | 每账户操作历史 | +| `operation_history` | 活跃 | 区块操作查询 | +| `committee_api` | 活跃 | 委员会请求查询 | +| `invite_api` | 活跃 | 邀请查询 | +| `paid_subscription_api` | 活跃 | 订阅查询 | +| `custom_protocol_api` | 活跃 | 自定义协议元数据 | +| `auth_util` | 活跃 | 权限验证 | +| `block_info` | 活跃 | 扩展区块信息 | +| `raw_block` | 活跃 | 原始区块导出 | +| `follow` | 已弃用 | 关注/动态索引 | +| `tags` | 已弃用 | 按标签发现内容 | +| `social_network` | 已弃用 | 高级内容查询 | +| `private_message` | 已弃用 | 加密消息索引 | +| `debug_node` | 仅开发 | 测试/调试操作 | + +--- + +## `database_api` 方法 + +| 方法 | 描述 | +|------|------| +| `get_block_header(block_num)` | 给定高度的区块头 | +| `get_block(block_num)` | 完整签名区块 | +| `get_irreversible_block_header(block_num)` | 不可逆时的区块头 | +| `get_irreversible_block(block_num)` | 不可逆时的完整区块 | +| `set_block_applied_callback(callback)` | WebSocket:订阅新区块 | +| `get_config()` | 编译时链常量 | +| `get_dynamic_global_properties()` | 当前链状态 | +| `get_chain_properties()` | 验证者中位数链属性 | +| `get_hardfork_version()` | 当前硬分叉版本字符串 | +| `get_next_scheduled_hardfork()` | 下一个待定硬分叉信息 | +| `get_accounts(names[])` | 完整账户对象 | +| `lookup_account_names(names[])` | 同 get_accounts 但支持 null | +| `lookup_accounts(lower_bound, limit)` | 分页账户名列表 | +| `get_account_count()` | 已注册账户总数 | +| `get_master_history(account)` | 主密钥更改历史 | +| `get_recovery_request(account)` | 待处理账户恢复请求 | +| `get_escrow(from, escrow_id)` | 托管对象 | +| `get_withdraw_routes(account, type)` | 质押提取路由(`"incoming"` / `"outgoing"` / `"all"`) | +| `get_vesting_delegations(account, from, limit, type)` | 委托(`"delegated"` / `"received"`) | +| `get_expiring_vesting_delegations(account, from, limit)` | 返回窗口内的委托 | +| `get_transaction_hex(trx)` | 十六进制编码的序列化交易 | +| `get_required_signatures(trx, available_keys[])` | 签名所需的最小密钥集 | +| `get_potential_signatures(trx)` | 所有可以签名的密钥 | +| `verify_authority(trx)` | 完全签名时返回 `true` | +| `verify_account_authority(name, keys[])` | 密钥满足授权时返回 `true` | +| `get_database_info()` | Chainbase 内存使用统计 | +| `get_proposed_transactions(account, from, limit)` | 需要账户批准的提案 | +| `get_accounts_on_sale(from, limit)` | 挂牌直接出售的账户 | +| `get_accounts_on_auction(from, limit)` | 挂牌拍卖的账户 | +| `get_subaccounts_on_sale(from, limit)` | 挂牌出售的子账户创建权 | + +--- + +## `network_broadcast_api` 方法 + +| 方法 | 描述 | +|------|------| +| `broadcast_transaction(trx)` | 广播(异步) | +| `broadcast_transaction_synchronous(trx)` | 广播并等待区块包含 | +| `broadcast_transaction_with_callback(callback, trx)` | 带 WebSocket 回调的广播 | +| `broadcast_block(block)` | 广播已签名区块(验证者) | + +--- + +## `witness_api` 方法 + +| 方法 | 描述 | +|------|------| +| `get_active_witnesses()` | 当前活跃验证者集合(21 个账户) | +| `get_witness_schedule()` | 完整验证者计划对象 | +| `get_witnesses(ids[])` | 按内部 ID 查询验证者 | +| `get_witness_by_account(account)` | 账户的验证者对象 | +| `get_witnesses_by_vote(lower_bound, limit)` | 按投票权重排名的验证者 | +| `get_witnesses_by_counted_vote(lower_bound, limit)` | 按计票排名的验证者 | +| `get_witness_count()` | 已注册验证者总数 | +| `lookup_witness_accounts(lower_bound, limit)` | 列出验证者账户名 | + +--- + +## `account_history` 方法 + +### `get_account_history(account, from, limit)` + +返回涉及 `account` 的操作。`from = -1` 从最近的开始。 + +```json +{ + "method": "account_history.get_account_history", + "params": ["alice", -1, 100] +} +``` + +返回 `{ sequence: { trx_id, block, op: [type_id, data] } }` 的映射。 + +--- + +## `operation_history` 方法 + +| 方法 | 描述 | +|------|------| +| `get_ops_in_block(block_num, only_virtual)` | 区块中的操作 | +| `get_transaction(trx_id)` | 按 ID 查询交易 | + +--- + +## `committee_api` 方法 + +| 方法 | 描述 | +|------|------| +| `get_committee_request(request_id)` | 请求详情和状态 | +| `get_committee_request_votes(request_id)` | 请求上的投票 | +| `get_committee_requests_list(from, limit)` | 请求 ID 列表 | + +--- + +## `invite_api` 方法 + +| 方法 | 描述 | +|------|------| +| `get_invites_list(from, limit)` | 所有活跃邀请 ID | +| `get_invite_by_id(id)` | 按内部 ID 查询邀请 | +| `get_invite_by_key(public_key)` | 按公钥查询邀请 | + +--- + +## `paid_subscription_api` 方法 + +| 方法 | 描述 | +|------|------| +| `get_paid_subscriptions(from, limit)` | 所有订阅服务 | +| `get_paid_subscription_options(account)` | 账户的订阅配置 | +| `get_paid_subscription_status(subscriber, account)` | 订阅状态 | +| `get_active_paid_subscriptions(subscriber, from, limit)` | 活跃订阅 | +| `get_inactive_paid_subscriptions(subscriber, from, limit)` | 已过期订阅 | + +--- + +## WebSocket 订阅 + +仅在持久 WebSocket 连接上可用。 + +| 方法 | 回调数据 | +|------|---------| +| `database_api.set_block_applied_callback` | 每个应用区块的区块头 | +| `database_api.set_pending_transaction_callback` | 进入待处理池时的交易 | +| `database_api.cancel_all_subscriptions` | 取消所有订阅 | + +--- + +## 推荐插件集 + +**最小 API 节点:** +```ini +plugin = chain json_rpc webserver p2p +plugin = database_api network_broadcast_api +``` + +**完整 API 节点(添加):** +```ini +plugin = witness_api account_by_key account_history operation_history +plugin = committee_api invite_api paid_subscription_api +``` + +**验证者节点(添加):** +```ini +plugin = validator snapshot +``` + +--- + +参见:[Database API](../plugins/database-api.md)、[Web 服务器插件](../plugins/webserver.md)、[操作概述](../protocol/operations/overview.md)。 diff --git a/@l10n/zh-CN/docs/consensus/block-processing.md b/@l10n/zh-CN/docs/consensus/block-processing.md new file mode 100644 index 0000000000..910dd4f62a --- /dev/null +++ b/@l10n/zh-CN/docs/consensus/block-processing.md @@ -0,0 +1,259 @@ +# 区块处理 + +区块应用、待处理交易管理和 fork 切换的内部机制。 + +--- + +## 概述 + +当节点通过 P2P 接收到新区块时,chain 插件调用 `database::push_block()`。序列为: + +1. 临时从数据库中移除待处理(mempool)交易。 +2. 应用传入的区块。 +3. 重新应用未包含在区块中的待处理交易。 + +这由 `db_with.hpp` 中的 `without_pending_transactions` 辅助类管理。 + +--- + +## 关键数据结构 + +| 结构 | 类型 | 用途 | +|------|------|------| +| `_pending_tx` | `vector` | Mempool:等待包含在区块中的已接收交易 | +| `_popped_tx` | `deque` | 来自弹出区块(fork 切换期间)的交易;切换后重新应用 | +| `_pending_tx_session` | `optional` | 覆盖所有待处理交易状态变化的撤销会话 | + +--- + +## 区块应用流程 + +``` +push_block(new_block) + └─ without_pending_transactions(db, skip, _pending_tx, callback) + ├─ pending_transactions_restorer ctor: clear_pending() + ├─ callback: _push_block(new_block) ← 应用传入的区块 + └─ ~pending_transactions_restorer() ← 恢复待处理交易 +``` + +### `_push_block()` 内部逐步说明 + +1. **早期拒绝检查**(见下文)。 +2. 将区块推送到 `fork_db`。 +3. 如果新 fork 头直接延伸当前头(`new_block.previous == head_block_id()`): + - 跳过 fork 切换逻辑,直接执行 `apply_block()`。 +4. 如果新头更高且偏离当前头: + - **投票加权 fork 比较**(HF12)— 参见 [Fork 解决](./fork-resolution.md)。 + - 弹出旧 fork 区块直到共同祖先。 + - 按顺序应用新 fork 区块。 +5. `apply_block()` 运行交易评估器,更新动态全局属性,处理虚拟操作。 +6. `update_last_irreversible_block()` — 如果 ≥14 个验证者已确认,则推进 LIB。 + +--- + +## 待处理交易恢复 + +`~pending_transactions_restorer()` 析构函数在新区块应用后按顺序处理两个列表。 + +### 步骤 1:重新应用 `_popped_tx`(来自 fork 切换) + +``` +对 _popped_tx 中的每个 tx: + 如果 time_elapsed > 200ms → 推迟(推回 _pending_tx) + 否则如果 is_known_transaction(tx) → 跳过(已在链中) + 否则 → _push_transaction(tx) → applied_txs++ +``` + +### 步骤 2:重新应用 `_pending_transactions`(原始 mempool) + +``` +对 _pending_transactions 中的每个 tx: + 如果 time_elapsed > 200ms → 推迟 + 否则如果 is_known_transaction(tx) → 跳过 + 否则 → _push_transaction(tx) → applied_txs++ + 遇到 transaction_exception → 丢弃(无效) + 遇到 fc::exception → 静默丢弃 +``` + +### 步骤 3:记录摘要 + +如果有任何交易被推迟: +``` +Postponed N pending transactions. M were applied. +``` + +--- + +## 时间限制 + +**`CHAIN_PENDING_TRANSACTION_EXECUTION_LIMIT` = 200 毫秒** + +从恢复开始计时,超时后所有剩余交易推回 `_pending_tx` 而不应用。这防止节点在大型 mempool 上阻塞。 + +限制在以下情况触发: +- 有大量待处理交易的高吞吐量区块 +- CPU 密集型操作 +- 系统负载下 + +--- + +## 生成期间的区块大小限制 + +**`CHAIN_BLOCK_GENERATION_POSTPONED_TX_LIMIT` = 5** + +在 `_generate_block()` 期间,会跳过超出 `maximum_block_size` 的交易。连续 5 个超大交易后,生成循环中断。这些交易保留在 `_pending_tx` 中等待下一个区块。 + +日志: +``` +Postponed N transactions due to block size limit +``` + +--- + +## Fork DB 头部播种 + +在推送区块之前,`_push_block()` 确保当前数据库头块存在于 `fork_db` 中: + +``` +如果 new_block.previous == head_block_id() + 且 head_block_id() 不在 fork_db 中: + 从 block log 获取头块(DLT 模式下从 dlt_block_log) + fork_db.start_block(head_block) +``` + +没有这个播种,有效的下一个区块会抛出 `unlinkable_block_exception`,因为它们的 `previous` 不在 `fork_db` 中。这也修复了生成自己区块的验证者节点 — `generate_block()` 将 `pending_block.previous = head_block_id()`。 + +--- + +## 直接延伸绕过 + +将区块推送到 `fork_db` 后,如果区块直接延伸数据库头: + +``` +如果 new_block.previous == head_block_id(): + → 跳过 fork 切换,直接执行 apply_block() +``` + +这处理了 `fork_db._head` 指向来自之前失败同步周期的过时更高区块的情况。没有此绕过,过时的头会触发 fork 切换逻辑,静默丢弃有效的下一个区块。 + +--- + +## 早期区块拒绝 + +`_push_block()` 应用几个早期拒绝检查以避免不必要的工作并防止无限同步循环: + +| 检查 | 条件 | 操作 | +|------|------|------| +| 已应用 | `block.num ≤ head` 且 ID 与现有区块匹配 | 静默忽略(重复) | +| 不同 fork | `block.num ≤ head`,不同 ID,父节点不在 fork_db | 静默拒绝 | +| 远超前,间隔 > 100 | `block.num > head`,父节点未知,间隔 > 100 个区块 | 静默拒绝(内存保护) | +| 远超前,间隔 ≤ 100 | `block.num > head`,父节点未知,间隔 ≤ 100 | 允许进入 fork_db(缓存在 unlinked index 中) | +| 直接下一个区块 | `block.previous == head_block_id()` | 始终允许 | + +100 个区块间隔阈值防止来自死 fork 链的内存膨胀,同时允许 P2P 同步期间正常的乱序区块处理。 + +--- + +## Fork 切换 + +当节点切换到不同的 fork 时: + +1. `pop_block()` 移除当前头块;其交易移到 `_popped_tx`。 +2. 重复直到到达共同祖先。 +3. 从共同祖先到新头,按顺序应用新 fork 区块。 +4. `~pending_transactions_restorer()` 先重新应用 `_popped_tx`,然后是原始 mempool。 + +已在新链中的交易通过 `is_known_transaction()` 静默跳过。 + +### 线性延伸 vs. 实际 fork + +当父节点到来时,`fork_db` 中的 `_push_next()` 可以自动链接多个孤儿区块,导致 `fork_db._head` 在一次 `push_block()` 调用中跳过比数据库头多几个区块。代码区分: + +- **线性延伸**(`branches.second.size() == 1` 且共同祖先 == 当前头):不需要弹出操作;区块直接应用。 +- **实际 fork 切换**(分叉分支):完整的弹出并重新应用序列。 + +这种区别在 DLT 模式下至关重要,其中 LIB == head 且撤销会话已提交——线性延伸上的弹出循环将是无限的。 + +--- + +## 孤儿区块处理(Unlinked Index) + +当父节点未知的区块到达时,`fork_db` 将其存储在 `_unlinked_index` 中。当缺失的父节点后来到达时: + +1. `_push_block(parent)` 将父节点链接到链。 +2. `_push_next(parent)` 迭代 `_unlinked_index` 寻找 `parent` 的子节点。 +3. 子节点移到 `_index` 并递归链接。 +4. `fork_db._head` 可能在一次调用中前进多个区块(触发线性延伸路径)。 + +--- + +## 基于罚分的节点软封禁 + +节点不会因发送不可链接的区块而立即被封禁。计数器累积: + +| 路径 | 阈值 | 重置条件 | +|------|------|---------| +| 正常操作:头部或以下的不可链接区块 | 20 次罚分 | 同一节点接受有效区块 | +| 同步路径:通用区块拒绝 | 20 次罚分 | 同一节点接受有效区块 | +| 死 fork / 区块过老 | 立即封禁 | — | + +诚实的节点可以从瞬时错误中恢复(快照重载、时序竞争、短暂的 micro-fork)。 + +--- + +## 验证者区块生产时序 + +验证者插件使用带 250ms 前瞻的 250ms 定时器: + +1. 定时器每 **250ms** 触发一次(对齐到 250ms 系统时钟边界,最小睡眠 50ms)。 +2. `maybe_produce_block()` 计算 `now = NTP_time + 250ms`。 +3. `get_slot_at_time(now)` 找到当前槽位。 +4. 如果槽位属于已配置的验证者且 `|scheduled_time - now| ≤ 500ms`,以确定性的 `scheduled_time` 作为时间戳生产区块。 + +``` +槽位在 T=6.000,定时器在 T=5.750: + now = 5.750 + 0.250 = 6.000 → 槽位匹配 → 生产 +``` + +这在延迟阈值对面提供了 500ms 安全边际。 + +### 生产条件(按顺序检查) + +| 条件 | 失败结果 | +|------|---------| +| 链已同步(或 `enable-stale-production`) | `not_synced` | +| `get_slot_at_time(now) > 0` | `not_time_yet` | +| 计划的验证者在我们配置的集合中 | `not_my_turn` | +| 链上非 null 签名密钥 | `not_my_turn` | +| 签名密钥的私钥在内存中 | `no_private_key` | +| 网络参与度 ≥ 阈值(HF12 之前) | `low_participation` | +| `|scheduled_time - now| ≤ 500ms` | `lag` | +| fork_db 中同高度无竞争区块 | `fork_collision` | +| 最后 21 个区块不全来自我们的验证者 | `minority_fork` | + +--- + +## 配置常量 + +| 常量 | 值 | 描述 | +|------|---|------| +| `CHAIN_PENDING_TRANSACTION_EXECUTION_LIMIT` | 200 毫秒 | 区块推送后重新应用待处理交易的最大时间 | +| `CHAIN_BLOCK_GENERATION_POSTPONED_TX_LIMIT` | 5 | 生成期间跳过的最大连续超大交易数 | +| `CHAIN_BLOCK_SIZE` | 65536 字节 | 硬区块大小限制 | +| `maximum_block_size` | 动态(验证者中位数) | 软区块大小限制 | +| `CHAIN_BLOCK_INTERVAL` | 3 秒 | 区块生产间隔 | + +--- + +## 调试日志前缀 + +| 前缀 | 含义 | +|------|------| +| `FORK-SWITCH-POP: popping head #H` | 正常 fork 切换——弹出旧 fork 区块 | +| `FORK-RECOVER-POP: popping head #H` | 错误恢复——回滚失败的 fork 切换 | +| `POP_BLOCK: db_head=#X fork_db_head=#Y` | 每次 `pop_block()` 调用前的状态 | +| `Fork switch: new_head=#X branches.first=N branches.second=M` | fork 切换前的分支;`M=0` 表示线性延伸 | + +--- + +参见:[Fair-DPOS](./fair-dpos.md)、[Fork 解决](./fork-resolution.md)、[验证者节点](../node/validator-node.md)。 diff --git a/@l10n/zh-CN/docs/consensus/emergency-consensus.md b/@l10n/zh-CN/docs/consensus/emergency-consensus.md new file mode 100644 index 0000000000..ff4acfeafb --- /dev/null +++ b/@l10n/zh-CN/docs/consensus/emergency-consensus.md @@ -0,0 +1,221 @@ +# 紧急共识模式 + +紧急共识模式(在 HF12 中引入)在网络停滞 1 小时后自动激活。特殊的"committee"验证者接管区块生产,以在真实验证者恢复其签名密钥之前维持链的连续性。 + +--- + +## 关键常量 + +| 常量 | 值 | 含义 | +|------|---|------| +| `CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC` | 3600 秒 | 激活前的不活动时间 | +| `CHAIN_EMERGENCY_WITNESS_ACCOUNT` | `"committee"` | 紧急区块生产者账户 | +| `CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY` | `VIZ75CR...` | 确定性紧急签名密钥 | +| `CHAIN_EMERGENCY_EXIT_NORMAL_BLOCKS` | 21 | 退出所需的连续真实验证者区块数 | +| `CHAIN_IRREVERSIBLE_THRESHOLD` | 75% | 退出所需的计划槽位比例 | +| `CHAIN_MAX_WITNESSES` | 21 | 最大验证者槽位数 | + +### `dynamic_global_property_object` 中的状态字段 + +| 字段 | 默认值 | 含义 | +|------|-------|------| +| `emergency_consensus_active` | `false` | 紧急模式已激活 | +| `emergency_consensus_start_block` | `0` | 激活时的区块号 | + +--- + +## 激活 + +`update_global_dynamic_data()` 在每个应用的区块上运行并检查: + +1. **HF12 门控** — 如果硬分叉尚未激活则跳过。 +2. **已激活** — 如果紧急模式已经开启则跳过。 +3. **block log 中有 LIB 区块** — 如果 LIB 区块不在 block log 中则跳过(快照恢复后的 DLT 节点有空的 block log;缺少 LIB 区块将计算数百万过时秒数并触发错误的激活死锁)。 +4. **超时** — 计算 `seconds_since_lib = current_block.timestamp − lib_block.timestamp`。如果 `< 3600` 则跳过。 + +所有检查仅使用区块内嵌的时间戳——没有系统时钟,没有跳过标志。这保证了在每个节点上,包括重放,具有相同的确定性激活。 + +### 激活序列 + +当超过超时阈值时: + +1. 设置 `dgp.emergency_consensus_active = true` 和 `dgp.emergency_consensus_start_block = block_num`。 +2. 创建或更新"committee"验证者对象: + - `signing_key = CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY` + - `props = current_median_props` + - 硬分叉投票设置为当前应用版本(中性投票者)。 +3. 禁用所有真实验证者:设置 `signing_key = zero`,重置 `penalty_percent = 0`,`current_run = 0`。 +4. 删除所有 `witness_penalty_expire` 对象。 +5. 覆盖验证者计划:所有 `CHAIN_MAX_WITNESSES` 槽位 → "committee"。 +6. 通知 fork DB:`_fork_db.set_emergency_mode(true)`(启用确定性哈希平局决胜)。 +7. 日志:`"EMERGENCY CONSENSUS MODE activated at block #N. No blocks for X seconds since LIB Y."` + +--- + +## 紧急操作 + +### 区块生产——主节点 vs. 从节点 + +在 `config.ini` 中配置了 `emergency-private-key` 的节点是**紧急主节点**;所有其他节点是**从节点**。 + +```ini +# 仅限紧急主节点 +emergency-private-key = 5Jzzz... # CHAIN_EMERGENCY_WITNESS_ACCOUNT 私钥 +``` + +| 角色 | DLT 同步检查 | 少数派 fork 检查 | 生产 | +|------|------------|---------------|------| +| 主节点 | 绕过(会死锁) | 跳过(预期所有区块都是"我们的") | 为所有槽位生产区块 | +| 从节点 | 正常 | 21 区块检查(1 个完整轮次) | 为自己计划的槽位标准验证者生产 | + +### Fork DB 确定性平局决胜 + +紧急期间,多个主节点(地理冗余)可能在同一高度以相同密钥生产竞争区块。到达顺序因 P2P 拓扑而异。 + +`fork_database::_push_block()` 解决平局: +``` +item->num == _head->num 且 _emergency_consensus_active: + item->id < _head->id → _head = item (较小的区块哈希获胜) + 否则 → 保留当前 _head +``` + +所有节点收敛到同一链顶,无论它们先看到哪个区块。 + +### LIB 推进 + +`update_last_irreversible_block()` 正常计算 LIB,但在紧急期间**将其上限限制为 HEAD − 1**。没有上限,由于所有 21 个槽位都是"committee"且 `committee.last_supported_block_num == HEAD`,`nth_element` 计算返回 HEAD——这将在应用过程中提交当前区块的撤销会话,导致不可逆的损坏。 + +3 个 committee 区块(`CHAIN_IRREVERSIBLE_SUPPORT_MIN_RUN`)后,LIB 每个区块推进一次,保持 fork DB 窗口小。 + +### 混合验证者计划 + +`update_witness_schedule()` 在每个轮次构建混合计划: + +- 真实验证者 `signing_key` 非零的槽位:保留真实验证者。 +- 空或零密钥槽位:填充"committee"。 + +这允许真实验证者逐步返回。每次真实验证者通过 `validator_update_operation` 恢复其签名密钥时,下一次计划重建包含它们。 + +Committee 被排除在硬分叉版本统计和中位链属性计算之外(它复制当前中位数,因此按槽位计算会扭曲中位数)。 + +--- + +## 停用 + +每次计划重建检查退出条件: + +``` +real_witness_slots >= CHAIN_MAX_WITNESSES × 75% +``` + +21 个验证者时:`21 × 0.75 = 15.75 → 15` 个真实验证者槽位需要。 + +当满足条件时: +1. `dgp.emergency_consensus_active = false`。 +2. `_fork_db.set_emergency_mode(false)`。 +3. 日志:`"EMERGENCY CONSENSUS MODE deactivated at block #N. R real validators active."`。 + +网络在下一个计划周期恢复正常运行。 + +--- + +## 启动恢复 + +`database::open()` 在重放后检查验证者计划。如果任何槽位为空(字符串 `""`),紧急状态在节点关闭时正在进行: + +1. 如果 `emergency_consensus_active` 尚未设置 → 设置它并设置 fork DB 紧急标志。 +2. 用"committee"填充所有槽位。 +3. 日志:`"schedule repaired, all N slots set to committee"`。 + +这确保在紧急模式期间不干净关机后计划始终一致。 + +--- + +## 验证者守护集成 + +`witness_guard` 插件在紧急期间继续运行,实际上更加关键: + +- 真实验证者在激活时被禁用(签名密钥设为 null)。 +- 验证者守护自动广播 `validator_update_operation`,一旦在链上检测到 null 密钥就恢复每个验证者的签名密钥。 +- 验证者守护中的 `enable-stale-production` 防护**不阻止**紧急模式下的密钥恢复("紧急共识处理自己的恢复,密钥恢复可能仍然需要")。 +- 一旦 15 个验证者恢复密钥,退出条件触发。 + +参见[验证者守护](../node/validator-guard.md)。 + +--- + +## P2P 交互防护 + +几个 P2P 安全机制知道紧急模式: + +| 防护 | 紧急期间的行为 | +|------|--------------| +| `resync_from_lib()` | **完全跳过** — 紧急期间弹出 LIB 附近的区块会崩溃 | +| `stale_sync_check_task()` | 如果主节点头部推进 → 重置计时器,跳过恢复;如果从节点头部卡住 → 允许恢复 | +| `handle_block()`(DLT,同步模式,间隔 0–2) | 视为正常(非同步)以防止生产循环中断 | +| 快照停滞同步检测 | 与停滞同步检查相同的逻辑 | + +`resync_from_lib()` 防护最为关键:紧急期间,LIB 接近 HEAD。将区块弹回 LIB 并重置 fork DB 会导致来自真实网络的节点区块链接到重新播种的 LIB,触发 fork 切换,弹出到已提交的 LIB 以下,要么崩溃要么损坏状态。 + +--- + +## 区块验证——宽松的槽位映射 + +`verify_signing_witness()` 通常断言区块生产者与计划的验证者完全匹配。紧急期间: + +``` +如果 block.validator != scheduled_witness: + dlog("Emergency mode: accepting block from BW at slot scheduled for SW") + → 无论如何接受(签名仍然针对 block.validator 的 signing_key 验证) +``` + +这允许紧急主节点生产区块,即使待定计划中的几个槽位仍然分配给真实验证者。 + +--- + +## 快照兼容性 + +紧急状态字段以向前兼容的默认值包含在快照中: + +- 快照中缺少 `emergency_consensus_active` → 默认为 `false`。 +- 缺少 `emergency_consensus_start_block` → 默认为 `0`。 + +在活跃紧急期间创建的快照正确保留状态;在 HF12 之前创建的快照作为非紧急导入。 + +--- + +## 防护摘要 + +| # | 位置 | 防护 | +|---|------|------| +| 1 | `update_global_dynamic_data` | 仅在 HF12 + 未激活 + LIB 区块可用时激活 | +| 2 | `update_witness_schedule` | 混合覆盖 + 真实验证者 ≥75% 时的退出检查 | +| 3 | `update_last_irreversible_block` | 紧急期间将 LIB 上限设为 HEAD − 1 | +| 4 | `verify_signing_witness` | 放宽槽位到验证者的映射 | +| 5 | `fork_db._push_block` | 确定性哈希平局决胜 | +| 6 | `maybe_produce_block`(主节点) | 绕过同步、过时、参与;跳过少数派 fork | +| 7 | `maybe_produce_block`(从节点) | 必须先同步;21 区块隔离检查 | +| 8 | `resync_from_lib` | 紧急期间**完全跳过** | +| 9 | `stale_sync_check_task` | 主节点头部推进时跳过;从节点卡住时允许 | +| 10 | `handle_block` | DLT 紧急中几乎追上的区块视为正常 | +| 11 | `database::open` | 启动计划修复 | +| 12 | `witness_guard` | 紧急期间不抑制密钥恢复 | +| 13 | `snapshot import` | 向前兼容的字段处理 | +| 14 | `update_witness_schedule` | 从硬分叉版本统计中排除 committee | +| 15 | `update_median_witness_props` | 从中位数计算中排除 committee | + +--- + +## 关键不变量 + +1. **确定性激活** — 仅使用区块内嵌的时间戳;在每个节点和每次重放上相同。 +2. **DLT 快照安全** — 如果 LIB 区块不在 block log 中则跳过激活。 +3. **紧急 fork 不变性** — `resync_from_lib()` 在紧急期间拒绝执行。 +4. **主/从区分** — 只有具有 `--emergency-private-key` 的节点是主节点。 +5. **Fork DB 收敛** — 确定性哈希平局决胜确保所有节点选择相同的区块。 +6. **LIB 安全** — 上限为 HEAD − 1 以保留撤销保护。 +7. **中性 committee 投票** — committee 为当前应用的硬分叉版本投票,复制中位数属性。 + +--- + +参见:[Fair-DPOS](./fair-dpos.md)、[Fork 解决](./fork-resolution.md)、[验证者节点](../node/validator-node.md)、[验证者守护](../node/validator-guard.md)。 diff --git a/@l10n/zh-CN/docs/consensus/fair-dpos.md b/@l10n/zh-CN/docs/consensus/fair-dpos.md new file mode 100644 index 0000000000..34cf85a4d3 --- /dev/null +++ b/@l10n/zh-CN/docs/consensus/fair-dpos.md @@ -0,0 +1,170 @@ +# Fair-DPOS 共识 + +VIZ Ledger 使用**公平委托权益证明(Fair-DPOS)**——经典 DPoS 算法的扩展,增加了参与度执行和错过惩罚机制,以防止验证者在不实际生产区块的情况下收取奖励。 + +--- + +## 经典 DPoS 的工作原理 + +在标准 DPoS 中: +- 代币持有者为验证者账户投票(权重与其 SHARES 成比例)。 +- 票数最高的验证者按轮询顺序被安排生产区块。 +- 每 3 秒触发一个槽位;被安排的验证者要么生产区块,要么错过该槽位。 + +VIZ 每个调度轮次运行 **21 个活跃验证者**。 + +--- + +## "公平"扩展 + +在经典 DPoS 中,验证者可以无限期错过区块,仍然获得投票(有时还有奖励)。Fair-DPOS 增加了: + +1. **参与度追踪** — 128 位位掩码追踪最近 128 个槽位。每个槽位标记为 1(已生产)或 0(已错过)。 +2. **参与度阈值** — 如果最近槽位中有不到 `required-participation`% 被任何验证者填充(默认 33%),节点将不会生产。这可防止少数派 fork 场景。 +3. **错过惩罚** — 错过区块的验证者会累积错过计数。在每次硬分叉评估时,表现最差的验证者可能被从活跃集合中移除。 +4. **奖励共享**(HF13)— 验证者区块奖励部分重新分配给其投票者,将委托者激励与验证者表现对齐。 + +--- + +## 验证者计划 + +### 构建计划 + +每 `CHAIN_WITNESS_SCHEDULE_BLOCK_NUM` 个区块(21 个),链重新计算活跃验证者集合: + +1. 选取总投票权重最高的 21 个验证者(委托给他们的 SHARES)。 +2. 添加**时间份额验证者**——为排名较低的验证者提供偶尔参与的旋转槽位,防止完全集中。 +3. 使用头区块 ID 作为熵种子对结果 21 个槽位进行洗牌(确定性洗牌 = 所有节点结果相同)。 + +生成的有序列表成为**当前计划**。每个位置对应一个 3 秒槽位。 + +### 槽位分配 + +给定墙时钟时间 `T`: + +``` +slot_num = (T - genesis_time) / CHAIN_BLOCK_INTERVAL +scheduled = schedule[slot_num % num_scheduled_validators] +``` + +区块时间戳始终是**确定性槽位时间**,而非原始时钟: +``` +block_time = genesis_time + slot_num × 3s +``` + +### 错过的槽位 + +当验证者错过其槽位时,`update_global_dynamic_data()` 递增 `current_aslot` 并在参与位掩码中将该槽位标记为已错过。其他验证者不会填充错过的槽位——无论如何,3 秒节奏继续到下一个槽位。 + +--- + +## 参与率 + +参与率为: + +``` +participation = popcount(recent_slots_filled) / 128 +``` + +其中 `recent_slots_filled` 是槽位结果的 128 位滑动窗口。 + +**当参与度降至 `required-participation`(默认 33%)以下时,验证者生产被阻止**。这可防止少数派 fork 上的节点在大多数网络不可达时继续生产区块。 + +配置: +```ini +required-participation = 33 # 最低 %,0–99 +``` + +--- + +## 最后不可逆区块(LIB) + +当超过 2/3 的活跃验证者在某区块之上构建时,该区块变为不可逆。链在 `last_irreversible_block_num` 中追踪这一信息。 + +``` +irreversibility_threshold = ceil(num_scheduled_validators * 2 / 3) +``` + +21 个验证者时:`ceil(21 × 2/3) = 14` 次确认。一旦 14 个验证者生产了从区块 N 派生的区块,区块 N 就成为 LIB。 + +**在紧急共识模式期间,LIB 推进被跳过**(参见[紧急共识](./emergency-consensus.md))。 + +--- + +## 硬分叉投票 + +验证者通过 `validator_update_operation` 设置其上报的 `hardfork_version_vote` 参与硬分叉激活。当以下条件满足时,硬分叉 N 激活: + +1. 当前验证者集合中的超级多数(>80%)已发出支持信号。 +2. 硬分叉的计划激活时间戳已过。 + +两个条件必须同时满足。这允许网络运营者即使在计划时间过后也可以通过拒绝投票来阻止不需要的硬分叉。 + +--- + +## 少数派 Fork 保护 + +如果最近 21 个连续区块全部由属于此节点配置的验证者集合的验证者生产,验证者插件判断节点已被隔离,并自动回滚到 LIB。这就是**少数派 fork 保护**。 + +在以下情况下跳过此检查: +- `enable-stale-production = true`(开发/测试网) +- 紧急共识模式已激活 + +--- + +## 紧急共识模式 + +如果在 `CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC`(默认 1 小时)内没有生产区块,链切换到**紧急模式**: + +- 所有 21 个验证者槽位分配给 `CHAIN_EMERGENCY_WITNESS_ACCOUNT`("committee")。 +- 紧急验证者使用 `CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY` 签名区块。 +- 所有验证者惩罚被重置;已关闭的验证者重新启用。 +- 紧急期间 LIB 推进暂停。 +- 在 `CHAIN_EMERGENCY_EXIT_NORMAL_BLOCKS`(21)个连续正常区块将 LIB 推进到紧急开始区块之后,紧急模式退出。 + +完整详情参见[紧急共识](./emergency-consensus.md)。 + +--- + +## HF12:投票权重 Fork 比较 + +从硬分叉 12 开始,当同一区块高度存在两个竞争 fork 时,链使用**投票权重 fork 比较**而不是简单的最长链: + +1. 对于每个 fork 分支,累加在该分支上生产区块的每个唯一验证者所委托的 SHARES 总量。 +2. 对更长的链应用 **+10% 奖励**,以在平局时支持生产连续性。 +3. 总投票权重更高的 fork 获胜。 +4. 如果仍然平局,则维持当前 fork,直到两级 fork 冲突解决器的 21 次延迟超时触发。 + +完整的 fork 冲突算法参见[Fork 解决](./fork-resolution.md)。 + +--- + +## 配置摘要 + +| 设置 | 默认值 | 描述 | +|------|-------|------| +| `required-participation` | `33`(33%) | 生产区块所需的最低参与度 | +| `enable-stale-production` | `false` | 绕过参与度检查(仅限测试网) | +| `emergency-private-key` | — | 可选的紧急共识签名密钥 | +| 活跃验证者 | 21 | 在 `CHAIN_MAX_WITNESSES` 中硬编码 | +| 区块间隔 | 3 秒 | `CHAIN_BLOCK_INTERVAL` | +| LIB 阈值 | ⌈21 × 2/3⌉ = 14 | 确认不可逆性所需的区块数 | +| 紧急超时 | 3600 秒 | `CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC` | + +--- + +## 关键源码位置 + +| 组件 | 文件 | +|------|------| +| 验证者计划构建 | `libraries/chain/database.cpp` — `update_witness_schedule()` | +| 参与位掩码更新 | `libraries/chain/database.cpp` — `update_global_dynamic_data()` | +| LIB 推进 | `libraries/chain/database.cpp` — `update_last_irreversible_block()` | +| 硬分叉投票 | `libraries/chain/database.cpp` — `process_hardforks()` | +| 生产循环 | `plugins/validator/validator.cpp` — `maybe_produce_block()` | +| 紧急模式激活 | `libraries/chain/database.cpp` — `check_emergency_consensus()` | +| HF12 Fork 比较 | `libraries/chain/database.cpp` — `compare_fork_branches()` | + +--- + +参见:[区块处理](./block-processing.md)、[Fork 解决](./fork-resolution.md)、[紧急共识](./emergency-consensus.md)、[验证者节点](../node/validator-node.md)。 diff --git a/@l10n/zh-CN/docs/consensus/fork-resolution.md b/@l10n/zh-CN/docs/consensus/fork-resolution.md new file mode 100644 index 0000000000..3476b4e436 --- /dev/null +++ b/@l10n/zh-CN/docs/consensus/fork-resolution.md @@ -0,0 +1,180 @@ +# Fork 解决 + +本页介绍 VIZ Ledger 如何检测、选择和解决竞争性 fork——从基本的 fork 数据库到 HF12 投票加权碰撞解析器。 + +--- + +## Fork 数据库 + +**Fork 数据库**(`fork_database`)是候选链顶端的内存树。通过 P2P 接收的每个区块在应用于链状态之前都插入此处。 + +关键操作: +- `push_block(b)` — 将 `b` 链接到其父节点;如果父节点未知,缓存在 `_unlinked_index` +- `_push_next(item)` — 父节点到达时,迭代链接所有缓存的子节点 +- `fetch_branch_from(a, b)` — 从两个分支追溯到共同祖先 +- `set_max_size(n)` — 从 linked 和 unlinked 索引中修剪最旧的区块 + +### 重复检测 + +插入前,fork DB 检查具有相同 ID 的区块是否已存在。如果是,则静默忽略。这防止了 P2P 同步期间重新广播的区块被重复处理。 + +### Unlinked index + +父节点尚未在 fork DB 中的区块存储在 `_unlinked_index` 中。当父节点后来到达时: +1. `_push_block(parent)` 链接父节点。 +2. `_push_next(parent)` 迭代 `_unlinked_index` 寻找父节点的子节点。 +3. 子节点移到 `_index` 并递归链接。 +4. fork DB 头可能在一次调用中跳过多个区块。 + +间隔阈值(100 个区块)防止内存膨胀:父节点未知且比数据库头超前超过 100 个区块的区块在到达 fork DB 之前被静默拒绝。 + +--- + +## Fork 选择:最长链规则 + +插入区块后,fork DB 返回新头。如果新头高于数据库头且与之分叉,则尝试 **fork 切换**。 + +**HF12 之前:** 简单的最长链规则——区块号最高的 fork 获胜。 + +--- + +## HF12:投票加权 Fork 比较 + +从第 12 次硬分叉开始,当两个竞争 fork 在同一区块高度时,使用 `compare_fork_branches()` 而非简单的最长链: + +### 算法 + +1. 通过 `fetch_branch_from(fork_a_tip, fork_b_tip)` **获取分支**到共同祖先。 +2. **按验证者汇总投票权重**——每个分支中每个唯一验证者账户只计算一次。紧急验证者账户(`"committee"`)被排除。 +3. 对更长的链**应用 +10% 奖励**。 +4. **返回**:如果分支 A 更强则返回 `+1`,B 更强则返回 `-1`,平局返回 `0`。 + +### Fork 碰撞处理 + +从验证者插件调用 `compare_fork_branches()` 时: +- 如果一个 fork 明显更强 → 在该 fork 上生产。 +- 如果平局或不确定 → 推迟(递增 `fork_collision_defer_count_`)。 +- 连续 **21 次推迟**(一个完整的验证者轮次)后 → 超时:调用 `remove_blocks_by_number(height)` 清除过时的竞争区块,然后在规范链上生产。 + +| 条件 | `peer_needs_sync_items_from_us` 标志 | +|------|-------------------------------------| +| 回复为空 | `false` — 我们的链为空 | +| 回复 = synopsis 中 1 个条目 | `false` — 节点已更新 | +| 回复 >1 个条目,`remaining == 0` | `false` — 节点几乎更新(切换到库存模式) | +| 回复 >1 个条目,`remaining > 0` | `true` — 节点远落后(保持同步模式) | + +--- + +## Fork 切换过程 + +当节点切换到更好的 fork 时: + +``` +1. fetch_branch_from(new_head, current_head) + → branches.first = [new_tip, ..., common_ancestor] + → branches.second = [current_tip, ..., common_ancestor] + +2. 线性延伸检查: + branches.second.size() == 1 且 common_ancestor == head + → 跳过弹出循环;直接应用 branches.first。 + +3. 实际 fork 切换: + 对 branches.second 中的每个区块(逆序): + FORK-SWITCH-POP: pop_block() ← 保存 txs 到 _popped_tx + 对 branches.first 中的每个区块(逆序): + FORK-SWITCH-APPLY: apply_block() + +4. 异常时: + 对上面已应用的每个区块: + FORK-RECOVER-POP: pop_block() ← 撤销部分应用 + 使失败的 fork 无效。 + 重新抛出异常。 +``` + +**线性延伸**的区分在 DLT 模式下至关重要,其中 LIB == head:弹出循环将是无限的,因为撤销会话已提交。 + +--- + +## 不可逆区块确定 + +每次区块应用后,`update_last_irreversible_block()` 推进 Last Irreversible Block (LIB): + +1. 收集 21 个计划验证者各自的 `last_supported_block_num`。 +2. 排序并取倒数第 `⌈21 × 25%⌉ = 5` 位(即 75% 的验证者处于该值或以上的值)。 +3. 结果区块号成为新的 LIB。 + +区块成为 LIB 后,写入 `block_log`(DLT 模式下为 `dlt_block_log`),其撤销会话被提交。 + +**紧急共识模式期间 LIB 上限为 HEAD − 1**,以防止提交当前正在应用的区块的撤销会话。 + +--- + +## 过时 Fork 修剪 + +两种机制防止过时数据积累: + +1. **`remove_blocks_by_number(num)`** — 删除特定高度的所有区块。在 21 次推迟超时后由 fork 碰撞解析器调用。 +2. **`set_max_size(n)`** — 当 fork DB 超过 `n` 个条目时,从 `_index` 和 `_unlinked_index` 修剪最旧的区块。 + +--- + +## 少数派 Fork 守护 + +在每次区块生产之前,验证者插件检查 fork DB 中的最后 21 个区块: + +- 如果所有 21 个都由该节点自己配置的验证者生产 → 节点在少数派 fork 上被隔离。 +- 操作(`enable-stale-production = false`):调用 `resync_from_lib()` — 弹出到 LIB,重置 fork DB,重新启动 P2P 同步,重新连接种子节点。 +- 操作(`enable-stale-production = true`):记录警告,继续生产。 +- 紧急共识激活 → 跳过检查(预期紧急主节点的所有槽位都是"我们的")。 + +--- + +## Fork 碰撞指标(HF12) + +HF12 在 `dynamic_global_property_object` 中添加了两个字段用于链上监控: + +| 字段 | 类型 | 描述 | +|------|------|------| +| `fork_collision_count` | `uint32_t` | 自 genesis 以来 fork 碰撞的累计次数 | +| `last_fork_collision_block_num` | `uint32_t` | 最近一次碰撞的区块号 | + +通过 `get_dynamic_global_properties` 读取。 + +--- + +## Fork DB 诊断 + +Fork DB 公开 O(1) 访问器用于监控: + +| 方法 | 返回 | +|------|------| +| `linked_size()` | linked index 中的区块数量 | +| `unlinked_size()` | unlinked index 中的区块数量 | +| `linked_min_block_num()` | linked index 中的最小区块号 | +| `linked_max_block_num()` | linked index 中的最大区块号 | +| `unlinked_min_block_num()` | unlinked index 中的最小区块号 | +| `unlinked_max_block_num()` | unlinked index 中的最大区块号 | + +P2P 统计任务每 5 分钟记录这些: + +``` +Block storage | dlt_log: [79174319..79274318] | dlt_resizes: 412 | fork_db: linked=18 unlinked=0 +``` + +不断增长且不减少的 `unlinked_size` 表明收到的区块流中存在持续性间隔(P2P 连接问题或节点处于隔离的 fork 上)。 + +--- + +## 故障排除 + +| 症状 | 诊断 | +|------|------| +| 生产结果 `fork_collision` | 目标高度有竞争区块;等待 21 次推迟超时或投票权重解决 | +| 生产结果 `minority_fork` | 节点被隔离;检查 P2P 节点和种子连接 | +| `unlinked_size` 无限增长 | 父区块未到达;检查 P2P 连接 | +| 日志中重复 fork 切换 | 两个验证者子集之间的网络分区;调查它们之间的连接 | +| DLT 模式下头部不推进 | 线性延伸与 fork 切换混淆;检查 `FORK-SWITCH-POP` 日志 | + +--- + +参见:[Fair-DPOS](./fair-dpos.md)、[区块处理](./block-processing.md)、[紧急共识](./emergency-consensus.md)。 diff --git a/@l10n/zh-CN/docs/consensus/hardforks.md b/@l10n/zh-CN/docs/consensus/hardforks.md new file mode 100644 index 0000000000..15aa75ad7b --- /dev/null +++ b/@l10n/zh-CN/docs/consensus/hardforks.md @@ -0,0 +1,171 @@ +# 硬分叉 + +硬分叉是改变共识规则的网络升级。所有节点必须在计划的激活时间戳之前升级;运行旧版软件的节点将在激活后与网络分叉。 + +--- + +## 激活机制 + +每个硬分叉包含: +- 唯一**编号**(N)。 +- **Unix 时间戳** — 硬分叉可以激活的最早系统时间。 +- **验证者投票超多数** — 当前验证者集合中 >80% 必须通过 `validator_update_operation` 发出新硬分叉版本信号。 + +两个条件必须同时满足。验证者可以通过即使在计划时间戳后仍不提交版本投票来阻止不需要的硬分叉。 + +--- + +## 硬分叉历史 + +| # | 版本 | 关键变更 | +|---|------|---------| +| 1–10 | 1.x – 2.x | 基础、社交图、能量系统、委员会、订阅 | +| 11 | 3.0.0 | — | +| 12 | 3.1.0 | Fork 碰撞指标、投票加权 fork 比较、紧急共识模式、NTP 改进 | +| 13 | 3.2.0 | 验证者奖励共享,按投票比例分配 | + +--- + +## HF12 摘要 + +HF12(版本 3.1.0)引入了: + +1. **Fork 碰撞计数器** — `fork_collision_count` 和 `last_fork_collision_block_num` 添加到 `dynamic_global_property_object`。可通过 `get_dynamic_global_properties` 观察。 +2. **投票加权 fork 比较**(`compare_fork_branches()`)— fork 选择使用每个验证者分支的总委托 SHARES + 较长链 10% 奖励。 +3. **紧急共识模式** — 1 小时无区块后自动激活;"committee"账户接管所有 21 个槽位。参见[紧急共识](./emergency-consensus.md)。 +4. **少数派 fork 自动重新同步** — 验证者插件检测节点隔离(连续 21 个自己的区块)并回滚到 LIB。 +5. **NTP 改进** — 专用 NTP 客户端,可配置服务器、间隔和往返时间阈值。 + +--- + +## HF13 摘要 + +HF13(版本 3.2.0)引入了: + +**验证者奖励共享**:每个区块的部分验证者奖励按比例重新分配给投票支持该验证者的账户(按其 SHARES 投票权重)。 + +- `witness_object` 上的新字段:`reward_percent` — 与投票者共享的区块奖励比例(0–10000 基点)。 +- 新虚拟操作:`validator_reward_virtual_operation` — 每次奖励分配触发一次。 +- 通过 `validator_update_operation` 设置。 + +--- + +## 实现新硬分叉 + +### 步骤 1:创建硬分叉定义文件 + +`libraries/chain/hardfork.d/N.hf`: + +```cpp +#ifndef CHAIN_HARDFORK_N +#define CHAIN_HARDFORK_N N +#define CHAIN_HARDFORK_N_TIME 1234567890 // Unix 时间戳 — 必须是未来的时间 +#define CHAIN_HARDFORK_N_VERSION hardfork_version(3, N, 0) +#endif +``` + +### 步骤 2:更新常量 + +`libraries/chain/hardfork.d/0-preamble.hf`: +```cpp +#define CHAIN_NUM_HARDFORKS N +``` + +`libraries/protocol/include/graphene/protocol/config.hpp`(如果对协议可见): +```cpp +#define CHAIN_VERSION (version(3, N, 0)) +``` + +### 步骤 3:模式版本 + +如果任何 chainbase 对象布局发生变化(新字段、删除字段、调整大小的类型),在 `config.hpp` 中**递增 `CHAIN_SCHEMA_VERSION`**: + +```cpp +#define CHAIN_SCHEMA_VERSION uint32_t(N) +``` + +chain 插件在启动时检查此项。不匹配时在打开前清除 `shared_memory.bin`,防止从旧布局中读取损坏数据。 + +新字段应始终具有**零值默认值**以避免迁移代码: +```cpp +uint16_t my_new_field = 0; +``` + +### 步骤 4:连接到 database.cpp + +`init_hardforks()`: +```cpp +FC_ASSERT(CHAIN_HARDFORK_N == N); +_hardfork_times[N] = fc::time_point_sec(CHAIN_HARDFORK_N_TIME); +_hardfork_versions[N] = hardfork_version(CHAIN_HARDFORK_N_VERSION); +``` + +`apply_hardfork()` 的 case: +```cpp +case CHAIN_HARDFORK_N: { + // 必要时进行迁移。如果零值默认值足够,留空并添加注释。 + break; +} +``` + +### 步骤 5:操作和评估器(如果有新操作) + +1. 在 `chain_operations.hpp` 中添加带 `validate()` 和权限获取器的结构体。 +2. 添加到 `operations.hpp` 中的 `static_variant`。 +3. 在 `chain_evaluator.hpp` 中声明 `DEFINE_EVALUATOR(my_new_op)`。 +4. 在 `.cpp` 评估器文件中实现 `do_apply()` — 始终先检查 `ASSERT_REQ_HF(CHAIN_HARDFORK_N, ...)`。 +5. 在 `database.cpp` 的 `initialize_evaluators()` 中注册。 + +### 步骤 6:插件更新 + +| 插件 | 需要更新的内容 | +|------|-------------| +| `account_history` | 为任何新虚拟操作添加影响提取器 | +| `witness_api` | 将 `witness_object` 中的新字段添加到 `witness_api_object` | +| `snapshot` | 将新 chainbase 对象添加到 `serialize_state` / `load_snapshot` | + +--- + +## 模式版本生命周期 + +``` +新节点(无现有数据): + stored = 0, compiled = N → 不匹配 + 清除 shared_memory(不存在时为无操作) + 写入 schema_version = N + genesis → 正常启动 + +升级(旧二进制版本为 M < N): + stored = M, compiled = N → 不匹配 + 清除 shared_memory.bin + 写入 schema_version = N + db.open() → 版本不匹配异常 + → 自动恢复:快照导入 + dlt_block_log 重放 + +正常重启: + stored = N, compiled = N → 匹配 + db.open() 正常进行 +``` + +**关键文件:** +- `config.hpp` — `CHAIN_SCHEMA_VERSION` +- `plugins/chain/plugin.cpp` — 模式检查和清除逻辑 +- `/schema_version` — 包含当前版本的纯文本文件 + +--- + +## 部署清单 + +- [ ] `CHAIN_NUM_HARDFORKS` 已递增 +- [ ] `CHAIN_VERSION` 已更新(如果对协议可见) +- [ ] `CHAIN_SCHEMA_VERSION` 已递增(如果任何 chainbase 对象布局发生变化) +- [ ] 硬分叉 `.hf` 文件已创建,带有未来激活时间戳 +- [ ] 所有新字段有零值默认值;`apply_hardfork` 注释解释为何不需要迁移 +- [ ] 新评估器在 `initialize_evaluators()` 中注册 +- [ ] 新虚拟操作在 `account_history` 插件中注册 +- [ ] 如果 `witness_object` 发生变化,`witness_api_object` 已更新 +- [ ] 如果添加了新 chainbase 对象,快照插件已更新 + +--- + +参见:[Fair-DPOS](./fair-dpos.md)、[紧急共识](./emergency-consensus.md)、[快照](../node/snapshot.md)。 diff --git a/@l10n/zh-CN/docs/development/building.md b/@l10n/zh-CN/docs/development/building.md new file mode 100644 index 0000000000..48b93b9371 --- /dev/null +++ b/@l10n/zh-CN/docs/development/building.md @@ -0,0 +1,196 @@ +# 构建 + +VIZ Ledger 节点使用 CMake 3.16+,需要带 coroutine 组件的 Boost 1.71+。支持的平台:Ubuntu 24.04+、macOS(Homebrew)、Windows(MSVC 或 MinGW)。 + +--- + +## Linux(Ubuntu/Debian) + +### 第一步:安装依赖(需要 root 权限) + +```bash +chmod +x install-deps-linux.sh +sudo ./install-deps-linux.sh +``` + +安装内容:CMake、GCC/G++、Git、Make、ccache、OpenSSL、Boost 1.71(所有必需组件,包括 coroutine/context)、readline 及压缩库。 + +### 第二步:构建(以普通用户身份运行,不要用 root) + +```bash +chmod +x build-linux.sh +./build-linux.sh +``` + +**常用选项:** + +```bash +./build-linux.sh # Release 构建(默认) +./build-linux.sh -l # LOW_MEMORY_NODE(验证者节点) +./build-linux.sh -n # Testnet 构建 +./build-linux.sh -t Debug -j4 # Debug 构建,4 个并行任务 +./build-linux.sh --skip-deps # 跳过依赖安装 +./build-linux.sh --install # 构建后安装到系统 + +# 自定义依赖路径 +./build-linux.sh --boost-root /opt/boost_1_74_0 --openssl-root /opt/openssl +``` + +### Fedora/RHEL + +相同脚本自动检测 `dnf`。安装的包:`cmake`、`gcc-c++`、`git`、`ccache`、`boost-devel`、`openssl-devel`、`bzip2-devel`、`zstd-devel`。 + +--- + +## macOS + +```bash +chmod +x build-mac.sh +./build-mac.sh +``` + +需要 Xcode 命令行工具和 Homebrew。脚本安装:`boost`、`cmake`、`git`、`autoconf`、`automake`、`libtool`、`openssl`、`readline`。 + +**选项:** + +```bash +./build-mac.sh -l # 低内存节点 +./build-mac.sh -n # Testnet +./build-mac.sh --skip-deps # 跳过 Homebrew 安装 +./build-mac.sh --boost-root /opt/boost_1_74_0 +``` + +--- + +## Windows(MinGW) + +设置必需的环境变量,然后运行批处理脚本: + +```cmd +set BOOST_ROOT=C:\Boost +set OPENSSL_ROOT_DIR=C:\OpenSSL-Win64 +build-mingw.bat +``` + +**可选变量:** + +| 变量 | 默认值 | 描述 | +|------|--------|------| +| `VIZ_BUILD_TYPE` | Release | Release 或 Debug | +| `VIZ_LOW_MEMORY` | OFF | 启用低内存节点 | +| `VIZ_BUILD_TESTNET` | OFF | Testnet 构建 | +| `VIZ_FULL_STATIC` | OFF | 完全静态二进制文件 | +| `VIZ_CMAKE_EXTRA` | — | 附加 CMake 标志 | + +**要求:** MinGW-w64(带 C++11 和 SSE4.2)、CMake 3.16+、Boost 1.71+(静态,`link=static threading=multi runtime-link=shared`)、Windows 版 OpenSSL。 + +--- + +## Windows(MSVC) + +```cmd +set BOOST_ROOT=C:\Boost +set OPENSSL_ROOT_DIR=C:\OpenSSL-Win64 +build-msvc.bat +``` + +**可选变量:** + +| 变量 | 默认值 | 描述 | +|------|--------|------| +| `VIZ_VS_VERSION` | "Visual Studio 17 2022" | Visual Studio 生成器 | +| `VIZ_BUILD_TYPE` | Release | 构建类型 | +| `VIZ_LOW_MEMORY` | OFF | 低内存节点 | +| `VIZ_BUILD_TESTNET` | OFF | Testnet 构建 | + +**要求:** Visual Studio 2019+(带"Desktop development with C++"工作负载)、CMake 3.16+。 + +--- + +## Docker + +仓库附带多种配置的 Dockerfile: + +| Dockerfile | 描述 | +|-----------|------| +| `Dockerfile-production` | 完整节点,Release,无 MongoDB | +| `Dockerfile-lowmem` | 同上但启用 `LOW_MEMORY_NODE=ON` | +| `Dockerfile-mongo` | 启用 MongoDB 插件 | +| `Dockerfile-testnet` | Testnet(`BUILD_TESTNET=ON`) | + +所有 Dockerfile 使用两阶段构建以最小化镜像大小,并使用 Boost 1.71 包(`libboost-coroutine-dev`、`libboost-context-dev`)。 + +--- + +## CMake 选项 + +| 选项 | 默认值 | 描述 | +|------|--------|------| +| `BUILD_TESTNET` | OFF | 为 testnet 构建 | +| `LOW_MEMORY_NODE` | OFF | 排除非共识数据(减少 RAM) | +| `CHAINBASE_CHECK_LOCKING` | OFF | 启用锁检查(仅用于开发) | +| `ENABLE_MONGO_PLUGIN` | OFF | 包含 MongoDB 插件 | +| `BUILD_SHARED_LIBRARIES` | OFF | 构建共享库 | +| `USE_PCH` | OFF | 启用预编译头文件(加速重新构建) | + +--- + +## 高级用法:`configure_build.py` + +封装 CMake,提供合理的默认值和交叉编译支持: + +```bash +# Release 构建 +python3 programs/build_helpers/configure_build.py --release --src ../.. + +# 带低内存的 Debug +python3 programs/build_helpers/configure_build.py --debug --low-memory + +# 使用 MinGW 交叉编译 Windows 版本 +python3 programs/build_helpers/configure_build.py --win --release + +# 自定义依赖路径 +python3 programs/build_helpers/configure_build.py \ + --boost-dir /opt/boost_1_74_0 \ + --openssl-dir /opt/openssl \ + --release +``` + +--- + +## 新插件脚手架 + +```bash +python3 programs/util/newplugin.py graphene myplugin +``` + +在 `libraries/plugins/myplugin/` 下生成:`CMakeLists.txt`、插件头文件/实现、API 头文件/实现。 + +--- + +## 构建目标 + +| 二进制文件 | 描述 | +|-----------|------| +| `vizd` | 主节点守护程序 | +| `cli_wallet` | 命令行钱包 | +| `js_operation_serializer` | JavaScript 操作序列化器 | +| `size_checker` | 大小分析工具 | + +--- + +## 故障排查 + +**Boost 版本低于 1.71:** 从包管理器安装 Boost 1.71+(Ubuntu 24.04 附带 1.74)。在 macOS 上,`brew install boost` 提供最新版本。在 Windows 上,从源码构建并包含 coroutine 组件。 + +**`Do not run this script as root` 错误:** 使用 `sudo ./install-deps-linux.sh` 安装依赖,然后以普通用户身份运行 `./build-linux.sh`。 + +**缺少 coroutine 组件:** 确保在 Ubuntu/Debian 上安装了 `libboost-coroutine-dev` 和 `libboost-context-dev`。 + +**macOS OpenSSL 未找到:** 手动设置 `OPENSSL_ROOT_DIR`:`export OPENSSL_ROOT_DIR=$(brew --prefix openssl)`。 + +**Windows MinGW 缺少变量:** 运行 `build-mingw.bat` 前必须设置 `BOOST_ROOT` 和 `OPENSSL_ROOT_DIR`。 + +--- + +参见:[插件开发](./plugin-development.md)、[测试](./testing.md)、[插件概述](../plugins/overview.md)。 diff --git a/@l10n/zh-CN/docs/development/debugging.md b/@l10n/zh-CN/docs/development/debugging.md new file mode 100644 index 0000000000..4874587050 --- /dev/null +++ b/@l10n/zh-CN/docs/development/debugging.md @@ -0,0 +1,186 @@ +# 调试 + +VIZ Ledger 节点提供多种调试工具:用于状态操作和重放的 `debug_node` 插件、用于密码学诊断的交易签名工具,以及带有 ANSI 颜色代码用于网络分析的 P2P 插件日志。 + +--- + +## Debug Node 插件 + +`debug_node` 插件提供 JSON-RPC API,用于: +- 从区块日志或 JSON 数组重放区块 +- 使用可配置的签名密钥在本地生成区块 +- 弹出(pop)区块以回滚状态 +- 检查验证者调度和硬分叉状态 +- 在特定区块高度应用数据库编辑 + +**使用受限 RPC 启用(仅限 localhost):** + +```ini +plugin = debug_node +webserver-http-endpoint = 127.0.0.1:8090 +``` + +### API 参考 + +| 方法 | 描述 | +|------|------| +| `debug_push_blocks(src, count)` | 从区块日志目录加载区块 | +| `debug_push_json_blocks(file, count, skip)` | 从 JSON 数组文件加载区块 | +| `debug_generate_blocks(key, count, skip, miss, edit)` | 使用给定签名密钥生成区块 | +| `debug_generate_blocks_until(key, time, sparse, skip)` | 将链推进到目标时间 | +| `debug_pop_block()` | 移除头区块并返回它 | +| `debug_get_witness_schedule()` | 获取当前验证者调度对象 | +| `debug_set_hardfork(id)` | 以编程方式设置硬分叉状态 | +| `debug_has_hardfork(id)` | 检查某个硬分叉是否已应用 | + +### 使用模式 + +```json +// 从区块日志重放 100 个区块 +{"method":"debug_node.debug_push_blocks","params":["/data/blockchain",100]} + +// 使用签名密钥生成 10 个区块(跳过验证) +{"method":"debug_node.debug_generate_blocks","params":["5K...",10,2,0,{}]} + +// 检查验证者调度 +{"method":"debug_node.debug_get_witness_schedule","params":[]} + +// 激活硬分叉 9 进行测试 +{"method":"debug_node.debug_set_hardfork","params":[9]} +``` + +**区块生成**临时修改活跃验证者的签名密钥以接受自签名区块,然后恢复原始密钥。 + +**数据库更新钩子**允许在特定区块高度注入状态变更: + +```cpp +// 从插件代码中 +debug_plugin.debug_update([&](database& db) { + // 在此修改 db 状态 +}, skip_flags); +``` + +--- + +## 交易签名工具 + +### sign_transaction + +从 stdin 读取 JSON 签名请求(每行一个),计算交易摘要和签名,并输出结果: + +```bash +echo '{"ref_block_num":1234,"ref_block_prefix":5678,...}' | ./sign_transaction +``` + +输出包括 `digest`、`sig_digest`、`key`(公钥)和 `signature`。 + +**诊断签名失败:** +1. 使用 `sign_transaction` 计算 `sig_digest`。 +2. 与钱包的 `sig_digest(chain_id)` 进行比较。 +3. 验证 WIF 密钥对应于声明的签名密钥。 + +### sign_digest + +使用 WIF 密钥对原始 SHA-256 摘要进行签名: + +```bash +echo '{"digest":"abc123...","wif":"5K..."}' | ./sign_digest +``` + +用于确认 chain ID 正确性并隔离签名可塑性问题。 + +--- + +## 网络调试(P2P 日志) + +P2P 插件使用 ANSI 颜色代码在控制台输出中进行视觉区分: + +| 颜色 | ANSI 代码 | 内容 | +|------|-----------|------| +| 白色 | `\033[97m` | 区块处理:交易数量、延迟 | +| 青色 | `\033[96m` | 对等节点统计:连接数、字节数、RTT | +| 灰色 | `\033[90m` | 详细调试上下文:DLT 模式、同步状态 | +| 橙色 | — | 连接警告和终止通知 | +| 红色 | — | 关键连接终止事件 | + +**解读 P2P 日志:** +- **白色**:快速了解区块处理活动和交易量。 +- **青色**:实时监控对等节点数量和连接健康状况。 +- **灰色**:调查 DLT 模式和同步协议详情。 +- **橙色/红色**:识别连接失败和对等节点封禁事件。 + +### 网络专用记录器 + +同步协商消息通过 `"sync"` 记录器发送。在 `config.ini` 中启用: + +```ini +[logger.sync] +level = info +appenders = stderr +``` + +P2P 节点消息使用 `"p2p"` 记录器(不是默认记录器): + +```ini +[logger.p2p] +level = info +appenders = stderr +``` + +--- + +## 调试配置 + +`share/vizd/config/config_debug.ini` 是为调试调优的配置模板: + +- 更大的共享内存大小和增长阈值,用于长时间重放。 +- 单写入线程,用于确定性区块生成。 +- 调优的读/写锁重试计数。 + +关键设置: + +```ini +shared-file-size = 12G +shared-file-full-threshold = 97 +shared-file-scale-rate = 3 +chainbase-check-locking = 0 +``` + +--- + +## 调试工作流 + +### 交易验证失败 + +1. 对失败的交易 JSON 运行 `sign_transaction`。 +2. 将计算得出的 `sig_digest` 与钱包生成的值进行比较。 +3. 验证 WIF 密钥对应于账户的权限。 +4. 使用 `debug_push_blocks` 重放包含该交易的区块并观察日志。 + +### 共识停滞 + +1. 使用 `debug_generate_blocks` 确定性地推进链。 +2. 使用 `debug_get_witness_schedule` 检查验证者调度。 +3. 如有需要,使用 `debug_set_hardfork` 设置硬分叉状态以测试激活逻辑。 + +### 网络连接问题 + +1. 检查**青色日志**了解对等节点数量和连接健康状况。 +2. 检查**白色日志**了解区块摄取延迟和空缺。 +3. 检查**灰色日志**了解快照同步期间的 DLT 同步状态。 +4. 检查**橙色/红色日志**了解终止事件和对等节点封禁。 +5. 将区块推送异常与日志中的特定区块号关联。 + +### 集成测试加速 + +使用跳过标志从 JSON 日志重放区块,绕过昂贵的验证: + +```json +{"method":"debug_node.debug_push_json_blocks","params":["/tmp/blocks.json",100,2]} +``` + +跳过标志:`1` = 跳过撤销会话,`2` = 跳过验证者签名,`4` = 跳过 merkle 检查。 + +--- + +参见:[构建](./building.md)、[测试](./testing.md)、[P2P 概述](../p2p/overview.md)、[插件概述](../plugins/overview.md)。 diff --git a/@l10n/zh-CN/docs/development/plugin-development.md b/@l10n/zh-CN/docs/development/plugin-development.md new file mode 100644 index 0000000000..c894fe745d --- /dev/null +++ b/@l10n/zh-CN/docs/development/plugin-development.md @@ -0,0 +1,252 @@ +# 插件开发 + +VIZ Ledger 的插件系统基于 AppBase 构建。每个插件遵循相同的生命周期,向 JSON-RPC 层注册其 API,并订阅链数据库信号。 + +--- + +## 插件结构 + +插件由以下部分组成: + +- **头文件**(`include/graphene/plugins//plugin.hpp`)— 声明插件类及其 API。 +- **实现**(`plugin.cpp`)— 生命周期钩子、信号订阅、API 方法主体。 +- **CMakeLists.txt** — 声明目标并链接依赖项。 + +### 创建新插件脚手架 + +```bash +python3 programs/util/newplugin.py graphene myplugin +``` + +在 `plugins/myplugin/` 下生成样板代码: +- `CMakeLists.txt` +- `include/graphene/plugins/myplugin/plugin.hpp` +- `plugin.cpp` +- API 头文件和实现文件 + +--- + +## 生命周期 + +``` +plugin_initialize(options) + └── 注册 API 工厂 + └── 解析选项 + +plugin_startup() + └── 连接数据库信号 + └── 启动后台线程 + +plugin_shutdown() + └── 断开信号 + └── 停止后台线程 +``` + +AppBase 按依赖顺序调用这三个方法。永远不要直接调用 `plugin_startup()`。 + +--- + +## JSON-RPC API 注册 + +插件使用宏驱动的访问者向 `json_rpc` 插件注册方法: + +```cpp +// 在 plugin.hpp 中 — 声明 API +DECLARE_API( + (get_account_history) + (get_ops_in_block) +) + +// 在 plugin.cpp 中 — 启动时 +plugin_startup() { + auto& json_rpc = appbase::app().get_plugin(); + json_rpc.add_api( + MAKE_API(this, get_account_history) + MAKE_API(this, get_ops_in_block) + ); +} +``` + +每个 API 方法接受单个参数结构体并返回单个结果结构体。Void 方法使用专用的空结果类型。 + +**方法命名:** JSON-RPC 方法名为 `<插件命名空间>.<方法名>`。例如,`account_history.get_account_history`。 + +--- + +## 数据库信号 + +链数据库发出插件可订阅的信号: + +| 信号 | 触发条件 | +|------|----------| +| `applied_block` | 区块应用后(后状态) | +| `pre_apply_operation` | 每个操作应用前 | +| `on_applied_transaction` | 交易应用后 | +| `post_apply_operation` | 每个操作应用后 | + +```cpp +// 在 plugin_startup() 中连接 +auto& db = appbase::app().get_plugin().db(); + +db.applied_block.connect([this](const signed_block& b) { + on_applied_block(b); +}); + +db.pre_apply_operation.connect([this](const operation_notification& note) { + on_pre_apply_operation(note); +}); +``` + +**重要:** 信号处理器在区块处理期间同步运行。不要在其中执行繁重的工作——将任务排队到后台线程。 + +--- + +## 数据库访问 + +### 读取(从 API 方法) + +使用弱读锁以最小化争用: + +```cpp +auto& db = appbase::app().get_plugin().db(); +// 在 API 处理器中 db 自动加读锁 +auto account = db.get_account("alice"); +``` + +### 写入(从信号处理器或评估器) + +只在信号处理器或评估器内写入——永远不要从 API 方法写入。 + +```cpp +// 在 applied_block 处理器内 +db.modify(db.get_account("alice"), [](account_object& a) { + a.some_field = new_value; +}); +``` + +--- + +## 自定义数据库索引 + +插件可以向数据库添加自己的索引: + +```cpp +// 在 plugin_startup() 中,链初始化后 +auto& db = appbase::app().get_plugin().db(); +db.add_plugin_index(); +``` + +按照现有对象的模式在头文件中定义对象和索引: + +```cpp +// 对象定义 +class my_object : public chainbase::object { + id_type id; + account_name_type account; + uint64_t some_field; +}; + +// MultiIndex 容器 +using my_index = chainbase::shared_multi_index_container< + my_object, + indexed_by< + ordered_unique, member>, + ordered_unique, member> + > +>; +``` + +--- + +## 自定义操作评估器 + +处理新操作类型: + +```cpp +// 在协议层定义操作并注册评估器 +class my_operation_evaluator : public evaluator { +public: + void do_apply(const my_operation& op) { + // 验证并应用状态变更 + auto& db = this->db(); + // ... + } +}; + +// 在数据库初始化时注册 +db.register_evaluator(); +``` + +使用 `has_hardfork(CHAIN_HARDFORK_N)` 检查来控制向后兼容的行为变更。 + +--- + +## WebSocket 实时事件 + +发送实时通知: + +```cpp +// 在 plugin_startup() 期间,向 webserver 注册区块回调 +auto& ws = appbase::app().get_plugin(); +ws.add_handler("my_stream", [this](const fc::variant& params, fc::variant& result) { + // 流处理器 +}); +``` + +Webserver 插件运行自己的 `io_service` 线程——使用 `ws.post([]{...})` 从任意线程发布回调。 + +--- + +## 依赖声明 + +在插件的 `plugin_requires()` 中声明依赖项: + +```cpp +static std::vector plugin_requires() { + return { &appbase::app().get_plugin(), + &appbase::app().get_plugin() }; +} +``` + +AppBase 自动解析初始化顺序。 + +--- + +## 性能指南 + +- **API 方法**:使用索引查找,不要全扫描。为热访问模式添加插件索引。 +- **信号处理器**:快速返回。将繁重处理排队到专用的 `fc::thread`。 +- **缓存**:在内存中缓存热路径结果;在 `applied_block` 时使缓存失效。 +- **分页**:始终对大型结果集分页,而不是返回无界集合。 + +--- + +## 测试插件 + +使用 `debug_node` 插件模拟链条件: + +```json +{"method":"debug_node.debug_generate_blocks","params":["5K...",10,0,0,{}]} +``` + +使用 Boost.Test 和现有测试框架编写单元测试。将测试添加到适当的类别套件(`operation_tests`、`block_tests` 等)。 + +对于集成测试,将插件与链一起加载,并使用 `debug_push_blocks` 重放已知的区块序列。 + +--- + +## 部署 + +在 `config.ini` 中启用插件: + +```ini +plugin = myplugin +``` + +某些插件在现有链上启用时需要完整重新索引(特别是那些跟踪历史操作的插件)。明确记录此要求。 + +对于外部(第三方)插件,将其放置在 `plugins/external/` 中——CMake 会自动发现它们。 + +--- + +参见:[插件概述](../plugins/overview.md)、[Database API](../plugins/database-api.md)、[构建](./building.md)、[调试](./debugging.md)。 diff --git a/@l10n/zh-CN/docs/development/testing.md b/@l10n/zh-CN/docs/development/testing.md new file mode 100644 index 0000000000..e6c4fcf0d5 --- /dev/null +++ b/@l10n/zh-CN/docs/development/testing.md @@ -0,0 +1,159 @@ +# 测试 + +VIZ Ledger 使用 Boost.Test 进行单元测试,并提供用于集成级测试的实用程序。 + +--- + +## 单元测试 + +单元测试二进制文件通过 CMake 作为 `libraries/chain` 目标的一部分构建。 + +### 测试类别 + +| 套件 | 描述 | +|------|------| +| `basic_tests` | 核心功能验证 | +| `block_tests` | 区块链特定逻辑 | +| `live_tests` | 过去硬分叉场景验证 | +| `operation_tests` | 操作验证 | +| `operation_time_tests` | 时间相关操作(锁仓提取等) | +| `serialization_tests` | 序列化往返检查 | + +### 运行测试 + +```bash +# 运行所有测试 +./tests/chain_test + +# 按套件过滤 +./tests/chain_test --run_test=basic_tests + +# 按特定测试用例过滤 +./tests/chain_test --run_test=basic_tests/my_test_case + +# 调整详细程度 +./tests/chain_test --log_level=all --report_level=detailed +``` + +### Boost.Test 运行时选项 + +| 选项 | 值 | 描述 | +|------|-----|------| +| `--log_level` | all, success, test_suite, message, warning, error, cpp_exception, system_error, fatal_error, nothing | 日志详细程度 | +| `--report_level` | no, confirm, short, detailed | 报告详细程度 | +| `--run_test` | `` 或 `/` | 过滤运行的测试 | + +--- + +## 代码覆盖率 + +在 Debug 构建中启用覆盖率: + +```bash +cmake -DCMAKE_BUILD_TYPE=Debug -DCOVERAGE=ON .. +make chain_test + +# 捕获基线 +lcov --capture --initial --directory . --output-file base.info + +# 运行测试 +./tests/chain_test + +# 捕获测试跟踪文件 +lcov --capture --directory . --output-file test.info + +# 合并并清理 +lcov --add-tracefile base.info --add-tracefile test.info --output-file merged.info +lcov --remove merged.info '/usr/*' '*/tests/*' --output-file coverage.info + +# 生成 HTML 报告 +genhtml coverage.info --output-directory coverage_report +``` + +--- + +## 集成工具 + +### 区块日志测试工具 + +`test_block_log` 工具测试区块存储和检索: + +```bash +# 构建位置:programs/util/test_block_log +./test_block_log /tmp/test_block_log_dir +``` + +打开区块日志,附加签名区块,刷新,然后读取回来。用于验证区块存储逻辑。 + +### 交易签名工具 + +```bash +# 签名交易(每行 JSON 输入) +echo '{"ref_block_num":...}' | ./sign_transaction + +# 签名原始摘要 +echo '{"digest":"...","wif":"5K..."}' | ./sign_digest +``` + +两个工具都输出计算得出的 `digest`、`sig_digest`、签名密钥和签名。通过将 sig_digest 与钱包生成的签名进行比较,可诊断签名失败。 + +--- + +## 测试 API 插件 + +`test_api` 插件为集成测试公开两个 JSON-RPC API(`test_api_a` 和 `test_api_b`)。在 `programs/vizd/main.cpp` 中注册,由节点进程加载。 + +--- + +## 测试网环境 + +用于隔离测试,使用 testnet 配置: + +```bash +# 启动 testnet 节点 +vizd --config share/vizd/config/config_testnet.ini + +# 或构建 testnet Docker 镜像 +docker build -f share/vizd/docker/Dockerfile-testnet -t viz-testnet . +``` + +`share/vizd/snapshot-testnet.json` 快照可用于快速初始化 testnet。 + +--- + +## 持续集成 + +CI 矩阵为多种变体构建 Docker 镜像: + +| 变体 | Dockerfile | +|------|-----------| +| 标准 | `Dockerfile-production` | +| 低内存 | `Dockerfile-lowmem` | +| MongoDB | `Dockerfile-mongo` | +| Testnet | `Dockerfile-testnet` | + +按分支和标签触发构建,配置凭据后发布构建产物。 + +--- + +## 编写新测试 + +使用 Boost.Test 宏将新测试套件添加到现有测试目标: + +```cpp +#include + +BOOST_AUTO_TEST_SUITE(my_feature_tests) + +BOOST_AUTO_TEST_CASE(basic_case) { + BOOST_CHECK_EQUAL(1 + 1, 2); +} + +BOOST_AUTO_TEST_SUITE_END() +``` + +将测试归入适当的类别套件。优先使用命中真实链状态的集成测试,而非模拟测试,以发现模拟与生产行为之间的差异。 + +--- + +参见:[构建](./building.md)、[调试](./debugging.md)、[插件开发](./plugin-development.md)。 diff --git a/@l10n/zh-CN/docs/governance/chain-properties.md b/@l10n/zh-CN/docs/governance/chain-properties.md new file mode 100644 index 0000000000..bec0b7b1c5 --- /dev/null +++ b/@l10n/zh-CN/docs/governance/chain-properties.md @@ -0,0 +1,158 @@ +# 链属性 + +链属性是网络的可治理参数:费用、区块大小、通胀率、惩罚规则等。没有任何中央机构设置这些参数——每个活跃验证者发布其首选值,区块链对所有活跃验证者取**中位数**并应用。 + +--- + +## 工作原理 + +### 1. 验证者发布首选项 + +每个验证者通过 `versioned_chain_properties_update_operation` 提交其首选参数: + +```json +[46, { + "owner": "alice", + "props": [3, { + "account_creation_fee": "1.000 VIZ", + "maximum_block_size": 131072, + ... + }] +}] +``` + +`[3, {...}]` 表示版本 3(`chain_properties_hf9`,当前格式)。 + +### 2. 中位数计算 + +在每次验证者计划更新时,区块链调用 `update_median_witness_props()`。对**每个属性独立地**: +1. 收集每个活跃验证者的值。 +2. 排序。 +3. 取**中位数**(索引 `active.size() / 2`)。 + +``` +示例——5 个验证者对 account_creation_fee 投票: + 0.5, 1.0, 1.0, 2.0, 5.0 VIZ + ↑ + 中位数 = 1.0 VIZ +``` + +中位数对极端值有抵抗力:单个验证者无法导致突然的大幅变化;要显著移动任何参数,需要多数人同意。 + +### 3. 应用 + +结果 `median_props` 对象存储在 `witness_schedule_object` 中,并在所有区块处理中强制执行。 + +--- + +## 所有可治理属性 + +### 账户和委托 + +| 属性 | 类型 | 默认值 | 描述 | +|------|------|-------|------| +| `account_creation_fee` | asset(VIZ) | 1.000 VIZ | 创建新账户的最低费用 | +| `create_account_delegation_ratio` | uint32 | 10 | 所需委托 = ratio × fee | +| `create_account_delegation_time` | uint32(秒) | 2592000(30天) | 创建委托的锁定时间 | +| `min_delegation` | asset(VIZ) | 1.000 VIZ | 任何 SHARES 委托的最低金额 | + +### 区块大小和带宽 + +| 属性 | 类型 | 默认值 | 描述 | +|------|------|-------|------| +| `maximum_block_size` | uint32(字节) | 131072 | 最大区块大小;控制吞吐量 | +| `bandwidth_reserve_percent` | uint16(bp) | 1000(10%) | 小账户的额外带宽 | +| `bandwidth_reserve_below` | asset(SHARES) | 500.000000 | 获得带宽预留的资格阈值 | +| `data_operations_cost_additional_bandwidth` | uint32(%) | 0 | 数据操作(custom_operation)的额外带宽倍数 | + +### 通胀和经济 + +| 属性 | 类型 | 默认值 | 描述 | +|------|------|-------|------| +| `inflation_witness_percent` | uint16(bp) | 2000(20%) | 验证者在区块通胀中的份额 | +| `inflation_ratio_committee_vs_reward_fund` | uint16(bp) | 5000(50%) | 剩余通胀的分配:委员会基金 vs 奖励基金 | +| `inflation_recalc_period` | uint32(区块) | 806400(~28天) | 通胀重新计算的频率 | + +通胀流程:`block_reward × inflation_witness_percent` → 验证者。剩余分配:`inflation_ratio_committee_vs_reward_fund` → 委员会基金;其余 → 奖励基金。 + +### 奖励系统 + +| 属性 | 类型 | 默认值 | 描述 | +|------|------|-------|------| +| `min_curation_percent` | uint16(bp) | 500(5%) | 内容支付中的最低策展奖励份额 | +| `max_curation_percent` | uint16(bp) | 500(5%) | 最高策展奖励份额 | +| `vote_accounting_min_rshares` | uint32 | 5000000 | 奖励产生非零收益所需的最低 rshares | +| `flag_energy_additional_cost` | uint16(bp) | 0 | 反对票/标记的额外能量成本 | + +### 验证者问责 + +| 属性 | 类型 | 默认值 | 描述 | +|------|------|-------|------| +| `witness_miss_penalty_percent` | uint16(bp) | 100(1%) | 错过区块时的投票权重降低 | +| `witness_miss_penalty_duration` | uint32(秒) | 86400(1天) | 错过惩罚的持续时间 | + +### 费用 + +所有费用进入委员会基金(DAO 国库)。 + +| 属性 | 类型 | 默认值 | 描述 | +|------|------|-------|------| +| `committee_create_request_fee` | asset(VIZ) | 100.000 VIZ | 创建委员会资金请求的费用 | +| `create_paid_subscription_fee` | asset(VIZ) | 100.000 VIZ | 创建付费订阅的费用 | +| `account_on_sale_fee` | asset(VIZ) | 10.000 VIZ | 将账户挂牌出售的费用 | +| `subaccount_on_sale_fee` | asset(VIZ) | 100.000 VIZ | 将子账户创建权挂牌出售的费用 | +| `witness_declaration_fee` | asset(VIZ) | 10.000 VIZ | 验证者注册的一次性费用 | +| `create_invite_min_balance` | asset(VIZ) | 10.000 VIZ | 最低邀请余额 | + +### 质押提取 + +| 属性 | 类型 | 默认值 | 描述 | +|------|------|-------|------| +| `withdraw_intervals` | uint16 | 28 | SHARES 解除质押的每日分期数 | + +--- + +## 属性版本 + +属性分阶段随硬分叉引入: + +| 版本 | 索引 | 硬分叉 | 新增字段 | +|------|------|-------|---------| +| `chain_properties_init` | 0 | 创世 | account_creation_fee、maximum_block_size、委托参数、策展、带宽、标记成本、最低 rshares 投票、委员会阈值 | +| `chain_properties_hf4` | 1 | HF4 | inflation_witness_percent、inflation_ratio_committee_vs_reward_fund、inflation_recalc_period | +| `chain_properties_hf6` | 2 | HF6 | data_operations_cost_additional_bandwidth、witness_miss_penalty_percent、witness_miss_penalty_duration | +| `chain_properties_hf9` | 3 | HF9 | create_invite_min_balance、committee_create_request_fee、create_paid_subscription_fee、account_on_sale_fee、subaccount_on_sale_fee、witness_declaration_fee、withdraw_intervals | + +所有新的验证者属性提交请使用版本索引 3(`chain_properties_hf9`)。 + +--- + +## 治理循环 + +``` +SHARES 持有者 → 为验证者投票 +验证者 → 发布首选属性值 +区块链 → 取活跃集合的中位数 +中位数 → 作为实时网络规则应用 +``` + +更改参数需要**大多数活跃验证者**发布新值。流程: +1. 社区讨论所需变更(例如降低费用)。 +2. 验证者更新其发布的属性。 +3. 用户将投票转移给发布所需值的验证者。 +4. 一旦大多数活跃验证者发布新值,中位数就会移动。 +5. 新值自动生效——无需硬分叉或治理投票。 + +--- + +## 读取当前属性 + +```json +{ "method": "database_api.get_chain_properties", "params": [] } +``` + +返回当前生效的中位数属性。参见 [Database API](../plugins/database-api.md#get_chain_properties)。 + +--- + +参见:[验证者](../protocol/operations/validators.md)、[Database API](../plugins/database-api.md)、[质押和 DAO](./staking-and-dao.md)。 diff --git a/@l10n/zh-CN/docs/governance/committee.md b/@l10n/zh-CN/docs/governance/committee.md new file mode 100644 index 0000000000..2e0d45683e --- /dev/null +++ b/@l10n/zh-CN/docs/governance/committee.md @@ -0,0 +1,116 @@ +# 委员会 DAO + +委员会是 VIZ Ledger 的去中心化资助机制。任何账户都可以创建工作者请求(资金提案);所有 SHARES 持有者通过股权加权的双极投票来批准或拒绝它。 + +委员会基金从请求创建费用中积累,以及从分配给它的区块通胀份额中积累(由 `inflation_ratio_committee_vs_reward_fund` 链属性管理)。 + +--- + +## 操作 + +| 操作 | ID | 授权 | 描述 | +|------|----|----|------| +| `committee_worker_create_request_operation` | 35 | `creator` 的 regular | 创建资金请求 | +| `committee_worker_cancel_request_operation` | 36 | `creator` 的 regular | 取消自己的活跃请求 | +| `committee_vote_request_operation` | 37 | `voter` 的 regular | 对请求投票 | + +完整字段详情参见[委员会操作](../protocol/operations/committee.md)。 + +--- + +## 投票机制 + +每个 SHARES 持有者可以在 **−10000 到 +10000**(基点)范围内用 `vote_percent` 投票: + +- 正票:支持请求。 +- 负票:反对请求。 +- 零:移除投票。 + +**投票权重** = `effective_vesting_shares × vote_percent / 10000`。 + +在请求活跃期间可随时更改投票。 + +--- + +## 批准计算 + +当请求的 `end_time` 到达时: + +``` +max_rshares = SUM(voter.effective_vesting_shares,所有投票者) +actual_rshares = SUM(voter.effective_vesting_shares × vote_percent / 10000) +``` + +**批准的三个条件:** + +1. **参与度:** `max_rshares ≥ total_vesting_shares × committee_request_approve_min_percent / 10000` + (未满足 → 拒绝,参与度不足) + +2. **共识:** `actual_rshares > 0` + (负数 → 拒绝,社区反对) + +3. **最低支付:** + ``` + payout = required_amount_max × (actual_rshares / max_rshares) + ``` + (如果 `payout < required_amount_min` → 拒绝) + +所有条件满足 → **批准**,支付 = 计算值。 + +--- + +## 请求生命周期 + +``` +[已创建,status=0] + └── 投票期(5–30 天) + ├── 参与度不足 → [已拒绝,status=2] + ├── 净负数/低于最低值 → [已过期,status=3] + ├── 创建者取消 → [已取消,status=1] + └── 已批准 → [已批准,status=4] + └── 支付(每 200 个区块) + └── [已完成,status=5] +``` + +--- + +## 支付处理 + +已批准请求(status 4)每 200 个区块(~10 分钟)从委员会基金获得支付。基金在所有当前已批准请求之间均等分配: + +``` +payment = min(committee_fund / count_approved_requests, remain_payout_amount) +``` + +生命周期中触发的虚拟操作: + +| 虚拟操作 | 触发条件 | +|---------|---------| +| `committee_cancel_request_operation`(ID 38) | 请求在未批准情况下过期 | +| `committee_approve_request_operation`(ID 39) | 达到批准阈值 | +| `committee_payout_request_operation`(ID 40) | 支付已处理 | +| `committee_pay_request_operation`(ID 41) | 工作者获得付款 | + +--- + +## 查询委员会状态 + +```json +{ "method": "committee_api.get_committee_request", "params": [42] } +{ "method": "committee_api.get_committee_request_votes", "params": [42] } +{ "method": "committee_api.get_committee_requests_list", "params": [0, 100] } +``` + +--- + +## 关键属性 + +- **双极性**:每个投票者以细粒度的强度表达支持或反对。 +- **股权加权**:投票由锁定代币加权,使操纵代价高昂。 +- **比例支付**:共识越强 → 支付越高(最高为 `required_amount_max`)。 +- **自我资助**:创建费用流入委员会基金。 +- **非托管**:无受信中介;协议自动强制执行所有规则。 + +--- + +参见:[委员会操作](../protocol/operations/committee.md)、[质押和 DAO](./staking-and-dao.md)、[链属性](./chain-properties.md)、[虚拟操作](../protocol/virtual-operations.md)。 diff --git a/@l10n/zh-CN/docs/governance/staking-and-dao.md b/@l10n/zh-CN/docs/governance/staking-and-dao.md new file mode 100644 index 0000000000..85064a989e --- /dev/null +++ b/@l10n/zh-CN/docs/governance/staking-and-dao.md @@ -0,0 +1,166 @@ +# 质押和 DAO 治理 + +## 质押(SHARES) + +质押将流动 VIZ 代币转换为 **SHARES**(归属份额)。质押的代币被锁定,不能直接转让,但授予与质押数量成比例的治理权力。 + +每个账户有三个归属字段: + +| 字段 | 含义 | +|------|------| +| `vesting_shares` | 账户拥有的 SHARES | +| `delegated_vesting_shares` | 委托给他人的 SHARES(减少权力) | +| `received_vesting_shares` | 通过委托收到的 SHARES(增加权力) | + +**有效归属份额**——所有加权操作中使用的治理权力: + +``` +effective_vesting_shares = vesting_shares − delegated_vesting_shares + received_vesting_shares +``` + +--- + +## 质押操作 + +### 质押:`transfer_to_vesting_operation`(ID 3) + +将流动 VIZ 转换为 SHARES。可以归属到另一个账户的余额。 + +```json +[3, {"from": "alice", "to": "alice", "amount": "1000.000 VIZ"}] +``` + +### 解质押:`withdraw_vesting_operation`(ID 4) + +通过 `withdraw_intervals` 次每日分期付款启动逐步提取(由链治理,默认 28 天)。设置为 `"0.000000 SHARES"` 可取消。 + +```json +[4, {"account": "alice", "vesting_shares": "1000.000000 SHARES"}] +``` + +每个间隔代币释放时,虚拟 `fill_vesting_withdraw_operation` 触发一次。 + +### 提取路由:`set_withdraw_vesting_route_operation`(ID 11) + +将一定百分比的提取路由到另一个账户,可选择立即重新归属。 + +```json +[11, {"from_account": "alice", "to_account": "bob", "percent": 5000, "auto_vest": true}] +``` + +每个账户最多 10 条路由;所有路由的总百分比不得超过 10000。 + +### 委托:`delegate_vesting_shares_operation`(ID 19) + +将治理权力(非所有权)转让给另一个账户。设置为 `"0.000000 SHARES"` 可移除。 + +```json +[19, {"delegator": "alice", "delegatee": "bob", "vesting_shares": "500.000000 SHARES"}] +``` + +移除委托时,SHARES 进入 7 天返回窗口。返回时触发虚拟 `return_vesting_delegation_operation`。 + +--- + +## SHARES 的使用场景 + +SHARES 是通用治理代币。每个有意义的操作都按 `effective_vesting_shares` 加权。 + +### 1. 验证者投票(Fair-DPOS) + +```json +[7, {"account": "alice", "witness": "bob", "approve": true}] +``` + +投票权重在账户投票的所有验证者之间**均等分配**: + +``` +fair_weight = effective_vesting_shares / witnesses_voted_for +``` + +这防止了集中——为 10 个验证者投票,每人获得你权重的 1/10。账户也可以设置代理(`account_witness_proxy_operation`),将所有验证者投票委托给另一个账户。 + +### 2. 委员会 DAO 投票 + +```json +[37, {"voter": "alice", "request_id": 42, "vote_percent": 7500}] +``` + +投票权重:`effective_vesting_shares × vote_percent / 10000`。 +范围:−10000(强烈反对)到 +10000(强烈支持)。 + +### 3. 奖励(社交奖励分配) + +```json +[47, {"initiator": "alice", "receiver": "bob", "energy": 1000, ...}] +``` + +奖励大小与以下比例: +``` +rshares = effective_vesting_shares × energy / 10000 +``` + +拥有 10× 更多 SHARES 的账户以相同能量创造 10× 更大的奖励。 + +### 4. 链参数治理 + +验证者发布首选链参数;区块链应用中位数。由于验证者由股权加权投票选出,所有链参数间接由 SHARES 持有者治理。 + +### 5. 交易带宽 + +网络带宽按 `effective_vesting_shares` 比例分配。低于 500 SHARES 的账户获得额外 10% 带宽预留。 + +### 6. 通过委托创建账户 + +可以通过以 10× 比例将 SHARES 委托给新账户来引导账户创建(锁定 30 天),使无需流动代币即可创建账户。 + +--- + +## VIZ 作为 DAO + +| 传统 DAO | VIZ 区块链 | +|---------|-----------| +| DAO 国库 | 委员会基金 + 奖励基金 | +| 治理代币 | SHARES | +| 提案投票 | 委员会工作者请求 | +| 董事会 | 当选验证者 | +| 董事选举 | 验证者投票(Fair-DPOS) | +| 股息分配 | 奖励机制(奖励基金) | +| 章程/规则 | 链属性(中位数治理) | +| 代理投票 | `delegate_vesting_shares` + 验证者代理 | + +### 治理属性 + +1. **比例代表制**:1 SHARES = 各处 1 单位影响力。 +2. **双极投票**:负票积极反对,而非仅仅弃权。 +3. **持续治理**:没有固定投票季节——投票可随时更改。 +4. **利益绑定**:SHARES 被锁定;退出需要 28 天。长期对齐。 +5. **无受信中介**:所有规则由协议代码强制执行。 +6. **无托管委托**:治理权力可随时借出和撤回。 + +### 治理循环 + +``` +质押 VIZ → 获得 SHARES → 治理权力 + ├── 为验证者投票 → 区块生产和链参数 + ├── 委员会投票 → 国库支出 + ├── 奖励其他账户 → 从奖励基金分配价值 + └── 委托给他人 → 放大盟友的治理权力 +``` + +--- + +## 关键常量 + +| 常量 | 值 | 描述 | +|------|---|------| +| `CHAIN_VESTING_WITHDRAW_INTERVALS` | 28 | 每日提取分期数 | +| `CHAIN_VESTING_WITHDRAW_INTERVAL_SECONDS` | 86400(1天) | 分期间隔 | +| `CHAIN_MAX_WITHDRAW_ROUTES` | 10 | 每账户最大提取路由数 | +| `CHAIN_ENERGY_REGENERATION_SECONDS` | 432000(5天) | 完全能量恢复时间 | +| `CHAIN_100_PERCENT` | 10000 | 基点分母 | +| `CHAIN_MAX_ACCOUNT_WITNESS_VOTES` | 100 | 每账户最大验证者投票数 | + +--- + +参见:[链属性](./chain-properties.md)、[委员会 DAO](./committee.md)、[奖励](../protocol/operations/awards.md)、[转账](../protocol/operations/transfers.md)。 diff --git a/@l10n/zh-CN/docs/introduction/architecture.md b/@l10n/zh-CN/docs/introduction/architecture.md new file mode 100644 index 0000000000..e1daeba35e --- /dev/null +++ b/@l10n/zh-CN/docs/introduction/architecture.md @@ -0,0 +1,177 @@ +# 架构概述 + +VIZ Ledger 实现为一个模块化的 C++ 守护进程(`vizd`),由分层库和插件系统组成。本页描述结构层次、设计模式及组件交互方式。 + +--- + +## 分层结构 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 程序 │ +│ vizd(节点守护进程) cli_wallet(CLI 钱包) │ +├─────────────────────────────────────────────────────────────────┤ +│ 插件 │ +│ chain │ validator │ p2p │ webserver │ json_rpc │ database_api │ +│ social_network │ snapshot │ committee_api │ invite_api │ ... │ +├─────────────────────────────────────────────────────────────────┤ +│ 核心库 │ +│ libraries/chain — 区块链状态机,fork 数据库 │ +│ libraries/protocol — 操作类型,交易 │ +│ libraries/network — P2P 消息传递 │ +│ libraries/api — 共享 API 属性类型 │ +│ libraries/wallet — 交易构建辅助工具 │ +│ libraries/time — NTP 感知时间工具 │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### 库 + +| 库 | 关键文件 | 用途 | +|----|---------|------| +| `libraries/chain` | `database.hpp` | 区块链状态:账户、区块、对象、fork 数据库、共享内存 | +| `libraries/protocol` | `operations.hpp` | 所有 64+ 操作类型的 `static_variant` 联合 | +| `libraries/network` | `node.hpp` | P2P 引擎:对等连接、同步、消息传播 | +| `libraries/api` | `chain_api_properties.hpp` | API 插件返回的共享类型 | +| `libraries/wallet` | `wallet.hpp` | 远程节点 API 调用、交易构建 | +| `libraries/time` | `time.hpp` | 用于区块槽位时序的 NTP 同步 | + +### 插件 + +插件在启动时向 `AppBase` 框架注册,并实现生命周期钩子(`plugin_initialize`、`plugin_startup`、`plugin_shutdown`)。 + +| 插件 | 作用 | +|------|------| +| `chain` | 打开数据库,验证并应用区块/交易 | +| `validator` | 按计划生产区块(Fair-DPOS),管理 NTP 和看门狗 | +| `p2p` | 管理对等连接,同步区块,传播交易 | +| `webserver` | API 访问的 HTTP 和 WebSocket 服务器 | +| `json_rpc` | 将 JSON-RPC 请求路由到已注册的 API 插件 | +| `database_api` | 只读查询:账户、区块、交易、全局数据 | +| `social_network` | 索引和查询内容、投票、回复 | +| `snapshot` | 创建和恢复状态快照 | +| `committee_api` | 委员会工作请求查询 | +| `invite_api` | 邀请对象查询 | +| `paid_subscription_api` | 付费订阅查询 | +| `account_history` | 账户操作历史索引 | +| `account_by_key` | 按公钥查找账户 | +| `follow` | 关注/屏蔽关系索引 | +| `tags` | 基于标签的内容索引 | +| `witness_api` | 验证者计划和签名密钥查询 | +| `debug_node` | 测试工具:注入区块、设置时间 | + +--- + +## 设计模式 + +### 事件驱动的观察者(信号) + +链的 `Database` 在关键点发出 `fc::signal` 事件。插件订阅这些信号以实现索引、历史记录和通知,而无需与核心链耦合。 + +``` +push_block() / push_transaction() + │ + ├── pre_apply_operation ──► 订阅者插件(前置钩子) + ├── [evaluator 应用状态变更] + ├── post_apply_operation ──► 订阅者插件(后置钩子) + └── applied_block ──► 订阅者插件(区块已最终确认) +``` + +### 工厂 + 策略(Evaluator 注册表) + +每种操作类型都有专用的 **evaluator** 类。`EvaluatorRegistry` 将操作类型 ID 映射到 evaluator 实例。应用交易时: + +1. `Database` 从 `static_variant` 中提取操作类型标签。 +2. 注册表返回已注册的 evaluator。 +3. Evaluator 的 `do_apply(op)` 修改数据库状态。 + +添加新操作只需注册新的 evaluator——无需修改调度循环。 + +### 基于插件的架构(AppBase) + +`vizd/main.cpp` 在调用 `app().exec()` 之前向 `AppBase` 注册所有插件。每个插件声明其选项和依赖项;AppBase 负责管理顺序和生命周期。 + +``` +main() ──► register_plugin() + ──► register_plugin() + ──► register_plugin() + ──► ... + ──► app().initialize(argc, argv) + ──► app().startup() + ──► app().exec() ← 事件循环运行至 SIGINT/SIGTERM +``` + +### MVC 分离 + +| 层 | 组件 | 职责 | +|----|------|------| +| 数据 | `libraries/chain/database` | 状态持久化、验证、信号 | +| 控制 | 插件(`chain`、`validator`、`p2p`) | 生命周期、区块/交易接收、协调 | +| 视图 | API 插件(`database_api`、`social_network`、…) | 只读查询端点 | + +--- + +## 数据流:传入区块 + +``` +对等节点(P2P)──► p2p_plugin::handle_block() + ──► chain_plugin::accept_block() + ──► database::push_block() + ├── 验证区块头和签名 + ├── 对每笔交易: + │ ├── 验证权限 + │ └── evaluator->do_apply(operation) + ├── 处理虚拟操作(奖励、兑现) + ├── 发出 applied_block 信号 + └── 更新 fork 数据库 / LIB +``` + +## 数据流:API 请求 + +``` +客户端(HTTP/WS)──► webserver_plugin + ──► json_rpc_plugin::call() + ──► registry.find_api_method(api, method) + ──► database_api / social_network / ... + ──► database::get_*(...) + ──► 返回 JSON 结果 +``` + +--- + +## 并发模型 + +| 关注点 | 方法 | +|--------|------| +| 写操作 | 单一写线程(可选配置 `single-write-thread`) | +| 读操作 | 通过 `chainbase` 共享内存支持多个并发读取者 | +| P2P I/O | 专用 `boost::asio::io_service` 线程池 | +| 区块生产计时器 | 验证者插件中独立的 `io_service` + 线程,防止 P2P 延迟 | +| RPC 服务 | 可配置线程池(`rpc-endpoint-thread-pool-size`) | + +最重要的不变量:**任何时刻只有一个线程可以持有数据库的写锁。** 所有 evaluator 和区块处理代码都在该锁下运行。 + +--- + +## 共享内存数据库 + +状态存储在由 `chainbase` 管理的内存映射文件(`shared_memory.bin`)中。关键属性: + +- 所有对象索引(账户、区块、内容、验证者、…)都存储在该文件中。 +- 当可用空间低于阈值时,文件会增量扩展。 +- 正常关闭后文件保持一致;崩溃可能需要从区块日志重放。 +- 节点可以在区块边界导出共享内存状态的**快照**——参见[快照](../storage/snapshots.md)。 + +--- + +## 源码映射 + +| 文件 | 架构中的角色 | +|------|------------| +| `programs/vizd/main.cpp` | 插件注册和启动 | +| `libraries/chain/include/graphene/chain/database.hpp` | 核心数据库接口和信号 | +| `libraries/chain/include/graphene/chain/evaluator_registry.hpp` | 操作 evaluator 工厂 | +| `libraries/network/include/graphene/network/node.hpp` | P2P 节点委托接口 | +| `libraries/protocol/include/graphene/protocol/operations.hpp` | 操作类型联合 | +| `plugins/chain/plugin.cpp` | 链插件:区块/交易接收 | +| `plugins/json_rpc/plugin.cpp` | JSON-RPC 调度 | diff --git a/@l10n/zh-CN/docs/introduction/key-concepts.md b/@l10n/zh-CN/docs/introduction/key-concepts.md new file mode 100644 index 0000000000..cf556ca25a --- /dev/null +++ b/@l10n/zh-CN/docs/introduction/key-concepts.md @@ -0,0 +1,161 @@ +# 核心概念 + +本页介绍在作为开发者、节点运营者或应用构建者使用 VIZ Ledger 之前需要了解的基本概念。 + +--- + +## 账户 + +VIZ Ledger 上的每个参与者都是一个**账户**。账户持有余额、创建内容、为验证者投票,并与所有协议功能交互。 + +### 账户命名规则 + +- 总长度:3–16 个字符 +- 点号分隔的标签:每个标签 ≥ 3 个字符 +- 每个标签以字母开头,以字母或数字结尾 +- 仅允许小写字母(`a-z`)、数字(`0-9`)、连字符(`-`) +- 有效名称示例:`alice`、`alice.bob`、`viz-user1` + +### 权限级别 + +每个账户有三个权限级别,每个级别持有一组密钥或账户代理: + +| 级别 | 用于 | 使用的密钥 | +|------|------|----------| +| `master` | 更换密钥、账户恢复、高安全性操作 | 主密钥(离线保存) | +| `active` | 代币转账、质押、验证者投票 | 活跃密钥 | +| `regular` | 内容、奖励、委员会投票、社交操作 | 常规密钥 | + +权限是多重签名结构:`{ weight_threshold, account_auths[], key_auths[] }`。当提供的签名权重之和达到或超过 `weight_threshold` 时,交易被授权。 + +--- + +## 代币 + +### VIZ — 流动代币 + +- 3 位小数:`"10.000 VIZ"` +- 用于转账、手续费和资金操作 +- 可通过 `transfer_to_vesting_operation` 转换为 SHARES + +### SHARES — 质押代币 + +- 6 位小数:`"10.000000 SHARES"` +- 代表投票权重和能量容量 +- 通过质押 VIZ 创建;经 28 个间隔(≈28 天)提取回 VIZ +- 不可直接转让;可委托给其他账户 + +--- + +## 能量系统 + +能量是控制社交行为(奖励、投票)影响力的资源,与账户的 SHARES 份额相关。 + +| 属性 | 值 | +|------|----| +| 单位 | 基点:`0` = 0%,`10000` = 100% | +| 恢复速率 | 24 小时(86400 秒)完全恢复 | +| 恢复公式 | `current_energy = min(10000, last_energy + elapsed_sec * 10000 / 86400)` | + +当账户以 `energy = 500`(5%)进行奖励时,该比例的账户 SHARES 用于确定奖励池分配。消耗能量不会"销毁"代币——它决定在奖励池中的权重。 + +--- + +## 验证者(区块生产者) + +**验证者**(曾称为"见证人")是生产区块并维护网络的账户。 + +- 任何账户都可以通过 `validator_update_operation` 注册为验证者。 +- 代币持有者使用 SHARES 权重为验证者投票。 +- 按投票权重排名靠前的验证者以轮询方式获得区块生产计划。 +- 每个区块槽位恰好为 3 秒。 +- 21 个验证者的一轮 = 21 个区块 = 63 秒。 + +### Fair-DPOS 参与机制 + +与标准 DPOS 不同,VIZ Ledger 对不活跃行为进行惩罚: +- 每个验证者都有基于近期区块生产的**参与度评分**。 +- 如果全网参与度降至 `required-participation`(默认 33%)以下,区块生产将暂停。 +- 错过过多区块的验证者会受到投票惩罚,惩罚持续 `witness_miss_penalty_duration` 秒。 + +--- + +## 区块和交易 + +### 区块 + +由验证者在其计划槽位生产的已签名交易集合。包含: +- `previous`:前一区块的哈希(链接) +- `timestamp`:确切的槽位时间 +- `witness`:生产验证者的名称 +- `transactions[]`:已签名交易列表 +- `witness_signature`:验证者的签名 + +### 交易 + +一个或多个经过分组和签名的操作。属性: +- `ref_block_num = head_block_number & 0xFFFF` +- `ref_block_prefix` = 引用区块 ID 的第 4–7 字节(小端 uint32) +- `expiration`:必须在当前时间 60 秒内(推荐) +- `operations[]`:1 个或多个操作 +- `signatures[]`:满足所有所需权限的 ECDSA 签名 + +### 操作 + +状态变更的原子单位。在交易内序列化为 `[type_id, operation_object]`。共有 64+ 种操作类型,涵盖转账、社交行为、治理等——参见[操作概述](../protocol/operations/overview.md)。 + +--- + +## 奖励池 + +通货膨胀持续添加到奖励池中。验证者和内容创作者从该池中获取奖励: + +| 接收方 | 来源 | +|--------|------| +| 验证者 | 区块奖励的 `inflation_witness_percent` | +| 委员会 | `inflation_ratio_committee_vs_reward_fund` 比例 | +| 奖励基金 | 余额——通过奖励和内容投票分配 | + +确切百分比由验证者通过 `versioned_chain_properties_update_operation` 以共识方式设定,由顶级验证者投票决定。 + +--- + +## Fork 与 LIB + +**Fork 数据库**(`fork_db`):最近接收的区块的内存树,这些区块可能尚未成为规范链的一部分。节点追踪所有候选 fork,始终延伸最重的(最多批准的)fork。 + +**LIB(Last Irreversible Block,最后不可逆区块)**:已被超过 2/3 验证者确认的最新区块。LIB 及其以下的区块永远不会被重组。一旦区块低于 LIB,它就会被写入永久区块日志。 + +--- + +## 快照 + +快照是特定区块编号时整个数据库状态的二进制转储。它允许新节点: +1. 下载快照文件 +2. 在几秒内加载(而非重放整个区块历史) +3. 从快照区块高度恢复同步 + +快照由 `snapshot` 插件创建,对规范链没有影响——它们纯粹是运维工具。 + +--- + +## 链属性(治理参数) + +链上共识参数由验证者通过 `versioned_chain_properties_update_operation` 控制。活跃参数是顶级 21 个验证者提交值的**中位数**。 + +关键参数包括: +- `account_creation_fee` — 创建新账户的费用 +- `maximum_block_size` — 每个区块的最大字节数 +- `inflation_witness_percent` — 验证者的区块奖励份额 +- `witness_miss_penalty_percent` / `witness_miss_penalty_duration` — 错过惩罚 +- `withdraw_intervals` — 质押提取间隔数 + +参见[链属性治理](../governance/chain-properties.md)查看完整参数列表。 + +--- + +## 硬分叉 + +协议升级以**硬分叉**的形式部署——在特定区块号处的计划激活。一旦 ≥17/21 个验证者表示支持某个硬分叉,它将在下一个计划区块处激活。硬分叉可以添加新的操作类型、更改共识规则或引入新的链属性。 + +参见[硬分叉](../consensus/hardforks.md)了解历史和升级流程。 diff --git a/@l10n/zh-CN/docs/introduction/what-is-viz.md b/@l10n/zh-CN/docs/introduction/what-is-viz.md new file mode 100644 index 0000000000..e738c10470 --- /dev/null +++ b/@l10n/zh-CN/docs/introduction/what-is-viz.md @@ -0,0 +1,121 @@ +# 什么是 VIZ Ledger? + +VIZ Ledger 是一种基于 **Fair-DPOS**(公平委托权益证明)共识算法构建的分布式账本技术(DLT)。它专为去中心化社交、金融和治理应用而设计,在这些场景中,公平性、透明度和效率至关重要。 + +> *VIZ Ledger 使用基于区块链的共识机制,并辅以快照辅助的状态存储,使其成为一种混合 DLT 系统。* + +--- + +## VIZ Ledger 与传统区块链的对比 + +传统区块链要求每个全节点从创世块开始存储所有交易的完整历史。VIZ Ledger 采用了不同的方法: + +| 属性 | 传统区块链 | VIZ Ledger | +|------|-----------|------------| +| 状态存储 | 每个节点存储完整历史 | 近期区块 + 定期快照 | +| 同步方式 | 从创世块重放所有区块 | 加载快照,重放近期区块 | +| 存储需求 | 无限增长 | 受快照间隔限制 | +| 安全模型 | 完整链验证 | 快照 + 共识验证 | +| 共识 | 各种 | Fair-DPOS | + +这种架构更接近业界广义所称的 **DLT**——类似于 Hedera Hashgraph 或 Corda——而非每个节点都保存完整账本历史的经典区块链。 + +### 为何称为"VIZ Ledger"? + +命名方式与 [XRP Ledger](https://xrpl.org) 相同: +- 对底层存储机制保持中立。 +- 准确反映核心功能:维护账户、交易和状态的分布式账本。 +- 为架构演进预留空间,无需重命名。 + +在技术文档中,完整描述为:*"VIZ Ledger 是一个采用快照辅助状态存储的 Fair-DPOS 分布式账本。"* + +--- + +## 核心特性 + +### Fair-DPOS 共识 + +VIZ Ledger 使用 **公平委托权益证明**,是标准 DPOS 的进化版本: + +- 代币持有者使用已质押的 SHARES 为**验证者**(区块生产者)投票。 +- 得票权重最高的验证者按轮询顺序安排生产区块。 +- **公平性保障**:错过区块的验证者其参与度评分会降低。若全网参与度低于所需阈值,区块生产将暂停。 +- 对非活跃验证者没有无限制奖励——生产需要实际参与。 + +### 快照辅助状态存储 + +- 节点将当前状态(账户、余额、内容、投票)存储在共享内存中。 +- 定期快照在特定区块高度捕获完整状态。 +- 新节点可以通过加载最近的快照并仅重放快照后的区块来快速同步,而无需重放整个链历史。 +- 区块日志(二进制格式)为需要历史访问的全节点存储所有区块。 + +### 社交与治理原语 + +VIZ Ledger 将社交和治理功能直接嵌入协议层——而非应用层: + +- **能量系统**:账户拥有能量池(0–100%),每 24 小时恢复一次。能量用于执行社交行为(奖励、投票),消耗量与账户质押量的影响成比例。 +- **奖励**:任何账户都可以使用能量奖励其他账户,从奖励池中分配代币。 +- **Committee DAO**:链上委员会工作请求、资金提案和投票。 +- **邀请码**:链上邀请机制,用于引导新账户的创建。 +- **付费订阅**:账户之间的链上订阅合约。 + +--- + +## 架构概览 + +``` +┌─────────────────────────────────────────────────────────────┐ +│ vizd 进程 │ +│ │ +│ ┌──────────┐ ┌──────────┐ ┌────────────┐ ┌─────────┐ │ +│ │ chain │ │validator │ │database_api│ │ p2p │ │ +│ │ plugin │ │ plugin │ │ plugin │ │ plugin │ │ +│ └────┬─────┘ └────┬─────┘ └─────┬──────┘ └────┬────┘ │ +│ │ │ │ │ │ +│ ┌────▼──────────────▼──────────────▼───────────────▼────┐ │ +│ │ libraries/chain (数据库) │ │ +│ └────────────────────────────────────────────────────────┘ │ +│ ┌────────────────────┐ ┌──────────────────────────────┐ │ +│ │ libraries/network │ │ libraries/protocol │ │ +│ └────────────────────┘ └──────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ + │ │ + 对等节点 钱包 / 应用程序 + (P2P 端口 2001) (HTTP/WS 端口 8090/8091) +``` + +关键组件: + +| 组件 | 作用 | +|------|------| +| `chain plugin` | 打开数据库,协调区块和交易处理 | +| `validator plugin` | 按 Fair-DPOS 规则按计划生产区块 | +| `database_api plugin` | 为钱包和应用提供 JSON-RPC 只读查询 | +| `p2p plugin` | 管理对等连接、区块和交易传播 | +| `webserver plugin` | JSON-RPC 的 HTTP 和 WebSocket 传输层 | +| `snapshot plugin` | 创建和加载状态快照 | + +--- + +## 代币系统 + +VIZ Ledger 有两种原生代币: + +| 代币 | 用途 | 小数位数 | +|------|------|---------| +| `VIZ` | 用于转账和手续费的流动代币 | 3 位(`10.000 VIZ`) | +| `SHARES` | 代表投票权重和能量容量的质押代币 | 6 位(`10.000000 SHARES`) | + +VIZ 可通过 `transfer_to_vesting_operation` 转换为 SHARES。SHARES 可在 28 个提款间隔内提取回 VIZ。 + +--- + +## 本文档适合哪些读者? + +| 读者类型 | 从这里开始 | +|---------|-----------| +| 节点运营者 | [快速开始](../node/getting-started.md) | +| 验证者运营者 | [运行验证者节点](../node/validator-node.md) | +| 应用开发者 | [JSON-RPC API](../api/json-rpc.md) | +| 钱包 / 库开发者 | [数据类型](../protocol/data-types.md) · [操作](../protocol/operations/overview.md) | +| 协议贡献者 | [架构](./architecture.md) · [共识](../consensus/fair-dpos.md) | diff --git a/@l10n/zh-CN/docs/node/building.md b/@l10n/zh-CN/docs/node/building.md new file mode 100644 index 0000000000..c3fdb30992 --- /dev/null +++ b/@l10n/zh-CN/docs/node/building.md @@ -0,0 +1,190 @@ +# 从源码构建 + +VIZ Ledger 使用基于 CMake 的构建系统,为每个平台提供专用构建脚本。Linux 的两步流程(`install-deps-linux.sh` + `build-linux.sh`)将依赖安装(需要 root 权限)与实际构建(以普通用户运行)分离。 + +--- + +## 要求 + +| 组件 | 版本 | +|------|------| +| CMake | 3.16+ | +| GCC | 4.8+ | +| Clang | 3.3+ | +| Boost | 1.71+(含 `coroutine` 组件) | +| OpenSSL | 任意近期版本 | + +--- + +## Linux(Ubuntu 20.04 / 22.04 / 24.04) + +### 步骤 1 — 克隆仓库 + +```bash +git clone --recursive https://github.com/VIZ-Blockchain/viz-cpp-node +cd viz-cpp-node +``` + +### 步骤 2 — 安装依赖(以 root 身份) + +```bash +chmod +x install-deps-linux.sh +sudo ./install-deps-linux.sh +``` + +安装内容:cmake、gcc/g++、git、boost(包括 coroutine/context 在内的所有组件)、openssl、readline、ccache 及压缩库。 + +### 步骤 3 — 构建(以普通用户身份) + +```bash +chmod +x build-linux.sh +./build-linux.sh +``` + +输出二进制文件:`build/programs/vizd/vizd` + +### 常用构建标志 + +```bash +# 低内存节点(验证者/种子节点 — 排除历史索引) +./build-linux.sh -l + +# 测试网构建 +./build-linux.sh -n + +# Debug 构建 +./build-linux.sh -t Debug + +# 并行任务数 +./build-linux.sh -j 8 + +# 跳过依赖安装(已安装) +./build-linux.sh --skip-deps + +# 自定义 Boost / OpenSSL 路径 +./build-linux.sh --boost-root /opt/boost_1_74_0 --openssl-root /opt/openssl +``` + +--- + +## macOS + +```bash +chmod +x build-mac.sh +./build-mac.sh +``` + +脚本自动安装 Xcode Command Line Tools(如需要)和 Homebrew 依赖,然后配置并构建。 + +```bash +# 指定 Boost 路径 +./build-mac.sh --boost-root /opt/homebrew/opt/boost + +# 跳过依赖安装 +./build-mac.sh --skip-deps +``` + +--- + +## Windows(MinGW) + +```cmd +set BOOST_ROOT=C:\Boost +set OPENSSL_ROOT_DIR=C:\OpenSSL-Win64 +build-mingw.bat +``` + +可选环境变量: + +| 变量 | 默认值 | 描述 | +|------|-------|------| +| `VIZ_BUILD_TYPE` | `Release` | `Release` 或 `Debug` | +| `VIZ_LOW_MEMORY` | `OFF` | `ON` 构建低内存节点 | +| `VIZ_BUILD_TESTNET` | `OFF` | `ON` 用于测试网构建 | +| `VIZ_FULL_STATIC` | `OFF` | `ON` 构建完全静态二进制文件 | + +--- + +## Windows(MSVC) + +需要 Visual Studio 2019+ 及"使用 C++ 的桌面开发"工作负载: + +```cmd +set BOOST_ROOT=C:\Boost +set OPENSSL_ROOT_DIR=C:\OpenSSL-Win64 +build-msvc.bat +``` + +--- + +## CMake 选项 + +直接使用 CMake 的高级配置: + +| 选项 | 默认值 | 描述 | +|------|-------|------| +| `BUILD_TESTNET` | `OFF` | 启用测试网专用代码 | +| `LOW_MEMORY_NODE` | `OFF` | 排除历史/索引插件 | +| `CHAINBASE_CHECK_LOCKING` | `OFF` | 启用锁断言检查(debug) | +| `ENABLE_MONGO_PLUGIN` | `OFF` | 构建 MongoDB 插件 | +| `BUILD_SHARED_LIBRARIES` | `OFF` | 构建共享库 | +| `USE_PCH` | `OFF` | 启用预编译头文件(加快重新构建) | + +示例: + +```bash +mkdir build && cd build +cmake -DCMAKE_BUILD_TYPE=Release \ + -DLOW_MEMORY_NODE=ON \ + -DCMAKE_INSTALL_PREFIX=/usr/local \ + .. +make -j$(nproc) +``` + +或使用辅助脚本: + +```bash +python3 programs/build_helpers/configure_build.py --release +``` + +--- + +## 构建目标 + +| 目标 | 二进制文件 | 描述 | +|------|-----------|------| +| `vizd` | `programs/vizd/vizd` | 主节点守护进程 | +| `cli_wallet` | `programs/cli_wallet/cli_wallet` | 命令行钱包 | + +--- + +## Docker 构建 + +仓库提供四个 Dockerfile: + +| 文件 | 用途 | +|------|------| +| `Dockerfile-production` | 完整主网节点(Release) | +| `Dockerfile-lowmem` | 低内存节点(`LOW_MEMORY_NODE=ON`) | +| `Dockerfile-mongo` | 含 MongoDB 插件的节点 | +| `Dockerfile-testnet` | 测试网节点(`BUILD_TESTNET=ON`) | + +构建示例: + +```bash +docker build -f share/vizd/docker/Dockerfile-production -t vizd:local . +``` + +完整生产 Docker 设置参见 [Docker](./docker.md)。 + +--- + +## 故障排除 + +| 问题 | 解决方案 | +|------|---------| +| `boost/coroutine.hpp` 未找到 | 安装 `libboost-coroutine-dev`(Ubuntu)或 Boost 1.71+ | +| CMake < 3.16 | 从 `cmake.org` 或 Kitware PPA 安装新版 CMake | +| `do not run as root` 错误 | 以普通用户(非 `sudo`)运行 `build-linux.sh` | +| macOS 链接错误(OpenSSL) | `export OPENSSL_ROOT_DIR=$(brew --prefix openssl)` | +| 编译时内存不足 | 使用 `-j 2` 减少并行任务数 | diff --git a/@l10n/zh-CN/docs/node/configuration.md b/@l10n/zh-CN/docs/node/configuration.md new file mode 100644 index 0000000000..64c4c2921b --- /dev/null +++ b/@l10n/zh-CN/docs/node/configuration.md @@ -0,0 +1,223 @@ +# 节点配置 + +VIZ Ledger 节点通过 INI 文件进行配置。仓库在 `share/vizd/config/` 中提供了几个模板: + +| 模板 | 使用场景 | +|------|---------| +| `config.ini` | 带公共 RPC 的完整主网节点 | +| `config_witness.ini` | 验证者节点(本地 RPC,区块生产) | +| `config_testnet.ini` | 测试网/开发环境 | +| `config_mongo.ini` | 带 MongoDB 历史后端的节点 | +| `config_lowmem.ini` | 低内存共识/种子节点 | +| `config_stock_exchange.ini` | 市场数据消费者(最少插件) | +| `config_debug.ini` | 调试模式 | + +--- + +## 网络和 P2P + +```ini +# P2P 连接监听地址(标准端口 2001) +p2p-endpoint = 0.0.0.0:2001 + +# 最大节点连接数(未设置则不限制) +p2p-max-connections = 200 + +# 引导连接的种子节点(可重复) +p2p-seed-node = seed1.viz.media:2001 +p2p-seed-node = seed2.viz.media:2001 + +# 检查点:受信任的 (block_num, block_id) 对(可重复) +# checkpoint = [12345,"0003039..." ] +``` + +--- + +## Web 服务器和 RPC + +```ini +# HTTP JSON-RPC 端点 +webserver-http-endpoint = 0.0.0.0:8090 + +# WebSocket JSON-RPC 端点 +webserver-ws-endpoint = 0.0.0.0:8091 + +# RPC 线程池大小 +webserver-thread-pool-size = 2 +``` + +> **安全说明:** 对于验证者节点,绑定到 `127.0.0.1` 以防止外部访问: +> ```ini +> webserver-http-endpoint = 127.0.0.1:8090 +> webserver-ws-endpoint = 127.0.0.1:8091 +> ``` + +--- + +## 锁定和并发 + +```ini +# 重试前等待读锁的微秒数 +read-wait-micro = 500000 + +# 最大读锁重试次数 +max-read-wait-retries = 2 + +# 重试前等待写锁的微秒数 +write-wait-micro = 500000 + +# 最大写锁重试次数 +max-write-wait-retries = 3 + +# 在单线程上序列化所有写操作(推荐) +single-write-thread = true + +# 在 push_transaction 上运行插件通知(增加延迟;默认 false) +enable-plugins-on-push-transaction = false +``` + +--- + +## 共享内存(数据库) + +区块链状态存储在内存映射文件(`shared_memory.bin`)中。 + +```ini +# 共享内存文件的初始大小 +shared-file-size = 4G + +# 触发调整大小前的最小可用空间 +min-free-shared-file-size = 500M + +# 调整大小时文件的增长量 +inc-shared-file-size = 2G + +# 每 N 个区块检查一次可用空间 +block-num-check-free-size = 1000 +``` + +根据链的大小调整 `shared-file-size`。主网建议从 `4G` 开始并监控增长情况。 + +--- + +## 插件激活 + +```ini +# 每个 'plugin' 行添加一个插件(可重复) +# 完整 API 节点的最小集合: +plugin = chain p2p webserver json_rpc database_api network_broadcast_api + +# 额外的索引插件(低内存节点上注释掉): +plugin = social_network tags follow account_history account_by_key +plugin = committee_api invite_api paid_subscription_api custom_protocol_api + +# 仅用于验证者节点: +plugin = validator witness_api +``` + +### 按节点类型划分的插件集 + +| 节点类型 | 插件 | +|---------|------| +| 全节点 | 以上所有 | +| 验证者 | `chain p2p webserver json_rpc database_api network_broadcast_api validator witness_api` | +| 低内存种子 | `chain p2p` | +| 交易所 | `chain p2p webserver json_rpc database_api network_broadcast_api account_history` | + +--- + +## 历史记录和追踪 + +```ini +# 丢弃此区块之前的投票对象(节省内存,0 = 保留所有) +clear-votes-before-block = 0 + +# 跳过虚拟操作的索引(在验证者上节省内存) +skip-virtual-ops = false + +# 仅追踪范围内账户的历史记录(可选) +# track-account-range = ["alice","alice.zzz"] + +# 历史记录的操作类型白名单/黑名单 +# history-whitelist-ops = transfer_operation +# history-blacklist-ops = custom_operation + +# 从此区块号开始索引历史记录 +# history-start-block = 1000000 + +# 每个账户的最大动态信息条目数(follow 插件) +follow-max-feed-size = 500 + +# 私信追踪范围(可选) +# pm-account-range = ["alice","alice.zzz"] +``` + +--- + +## 验证者(区块生产) + +非验证者节点请不要设置这些参数。 + +```ini +# 允许在链过期时生产(仅用于开发/测试网) +enable-stale-production = false + +# 生产区块所需的最低参与度 % (0–99) +required-participation = 33 + +# 验证者账户名(可重复,用于一个节点上的多个验证者) +validator = alice + +# 验证者的 WIF 签名密钥 +private-key = 5JxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxWIF + +# 紧急共识私钥(可选) +# emergency-private-key = 5Jxxx... +``` + +完整的验证者配置参见[验证者节点](./validator-node.md)。 + +--- + +## 日志 + +```ini +# 控制台日志记录器(输出到 stderr) +log.console_appender.stderr.stream = std_error + +# P2P 消息的文件日志记录器 +log.file_appender.p2p.filename = logs/p2p/p2p.log + +# 日志级别:all, debug, info, warn, error, off +logger.default.level = warn +logger.default.appenders = stderr + +logger.p2p.level = warn +logger.p2p.appenders = p2p +``` + +--- + +## MongoDB(可选) + +仅当节点使用 `ENABLE_MONGO_PLUGIN=ON` 构建时有效: + +```ini +plugin = mongo_db +mongodb-uri = mongodb://localhost:27017/vizd +``` + +--- + +## 完整参考 + +按源文件列出的所有选项: + +| 源文件 | 涵盖的选项 | +|--------|----------| +| `plugins/chain/plugin.hpp` | `shared-file-size`, `min-free-shared-file-size`, `inc-shared-file-size`, `block-num-check-free-size`, `single-write-thread`, `enable-plugins-on-push-transaction`, `read-wait-micro`, `max-read-wait-retries`, `write-wait-micro`, `max-write-wait-retries`, `skip-virtual-ops`, `clear-votes-before-block`, `track-account-range`, `history-whitelist-ops`, `history-blacklist-ops`, `history-start-block` | +| `plugins/p2p/p2p_plugin.hpp` | `p2p-endpoint`, `p2p-max-connections`, `p2p-seed-node`, `checkpoint` | +| `plugins/webserver/webserver_plugin.hpp` | `webserver-http-endpoint`, `webserver-ws-endpoint`, `webserver-thread-pool-size` | +| `plugins/validator/validator.hpp` | `enable-stale-production`, `required-participation`, `validator`, `private-key`, `emergency-private-key`, `fork-collision-timeout-blocks`, `ntp-server`, `ntp-request-interval`, `debug-block-production` | +| `plugins/follow/` | `follow-max-feed-size` | +| `plugins/private_message/` | `pm-account-range` | diff --git a/@l10n/zh-CN/docs/node/docker.md b/@l10n/zh-CN/docs/node/docker.md new file mode 100644 index 0000000000..a892430366 --- /dev/null +++ b/@l10n/zh-CN/docs/node/docker.md @@ -0,0 +1,183 @@ +# Docker 部署 + +VIZ Ledger 提供四种 Docker 镜像,适用于不同的部署场景。所有镜像均采用两阶段构建:builder 阶段编译二进制文件;runtime 阶段仅包含二进制文件和配置。 + +--- + +## 可用镜像 + +| Dockerfile | 标签 | 描述 | +|-----------|-----|------| +| `Dockerfile-production` | `latest` | 完整主网节点(Release,全部插件) | +| `Dockerfile-lowmem` | `lowmem` | 低内存节点(`LOW_MEMORY_NODE=ON`,无历史索引) | +| `Dockerfile-mongo` | `mongo` | 含 MongoDB 历史插件的完整节点 | +| `Dockerfile-testnet` | `testnet` | 测试网节点(`BUILD_TESTNET=ON`) | + +--- + +## 快速开始 + +```bash +docker run -d \ + --name vizd \ + --restart unless-stopped \ + -p 2001:2001 \ + -p 8090:8090 \ + -p 8091:8091 \ + -v /data/vizd:/var/lib/vizd \ + vizblockchain/vizd:latest +``` + +查看日志: + +```bash +docker logs -f vizd +``` + +--- + +## 卷 + +| 容器路径 | 用途 | +|---------|------| +| `/var/lib/vizd` | 区块链数据、共享内存、block log | +| `/etc/vizd` | 配置文件和种子节点列表 | + +始终挂载 `/var/lib/vizd` 以在容器重启后保留状态。 + +使用自定义配置: + +```bash +docker run -d \ + -v /data/vizd:/var/lib/vizd \ + -v /my/config.ini:/etc/vizd/config.ini \ + vizblockchain/vizd:latest +``` + +--- + +## 环境变量 + +入口脚本(`vizd.sh`)读取以下环境变量: + +| 变量 | 描述 | 示例 | +|------|------|------| +| `VIZD_SEED_NODES` | 空格分隔的种子节点列表(覆盖 `/etc/vizd/seednodes`) | `seed1.viz.media:2001 seed2.viz.media:2001` | +| `VIZD_RPC_ENDPOINT` | 覆盖 HTTP RPC 端点 | `0.0.0.0:8090` | +| `VIZD_P2P_ENDPOINT` | 覆盖 P2P 端点 | `0.0.0.0:2001` | +| `VIZD_WITNESS` | 验证者账户名(启用区块生产) | `alice` | +| `VIZD_PRIVATE_KEY` | WIF 格式的验证者签名密钥 | `5J...` | + +--- + +## 端口 + +| 端口 | 协议 | 用途 | +|------|------|------| +| 2001 | TCP | P2P 节点连接 | +| 8090 | TCP | HTTP JSON-RPC | +| 8091 | TCP | WebSocket JSON-RPC | + +--- + +## 验证者节点(Docker) + +```bash +docker run -d \ + --name vizd-validator \ + --restart unless-stopped \ + -p 2001:2001 \ + -v /data/vizd:/var/lib/vizd \ + -e VIZD_WITNESS=myvalidator \ + -e VIZD_PRIVATE_KEY=5Jxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx \ + vizblockchain/vizd:latest +``` + +对于验证者节点,**不要**公开暴露端口 8090/8091 — 仅绑定到 localhost: + +```bash +-e VIZD_RPC_ENDPOINT=127.0.0.1:8090 +``` + +--- + +## 测试网节点 + +```bash +docker run -d \ + --name vizd-testnet \ + -p 2001:2001 \ + -p 8090:8090 \ + -v /data/vizd-testnet:/var/lib/vizd \ + vizblockchain/vizd:testnet +``` + +--- + +## 本地构建镜像 + +```bash +# Production +docker build \ + -f share/vizd/docker/Dockerfile-production \ + -t vizd:local \ + . + +# Low-memory +docker build \ + -f share/vizd/docker/Dockerfile-lowmem \ + -t vizd:lowmem \ + . +``` + +### 各镜像的 CMake 标志 + +| 镜像 | `LOW_MEMORY_NODE` | `ENABLE_MONGO_PLUGIN` | `BUILD_TESTNET` | +|------|:-----------------:|:---------------------:|:---------------:| +| production | OFF | OFF | OFF | +| lowmem | ON | OFF | OFF | +| mongo | OFF | ON | OFF | +| testnet | OFF | OFF | ON | + +--- + +## CI/CD(GitHub Actions) + +仓库提供 `.github/workflows/docker-main.yml`,每次推送到 `master` 时自动构建并推送标记为 `latest` 的 production 镜像。 + +```yaml +- name: Build and push + uses: docker/build-push-action@v2 + with: + file: share/vizd/docker/Dockerfile-production + tags: vizblockchain/vizd:latest + push: true +``` + +--- + +## 资源规划 + +| 节点类型 | 内存 | 磁盘 | +|---------|------|------| +| 完整节点(主网) | 8 GB+ | 50 GB+ | +| 低内存 / 验证者 | 4 GB | 20 GB | +| 测试网 | 4 GB | 10 GB | + +共享内存大小应能舒适地放入 RAM。在 `config.ini` 中: + +```ini +shared-file-size = 4G +``` + +--- + +## 故障排除 + +| 症状 | 原因 | 解决方案 | +|------|------|---------| +| 容器立即退出 | 配置错误或缺少卷 | `docker logs vizd` — 检查启动错误 | +| 端口 8090 不可达 | RPC 绑定到 localhost | 删除 `127.0.0.1:` 前缀或使用反向代理 | +| 无对等节点 | 防火墙阻止端口 2001 | 开放 2001 TCP 入站 | +| 同步缓慢 | 未加载快照 | 首次启动前在卷中提供快照 | +| `/var/lib/vizd` 权限拒绝 | 卷所有权不匹配 | `chown -R 1000:1000 /data/vizd` | diff --git a/@l10n/zh-CN/docs/node/getting-started.md b/@l10n/zh-CN/docs/node/getting-started.md new file mode 100644 index 0000000000..6d06892270 --- /dev/null +++ b/@l10n/zh-CN/docs/node/getting-started.md @@ -0,0 +1,184 @@ +# 快速开始 + +本指南涵盖运行 VIZ Ledger 节点所需的一切——从安装依赖项到初始同步。 + +--- + +## 前置要求 + +| 要求 | 最低配置 | 推荐配置 | +|------|---------|---------| +| 操作系统 | Ubuntu 20.04 LTS | Ubuntu 24.04 LTS | +| 内存 | 4 GB | 8 GB+ | +| 磁盘 | 20 GB | 50 GB+ SSD | +| CPU | 2 核 | 4+ 核 | +| 网络 | 公网 IP,开放端口 2001 | 稳定连接 | + +**使用的端口:** + +| 端口 | 协议 | 用途 | +|------|------|------| +| 2001 | TCP | P2P 节点连接 | +| 8090 | TCP | HTTP JSON-RPC | +| 8091 | TCP | WebSocket JSON-RPC | + +--- + +## 方案 A:Docker(推荐快速启动) + +### 1. 拉取生产镜像 + +```bash +docker pull vizblockchain/vizd:latest +``` + +### 2. 运行节点 + +```bash +docker run -d \ + --name vizd \ + -p 2001:2001 \ + -p 8090:8090 \ + -p 8091:8091 \ + -v /data/vizd:/var/lib/vizd \ + vizblockchain/vizd:latest +``` + +### 3. 查看日志 + +```bash +docker logs -f vizd +``` + +几分钟内应该看到节点连接和区块同步进度。 + +### 环境变量(Docker) + +| 变量 | 用途 | 示例 | +|------|------|------| +| `VIZD_SEED_NODES` | 覆盖默认种子节点 | `node1.viz.media:2001` | +| `VIZD_WITNESS` | 验证者名称(验证者节点时使用) | `alice` | +| `VIZD_PRIVATE_KEY` | 验证者签名密钥(WIF 格式) | `5J...` | + +--- + +## 方案 B:从源码构建 + +### 1. 安装依赖(Linux) + +```bash +git clone --recursive https://github.com/VIZ-Blockchain/viz-cpp-node +cd viz-cpp-node +chmod +x install-deps-linux.sh +sudo ./install-deps-linux.sh +``` + +### 2. 构建 + +```bash +chmod +x build-linux.sh +./build-linux.sh +``` + +低内存构建(验证者和种子节点——不含索引插件): + +```bash +./build-linux.sh -l +``` + +二进制文件位于 `build/programs/vizd/vizd`。 + +### 3. macOS + +```bash +chmod +x build-mac.sh +./build-mac.sh +``` + +### 4. Windows(MinGW) + +```cmd +set BOOST_ROOT=C:\Boost +set OPENSSL_ROOT_DIR=C:\OpenSSL-Win64 +build-mingw.bat +``` + +详细的平台说明和 CMake 选项参见[构建](./building.md)。 + +--- + +## 初始配置 + +复制主网配置模板: + +```bash +cp share/vizd/config/config.ini /data/vizd/config.ini +``` + +公共节点的最小修改: + +```ini +# P2P +p2p-endpoint = 0.0.0.0:2001 +p2p-seed-node = seed1.viz.media:2001 +p2p-seed-node = seed2.viz.media:2001 + +# RPC +webserver-http-endpoint = 0.0.0.0:8090 +webserver-ws-endpoint = 0.0.0.0:8091 + +# 共享内存——根据可用磁盘调整 +shared-file-size = 4G + +# 插件(全节点) +plugin = chain p2p webserver json_rpc database_api network_broadcast_api +plugin = social_network tags follow account_history +``` + +验证者节点配置参见[验证者节点](./validator-node.md)。 + +--- + +## 启动节点 + +```bash +./vizd --config-file /data/vizd/config.ini --data-dir /data/vizd +``` + +使用 Docker 时,将数据目录作为卷挂载(参见方案 A)。 + +--- + +## 验证同步 + +通过 HTTP RPC 查询节点: + +```bash +curl -s -X POST http://localhost:8090 \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"call","params":["database_api","get_dynamic_global_properties",[]],"id":1}' \ + | python3 -m json.tool +``` + +检查 `head_block_number`——同步后应每 3 秒增加一次。 + +--- + +## 节点类型 + +| 类型 | 配置模板 | 描述 | +|------|---------|------| +| 全节点 | `config.ini` | 所有插件,公共 RPC 端点 | +| 验证者 | `config_witness.ini` | 区块生产,RPC 仅限本地 | +| 测试网 | `config_testnet.ini` | 开发和测试 | +| 低内存 | `config.ini` + `LOW_MEMORY_NODE` 构建标志 | 仅共识,无历史索引 | +| MongoDB | `config_mongo.ini` | MongoDB 中的完整历史 | + +--- + +## 后续步骤 + +- [配置参考](./configuration.md) — 所有配置选项说明 +- [Docker 部署](./docker.md) — 生产环境 Docker 设置 +- [验证者节点](./validator-node.md) — 运行区块生产验证者 +- [快照](./snapshot.md) — 使用状态快照快速同步 diff --git a/@l10n/zh-CN/docs/node/monitoring.md b/@l10n/zh-CN/docs/node/monitoring.md new file mode 100644 index 0000000000..a2925e2603 --- /dev/null +++ b/@l10n/zh-CN/docs/node/monitoring.md @@ -0,0 +1,313 @@ +# 监控 + +本页介绍 VIZ Ledger 节点的健康检查、日志模式、P2P 节点统计以及与外部监控系统的集成。 + +--- + +## 健康检查:节点同步 + +查询节点的动态全局属性以验证其正在运行和同步: + +```bash +curl -s -X POST http://localhost:8090 \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"call","params":["database_api","get_dynamic_global_properties",[]],"id":1}' \ + | python3 -m json.tool +``` + +检查 `head_block_number` — 同步时应每 3 秒增加一次。检查 `time` — 应与系统时钟相差几秒以内。 + +简单的存活探测脚本: + +```bash +#!/bin/bash +RESPONSE=$(curl -sf -X POST http://localhost:8090 \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"call","params":["database_api","get_dynamic_global_properties",[]],"id":1}') +if [ $? -ne 0 ]; then echo "CRIT: RPC unreachable"; exit 2; fi + +HEAD=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['result']['head_block_number'])") +echo "OK: head_block_number=$HEAD" +``` + +--- + +## 日志模式 + +### 区块生产(验证者节点) + +``` +# 正常:槽位已生产 +produced block #123456 @2025-01-01T00:00:03 validator=alice sz=2048 + +# 错过槽位 +MISSED-SLOT-OUR-validator: alice missed slot at 2025-01-01T00:00:06 + +# 检测到少数派 fork +MINORITY FORK DETECTED: rolling back to LIB #123400 + +# 看门狗触发 +WATCHDOG: no production for 180s, clearing flags +``` + +### P2P 连接 + +``` +# 新节点已连接 +New peer is connected (203.0.113.10:2001), now 8 active peers + +# 节点软封禁 +soft-banning peer 203.0.113.10:2001 for 300s: reason=only_fork_db_blocks_no_progress + +# 同步完成 +Sync: peer 203.0.113.10 says we're up-to-date +``` + +### 快照和恢复 + +``` +# 快照已创建 +Snapshot created at block 5000000 in 14.2s: /data/snapshots/snapshot-block-5000000.json + +# 自动恢复已触发 +shared_memory_corruption_exception detected — starting auto-recovery +Recovery complete. Resumed from block 4999500 +``` + +### 同步日志(DLT 模式) + +启用 `sync` 日志记录器查看同步协商详情: + +```ini +[logger.sync] +level = info +appenders = stderr +``` + +关键消息: +- `Starting sync with peer ...` — 同步已启动 +- `on_blockchain_item_ids_inventory: ...` — 收到区块 ID 批次 +- `Sync: peer X says we're up-to-date` — 与此节点的同步完成 +- `DEFERRED_RESIZE: sync block #N deferred` — 因共享内存调整大小而延迟的同步区块 +- `auto-clearing stuck peer_needs_sync_items_from_us` — 30 秒安全网清除了卡住的标志 + +--- + +## 日志配置 + +日志在 `config.ini` 中配置: + +```ini +# 控制台输出 +log.console_appender.stderr.stream = std_error + +# P2P 日志文件 +log.file_appender.p2p.filename = logs/p2p/p2p.log + +# 日志级别:all, debug, info, warn, error, off +logger.default.level = warn +logger.default.appenders = stderr + +logger.p2p.level = warn +logger.p2p.appenders = p2p +``` + +> **注意:** `node.cpp` 将所有 `ilog`/`wlog` 调用路由到 `p2p` 日志记录器。要查看 P2P 消息,请将 `p2p` 日志记录器级别配置为 `info`。 + +通过 `logrotate` 进行日志轮换(示例 `/etc/logrotate.d/vizd`): + +``` +/data/vizd/logs/p2p/p2p.log { + daily + rotate 14 + compress + missingok + copytruncate +} +``` + +--- + +## P2P 节点统计 + +P2P 插件每 5 分钟记录一次节点健康指标(可配置)。在 `config.ini` 中启用: + +```ini +p2p-stats-enabled = true +p2p-stats-interval = 300 # 秒 +``` + +示例日志输出: + +``` +P2P peer | ip: 203.0.113.10 | port: 2001 | latency: 45ms | bytes_in: 12345 | blocked: false | reason: +P2P peer | ip: 198.51.100.5 | port: 2001 | latency: 120ms | bytes_in: 8765 | blocked: true | reason: soft_ban +Block storage | dlt_log: [79174319..79274318] | dlt_resizes: 412 | fork_db: linked=18 unlinked=0 +``` + +字段说明: +- `latency` — 往返延迟(毫秒) +- `bytes_in` — 自上次测量以来接收的字节增量 +- `blocked` / `reason` — 软封禁或限制状态及原因 +- `Block storage` — DLT block log 范围、调整大小计数、fork_db 状态 + +高 `dlt_resizes` 计数加上缩小的 `dlt_log` 范围可能表明映射文件自愈已运行。`reason: soft_ban` 的节点可能处于 fork 上或仅发送过时数据。 + +--- + +## Prometheus 和 Grafana + +节点不原生暴露 Prometheus 端点。使用 [Node Exporter](https://github.com/prometheus/node_exporter) 获取操作系统级指标,并使用自定义导出器抓取 JSON-RPC 端点: + +```python +# 最简示例:抓取 head_block_number +import requests, time +from prometheus_client import Gauge, start_http_server + +g = Gauge('viz_head_block_number', 'Current head block') + +def collect(): + r = requests.post('http://localhost:8090', json={ + "jsonrpc": "2.0", "method": "call", + "params": ["database_api", "get_dynamic_global_properties", []], + "id": 1 + }, timeout=5) + g.set(r.json()['result']['head_block_number']) + +start_http_server(9100) +while True: + collect() + time.sleep(3) +``` + +**推荐仪表板面板:** + +| 面板 | 指标 / 来源 | +|------|-----------| +| 头块 | `viz_head_block_number`(同步时每 3 秒增加) | +| 区块延迟 | `time() - viz_head_block_time`(落后系统时钟的秒数) | +| 节点数 | 从 P2P 统计日志解析 | +| 节点延迟 | P2P 统计日志,按节点 IP | +| 共享内存空闲 | 自定义导出器的 `viz_shared_memory_free_mb` | +| CPU / RAM | Node Exporter 标准指标 | +| 磁盘 I/O | Node Exporter `node_disk_*` | + +--- + +## ELK / 集中日志记录 + +将节点日志转发到中央收集器。Filebeat 示例: + +```yaml +# filebeat.yml +filebeat.inputs: + - type: log + paths: + - /data/vizd/logs/p2p/p2p.log + fields: + service: vizd + node: validator-1 + +output.logstash: + hosts: ["logstash:5044"] +``` + +解析关键字段(Logstash grok 或 Elasticsearch 摄取管道): + +``` +MISSED-SLOT-OUR-validator: %{WORD:validator} missed slot at %{TIMESTAMP_ISO8601:slot_time} +produced block #%{NUMBER:block_num} @%{TIMESTAMP_ISO8601:block_time} validator=%{WORD:producer} +``` + +--- + +## 验证者专项监控 + +### 需要告警的关键指标 + +| 条件 | 严重程度 | 操作 | +|------|---------|------| +| 日志中出现 `MISSED-SLOT-OUR-validator` | 警告 | 检查 NTP、网络延迟、CPU 负载 | +| `MINORITY FORK DETECTED` | 严重 | 验证与种子节点的 P2P 连接 | +| `WATCHDOG: no production for 180s` | 严重 | 检查验证者密钥和节点健康状况 | +| 结果代码 `no_private_key` | 严重 | 签名密钥不匹配——检查配置 | +| 结果代码 `low_participation` | 警告 | 网络健康状况下降 | +| 头块停止推进 | 严重 | 节点可能已停滞 | +| 节点数降至 0 | 严重 | 网络分区或防火墙问题 | + +### NTP 检查 + +```bash +chronyc tracking | grep "System time" +# 或 +timedatectl | grep "NTP synchronized" +``` + +验证者插件使用自己的 NTP 客户端(通过 config 中的 `ntp-server` 配置),但操作系统时钟同步也很重要。漂移 >200ms 可能导致错过槽位。 + +--- + +## 数据库维护 + +### 共享内存大小 + +监控日志中的空间不足警告: + +``` +chainbase: shared memory low — resizing from 4G to 6G +``` + +在 `config.ini` 中主动配置增长参数: + +```ini +shared-file-size = 4G +min-free-shared-file-size = 500M +inc-shared-file-size = 2G +block-num-check-free-size = 1000 +``` + +### 快照备份验证 + +创建快照后,在测试节点上验证它能正常加载: + +```bash +vizd --create-snapshot /tmp/verify-snap.json --plugin snapshot +# 预期:正常退出并显示 "Snapshot created at block N" +``` + +定期测试崩溃恢复: + +```bash +vizd --replay-from-snapshot --snapshot-auto-latest --plugin snapshot +# 预期:导入快照,重放 dlt_block_log,输出 "Recovery complete" +``` + +--- + +## 事件响应清单 + +**节点未同步:** +1. 检查节点数(`p2p-stats-enabled` 日志或 RPC `get_info`)。 +2. 验证防火墙允许 TCP 端口 2001 入站。 +3. 检查 `p2p-seed-node` 设置——尝试备用种子节点。 +4. 在 P2P 统计中查找 `soft_ban` 条目——节点可能处于 fork 上。 + +**验证者不生产区块:** +1. 检查 `config.ini` 中的 `validator` 和 `private-key` 是否与链上签名密钥匹配。 +2. 验证 `low_participation` 是否是原因(网络健康状况)。 +3. 检查 NTP 同步。 +4. 查找 `MINORITY FORK DETECTED` — 节点可能需要重新同步。 + +**节点崩溃 / 共享内存损坏:** +1. 如果 `--auto-recover-from-snapshot` 已启用(默认)且快照存在,节点自动恢复——检查日志。 +2. 手动恢复:`vizd --replay-from-snapshot --snapshot-auto-latest --plugin snapshot`。 +3. 如果没有快照:`vizd --replay-blockchain`(需要完整 block log;DLT 模式下不可用)。 + +**RPC 不可达:** +1. 检查 `webserver-http-endpoint` 绑定——验证者默认使用 `127.0.0.1:8090`。 +2. 检查防火墙或反向代理配置。 +3. 验证插件列表包含 `webserver json_rpc database_api`。 + +--- + +参见:[验证者节点](./validator-node.md)、[验证者守护](./validator-guard.md)、[快照](./snapshot.md)、[配置](./configuration.md)。 diff --git a/@l10n/zh-CN/docs/node/snapshot.md b/@l10n/zh-CN/docs/node/snapshot.md new file mode 100644 index 0000000000..dc4030c61f --- /dev/null +++ b/@l10n/zh-CN/docs/node/snapshot.md @@ -0,0 +1,377 @@ +# 快照 + +快照插件通过将完整区块链状态序列化为 JSON 文件,实现节点的近乎即时启动。节点无需从 block log 重放数百万个区块,而是加载快照并仅同步快照创建后生产的区块。 + +从快照加载的节点以 **DLT 模式**运行:主 block log 保持为空,紧凑的 **DLT 滚动 block log**(`dlt_block_log`)保存最近的不可逆区块。 + +--- + +## 快速参考 + +| 目标 | 命令 / 配置 | +|------|-----------| +| 一次性创建快照(停止节点) | `vizd --create-snapshot /path/snap.json --plugin snapshot` | +| 在第 N 个区块创建快照 | `snapshot-at-block = N` + `snapshot-dir = /path` | +| 每 N 个区块创建快照 | `snapshot-every-n-blocks = N` + `snapshot-dir = /path` | +| 从文件引导新节点 | `vizd --snapshot /path/snap.json --plugin snapshot` | +| 崩溃恢复 | `vizd --replay-from-snapshot --snapshot-auto-latest --plugin snapshot` | +| P2P 自动引导 | `sync-snapshot-from-trusted-peer = true` + `trusted-snapshot-peer = host:8092` | + +--- + +## 启用插件 + +```ini +plugin = snapshot +``` + +--- + +## 配置参考 + +### 仅 CLI 标志 + +| 标志 | 描述 | +|------|------| +| `--snapshot ` | 从快照文件引导(DLT 模式)。如果 `shared_memory.bin` 已存在或文件已被导入(重命名为 `.used`),则跳过。 | +| `--snapshot-auto-latest` | 通过解析文件名中的区块号自动发现 `snapshot-dir` 中的最新快照。与 `--replay-from-snapshot` 配合使用。设置了 `--snapshot` 时忽略此项。 | +| `--replay-from-snapshot` | 崩溃恢复:始终清除共享内存,导入快照,重放 `dlt_block_log`。不重命名快照文件。需要 `--snapshot` 或 `--snapshot-auto-latest`。 | +| `--auto-recover-from-snapshot` | (默认:`true`)检测到共享内存损坏时自动恢复运行时——无需重启。通过 `--no-auto-recover-from-snapshot` 禁用。 | +| `--create-snapshot ` | 使用当前数据库在指定路径创建快照,然后退出。 | +| `--sync-snapshot-from-trusted-peer` | (默认:`false`)状态为空时从受信任节点下载快照。必须明确启用。 | + +### 配置文件选项 + +| 选项 | 默认值 | 描述 | +|------|-------|------| +| `snapshot-at-block` | `0` | 到达此区块号时创建快照(0 = 禁用)。 | +| `snapshot-every-n-blocks` | `0` | 每 N 个区块创建一次快照(0 = 禁用)。仅对实时区块触发——P2P 追赶期间跳过。 | +| `snapshot-dir` | — | 自动生成快照的目录。不存在时自动创建。 | +| `snapshot-max-age-days` | `90` | 创建新快照后删除超过 N 天的旧快照(0 = 禁用)。 | +| `allow-snapshot-serving` | `false` | 通过 TCP 向其他节点提供快照服务。 | +| `allow-snapshot-serving-only-trusted` | `false` | 仅向受信任节点提供服务。 | +| `snapshot-serve-endpoint` | `0.0.0.0:8092` | 快照服务器的 TCP 端点。 | +| `trusted-snapshot-peer` | — | P2P 快照同步的受信任节点地址(可重复)。 | +| `dlt-block-log-max-blocks` | `100000` | DLT 模式下滚动 block log 的大小(chain 插件选项)。0 = 禁用。 | + +--- + +## 创建快照 + +### 方法 1:一次性(停止节点,创建文件,退出) + +首先停止运行中的节点,然后: + +```bash +vizd --create-snapshot /data/snapshots/viz-snapshot.json --plugin snapshot +``` + +节点打开现有数据库,必要时进行重放,写入快照,然后在 P2P 或验证者插件激活之前退出。 + +### 方法 2:在特定区块创建快照(无停机) + +```ini +plugin = snapshot +snapshot-at-block = 5000000 +snapshot-dir = /data/snapshots +``` + +应用第 5,000,000 个区块时,快照写入 `/data/snapshots/snapshot-block-5000000.json`,节点不会停止。 + +### 方法 3:周期性快照(推荐用于生产环境) + +```ini +plugin = snapshot +snapshot-every-n-blocks = 28800 # 按 3 秒/区块计约 24 小时 +snapshot-dir = /data/snapshots +snapshot-max-age-days = 90 +``` + +文件自动命名为 `snapshot-block-.json`。快照创建是异步的: + +- **阶段 1**(读锁,约 1 秒):将所有数据库对象序列化到内存。 +- **阶段 2**(无锁,约 2 秒):压缩、校验和、写入磁盘。 + +只有阶段 1 期间暂停区块处理;API 和 P2P 读取全程不受影响。 + +**推荐间隔:** + +| 频率 | 区块数 | 大约时间 | +|------|-------|---------| +| 频繁 | 10,000 | 约 8 小时 | +| 每日 | 28,800 | 约 24 小时 | +| 每周 | 100,000 | 约 3.5 天 | + +### 方法 4:结合 at-block 和周期性 + +两个设置可同时激活: + +```ini +snapshot-at-block = 5000000 +snapshot-every-n-blocks = 100000 +snapshot-dir = /data/snapshots +``` + +--- + +## 引导:从快照加载(DLT 模式) + +将快照文件传输到新节点,然后: + +```bash +vizd \ + --snapshot /data/snapshots/viz-snapshot.json \ + --plugin snapshot \ + --plugin p2p \ + --p2p-seed-node seed1.viz.media:2001 +``` + +节点在数秒内加载状态,并从快照区块高度开始 P2P 同步。 + +### 加载过程 + +1. `chain::plugin_startup()` 检测到 `--snapshot`。 +2. 三项安全检查(按顺序):共享内存已存在 → 跳过;文件未找到(已为 `.used`)→ 跳过;否则继续。 +3. 通过 `open_from_snapshot()` 打开数据库(清除并重新初始化 chainbase)。 +4. 验证快照 JSON(格式版本、chain ID、SHA-256 校验和),导入全部 32 种对象类型。 +5. 快照文件重命名为 `.used` 以防止重启时重复导入。 +6. LIB 提升至 `head_block_num`,使 P2P synopsis 从快照头部开始。 +7. Fork database 以快照的头部区块为种子。 +8. P2P 插件启动,从 `LIB + 1` 开始同步区块。 + +### 重启安全性 + +| 场景 | 结果 | +|------|------| +| 首次启动(无 `shared_memory.bin`,文件存在) | 导入快照,重命名为 `.used` | +| 重启(shared_memory 存在) | 跳过导入——使用现有状态 | +| 重启(shared_memory 已清除,文件已为 `.used`) | 跳过导入——文件未找到 | +| 强制重新导入 | `--resync-blockchain` + 新快照文件 | + +初始导入后无需从命令行或 Docker `VIZD_EXTRA_OPTS` 中删除 `--snapshot`。 + +--- + +## 快照文件格式 + +快照是单个 JSON 文件: + +```json +{ + "header": { + "version": 1, + "chain_id": "...", + "snapshot_block_num": 12345678, + "snapshot_block_id": "...", + "snapshot_block_time": "2025-01-01T00:00:00", + "last_irreversible_block_num": 12345660, + "payload_checksum": "sha256...", + "object_counts": { "account": 50000, ... } + }, + "state": { + "dynamic_global_property": [ ... ], + "account": [ ... ], + ... + } +} +``` + +### 32 种包含的对象类型 + +**关键(11 种)** — 共识必需: +`dynamic_global_property`、`witness_schedule`、`hardfork_property`、`account`、`account_authority`、`validator`、`witness_vote`、`block_summary`、`content`、`content_vote`、`block_post_validation` + +**重要(15 种)** — 完整运行所需: +`transaction`、`vesting_delegation`、`vesting_delegation_expiration`、`fix_vesting_delegation`、`withdraw_vesting_route`、`escrow`、`proposal`、`required_approval`、`committee_request`、`committee_vote`、`invite`、`award_shares_expire`、`paid_subscription`、`paid_subscribe`、`witness_penalty_expire` + +**可选(5 种)** — 元数据和恢复: +`content_type`、`account_metadata`、`master_authority_history`、`account_recovery_request`、`change_recovery_account_request` + +--- + +## DLT 滚动 block log + +DLT 模式下主 `block_log` 保持为空。`dlt_block_log`(文件 `dlt_block_log.log` + `dlt_block_log.index`)存储最近的不可逆区块用于: + +- **P2P 区块服务** — 节点可以请求最近的区块用于 fork 解决。 +- **API 访问** — `get_block` 对滚动窗口内的区块有效。 + +```ini +dlt-block-log-max-blocks = 100000 # 保留最近约 3.5 天的区块 +``` + +当日志超过此大小时,旧区块从前端修剪。实现独立于内存映射文件跟踪逻辑文件大小,以防止数千次调整大小循环后出现过期大小错误。 + +--- + +## 崩溃恢复:`--replay-from-snapshot` + +当 `shared_memory.bin` 损坏(不干净关机、磁盘满、硬件故障)时使用此模式。DLT 模式下普通的 `--replay-blockchain` 不可用,因为 `block_log` 为空。 + +```bash +# 明确指定快照路径 +vizd --replay-from-snapshot --snapshot /data/snapshots/snapshot-block-79273800.json --plugin snapshot + +# 或自动发现最新快照 +vizd --replay-from-snapshot --snapshot-auto-latest --plugin snapshot +``` + +恢复步骤: +1. 始终清除 `shared_memory.bin`(假设已损坏)。 +2. 导入快照状态。 +3. 从 `snapshot_head + 1` 开始重放 `dlt_block_log`。 +4. 发出 `on_sync` — P2P 填补到实时链头的剩余差距。 + +快照文件**不**重命名为 `.used`(可能再次需要)。 + +### 恢复场景示例 + +DLT 节点在区块 79,274,318 处崩溃,配置了 `snapshot-every-n-blocks = 100000` 和 `dlt-block-log-max-blocks = 100000`: + +``` +/data/viz-snapshots/snapshot-block-79273800.json ← 最新快照 +/blockchain/dlt_block_log.* ← 包含区块 79174319..79274318 +/blockchain/shared_memory.bin ← 已损坏 +``` + +```bash +vizd --replay-from-snapshot --snapshot-auto-latest --plugin snapshot +``` + +``` +Loading state from snapshot: .../snapshot-block-79273800.json (12.3 s) +Replaying dlt_block_log from block 79273801 to 79274318... + 100% 518 of 518 (block 79274318, elapsed 7.2 s) +Recovery complete. Started on blockchain with 79274318 blocks. +``` + +节点现在处于区块 79,274,318;P2P 同步提供其余部分。 + +--- + +## 自动运行时恢复:`--auto-recover-from-snapshot` + +默认启用(`true`)。当运行时区块处理或生成过程中检测到损坏时,节点: + +1. 在 `snapshot-dir` 中找到最新快照。 +2. 关闭数据库。 +3. 使用与 `--replay-from-snapshot` 相同的代码路径清除并重新导入。 +4. 恢复 P2P 同步——无需重启。 + +**前提条件:** `plugin = snapshot` 已启用且 `snapshot-dir` 中存在快照。 + +禁用(用于调试): + +```bash +vizd --no-auto-recover-from-snapshot +``` + +--- + +## P2P 快照同步 + +节点可以通过自定义 TCP 协议从受信任节点下载快照,无需手动传输文件。 + +### 快照服务器 + +```ini +plugin = snapshot +allow-snapshot-serving = true +snapshot-serve-endpoint = 0.0.0.0:8092 +snapshot-every-n-blocks = 28800 +snapshot-dir = /data/viz-snapshots +``` + +### 新节点引导 + +```ini +plugin = snapshot +trusted-snapshot-peer = seed1.viz.media:8092 +trusted-snapshot-peer = seed2.viz.media:8092 +sync-snapshot-from-trusted-peer = true +``` + +当节点以 0 个区块启动且 `sync-snapshot-from-trusted-peer = true` 时,它查询所有受信任节点,选择快照最高的节点,以 1 MB 块下载,验证 SHA-256 校验和并导入——所有这些在 P2P 或验证者插件激活之前完成。 + +### 安全性 + +- 超过 2 GB 的下载被拒绝。 +- 通过流式 SHA-256 验证校验和(从不完全加载到内存)。 +- 速率限制,最多 5 个并发连接,每个连接 60 秒截止时间。 +- `allow-snapshot-serving-only-trusted = true` 限制为 `trusted-snapshot-peer` 列表。 + +--- + +## Docker + +设置 `VIZD_EXTRA_OPTS` 传递快照标志: + +```bash +# 从快照引导 +docker run -e VIZD_EXTRA_OPTS="--snapshot /var/lib/vizd/snapshots/snap.json --plugin snapshot" ... + +# 崩溃恢复 +docker run -e VIZD_EXTRA_OPTS="--replay-from-snapshot --snapshot-auto-latest --plugin snapshot" ... +``` + +通过 `config.ini` 配置周期性快照(无需 `VIZD_EXTRA_OPTS`): + +```ini +plugin = snapshot +snapshot-every-n-blocks = 28800 +snapshot-dir = /var/lib/vizd/snapshots +``` + +快照文件可在挂载卷路径的主机上访问。 + +| 任务 | 方法 | +|------|------| +| 周期性快照 | config 中的 `snapshot-every-n-blocks` | +| 一次性快照 | 通过 `VIZD_EXTRA_OPTS` 的 `--create-snapshot` | +| 引导新节点 | 通过 `VIZD_EXTRA_OPTS` 的 `--snapshot` | +| 崩溃恢复 | 通过 `VIZD_EXTRA_OPTS` 的 `--replay-from-snapshot --snapshot-auto-latest` | +| 自动恢复 | 默认——确保 `plugin = snapshot` 且设置了 `snapshot-dir` | +| P2P 自动引导 | config 中的 `sync-snapshot-from-trusted-peer = true` + `trusted-snapshot-peer` | + +--- + +## 推荐生产配置 + +```ini +plugin = snapshot + +# 每约 24 小时创建快照 +snapshot-every-n-blocks = 28800 +snapshot-dir = /data/viz-snapshots +snapshot-max-age-days = 90 + +# DLT 滚动 block log:保留最近约 3.5 天 +dlt-block-log-max-blocks = 100000 + +shared-file-size = 4G +plugin = p2p +p2p-seed-node = seed1.viz.media:2001 +``` + +--- + +## 过期快照检测 + +如果服务节点的最新快照早于 `dlt_block_log` 起始区块,下载快照的新节点将无法同步缺失的区块。启动时插件检测到此情况并在下一个实时区块自动创建新快照——无需手动干预。 + +--- + +## 故障排除 + +| 问题 | 检查 | +|------|------| +| 节点每次重启都重新导入快照 | 快照文件未重命名为 `.used` — 检查快照目录的写入权限 | +| 节点报告 `item_not_available` | DLT block log 可能未覆盖所宣告的区块——验证 `dlt-block-log-max-blocks` 是否足够大 | +| 快照加载后 P2P 同步停滞 | 检查 config 中的 `[logger.sync]`;验证导入后 LIB 是否已提升至头部 | +| 快照创建失败 | 检查 `snapshot-dir` 的磁盘空间;节点在失败时继续运行 | +| 自动恢复意外触发 | 检查磁盘错误;查看日志中的 `shared_memory_corruption_exception` | +| P2P 下载被拒绝(>2 GB) | 快照过大——增加服务节点上的 `dlt-block-log-max-blocks` 以减小快照大小 | + +--- + +参见:[快照插件](../plugins/snapshot.md) — 完整实现参考;[P2P 概述](../p2p/overview.md) — DLT 同步协议详情。 diff --git a/@l10n/zh-CN/docs/node/validator-guard.md b/@l10n/zh-CN/docs/node/validator-guard.md new file mode 100644 index 0000000000..f1fbd896e6 --- /dev/null +++ b/@l10n/zh-CN/docs/node/validator-guard.md @@ -0,0 +1,138 @@ +# 验证者守护(Validator Guard) + +`witness_guard` 插件为验证者账户自动化签名密钥恢复。当验证者的签名密钥被重置为 null(禁用区块生产)时,插件检测到此变化并广播 `witness_update_operation` 以恢复密钥——无需手动干预。 + +--- + +## 何时使用此插件 + +- 您的验证者账户的签名密钥可能被紧急共识主节点、安全协议或手动操作清空。 +- 没有此插件,您必须手动监控链上密钥并在计划槽位到来前恢复它。 +- 有了此插件,节点每 N 个区块监视一次 null 密钥并自动恢复。 + +--- + +## 启用插件 + +```ini +plugin = witness_guard +``` + +--- + +## 配置 + +| 选项 | 默认值 | 描述 | +|------|-------|------| +| `validator-guard-enabled` | `true` | 全局启用或禁用插件。 | +| `validator-guard-interval` | `20` | 检查间隔,以区块为单位(按 3 秒/区块计约 60 秒)。 | +| `validator-guard-validator` | — | JSON 三元组 `[name, signing_wif, active_wif]`。可重复。 | +| `validator-guard-disable` | `5` | 同一验证者连续生产的区块数,超过后自动禁用。`0` = 禁用。 | + +插件还从验证者插件配置中读取 `enable-stale-production`。 + +### 示例 + +```ini +plugin = witness_guard + +# 监控一个验证者 +validator-guard-validator = ["alice", "5K_SIGNING_WIF", "5K_ACTIVE_WIF"] + +# 监控第二个验证者 +validator-guard-validator = ["alice.backup", "5J_SIGNING_WIF", "5J_ACTIVE_WIF"] + +# 每 10 个区块检查一次 +validator-guard-interval = 10 +``` + +> **安全说明:** 活跃私钥以明文存储在 `config.ini` 中。限制文件权限(`chmod 600 config.ini`),避免将文件暴露给不受信任的进程。 + +--- + +## 工作原理 + +### 启动 + +1. 解析并验证所有配置的 WIF 密钥。 +2. 如果 `enable-stale-production = true`,自动恢复启动时处于禁用状态(参见安全机制)。 +3. 链数据库打开后,针对链上权限验证每个配置的活跃密钥。账户未找到或密钥不匹配的验证者将从监控中删除并发出警告。 +4. 运行立即检查;缓存结果以与周期性计划对齐。 + +### 每区块处理程序 + +每个区块时: + +1. **连续区块自动禁用**:如果被监控的验证者连续生产了 `validator-guard-disable` 个区块,广播带 null 密钥的 `witness_update_operation` 以禁用它,并将该验证者标记为自动禁用。*其他*验证者的任何区块都会重置所有连续计数器。 +2. **交易确认**:扫描区块中待处理的恢复交易 ID。匹配时,将恢复标记为已确认并清除跟踪状态。 +3. **前瞻调度**:如果任何被监控的验证者在接下来的 3 个槽位内有安排,触发立即检查,以便在槽位到来前恢复密钥。 +4. **周期性检查**:否则,每 `validator-guard-interval` 个区块运行核心检查。节点启动后仍在追赶时,每 10 个区块检查一次。 + +### 核心检查 + +每次检查(按顺序): + +1. **过时生产防护**:如果 `enable-stale-production` 激活且网络参与度 < 33%,跳过所有恢复。参与度达到 ≥ 33% 时自动清除。 +2. **同步检查**:如果头块时间比系统时钟落后超过 2 个区块间隔,则跳过。 +3. **长 fork 安全**:如果 LIB 超过 200 秒,则跳过。 +4. **过期清理**:清理过期的进行中恢复尝试,以便可以重试。 +5. **每验证者密钥检查**:读取链上签名密钥。 + - 密钥存在 → 清除待处理恢复状态和自动禁用标志。 + - 密钥为 null + 验证者已自动禁用 → 跳过自动恢复(操作员必须调查)。 + - 密钥为 null + 无进行中的恢复 → 调用 `send_witness_update`。 + +### 恢复交易 + +1. 构建 `witness_update_operation`,保留当前链上 URL,并将签名密钥设置为配置的公钥。 +2. 包装为 `signed_transaction`,30 秒到期时间,引用当前头块。 +3. 使用配置的活跃私钥签名。 +4. 通过 P2P 广播。 +5. 在 `_pending_confirmations` 中跟踪交易 ID 以防止重复发送。 + +--- + +## 安全机制 + +| 机制 | 行为 | +|------|------| +| **过时生产** | 当 `enable-stale-production = true` 时,自动恢复被禁用以避免在少数派 fork 上广播。参与度 ≥ 33% 时自动清除。 | +| **紧急模式** | 紧急共识期间,过时生产防护被绕过——密钥恢复可能是恢复所需的。 | +| **同步检查** | 仅在节点同步时运行。 | +| **长 fork 检测** | 如果 LIB 超过 200 秒则跳过。 | +| **权限验证** | 启动时针对链上权限验证活跃密钥。 | +| **连续区块自动禁用** | 同一验证者连续生产 N 个区块后自动清空签名密钥。操作员手动修复密钥前,自动恢复被抑制。 | +| **重复防护** | 进行中的恢复以过期方式跟踪;不发送重复交易。 | + +--- + +## 日志消息 + +| 消息 | 含义 | +|------|------| +| `monitoring validator 'alice' (signing key: VIZ...)` | 插件已为此验证者启动 | +| `enable-stale-production detected — auto-restore is DISABLED` | 过时生产模式激活;恢复被抑制 | +| `network is healthy (XX%), auto-clearing stale production override` | 过时生产防护已解除 | +| `'alice' has null signing key on-chain — initiating restore` | 检测到 null 密钥,即将广播 | +| `broadcasting witness_update [ID: ...] for 'alice' — restoring key to VIZ...` | 恢复交易已发送 | +| `CONFIRMED restoration for 'alice' in block #N` | 恢复已在链上确认 | +| `POTENTIAL LONG FORK DETECTED! LIB #N is Xs old. Skipping restoration.` | 由于 LIB 过期而跳过恢复 | +| `validator 'alice' produced N consecutive blocks — auto-disabling` | 连续区块阈值已达到 | +| `'alice' was auto-disabled (consecutive block limit), skipping auto-restore` | 自动禁用后抑制自动恢复 | +| `witness_update FAILED for 'alice': [error]` | 广播失败 | + +--- + +## 故障排除 + +| 问题 | 检查 | +|------|------| +| 恢复未触发 | 验证 `validator-guard-enabled = true`;确保节点已同步;确认账户是已注册的验证者 | +| `enable-stale-production = true` 时禁用 | 预期行为——等待网络参与度 ≥ 33% | +| 交易失败 | 验证 `active_wif` 是否与账户的活跃权限匹配。检查启动时关于密钥不匹配的警告 | +| 配置解析错误 | 每个条目必须是有效的 3 元素 JSON 数组:`["name", "signing_wif", "active_wif"]` | +| 验证者已自动禁用且未恢复 | 连续区块阈值已触发。调查原因,手动在链上恢复签名密钥;一旦密钥检测为非 null,自动禁用标志将清除 | +| 启动时权限警告 | `WARNING: Configured active key ... does NOT have authority on-chain` — 在配置中更新密钥 | + +--- + +参见:[验证者节点](./validator-node.md) — 签名密钥设置;[验证者插件](../plugins/validator.md) — 生产循环内部机制。 diff --git a/@l10n/zh-CN/docs/node/validator-node.md b/@l10n/zh-CN/docs/node/validator-node.md new file mode 100644 index 0000000000..1cf8f2e777 --- /dev/null +++ b/@l10n/zh-CN/docs/node/validator-node.md @@ -0,0 +1,235 @@ +# 运行验证者节点 + +验证者(区块生产者)是由 Fair-DPOS 算法计划每 3 秒生产一个区块的账户。运行验证者节点需要已注册的验证者账户、签名密钥和正确配置的节点。 + +--- + +## 前置要求 + +1. 通过 `validator_update_operation` 注册为验证者的 VIZ Ledger 账户。 +2. 与链上注册的签名密钥对应的 WIF 私钥。 +3. 已同步的全节点(验证者插件需要 chain + p2p + snapshot 插件)。 + +--- + +## 配置 + +使用 `share/vizd/config/config_witness.ini` 作为基础模板。 + +关键设置: + +```ini +# P2P — 允许公共入站连接以传播区块 +p2p-endpoint = 0.0.0.0:2001 +p2p-seed-node = seed1.viz.media:2001 + +# RPC — 绑定到 localhost 以确保安全(验证者不需要公共 API) +webserver-http-endpoint = 127.0.0.1:8090 +webserver-ws-endpoint = 127.0.0.1:8091 + +# 验证者所需插件 +plugin = chain p2p webserver json_rpc database_api network_broadcast_api validator witness_api + +# 跳过虚拟操作索引以节省内存 +skip-virtual-ops = true + +# 共享内存 — 对于低内存验证者构建 2G 已足够 +shared-file-size = 2G + +# ─── 验证者身份 ──────────────────────────────────────────────── +# 您的验证者账户名 +validator = myvalidator + +# 您的 WIF 格式签名私钥 +private-key = 5JxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxWIF + +# 参与度阈值 — 网络参与度低于此值时停止生产 +required-participation = 33 + +# 主网上不要启用此选项(仅用于测试网/引导) +# enable-stale-production = false +``` + +--- + +## NTP 时间同步 + +精确时间对区块生产至关重要。验证者插件维护自己的 NTP 客户端: + +```ini +# NTP 服务器(默认:pool.ntp.org, time.google.com, time.cloudflare.com) +ntp-server = pool.ntp.org +ntp-server = time.cloudflare.com + +# 每 15 分钟检查一次 NTP +ntp-request-interval = 900 + +# 丢弃往返时间 > 150ms 的 NTP 响应 +ntp-round-trip-threshold = 150 +``` + +确保服务器操作系统时钟也已同步(通过 `chrony` 或 `systemd-timesyncd`)。 + +--- + +## 启动节点 + +```bash +./vizd --config-file /data/vizd/config.ini --data-dir /data/vizd +``` + +### Docker + +```bash +docker run -d \ + --name vizd-validator \ + --restart unless-stopped \ + -p 2001:2001 \ + -v /data/vizd:/var/lib/vizd \ + -e VIZD_WITNESS=myvalidator \ + -e VIZD_PRIVATE_KEY=5Jxxx... \ + vizblockchain/vizd:lowmem +``` + +验证者请使用 `lowmem` 镜像——它不含不必要的索引插件。 + +--- + +## 注册/更新验证者 + +使用 `cli_wallet` 或任何兼容钱包广播 `validator_update_operation`: + +```json +{ + "type": "validator_update_operation", + "value": { + "owner": "myvalidator", + "url": "https://mysite.example/validator", + "block_signing_key": "VIZ5hqSa...", + "props": [3, { + "account_creation_fee": "1.000 VIZ", + "maximum_block_size": 65536 + }] + } +} +``` + +`block_signing_key` 必须与节点配置中的 `private-key` 匹配。 + +要禁用验证者(从计划中移除),将 `block_signing_key` 设置为空密钥: + +```json +"block_signing_key": "VIZ1111111111111111111111111111111114T1Anm" +``` + +--- + +## 生产循环:节点的工作流程 + +验证者插件在**专用线程**(与 P2P I/O 隔离)上运行 250ms 生产计时器。每次触发时调用 `maybe_produce_block()`,按顺序检查: + +1. **同步检查**(DLT 模式):追赶节点时不生产。 +2. **快照检查**:快照创建进行中时不生产。 +3. **参与度检查**:网络验证者参与度必须 ≥33%。 +4. **槽位分配**:当前槽位是否安排了此节点的验证者? +5. **密钥检查**:节点是否有正确的私钥? +6. **少数派 fork 检测**:如果最后 21 个区块都来自此节点自己的验证者——回滚并重新同步。 +7. **Fork 冲突解决**:如果同一高度存在另一个区块,应用投票权重比较。 +8. **延迟检查**:如果节点超过槽位边界 >500ms——跳过。 +9. **生成并广播**区块。 + +完整执行流程参见[验证者插件](../plugins/validator.md)。 + +--- + +## 生产结果(日志消息) + +| 结果 | 含义 | +|------|------| +| `produced` | 区块成功生产并广播 | +| `not_synced` | 节点仍在追赶或快照进行中 | +| `not_time_yet` | 无可用槽位或 NTP 漂移 | +| `not_my_turn` | 此槽位安排了另一个验证者 | +| `no_private_key` | 已配置验证者被安排,但私钥缺失 | +| `low_participation` | 网络参与度低于阈值 | +| `lag` | 超过槽位 >500ms 才醒来——槽位已错过 | +| `fork_collision` | 下一高度存在竞争区块——等待中 | +| `minority_fork` | 节点处于孤立 fork——回滚中 | + +--- + +## 安全机制 + +### 网络分区保护 +如果少于 33% 的验证者在参与,则停止生产以防止脑裂场景。仅在引导/测试网时使用 `enable-stale-production = true` 覆盖。 + +### 少数派 Fork 检测 +如果节点的 fork 数据库显示 21+ 个连续区块全部来自此节点自己的验证者,它会自动回滚到 LIB 并重新同步。这可以捕获网络隔离情况。 + +### 生产 Watchdog +如果在 `should_be_producing` 为 true 的情况下 180 秒内(紧急主节点为 60 秒)没有生产区块,watchdog 会自动清除卡住的标志(`minority_fork_recovering`、P2P 追赶、链同步)并尝试恢复生产。 + +### 快照安全 +创建快照期间暂停区块生产,以避免写锁冲突。 + +--- + +## 监控 + +注意以下日志模式: + +``` +# 正常:区块已生产 +produced block #123456 ... validator=myvalidator + +# 警告:错过槽位 +MISSED-SLOT-OUR-validator: ... + +# 警告:少数派 fork +MINORITY FORK DETECTED: rolling back to LIB + +# 警告:watchdog 触发 +WATCHDOG: no production for 180s, clearing flags +``` + +自动告警参见[监控](./monitoring.md)和[验证者守护](./validator-guard.md)。 + +--- + +## 一个节点上的多个验证者 + +`validator` 和 `private-key` 选项可重复: + +```ini +validator = alice +validator = alice.backup +private-key = 5Jxxx... # Alice 的密钥 +private-key = 5Jyyy... # Alice.backup 的密钥 +``` + +节点将按计划为任何已配置的验证者生产区块。 + +--- + +## 紧急共识密钥 + +对于参与紧急共识恢复的节点: + +```ini +emergency-private-key = 5Jzzz... # 委员会紧急密钥 +``` + +设置后,节点自动将 `CHAIN_EMERGENCY_WITNESS_ACCOUNT` 添加到其验证者集合,并参与紧急区块生产。参见[紧急共识](../consensus/emergency-consensus.md)。 + +--- + +## 故障排除 + +| 问题 | 检查内容 | +|------|---------| +| 未生产区块 | 验证配置中的 `validator` 和 `private-key`;检查链上注册的签名密钥与配置是否匹配 | +| 日志中出现 `no_private_key` | 链上签名密钥与配置中的任何 `private-key` 不匹配 | +| `low_participation` | 网络健康问题——检查节点数量和其他验证者状态 | +| `minority_fork` | 网络隔离——验证到种子节点的连接 | +| NTP 停滞警告 | 检查操作系统 NTP 同步:`chronyc tracking` 或 `timedatectl` | +| 槽位被抢占 | 签名密钥可能被紧急主节点清空;通过 `validator_update_operation` 恢复 | diff --git a/@l10n/zh-CN/docs/p2p/forward-mode.md b/@l10n/zh-CN/docs/p2p/forward-mode.md new file mode 100644 index 0000000000..d69b4d08bf --- /dev/null +++ b/@l10n/zh-CN/docs/p2p/forward-mode.md @@ -0,0 +1,214 @@ +# 转发模式 — 区块和交易交换 + +转发模式(`DLT_NODE_STATUS_FORWARD`)是节点追上网络后的正常运行状态。节点不再从对端拉取区块范围,而是在新区块和交易到达时**推送**给所有 fork 对齐的对端。 + +--- + +## 交付门控:`exchange_enabled` + +所有转发模式流量通过每个对端的两个标志过滤: + +| 标志 | 含义 | +|------|------| +| `exchange_enabled == true` | 对端已 fork 对齐 — 其 head 区块为我们所知(或我们的为其所知) | +| `lifecycle_state == ACTIVE` | 对端已完成握手 | + +两者都必须为 true,对端才能接收区块和交易广播。 + +中央广播函数 `send_to_all_our_fork_peers()` 迭代所有连接对端,跳过未通过任一检查的对端。 + +--- + +## 设置 `exchange_enabled` + +### 初始设置(hello 握手) + +在 hello 期间,接收方调用 `check_fork_alignment()` — 一个多级的 DLT 范围感知检查: + +| 情况 | 检查 | +|------|------| +| 对端无区块(`head_num == 0`) | → 对齐 | +| 对端 head 在我们的 DLT 范围内 | `is_block_known(peer.head_id)` | +| 对端 head + 1 == 我们的 DLT 最早区块 | 读取我们最早的区块;验证 `previous == peer.head_id` | +| LIB 回退 | `is_block_known(peer.lib_id)` | + +任一检查通过 → `exchange_enabled = true`。 + +### OR 组合 + +双方互发 hello 消息,各自独立计算 `exchange_enabled`。对端的最终值是双方判定的**逻辑 OR**。如果任一方识别对方的链,交换即被启用。 + +head 落后于 master DLT 范围的 slave 的 `check_fork_alignment` 失败(它尚未应用 master 的区块),但 master 的检查成功(它知道 slave 的 head)。OR 确保即使在这种不对称情况下也启用交换。 + +### 重新评估触发器 + +当节点对对端区块的了解发生变化时,`exchange_enabled` 被重新评估: + +| 触发器 | 时机 | +|--------|------| +| `transition_to_forward()` | 对每个 `exchange_enabled=false` 的对端;重新检查 `is_block_known(peer_head_id)` | +| `on_dlt_fork_status()` | 对端从 SYNC 过渡到 FORWARD;重新检查 fork 对齐 | +| 接受来自对端的区块 | 如果区块应用到我们的链,立即为该对端启用交换 | + +--- + +## 区块广播 + +### 自产区块 + +当验证者生产区块时: + +``` +validator.cpp → p2p_plugin.broadcast_block(block) + → dlt_p2p_node.broadcast_block(block) + → send_to_all_our_fork_peers(dlt_block_reply_message, exclude=none, block_id=block.id()) +``` + +区块发送给**所有** ACTIVE 且启用交换的对端。回声抑制防止向已有该区块的对端重复发送。 + +### 中继收到的区块 + +当区块从对端 X 到达时: + +1. 记录 X 拥有此区块(`state.record_known_block(block.id())`)。 +2. 将区块应用到链上。 +3. `send_to_all_our_fork_peers(block_reply, exclude=X, block_id=block.id())` — 发送给所有其他启用交换的 ACTIVE 对端。 + +--- + +## 区块回声抑制 + +没有抑制时,区块会通过中继链回到生产者: + +``` +A 生产区块 N → 发送给 B、C +B 将 N 中继给 A、C +C 将 N 中继给 A、B +A 从 B 和 C 收回自己的区块 N — 浪费带宽 +``` + +每个对端状态维护一个**最近 20 个区块 ID 的环形缓冲区**(`known_blocks`)。在向对端发送区块之前,节点检查 `peer.has_block(block_id)`。如果已知,则跳过发送。 + +对端在两种情况下被记录为"拥有"区块: +- **我们刚发送给它** — 在 `send_to_all_our_fork_peers` 发送后记录。 +- **它发送给了我们** — 在 `on_dlt_block_reply` 接收时记录。 + +中继日志显示回声过滤计数: +``` +Relay block_reply to 3 peers (0 skipped: no_exchange, 0 skipped: not_active, 1 skipped: echo) +``` + +--- + +## 交易广播 + +### 自发起(通过 API) + +通过 `network_broadcast_api` 提交的交易 → 添加到 P2P 内存池 → `dlt_transaction_message` 发送给所有启用交换的 ACTIVE 对端。 + +### 中继收到的交易 + +交易从对端 X 到达 → 添加到内存池 → 中继给所有启用交换的 ACTIVE 对端(**除 X 外**)。 + +### 内存池预过滤 + +在交易被接受进内存池或转发之前,必须通过: + +| 检查 | 失败 | +|------|------| +| 重复(`trx_id` 已在内存池中) | 静默跳过 | +| 过期(`expiration < now`) | 拒绝;如来自对端则增加 spam 计数 | +| 到期太远(未来 >24 小时) | 拒绝;增加 spam 计数 | +| 过大(>`dlt-mempool-max-tx-size`,默认 64 KB) | 拒绝;增加 spam 计数 | +| TaPoS 无效(引用区块未知) | 拒绝;增加 spam 计数 | +| 内存池已满 | 驱逐最早到期的条目,然后添加 | + +**临时条目:** SYNC 模式期间收到的交易被标记为 `is_provisional = true` — 在本地存储但不转发给对端。过渡到 FORWARD 时,临时条目根据当前 head 重新验证,无效的被清除。 + +--- + +## SYNC → FORWARD 过渡 + +### 触发器 + +| 触发器 | 条件 | +|--------|------| +| 带 `is_last=true` 的区块范围回复 | 且至少应用了一个区块(不全是 dead-fork) | +| `check_sync_catchup()` | `our_head >= 所有活跃对端 head` 且至少有一个活跃对端 | +| 停滞超时 | 30 秒无区块,3 次重试耗尽 | + +`check_sync_catchup()` 在每次区块接受后和周期任务每 5 秒运行一次。 + +**隔离防护:** 当零个活跃对端存在时,`check_sync_catchup()` **不**声称已追上。而是启动 60 秒隔离计时器;到期后触发 `emergency_peer_reset()`(见下文)。 + +### 过渡时的操作 + +1. 通知所有连接对端:向每个活跃/同步中的对端(不仅限于启用交换的)广播带 `node_status=FORWARD` 的 `dlt_fork_status_message`。这让对端立即重新评估我们的 `exchange_enabled`。 +2. 为所有对端重新评估 `exchange_enabled`。 +3. 重新验证并清除无效的临时内存池条目。 +4. 重置 `_sync_stagnation_retries = 0`。 +5. 重置 `_last_block_received_time = now`,使转发停滞计时器重新开始。 + +--- + +## FORWARD → SYNC 回退 + +如果区块在 FORWARD 模式下停止到达,节点回退到 SYNC: + +| 触发器 | 条件 | +|--------|------| +| Hello 回复显示对端遥遥领先 | 收到 hello_reply 时 `peer_head_num > our_head + 2` | +| 周期检查 | `check_forward_behind()`:任何活跃对端有 `peer_head_num > our_head + 2` | +| 停滞 | `check_forward_stagnation()`:head 停滞 30 秒且至少有一个对端在前面 | + +**当没有对端在前时的无操作:** 当所有连接对端有相同的 head 时,`check_forward_stagnation()` **不**过渡到 SYNC。没有东西可同步;过渡只会造成振荡。停滞计时器重置,节点保持在 FORWARD。 + +过渡到 SYNC 时,`_last_block_received_time` 重置为 `now`,使同步停滞计时器重新开始(而不是从 FORWARD 阶段继承)。 + +--- + +## 对端隔离恢复 + +当所有对端都断开连接或被封禁时(例如快照暂停后),正常的 SYNC/FORWARD 模式过渡无意义地循环。**60 秒**零活跃连接后: + +`emergency_peer_reset()`: +1. 将所有 BANNED 对端移回 DISCONNECTED 状态;清除 `spam_strikes`。 +2. 将所有 DISCONNECTED 对端的退避重置为 30 秒(`INITIAL_RECONNECT_BACKOFF_SEC`),`next_reconnect_attempt = now`。 +3. 清除停滞重试计数器。 +4. 在下一个周期任务 tick(~5 秒),`periodic_reconnect_check()` 立即重连。 + +--- + +## 不被转发的内容 + +| 场景 | 流量 | +|------|------| +| 对端有 `exchange_enabled=false` | 无区块,无交易 | +| 节点在 SYNC 模式 | 无广播;仅范围请求和 gap fill 请求 | +| 区块处理暂停(`_block_processing_paused=true`) | 接收区块并排队,但跳过周期性的数据库访问任务 | + +--- + +## 交付摘要 + +| 事件 | 接收者 | 排除 | 回声过滤 | +|------|--------|------|---------| +| 节点生产区块 | 所有 ACTIVE 且 `exchange_enabled=true` 的对端 | (无) | `known_blocks` 中有区块的对端 | +| 节点从 X 收到区块 | 所有 ACTIVE 且 `exchange_enabled=true` 的对端 | X | `known_blocks` 中有区块的对端 | +| 节点发起交易 | 所有 ACTIVE 且 `exchange_enabled=true` 的对端 | (无) | (无) | +| 节点从 X 收到交易 | 所有 ACTIVE 且 `exchange_enabled=true` 的对端 | X | (无) | + +--- + +## `peer_head_num` 是过时快照 + +[统计](./stats-reference.md)中显示的 `peer_head_num` 从以下来源更新: +- Hello 握手 +- `dlt_fork_status_message` 交换 +- 区块中继(收到区块 N 意味着 `peer_head_num ≥ N`) + +在这些事件之间,对端的实际链 head 可能显著更高。不要将 `peer_head_num` 视为实时值。 + +--- + +参见:[P2P 概述](./overview.md)、[同步场景](./sync-scenarios.md)、[统计参考](./stats-reference.md)、[消息](./messages.md)。 diff --git a/@l10n/zh-CN/docs/p2p/messages.md b/@l10n/zh-CN/docs/p2p/messages.md new file mode 100644 index 0000000000..c7454ce69f --- /dev/null +++ b/@l10n/zh-CN/docs/p2p/messages.md @@ -0,0 +1,459 @@ +# P2P 消息参考 + +DLT P2P 使用原始 TCP 上的二进制协议。每条消息以 4 字节小端头帧定,包含消息类型 ID,后跟通过 FC 反射序列化的长度前缀数据载荷。 + +**头文件:** [libraries/network/include/graphene/network/dlt_p2p_messages.hpp](../../libraries/network/include/graphene/network/dlt_p2p_messages.hpp) + +--- + +## 消息类型摘要 + +| 类型 ID | 名称 | 方向 | 用途 | +|---------|------|------|------| +| 5100 | `dlt_hello_message` | 发起方 → 接收方 | 初始握手 — 链状态和能力 | +| 5101 | `dlt_hello_reply_message` | 接收方 → 发起方 | 握手回复 — fork 对齐,交换状态 | +| 5102 | `dlt_range_request_message` | 任意 | 询问对端是否有特定区块 | +| 5103 | `dlt_range_reply_message` | 任意 | 响应:区块可用范围 | +| 5104 | `dlt_get_block_range_message` | 同步中 → 对端 | 请求一段区块(批量同步) | +| 5105 | `dlt_block_range_reply_message` | 对端 → 同步中 | 批量区块交付 | +| 5106 | `dlt_get_block_message` | 任意 | 请求单个区块 | +| 5107 | `dlt_block_reply_message` | 任意 | 单个区块交付 | +| 5108 | `dlt_not_available_message` | 任意 | 请求的区块不可用 | +| 5109 | `dlt_fork_status_message` | 任意 | 实时链状态更新(head、LIB、fork、DLT 范围) | +| 5110 | `dlt_peer_exchange_request` | 任意 | 请求对端地址列表 | +| 5111 | `dlt_peer_exchange_reply` | 任意 | 对端地址列表响应 | +| 5112 | `dlt_peer_exchange_rate_limited` | 任意 | 对端交换请求的速率限制响应 | +| 5113 | `dlt_transaction_message` | 任意 | 交易广播 | +| 5114 | `dlt_soft_ban_message` | 任意 → 被封禁方 | 断开前的软封禁通知 | +| 5115 | `dlt_gap_fill_request` | 任意 | 请求特定区块以填补间隙 | +| 5116 | `dlt_gap_fill_reply` | 任意 | 间隙填补区块交付 | + +--- + +## 枚举类型 + +### `dlt_node_status` + +| 值 | 含义 | +|----|------| +| `DLT_NODE_STATUS_SYNC` (0) | 节点落后;正在主动从对端拉取区块 | +| `DLT_NODE_STATUS_FORWARD` (1) | 节点已追上;通过广播交换区块 | + +### `dlt_fork_status` + +| 值 | 含义 | +|----|------| +| `DLT_FORK_STATUS_NORMAL` (0) | 在多数派 fork 上 | +| `DLT_FORK_STATUS_LOOKING_RESOLUTION` (1) | 检测到 fork;运行解决算法 | +| `DLT_FORK_STATUS_MINORITY` (2) | 确认在少数派 fork 上 | + +### `dlt_peer_lifecycle_state` + +| 值 | 含义 | +|----|------| +| `DLT_PEER_LIFECYCLE_CONNECTING` (0) | TCP 连接进行中(5 秒超时) | +| `DLT_PEER_LIFECYCLE_HANDSHAKING` (1) | Hello 交换进行中(10 秒超时) | +| `DLT_PEER_LIFECYCLE_SYNCING` (2) | 交换同步区块(`we_need_sync_items` 或对端需要我们的区块) | +| `DLT_PEER_LIFECYCLE_ACTIVE` (3) | 完全同步;正常区块/交易交换 | +| `DLT_PEER_LIFECYCLE_DISCONNECTED` (4) | 未连接;退避后可重连 | +| `DLT_PEER_LIFECYCLE_BANNED` (5) | 软封禁;封禁到期前不重连 | + +--- + +## 详细消息参考 + +### 5100 — `dlt_hello_message` + +建立 TCP 连接后立即发送。携带发起节点的完整链状态和能力。 + +```cpp +struct dlt_hello_message { + uint16_t protocol_version; // 当前为 1 + block_id_type head_block_id; + uint32_t head_block_num; + block_id_type lib_block_id; + uint32_t lib_block_num; + uint32_t dlt_earliest_block; // 我们滚动 DLT 日志中最旧的区块(无则为 0) + uint32_t dlt_latest_block; // 我们 DLT 日志中最新的区块 + bool emergency_active; // 紧急共识当前是否激活 + bool has_emergency_key; // 我们是否持有紧急委员会私钥 + uint8_t fork_status; // dlt_fork_status 枚举 + uint8_t node_status; // dlt_node_status 枚举(SYNC 或 FORWARD) +}; +``` + +**注意事项:** +- `dlt_earliest_block` 对于 DLT 范围感知的 fork 对齐至关重要。接收方使用它来避免请求不在发起方滚动窗口中的区块。 +- `has_emergency_key` 标识紧急主节点。在紧急共识模式下,其他节点可优先从此对端同步。 + +--- + +### 5101 — `dlt_hello_reply_message` + +接收方响应 5100 发送。完成握手。 + +```cpp +struct dlt_hello_reply_message { + bool exchange_enabled; // 如果认为发起方已追上则为 true + bool fork_alignment; // 如果发起方在同一 fork 上则为 true + block_id_type initiator_head_seen; // 回显:我们看到的发起方 head_block_id + block_id_type initiator_lib_seen; // 回显:我们看到的发起方 lib_block_id + uint32_t our_dlt_earliest; // 我们最早的 DLT 区块 + uint32_t our_dlt_latest; // 我们最新的 DLT 区块 + uint8_t our_fork_status; // dlt_fork_status 枚举 + uint8_t our_node_status; // dlt_node_status 枚举 +}; +``` + +**Fork 对齐检查分多级**以处理 DLT 裁剪的区块范围: + +| 情况 | 执行的检查 | +|------|-----------| +| 对端无区块(`head_num == 0`) | → 对齐 | +| 对端 head 在我们的 DLT 范围内 | `is_block_known(peer.head_block_id)` | +| 对端 head + 1 == 我们的最早区块 | 读取 `our_earliest_block.previous == peer.head_block_id` | +| 回退 | `is_block_known(peer.lib_block_id)` | + +**`exchange_enabled`** 当接收方的 fork_db 包含发起方的 head 区块时为 `true`(即发起方在交换窗口内且在同一 fork 上)。只有启用交换的对端才能接收区块和交易广播。 + +--- + +### 5102 — `dlt_range_request_message` + +询问对端是否有特定编号和/或 ID 的区块。 + +```cpp +struct dlt_range_request_message { + uint32_t block_num; + block_id_type block_id; // 被询问区块的哈希 +}; +``` + +--- + +### 5103 — `dlt_range_reply_message` + +5102 的响应。返回对端可用的服务范围。 + +```cpp +struct dlt_range_reply_message { + uint32_t range_start; // 对端能服务的最早区块 + uint32_t range_end; // 对端能服务的最新区块 + bool has_blocks; // 如果对端完全没有区块则为 false +}; +``` + +--- + +### 5104 — `dlt_get_block_range_message` + +SYNC 模式下请求连续区块范围。每次请求最多 200 个区块。 + +```cpp +struct dlt_get_block_range_message { + uint32_t start_block_num; + uint32_t end_block_num; + block_id_type prev_block_id; // 区块 (start_block_num - 1) 的哈希;用于验证链连续性 +}; +``` + +**注意事项:** +- 服务对端在发送前验证 `blocks[0].previous == prev_block_id`。 +- `start_block_num` 与服务对端 `dlt_earliest_block` 之间的间隙可能需要桥接对端(参见[P2P 概述 — 间隙检测](./overview.md#sync-mode))。 + +--- + +### 5105 — `dlt_block_range_reply_message` + +5104 的响应。包含最多 200 个区块。 + +```cpp +struct dlt_block_range_reply_message { + std::vector blocks; + uint32_t last_block_next_available; // 此批次后下一个可用区块 + bool is_last; // 如果此对端没有更多区块则为 true +}; +``` + +**`is_last = true`** 在接收方节点追上后触发 `transition_to_forward()`。 + +--- + +### 5106 — `dlt_get_block_message` + +按编号请求单个区块。 + +```cpp +struct dlt_get_block_message { + uint32_t block_num; + block_id_type prev_block_id; // (block_num - 1) 的哈希,用于链接验证 +}; +``` + +--- + +### 5107 — `dlt_block_reply_message` + +5106 的响应。 + +```cpp +struct dlt_block_reply_message { + signed_block block; + uint32_t next_available; // 对端能服务的下一个区块编号(在 head 时为 0) + bool is_last; // 如果这是对端的 head 区块则为 true +}; +``` + +--- + +### 5108 — `dlt_not_available_message` + +当对端无法服务请求的区块时发送(区块在 DLT 日志范围外,或区块未知)。 + +```cpp +struct dlt_not_available_message { + uint32_t block_num; +}; +``` + +请求节点应寻找范围覆盖该区块的另一对端,或触发间隙填补/SYNC 模式。 + +--- + +### 5109 — `dlt_fork_status_message` + +实时链状态更新。当节点的 head、LIB、DLT 窗口或 fork 状态发生变化时发送,以及在 SYNC → FORWARD 转换时发送。 + +```cpp +struct dlt_fork_status_message { + uint8_t fork_status; // dlt_fork_status 枚举 + block_id_type head_block_id; + uint32_t head_block_num; + block_id_type lib_block_id; + uint32_t lib_block_num; + uint32_t dlt_earliest_block; + uint32_t dlt_latest_block; + uint8_t node_status; // dlt_node_status 枚举 +}; +``` + +**主要用例:** 当节点从 SYNC 转换为 FORWARD 时,向所有连接对端广播此消息,使它们能立即重新评估此节点的 `exchange_enabled`,而无需等待下次 hello 周期。 + +接收节点更新发送方的本地 `dlt_peer_state` 并重新检查 fork 对齐和交换资格。 + +--- + +### 5110 — `dlt_peer_exchange_request` + +请求已知对端列表的空消息。 + +```cpp +struct dlt_peer_exchange_request { + // 空 +}; +``` + +速率限制:每个对端每 **5 分钟滑动窗口 3 次请求**。违规者收到 `dlt_peer_exchange_rate_limited`(5112)。 + +--- + +### 5111 — `dlt_peer_exchange_reply` + +5110 的响应。包含已知对端的端点信息。 + +```cpp +struct dlt_peer_endpoint_info { + fc::ip::endpoint endpoint; + node_id_t node_id; +}; + +struct dlt_peer_exchange_reply { + std::vector peers; +}; +``` + +**纳入回复前应用的过滤器:** +- 对端正常运行时间 ≥ `dlt-peer-exchange-min-uptime-sec`(默认 600 秒) +- 每个 /24 子网最多 `dlt-peer-exchange-max-per-subnet`(默认 2)个对端 +- 排除 `is_incoming` 对端(临时源端口) +- 每次回复最多 `dlt-peer-exchange-max-per-reply`(默认 10)个对端 + +--- + +### 5112 — `dlt_peer_exchange_rate_limited` + +当超出请求速率限制时,代替 5111 发送。 + +```cpp +struct dlt_peer_exchange_rate_limited { + uint32_t wait_seconds; // 请求方重试前应等待的时间 +}; +``` + +--- + +### 5113 — `dlt_transaction_message` + +携带用于内存池传播的已签名交易。 + +```cpp +struct dlt_transaction_message { + signed_transaction trx; +}; +``` + +收到的交易在推送到链的 `_pending_tx` 前被添加到 P2P 内存池(按到期时间、TaPoS、大小过滤)。成功接受的交易被转发给所有启用交换的对端。 + +--- + +### 5114 — `dlt_soft_ban_message` + +因垃圾消息或协议违规即将关闭连接前发送给对端。接收对端进入 BANNED 状态持续 `ban_duration_sec`,封禁到期前不会尝试重连。 + +```cpp +struct dlt_soft_ban_message { + uint32_t ban_duration_sec; // 封禁持续时间(秒) + std::string reason; // 人类可读的原因 +}; +``` + +常见记录原因(双方均以橙色/黄色标记): +- `"spam_strikes_exceeded"` — 10 次无效数据包计数 +- `"dead_fork_blocks"` — 对端反复发送来自死亡 fork 的区块 +- `"protocol_violation"` — 意外的消息类型或格式错误的数据 + +--- + +### 5115 — `dlt_gap_fill_request` + +请求特定区块以填补节点区块流中检测到的间隙。在 SYNC 和 FORWARD 模式下均可工作。 + +```cpp +struct dlt_gap_fill_request { + std::vector block_nums; // 请求的特定区块编号 +}; +``` + +**约束:** +- 每次请求最多 **100 个区块**(`GAP_FILL_MAX_BLOCKS`)。 +- 同一节点请求之间 **5 秒冷却时间**(`GAP_FILL_COOLDOWN_SEC`)。 +- 较大的间隙以 100 个区块为单位分批请求;后续批次在下一个周期任务循环时触发。 +- 服务对端从其 `dlt_block_log` 读取区块;服务范围外的区块编号返回 5108。 +- 服务接受来自任何活跃对端的请求(不仅限于启用交换的对端)。 + +从三个位置触发: +1. `on_dlt_block_reply()` — 检测到乱序区块 +2. `periodic_task()` — 每 5 秒的主动间隙检查 +3. `resume_block_processing()` — 快照暂停完成后 + +--- + +### 5116 — `dlt_gap_fill_reply` + +5115 的响应。包含请求的区块(如果某些不可用,可能是子集)。 + +```cpp +struct dlt_gap_fill_reply { + std::vector blocks; // 请求的区块;可能是部分 +}; +``` + +--- + +## 握手流程 + +``` +发起方 接收方 + │ │ + │──TCP 连接────────────────────────►│ + │ │ + │ 5100 dlt_hello_message │ + │──────────────────────────────────►│ + │ (head/lib, DLT 范围, │ + │ emergency_active, node_status) │ + │ │ 检查 fork 对齐 + │ │ 设置 exchange_enabled + │ 5101 dlt_hello_reply_message │ + │◄──────────────────────────────────│ + │ (exchange_enabled, fork_alignment, │ + │ 我们的 DLT 范围, our_node_status) │ + │ │ + ├── exchange_enabled = true ──────────┼── 开始 FORWARD 或同步交换 + └── exchange_enabled = false ─────────┴── 发起方进入 SYNC 模式 +``` + +--- + +## 同步模式流程 + +``` +同步中节点 服务对端 + │ │ + │ 5104 dlt_get_block_range │ + │ (start=our_head+1, end=+200, │ + │ prev_block_id=our_head_id) │ + │──────────────────────────────────►│ + │ │ 读取 dlt_block_log + │ 5105 dlt_block_range_reply │ + │◄──────────────────────────────────│ + │ (blocks=[N+1..N+200], is_last) │ + │ │ + │ 应用每个区块 │ + │ 如果 is_last → transition_to_forward│ + │ 否则 → 请求下一批 │ +``` + +如果 `our_head + 1` 与服务对端 `dlt_earliest_block` 之间存在间隙,节点在请求前先搜索桥接对端。 + +--- + +## 转发模式广播 + +``` +区块生产者 对端 A 对端 B(启用交换) + │ │ │ + │ 生产区块 │ │ + │ ─5109 fork_status──────►│ │ + │ (通过转换通知) │ │ + │ │ │ + │ ─区块广播 ───────────────►│(启用交换) │ + │ │ ─区块广播 ─────────────►│ + │ │ │ push_block() + │ │ │ ─5109 fork_status─► 对端 +``` + +只有处于 `DLT_FORK_STATUS_NORMAL` 或 `DLT_FORK_STATUS_LOOKING_RESOLUTION` 状态的启用交换对端才能接收区块广播。 + +--- + +## 间隙填补流程 + +``` +节点(FORWARD,检测到间隙) 对端(拥有间隙区块) + │ │ + │ 5115 dlt_gap_fill_request │ + │ (block_nums=[N+1, N+2, N+3]) │ + │──────────────────────────────────►│ + │ │ 读取 dlt_block_log + │ 5116 dlt_gap_fill_reply │ + │◄──────────────────────────────────│ + │ (blocks=[N+1, N+2, N+3]) │ + │ │ + │ 应用区块 → head 前进 │ +``` + +--- + +## 线路格式 + +每条消息写为: + +``` +[ 4 字节:类型 ID (uint32 LE) ][ 4 字节:载荷长度 (uint32 LE) ][ N 字节:FC 序列化载荷 ] +``` + +读取使用 `fc::tcp_socket::readsome()` / `writesome()`(非阻塞,fiber 让出)。没有加密层 — 同一链上的所有对端共享公共网络身份,消息不进行逐消息签名。 + +--- + +参见:[P2P 概述](./overview.md)、[同步场景](./sync-scenarios.md)、[统计参考](./stats-reference.md)。 diff --git a/@l10n/zh-CN/docs/p2p/overview.md b/@l10n/zh-CN/docs/p2p/overview.md new file mode 100644 index 0000000000..d206298b3e --- /dev/null +++ b/@l10n/zh-CN/docs/p2p/overview.md @@ -0,0 +1,286 @@ +# P2P 网络概述 + +VIZ Ledger 使用自定义 DLT P2P 协议,取代了旧版基于 synopsis 的 graphene 网络层。新设计针对 DLT 模式(基于快照的节点,带滚动 block log)进行了优化,用更简单的基于范围的区块交换替代了复杂的 graphene 祖先 synopsis。 + +--- + +## 架构 + +``` +┌─────────────────────────────────────────────────────────┐ +│ p2p_plugin(AppBase 插件) │ +│ └─ dlt_delegate(实现 dlt_p2p_delegate) │ +│ └─ 桥接链状态:db()、fork_db、block_log │ +├─────────────────────────────────────────────────────────┤ +│ dlt_p2p_node │ +│ ├─ 接受循环(传入 TCP 连接) │ +│ ├─ 周期性任务(5s 滴答:重连、统计、间隔) │ +│ └─ dlt_peer_state × N(每个已连接节点一个) │ +├─────────────────────────────────────────────────────────┤ +│ 线格式:原始 TCP,头部(类型 + 长度)+ 数据 │ +│ 纤程模型:所有 I/O 在一个 fc::thread,协作 │ +└─────────────────────────────────────────────────────────┘ +``` + +### 设计决策 + +| 决策 | 理由 | +|------|------| +| 委托模式 | `dlt_p2p_node` 仅链接 `fc` + `graphene_protocol`。链访问通过 `dlt_p2p_delegate` 暴露,避免循环依赖。 | +| 原始 TCP(无 STCP 加密) | DLT 紧急模式同时切换所有验证者——不需要向后兼容的加密。更简单的线格式。 | +| 协作纤程(fc::thread) | 所有 I/O 使用 `readsome()`/`writesome()`,让出纤程。一个线程上多个节点,无互斥锁。 | +| 独立 P2P mempool | 链的 `_pending_tx` 仅在接受后生效。P2P mempool 在推送到链之前按过期、TaPoS 和大小过滤,减少评估浪费。 | +| 就地插件替换 | 插件名称仍为 `"p2p"`,端口仍为 `2001`/`4243`,公共 API 不变。旧协议和新协议不兼容;双模式会创建隔离的子网。 | + +--- + +## 节点生命周期 + +每个节点连接经历以下状态: + +``` +CONNECTING ──(TCP 已建立)──► HANDSHAKING + 5s 超时 ↓ ↓ 10s 超时 + DISCONNECTED hello/hello_reply + ▲ ↓ + │ SYNCING ──(已追上)──► ACTIVE + │ │ + └──(断开/错误)──────────────────────┘ + │ + BANNED ◄──(spam_strikes ≥ 10)───────┘ +``` + +**超时值:** +- Connecting → DISCONNECTED:**5 秒** +- Handshaking → DISCONNECTED:**10 秒** + +**重连退避:** 30 秒 → 60 秒 → … → 3600 秒,带 ±25% 抖动。稳定连接 >5 分钟后退避重置。无响应 8 小时的节点永久移除。 + +**紧急节点重置:** 如果所有节点都被隔离(零活跃连接)持续 60 秒,`emergency_peer_reset()` 清除所有软封禁并将所有退避重置为初始值,立即尝试重连。 + +--- + +## Hello 握手 + +连接时,发起节点发送包含以下内容的 `dlt_hello_message`: + +- `head_block_num` / `head_block_id` +- `lib_block_num` / `lib_block_id` +- `dlt_earliest_block_num` — 节点滚动 DLT block log 中可用的最旧区块 +- `node_status` — SYNC 或 FORWARD + +接收节点以包含以下内容的 `dlt_hello_reply_message` 响应: + +- `fork_alignment` — 区块是否在同一 fork 上重叠 +- `exchange_enabled` — 响应节点是否认为发送者已追上 + +### Fork 对齐检查(DLT 范围感知) + +由于 DLT 节点修剪旧区块,朴素的 head-ID 比较会错误地将同链节点标记为"不同 fork"。检查是多层的: + +| 情况 | 检查 | +|------|------| +| 节点没有区块(`head_num == 0`) | 已对齐 | +| 节点头在我们的 DLT 范围内 | `is_block_known(peer.head_id)` | +| 节点头 + 1 == 我们最早的区块 | 读取我们最早的区块,验证 `previous == peer.head_id` | +| 回退 | `is_block_known(peer.lib_id)` | + +--- + +## 同步模式 + +每个节点在任何时刻处于以下两种模式之一: + +### SYNC 模式(拉取) + +当节点落后于网络时使用。节点以每次最多 **200 个区块**的范围向节点请求区块: + +``` +我们 节点 + │──dlt_get_block_range──►│ + │◄──dlt_block_range_reply─│ + │ (最多 200 个区块) │ + │──应用每个区块──►chain │ + │ │ + │ (当 is_last=true 时) │ + │──transition_to_forward │ +``` + +**间隔检测:** 如果 `our_head + 1 < peer.dlt_earliest`(缺失的区块不再在节点的滚动日志中),节点搜索另一个能够弥合间隔的节点。如果没有节点能服务该间隔,建议导入快照。 + +**停滞保护:** 如果 30 秒内未收到区块,节点最多重试 3 次,然后带警告转换到 FORWARD 模式。 + +### FORWARD 模式(推送) + +当节点已追上时使用。区块通过 `dlt_block_message` 传播。每个区块广播到所有共享相同 fork 的**启用了交换**的节点。 + +**FORWARD → SYNC 回退:** 如果节点的头在 **30 秒**内没有推进(`check_forward_stagnation`)且至少一个节点领先,节点重新进入 SYNC 模式。 + +### SYNC ↔ FORWARD 转换 + +| 转换 | 触发器 | +|------|-------| +| SYNC → FORWARD | 带 `is_last=true` 的区块范围回复 | +| SYNC → FORWARD | `check_sync_catchup()`:我们的头 ≥ 所有节点 | +| SYNC → FORWARD | 3 次重试后停滞 | +| FORWARD → SYNC | `check_forward_stagnation()`:头部 30s 停滞且节点领先 | +| FORWARD → SYNC | 间隔填充失败且无可用节点 | + +SYNC → FORWARD 时,节点向所有已连接节点广播 `node_status=FORWARD` 的 `dlt_fork_status_message`,使其重新评估该节点的 `exchange_enabled`。 + +--- + +## 间隔填充 + +间隔填充是一种轻量级机制,用于获取少量特定区块而不进入完整 SYNC 模式。使用两种专用消息类型(`dlt_gap_fill_request` / `dlt_gap_fill_reply`),在三个地方触发: + +1. 当乱序区块到达时(`on_dlt_block_reply`) +2. 每 5 秒从 `periodic_task()` 触发 +3. 快照暂停完成后(`resume_block_processing()`) + +**规则:** +- 每次请求最多 **100 个区块**(`GAP_FILL_MAX_BLOCKS`);较大的间隔使用分块请求。 +- 间隔填充请求之间 **5 秒冷却**。 +- 请求节点选择头部区块号最高的活跃节点。 +- 服务节点从其 DLT block log 读取区块;日志范围外的请求被拒绝。 +- SYNCING 生命周期节点是合格候选(不仅限于 ACTIVE)。 +- 如果找不到合适的节点,节点立即转换到 SYNC 模式。 + +--- + +## Mempool + +DLT P2P 层维护自己的 mempool,独立于链的 `_pending_tx`。这允许在将交易推送到链评估器之前进行早期过滤。 + +**准入检查:** +- 按 `tx_id` 重复——收到时去重 +- 过期——拒绝已过期的 +- TaPoS(`tapos_block_num`)——如果引用区块未知则拒绝 +- 大小——如果 `tx.size > dlt-mempool-max-tx-size`(默认 64 KB)则拒绝 +- 到期范围——如果到期时间超过 `dlt-mempool-max-expiration-hours`(默认 24 小时)则拒绝 + +**驱逐:** 当 mempool 超过 `dlt-mempool-max-tx`(默认 10,000)或 `dlt-mempool-max-bytes`(默认 100 MB)时,最近到期的条目首先被驱逐。 + +**生命周期:** +- SYNC 期间接收的交易标记为**临时**,在转换到 FORWARD 时重新验证(TaPoS 区块现在可能已知)。 +- 区块应用时,包含的交易被修剪(`remove_transactions_in_block`)。 +- fork 切换时,TaPoS 无效条目被修剪(`prune_mempool_on_fork_switch`)。 +- `periodic_mempool_cleanup()` 在每个周期删除过期和 TaPoS 无效条目。 + +--- + +## Fork 解决 + +DLT P2P 层以 **42 区块阈值**(2 个完整验证者轮次 = `CHAIN_MAX_WITNESSES × 2`)跟踪 fork 状态。 + +`track_fork_state()` 在每次区块应用后被调用。当检测到持续 ≥ 42 个区块的竞争 fork 时,`resolve_fork()` 通过总投票权重计算**最重的分支**。候选分支必须积累 **6 个连续确认区块**(`dlt_fork_resolution_state::CONFIRMATION_BLOCKS`)才能切换节点(滞后效应)。 + +当前 fork 状态通过 `is_on_majority_fork()` 暴露,验证者插件用它来决定是否生产区块。 + +--- + +## 反垃圾 + +每个节点有一个 **`spam_strikes`** 计数器: + +- 在以下情况递增:无效区块、无效交易、协议违规 +- 在以下情况重置:任何有效数据包 +- 软封禁阈值:**10 次罚分** + +软封禁的节点在连接关闭前收到 `dlt_soft_ban_message`(包含 `ban_duration_sec` 和人类可读的原因)。被封禁的节点进入 BANNED 状态,持续指定时间,到期前不会重连。 + +**按 IP 连接去重**防止来自同一节点的多个连接: +- `accept_loop()` 拒绝来自已有活跃条目的 IP 的传入连接。 +- `connect_to_peer()` 如果目标 IP 已有活跃条目则跳过出站连接。 +- 广播(`send_to_all_our_fork_peers`)跟踪 `set` 并跳过本次广播已发送的 IP。 + +**重复/乱序区块容忍度:** +- 已应用的区块静默跳过(不计为垃圾)。 +- 范围回复中的乱序区块转到 `fork_db` 而不是触发软封禁。 +- 反序列化错误不增加 spam strikes。 +- 来自旧协议节点的超大消息触发断开连接而不增加退避时间。 + +--- + +## 节点交换 + +节点共享节点地址以辅助发现。 + +**速率限制:** 每个节点每 **5 分钟窗口 3 个请求**。 + +**共享节点地址前应用的过滤器:** +- 最小运行时间:**600 秒** +- 子网多样性:每 **/24** 子网最多 **2 个节点** +- 临时端口排除:`is_incoming` 节点从不共享(其端口是临时的) + +**每次回复限制:** `dlt-peer-exchange-max-per-reply`(默认 10)。 + +--- + +## 区块处理暂停/恢复 + +快照插件(及其他需要独占访问的插件)可以通过 `pause_block_processing()` 暂停 P2P 区块摄取。暂停期间: + +- `periodic_task()` 跳过需要数据库读锁的操作:`sync_stagnation_check()`、`periodic_peer_exchange()`、`log_peer_stats()`。 +- 停滞同步和前向停滞计时器被重置,以便节点不进入不必要的模式转换。 +- 无 DB 的日常工作继续:重连、生命周期管理、mempool 清理、封禁节点解封。 + +在 `resume_block_processing()` 时,节点在回退到 SYNC 模式之前尝试间隔填充。 + +--- + +## 配置 + +| 选项 | 默认值 | 描述 | +|------|-------|------| +| `p2p-endpoint` | `0.0.0.0:2001` | 监听地址和端口 | +| `seed-node` | — | 静态种子节点地址 | +| `p2p-max-connections` | — | 最大同时节点连接数 | +| `dlt-block-log-max-blocks` | 100000 | 滚动 DLT block log 容量 | +| `dlt-peer-max-disconnect-hours` | 8 | N 小时后移除无响应节点 | +| `dlt-mempool-max-tx` | 10000 | mempool 条目数硬上限 | +| `dlt-mempool-max-bytes` | 104857600 | mempool 总内存硬上限(100 MB) | +| `dlt-mempool-max-tx-size` | 65536 | 拒绝大于此值的交易(64 KB) | +| `dlt-mempool-max-expiration-hours` | 24 | 拒绝到期时间超过 N 小时的交易 | +| `dlt-peer-exchange-max-per-reply` | 10 | 每次节点交换回复的最大地址数 | +| `dlt-peer-exchange-max-per-subnet` | 2 | 每 /24 子网共享的最大节点数 | +| `dlt-peer-exchange-min-uptime-sec` | 600 | 共享地址前节点的最小运行时间 | +| `dlt-stats-interval-sec` | 300 | 节点统计日志输出间隔(最小 30 秒) | + +--- + +## 节点统计日志 + +每 `dlt-stats-interval-sec`(默认 5 分钟),节点记录节点统计摘要: + +``` +[DLT-P2P] node=FORWARD head=#79274318 lib=#79274297 fork=MAJORITY + peer 192.168.1.10:2001 ACTIVE head=#79274318 exch=YES dlt=[79174319..79274318] strikes=0 + peer 192.168.1.11:2001 SYNCING head=#79274100 exch=no dlt=[79174319..79274100] strikes=0 + peer 192.168.1.12:2001 BANNED ban_remaining=3540s +``` + +字段说明: +- `exch=YES/no` — 是否与此节点启用了区块/交易交换 +- `dlt=[min..max]` — 节点可服务的 DLT block log 范围 +- `strikes` — 当前 spam strike 计数(任何有效数据包重置) +- `ban_remaining` — 软封禁到期前的秒数 + +统计间隔可通过 `set_stats_log_interval()` 在运行时更新。 + +--- + +## 诊断摘要 + +| 症状 | 可能原因 | +|------|---------| +| 节点卡在 SYNC,头部不推进 | 我们的头和节点的 DLT 范围之间存在间隔——节点无法弥合;考虑快照导入 | +| SYNC ↔ FORWARD 快速振荡 | 没有节点领先,或所有节点被隔离——检查 `emergency_peer_reset` 日志条目 | +| 所有节点显示 `exch=no` | FORWARD 转换没有通知节点;应在下一个 `broadcast_chain_status` 周期自行解决 | +| `spam_strikes` 在所有节点上增长 | 可能是 fork 分歧——通过 hello 日志检查 fork 对齐 | +| fork_db 中 `unlinked_size` 增长 | 父区块未到达;间隔填充应在 5 秒内恢复 | +| 统计中 `peer_head_num` 看起来过时 | 预期——`peer_head_num` 是来自最后一次 hello/fork_status 交换的快照,不是实时的 | + +--- + +参见:[消息](./messages.md)、[同步场景](./sync-scenarios.md)、[前向模式](./forward-mode.md)、[统计参考](./stats-reference.md)、[快照](../node/snapshot.md)、[Fork 解决](../consensus/fork-resolution.md)。 diff --git a/@l10n/zh-CN/docs/p2p/stats-reference.md b/@l10n/zh-CN/docs/p2p/stats-reference.md new file mode 100644 index 0000000000..5faeef439e --- /dev/null +++ b/@l10n/zh-CN/docs/p2p/stats-reference.md @@ -0,0 +1,233 @@ +# P2P 统计参考 + +DLT P2P 层为监控输出两条周期性日志行: + +| 日志前缀 | 默认间隔 | 用途 | +|---------|---------|------| +| `DLT Status \|` | ~30 秒 | 紧凑单行,用于 tail/grep 健康监控 | +| `=== DLT P2P Stats \|` | ~120 秒(可通过 `dlt-stats-interval-sec` 配置) | 完整的逐对端详细信息 | + +--- + +## 紧凑状态行 + +``` +DLT Status | FORWARD | head=#79881136 lib=#79881130 | dlt_range=79000000-79881136 | peers=6active/8conn | uptime=2h15m43s | flags=... +``` + +| 字段 | 示例 | 含义 | +|------|------|------| +| 模式 | `FORWARD` | 节点运行模式(`SYNC` 或 `FORWARD`) | +| `head=#N` | `head=#79881136` | 当前 head 区块编号 | +| `lib=#N` | `lib=#79881130` | 最后不可逆区块编号 | +| `dlt_range=A-B` | `dlt_range=79000000-79881136` | 滚动 DLT 区块日志中存储的区块范围 | +| `peers=Xactive/Yconn` | `peers=6active/8conn` | 启用交换的对端 / 总 TCP 连接数 | +| `uptime` | `2h15m43s` | 节点启动以来的时间 | +| `flags` | 各种 | 活跃标志(snapshot、paused、catchup 等) | + +--- + +## 完整统计 — 标题行 + +``` +=== DLT P2P Stats | status=FWD fork=NORMAL head=79881136 lib=79881130 peers=6 conn=4 paused=no uptime=0h20m30s === +``` + +### `status` — 节点运行模式 + +| 值 | 含义 | +|----|------| +| `SYNC` | 追赶中 — 从对端拉取区块;不广播交易 | +| `FWD` | 已追上 — 实时生产和中继区块及交易 | + +**`SYNC` 的原因:** 节点刚启动;在停机期间落后;检测到少数派 fork 并正在重新同步;head 在有领先对端的情况下停滞超过 30 秒。 + +**`FWD` 的原因:** 节点已追上网络 head;所有区块通过实时广播到达。 + +### `fork` — Fork 状态 + +| 值 | 含义 | +|----|------| +| `NORMAL` | 在多数派 fork 上 — 无冲突 | +| `LOOKING` | 检测到竞争 tips;正在比较分支(阈值:42 个区块 = 2 个完整轮次) | +| `MINORITY` | 确认在少数派 fork 上;等待切换 | + +**非 `NORMAL` 的原因:** 两个验证者在同一槽位生产;网络分区将验证者分散到不同 tips;收到替代 fork 区块。 + +### `head` 和 `lib` + +- **`head`** — 当前链 tip 的区块编号 +- **`lib`** — 最后不可逆区块;此级别及以下的区块已最终确定 + +在正常 DLT 运行中,head-to-lib 差距通常为 1–10 个区块。 + +### `peers` 和 `conn` + +- **`peers`** — 对端表中的总对端条目(活跃 + 连接中 + 断开连接,追踪以便重连) +- **`conn`** — 当前活跃 TCP 连接数 + +当 `peers` 显著超过 `conn` 时,节点有断开连接的对端在退避队列中等待。 + +### `paused` + +| 值 | 含义 | +|----|------| +| `no` | 区块处理活跃 | +| `YES` | 区块接收暂时停止(快照导出进行中) | + +暂停期间,P2P 连接正常继续。收到的区块在 fork DB 中排队,恢复时应用。过时同步和转发停滞计时器重置,以避免暂停期间发生虚假模式转换。 + +--- + +## 完整统计 — 逐对端行 + +### 活跃对端 + +``` +62.109.17.82:2001 | ACTIVE | exch=YES | head=79881136 lib=79880729 | range=79869724-79880729 | peer_fork=NORM peer_node=FWD | spam=0 | +align+emrg+sync +``` + +#### 生命周期状态 + +| 标签 | 含义 | +|------|------| +| `CONNECT` | TCP 连接进行中(5 秒超时) | +| `HANDSHAKE` | Hello/hello-reply 交换进行中(10 秒超时) | +| `SYNCING` | 从此对端下载区块范围 | +| `ACTIVE` | 完全运行;交换已建立 | +| `DISC` | 已断开连接;退避后将重连 | +| `BANNED` | 软封禁;封禁到期前不重连 | + +#### `exch` — 交换状态 + +| 值 | 含义 | +|----|------| +| `YES` | 区块和交易交换已启用;双方在同一 fork 上 | +| `no` | 交换已禁用;fork 对齐未确认 | + +**`no` 的原因:** 握手刚完成,fork 对齐尚未验证;对端的 head/LIB 不在我们的 fork DB 中;对端报告 fork 不匹配;SYNC → FORWARD 转换尚未传播。 + +**交换如何变为 `YES`:** +1. Hello 握手:接收方调用 `is_block_known(peer.head_block_id)` — 匹配时在 hello 回复中设置 `exchange_enabled=true`。 +2. 区块接受:当来自此对端的区块应用到我们的链时,交换被启用。 +3. FORWARD 转换:对端广播带 `node_status=FORWARD` 的 `dlt_fork_status_message`,触发 fork 对齐重新检查。 + +#### `head` / `lib`(对端值) + +对端最后报告的 head 和 LIB 区块编号 — 来自最后一次 hello、fork_status 消息或区块中继的**快照**。对端的实际链可能领先这些值,尤其是在快速出块期间的 FORWARD 模式下。 + +#### `range` — DLT 区块日志范围 + +`earliest-latest`:对端滚动 DLT 区块日志中可用的区块编号。 + +如果间隙填补或初始同步所需的区块低于 `earliest`,此对端无法服务这些区块。节点会搜索其范围覆盖缺失区块的另一对端。 + +#### `peer_fork` — 对端自报 fork 状态 + +| 标签 | 含义 | +|------|------| +| `NORM` | 对端报告在多数派 fork 上 | +| `LOOK` | 对端正在解决 fork 冲突 | +| `MINO` | 对端报告在少数派 fork 上 | + +报告 `MINO` 的对端可能正在切换 fork,其 head 可能很快改变。来自它们的区块不应视为规范。 + +#### `peer_node` — 对端运行模式 + +| 标签 | 含义 | +|------|------| +| `SYNC` | 对端正在追赶;不会广播交易 | +| `FWD` | 对端已追上并实时中继区块 | + +#### `spam` — 反垃圾计数器 + +自上次有效数据包以来累积的计数。软封禁在 **10 次**时触发。在任何有效数据包、成功重连或封禁到期时重置。 + +**触发计数:** 反序列化失败;协议违规(当前状态下的意外消息);dead-fork 区块(宽限期后)。 + +**注意:** 范围回复中的重复区块和乱序区块**不**增加计数。 + +#### 标志 + +| 标志 | 含义 | +|------|------| +| `+align` | Fork 对齐已验证 — 来自此对端的区块可干净地应用到我们的链 | +| `+emrg` | 对端报告紧急共识已激活 | +| `+ekey` | 对端持有紧急委员会私钥(紧急主节点候选) | +| `+sync` | 与此对端的区块范围同步待进行或进行中 | + +--- + +### 断开连接的对端 + +``` +138.201.117.201:2001 | DISC | disconnected=74s | backoff=480s | reconnect_in=502s | spam=0 +``` + +| 字段 | 含义 | +|------|------| +| `disconnected` | 连接丢失后的秒数 | +| `backoff` | 当前重连间隔;每次失败翻倍:30 → 60 → 120 → … → 3600 秒 | +| `reconnect_in` | 下次重连尝试前的秒数 | +| `spam` | 上一会话残留的计数 | + +当连接稳定超过 5 分钟时,退避重置为初始值(30 秒)。 + +--- + +### 被封禁的对端 + +``` +1.2.3.4:2001 | BANNED | ban_remaining=1800s | reason=spam strike threshold exceeded +``` + +| 字段 | 含义 | +|------|------| +| `ban_remaining` | 封禁到期前的秒数(默认封禁:3600 秒) | +| `reason` | 在 `dlt_soft_ban_message` 中发送的人类可读封禁原因 | + +到期后条目恢复为 DISCONNECTED,正常退避重连恢复。 + +--- + +## 场景解读 + +| 症状 | 可能原因 | 措施 | +|------|---------|------| +| 所有对端 `exch=no` | 握手刚完成;fork 不匹配;节点在 SYNC 中且对端未识别 | 等待 FORWARD 转换触发重新评估;检查 `fork` 状态 | +| `status=SYNC` 不前进 | 对端 DLT 范围存在间隙;没有可用桥接对端 | 检查对端的 `range`;可能需要导入快照 | +| 多个对端 `peer_fork=MINO` | 网络范围的 fork 分裂 | 观察;协议自动收敛 | +| 断开连接对端的 `backoff` 很高 | 反复连接失败;网络不稳定 | 检查端口 2001 的连通性;高退避是预期的,成功时重置 | +| `paused=YES` 意外出现 | 快照在导出期间卡住或崩溃 | 检查快照插件日志 | +| `fork=LOOKING` 未解决 | Fork 持续 > 42 个区块没有明确多数 | 检查验证者连通性;检查两个 tips 上的链 | +| 一个对端的 `spam` 增长 | 协议不匹配;对端在不兼容 fork 上 | 10 次计数时自动封禁;检查对端软件版本 | +| SYNC ↔ FWD 快速振荡 | 没有领先对端;所有对端在同一 head | 隔离 60 秒后触发 `emergency_peer_reset()`;也检查日志中的 P53 修复 | + +--- + +## 协议常量 + +| 常量 | 值 | 描述 | +|------|----|----| +| `SPAM_STRIKE_THRESHOLD` | 10 | 软封禁前的计数 | +| `BAN_DURATION_SEC` | 3600 | 默认软封禁持续时间(1 小时) | +| `INITIAL_RECONNECT_BACKOFF_SEC` | 30 | 首次重连延迟 | +| `MAX_RECONNECT_BACKOFF_SEC` | 3600 | 最大重连延迟(1 小时) | +| `STABLE_CONNECTION_RESET_SEC` | 300 | 退避重置前的连接持续时间(5 分钟) | +| `PEER_EXCHANGE_MAX_REQUESTS` | 3 | 每滑动窗口最大对端交换请求数 | +| `PEER_EXCHANGE_WINDOW_SEC` | 300 | 对端交换速率限制窗口(5 分钟) | +| `CONNECTING_TIMEOUT` | 5 秒 | TCP 连接超时 | +| `HANDSHAKING_TIMEOUT` | 10 秒 | Hello 交换超时 | +| `PEER_REMOVAL_HOURS` | 8 小时 | 此时间后移除无响应对端 | +| `ISOLATION_RESET_SEC` | 60 | 零活跃对端到触发 `emergency_peer_reset()` 的秒数 | +| `GAP_FILL_MAX_BLOCKS` | 100 | 每次间隙填补请求的最大区块数 | +| `GAP_FILL_COOLDOWN_SEC` | 5 | 间隙填补请求之间的最小间隔 | +| `GAP_FILL_TIMEOUT_SEC` | 15 | 间隙填补进行中标志超时 | +| `FORWARD_STAGNATION_SEC` | 30 | FORWARD 模式下 head 不前进阈值 | +| `SYNC_STAGNATION_SEC` | 30 | SYNC 模式下未收到区块阈值 | +| `FORK_RESOLUTION_BLOCK_THRESHOLD` | 42 | 触发 fork 解决前的区块数(2 × CHAIN_MAX_WITNESSES) | +| `FORK_RESOLUTION_CONFIRMATION_BLOCKS` | 6 | 确认 fork 解决的连续区块数 | + +--- + +参见:[P2P 概述](./overview.md)、[消息](./messages.md)、[同步场景](./sync-scenarios.md)。 diff --git a/@l10n/zh-CN/docs/p2p/sync-scenarios.md b/@l10n/zh-CN/docs/p2p/sync-scenarios.md new file mode 100644 index 0000000000..22e8a8ddc0 --- /dev/null +++ b/@l10n/zh-CN/docs/p2p/sync-scenarios.md @@ -0,0 +1,215 @@ +# P2P 同步场景 + +本页描述 DLT P2P 层如何处理常见同步情况:初始启动、停机后追赶、DLT 范围间隙、fork 恢复和紧急共识。 + +--- + +## 节点分类 + +以下场景使用 4 节点参考配置: + +| 角色 | 描述 | +|------|------| +| **Master** | FORWARD 模式;DLT 区块日志 `[A..B]`;持有紧急私钥 | +| **Slave (NEAR)** | Head 在 `A-1`(恰好毗邻 master 的 DLT 范围) | +| **Slave (FAR)** | Head 显著低于 `A`(不在 master 的 DLT 范围内) | +| **Fresh node** | 无区块;仅 genesis 状态 | + +--- + +## 场景 1:NEAR Slave(head 毗邻 master 的 DLT 范围) + +**配置:** Master DLT 范围 `[1000-2000]`。Slave head = 999。 + +### Hello 握手 + +1. Slave 发送 hello:`head_num=999, head_id=H999`。 +2. Master 的 `check_fork_alignment` — 多级检查: + - `head_num=999` 低于 `dlt_earliest=1000` — 不在范围内。 + - `head_num + 1 == dlt_earliest (1000)` → **边界链接检查**:读取区块 1000,验证 `block_1000.previous == H999`。 + - 匹配 → `fork_alignment = true`,`exchange_enabled = true`。 +3. Master 回复:`exchange_enabled=true, fork_alignment=true`。 +4. Slave 在 master 上进入 **SYNCING** 生命周期状态。 + +### 区块同步 + +Slave 请求 `dlt_get_block_range(start=1000, end=1199, prev=H999)`。Master 从其 DLT 日志响应区块 1000–1199。Slave 应用每个区块。此过程以 200 个区块为批次重复,直到 slave 达到区块 2000 且 `is_last=true` 触发 `transition_to_forward()`。 + +**结果:** 无需快照下载的干净 P2P 同步。无退避惩罚。 + +--- + +## 场景 2:FAR Slave(head 远低于 master 的 DLT 范围) + +**配置:** Master DLT 范围 `[1000-2000]`。Slave head = 800。 + +### Hello 握手 + +1. Slave 发送 hello:`head_num=800, head_id=H800`。 +2. Master 的 fork 对齐检查:`800 < 1000`,边界链接检查失败(`800 + 1 ≠ 1000`),LIB 回退也失败(LIB ID 已裁剪)。 +3. `fork_alignment = false`,但 `exchange_enabled = false`。 +4. Master **不断开** slave,因为 `hello.node_status == SYNC` — SYNC 对端始终进入 ACTIVE 生命周期状态。 + +### 同步尝试 + +Slave 在 master 上进入 ACTIVE 生命周期状态。由于 `exchange_enabled = false`,master 不发送 forward 区块。Slave 尝试区块范围请求:`request_blocks_from_peer` 检测到 `our_head+1 (801) < peer_dlt_earliest (1000)` — **检测到间隙**。 + +节点在所有连接的对端中搜索 DLT 范围覆盖区块 801 的对端。如果找到,该对端用作桥接同步源。如果没有对端能弥补间隙: + +``` +[P2P] Gap detected: our_head=800, nearest_peer_dlt_earliest=1000 + No peer can serve blocks 801-999. Snapshot import may be required. +``` + +约 90 秒无 head 进展后,快照插件的停滞同步检测器触发,从受信对端发起快照下载(通过 `trusted-peer-for-snapshot` 配置)。在区块 1500 导入快照后,slave 重新进入 SYNC 模式并正常追赶。 + +--- + +## 场景 3:Fresh Node(无区块) + +**配置:** 节点无区块;`head_num=0, head_id=zero_id`。 + +### Hello 握手 + +1. 新节点发送 hello:`head_num=0`。 +2. Master 的 fork 检查:`head_num == 0` → **空对端** → `fork_alignment = true`(视为"新节点,尚未在任何 fork 上")。 +3. `exchange_enabled = true`(master 将接受来自此节点的区块)。 +4. 新节点在 master 上进入 ACTIVE 生命周期状态。 + +### 同步尝试 + +在 `request_blocks_from_peer` 中,`our_head=0`,`peer_dlt_latest=2000`。但 `peer_dlt_earliest=1000`,所以最早可用的是区块 1000。请求从 `max(our_head+1, peer_dlt_earliest) = 1000` 开始。节点收到区块 1000+,但无法应用它们,因为链数据库没有区块 1000 之前的状态。 + +快照插件检测到停滞并下载快照(例如在区块 1500)。导入后,新节点从区块 1500 → 2000 正常追赶。 + +--- + +## 场景 4:崩溃后重启 + +**配置:** 节点在 head 1912,DLT 范围 `[1750-1912]`。重启后,对端在区块 2000。 + +### 启动恢复 + +1. `database::open()` 检查 DLT 区块日志一致性:如果日志 head 与数据库 head 匹配 → 一致;否则重置日志。 +2. 来自 DLT 区块日志的最后 **100 个区块**被播种到 `fork_db`(区块 1813–1912)。这为新到达的区块提供 100 个区块的父窗口,无需先获取它们。 +3. **60 秒宽限期**适用:启动后前 60 秒,head 附近 10 个区块内的区块被视为 `FORK_DB_ONLY` 而非 `DEAD_FORK`。这防止了"拒绝级联"——当对端重放 fork_db 尚不知道的接近 head 的区块时。 + +### 追赶 + +节点重新进入 SYNC 模式,从 1913 开始请求区块。DLT 范围 `[1800-2000]` 的对端可以服务所有所需区块。节点追赶到 2000 并过渡到 FORWARD。 + +--- + +## 场景 5:Fork 切换 + +**配置:** 节点在 fork A 的 head `H`。对端有 fork B 的 head `H'`,其中 `H' > H` 且 fork B 有更多投票权重。 + +### Fork 检测 + +1. 来自 fork B 的区块通过广播到达。Fork DB 将其链接到其父链。 +2. 每个区块后调用 `track_fork_state()`。当 fork B 维持其领先 **42 个区块**(2 个完整的验证者轮次)时,运行 `resolve_fork()`。 +3. `resolve_fork()` 计算每个分支上验证者的总投票权重(委托的 SHARES)。Fork B 必须在切换提交前维持 6 个连续区块的确认。 + +### Fork 切换执行 + +1. `pop_block()` 将 fork A 的区块回滚到公共祖先。弹出的交易进入 `_popped_tx`。 +2. Fork B 的区块从公共祖先应用到新 head。 +3. `_popped_tx` 和 `_pending_tx` 被重新应用;已在 fork B 链中的交易静默跳过。 + +**统计中的 fork 状态:** 转换 `NORMAL → LOOKING → NORMAL`(如果此节点在失败分支上则为 `MINORITY`)。 + +--- + +## 场景 6:紧急共识同步 + +**配置:** 网络停滞超过 3600 秒。紧急共识已激活。 + +### Master 运行 + +紧急 master(配置中有 `emergency-private-key` 的节点)使用"committee"签名密钥每轮生产所有 21 个区块。统计中:`+emrg +ekey`。 + +### 紧急期间的 Slave 同步 + +1. Slave 连接到 master。Master 的 hello 包含 `emergency_active=true, has_emergency_key=true`。 +2. Slave 的 fork 对齐检查正常进行 — 从 P2P 层的角度来看,committee 区块是常规签名区块。 +3. Slave 进入 SYNC 模式,从 master 请求 committee 生产的区块。 +4. 区块验证:`verify_signing_witness()` 在紧急期间放宽槽位生产者映射检查 — 如果区块生产者与确切的计划槽位不匹配,只要签名根据生产者的 `signing_key` 验证通过,就被接受。 + +### 验证者密钥恢复 + +当真实验证者恢复其签名密钥(通过 `validator_update_operation`)时,调度重建将它们纳入混合调度。一旦 21 个验证者槽位中有 **15 个**是真实的(非 committee),紧急模式停用。后续区块由真实验证者生产并正常同步。 + +--- + +## 场景 7:停滞同步恢复 + +**条件:** SYNC 模式,30 秒内未收到区块。 + +1. `sync_stagnation_check()` 触发:第 1/3 次重试 — 重新从所有启用交换的活跃对端请求区块。 +2. 30 秒后:第 2/3 次重试。 +3. 30 秒后:第 3/3 次重试。 +4. 第三次重试后:带停滞警告的 `transition_to_forward()`。 + +如果节点在过渡到 FORWARD 时仍然落后,`check_forward_stagnation()` 将在 30 秒后检测到无 head 进展并过渡回 SYNC 模式,开始新循环。 + +--- + +## 场景 8:Gap Fill(间隙填补) + +**条件:** FORWARD 模式;区块流中缺少 1–100 个区块。 + +Gap fill 在以下情况自动触发: +- 收到乱序区块(区块 N+2 在 N+1 之前到达)。 +- `periodic_task()` 检测到 `highest_seen_block_num > our_head + 1`。 +- 快照暂停后调用 `resume_block_processing()`。 + +**协议:** +1. 选择活跃对端中 `peer_head_num` 最高的对端。 +2. 发送 `dlt_gap_fill_request(block_nums=[N+1, N+2, ...])`(最多 100 个区块)。 +3. 等待回复最多 **15 秒**。 +4. 收到后应用返回的区块。如果区块仍然缺失,在下一个周期任务中触发另一次 gap fill。 + +**如果没有对端能服务间隙**(没有有更高 head 的 exchange-enabled 或 SYNCING 对端),节点立即过渡到 SYNC 模式。 + +--- + +## 场景 9:SYNC ↔ FORWARD 振荡防止 + +**振荡的根本原因:** 从 FORWARD→SYNC 过渡后,同步停滞计时器继承了一个过时的时间戳,立即触发,`check_sync_catchup` 看到零个前方对端 → 过渡回 FORWARD。循环继续。 + +**已实施的修复:** +- `transition_to_sync()` 将 `_last_block_received_time` 重置为 `now`,使停滞计时器重新开始。 +- `check_forward_stagnation()` 当所有连接对端与我们节点 head 相同时**不**过渡到 SYNC — 当没有人在前面时没有必要同步。 +- `check_sync_catchup()` 当零个活跃对端存在时**不**声称"已追上";而是启动 60 秒隔离计时器。 +- 60 秒隔离后,`emergency_peer_reset()` 清除所有软封禁和退避,强制立即重连所有已知对端。 + +--- + +## 场景 10:Dead Fork 区块 + +**条件:** 对端发送来自在节点 fork DB 窗口之前分叉的链的区块。`push_block()` 抛出 `unlinkable_block_exception`,区块编号 ≤ `head_block_num`。 + +**行为:** +1. `dlt_delegate::accept_block()` 返回 `DEAD_FORK`。 +2. 区块**不**存储在 `fork_db._unlinked_index` 中(防止内存增长)。 +3. 对端每个 dead-fork 区块累积一次 spam 计数。 +4. 10 次计数后对端被软封禁 3600 秒。 +5. 同步循环中断 — 当前批次中不再处理来自此对端的区块。 + +**宽限期(P22 修复):** 节点启动后前 60 秒,当前 head 附近 10 个区块内以 `unlinkable_block_exception` 失败的区块返回为 `FORK_DB_ONLY`(而非 `DEAD_FORK`)。这防止了在 fork_db 从最后 100 个区块种子完全重建之前,对发送接近 head 区块的合法对端的误封禁。 + +--- + +## 与同步相关的配置 + +| 设置 | 默认值 | 效果 | +|------|--------|------| +| `seed-node` | — | 静态对端;`emergency_peer_reset()` 后重连 | +| `dlt-block-log-max-blocks` | 100000 | DLT 日志容量;影响对端可向后弥合间隙的程度 | +| `trusted-peer-for-snapshot` | — | 接受快照下载的对端 | +| `stalled-sync-timeout-minutes` | 2 | 快照插件触发恢复前的分钟数 | +| `enable-stale-production` | false | 允许验证者在未同步时生产(仅开发用) | + +--- + +参见:[P2P 概述](./overview.md)、[转发模式](./forward-mode.md)、[紧急共识](../consensus/emergency-consensus.md)、[快照](../node/snapshot.md)。 diff --git a/@l10n/zh-CN/docs/plugins/chain.md b/@l10n/zh-CN/docs/plugins/chain.md new file mode 100644 index 0000000000..b7397a3eb8 --- /dev/null +++ b/@l10n/zh-CN/docs/plugins/chain.md @@ -0,0 +1,125 @@ +# 链插件 + +链插件是每个 VIZ Ledger 节点的核心组件。它管理 chainbase 共享内存数据库,接受并验证区块和交易,维护 fork 数据库和区块日志状态,并协调与快照和 P2P 插件的启动。所有其他插件都依赖它。 + +**源码:** [plugins/chain/plugin.cpp](../../plugins/chain/plugin.cpp) + +--- + +## 依赖 + +``` +json_rpc::plugin +``` + +链插件必须是第一个初始化的域插件;`json_rpc` 是它唯一的正式依赖,由 AppBase 框架首先加载。 + +--- + +## 配置 + +### 仅 CLI 标志 + +这些是一次性恢复或维护操作;它们使节点在启动时执行特定操作,不能在 `config.ini` 中设置。 + +| 标志 | 描述 | +|------|------| +| `--replay-blockchain` | 清除 chainbase 共享内存并从区块 1 重放完整的区块日志。 | +| `--force-replay-blockchain` | 与 `--replay-blockchain` 相同,但跳过损坏检查。当区块日志完整但 chainbase 不可读时使用。 | +| `--replay-from-snapshot ` | DLT 节点的崩溃恢复:清除共享内存,导入快照,然后重放 DLT 滚动区块日志。参见[快照插件](./snapshot.md)。 | +| `--snapshot-auto-latest` | 与 `--replay-from-snapshot` 一起:自动发现 `snapshot-dir` 中的最新快照,而不是手动指定路径。 | +| `--auto-recover-from-snapshot` | 默认 `true`。当区块处理或生成期间检测到共享内存损坏时,无需重启即可自动运行时恢复。通过 `--no-auto-recover-from-snapshot` 禁用。 | +| `--resync-blockchain` | 清除共享内存和区块日志;从 genesis 或快照重新开始。具有破坏性 — 仅在从完全数据损失中恢复时使用。 | +| `--check-locks` | 验证锁排序(仅开发用)。 | +| `--validate-database-invariants` | 在每个区块上运行数据库一致性检查(非常慢;仅开发用)。 | + +### 配置文件选项 + +#### 共享内存 + +| 选项 | 默认值 | 描述 | +|------|--------|------| +| `shared-file-dir` | `state` | 共享内存文件的目录(绝对路径,或相对于数据目录)。 | +| `shared-file-size` | `2G` | 初始共享内存大小。根据链龄和对象数量,生产节点使用 `4G`–`16G`。 | +| `inc-shared-file-size` | `2G` | 当可用空间低于最小阈值时的增长增量。 | +| `min-free-shared-file-size` | `500M` | 当可用共享内存低于此值时自动增长。 | +| `block-num-check-free-size` | `1000` | 每 N 个区块检查一次可用空间。 | +| `flush-state-interval` | `10000` | 每 N 个区块强制完整刷新到磁盘。较高的值以不干净关闭后需要重放更多数据为代价提高吞吐量。 | + +#### 区块日志和 DLT + +| 选项 | 默认值 | 描述 | +|------|--------|------| +| `dlt-block-log-max-blocks` | `100000` | 在 DLT 滚动区块日志(`dlt_block_log.log`)中保留的最近区块数。仅在 DLT 模式下有效(快照导入后)。设为 `0` 禁用。 | +| `checkpoint` | — | 重放期间必须匹配的区块编号/区块 ID 对;可多次指定。 | + +#### 性能 + +| 选项 | 默认值 | 描述 | +|------|--------|------| +| `single-write-thread` | `false` | 通过专用 io_service 线程路由所有写操作。在高并发下提高一致性;略微降低吞吐量。 | +| `skip-virtual-ops` | `false` | 跳过虚拟操作处理。减少内存使用;破坏索引虚拟操作的插件(`account_history`、`operation_history`)。 | +| `enable-plugins-on-push-transaction` | `false` | 当交易进入待处理池时通知观察者插件(区块应用之前)。 | +| `read-wait-micro` | *(db 默认)* | 读锁超时(微秒)。 | +| `max-read-wait-retries` | *(db 默认)* | 读锁超时变为致命前的重试次数。 | +| `write-wait-micro` | *(db 默认)* | 写锁超时(微秒)。 | +| `max-write-wait-retries` | *(db 默认)* | 写锁超时变为致命前的重试次数。 | + +--- + +## 启动顺序 + +``` +plugin_initialize() ← 解析 CLI 和配置选项;验证快照路径 +plugin_startup() ← 打开或创建数据库 + ├─ --resync → 清除共享内存 + 区块日志;初始化 genesis + ├─ --replay → 清除共享内存;从区块日志重放 + ├─ --snapshot → 导入快照;启动 DLT 模式 + ├─ --replay-from-snapshot → 导入快照;重放 dlt_block_log + └─ 正常重启 → 打开现有共享内存;修订不匹配时重放 +emit on_sync() ← P2P 和验证者插件激活 +``` + +所有快照加载在 `plugin_startup()` 内发生,在 P2P 或验证者看到数据库之前。 + +--- + +## 区块接受 + +`chain::plugin::accept_block()` 是所有传入区块(来自 P2P 和验证者)的入口点。它: + +1. 验证区块时间戳不会太超前于未来。 +2. 在写锁下调用 `database::push_block()`。 +3. 更新 fork 数据库和区块日志。 +4. 向所有订阅者插件发出 `applied_block` 信号。 +5. 在 `shared_memory_corruption_exception` 时,如果启用了自动恢复,则调用 `attempt_auto_recovery()`。 + +交易接受(`accept_transaction()`)通过 `database::push_transaction()` 遵循相同路径。 + +--- + +## 共享内存 + +chainbase 数据库存在于 `shared-file-dir` 中的单个内存映射文件(`shared_memory.bin`)中。关键大小指导: + +- 对于从近期快照加载的节点,从 `shared-file-size = 4G` 开始。 +- 当可用空间低于 `min-free-shared-file-size` 时,数据库自动增长 `inc-shared-file-size`。 +- 干净关闭后,文件缩小到实际使用大小。 +- 崩溃后,使用 `--replay-blockchain` 或 `--replay-from-snapshot` 重建一致状态。 + +--- + +## 故障排除 + +| 症状 | 操作 | +|------|------| +| 启动时 `FC_ASSERT` 或 `database_revision_exception` | 修订不匹配 — 运行 `--replay-blockchain` | +| Chainbase 打开因损坏错误而失败 | 运行 `--replay-from-snapshot --snapshot-auto-latest`(DLT 节点)或 `--replay-blockchain`(完整节点) | +| `--resync-blockchain` 后节点卡在 genesis | 区块日志也被清除;提供 `--snapshot` 从快照加载状态 | +| 共享内存无限增长 | 检查 `inc-shared-file-size` 和 `min-free-shared-file-size` 设置;验证链正常应用区块 | +| `write lock timeout` 错误 | 另一个进程持有写锁;检查过时的 `vizd` 进程 | +| 自动恢复反复触发 | 底层存储可能有硬件故障;检查磁盘健康状况;同时验证 `snapshot-every-n-blocks` 已配置,以便存在新鲜快照 | + +--- + +参见:[快照插件](./snapshot.md)、[验证者插件](./validator.md)、[P2P 概述](../p2p/overview.md)、[区块处理](../consensus/block-processing.md)。 diff --git a/@l10n/zh-CN/docs/plugins/database-api.md b/@l10n/zh-CN/docs/plugins/database-api.md new file mode 100644 index 0000000000..004ddd10e7 --- /dev/null +++ b/@l10n/zh-CN/docs/plugins/database-api.md @@ -0,0 +1,408 @@ +# Database API + +`database_api` 插件提供只读 JSON-RPC 方法,用于查询区块链状态:区块、账户、链属性、委托、权限验证和治理。 + +**源码:** [plugins/database_api/](../../plugins/database_api/) + +--- + +## 依赖 + +``` +json_rpc::plugin, chain::plugin +``` + +--- + +## 请求格式 + +所有方法通过 HTTP POST 或 WebSocket 使用 JSON-RPC 2.0: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "method": "database_api.", + "params": [, , ...] +} +``` + +--- + +## 区块和交易 + +### `get_block_header(block_num)` + +返回给定区块编号的区块头,如果未找到则返回 `null`。 + +```json +{ "method": "database_api.get_block_header", "params": [12345678] } +``` + +**返回:** `block_header` — `previous`、`timestamp`、`witness`、`transaction_merkle_root`、`extensions`。 + +--- + +### `get_block(block_num)` + +返回包含所有交易的完整签名区块。 + +```json +{ "method": "database_api.get_block", "params": [12345678] } +``` + +**返回:** `signed_block` — 所有头字段加上包含完整操作数据的 `transactions[]`,以及 `block_id`、`signing_key`、`transaction_ids[]`。 + +--- + +### `get_irreversible_block_header(block_num)` + +与 `get_block_header` 相同,但仅当区块达到 LIB(不可逆)时才返回。 + +--- + +### `get_irreversible_block(block_num)` + +与 `get_block` 相同,但仅当区块达到 LIB 时才返回。 + +--- + +### `set_block_applied_callback(callback)` + +注册 WebSocket 回调,在每个应用的区块上收到通知。回调接收区块头作为 JSON 变体。 + +**仅 WebSocket。** 通过 `cancel_all_subscriptions` 取消订阅。 + +--- + +## 链全局参数 + +### `get_config()` + +返回编译时常量:链 ID、代币符号、区块间隔、最大区块大小、投票周期和所有 `CHAIN_*` 常量。 + +```json +{ "method": "database_api.get_config", "params": [] } +``` + +--- + +### `get_dynamic_global_properties()` + +返回实时链状态:当前 head 区块编号和 ID、时间、当前验证者、总 vesting shares、参与率、DPO 基金余额等。 + +```json +{ "method": "database_api.get_dynamic_global_properties", "params": [] } +``` + +关键字段:`head_block_number`、`head_block_id`、`time`、`current_witness`、`total_vesting_shares`、`total_vesting_fund_viz`、`committee_fund`、`last_irreversible_block_num`、`participation_count`。 + +--- + +### `get_chain_properties()` + +返回当前链上治理参数(通过 `chain_properties_update_operation` 设置):最低账户创建费、最大区块大小、VIZ 账户创建费、带宽预留百分比和奖励参数。 + +```json +{ "method": "database_api.get_chain_properties", "params": [] } +``` + +--- + +### `get_hardfork_version()` + +返回当前活跃硬分叉的版本字符串(例如 `"1.0.0"`)。 + +```json +{ "method": "database_api.get_hardfork_version", "params": [] } +``` + +--- + +### `get_next_scheduled_hardfork()` + +返回下一个待定硬分叉的版本和计划激活时间。 + +```json +{ "method": "database_api.get_next_scheduled_hardfork", "params": [] } +``` + +**返回:** `{ "hf_version": "1.0.0", "live_time": "2025-01-01T00:00:00" }` + +--- + +## 账户 + +### `get_accounts(account_names)` + +返回给定账户名列表的完整账户对象。 + +```json +{ "method": "database_api.get_accounts", "params": [["alice", "bob"]] } +``` + +**返回:** `account_api_object` 数组 — 名称、余额、vesting shares、收到的 vesting、委托的 vesting、密钥、恢复账户、创建时间、帖子数、投票权等。 + +--- + +### `lookup_account_names(account_names)` + +与 `get_accounts` 相同,但对不存在的账户返回 `null`。 + +```json +{ "method": "database_api.lookup_account_names", "params": [["alice", "nonexistent"]] } +``` + +**返回:** `optional` 数组 — 缺失账户为 `null`。 + +--- + +### `lookup_accounts(lower_bound_name, limit)` + +返回从 `lower_bound_name` 开始的账户名集合,最多 `limit` 个结果(最大 1000)。用于分页账户枚举。 + +```json +{ "method": "database_api.lookup_accounts", "params": ["alice", 100] } +``` + +**返回:** 账户名字符串集合。 + +--- + +### `get_account_count()` + +返回已注册账户的总数。 + +```json +{ "method": "database_api.get_account_count", "params": [] } +``` + +--- + +## 账户状态 + +### `get_master_history(account)` + +返回给定账户的主权限变更历史。 + +```json +{ "method": "database_api.get_master_history", "params": ["alice"] } +``` + +**返回:** `master_authority_history_api_object` 数组 — `account`、`previous_master_authority`、`last_valid_time`。 + +--- + +### `get_recovery_request(account)` + +返回给定账户的待处理账户恢复请求(如有)。 + +```json +{ "method": "database_api.get_recovery_request", "params": ["alice"] } +``` + +**返回:** `optional` — `account_to_recover`、`new_master_authority`、`expires`。 + +--- + +### `get_escrow(from, escrow_id)` + +返回给定发送者和托管 ID 的托管转账对象。 + +```json +{ "method": "database_api.get_escrow", "params": ["alice", 1] } +``` + +**返回:** `optional` — 所有托管字段,包括 `from`、`to`、`agent`、`ratification_deadline`、`escrow_expiration`、金额和批准状态。 + +--- + +### `get_withdraw_routes(account, type)` + +返回账户的 vesting 提取路由。`type` 为 `"incoming"`、`"outgoing"` 或 `"all"` 之一。 + +```json +{ "method": "database_api.get_withdraw_routes", "params": ["alice", "outgoing"] } +``` + +**返回:** `{ "from_account", "to_account", "percent", "auto_vest" }` 数组。 + +--- + +### `get_vesting_delegations(account, from, limit, type)` + +返回账户的 vesting 委托。`type` 为 `"delegated"`(此账户委托出去的)或 `"received"`(收到的委托)。 + +```json +{ "method": "database_api.get_vesting_delegations", "params": ["alice", "", 100, "delegated"] } +``` + +**返回:** `vesting_delegation_api_object` 数组 — `delegator`、`delegatee`、`vesting_shares`、`min_delegation_time`。 + +--- + +### `get_expiring_vesting_delegations(account, from, limit)` + +返回账户的 vesting 委托到期条目 — 已撤销并等待返还窗口的委托。 + +```json +{ "method": "database_api.get_expiring_vesting_delegations", "params": ["alice", "1970-01-01T00:00:00", 100] } +``` + +**返回:** `vesting_delegation_expiration_api_object` 数组 — `delegator`、`vesting_shares`、`expiration`。 + +--- + +## 权限和交易验证 + +### `get_transaction_hex(trx)` + +返回交易的十六进制编码序列化二进制形式。用于签名和广播。 + +```json +{ "method": "database_api.get_transaction_hex", "params": [{ ...交易对象... }] } +``` + +**返回:** 十六进制字符串。 + +--- + +### `get_required_signatures(trx, available_keys)` + +给定部分签名的交易和签名者可用的公钥集合,返回授权交易必须签名的最小密钥子集。 + +```json +{ + "method": "database_api.get_required_signatures", + "params": [{ ...trx... }, ["VIZ5...", "VIZ6..."]] +} +``` + +**返回:** 公钥字符串集合。 + +--- + +### `get_potential_signatures(trx)` + +返回所有可能签署交易的公钥(跨所有涉及的账户和权限级别)。在调用 `get_required_signatures` 之前,用此方法预过滤钱包的密钥集。 + +```json +{ "method": "database_api.get_potential_signatures", "params": [{ ...trx... }] } +``` + +**返回:** 公钥字符串集合。 + +--- + +### `verify_authority(trx)` + +如果交易具有所有必要的签名则返回 `true`;否则抛出错误,描述缺少什么。 + +```json +{ "method": "database_api.verify_authority", "params": [{ ...signed_trx... }] } +``` + +--- + +### `verify_account_authority(name, signers)` + +如果给定的公钥集合具有足够的权限代表 `name` 行动则返回 `true`。 + +```json +{ "method": "database_api.verify_account_authority", "params": ["alice", ["VIZ5..."]] } +``` + +--- + +## 数据库信息 + +### `get_database_info()` + +返回 chainbase 共享内存使用统计。 + +```json +{ "method": "database_api.get_database_info", "params": [] } +``` + +**返回:** +```json +{ + "total_size": 4294967296, + "free_size": 1073741824, + "reserved_size": 0, + "used_size": 3221225472, + "index_list": [ + { "name": "account_object", "record_count": 52341 }, + ... + ] +} +``` + +--- + +## 治理 + +### `get_proposed_transactions(account, from, limit)` + +返回需要 `account` 批准的治理提案。 + +```json +{ "method": "database_api.get_proposed_transactions", "params": ["alice", 0, 100] } +``` + +**返回:** `proposal_api_object` 数组 — 完整的提案详细信息,包括所需批准、到期时间和操作列表。 + +--- + +## 账户市场 + +### `get_accounts_on_sale(from, limit)` + +返回当前挂牌出售的账户(直接出售,非拍卖)。 + +```json +{ "method": "database_api.get_accounts_on_sale", "params": [0, 100] } +``` + +**返回:** `account_on_sale_api_object` 数组 — `account`、`account_seller`、`account_offer_price`、`account_on_sale_start_time`、`target_buyer`。 + +--- + +### `get_accounts_on_auction(from, limit)` + +返回挂牌拍卖的账户。 + +```json +{ "method": "database_api.get_accounts_on_auction", "params": [0, 100] } +``` + +**返回:** `account_on_sale_api_object` 数组 — 同上,另加 `current_bid`、`current_bidder`、`current_bidder_key`、`last_bid`。 + +--- + +### `get_subaccounts_on_sale(from, limit)` + +返回可出售的账户命名空间注册(子账户创建权限)。 + +```json +{ "method": "database_api.get_subaccounts_on_sale", "params": [0, 100] } +``` + +**返回:** `subaccount_on_sale_api_object` 数组 — `account`、`subaccount_seller`、`subaccount_offer_price`。 + +--- + +## 错误代码 + +| 代码 | 含义 | +|------|------| +| `-32700` | 解析错误 — 无效 JSON | +| `-32600` | 无效请求 — 缺少必填字段 | +| `-32601` | 方法未找到 | +| `-32602` | 无效参数 | +| `-32603` | 内部错误 | +| `-32099` 至 `-32000` | 服务器错误(方法处理器的异常) | + +--- + +参见:[插件概述](./overview.md)、[witness_api 方法](./overview.md#witness_api)、[JSON-RPC API](../api/json-rpc.md)。 diff --git a/@l10n/zh-CN/docs/plugins/overview.md b/@l10n/zh-CN/docs/plugins/overview.md new file mode 100644 index 0000000000..a9d593c611 --- /dev/null +++ b/@l10n/zh-CN/docs/plugins/overview.md @@ -0,0 +1,379 @@ +# 插件概述 + +VIZ Ledger 使用 **AppBase** 插件框架。每个插件都有生命周期(`plugin_initialize` → `plugin_startup` → `plugin_shutdown`),可以注册 JSON-RPC 方法,可以在 chainbase 数据库中存储数据,并可以订阅链信号(如 `applied_block`)。 + +--- + +## 插件类别 + +| 类别 | 描述 | +|------|------| +| **核心** | 任何节点操作所必需 | +| **基础设施** | 网络、Web 服务器、快照 | +| **API** | 为客户端公开 JSON-RPC 端点 | +| **索引** | 将链数据索引到 chainbase 以进行快速查询 | +| **生产** | 区块签名和生产 | +| **外部** | 与外部系统集成(MongoDB) | +| **调试/测试** | 仅用于开发;不用于生产 | + +--- + +## 插件清单 + +### 核心 + +| 插件 | 状态 | 依赖 | JSON-RPC | +|------|------|------|---------| +| `chain` | 必需 | `json_rpc` | — | +| `json_rpc` | 必需 | — | — | + +### 基础设施 + +| 插件 | 状态 | 依赖 | JSON-RPC | +|------|------|------|---------| +| `webserver` | API 必需 | `json_rpc` | — | +| `p2p` | 网络必需 | `chain` | — | +| `snapshot` | 推荐 | `chain` | — | +| `witness_guard` | 验证者推荐 | `chain`, `p2p` | — | + +### API + +| 插件 | 状态 | 依赖 | JSON-RPC | +|------|------|------|---------| +| `database_api` | 活跃 | `json_rpc`, `chain` | 是 | +| `network_broadcast_api` | 活跃 | `json_rpc`, `chain`, `p2p` | 是 | +| `witness_api` | 活跃 | `json_rpc`, `chain` | 是 | +| `account_by_key` | 活跃 | `json_rpc`, `chain` | 是 | +| `account_history` | 活跃 | `json_rpc`, `chain`, `operation_history` | 是 | +| `operation_history` | 活跃 | `json_rpc`, `chain` | 是 | +| `committee_api` | 活跃 | `json_rpc`, `chain` | 是 | +| `invite_api` | 活跃 | `json_rpc`, `chain` | 是 | +| `paid_subscription_api` | 活跃 | `json_rpc`, `chain` | 是 | +| `custom_protocol_api` | 活跃 | `json_rpc`, `chain` | 是 | +| `auth_util` | 活跃 | `json_rpc`, `chain` | 是 | +| `block_info` | 活跃 | `json_rpc`, `chain` | 是 | +| `raw_block` | 活跃 | `json_rpc`, `chain` | 是 | +| `follow` | 已弃用 | `json_rpc`, `chain` | 是 | +| `tags` | 已弃用 | `json_rpc`, `chain`, `follow` | 是 | +| `social_network` | 已弃用 | `json_rpc`, `chain` | 是 | +| `private_message` | 已弃用 | `json_rpc`, `chain` | 是 | + +### 生产 + +| 插件 | 状态 | 依赖 | JSON-RPC | +|------|------|------|---------| +| `validator` | 活跃 | `chain`, `p2p` | — | + +### 外部 + +| 插件 | 状态 | 依赖 | JSON-RPC | +|------|------|------|---------| +| `mongo_db` | 活跃 | `chain` | — | + +### 调试/测试 + +| 插件 | 状态 | 依赖 | JSON-RPC | +|------|------|------|---------| +| `debug_node` | 仅开发 | `chain` | 是 | +| `test_api` | 仅测试 | `json_rpc` | — | + +--- + +## 核心插件 + +### `chain` + +管理 chainbase 数据库,应用区块和交易,并向所有其他插件发出信号。 + +**关键配置选项:** + +| 选项 | 默认值 | 描述 | +|------|-------|------| +| `shared-file-size` | `2G` | 共享内存文件初始大小 | +| `shared-file-dir` | `state` | 共享内存文件目录 | +| `inc-shared-file-size` | `2G` | 空间不足时的增长量 | +| `min-free-shared-file-size` | `500M` | 触发自动增长的阈值 | +| `flush-state-interval` | `10000` | 每 N 个区块强制刷新到磁盘 | +| `skip-virtual-ops` | `false` | 跳过虚拟操作(减少内存) | +| `dlt-block-log-max-blocks` | `100000` | 滚动 DLT 区块日志容量 | + +**关键 CLI 标志:** + +| 标志 | 描述 | +|------|------| +| `--replay-blockchain` | 清除 chainbase 并从区块日志重放 | +| `--force-replay-blockchain` | 同上,忽略损坏检查 | +| `--replay-from-snapshot` | 导入快照然后重放 DLT 区块日志(崩溃恢复) | +| `--auto-recover-from-snapshot` | 启用共享内存损坏时的自动恢复 | +| `--resync-blockchain` | 清除 chainbase 和区块日志;从创世或快照重新开始 | + +--- + +### `json_rpc` + +框架插件;无配置。注册并调度所有 JSON-RPC 方法。必须首先加载。 + +所有 JSON-RPC 请求使用 2.0 格式: +```json +{ + "jsonrpc": "2.0", + "method": "api_name.method_name", + "params": {}, + "id": 1 +} +``` + +--- + +## 基础设施插件 + +### `webserver` + +将请求转发到 `json_rpc` 的 HTTP 和 WebSocket 服务器。包含只读响应缓存。 + +**关键配置选项:** + +| 选项 | 默认值 | 描述 | +|------|-------|------| +| `webserver-http-endpoint` | — | HTTP 监听地址(如 `0.0.0.0:8090`) | +| `webserver-ws-endpoint` | — | WebSocket 监听地址(如 `0.0.0.0:8091`) | +| `webserver-thread-pool-size` | `32` | HTTP/WS 处理工作线程 | +| `webserver-cache-enabled` | `true` | 启用响应缓存 | +| `webserver-cache-size` | `10000` | 最大缓存条目数 | + +缓存键由 `method + params`(非 `id`)派生,防止通过轮换请求 `id` 来绕过。变更方法(`network_broadcast_api.*`、`debug_node.*`)永不缓存。每个新应用区块时缓存清空。 + +完整详情参见 [Web 服务器](./webserver.md)。 + +--- + +### `p2p` + +DLT P2P 网络——区块和交易传播、节点管理、少数派 fork 恢复。 + +**关键配置选项:** + +| 选项 | 默认值 | 描述 | +|------|-------|------| +| `p2p-endpoint` | — | 监听地址(如 `0.0.0.0:2001`) | +| `seed-node` | — | 静态种子节点 | +| `p2p-max-connections` | — | 最大同时连接数 | +| `dlt-block-log-max-blocks` | `100000` | 滚动 DLT 日志容量 | +| `dlt-stats-interval-sec` | `300` | 节点统计日志间隔 | + +完整 P2P 架构参见 [P2P 概述](../p2p/overview.md)。 + +--- + +### `snapshot` + +快照创建、加载和 P2P 快照同步,用于快速引导和崩溃恢复。 + +详情参见[快照](../node/snapshot.md)和[插件:快照](./snapshot.md)。 + +--- + +## API 插件 + +### `database_api` + +主要读取 API。查询区块、交易、账户、链状态、硬分叉版本、委托、提案。 + +完整方法参考参见 [Database API](./database-api.md)。 + +--- + +### `network_broadcast_api` + +提交和广播已签名的交易和区块。 + +| 方法 | 描述 | +|------|------| +| `broadcast_transaction` | 提交交易(异步) | +| `broadcast_transaction_synchronous` | 提交并等待区块包含 | +| `broadcast_transaction_with_callback` | 提交并在包含或过期时回调 | +| `broadcast_block` | 提交已签名区块(仅限验证者) | + +--- + +### `witness_api` + +查询验证者状态:活跃集合、计划、单个验证者、投票排名。 + +| 方法 | 描述 | +|------|------| +| `get_active_witnesses` | 当前 21 验证者活跃集合 | +| `get_witness_schedule` | 完整计划对象 | +| `get_witnesses` | 按数据库 ID 查询验证者 | +| `get_witness_by_account` | 按账户名查询单个验证者 | +| `get_witnesses_by_vote` | 按总投票权重排序的验证者 | +| `get_witnesses_by_counted_vote` | 按计票权重排序的验证者 | +| `get_witness_count` | 已注册验证者总数 | +| `lookup_witness_accounts` | 按前缀列出验证者账户名 | + +--- + +### `account_by_key` + +按公钥反向查找账户。 + +| 方法 | 描述 | +|------|------| +| `get_key_references` | 获取使用给定公钥的账户名 | + +--- + +### `account_history` + +账户操作历史,分页显示。 + +| 方法 | 描述 | +|------|------| +| `get_account_history(account, from, limit)` | 获取操作;`from=-1` 返回最新;每次最多 1000 条 | + +**配置选项:** +- `track-account-range` — 索引的账户名范围(默认:所有账户) +- `history-count-blocks` — 保留 N 个区块的历史 + +--- + +### `operation_history` + +用于区块级和交易查询的所有操作索引。 + +| 方法 | 描述 | +|------|------| +| `get_ops_in_block(block_num, virtual_ops)` | 区块中的操作;`virtual_ops=true` 包含虚拟操作 | +| `get_transaction(tx_id)` | 按 ID 查询交易 | + +**配置选项:** +- `history-whitelist-ops` / `history-blacklist-ops` — 过滤存储的操作类型 +- `history-start-block` — 从此区块号开始索引 +- `history-count-blocks` — 保留 N 个区块的历史 + +--- + +### `committee_api` + +查询委员会工作者请求和投票。 + +| 方法 | 描述 | +|------|------| +| `get_committee_request(id)` | 按 ID 查询请求 | +| `get_committee_request_votes(id)` | 请求上的投票 | +| `get_committee_requests_list(from, limit, status)` | 分页请求列表 | + +--- + +### `invite_api` + +查询活跃邀请码。 + +| 方法 | 描述 | +|------|------| +| `get_invites_list` | 所有邀请 ID | +| `get_invite_by_id(id)` | 按数据库 ID 查询邀请 | +| `get_invite_by_key(pub_key)` | 按公钥查询邀请 | + +--- + +### `paid_subscription_api` + +查询订阅服务和订阅者状态。 + +| 方法 | 描述 | +|------|------| +| `get_paid_subscriptions` | 所有活跃订阅服务 | +| `get_paid_subscription_options(account)` | 账户的订阅配置 | +| `get_paid_subscription_status(subscriber, account)` | 特定订阅的状态 | +| `get_active_paid_subscriptions(subscriber)` | 订阅者的活跃订阅 | +| `get_inactive_paid_subscriptions(subscriber)` | 已过期订阅 | + +--- + +### 已弃用的 API 插件 + +| 插件 | 方法 | 说明 | +|------|------|------| +| `follow` | 关注者/关注、动态、博客、转发 | 仍可用;不建议新集成使用 | +| `tags` | 按标签的热门/最新内容 | 仍可用;不建议新集成使用 | +| `social_network` | 内容、投票、回复 | 封装委员会/邀请查询;仍可用 | +| `private_message` | 加密消息的收件箱/发件箱 | 基于 `custom_operation`;仍可用 | + +--- + +## 生产插件 + +### `validator` + +区块签名和生产。运行 250ms 定时器循环;当下一个槽位分配给已配置的验证者账户时生产区块。 + +**关键配置选项:** + +| 选项 | 默认值 | 描述 | +|------|-------|------| +| `validator` | — | 验证者账户名 | +| `private-key` | — | 签名用 WIF 私钥 | +| `emergency-private-key` | — | 紧急共识签名密钥 | +| `enable-stale-production` | `false` | 链过期时也生产(仅限测试网) | +| `required-participation` | `3300` | 最低参与度(基点,3300 = 33%) | +| `fork-collision-timeout-blocks` | `21` | 冲突时强制生产前的延迟次数 | + +`required-participation` 始终以**基点**表示(0–10000 = 0%–100%)。 + +完整生产时序和 fork 处理详情参见[验证者插件](./validator.md)。 + +--- + +## 推荐插件集 + +### 最小 API 节点 + +```ini +plugin = chain +plugin = json_rpc +plugin = webserver +plugin = p2p +plugin = database_api +plugin = network_broadcast_api +``` + +### 完整 API 节点 + +```ini +plugin = chain +plugin = json_rpc +plugin = webserver +plugin = p2p +plugin = database_api +plugin = network_broadcast_api +plugin = witness_api +plugin = account_by_key +plugin = account_history +plugin = operation_history +plugin = committee_api +plugin = invite_api +plugin = paid_subscription_api +``` + +### 验证者节点 + +```ini +plugin = chain +plugin = p2p +plugin = validator +plugin = json_rpc +plugin = webserver +plugin = database_api +plugin = network_broadcast_api +plugin = witness_api +plugin = snapshot + +snapshot-every-n-blocks = 28800 +snapshot-dir = /data/snapshots +dlt-block-log-max-blocks = 100000 +``` + +--- + +参见:[Chain 插件](./chain.md)、[验证者插件](./validator.md)、[快照插件](./snapshot.md)、[P2P 概述](../p2p/overview.md)、[JSON-RPC API](../api/json-rpc.md)。 diff --git a/@l10n/zh-CN/docs/plugins/snapshot.md b/@l10n/zh-CN/docs/plugins/snapshot.md new file mode 100644 index 0000000000..e56b9bf2dd --- /dev/null +++ b/@l10n/zh-CN/docs/plugins/snapshot.md @@ -0,0 +1,347 @@ +# 快照插件 + +快照插件通过将完整的区块链状态序列化并恢复为 JSON 文件,实现节点的近乎即时启动。节点不需要从区块日志重放数百万个区块,而是加载预构建的快照,并通过 P2P 从快照的区块高度开始同步。 + +**源码:** [plugins/snapshot/snapshot.cpp](../../plugins/snapshot/snapshot.cpp) + +--- + +## 依赖 + +``` +chain::plugin +``` + +--- + +## 配置 + +### 仅 CLI 选项 + +| 选项 | 默认值 | 描述 | +|------|--------|------| +| `--snapshot ` | — | 从快照文件加载状态(DLT 模式)。如果 `shared_memory.bin` 已存在则跳过导入;成功导入后将文件重命名为 `.used`。 | +| `--snapshot-auto-latest` | `false` | 通过文件名中的区块编号自动发现 `snapshot-dir` 中的最新快照。如果同时指定了 `--snapshot` 则被忽略。 | +| `--replay-from-snapshot` | `false` | 崩溃恢复:导入快照,然后从 DLT 滚动区块日志重放区块。始终清除共享内存;**不**将快照文件重命名为 `.used`。需要 `--snapshot` 或 `--snapshot-auto-latest`。 | +| `--auto-recover-from-snapshot` | `true` | 当区块处理或区块生成期间检测到共享内存损坏时,自动运行时恢复 — 无需重启。需要 `plugin = snapshot` 和 `snapshot-dir` 中存在快照。通过 `--no-auto-recover-from-snapshot` 禁用。 | +| `--create-snapshot ` | — | 使用当前数据库状态在给定路径创建快照,然后退出。在 P2P 或验证者激活之前运行。 | +| `--sync-snapshot-from-trusted-peer` | `false` | 当状态为空时,从受信对端下载并加载快照。需要 `trusted-snapshot-peer`。需要显式启用以防止意外状态清除。 | + +### 配置文件选项 + +| 选项 | 默认值 | 描述 | +|------|--------|------| +| `snapshot-at-block` | `0` | 当达到此区块编号时创建快照(0 = 禁用)。 | +| `snapshot-every-n-blocks` | `0` | 每 N 个区块创建一个快照(0 = 禁用)。仅在实时区块上触发 — 初始 P2P 同步期间跳过。 | +| `snapshot-dir` | — | 自动生成快照文件的目录。不存在时自动创建。 | +| `snapshot-max-age-days` | `90` | 创建新快照后删除超过 N 天的旧快照(0 = 禁用)。 | +| `allow-snapshot-serving` | `false` | 启用通过 TCP 向其他节点提供快照服务。 | +| `allow-snapshot-serving-only-trusted` | `false` | 将快照服务限制为仅配置的受信对端。 | +| `snapshot-serve-endpoint` | `0.0.0.0:8092` | 快照服务监听器的 TCP 端点。 | +| `trusted-snapshot-peer` | — | P2P 快照同步的受信对端端点(`IP:port`);可重复。 | + +`dlt-block-log-max-blocks` 选项(在 `chain` 插件配置部分)控制滚动 DLT 区块日志大小,与快照操作密切相关 — 参见下方的 [DLT 滚动区块日志](#dlt-滚动区块日志)。 + +--- + +## 创建快照 + +### 方法 1:一次性(节点已停止) + +停止节点,然后使用 `--create-snapshot` 重启。节点打开现有数据库(如需则从区块日志重放),写入快照,然后在 P2P 或验证者激活之前退出: + +```bash +vizd --create-snapshot /data/snapshots/viz-snapshot.json --plugin snapshot +``` + +### 方法 2:在特定区块(无停机) + +```ini +plugin = snapshot +snapshot-at-block = 5000000 +snapshot-dir = /data/snapshots +``` + +当应用区块 5,000,000 时,节点写入 `/data/snapshots/snapshot-block-5000000.json` 而不中断运行。 + +### 方法 3:周期性(推荐) + +```ini +plugin = snapshot +snapshot-every-n-blocks = 28800 +snapshot-dir = /data/snapshots +snapshot-max-age-days = 90 +``` + +文件命名为 `snapshot-block-.json`。推荐间隔: + +| 间隔 | 区块数 | 时间(3 秒/区块) | +|------|--------|-----------------| +| 频繁 | 10,000 | ~8 小时 | +| 每日 | 28,800 | ~24 小时 | +| 每周 | 100,000 | ~3.5 天 | + +### 方法 4:结合 at-block 和周期性 + +两个选项可以同时设置;`snapshot-at-block` 触发一次,`snapshot-every-n-blocks` 重复触发。 + +### 快照创建工作原理 + +快照创建是异步的,分为两个阶段以最小化影响: + +- **阶段 1(读锁,~1 秒):** 所有 32 种被跟踪的对象类型被序列化到内存中。此阶段区块处理等待;API 和 P2P 读取并发进行。 +- **阶段 2(无锁,~2 秒):** 压缩、SHA-256 校验和计算和文件 I/O。正常节点操作恢复。 + +如果创建失败(例如磁盘已满),错误被记录到日志,节点继续运行。 + +--- + +## 从快照加载(DLT 模式) + +### 首次启动 + +```bash +vizd --snapshot /path/to/snapshot.json --plugin snapshot +``` + +首次加载时: +1. 快照插件验证文件头(格式版本、链 ID、SHA-256 校验和)。 +2. 共享内存被清除并从快照状态重新初始化。 +3. 所有 32 种对象类型被导入。 +4. LIB 被提升到 `head_block_num`,使 P2P 同步从快照点开始。 +5. 快照文件重命名为 `.used` 以防止重启时重新导入。 +6. P2P 插件启动并从快照 head 向前同步。 + +### 重启安全性 + +节点使用命令行上仍有 `--snapshot` 的方式重启是安全的(例如,通过 `VIZD_EXTRA_OPTS`): + +| 场景 | 行为 | +|------|------| +| 首次启动(无共享内存,文件存在) | 导入快照;重命名为 `.used` | +| 重启(共享内存存在) | 跳过导入 — 使用现有状态 | +| 重启(共享内存已清除,文件已是 `.used`) | 跳过导入 — "snapshot file not found" | +| 强制重新导入 | 使用 `--resync-blockchain` + 提供新鲜快照文件 | + +### DLT 模式 + +快照导入后,节点以 **DLT 模式**运行:主 `block_log` 为空。单独的 [DLT 滚动区块日志](#dlt-滚动区块日志)存储最近的区块,用于 P2P 服务和本地区块查询。模式在后续重启时自动检测(空 `block_log` + 现有 chainbase 状态)。 + +--- + +## 快照文件格式 + +每个快照是一个 JSON 文件: + +```json +{ + "header": { + "version": 1, + "chain_id": "...", + "snapshot_block_num": 12345678, + "snapshot_block_id": "...", + "snapshot_block_time": "2025-01-01T00:00:00", + "last_irreversible_block_num": 12345660, + "payload_checksum": "sha256...", + "object_counts": { "account": 50000, ... } + }, + "state": { + "dynamic_global_property": [...], + "account": [...], + ... + } +} +``` + +### 包含的对象类型(共 32 种) + +**关键(11 种)** — 共识必需: +`dynamic_global_property`、`witness_schedule`、`hardfork_property`、`account`、`account_authority`、`validator`、`witness_vote`、`block_summary`、`content`、`content_vote`、`block_post_validation` + +**重要(15 种)** — 完整运行所需: +`transaction`、`vesting_delegation`、`vesting_delegation_expiration`、`fix_vesting_delegation`、`withdraw_vesting_route`、`escrow`、`proposal`、`required_approval`、`committee_request`、`committee_vote`、`invite`、`award_shares_expire`、`paid_subscription`、`paid_subscribe`、`witness_penalty_expire` + +**可选(5 种)** — 元数据和恢复: +`content_type`、`account_metadata`、`master_authority_history`、`account_recovery_request`、`change_recovery_account_request` + +--- + +## DLT 滚动区块日志 + +在 DLT 模式下,主 `block_log` 为空。**DLT 滚动区块日志**(`dlt_block_log.log` + `dlt_block_log.index`)存储最近的不可逆区块。 + +这支持: +- **P2P 区块服务** — 对端请求最近区块用于 fork 解决和同步追赶。 +- **本地区块查询** — `get_block` 等 API 调用在存储范围内有效。 + +### 配置 + +```ini +# 保留最后 100,000 个区块(默认) +dlt-block-log-max-blocks = 100000 + +# 完全禁用 DLT 区块日志 +# dlt-block-log-max-blocks = 0 +``` + +日志使用滚动窗口:当超过 `dlt-block-log-max-blocks` 时,旧区块从前端裁剪。重启时,日志被保留,新区块从上次停止处追加。 + +### 过时快照检测 + +如果最新快照的区块编号小于 DLT 日志的起始区块,下载节点无法弥合间隙(快照在区块 N,DLT 日志从 M > N 开始,区块 N+1..M-1 缺失)。插件在启动时检测到这一情况,并在同步完成后的第一个实时区块上创建紧急新鲜快照。 + +--- + +## 崩溃恢复:`--replay-from-snapshot` + +当 `shared_memory.bin` 损坏且节点无法正常启动时,`--replay-from-snapshot` 将快照导入与 DLT 区块日志重放结合: + +```bash +# 明确指定快照 +vizd --replay-from-snapshot --snapshot /data/snapshots/snapshot-block-79273800.vizjson --plugin snapshot + +# 或自动发现最新快照 +vizd --replay-from-snapshot --snapshot-auto-latest --plugin snapshot +``` + +**恢复步骤:** +1. 清除共享内存(始终如此 — 假设损坏)。 +2. 导入快照。 +3. 从快照 head 之后的 `dlt_block_log` 重放区块,恢复到最新可用状态。 +4. 从重放的 head 区块恢复 P2P 同步。 + +### 比较:`--snapshot` 与 `--replay-from-snapshot` + +| 方面 | `--snapshot` | `--replay-from-snapshot` | +|------|-------------|--------------------------| +| 用途 | 引导新节点 | 从损坏中恢复 | +| 共享内存检查 | 如已存在则跳过 | 始终清除并重新导入 | +| 快照文件重命名 | 重命名为 `.used` | 不重命名 | +| DLT 区块日志重放 | 否 | 是 | +| 典型用途 | 首次设置 | 崩溃恢复 | + +### 恢复示例 + +DLT 节点在区块 79,274,318 崩溃。状态: +``` +snapshots/snapshot-block-79273800.vizjson ← 落后崩溃点 518 个区块 +blockchain/dlt_block_log.log ← 包含区块 79174319..79274318 +blockchain/shared_memory.bin ← 已损坏 +``` + +恢复命令:`vizd --replay-from-snapshot --snapshot-auto-latest --plugin snapshot` + +``` +Loading state from snapshot: snapshot-block-79273800.vizjson +Snapshot loaded at block 79273800, elapsed time 12.3 sec +Replaying dlt_block_log from block 79273801 to 79274318 (518 blocks)... +Done replaying, head_block=79274318, elapsed time: 7.3 sec +``` + +P2P 同步填补到实时链 head 的间隙。 + +--- + +## 自动运行时恢复:`--auto-recover-from-snapshot` + +默认启用。当区块处理或区块生成期间检测到共享内存损坏时,节点: + +1. 关闭数据库。 +2. 在 `snapshot-dir` 中找到最新快照。 +3. 清除共享内存,导入快照,重放 `dlt_block_log`。 +4. 恢复 P2P 同步 — 无需重启。 + +**前提条件:** +- 必须启用 `plugin = snapshot`。 +- `snapshot-dir` 中必须存在快照。使用 `snapshot-every-n-blocks` 自动创建。 +- `dlt_block_log` 应覆盖快照之后的区块以最小化数据损失。 + +如果恢复失败(未找到快照、导入错误),节点记录错误并干净关闭。 + +禁用:`--no-auto-recover-from-snapshot` + +--- + +## P2P 快照同步 + +节点可以通过 TCP 提供和下载快照,实现完全自动化的引导,无需手动文件传输。 + +### 服务器配置 + +```ini +plugin = snapshot +allow-snapshot-serving = true +snapshot-serve-endpoint = 0.0.0.0:8092 +snapshot-every-n-blocks = 28800 +snapshot-dir = /data/snapshots + +# 可选:仅限受信对端 +# allow-snapshot-serving-only-trusted = true +# trusted-snapshot-peer = 1.2.3.4:8092 +``` + +### 客户端配置 + +```ini +plugin = snapshot +trusted-snapshot-peer = seed1.viz.world:8092 +trusted-snapshot-peer = seed2.viz.world:8092 +sync-snapshot-from-trusted-peer = true +``` + +`sync-snapshot-from-trusted-peer` 默认为 `false` — 必须显式启用以防止意外状态清除。 + +### 工作原理 + +1. **查询:** 连接到每个受信对端,发送 `snapshot_info_request`,收集元数据(区块编号、校验和、大小)。 +2. **选择:** 选择区块编号最高的对端。 +3. **下载:** 以 1 MB 块下载;每 5% 向控制台记录进度。 +4. **验证:** 通过流式传输验证 SHA-256 校验和(不将整个文件加载到内存)。 +5. **导入:** 清除状态,加载已验证的快照,初始化硬分叉。 + +所有操作在 `chain::plugin_startup()` 内发生,在 P2P 和验证者激活之前。节点完全阻塞,直到导入完成。 + +### 安全性 + +- 最大下载大小:2 GB。 +- 每个服务器最多 5 个并发连接,每个连接有 60 秒连接截止时间。 +- 按 IP 速率限制。 +- 控制消息限制 64 KB;只有数据回复允许最多 64 MB。 +- TCP 服务器运行在专用线程上,独立于主 I/O 循环。 + +--- + +## 推荐生产配置 + +```ini +plugin = snapshot + +# 每日快照(3 秒/区块时约 24 小时) +snapshot-every-n-blocks = 28800 +snapshot-dir = /data/viz-snapshots + +# 保留 90 天快照(默认) +snapshot-max-age-days = 90 + +# DLT 滚动区块日志:最后 100k 个区块(默认) +dlt-block-log-max-blocks = 100000 +``` + +--- + +## 故障排除 + +| 症状 | 检查 | +|------|------| +| 节点每次重启都重新导入快照 | `shared_memory.bin` 在重启之间被删除;或快照文件从未被重命名为 `.used` | +| 启动时 `Snapshot file not found` | 文件在之前成功导入时已被重命名为 `.used`;或路径错误 | +| 快照加载时 `Chain ID mismatch` | 快照是从不同链创建的;无法导入 | +| `Checksum mismatch` | 快照文件已损坏或传输不完整 | +| 快照创建从不触发 | 未设置 `snapshot-dir`,或节点仍在 P2P 同步(周期性快照跳过同步模式) | +| 启动时过时快照警告 | 最新快照早于 `dlt_block_log` 起始;节点将在下一个实时区块后创建新鲜快照 | +| 自动恢复触发但失败 | `snapshot-dir` 中无快照;检查 `snapshot-every-n-blocks` 是否已配置 | +| P2P 快照下载失败 | 检查 `trusted-snapshot-peer` 是否在端口 8092 可达;检查服务器上 `allow-snapshot-serving = true` | + +--- + +参见:[快照](../node/snapshot.md)、[验证者插件](./validator.md)、[链插件](./chain.md)、[P2P 概述](../p2p/overview.md)。 diff --git a/@l10n/zh-CN/docs/plugins/validator.md b/@l10n/zh-CN/docs/plugins/validator.md new file mode 100644 index 0000000000..558eacfa55 --- /dev/null +++ b/@l10n/zh-CN/docs/plugins/validator.md @@ -0,0 +1,226 @@ +# 验证者插件 + +验证者插件负责区块签名和生产。它在自己的 OS 线程上运行专用的 250ms 定时器循环,在每次触发时执行一系列安全检查,并在满足所有条件时调用 `database::generate_block()`。 + +**源码:** [plugins/validator/validator.cpp](../../plugins/validator/validator.cpp) + +--- + +## 依赖项 + +``` +chain::plugin, p2p::p2p_plugin, snapshot::snapshot_plugin +``` + +--- + +## 配置 + +### 区块生产 + +| 选项 | 默认值 | 描述 | +|------|-------|------| +| `validator` / `-w` | — | 验证者账户名;可重复 | +| `private-key` | — | 用于签名的 WIF 私钥;可重复 | +| `emergency-private-key` | — | 紧急共识的 WIF 密钥;自动将 `CHAIN_EMERGENCY_WITNESS_ACCOUNT` 添加到验证者集合 | +| `enable-stale-production` | `false` | 绕过参与度和同步检查(仅用于测试网/网络恢复) | +| `required-participation` | `3300` | 最低验证者参与度(**基点**,3300 = 33%) | +| `fork-collision-timeout-blocks` | `21` | 强制生产前的连续 fork 冲突延迟次数(一个完整的验证者轮次) | + +### NTP 同步 + +| 选项 | 默认值 | 描述 | +|------|-------|------| +| `ntp-server` | `pool.ntp.org`, `time.google.com`, `time.cloudflare.com` | NTP 服务器;可重复 | +| `ntp-request-interval` | `900` | 正常同步间隔(秒) | +| `ntp-retry-interval` | `300` | 无 NTP 响应时的重试间隔 | +| `ntp-round-trip-threshold` | `150` | 丢弃往返时间 > N ms 的 NTP 响应 | +| `ntp-history-size` | `5` | NTP 增量平滑的移动平均窗口 | + +### 调试 + +| 选项 | 默认值 | 描述 | +|------|-------|------| +| `debug-block-production` | `false` | 在链数据库中启用详细调试日志 | + +--- + +## 生产定时器 + +生产循环运行在**专用的 `production_io_service_`** 和其自己的 OS 线程上——与 AppBase/P2P 共享的 io_service 完全分离。这可防止 P2P 活动(节点断开、TLS 握手、发送队列清空)延迟 250ms 定时器回调。 + +**触发对齐:** +``` +定时器每 250ms 触发,与墙钟 250ms 边界对齐 +最短休眠:50ms(吸收 OS 抖动) +``` + +**前瞻:** `now = ntp_time + 250ms` — 将生产决策向前移动,使在 `T_slot - 250ms` 的触发恰好对齐到槽位边界: +``` +T=6.000s 的槽位: + T=5.750 触发 → now=6.000 → 槽位匹配 → lag=0ms 时生产 + T=6.000 触发 → now=6.250 → lag=250ms → 仍在 500ms 阈值内 +``` + +**延迟跳过:** 在 `lag` 结果之后,同一槽位在剩余 3 秒槽位间隔内每次触发时都会重新激活。保护机制跳过到下一个槽位边界,使循环释放 CPU 而不是空转。 + +--- + +## `maybe_produce_block()` — 安全检查序列 + +以下检查在每次 `slot > 0` 的触发时**按顺序**运行。 + +| # | 检查 | 失败结果 | +|---|------|---------| +| 1 | DLT 同步门控(仅 DLT 模式):`chain().is_syncing()` 为 false,或此节点是紧急主节点 | `not_synced` | +| 2 | 快照暂停门控:`snapshot().is_snapshot_in_progress()` 为 false | `not_synced` | +| 3 | P2P 追赶门控:`p2p().is_catching_up_after_pause()` 为 false | `not_synced` | +| 4 | HF12 三态安全(见下文) | `not_synced` / `low_participation` | +| 5 | `slot = db.get_slot_at_time(now) > 0` | `not_time_yet` | +| 6 | 计划的验证者在我们配置的集合中 | `not_my_turn` | +| 7 | 槽位尚未填充(`scheduled_time > head_block_time`) | `not_time_yet` | +| 8 | 验证者链上 `signing_key` 非零 | `not_my_turn` | +| 9 | `signing_key` 的私钥已加载 | `no_private_key` | +| 10 | HF12 之前:参与度 ≥ 阈值 | `low_participation` | +| 11 | `|scheduled_time - now| ≤ 500ms` | `lag` | +| 12 | Fork 冲突检查(见下文) | `fork_collision` | +| 13 | 第二次快照暂停检查(竞争窗口) | `not_time_yet` | +| 14 | `db.generate_block()` + `p2p().broadcast_block()` | `produced` | + +### HF12 三态安全(检查 #4) + +**紧急共识激活:** +- 紧急主节点(有 `emergency-private-key` + 计划中有 committee):无条件继续。 +- 从节点:生产前需要 `get_slot_time(1) >= now`(链未过期)。 + +**正常模式(HF12+):** +- 参与度 ≥ 33%:健康网络;通过 `get_slot_time(1)` 进行同步检查。 +- 参与度 < 33%:受损网络;应用参与度 vs `required-participation` 阈值。 +- `enable-stale-production=true`:绕过参与度和同步检查。 + +**HF12 之前:** 通过 `get_slot_time(1)` 进行简单同步检查。 + +### Fork 冲突解决(检查 #12) + +当 `head_block_num + 1` 处存在竞争区块时: + +1. **投票权重比较(HF12+):** `compare_fork_branches()` 计算委托给每个分支的总 SHARES。如果我们的分支更重,则继续并删除竞争区块。若平局或更轻,则延迟。 +2. **卡住头超时:** 经过 `fork-collision-timeout-blocks` 次连续延迟(默认 21 = 63 秒)后,竞争区块被删除并恢复生产。这处理来自断开节点的死 fork 区块。 + +**紧急模式:** 任何竞争区块都触发延迟;不使用投票权重路径。 + +--- + +## 少数派 Fork 检测 + +在每次生产尝试之前(在 HF12 安全检查之后),插件遍历 `fork_db` 中最后 21 个区块。如果所有 21 个都由节点自己配置的验证者生产,则节点被隔离在少数派 fork 上。 + +- **默认操作:** 调用 `p2p().resync_from_lib()` — 回滚到 LIB,重置 fork DB,重新启动 P2P 同步,重新连接种子节点。返回 `minority_fork`。 +- **使用 `enable-stale-production=true`:** 记录警告,继续生产。 +- **跳过时机:** 紧急共识激活时(committee 区块总会匹配我们配置的集合)。在紧急模式下,DLT 特定的从节点隔离检查取代它。 + +--- + +## NTP 停滞检测 + +如果 `get_slot_at_time(now)` 返回 0(NTP 落后于链时间),`_slot_zero_streak` 计数器递增: + +| 连续次数 | 时间 | 动作 | +|---------|------|------| +| 3 | ~750ms | 警告 | +| 10 | ~2.5s | 强制 NTP 重新同步 | +| 60 | ~15s | 长期停滞警告 | +| 120 | ~30s | 严重错误 | + +计数器在任何非零槽位结果时重置。 + +--- + +## 生产 Watchdog + +如果节点曾经生产过区块且 `should_be_producing` 为 true(从链实时状态推导:参与度 ≥ 33% 或使用我们密钥的紧急共识激活),但在以下时间内没有生产区块: +- 紧急主节点:**60 秒** +- 普通验证者:**180 秒** + +Watchdog 每 30 秒触发一次并记录诊断。如果满足恢复条件(头部在过去 30 秒内推进,未同步,有节点连接,链上有非零签名密钥),它会强制清除阻塞条件: + +1. 清除 `_minority_fork_recovering` 标志。 +2. 调用 `p2p().clear_catchup_flag()` — 清除 P2P 暂停后追赶标志。 +3. 调用 `chain().clear_syncing()` — 清除链同步标志。 + +生产在下一次触发时自动恢复。 + +--- + +## `on_block_applied()` — 信号处理器 + +连接到 `database::applied_block`。对每个传入区块运行。 + +### 错过槽位检测 + +当 `block_num > prev_num + 1`(区块流中有间隙)时,处理器确定我们的验证者是否被计划在任何错过的槽位,并记录完整诊断状态(生产标志、NTP 偏移、同步状态、签名密钥状态、下一槽位时间)。 + +### 槽位劫持检测(DLT 紧急共识模式) + +当紧急共识激活时,紧急主节点可能清空我们验证者的签名密钥,并在我们计划的槽位中生产 committee 区块。处理器通过 `_slot_hijack_count` 追踪这一情况。当我们自己的验证者之一生产区块时重置。 + +--- + +## 公共 API + +### `is_witness_scheduled_soon()` + +如果本地控制的验证者被计划在接下来 4 个槽位(~12 秒)内生产,则返回 `true`。快照插件在计划快照之前调用此方法,以在生产即将进行时延迟。 + +### `is_emergency_master()` + +在以下条件下返回 `true`: +1. 已配置 `emergency-private-key`(`_witnesses` 中的 `CHAIN_EMERGENCY_WITNESS_ACCOUNT`)。 +2. "committee" 账户在当前验证者计划中。 + +只有满足两个条件的节点才应在紧急模式下独自生产;其他节点是从属节点,必须先同步。 + +### `is_emergency_key_configured()` + +如果已配置 `emergency-private-key`,则返回 `true`,不考虑当前计划。用于 P2P 握手消息(`has_emergency_key` 字段)。 + +### `get_production_diagnostics()` + +返回紧凑诊断字符串: +``` +validator[skip_flags=0x0 catching_up=0 head=#79881136 last_prod=45s_ago minority_rcv=0 slot_hijacks=0] +``` +当节点在没有前方节点的情况下卡住时,包含在 P2P FORWARD 停滞日志中。 + +--- + +## 关键不变量 + +1. **DLT 模式同步时绝不生产** — 在过期头上创建区块,导致 fork 振荡。 +2. **快照进行中绝不生产** — 写锁死锁。 +3. **槽位已填充时绝不生产** — 创建微型 fork。 +4. **紧急主节点必须始终生产** — 它是唯一的区块生产者;等待会造成死锁。 +5. **从节点在紧急模式下生产前必须同步** — 在过期头上生产 = 少数派 fork。 +6. **参与度 < 33% 停止生产** — 网络分区保护(可覆盖)。 +7. **21 个连续自有验证者区块 → 回滚到 LIB** — 少数派 fork 恢复。 +8. **所有数据库读取均为最新** — 无状态缓存;紧急模式可在任何区块激活/停用。 + +--- + +## 故障排除 + +| 症状 | 检查内容 | +|------|---------| +| `not_synced` 日志 | DLT 同步激活或快照进行中——等待;卡住时 watchdog 会自动清除 | +| `not_time_yet` 重复出现 | NTP 落后于链时间;检查 `_slot_zero_streak` 警告和 NTP 偏移 | +| 我们的槽位出现 `not_my_turn` | 链上签名密钥被清空;发送 `validator_update_operation` 恢复 | +| `no_private_key` | 配置中缺少链上注册的签名密钥对应的 `private-key` | +| `low_participation` | 网络参与度 < 33%;检查节点连接或设置 `enable-stale-production=true` | +| `fork_collision` | 下一高度有竞争区块;等待投票权重解决或 21 次延迟超时 | +| `minority_fork` | 已隔离;插件自动重新同步到 LIB | +| Watchdog 重复触发 | 同步或追赶标志卡住;头部推进时 watchdog 会自动清除 | +| `SLOT-HIJACK` 日志 | 紧急主节点清空了我们的密钥;通过 `validator_update_operation` 恢复 | + +--- + +参见:[验证者守护](../node/validator-guard.md)、[Fair-DPOS](../consensus/fair-dpos.md)、[紧急共识](../consensus/emergency-consensus.md)、[区块处理](../consensus/block-processing.md)。 diff --git a/@l10n/zh-CN/docs/plugins/webserver.md b/@l10n/zh-CN/docs/plugins/webserver.md new file mode 100644 index 0000000000..5c247d6496 --- /dev/null +++ b/@l10n/zh-CN/docs/plugins/webserver.md @@ -0,0 +1,117 @@ +# 网络服务器插件 + +网络服务器插件提供 HTTP 和 WebSocket 端点,将 JSON-RPC 请求转发到 `json_rpc` 插件。它包含以 `method + params`(不含 `id`)为键的响应缓存(每个应用的区块使其失效),以及用于并发请求处理的线程池。 + +**源码:** [plugins/webserver/webserver_plugin.cpp](../../plugins/webserver/webserver_plugin.cpp) + +--- + +## 依赖 + +``` +json_rpc::plugin +``` + +--- + +## 配置 + +| 选项 | 默认值 | 描述 | +|------|--------|------| +| `webserver-http-endpoint` | — | HTTP 监听地址,例如 `0.0.0.0:8090` | +| `webserver-ws-endpoint` | — | WebSocket 监听地址,例如 `0.0.0.0:8091` | +| `webserver-thread-pool-size` | `256` | 处理 HTTP 和 WebSocket 请求的工作线程数。 | +| `webserver-cache-enabled` | `true` | 启用响应缓存。 | +| `webserver-cache-size` | `10000` | 缓存响应的最大数量。达到此限制时整个缓存被驱逐。 | + +插件至少需要设置 `webserver-http-endpoint` 或 `webserver-ws-endpoint` 之一才能工作。两者可同时启用。 + +--- + +## 最小 config.ini + +```ini +plugin = webserver + +webserver-http-endpoint = 0.0.0.0:8090 +webserver-ws-endpoint = 0.0.0.0:8091 +``` + +--- + +## 响应缓存 + +### 缓存什么 + +每个只读 JSON-RPC 响应都有资格被缓存。缓存键是 `method + params` 的 SHA-256 哈希 — 故意**排除 `id`**,这样轮换请求 `id` 就无法绕过缓存。 + +### 永不缓存 + +| 命名空间 | 原因 | +|---------|------| +| `network_broadcast_api.*` | 改变状态(交易/区块广播) | +| `debug_node.*` | 改变状态的调试操作 | +| 格式错误/无法解析的请求 | 无法可靠地派生键 | + +批量请求(JSON 数组)被视为单个原子缓存条目,以完整数组哈希为键。 + +### 失效 + +缓存在每个应用的区块上被清除。响应永远不会超过一个区块间隔(3 秒)地服务过时内容。 + +### 禁用缓存 + +```ini +webserver-cache-enabled = false +``` + +对于服务高延迟敏感或实时客户端(3 秒缓存窗口不可接受)的节点,请禁用。 + +--- + +## 线程池 + +HTTP 和 WebSocket 服务器各自运行在专用的 `io_service` 实例上。传入请求被分派到 `webserver-thread-pool-size` 个工作线程的共享线程池。 + +**大小指导:** +- 混合读写流量的公共 API 节点:`256`(默认)对大多数工作负载足够。 +- 高吞吐量节点:增加到 `512` 或更多。监控 CPU 饱和 — 线程数超过 CPU 核心数对 CPU 密集操作没有帮助。 +- 开发/本地节点:`4`–`8` 足够。 + +--- + +## WebSocket 订阅 + +WebSocket 客户端可以注册回调: + +| 方法 | 描述 | +|------|------| +| `database_api.set_block_applied_callback` | 每个应用的区块触发,带区块头 | +| `database_api.set_pending_transaction_callback` | 交易进入待处理池时触发 | +| `database_api.cancel_all_subscriptions` | 取消所有回调的订阅 | + +订阅需要持久的 WebSocket 连接。普通 HTTP 不支持。 + +--- + +## 安全性 + +- **绑定到 localhost**(`127.0.0.1`)并使用反向代理(nginx/Caddy)进行公共暴露。绑定到 `0.0.0.0` 会将 RPC 直接暴露给网络。 +- 插件没有内置身份验证或速率限制。在反向代理层应用这些。 +- 变更方法(`network_broadcast_api`、`debug_node`)在设计上受到保护,不受缓存中毒影响,但它们仍可从任何连接的客户端调用 — 如需要,在网络级别限制访问。 + +--- + +## 故障排除 + +| 症状 | 检查 | +|------|------| +| 启动时端口已被使用 | 另一个进程绑定到配置的端口;更改端口或终止冲突进程 | +| 高内存使用 | 减少 `webserver-cache-size` 或禁用缓存 | +| 负载下响应慢 | 增加 `webserver-thread-pool-size`;检查 CPU 饱和 | +| WebSocket 订阅未触发 | 订阅需要 WebSocket 连接,而非 HTTP | +| 过时响应 | 如果 `webserver-cache-enabled = true`,响应在一个区块间隔内(~3 秒)是新鲜的;对于实时使用请禁用缓存 | + +--- + +参见:[插件概述](./overview.md)、[Database API](./database-api.md)、[JSON-RPC API](../api/json-rpc.md)。 diff --git a/@l10n/zh-CN/docs/protocol/data-types.md b/@l10n/zh-CN/docs/protocol/data-types.md new file mode 100644 index 0000000000..4086ac3670 --- /dev/null +++ b/@l10n/zh-CN/docs/protocol/data-types.md @@ -0,0 +1,257 @@ +# 公共数据类型 + +VIZ Ledger 协议操作和虚拟操作中使用的所有共享数据类型。 + +--- + +## 原始类型 + +| C++ 类型 | JSON 表示 | 描述 | +|---------|----------|------| +| `string` | `string` | UTF-8 字符串 | +| `bool` | `boolean` | `true` / `false` | +| `uint8_t` | `integer` | 无符号 8 位整数 | +| `uint16_t` | `integer` | 无符号 16 位整数(0–65535) | +| `int16_t` | `integer` | 有符号 16 位整数(−32768–32767) | +| `uint32_t` | `integer` | 无符号 32 位整数 | +| `int32_t` | `integer` | 有符号 32 位整数 | +| `uint64_t` | `string` 或 `integer` | 无符号 64 位整数——在 JavaScript 中使用字符串以避免溢出 | +| `int64_t` | `string` 或 `integer` | 有符号 64 位整数 | +| `share_type` | `integer` | `safe` 的别名——以最小单位表示的代币数量 | +| `time_point_sec` | `string` | ISO 8601 UTC 日期时间:`"2024-01-15T12:00:00"`(无时区后缀) | + +--- + +## `account_name_type` + +标识账户的固定长度字符串(最多 16 字节)。规则: + +- 点号分隔的标签;每个标签至少 3 个字符。 +- 以字母开头,以字母或数字结尾。 +- 仅小写字母(`a`–`z`)、数字(`0`–`9`)、连字符(`-`)。 +- 最小长度:2 个字符(`CHAIN_MIN_ACCOUNT_NAME_LENGTH`)。 +- 最大长度:16 个字符(`CHAIN_MAX_ACCOUNT_NAME_LENGTH`)。 + +**JSON:** 普通字符串——`"alice"`、`"alice.bob"` + +--- + +## `public_key_type` + +以 base58check 编码并带有 `VIZ` 前缀的 secp256k1 压缩公钥。 + +**JSON:** 字符串——`"VIZ5hqSa4NkEZGAMUpoH5EaEr64mBJuMcPpGjvk8qb7hcPFTbXSQ9"` + +- 前缀必须是 `VIZ`(不是 `STM`、`GLS` 或其他)。 +- 由 33 字节压缩公钥 + 4 字节校验和 = 共 37 字节进行 base58 编码。 + +--- + +## `asset` + +表示带有符号的代币数量。在 JSON API 响应和操作参数中序列化为人类可读的字符串: + +``` +"10.000 VIZ" +"5.000000 SHARES" +``` + +### 代币符号 + +| 符号 | 字符串 | 小数位数 | 描述 | +|------|-------|---------|------| +| `TOKEN_SYMBOL` | `VIZ` | 3 | 主要流动代币 | +| `SHARES_SYMBOL` | `SHARES` | 6 | 质押份额(已质押的 VIZ) | + +构建操作时,始终使用字符串格式。通过空格分割解析:左边是数量,右边是符号。VIZ 使用 3 位小数;SHARES 使用 6 位。 + +--- + +## `authority` + +控制账户权限级别的多重签名授权结构。 + +```json +{ + "weight_threshold": 1, + "account_auths": [ + ["alice", 1] + ], + "key_auths": [ + ["VIZ5hqSa4NkEZGAMUpoH5EaEr64mBJuMcPpGjvk8qb7hcPFTbXSQ9", 1] + ] +} +``` + +| 字段 | 类型 | 描述 | +|------|------|------| +| `weight_threshold` | `uint32_t` | 满足授权所需的最低总权重 | +| `account_auths` | `[[account_name, weight], ...]` | 基于账户的签名者 | +| `key_auths` | `[[public_key, weight], ...]` | 基于密钥的签名者 | + +满足的签名者权重之和必须 ≥ `weight_threshold`。空授权:`{ "weight_threshold": 0, "account_auths": [], "key_auths": [] }`。 + +### 授权级别 + +| 级别 | 用于 | +|------|------| +| `master` | 最高安全性——更换密钥、账户恢复 | +| `active` | 代币操作——转账、质押、验证者投票 | +| `regular` | 社交操作——内容、奖励、委员会投票 | + +--- + +## `beneficiary_route_type` + +指定内容支付的受益人及其奖励份额。 + +```json +{ "account": "alice", "weight": 2500 } +``` + +| 字段 | 类型 | 描述 | +|------|------|------| +| `account` | `account_name_type` | 受益人账户 | +| `weight` | `uint16_t` | 以基点表示的份额(10000 = 100%) | + +- 所有受益人权重之和不得超过 10000。 +- 受益人必须在数组中按账户名升序排列。 +- 每个受益人账户必须在链上存在。 + +--- + +## `extensions_type` + +当前未使用——始终序列化为空数组。 + +```json +"extensions": [] +``` + +--- + +## `versioned_chain_properties` + +包含链属性某一版本的静态变体。序列化为 2 元素数组 `[type_index, object]`。 + +| 索引 | 类型 | +|------|------| +| 0 | `chain_properties_init` | +| 1 | `chain_properties_hf4` | +| 2 | `chain_properties_hf6` | +| 3 | `chain_properties_hf9`(当前) | + +每个版本的完整字段参考参见[链属性](../governance/chain-properties.md)。 + +--- + +## `operation`(静态变体) + +每个操作序列化为 2 元素数组:`[type_id, operation_object]`。 + +### 常规操作(用户广播) + +| ID | 操作 | +|----|------| +| 0 | `vote_operation` *(已弃用)* | +| 1 | `content_operation` *(已弃用)* | +| 2 | `transfer_operation` | +| 3 | `transfer_to_vesting_operation` | +| 4 | `withdraw_vesting_operation` | +| 5 | `account_update_operation` | +| 6 | `witness_update_operation` | +| 7 | `account_witness_vote_operation` | +| 8 | `account_witness_proxy_operation` | +| 9 | `delete_content_operation` *(已弃用)* | +| 10 | `custom_operation` | +| 11 | `set_withdraw_vesting_route_operation` | +| 12 | `request_account_recovery_operation` | +| 13 | `recover_account_operation` | +| 14 | `change_recovery_account_operation` | +| 15 | `escrow_transfer_operation` | +| 16 | `escrow_dispute_operation` | +| 17 | `escrow_release_operation` | +| 18 | `escrow_approve_operation` | +| 19 | `delegate_vesting_shares_operation` | +| 20 | `account_create_operation` | +| 21 | `account_metadata_operation` | +| 22 | `proposal_create_operation` | +| 23 | `proposal_update_operation` | +| 24 | `proposal_delete_operation` | +| 25 | `chain_properties_update_operation` | +| 35 | `committee_worker_create_request_operation` | +| 36 | `committee_worker_cancel_request_operation` | +| 37 | `committee_vote_request_operation` | +| 43 | `create_invite_operation` | +| 44 | `claim_invite_balance_operation` | +| 45 | `invite_registration_operation` | +| 46 | `versioned_chain_properties_update_operation` | +| 47 | `award_operation` | +| 50 | `set_paid_subscription_operation` | +| 51 | `paid_subscribe_operation` | +| 54 | `set_account_price_operation` | +| 55 | `set_subaccount_price_operation` | +| 56 | `buy_account_operation` | +| 58 | `use_invite_balance_operation` | +| 60 | `fixed_award_operation` | +| 61 | `target_account_sale_operation` | + +### 虚拟操作(区块链生成,不可广播) + +| ID | 操作 | +|----|------| +| 26 | `author_reward_operation` | +| 27 | `curation_reward_operation` | +| 28 | `content_reward_operation` | +| 29 | `fill_vesting_withdraw_operation` | +| 30 | `shutdown_witness_operation` | +| 31 | `hardfork_operation` | +| 32 | `content_payout_update_operation` | +| 33 | `content_benefactor_reward_operation` | +| 34 | `return_vesting_delegation_operation` | +| 38 | `committee_cancel_request_operation` | +| 39 | `committee_approve_request_operation` | +| 40 | `committee_payout_request_operation` | +| 41 | `committee_pay_request_operation` | +| 42 | `witness_reward_operation` | +| 48 | `receive_award_operation` | +| 49 | `benefactor_award_operation` | +| 52 | `paid_subscription_action_operation` | +| 53 | `cancel_paid_subscription_operation` | +| 57 | `account_sale_operation` | +| 59 | `expire_escrow_ratification_operation` | +| 62 | `bid_operation` | +| 63 | `outbid_operation` | + +--- + +## 交易构建 + +已签名交易包含: + +| 字段 | 值 | +|------|---| +| `ref_block_num` | `head_block_number & 0xFFFF` | +| `ref_block_prefix` | `block_id` 的第 4–7 字节(小端 `uint32`) | +| `expiration` | UTC 时间字符串;建议从广播时间起最多 ~60 秒 | +| `operations` | `[type_id, object]` 对的数组 | +| `extensions` | 始终为 `[]` | +| `signatures` | 紧凑十六进制编码的 ECDSA 签名数组 | + +**签名:** `sha256(chain_id + serialized_transaction_body)` → secp256k1 上的紧凑 ECDSA 签名。 + +**私钥:** WIF 格式(base58check,版本字节 `0x80`)。 + +--- + +## 能量系统 + +能量由奖励类型操作使用。 + +- 以基点存储:0–10000(0%–100%)。 +- 每 24 小时恢复 100%(`CHAIN_ENERGY_REGENERATION_SECONDS = 86400`)。 +- 当前能量:`min(10000, last_energy + elapsed_seconds × 10000 / 86400)`。 + +--- + +参见:[操作概述](./operations/overview.md)、[虚拟操作](./virtual-operations.md)、[链属性](../governance/chain-properties.md)。 diff --git a/@l10n/zh-CN/docs/protocol/operations/account-market.md b/@l10n/zh-CN/docs/protocol/operations/account-market.md new file mode 100644 index 0000000000..41472391d2 --- /dev/null +++ b/@l10n/zh-CN/docs/protocol/operations/account-market.md @@ -0,0 +1,120 @@ +# 账户市场操作 + +账户市场允许在链上挂牌出售和购买账户及子账户命名空间。 + +--- + +## `set_account_price_operation`(ID 54) + +**授权:** `account` 的 `master` + +将账户挂牌公开出售或更新挂牌信息。挂牌时收取 `account_on_sale_fee`。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `account` | `account_name_type` | 挂牌出售的账户 | +| `account_seller` | `account_name_type` | 接收付款的账户(可与 `account` 不同) | +| `account_offer_price` | `asset`(VIZ) | 要价 | +| `account_on_sale` | `bool` | `true` 挂牌;`false` 撤牌 | + +```json +[54, { + "account": "alice", + "account_seller": "alice", + "account_offer_price": "1000.000 VIZ", + "account_on_sale": true +}] +``` + +- `account_on_sale: false` 撤牌但不退还手续费。 +- `account_seller` 可以是任意账户,适用于经纪销售。 + +--- + +## `set_subaccount_price_operation`(ID 55) + +**授权:** `account` 的 `master` + +将创建子账户的权利(如 `account.childname`)挂牌出售。挂牌时收取 `subaccount_on_sale_fee`。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `account` | `account_name_type` | 父账户 | +| `subaccount_seller` | `account_name_type` | 接收付款的账户 | +| `subaccount_offer_price` | `asset`(VIZ) | 每次创建子账户权利的价格 | +| `subaccount_on_sale` | `bool` | `true` 挂牌;`false` 撤牌 | + +```json +[55, { + "account": "alice", + "subaccount_seller": "alice", + "subaccount_offer_price": "50.000 VIZ", + "subaccount_on_sale": true +}] +``` + +- 买家每次交易购买在 `account` 命名空间下创建一个子账户的权利。 + +--- + +## `buy_account_operation`(ID 56) + +**授权:** `buyer` 的 `active` + +购买当前挂牌出售的账户。所有权限转移给买家。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `buyer` | `account_name_type` | 购买账户 | +| `account` | `account_name_type` | 被购买的账户 | +| `account_offer_price` | `asset`(VIZ) | 购买价格(必须与挂牌价格完全匹配) | +| `account_authorities_key` | `public_key_type` | 设置为被购账户 master、active、regular 和 memo 的新密钥 | +| `tokens_to_shares` | `asset`(VIZ) | 为被购账户额外转换为 SHARES 的 VIZ(可为 `"0.000 VIZ"`) | + +```json +[56, { + "buyer": "bob", + "account": "alice", + "account_offer_price": "1000.000 VIZ", + "account_authorities_key": "VIZ5newowner...", + "tokens_to_shares": "0.000 VIZ" +}] +``` + +- `account_offer_price` 必须与 `set_account_price_operation` 中的价格完全匹配。 +- `account_authorities_key` 同时应用于所有四个权限槽。 +- 付款发送至挂牌中指定的 `account_seller`。 +- 购买成功时触发虚拟操作 `account_sale_operation`。 + +--- + +## `target_account_sale_operation`(ID 61) + +**授权:** `account` 的 `master` + +将账户挂牌为仅限特定买家的私下(定向)销售。只有 `target_buyer` 才能购买此挂牌。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `account` | `account_name_type` | 挂牌出售的账户 | +| `account_seller` | `account_name_type` | 接收付款的账户 | +| `target_buyer` | `account_name_type` | 唯一允许购买的账户 | +| `account_offer_price` | `asset`(VIZ) | 要价 | +| `account_on_sale` | `bool` | `true` 挂牌;`false` 撤牌 | + +```json +[61, { + "account": "alice", + "account_seller": "alice", + "target_buyer": "charlie", + "account_offer_price": "500.000 VIZ", + "account_on_sale": true +}] +``` + +- `account_on_sale: false` 取消定向挂牌。 +- 买家使用标准的 `buy_account_operation` 完成购买。 + +--- + +参见:[数据类型](../data-types.md)、[操作概述](./overview.md)、[虚拟操作](../virtual-operations.md)、[Database API — 账户市场](../../plugins/database-api.md#account-market)。 diff --git a/@l10n/zh-CN/docs/protocol/operations/accounts.md b/@l10n/zh-CN/docs/protocol/operations/accounts.md new file mode 100644 index 0000000000..0428056617 --- /dev/null +++ b/@l10n/zh-CN/docs/protocol/operations/accounts.md @@ -0,0 +1,96 @@ +# 账户操作 + +--- + +## `account_create_operation`(ID 20) + +**授权:** `creator` 的 `active` + +创建新的区块链账户。费用被转换为新账户的 SHARES。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `fee` | `asset`(VIZ) | 创建费 ≥ 链的 `account_creation_fee` | +| `delegation` | `asset`(SHARES) | 给新账户的初始 SHARES 委托 | +| `creator` | `account_name_type` | 支付费用的账户 | +| `new_account_name` | `account_name_type` | 新账户名 | +| `master` | `authority` | 主权限 | +| `active` | `authority` | 活跃权限 | +| `regular` | `authority` | 常规权限 | +| `memo_key` | `public_key_type` | Memo 公钥 | +| `json_metadata` | `string` | JSON 元数据(可为 `""`) | +| `referrer` | `account_name_type` | 推荐账户(可为 `""`) | +| `extensions` | `extensions_type` | 始终为 `[]` | + +```json +[20, { + "fee": "1.000 VIZ", + "delegation": "10.000000 SHARES", + "creator": "alice", + "new_account_name": "bob", + "master": { "weight_threshold": 1, "account_auths": [], "key_auths": [["VIZ5...", 1]] }, + "active": { "weight_threshold": 1, "account_auths": [], "key_auths": [["VIZ5...", 1]] }, + "regular": { "weight_threshold": 1, "account_auths": [], "key_auths": [["VIZ5...", 1]] }, + "memo_key": "VIZ5...", + "json_metadata": "", + "referrer": "", + "extensions": [] +}] +``` + +- 三种权限都是必填的(即使使用相同的密钥)。 +- `fee.symbol` 必须为 `VIZ`;`delegation.symbol` 必须为 `SHARES`。 + +--- + +## `account_update_operation`(ID 5) + +**授权:** `account` 的 `master`(如果存在 `master` 字段),否则 `active` + +更新账户的密钥和元数据。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `account` | `account_name_type` | 要更新的账户 | +| `master` | `optional` | 新的主权限(不更改则省略) | +| `active` | `optional` | 新的活跃权限 | +| `regular` | `optional` | 新的常规权限 | +| `memo_key` | `public_key_type` | 新的 memo 密钥(即使不更改也是必填) | +| `json_metadata` | `string` | 新的 JSON 元数据 | + +```json +[5, { + "account": "alice", + "active": { "weight_threshold": 1, "account_auths": [], "key_auths": [["VIZ5new...", 1]] }, + "memo_key": "VIZ5new...", + "json_metadata": "{\"profile\":\"updated\"}" +}] +``` + +- 如果存在 `master` → 用当前 **master** 密钥签名。 +- 如果 `master` 不存在 → 用当前 **active** 密钥签名。 +- `memo_key` 始终必填。 + +--- + +## `account_metadata_operation`(ID 21) + +**授权:** `account` 的 `regular` + +仅更新账户的 JSON 元数据。带宽成本低于 `account_update`。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `account` | `account_name_type` | 要更新的账户 | +| `json_metadata` | `string` | 新的 JSON 元数据字符串 | + +```json +[21, { + "account": "alice", + "json_metadata": "{\"name\":\"Alice\",\"about\":\"Hello!\"}" +}] +``` + +--- + +参见:[数据类型](../data-types.md)、[操作概述](./overview.md)。 diff --git a/@l10n/zh-CN/docs/protocol/operations/awards.md b/@l10n/zh-CN/docs/protocol/operations/awards.md new file mode 100644 index 0000000000..3ce2c9d473 --- /dev/null +++ b/@l10n/zh-CN/docs/protocol/operations/awards.md @@ -0,0 +1,94 @@ +# 奖励操作 + +奖励是主要的社交激励机制。账户消耗*能量*,从奖励池中直接向另一账户授予 SHARES。奖励大小与发起者的能量消耗和 SHARES 质押量成比例。 + +**能量:** 以基点 0–10000 存储;每 24 小时恢复至 100%(`CHAIN_ENERGY_REGENERATION_SECONDS = 86400`)。 + +--- + +## `award_operation`(ID 47) + +**授权:** `initiator` 的 `regular` + +向 `receiver` 授予 SHARES,消耗指定百分比的能量。实际 SHARES 数量由发起者的质押量和奖励池深度决定。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `initiator` | `account_name_type` | 给予奖励的账户 | +| `receiver` | `account_name_type` | 接收奖励的账户 | +| `energy` | `uint16_t` | 消耗的能量(基点,1–10000) | +| `custom_sequence` | `uint64_t` | 应用程序定义的序列号(可为 0) | +| `memo` | `string` | 可选消息或原因 | +| `beneficiaries` | `vector` | 可选受益人,获得奖励的一部分 | + +```json +[47, { + "initiator": "alice", + "receiver": "bob", + "energy": 1000, + "custom_sequence": 0, + "memo": "great article!", + "beneficiaries": [] +}] +``` + +含受益人的示例: +```json +[47, { + "initiator": "alice", + "receiver": "bob", + "energy": 1000, + "custom_sequence": 1, + "memo": "", + "beneficiaries": [ + {"account": "charlie", "weight": 2000} + ] +}] +``` + +- `energy` = 10000 消耗当前 100% 的能量。 +- 若存在受益人,`receiver` 获得奖励的 `(10000 − 权重之和) / 10000`。 +- 受益人权重之和必须 ≤ 10000;受益人必须按账户名升序排列。 +- 虚拟操作 `receive_award_operation` 为 `receiver` 触发。 +- 虚拟操作 `benefactor_award_operation` 为每个受益人触发。 + +--- + +## `fixed_award_operation`(ID 60) + +**授权:** `initiator` 的 `regular` + +向 `receiver` 授予**固定数量**的 SHARES。能量按所需奖励大小比例消耗;`max_energy` 限制最大消耗。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `initiator` | `account_name_type` | 给予奖励的账户 | +| `receiver` | `account_name_type` | 接收奖励的账户 | +| `reward_amount` | `asset`(SHARES) | 授予的固定 SHARES 数量 | +| `max_energy` | `uint16_t` | 最大消耗能量(基点;0 = 无限制) | +| `custom_sequence` | `uint64_t` | 应用程序定义的序列号 | +| `memo` | `string` | 可选消息或原因 | +| `beneficiaries` | `vector` | 可选受益人 | + +```json +[60, { + "initiator": "alice", + "receiver": "bob", + "reward_amount": "10.000000 SHARES", + "max_energy": 5000, + "custom_sequence": 1, + "memo": "fixed reward", + "beneficiaries": [ + {"account": "charlie", "weight": 1000} + ] +}] +``` + +- `reward_amount.symbol` 必须为 `SHARES`。 +- `max_energy = 0` 表示无能量限制 — 操作将消耗所需的任意能量。 +- 实际消耗的能量取决于发起者的质押量和当前奖励池深度。 +- 受益人规则与 `award_operation` 相同。 + +--- + +参见:[数据类型](../data-types.md)、[操作概述](./overview.md)、[虚拟操作](../virtual-operations.md)。 diff --git a/@l10n/zh-CN/docs/protocol/operations/committee.md b/@l10n/zh-CN/docs/protocol/operations/committee.md new file mode 100644 index 0000000000..6e706e2fbc --- /dev/null +++ b/@l10n/zh-CN/docs/protocol/operations/committee.md @@ -0,0 +1,103 @@ +# 委员会操作 + +委员会(工作者提案)系统允许社区成员从委员会基金申请资金。SHARES 持有者投票批准或拒绝请求;批准的请求从基金中获得支付。 + +--- + +## `committee_worker_create_request_operation`(ID 35) + +**授权:** `creator` 的 `regular` + +创建新的资金请求。提交时从创建者处收取 `committee_create_request_fee`。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `creator` | `account_name_type` | 创建请求的账户 | +| `url` | `string` | 描述提案的 URL(非空,最多 255 字节) | +| `worker` | `account_name_type` | 将接收支付的账户 | +| `required_amount_min` | `asset`(VIZ) | 最低可接受支付金额 | +| `required_amount_max` | `asset`(VIZ) | 最高可接受支付金额 | +| `duration` | `uint32_t` | 请求持续时间(秒) | + +```json +[35, { + "creator": "alice", + "url": "https://alice.example.com/proposal", + "worker": "alice", + "required_amount_min": "100.000 VIZ", + "required_amount_max": "500.000 VIZ", + "duration": 604800 +}] +``` + +**约束:** + +| 参数 | 值 | +|------|-----| +| 最小持续时间 | 5 天(432000 秒) | +| 最大持续时间 | 30 天(2592000 秒) | +| `required_amount_max` | 必须 > `required_amount_min` | + +- `required_amount_min` ≥ 0;`required_amount_max` > `required_amount_min`。 +- `worker` 可以与 `creator` 不同。 + +--- + +## `committee_worker_cancel_request_operation`(ID 36) + +**授权:** `creator` 的 `regular` + +在到期之前取消现有资金请求。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `creator` | `account_name_type` | 请求的创建者 | +| `request_id` | `uint32_t` | 要取消的请求 ID | + +```json +[36, { + "creator": "alice", + "request_id": 42 +}] +``` + +- 只有请求的 `creator` 才能取消它。 +- `request_id` 必须指向现有的活跃请求。 + +--- + +## `committee_vote_request_operation`(ID 37) + +**授权:** `voter` 的 `regular` + +对资金请求投票。投票权重与投票者的 SHARES 质押成比例。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `voter` | `account_name_type` | 投票的账户 | +| `request_id` | `uint32_t` | 请求 ID | +| `vote_percent` | `int16_t` | 投票权重(基点,−10000 到 10000) | + +```json +[37, { + "voter": "bob", + "request_id": 42, + "vote_percent": 10000 +}] +``` + +- `vote_percent` > 0 → 支持;`vote_percent` < 0 → 反对;`vote_percent` = 0 → 移除投票。 +- 当加权净投票百分比 ≥ 链属性 `committee_request_approve_min_percent` 时,请求被批准。 + +**委员会生命周期触发的虚拟操作:** + +| 虚拟操作 | 触发条件 | +|---------|---------| +| `committee_cancel_request_operation` | 请求在未批准的情况下到期 | +| `committee_approve_request_operation` | 请求达到批准阈值 | +| `committee_payout_request_operation` | 处理支付 | +| `committee_pay_request_operation` | 工作者收到付款 | + +--- + +参见:[数据类型](../data-types.md)、[操作概述](./overview.md)、[虚拟操作](../virtual-operations.md)、[委员会治理](../../governance/committee.md)。 diff --git a/@l10n/zh-CN/docs/protocol/operations/content.md b/@l10n/zh-CN/docs/protocol/operations/content.md new file mode 100644 index 0000000000..e5d5baf117 --- /dev/null +++ b/@l10n/zh-CN/docs/protocol/operations/content.md @@ -0,0 +1,104 @@ +# 内容操作 + +> **弃用通知:** `vote_operation`(ID 0)、`content_operation`(ID 1)和 `delete_content_operation`(ID 9)已**弃用**。它们保留在操作变体中以保持历史和归档兼容性,但不应在新代码中使用。`custom_operation`(ID 10)是活跃的通用数据通道。 + +--- + +## `content_operation` *(已弃用)*(ID 1) + +**授权:** `author` 的 `regular` + +创建或更新内容对象(帖子或评论)。内容通过 `author` + `permlink` 寻址。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `parent_author` | `account_name_type` | 父内容的作者;根帖子为 `""` | +| `parent_permlink` | `string` | 父内容的 permlink;对根帖子充当分类标签 | +| `author` | `account_name_type` | 内容作者 | +| `permlink` | `string` | 作者命名空间内的唯一标识符 | +| `title` | `string` | 帖子标题 | +| `body` | `string` | 帖子正文(Markdown) | +| `curation_percent` | `int16_t` | 策展奖励份额(基点,0–10000) | +| `json_metadata` | `string` | JSON 元数据字符串 | +| `extensions` | `content_extensions_type` | 可选受益人列表 | + +受益人扩展格式(在 `extensions` 内): +```json +[[0, { + "beneficiaries": [ + {"account": "bob", "weight": 2500} + ] +}]] +``` + +- `parent_author == ""` → 根帖子;否则为评论。 +- `permlink` 在每个作者内必须唯一。 +- `curation_percent` 必须在链的 `[min_curation_percent, max_curation_percent]` 范围内。 +- 受益人权重之和必须 ≤ 10000,且按账户名升序排列。 + +--- + +## `vote_operation` *(已弃用)*(ID 0) + +**授权:** `voter` 的 `regular` + +对内容进行加权投票。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `voter` | `account_name_type` | 投票账户 | +| `author` | `account_name_type` | 内容作者 | +| `permlink` | `string` | 内容 permlink | +| `weight` | `int16_t` | 投票权重:负数 = 标记,正数 = 点赞,0 = 取消投票 | + +- `weight` 范围:−10000 到 10000。 +- 标记投票可能产生额外能量成本(链属性 `flag_energy_additional_cost`)。 + +--- + +## `delete_content_operation` *(已弃用)*(ID 9) + +**授权:** `author` 的 `regular` + +删除内容对象。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `author` | `account_name_type` | 内容作者 | +| `permlink` | `string` | 要删除的内容 permlink | + +- 有待发放奖励的内容无法删除。 + +--- + +## `custom_operation`(ID 10) + +**授权:** 签名者的 `active` 或 `regular`(至少一个) + +将任意 JSON 数据发布到区块链。应用程序使用它构建自定义链上协议。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `required_active_auths` | `flat_set` | 需要活跃密钥签名的账户 | +| `required_regular_auths` | `flat_set` | 需要常规密钥签名的账户 | +| `id` | `string` | 应用定义的命名空间标识符(最多 32 个字符) | +| `json` | `string` | 有效的 UTF-8 JSON 载荷 | + +```json +[10, { + "required_active_auths": [], + "required_regular_auths": ["alice"], + "id": "my_app", + "json": "{\"action\":\"follow\",\"target\":\"bob\"}" +}] +``` + +- `required_active_auths` 或 `required_regular_auths` 至少一个必须非空。 +- `required_active_auths` 中的账户必须用其 **active** 密钥签名。 +- `required_regular_auths` 中的账户必须用其 **regular** 密钥签名。 +- 两个集合可以同时填写,用于多权限操作。 +- `json` 字段被计为数据操作 — 可能产生额外带宽成本(链属性 `data_operations_cost_additional_bandwidth`)。 + +--- + +参见:[数据类型](../data-types.md)、[操作概述](./overview.md)、[奖励](./awards.md)。 diff --git a/@l10n/zh-CN/docs/protocol/operations/escrow.md b/@l10n/zh-CN/docs/protocol/operations/escrow.md new file mode 100644 index 0000000000..a3f1d0d07f --- /dev/null +++ b/@l10n/zh-CN/docs/protocol/operations/escrow.md @@ -0,0 +1,158 @@ +# 托管操作 + +托管将 VIZ 代币保存在条件转账中:资金只有在收款方和中立代理人双方批准后才会释放,或在争议情况下由代理人仲裁。 + +**托管流程:** +``` +escrow_transfer → escrow_approve(由"to"和"agent"双方) + → escrow_release(由"from"或"to") + → [escrow_dispute] → escrow_release(仅由"agent") + ↓ + (批准截止日期已过) + → expire_escrow_ratification_operation [虚拟 — 资金返还] +``` + +--- + +## `escrow_transfer_operation`(ID 15) + +**授权:** `from` 的 `active` + +创建托管。资金立即从 `from` 转入托管余额;`agent` 和 `to` 都必须批准才能释放。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `from` | `account_name_type` | 发送方 | +| `to` | `account_name_type` | 预期收款方 | +| `agent` | `account_name_type` | 中立托管代理人(仲裁者) | +| `escrow_id` | `uint32_t` | 发送方选择的唯一 ID(默认 30) | +| `token_amount` | `asset`(VIZ) | 托管中持有的金额 | +| `fee` | `asset`(VIZ) | 代理费 — 批准后支付给代理人 | +| `ratification_deadline` | `time_point_sec` | 双方批准的截止日期 | +| `escrow_expiration` | `time_point_sec` | 如果托管从未释放的到期时间 | +| `json_metadata` | `string` | 可选条款/元数据 | + +```json +[15, { + "from": "alice", + "to": "bob", + "agent": "charlie", + "escrow_id": 1001, + "token_amount": "100.000 VIZ", + "fee": "1.000 VIZ", + "ratification_deadline": "2024-06-01T00:00:00", + "escrow_expiration": "2024-07-01T00:00:00", + "json_metadata": "{\"description\":\"payment for work\"}" +}] +``` + +- `ratification_deadline` 必须早于 `escrow_expiration`。 +- 广播时两个时间戳都必须在未来。 +- `escrow_id` 在 `from` 账户中必须唯一。 +- 如果在 `ratification_deadline` 之前未批准,虚拟 `expire_escrow_ratification_operation` 触发,资金返还给 `from`。 + +--- + +## `escrow_approve_operation`(ID 18) + +**授权:** `who` 的 `active` + +批准或拒绝托管。`to` 和 `agent` 都必须批准,托管才能激活。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `from` | `account_name_type` | 原始托管发送方 | +| `to` | `account_name_type` | 原始托管收款方 | +| `agent` | `account_name_type` | 托管代理人 | +| `who` | `account_name_type` | 批准者:必须是 `to` 或 `agent` | +| `escrow_id` | `uint32_t` | 托管 ID | +| `approve` | `bool` | `true` 批准,`false` 拒绝 | + +```json +[18, { + "from": "alice", + "to": "bob", + "agent": "charlie", + "who": "bob", + "escrow_id": 1001, + "approve": true +}] +``` + +- `who` 必须是 `to` 或 `agent`。 +- 一旦批准,无法撤销。 +- 如果 `approve: false` — 托管取消,资金返还给 `from`。 +- 必须在 `ratification_deadline` 之前广播。 + +--- + +## `escrow_dispute_operation`(ID 16) + +**授权:** `who` 的 `active` + +对已批准的托管提出争议。争议后,只有 `agent` 可以释放资金。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `from` | `account_name_type` | 原始托管发送方 | +| `to` | `account_name_type` | 原始托管收款方 | +| `agent` | `account_name_type` | 托管代理人 | +| `who` | `account_name_type` | 提出争议者:必须是 `from` 或 `to` | +| `escrow_id` | `uint32_t` | 托管 ID | + +```json +[16, { + "from": "alice", + "to": "bob", + "agent": "charlie", + "who": "alice", + "escrow_id": 1001 +}] +``` + +- 只能对**已批准**的托管(`to` 和 `agent` 都已批准)提出争议。 +- 必须在 `escrow_expiration` 之前提出。 + +--- + +## `escrow_release_operation`(ID 17) + +**授权:** `who` 的 `active` + +将托管资金释放给 `receiver`。允许部分释放。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `from` | `account_name_type` | 原始托管发送方 | +| `to` | `account_name_type` | 原始托管收款方 | +| `agent` | `account_name_type` | 托管代理人 | +| `who` | `account_name_type` | 释放资金的账户 | +| `receiver` | `account_name_type` | 接收资金的账户(必须是 `from` 或 `to`) | +| `escrow_id` | `uint32_t` | 托管 ID | +| `token_amount` | `asset`(VIZ) | 释放金额(可部分释放) | + +```json +[17, { + "from": "alice", + "to": "bob", + "agent": "charlie", + "who": "alice", + "receiver": "bob", + "escrow_id": 1001, + "token_amount": "100.000 VIZ" +}] +``` + +**释放权限规则:** + +| 状态 | 谁可以释放 | 释放给谁 | +|------|-----------|---------| +| 无争议,到期前 | `from` 或 `to` | 另一方 | +| 无争议,到期后 | `from` 或 `to` | 任意一方 | +| 争议中 | 仅 `agent` | 任意一方 | + +- 允许部分释放;剩余部分留在托管中。 + +--- + +参见:[数据类型](../data-types.md)、[操作概述](./overview.md)、[虚拟操作](../virtual-operations.md)。 diff --git a/@l10n/zh-CN/docs/protocol/operations/invites.md b/@l10n/zh-CN/docs/protocol/operations/invites.md new file mode 100644 index 0000000000..0e10717dbb --- /dev/null +++ b/@l10n/zh-CN/docs/protocol/operations/invites.md @@ -0,0 +1,117 @@ +# 邀请操作 + +邀请允许现有 VIZ 用户引入新账户,而接收方无需拥有预先存在的账户。邀请是一次性使用的密钥对,用 VIZ 代币资助;私钥充当邀请秘密。 + +**邀请流程:** +``` +create_invite → invite_registration_operation (创建新账户) + → claim_invite_balance_operation (将余额转入现有账户) + → use_invite_balance_operation (替代性领取) +``` + +--- + +## `create_invite_operation`(ID 43) + +**授权:** `creator` 的 `active` + +通过生成密钥并将 VIZ 代币锁定在其中来创建邀请。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `creator` | `account_name_type` | 创建邀请的账户 | +| `balance` | `asset`(VIZ) | 锁定在邀请中的 VIZ | +| `invite_key` | `public_key_type` | 邀请密钥对的公钥 | + +```json +[43, { + "creator": "alice", + "balance": "5.000 VIZ", + "invite_key": "VIZ5invite..." +}] +``` + +- 生成一个随机的 secp256k1 密钥对。**公钥**放入 `invite_key`;**私钥**(WIF)成为要分享的邀请秘密。 +- `balance` 必须 ≥ 链属性 `create_invite_min_balance`。 + +--- + +## `claim_invite_balance_operation`(ID 44) + +**授权:** `initiator` 的 `active` + +从邀请中领取 VIZ 余额,将其转给 `receiver`。邀请被使用,无法再次使用。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `initiator` | `account_name_type` | 领取邀请的现有账户 | +| `receiver` | `account_name_type` | 接收 VIZ 余额的账户 | +| `invite_secret` | `string` | 邀请的 WIF 私钥 | + +```json +[44, { + "initiator": "bob", + "receiver": "bob", + "invite_secret": "5Ky1MXn..." +}] +``` + +- `receiver` 可以与 `initiator` 不同 — 余额可以被重定向。 +- `invite_secret` 是邀请密钥对的 WIF 编码私钥。 + +--- + +## `invite_registration_operation`(ID 45) + +**授权:** `initiator` 的 `active` + +使用邀请创建新的区块链账户。邀请余额被转换为 SHARES 并分配给新账户。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `initiator` | `account_name_type` | 触发注册的现有账户 | +| `new_account_name` | `account_name_type` | 新账户名 | +| `invite_secret` | `string` | 邀请的 WIF 私钥 | +| `new_account_key` | `public_key_type` | 新账户的主、活跃、常规和 memo 密钥 | + +```json +[45, { + "initiator": "bob", + "new_account_name": "carol", + "invite_secret": "5Ky1MXn...", + "new_account_key": "VIZ5newacct..." +}] +``` + +- `new_account_key` 应用于所有四个权限槽(master、active、regular、memo)。 +- 邀请余额被转换为 SHARES(而非流动 VIZ)分配给新账户。 +- 使用后邀请被消耗。 + +--- + +## `use_invite_balance_operation`(ID 58) + +**授权:** `initiator` 的 `active` + +替代性邀请领取,可能将余额转换为 SHARES 给接收者而非流动 VIZ。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `initiator` | `account_name_type` | 使用邀请的账户 | +| `receiver` | `account_name_type` | 接收余额的现有账户 | +| `invite_secret` | `string` | 邀请的 WIF 私钥 | + +```json +[58, { + "initiator": "bob", + "receiver": "bob", + "invite_secret": "5Ky1MXn..." +}] +``` + +- `receiver` 必须是现有账户。 +- 使用后邀请被消耗。 + +--- + +参见:[数据类型](../data-types.md)、[操作概述](./overview.md)、[账户](./accounts.md)。 diff --git a/@l10n/zh-CN/docs/protocol/operations/overview.md b/@l10n/zh-CN/docs/protocol/operations/overview.md new file mode 100644 index 0000000000..fe92296ae7 --- /dev/null +++ b/@l10n/zh-CN/docs/protocol/operations/overview.md @@ -0,0 +1,111 @@ +# 操作概述 + +VIZ Ledger 操作是包含在交易中的原子状态变更动作。每个操作在已签名交易内序列化为 2 元素数组 `[type_id, object]`。 + +--- + +## 常规操作 + +这些是用户发起的可以广播到网络的操作。 + +| ID | 操作 | 授权级别 | 参考 | +|----|------|---------|------| +| 0 | `vote_operation` *(已弃用)* | regular | [内容](./content.md) | +| 1 | `content_operation` *(已弃用)* | regular | [内容](./content.md) | +| 2 | `transfer_operation` | active(VIZ)/ master(SHARES) | [转账](./transfers.md) | +| 3 | `transfer_to_vesting_operation` | active | [转账](./transfers.md) | +| 4 | `withdraw_vesting_operation` | active | [转账](./transfers.md) | +| 5 | `account_update_operation` | master / active | [账户](./accounts.md) | +| 6 | `witness_update_operation` | active | [验证者](./validators.md) | +| 7 | `account_witness_vote_operation` | active | [验证者](./validators.md) | +| 8 | `account_witness_proxy_operation` | active | [验证者](./validators.md) | +| 9 | `delete_content_operation` *(已弃用)* | regular | [内容](./content.md) | +| 10 | `custom_operation` | active / regular | [内容](./content.md) | +| 11 | `set_withdraw_vesting_route_operation` | active | [转账](./transfers.md) | +| 12 | `request_account_recovery_operation` | active | [恢复](./recovery.md) | +| 13 | `recover_account_operation` | master(×2) | [恢复](./recovery.md) | +| 14 | `change_recovery_account_operation` | master | [恢复](./recovery.md) | +| 15 | `escrow_transfer_operation` | active | [托管](./escrow.md) | +| 16 | `escrow_dispute_operation` | active | [托管](./escrow.md) | +| 17 | `escrow_release_operation` | active | [托管](./escrow.md) | +| 18 | `escrow_approve_operation` | active | [托管](./escrow.md) | +| 19 | `delegate_vesting_shares_operation` | active | [转账](./transfers.md) | +| 20 | `account_create_operation` | active | [账户](./accounts.md) | +| 21 | `account_metadata_operation` | regular | [账户](./accounts.md) | +| 22 | `proposal_create_operation` | active | [提案](./proposals.md) | +| 23 | `proposal_update_operation` | 不定 | [提案](./proposals.md) | +| 24 | `proposal_delete_operation` | active | [提案](./proposals.md) | +| 25 | `chain_properties_update_operation` | active | [验证者](./validators.md) | +| 35 | `committee_worker_create_request_operation` | regular | [委员会](./committee.md) | +| 36 | `committee_worker_cancel_request_operation` | regular | [委员会](./committee.md) | +| 37 | `committee_vote_request_operation` | regular | [委员会](./committee.md) | +| 43 | `create_invite_operation` | active | [邀请](./invites.md) | +| 44 | `claim_invite_balance_operation` | active | [邀请](./invites.md) | +| 45 | `invite_registration_operation` | active | [邀请](./invites.md) | +| 46 | `versioned_chain_properties_update_operation` | active | [验证者](./validators.md) | +| 47 | `award_operation` | regular | [奖励](./awards.md) | +| 50 | `set_paid_subscription_operation` | active | [订阅](./subscriptions.md) | +| 51 | `paid_subscribe_operation` | active | [订阅](./subscriptions.md) | +| 54 | `set_account_price_operation` | master | [账户市场](./account-market.md) | +| 55 | `set_subaccount_price_operation` | master | [账户市场](./account-market.md) | +| 56 | `buy_account_operation` | active | [账户市场](./account-market.md) | +| 58 | `use_invite_balance_operation` | active | [邀请](./invites.md) | +| 60 | `fixed_award_operation` | regular | [奖励](./awards.md) | +| 61 | `target_account_sale_operation` | master | [账户市场](./account-market.md) | + +--- + +## 虚拟操作 + +虚拟操作由区块链本身在区块处理过程中生成。它们从不由用户广播——它们仅出现在账户历史和区块数据中供参考。 + +| ID | 操作 | 触发条件 | 参考 | +|----|------|---------|------| +| 26 | `author_reward_operation` | 内容支付 | [虚拟操作](../virtual-operations.md) | +| 27 | `curation_reward_operation` | 内容支付 | [虚拟操作](../virtual-operations.md) | +| 28 | `content_reward_operation` | 内容支付 | [虚拟操作](../virtual-operations.md) | +| 29 | `fill_vesting_withdraw_operation` | 提取间隔触发 | [虚拟操作](../virtual-operations.md) | +| 30 | `shutdown_witness_operation` | 验证者停用 | [虚拟操作](../virtual-operations.md) | +| 31 | `hardfork_operation` | 硬分叉激活 | [虚拟操作](../virtual-operations.md) | +| 32 | `content_payout_update_operation` | 内容支付更新 | [虚拟操作](../virtual-operations.md) | +| 33 | `content_benefactor_reward_operation` | 内容支付 | [虚拟操作](../virtual-operations.md) | +| 34 | `return_vesting_delegation_operation` | 委托返回窗口结束 | [虚拟操作](../virtual-operations.md) | +| 38 | `committee_cancel_request_operation` | 委员会请求过期 | [虚拟操作](../virtual-operations.md) | +| 39 | `committee_approve_request_operation` | 委员会请求获批 | [虚拟操作](../virtual-operations.md) | +| 40 | `committee_payout_request_operation` | 委员会支付处理 | [虚拟操作](../virtual-operations.md) | +| 41 | `committee_pay_request_operation` | 委员会工作者已付款 | [虚拟操作](../virtual-operations.md) | +| 42 | `witness_reward_operation` | 区块已生产 | [虚拟操作](../virtual-operations.md) | +| 48 | `receive_award_operation` | 奖励已接收 | [虚拟操作](../virtual-operations.md) | +| 49 | `benefactor_award_operation` | 带受益人的奖励 | [虚拟操作](../virtual-operations.md) | +| 52 | `paid_subscription_action_operation` | 订阅支付 | [虚拟操作](../virtual-operations.md) | +| 53 | `cancel_paid_subscription_operation` | 订阅取消/过期 | [虚拟操作](../virtual-operations.md) | +| 57 | `account_sale_operation` | 账户已售出 | [虚拟操作](../virtual-operations.md) | +| 59 | `expire_escrow_ratification_operation` | 托管截止日期错过 | [虚拟操作](../virtual-operations.md) | +| 62 | `bid_operation` | 拍卖出价 | [虚拟操作](../virtual-operations.md) | +| 63 | `outbid_operation` | 拍卖被超价 | [虚拟操作](../virtual-operations.md) | + +--- + +## 交易构建 + +```json +{ + "ref_block_num": 12345, + "ref_block_prefix": 678901234, + "expiration": "2024-01-15T12:01:00", + "operations": [ + [2, { "from": "alice", "to": "bob", "amount": "1.000 VIZ", "memo": "" }] + ], + "extensions": [], + "signatures": ["1f2a3b..."] +} +``` + +- `ref_block_num` = `head_block_number & 0xFFFF` +- `ref_block_prefix` = `block_id` 的第 4–7 字节(小端 `uint32`) +- `expiration` = 当前 UTC 时间 + TTL(最长建议:60 秒) +- 签名:`sha256(chain_id || serialized_tx)` → 紧凑 secp256k1 ECDSA 签名 + +--- + +参见:[数据类型](../data-types.md)、[虚拟操作](../virtual-operations.md)、[JSON-RPC API](../../api/json-rpc.md)。 diff --git a/@l10n/zh-CN/docs/protocol/operations/proposals.md b/@l10n/zh-CN/docs/protocol/operations/proposals.md new file mode 100644 index 0000000000..3717095d9d --- /dev/null +++ b/@l10n/zh-CN/docs/protocol/operations/proposals.md @@ -0,0 +1,125 @@ +# 提案操作 + +提案实现多签名治理:一个账户创建一组操作,需要规定签署人集合批准后才能执行。当收集到足够多的批准后,提案自动执行。 + +--- + +## `proposal_create_operation`(ID 22) + +**授权:** `author` 的 `active` + +创建交易提案。提案由 `author` + `title` 对标识。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `author` | `account_name_type` | 创建提案的账户 | +| `title` | `string` | 每个作者唯一的标题(用作提案 ID) | +| `memo` | `string` | 人类可读的描述 | +| `expiration_time` | `time_point_sec` | 提案过期时间 | +| `proposed_operations` | `vector` | 批准后执行的操作 | +| `review_period_time` | `optional` | 可选:此时间后不再接受新批准 | +| `extensions` | `extensions_type` | 始终为 `[]` | + +`proposed_operations` 中的每个条目是一个 `operation_wrapper`: +```json +{"op": [type_id, operation_object]} +``` + +```json +[22, { + "author": "alice", + "title": "transfer-proposal-001", + "memo": "Joint transfer to shared fund", + "expiration_time": "2024-12-31T23:59:59", + "proposed_operations": [ + { + "op": [2, { + "from": "multisig-wallet", + "to": "fund", + "amount": "1000.000 VIZ", + "memo": "" + }] + } + ], + "review_period_time": null, + "extensions": [] +}] +``` + +- `title` 对每个 `author` 必须唯一。 +- `expiration_time` 必须在未来。 +- 若设置了 `review_period_time`,它必须早于 `expiration_time`;此时间后不再接受新批准。 +- `proposed_operations` 可包含任意类型的多个操作。 + +--- + +## `proposal_update_operation`(ID 23) + +**授权:** 取决于被修改的批准集合 + +添加或移除批准。收集到足够批准后提案自动执行。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `author` | `account_name_type` | 提案作者 | +| `title` | `string` | 提案标题 | +| `active_approvals_to_add` | `flat_set` | 授予 active 批准的账户 | +| `active_approvals_to_remove` | `flat_set` | 撤销 active 批准的账户 | +| `master_approvals_to_add` | `flat_set` | 授予 master 批准的账户 | +| `master_approvals_to_remove` | `flat_set` | 撤销 master 批准的账户 | +| `regular_approvals_to_add` | `flat_set` | 授予 regular 批准的账户 | +| `regular_approvals_to_remove` | `flat_set` | 撤销 regular 批准的账户 | +| `key_approvals_to_add` | `flat_set` | 授予批准的公钥 | +| `key_approvals_to_remove` | `flat_set` | 撤销批准的公钥 | +| `extensions` | `extensions_type` | 始终为 `[]` | + +```json +[23, { + "author": "alice", + "title": "transfer-proposal-001", + "active_approvals_to_add": ["bob"], + "active_approvals_to_remove": [], + "master_approvals_to_add": [], + "master_approvals_to_remove": [], + "regular_approvals_to_add": [], + "regular_approvals_to_remove": [], + "key_approvals_to_add": [], + "key_approvals_to_remove": [], + "extensions": [] +}] +``` + +- 交易必须由与被添加或移除批准对应的密钥签名。 +- 不需要的 `*_to_add` 和 `*_to_remove` 字段默认为 `[]`。 +- 执行后提案标记为已完成;后续更新将被拒绝。 + +--- + +## `proposal_delete_operation`(ID 24) + +**授权:** `requester` 的 `active` + +永久删除(否决)提案。提案的任何必需权限方均可调用此操作。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `author` | `account_name_type` | 提案作者 | +| `title` | `string` | 提案标题 | +| `requester` | `account_name_type` | 请求删除的账户 | +| `extensions` | `extensions_type` | 始终为 `[]` | + +```json +[24, { + "author": "alice", + "title": "transfer-proposal-001", + "requester": "bob", + "extensions": [] +}] +``` + +- `requester` 必须是该提案的必需权限方。 +- 删除是永久性的,无法撤销。 + +--- + +参见:[数据类型](../data-types.md)、[操作概述](./overview.md)、[Database API — get_proposed_transactions](../../plugins/database-api.md#get_proposed_transactionsaccount-from-limit)。 diff --git a/@l10n/zh-CN/docs/protocol/operations/recovery.md b/@l10n/zh-CN/docs/protocol/operations/recovery.md new file mode 100644 index 0000000000..0206f81a09 --- /dev/null +++ b/@l10n/zh-CN/docs/protocol/operations/recovery.md @@ -0,0 +1,106 @@ +# 账户恢复操作 + +恢复机制允许预先指定的受信账户(*恢复账户*)使用最近有效的主权限帮助恢复对被入侵账户的访问。 + +**恢复流程:** +``` +request_account_recovery → recover_account (24 小时内) +change_recovery_account (30 天延迟后生效) +``` + +--- + +## `request_account_recovery_operation`(ID 12) + +**授权:** `recovery_account` 的 `active` + +发起账户恢复请求。恢复账户为被入侵账户提议新的主权限;账户所有者有 24 小时通过 `recover_account_operation` 确认。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `recovery_account` | `account_name_type` | 受信恢复账户 | +| `account_to_recover` | `account_name_type` | 要恢复的被入侵账户 | +| `new_master_authority` | `authority` | 确认后要分配的新主权限 | +| `extensions` | `extensions_type` | 始终为 `[]` | + +```json +[12, { + "recovery_account": "recover-service", + "account_to_recover": "alice", + "new_master_authority": { + "weight_threshold": 1, + "account_auths": [], + "key_auths": [["VIZ5newkey...", 1]] + }, + "extensions": [] +}] +``` + +- 只有 `account_to_recover` 的指定恢复账户才能发送此操作。 +- 每个账户只允许一个活跃恢复请求;再次发送会更新请求并重置 24 小时窗口。 +- 取消:将 `new_master_authority.weight_threshold` 设置为 `0`。 + +--- + +## `recover_account_operation`(ID 13) + +**授权:** 同时满足 `new_master_authority` **和** `recent_master_authority` 的签名 + +通过证明过去的所有权确认恢复。必须在恢复请求后 24 小时内广播。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `account_to_recover` | `account_name_type` | 正在恢复的账户 | +| `new_master_authority` | `authority` | 新的主权限(必须与恢复请求完全匹配) | +| `recent_master_authority` | `authority` | 过去 30 天内有效的主权限 | +| `extensions` | `extensions_type` | 始终为 `[]` | + +```json +[13, { + "account_to_recover": "alice", + "new_master_authority": { + "weight_threshold": 1, + "account_auths": [], + "key_auths": [["VIZ5newkey...", 1]] + }, + "recent_master_authority": { + "weight_threshold": 1, + "account_auths": [], + "key_auths": [["VIZ5oldkey...", 1]] + }, + "extensions": [] +}] +``` + +- 交易必须由同时满足新旧两种权限的密钥签名。 +- `new_master_authority` 必须与待处理恢复请求中的完全一致。 +- 恢复后,旧的主密钥失效。 + +--- + +## `change_recovery_account_operation`(ID 14) + +**授权:** `account_to_recover` 的 `master` + +更改恢复账户。更改在 **30 天延迟**后生效,以防止攻击者在活跃攻击期间替换恢复账户。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `account_to_recover` | `account_name_type` | 更改其恢复账户的账户 | +| `new_recovery_account` | `account_name_type` | 新的恢复账户名 | +| `extensions` | `extensions_type` | 始终为 `[]` | + +```json +[14, { + "account_to_recover": "alice", + "new_recovery_account": "new-recovery-service", + "extensions": [] +}] +``` + +- `new_recovery_account` 必须是现有账户。 +- 如果 `new_recovery_account` 为 `""`,得票最多的验证者成为恢复账户。 + +--- + +参见:[数据类型](../data-types.md)、[操作概述](./overview.md)、[账户](./accounts.md)。 diff --git a/@l10n/zh-CN/docs/protocol/operations/subscriptions.md b/@l10n/zh-CN/docs/protocol/operations/subscriptions.md new file mode 100644 index 0000000000..dd31535b76 --- /dev/null +++ b/@l10n/zh-CN/docs/protocol/operations/subscriptions.md @@ -0,0 +1,77 @@ +# 付费订阅操作 + +付费订阅允许账户提供以 VIZ 代币支付的分级定期服务,支持可选的自动续订。 + +--- + +## `set_paid_subscription_operation`(ID 50) + +**授权:** `account` 的 `active` + +创建或更新订阅服务。首次创建时收取 `create_paid_subscription_fee`。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `account` | `account_name_type` | 提供订阅的账户 | +| `url` | `string` | 包含订阅详情的 URL | +| `levels` | `uint16_t` | 订阅等级数量(≥ 1) | +| `amount` | `asset`(VIZ) | 每期每单位等级的基础价格 | +| `period` | `uint16_t` | 订阅周期(天,≥ 1) | + +```json +[50, { + "account": "alice", + "url": "https://alice.example.com/subscribe", + "levels": 3, + "amount": "10.000 VIZ", + "period": 30 +}] +``` + +- 订阅者的实际费用 = `amount × level`。 +- `levels = 3`,`amount = "10.000 VIZ"` → 等级 1 每期 10 VIZ,等级 2 每期 20 VIZ,等级 3 每期 30 VIZ。 +- 更新此操作会更改未来订阅的参数;现有活跃订阅在续订前继续按旧条款执行。 + +--- + +## `paid_subscribe_operation`(ID 51) + +**授权:** `subscriber` 的 `active` + +订阅或续订付费订阅。代币立即从 `subscriber` 转账给 `account`。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `subscriber` | `account_name_type` | 订阅账户 | +| `account` | `account_name_type` | 提供订阅的账户 | +| `level` | `uint16_t` | 订阅等级(1 – `levels`) | +| `amount` | `asset`(VIZ) | 支付金额 | +| `period` | `uint16_t` | 支付的周期数 | +| `auto_renewal` | `bool` | 启用每期自动续订 | + +```json +[51, { + "subscriber": "bob", + "account": "alice", + "level": 2, + "amount": "20.000 VIZ", + "period": 1, + "auto_renewal": true +}] +``` + +- `amount` 必须与 `subscription.amount × level × period` 完全匹配。 +- `level` 必须在范围 [1, `subscription.levels`] 内。 +- `auto_renewal: true` — 在余额充足的情况下每期自动扣除代币。 +- `auto_renewal: false` — 一次性订阅;在付费周期结束后到期。 + +**虚拟操作:** + +| 虚拟操作 | 触发条件 | +|---------|---------| +| `paid_subscription_action_operation` | 支付已处理 | +| `cancel_paid_subscription_operation` | 订阅到期或已取消 | + +--- + +参见:[数据类型](../data-types.md)、[操作概述](./overview.md)、[虚拟操作](../virtual-operations.md)。 diff --git a/@l10n/zh-CN/docs/protocol/operations/transfers.md b/@l10n/zh-CN/docs/protocol/operations/transfers.md new file mode 100644 index 0000000000..dd24524885 --- /dev/null +++ b/@l10n/zh-CN/docs/protocol/operations/transfers.md @@ -0,0 +1,136 @@ +# 转账操作 + +--- + +## `transfer_operation`(ID 2) + +**授权:** `from` 的 `active`(VIZ)/ `from` 的 `master`(SHARES) + +在账户间转账 VIZ 或 SHARES 代币。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `from` | `account_name_type` | 发送账户 | +| `to` | `account_name_type` | 接收账户 | +| `amount` | `asset` | 转账金额(VIZ 或 SHARES) | +| `memo` | `string` | Memo 文本(明文或加密;可为 `""`) | + +```json +[2, { + "from": "alice", + "to": "bob", + "amount": "10.000 VIZ", + "memo": "payment for services" +}] +``` + +- `amount.symbol` 必须为 `VIZ` 或 `SHARES`。 +- VIZ 转账需要 **active** 权限;SHARES 转账需要 **master** 权限。 +- 加密 memo 格式:`#` 后跟 base58 编码的密文。 + +--- + +## `transfer_to_vesting_operation`(ID 3) + +**授权:** `from` 的 `active` + +将流动 VIZ 转换为 SHARES(质押)。SHARES 可以被转入不同的账户。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `from` | `account_name_type` | 提供 VIZ 的账户 | +| `to` | `account_name_type` | 接收 SHARES 的账户(可与 `from` 相同) | +| `amount` | `asset`(VIZ) | 要质押的 VIZ 数量 | + +```json +[3, { + "from": "alice", + "to": "alice", + "amount": "100.000 VIZ" +}] +``` + +- `amount.symbol` 必须为 `VIZ`。 +- `to` 可以是任意现有账户 — 适合赠送质押份额。 + +--- + +## `withdraw_vesting_operation`(ID 4) + +**授权:** `account` 的 `active` + +启动通过多个间隔逐步将 SHARES 提取回流动 VIZ 的过程。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `account` | `account_name_type` | 发起提取的账户 | +| `vesting_shares` | `asset`(SHARES) | 要提取的总 SHARES;`0.000000 SHARES` 取消 | + +```json +[4, { + "account": "alice", + "vesting_shares": "1000.000000 SHARES" +}] +``` + +- 提取分布在 `withdraw_intervals` 个间隔上(链属性,默认 28)。 +- 每个间隔:转换 `vesting_shares / withdraw_intervals` SHARES。 +- 设置为 `"0.000000 SHARES"` 取消活跃提取。 + +--- + +## `set_withdraw_vesting_route_operation`(ID 11) + +**授权:** `from_account` 的 `active` + +将 vesting 提取的一定百分比路由到指定账户,可选择将路由部分重新质押。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `from_account` | `account_name_type` | 提取被路由的账户 | +| `to_account` | `account_name_type` | 目标账户 | +| `percent` | `uint16_t` | 路由百分比(0–10000 基点) | +| `auto_vest` | `bool` | 如果为 `true`,路由的代币立即在 `to_account` 中重新质押 | + +```json +[11, { + "from_account": "alice", + "to_account": "bob", + "percent": 5000, + "auto_vest": false +}] +``` + +- `percent` = 0 删除到 `to_account` 的此路由。 +- `from_account` 所有路由之和不得超过 10000。 +- 允许到不同账户的多条路由。 + +--- + +## `delegate_vesting_shares_operation`(ID 19) + +**授权:** `delegator` 的 `active` + +将 SHARES 委托给另一个账户。受托方获得带宽和投票权;所有权保留在委托方。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `delegator` | `account_name_type` | 委托 SHARES 的账户 | +| `delegatee` | `account_name_type` | 接收委托的账户 | +| `vesting_shares` | `asset`(SHARES) | 委托金额;`0.000000 SHARES` 移除委托 | + +```json +[19, { + "delegator": "alice", + "delegatee": "bob", + "vesting_shares": "500.000000 SHARES" +}] +``` + +- `vesting_shares` 必须 ≥ 链属性 `min_delegation`,或恰好为 `0.000000 SHARES` 以移除。 +- 移除委托时,SHARES 进入 7 天返还窗口后才被重新记入。 +- 返还窗口结束时触发虚拟 `return_vesting_delegation_operation`。 + +--- + +参见:[数据类型](../data-types.md)、[操作概述](./overview.md)。 diff --git a/@l10n/zh-CN/docs/protocol/operations/validators.md b/@l10n/zh-CN/docs/protocol/operations/validators.md new file mode 100644 index 0000000000..e4893df7f4 --- /dev/null +++ b/@l10n/zh-CN/docs/protocol/operations/validators.md @@ -0,0 +1,166 @@ +# 验证者操作 + +--- + +## `witness_update_operation`(ID 6) + +**授权:** `owner` 的 `active` + +注册或更新验证者节点。将 `block_signing_key` 设置为空密钥可将验证者从区块生产中移除。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `owner` | `account_name_type` | 验证者账户名 | +| `url` | `string` | 验证者网站或信息 URL(非空,最多 256 字节) | +| `block_signing_key` | `public_key_type` | 用于签署生产区块的密钥 | + +```json +[6, { + "owner": "alice", + "url": "https://alice.example.com", + "block_signing_key": "VIZ5hqSa4NkEZGAMUpoH5EaEr64mBJuMcPpGjvk8qb7hcPFTbXSQ9" +}] +``` + +- **空密钥**(停用):`"VIZ1111111111111111111111111111111114T1Anm"` — 从区块生产中移除,不删除验证者记录。 +- 广播此操作需要 `witness_declaration_fee`(支付到委员会基金)。 + +--- + +## `chain_properties_update_operation`(ID 25) + +**授权:** `owner` 的 `active` + +对基础链属性投票(`chain_properties_init` 格式)。链上值是所有活跃验证者的中位数。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `owner` | `account_name_type` | 投票的验证者 | +| `props` | `chain_properties_init` | 提议的链参数 | + +```json +[25, { + "owner": "alice", + "props": { + "account_creation_fee": "1.000 VIZ", + "maximum_block_size": 65536, + "create_account_delegation_ratio": 10, + "create_account_delegation_time": 2592000, + "min_delegation": "1.000 VIZ", + "min_curation_percent": 0, + "max_curation_percent": 10000, + "bandwidth_reserve_percent": 1000, + "bandwidth_reserve_below": "1.000000 SHARES", + "flag_energy_additional_cost": 1000, + "vote_accounting_min_rshares": 0, + "committee_request_approve_min_percent": 1000 + } +}] +``` + +- 所有百分比字段以基点表示(0–10000)。 +- `min_curation_percent` 必须 ≤ `max_curation_percent`。 +- 对于 HF9+ 扩展属性,使用 `versioned_chain_properties_update_operation`(ID 46)。 + +--- + +## `versioned_chain_properties_update_operation`(ID 46) + +**授权:** `owner` 的 `active` + +对支持所有硬分叉扩展的版本化链属性投票。当前节点优先于 `chain_properties_update_operation`。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `owner` | `account_name_type` | 投票的验证者 | +| `props` | `versioned_chain_properties` | 序列化为 `[index, object]` 的版本化属性 | + +```json +[46, { + "owner": "alice", + "props": [3, { + "account_creation_fee": "1.000 VIZ", + "maximum_block_size": 65536, + "create_account_delegation_ratio": 10, + "create_account_delegation_time": 2592000, + "min_delegation": "1.000 VIZ", + "min_curation_percent": 0, + "max_curation_percent": 10000, + "bandwidth_reserve_percent": 1000, + "bandwidth_reserve_below": "1.000000 SHARES", + "flag_energy_additional_cost": 1000, + "vote_accounting_min_rshares": 0, + "committee_request_approve_min_percent": 1000, + "inflation_witness_percent": 2000, + "inflation_ratio_committee_vs_reward_fund": 1000, + "inflation_recalc_period": 28800, + "data_operations_cost_additional_bandwidth": 0, + "witness_miss_penalty_percent": 100, + "witness_miss_penalty_duration": 86400, + "create_invite_min_balance": "1.000 VIZ", + "committee_create_request_fee": "1.000 VIZ", + "create_paid_subscription_fee": "1.000 VIZ", + "account_on_sale_fee": "10.000 VIZ", + "subaccount_on_sale_fee": "1.000 VIZ", + "witness_declaration_fee": "1.000 VIZ", + "withdraw_intervals": 28 + }] +}] +``` + +- `props` 是静态变体:使用索引 `3` 表示 `chain_properties_hf9`(当前)。 +- 各版本索引的完整字段列表见[数据类型](../data-types.md#versioned_chain_properties)。 + +--- + +## `account_witness_vote_operation`(ID 7) + +**授权:** `account` 的 `active` + +为验证者投票或移除投票。按累积投票权重排名前 21 的验证者生产区块。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `account` | `account_name_type` | 投票账户 | +| `witness` | `account_name_type` | 要投票的验证者 | +| `approve` | `bool` | `true` 添加投票,`false` 移除投票 | + +```json +[7, { + "account": "alice", + "witness": "bob", + "approve": true +}] +``` + +- 投票权重与投票者的 SHARES 质押成比例。 +- `approve: false` 移除之前投出的票。 + +--- + +## `account_witness_proxy_operation`(ID 8) + +**授权:** `account` 的 `active` + +将所有验证者投票委托给代理账户。设置代理时,所有现有直接投票被移除。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `account` | `account_name_type` | 设置代理的账户 | +| `proxy` | `account_name_type` | 代理账户;`""` 移除代理 | + +```json +[8, { + "account": "alice", + "proxy": "bob" +}] +``` + +- `proxy: ""`(空字符串)移除代理并恢复直接投票。 +- 不能将代理设置为自己。 +- 代理链被传递解析(A→B→C);最大链深度受到限制。 +- 设置代理会移除所有直接验证者投票。 + +--- + +参见:[数据类型](../data-types.md)、[操作概述](./overview.md)、[链属性](../../governance/chain-properties.md)。 diff --git a/@l10n/zh-CN/docs/protocol/virtual-operations.md b/@l10n/zh-CN/docs/protocol/virtual-operations.md new file mode 100644 index 0000000000..221005aad8 --- /dev/null +++ b/@l10n/zh-CN/docs/protocol/virtual-operations.md @@ -0,0 +1,354 @@ +# 虚拟操作 + +虚拟操作由区块链自身在区块处理期间生成。它们**从不由用户广播** — 仅出现在账户历史和区块数据中,用于信息展示。 + +虚拟操作与普通操作共享相同的操作变体,但类型 ID 更高。可通过 `account_history` API 或区块流观察它们。 + +--- + +## 内容奖励 *(已弃用)* + +### `author_reward_operation`(ID 26) + +**触发条件:** 内容帖子结算 + +当作者从内容结算中获得其奖励份额时触发。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `author` | `account_name_type` | 内容作者 | +| `permlink` | `string` | 内容 permlink | +| `token_payout` | `asset`(VIZ) | 流动 VIZ 部分 | +| `vesting_payout` | `asset`(SHARES) | 锁仓部分 | + +--- + +### `curation_reward_operation`(ID 27) + +**触发条件:** 内容帖子结算 + +当策展人获得其策展奖励时触发。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `curator` | `account_name_type` | 策展人账户 | +| `reward` | `asset`(SHARES) | 策展奖励 | +| `content_author` | `account_name_type` | 被策展内容的作者 | +| `content_permlink` | `string` | 被策展内容的 permlink | + +--- + +### `content_reward_operation`(ID 28) + +**触发条件:** 内容帖子结算 + +当帖子到达结算时间时触发。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `author` | `account_name_type` | 内容作者 | +| `permlink` | `string` | 内容 permlink | +| `payout` | `asset` | 总结算金额 | + +--- + +### `content_payout_update_operation`(ID 32) + +**触发条件:** 内容结算重新计算(如投票变化后) + +| 字段 | 类型 | 描述 | +|------|------|------| +| `author` | `account_name_type` | 内容作者 | +| `permlink` | `string` | 内容 permlink | + +--- + +### `content_benefactor_reward_operation`(ID 33) + +**触发条件:** 内容帖子结算 — 为每个受益人触发 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `benefactor` | `account_name_type` | 受益人账户 | +| `author` | `account_name_type` | 内容作者 | +| `permlink` | `string` | 内容 permlink | +| `reward` | `asset` | 受益人奖励份额 | + +--- + +## 锁仓提取 + +### `fill_vesting_withdraw_operation`(ID 29) + +**触发条件:** 每个锁仓提取间隔触发 + +每个活跃提取路径每个间隔触发一次。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `from_account` | `account_name_type` | 提取账户 | +| `to_account` | `account_name_type` | 目标账户(可通过提取路由不同) | +| `withdrawn` | `asset`(SHARES) | 本间隔提取的 SHARES 数量 | +| `deposited` | `asset` | 存入 `to_account`(VIZ,或在 `auto_vest = true` 时为 SHARES) | + +```json +[29, { + "from_account": "alice", + "to_account": "alice", + "withdrawn": "35.714285 SHARES", + "deposited": "10.000 VIZ" +}] +``` + +--- + +### `return_vesting_delegation_operation`(ID 34) + +**触发条件:** `delegate_vesting_shares_operation` 零额度后 7 天返回窗口结束 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `account` | `account_name_type` | 接收返还 SHARES 的账户 | +| `vesting_shares` | `asset`(SHARES) | 从临时状态返还的 SHARES | + +--- + +## 验证者操作 + +### `shutdown_witness_operation`(ID 30) + +**触发条件:** 验证者因票权不足被停用 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `owner` | `account_name_type` | 被停用的验证者 | + +--- + +### `witness_reward_operation`(ID 42) + +**触发条件:** 生产区块 — 验证者获得区块奖励 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `witness` | `account_name_type` | 验证者账户 | +| `shares` | `asset`(SHARES) | 区块奖励 | + +```json +[42, { + "witness": "alice", + "shares": "1.234567 SHARES" +}] +``` + +--- + +## 网络事件 + +### `hardfork_operation`(ID 31) + +**触发条件:** 网络硬分叉激活 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `hardfork_id` | `uint32_t` | 硬分叉编号 | + +--- + +## 奖励 + +### `receive_award_operation`(ID 48) + +**触发条件:** `award_operation` 或 `fixed_award_operation` + +为奖励的主要接收者触发。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `initiator` | `account_name_type` | 发起奖励的账户 | +| `receiver` | `account_name_type` | 接收奖励的账户 | +| `custom_sequence` | `uint64_t` | 奖励操作中的应用程序序列号 | +| `memo` | `string` | 奖励操作的备注 | +| `shares` | `asset`(SHARES) | 收到的 SHARES | + +```json +[48, { + "initiator": "alice", + "receiver": "bob", + "custom_sequence": 0, + "memo": "great article!", + "shares": "5.000000 SHARES" +}] +``` + +--- + +### `benefactor_award_operation`(ID 49) + +**触发条件:** `award_operation` 或 `fixed_award_operation` 含受益人 + +每个受益人触发一次。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `initiator` | `account_name_type` | 发起奖励的账户 | +| `benefactor` | `account_name_type` | 受益人账户 | +| `receiver` | `account_name_type` | 奖励的主要接收者 | +| `custom_sequence` | `uint64_t` | 应用程序序列号 | +| `memo` | `string` | 奖励操作的备注 | +| `shares` | `asset`(SHARES) | 受益人收到的 SHARES | + +--- + +## 委员会 + +### `committee_cancel_request_operation`(ID 38) + +**触发条件:** 委员会资金请求在未达到批准阈值的情况下到期 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `request_id` | `uint32_t` | 已取消请求的 ID | + +--- + +### `committee_approve_request_operation`(ID 39) + +**触发条件:** 委员会资金请求达到所需批准阈值 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `request_id` | `uint32_t` | 已批准请求的 ID | + +--- + +### `committee_payout_request_operation`(ID 40) + +**触发条件:** 委员会请求结算处理完成 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `request_id` | `uint32_t` | 已结算请求的 ID | + +--- + +### `committee_pay_request_operation`(ID 41) + +**触发条件:** 工作者从委员会基金收到付款 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `worker` | `account_name_type` | 工作者账户 | +| `request_id` | `uint32_t` | 委员会请求 ID | +| `tokens` | `asset`(VIZ) | 支付金额 | + +```json +[41, { + "worker": "alice", + "request_id": 42, + "tokens": "250.000 VIZ" +}] +``` + +--- + +## 付费订阅 + +### `paid_subscription_action_operation`(ID 52) + +**触发条件:** 执行 `paid_subscribe_operation` 或处理自动续订付款 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `subscriber` | `account_name_type` | 订阅者账户 | +| `account` | `account_name_type` | 订阅提供者 | +| `level` | `uint16_t` | 订阅等级 | +| `amount` | `asset`(VIZ) | 支付金额 | +| `period` | `uint16_t` | 周期数 | +| `summary_duration_sec` | `uint64_t` | 订阅累计时长(秒) | +| `summary_amount` | `asset`(VIZ) | 迄今总支付金额 | + +--- + +### `cancel_paid_subscription_operation`(ID 53) + +**触发条件:** 订阅到期或自动续订余额不足 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `subscriber` | `account_name_type` | 订阅者账户 | +| `account` | `account_name_type` | 订阅提供者 | + +--- + +## 账户市场 + +### `account_sale_operation`(ID 57) + +**触发条件:** `buy_account_operation` 成功完成 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `account` | `account_name_type` | 已出售的账户 | +| `price` | `asset`(VIZ) | 出售价格 | +| `buyer` | `account_name_type` | 买家账户 | +| `seller` | `account_name_type` | 卖家(付款接收方) | + +```json +[57, { + "account": "alice", + "price": "1000.000 VIZ", + "buyer": "bob", + "seller": "alice" +}] +``` + +--- + +### `bid_operation`(ID 62) + +**触发条件:** 对拍卖中的账户出现新竞价 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `account` | `account_name_type` | 被竞价的账户 | +| `bidder` | `account_name_type` | 出价账户 | +| `bid` | `asset`(VIZ) | 出价金额 | + +--- + +### `outbid_operation`(ID 63) + +**触发条件:** 之前的出价被更高出价取代 + +为被超出的账户触发;之前的出价金额将返还。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `account` | `account_name_type` | 被竞价的账户 | +| `bidder` | `account_name_type` | 被超出的账户 | +| `bid` | `asset`(VIZ) | 返还的出价金额 | + +--- + +## 托管 + +### `expire_escrow_ratification_operation`(ID 59) + +**触发条件:** 错过托管 `ratification_deadline` — `to` 和 `agent` 均未及时批准 + +所有锁定资金返还给 `from`。 + +| 字段 | 类型 | 描述 | +|------|------|------| +| `from` | `account_name_type` | 原始托管发送方 | +| `to` | `account_name_type` | 预期接收方 | +| `agent` | `account_name_type` | 托管代理人 | +| `escrow_id` | `uint32_t` | 托管 ID | +| `token_amount` | `asset`(VIZ) | 返还的代币金额 | +| `fee` | `asset`(VIZ) | 返还的手续费(托管未批准,代理人不获付款) | +| `ratification_deadline` | `time_point_sec` | 错过的截止时间 | + +--- + +参见:[操作概述](./operations/overview.md)、[奖励](./operations/awards.md)、[委员会](./operations/committee.md)。 diff --git a/@l10n/zh-CN/docs/storage/block-log.md b/@l10n/zh-CN/docs/storage/block-log.md new file mode 100644 index 0000000000..934bb4d432 --- /dev/null +++ b/@l10n/zh-CN/docs/storage/block-log.md @@ -0,0 +1,166 @@ +# 区块日志 + +VIZ 将区块存储在二进制日志文件中。存在两种变体: + +| 变体 | 文件 | 用途 | +|------|------|------| +| `block_log` | `block_log` + `block_log.index` | 完整历史(归档节点) | +| `dlt_block_log` | `dlt_block_log` + `dlt_block_log.index` | 滚动窗口(DLT/快照节点) | + +两者共享相同的数据文件格式;索引格式略有不同。 + +--- + +## 二进制序列化(`fc::raw`) + +所有数据使用小端序编码。 + +| 类型 | 格式 | +|------|------| +| `uint8_t` – `uint64_t` | 固定宽度小端序 | +| `fc::unsigned_int` | 可变长度(varint):每字节 7 个数据位 + 1 个续接位 | +| `string` | `[varint: 长度][UTF-8 字节]` | +| `vector` | `[varint: 数量][元素...]` | +| `optional` | `[uint8: 0 或 1][值(若为 1)]` | +| `static_variant` | `[varint: 类型索引][序列化值]` | + +--- + +## 数据文件布局 + +`block_log` 和 `dlt_block_log` 使用相同格式: + +``` +[区块 1 二进制][uint64 LE: 区块 1 位置] +[区块 2 二进制][uint64 LE: 区块 2 位置] +... +``` + +每个条目 = 序列化的 `signed_block`,后跟其自身起始偏移量(`uint64_t`)。 + +**读取头区块:** 跳转到最后 8 个字节,读取偏移量,跳转到该位置,反序列化。 + +--- + +## 索引文件 + +### `block_log.index` + +每个条目是指向 `block_log` 的 8 字节 `uint64_t` 偏移量。 + +``` +offset = 8 × (block_num − 1) +``` + +### `dlt_block_log.index` + +以 8 字节头部开始: + +``` +[uint64 LE: start_block_num][uint64 LE: start_block_num 的偏移量][...] +``` + +``` +offset = 8 + 8 × (block_num − start_block_num) +``` + +--- + +## `signed_block` 结构 + +``` +block_header: + [20 字节: 前一区块 ID(ripemd160)] + [4 字节: 时间戳(uint32 Unix 秒)] + [varint + string: 验证者账户名] + [20 字节: transaction_merkle_root(ripemd160)] + [varint + vector: extensions] + +signed_block_header(附加): + [65 字节: witness_signature(1 字节恢复 + 32 字节 r + 32 字节 s)] + +signed_block(附加): + [varint + vector: 交易列表] +``` + +区块号**不直接存储**。推导方式: +``` +block_num = num_from_id(previous) + 1 +num_from_id = block_id 的前 4 字节,按 uint32_t LE 解读 +``` +(创世区块:`previous` 全为零 → `block_num = 1`。) + +--- + +## `signed_transaction` 结构 + +``` +transaction: + [2 字节: ref_block_num(uint16 LE)] + [4 字节: ref_block_prefix(uint32 LE)] + [4 字节: expiration(uint32 Unix 秒)] + [varint + vector: 操作列表] + [varint + extensions_type: extensions] + +signed_transaction(附加): + [varint + vector: 签名列表] +``` + +--- + +## 操作序列化 + +`operations` 向量中的每个操作是一个静态变体: + +``` +[varint: type_id][操作特定字段...] +``` + +类型 ID:参见[操作概述](../protocol/operations/overview.md)。 + +**资产线格格式:** +``` +[int64: amount][uint64: symbol] +``` +Symbol 是压缩的 `uint64`:字节 0 = 小数位数,字节 1–6 = ASCII 名称,字节 7 = 0x00。 + +| 符号 | 十六进制(LE) | +|------|--------------| +| VIZ(3 位小数) | `03 56 49 5A 00 00 00 00` | +| SHARES(6 位小数) | `06 53 48 41 52 45 53 00` | + +**公钥线格格式:** 33 个原始字节(压缩 secp256k1):`[0x02 或 0x03][32 字节 x]`。 + +--- + +## 区块头扩展 + +| 索引 | 类型 | 数据 | +|------|------|------| +| 0 | `void_t` | (无) | +| 1 | `version` | `uint32_t` 版本号(major 8 \| hf 8 \| release 16 位) | +| 2 | `hardfork_version_vote` | `uint32_t` hf_version + `uint32_t` hf_time | + +--- + +## `dlt_block_log` 滚动窗口 + +DLT 日志仅保留最近的区块窗口;旧区块被修剪。从 `start_block_num > 1` 开始。使用快照的节点使用此文件进行崩溃恢复(从快照 + dlt_block_log 重放)。 + +--- + +## 区块日志查看器 + +工具集中包含一个终端区块日志查看器(`block-log-viewer.js`): + +``` +node block-log-viewer.js [--dlt] +``` + +主要命令:`f` 第一个,`l` 最后一个,`n`/`p` 下一个/上一个,`g ` 跳转到区块 N,`o` 显示操作,`s ` 按操作类型搜索,`S ` 按内容搜索,`scan` 构建快速导航位掩码。 + +`scan` 命令构建位掩码文件(`block_log.bitmask`),标记包含非空操作的区块,实现即时 `N`/`P` 跳转。 + +--- + +参见:[共享内存](./shared-memory.md)、[快照](./snapshots.md)、[Chain 插件](../plugins/chain.md)。 diff --git a/@l10n/zh-CN/docs/storage/shared-memory.md b/@l10n/zh-CN/docs/storage/shared-memory.md new file mode 100644 index 0000000000..9e4a96baa8 --- /dev/null +++ b/@l10n/zh-CN/docs/storage/shared-memory.md @@ -0,0 +1,150 @@ +# 共享内存 + +VIZ 节点的所有区块链状态存储在由 **chainbase** 库管理的单一内存映射文件(`shared_memory.bin`)中。没有该文件节点无法运行。 + +--- + +## 架构 + +``` +vizd 进程 +├── block_log / dlt_block_log — 原始区块字节(磁盘) +└── shared_memory.bin (mmap) — 所有链状态(chainbase) + ├── account_index + ├── witness_index + ├── transaction_index + └── ... (所有其他对象索引) +``` + +API 线程(Web 服务器线程池)获取**共享读锁**;区块应用持有**独占写锁**。多个读者可以共存;写入时阻塞所有读者。 + +--- + +## 配置 + +所有选项在 `config.ini` 中设置。 + +### 大小选项 + +| 选项 | 默认值 | 描述 | +|------|--------|------| +| `shared-file-dir` | `state` | `shared_memory.bin` 的目录(相对于数据目录或绝对路径) | +| `shared-file-size` | `2G` | 初始分配大小。若文件已存在且此值更大,文件将增长,不触发重放。 | +| `inc-shared-file-size` | `2G` | 空闲空间低于阈值时的自动增长步长 | +| `min-free-shared-file-size` | `500M` | 触发自动增长的空闲空间阈值 | + +**规则:** `min-free-shared-file-size` 必须小于 `inc-shared-file-size`,否则会发生级联大小调整。 + +### 锁超时选项 + +| 选项 | 默认值 | 描述 | +|------|--------|------| +| `read-wait-micro` | `500000`(500 毫秒) | 每次读锁尝试的超时时间 | +| `max-read-wait-retries` | `3` | 出错前最大读取尝试次数 | +| `write-wait-micro` | `500000`(500 毫秒) | 每次写锁尝试的超时时间 | +| `max-write-wait-retries` | `3` | 出错前最大写入尝试次数 | + +### 性能选项 + +| 选项 | 默认值 | 描述 | +|------|--------|------| +| `single-write-thread` | `false` | 序列化所有区块/交易推送。**生产环境推荐。** | +| `block-num-check-free-size` | `1000` | 每 N 个区块检查一次空闲空间 | +| `flush-state-interval` | — | 每 N 个区块将共享内存刷新到磁盘 | +| `clear-votes-before-block` | `0` | 丢弃早于此区块的投票(0 = 保留全部)。减少内存使用。 | +| `skip-virtual-ops` | `false` | 跳过虚拟操作通知。重放时节省 CPU。 | + +--- + +## 推荐配置 + +**验证者节点(生产环境):** +```ini +shared-file-size = 4G +inc-shared-file-size = 2G +min-free-shared-file-size = 500M +single-write-thread = true +``` + +**API 节点(高读取吞吐量):** +```ini +shared-file-size = 8G +inc-shared-file-size = 2G +min-free-shared-file-size = 500M +single-write-thread = true +read-wait-micro = 1000000 +max-read-wait-retries = 10 +webserver-thread-pool-size = 256 +``` + +**重放 / 初始同步:** +```ini +shared-file-size = 8G +inc-shared-file-size = 4G +min-free-shared-file-size = 500M +block-num-check-free-size = 10 +skip-virtual-ops = true +``` + +--- + +## 自动调整大小 + +当空闲空间降至 `min-free-shared-file-size` 以下时,数据库自动增长。每次调整大小时: + +1. 暂停所有操作(包括区块生产和 API 请求)。 +2. 销毁当前内存映射。 +3. 按 `inc-shared-file-size` 扩展文件。 +4. 重新映射文件并重建所有索引指针。 + +预先充裕地分配 `shared-file-size` 以最小化调整大小频率。每次调整大小都会导致延迟峰值。 + +--- + +## 大小规划 + +VIZ 主网全节点的大致使用量: + +| 组件 | 估计大小 | +|------|----------| +| 账户索引(约 14K 个账户) | ~50 MB | +| 验证者索引 | ~5 MB | +| 操作历史(operation_history 插件) | 200–500 MB | +| 账户历史(account_history 插件) | 100–300 MB | +| 其他索引 | 100–200 MB | +| **推荐起始大小** | **4–8 GB** | + +--- + +## 启动序列 + +``` +1. 打开 shared_memory.bin(若 shared-file-size 更大则扩展) +2. 获取独占文件锁 +3. 初始化索引 +4. 若缺少 genesis → init_genesis() +5. 打开 block_log 或 dlt_block_log +6. undo_all() → 回滚到最后一个不可逆区块 +7. 验证头区块与区块日志匹配 +``` + +--- + +## 恢复 + +| 症状 | 操作 | +|------|------| +| `CRITICAL: validator X account object MISSING` | 损坏 — 使用 `--replay-blockchain` | +| `Could not modify object, uniqueness constraint violated` | 损坏 — 重放或重新同步 | +| `Unable to acquire READ lock` | 锁竞争 — 增大 `read-wait-micro` / 启用 `single-write-thread` | +| 节点启动时循环崩溃 | 文件损坏 — `--replay-blockchain` 或 `--snapshot` | + +恢复选项: + +- `--replay-blockchain` — 删除共享内存,从区块日志重放。 +- `--resync-blockchain` — 删除共享内存和区块日志,从对等节点同步。 +- `--snapshot ` — 从快照加载,在其之上重放 dlt_block_log。 + +--- + +参见:[Chain 插件](../plugins/chain.md)、[Snapshot 插件](../plugins/snapshot.md)、[区块日志](./block-log.md)。 diff --git a/@l10n/zh-CN/docs/storage/snapshots.md b/@l10n/zh-CN/docs/storage/snapshots.md new file mode 100644 index 0000000000..f7292f9b26 --- /dev/null +++ b/@l10n/zh-CN/docs/storage/snapshots.md @@ -0,0 +1,286 @@ +# Snapshot 插件 + +Snapshot 插件通过将完整区块链状态序列化并恢复为 JSON 快照文件,实现近乎即时的节点启动。节点无需从区块日志重放数百万个区块,而是可以加载预构建的快照并通过 P2P 从该点开始同步。 + +--- + +## 启用 + +```ini +plugin = snapshot +``` + +或通过命令行: + +```bash +vizd --plugin snapshot +``` + +--- + +## 配置参考 + +### 仅限命令行的选项 + +| 选项 | 类型 | 描述 | +|------|------|------| +| `--snapshot ` | string | 从快照文件加载状态(DLT 模式)。若 `shared_memory.bin` 已存在则跳过导入;成功导入后将文件重命名为 `.used`。 | +| `--snapshot-auto-latest` | bool | 按文件名中的区块号自动发现 `snapshot-dir` 中的最新快照。若设置了 `--snapshot` 则忽略此项。 | +| `--replay-from-snapshot` | bool | 崩溃恢复:导入快照后重放 `dlt_block_log` 以达到最新可用状态。总是清除共享内存;不重命名快照。 | +| `--auto-recover-from-snapshot` | bool(默认:`true`) | 检测到共享内存损坏时自动运行时恢复。关闭数据库,找到最新快照,清除共享内存,导入并重放——无需重启。 | +| `--create-snapshot ` | string | 从当前数据库状态创建快照,然后退出。 | +| `--sync-snapshot-from-trusted-peer` | bool(默认:`false`) | 当状态为空时从可信对等节点下载并加载快照。必须显式启用。 | + +### 配置文件选项 + +| 选项 | 类型 | 默认值 | 描述 | +|------|------|--------|------| +| `snapshot-at-block` | uint32 | `0` | 到达此区块号时创建快照 | +| `snapshot-every-n-blocks` | uint32 | `0` | 每 N 个区块创建一次快照(0 = 禁用) | +| `snapshot-dir` | string | `""` | 自动生成快照文件的目录 | +| `snapshot-max-age-days` | uint32 | `90` | 删除超过 N 天的快照(0 = 禁用) | +| `allow-snapshot-serving` | bool | `false` | 启用通过 TCP 向其他节点提供快照服务 | +| `allow-snapshot-serving-only-trusted` | bool | `false` | 仅限可信对等节点 | +| `snapshot-serve-endpoint` | string | `0.0.0.0:8092` | 快照服务的 TCP 端点 | +| `trusted-snapshot-peer` | string(多值) | — | 用于快照同步的可信对等节点(`IP:port`)。可重复。 | +| `dlt-block-log-max-blocks` | uint32 | `100000` | DLT 滚动区块日志保留的最近区块数(chain 插件) | + +--- + +## 创建快照 + +### 方法 1:一次性(节点停止并退出) + +```bash +vizd --create-snapshot /data/snapshots/viz-snapshot.json --plugin snapshot +``` + +节点打开现有数据库,必要时重放,创建快照,然后在 P2P 激活前退出。 + +### 方法 2:在特定区块创建(无停机时间) + +```ini +plugin = snapshot +snapshot-at-block = 5000000 +snapshot-dir = /data/snapshots +``` + +当应用第 5,000,000 个区块时,快照被写入 `/data/snapshots/snapshot-block-5000000.json`(在后台线程中)—— 区块处理仅短暂暂停。 + +### 方法 3:定期自动快照(推荐) + +```ini +plugin = snapshot +snapshot-every-n-blocks = 100000 +snapshot-dir = /data/snapshots +``` + +每 100,000 个区块(约 3.5 天)创建一次快照。同步期间跳过快照——仅在实时区块上触发。 + +**推荐间隔:** + +| 间隔 | 区块数 | 大约时间 | +|------|--------|----------| +| 频繁 | 10,000 | ~8 小时 | +| 每日 | 28,800 | ~24 小时 | +| 每周 | 100,000 | ~3.5 天 | +| 稀少 | 1,000,000 | ~35 天 | + +### 快照轮换 + +每次创建新快照后,超过 `snapshot-max-age-days`(默认 90)的旧快照会被自动删除。禁用方法: + +```ini +snapshot-max-age-days = 0 +``` + +--- + +## 从快照加载(DLT 模式) + +```bash +vizd --snapshot /path/to/snapshot.json --plugin snapshot +``` + +加载过程中发生的事情: + +1. Snapshot 插件在 chain 插件上注册加载回调。 +2. Chain 插件检查:若 `shared_memory.bin` 已存在 → 跳过导入(重启安全)。若找不到快照文件 → 跳过导入。 +3. 通过 `open_from_snapshot()` 打开数据库——清除共享内存,初始化 chainbase。 +4. 验证快照(格式版本、chain ID、SHA-256 校验和),导入所有 32 种对象类型。 +5. 快照文件重命名为 `.used`。 +6. LIB 提升至 `head_block_num`,使 P2P 摘要从快照头开始。 +7. Fork 数据库以快照头区块为种子。 +8. P2P 从 LIB + 1 开始同步。 + +### 重启安全性 + +| 重启场景 | 发生的事情 | +|----------|-----------| +| 首次启动(无 shared_memory,文件存在) | 导入快照,重命名为 `.used` | +| 重启(shared_memory 存在) | 跳过导入 | +| 重启(shared_memory 被清除,文件为 `.used`) | 跳过导入 | +| 强制重新导入 | `--resync-blockchain` + 新鲜快照文件 | + +`--snapshot` 标志可以安全地永久留在命令行中。 + +--- + +## DLT 滚动区块日志 + +以 DLT 模式运行时(从快照加载),主 `block_log` 为空。单独的 **DLT 滚动区块日志**(`dlt_block_log`)存储最近的不可逆区块。 + +- 支持 P2P 区块服务(对等节点可以请求最近的区块用于分叉解决)。 +- 支持对最近区块调用 `get_block` 等 API。 +- 存储在区块链数据目录的 `dlt_block_log.log` 和 `dlt_block_log.index` 中。 +- 滚动窗口:当日志超过 `dlt-block-log-max-blocks` 时,旧区块从前端截断。 + +```ini +dlt-block-log-max-blocks = 100000 +``` + +--- + +## 崩溃恢复:`--replay-from-snapshot` + +当 `shared_memory.bin` 在非正常关机后损坏时,使用此模式: + +```bash +# 显式指定快照路径 +vizd --replay-from-snapshot --snapshot /data/snapshots/snapshot-block-79273800.vizjson --plugin snapshot + +# 自动发现最新快照 +vizd --replay-from-snapshot --snapshot-auto-latest --plugin snapshot +``` + +恢复步骤: +1. 清除 `shared_memory.bin`(始终——假设已损坏)。 +2. 导入快照状态。 +3. 重放快照头之后的 `dlt_block_log` 区块。 +4. 从重放的头恢复 P2P 同步。 + +| 方面 | `--snapshot` | `--replay-from-snapshot` | +|------|-------------|--------------------------| +| 共享内存检查 | 若存在则跳过 | 始终清除 | +| 快照重命名 | 重命名为 `.used` | 不重命名 | +| DLT 区块日志重放 | 否 | 是 | +| 用例 | 引导启动 | 崩溃恢复 | + +--- + +## 自动运行时恢复:`--auto-recover-from-snapshot` + +默认启用(`true`)。当在区块处理或区块生成过程中检测到共享内存损坏时,节点: + +1. 关闭数据库。 +2. 在 `snapshot-dir` 中找到最新快照。 +3. 清除共享内存,导入快照,重放 `dlt_block_log`。 +4. 恢复 P2P 同步——**无需重启**。 + +前提条件: +- 必须启用 `plugin = snapshot`。 +- `snapshot-dir` 中必须存在快照。 + +禁用方法(如用于调试): + +```bash +vizd --no-auto-recover-from-snapshot +``` + +--- + +## P2P 快照同步 + +节点可以通过 TCP 直接从可信对等节点下载快照。 + +### 服务器配置 + +```ini +plugin = snapshot +allow-snapshot-serving = true +snapshot-serve-endpoint = 0.0.0.0:8092 +snapshot-every-n-blocks = 28800 +snapshot-dir = /data/viz-snapshots +``` + +### 客户端配置 + +```ini +trusted-snapshot-peer = seed1.viz.world:8092 +trusted-snapshot-peer = seed2.viz.world:8092 +sync-snapshot-from-trusted-peer = true +``` + +客户端连接到每个可信对等节点,选择区块号最高的那个,以 1 MB 分块下载快照,验证 SHA-256 校验和,然后导入。 + +安全功能:最大 2 GB 快照大小、可信对等节点列表、速率限制、60 秒连接截止时间、流式校验和验证。 + +--- + +## 快照文件格式 + +```json +{ + "header": { + "version": 1, + "chain_id": "...", + "snapshot_block_num": 12345678, + "snapshot_block_id": "...", + "snapshot_block_time": "2025-01-01T00:00:00", + "last_irreversible_block_num": 12345660, + "payload_checksum": "sha256...", + "object_counts": { "account": 50000, ... } + }, + "state": { + "dynamic_global_property": [ ... ], + "account": [ ... ], + ... + } +} +``` + +### 包含的对象类型(共 32 种) + +**关键(11 种):** `dynamic_global_property`, `witness_schedule`, `hardfork_property`, `account`, `account_authority`, `validator`, `witness_vote`, `block_summary`, `content`, `content_vote`, `block_post_validation` + +**重要(15 种):** `transaction`, `vesting_delegation`, `vesting_delegation_expiration`, `fix_vesting_delegation`, `withdraw_vesting_route`, `escrow`, `proposal`, `required_approval`, `committee_request`, `committee_vote`, `invite`, `award_shares_expire`, `paid_subscription`, `paid_subscribe`, `witness_penalty_expire` + +**可选(5 种):** `content_type`, `account_metadata`, `master_authority_history`, `account_recovery_request`, `change_recovery_account_request` + +--- + +## 推荐生产配置 + +```ini +plugin = snapshot + +# 每 ~24 小时创建一次快照 +snapshot-every-n-blocks = 28800 +snapshot-dir = /data/viz-snapshots + +# 自动删除超过 90 天的快照 +snapshot-max-age-days = 90 + +# DLT 滚动区块日志:保留最近 10 万个区块 +dlt-block-log-max-blocks = 100000 + +shared-file-size = 4G +plugin = p2p +p2p-seed-node = seed1.viz.world:2001 +``` + +--- + +## Docker 快速参考 + +| 任务 | 命令 | +|------|------| +| 启动并定期创建快照 | 在配置中添加 `snapshot-every-n-blocks`,重启容器 | +| 一次性快照 | `VIZD_EXTRA_OPTS="--create-snapshot /var/lib/vizd/snapshots/snap.json --plugin snapshot"` | +| 从快照加载 | `VIZD_EXTRA_OPTS="--snapshot /var/lib/vizd/snapshots/snap.json --plugin snapshot"` | +| 崩溃恢复 | `VIZD_EXTRA_OPTS="--replay-from-snapshot --snapshot-auto-latest --plugin snapshot"` | +| 自动恢复(默认) | 默认启用;确保已设置 `plugin = snapshot` 和 `snapshot-every-n-blocks` | + +--- + +参见:[Chain 插件](../plugins/chain.md)、[共享内存](./shared-memory.md)、[区块日志](./block-log.md)、[P2P 同步场景](../p2p/sync-scenarios.md)。 diff --git a/docs/PLAN.md b/docs/PLAN.md new file mode 100644 index 0000000000..329aac28a5 --- /dev/null +++ b/docs/PLAN.md @@ -0,0 +1,285 @@ +# VIZ Ledger Documentation — Build Plan + +> Принцип: `new-docs.md` — позиционирование **VIZ Ledger** (hybrid DLT), инструмент **Redocly Realm**, три языка EN / RU / ZH-CN, структура как у XRPL.org, контент колоцирован с кодом в репозитории. + +--- + +## 1. Toolchain Setup + +| Step | Action | File / Command | +|------|--------|----------------| +| 1 | Init npm project | `npm init -y` | +| 2 | Install Redocly Realm | `npm install --save-dev @redocly/realm` | +| 3 | Create site config | `redocly.yaml` (see template below) | +| 4 | Add npm scripts | `"start": "realm dev"`, `"build": "realm build"` | +| 5 | Add CI/CD | `.github/workflows/docs.yml` → GitHub Pages | + +### `redocly.yaml` template + +```yaml +l10n: + defaultLocale: en-US + locales: + - code: en-US + name: English + - code: ru + name: Русский + - code: zh-CN + name: 中文 + +navbar: + items: + - label: Introduction + page: docs/introduction/what-is-viz.md + - label: Run a Node + page: docs/node/getting-started.md + - label: Protocol + page: docs/protocol/data-types.md + - label: API Reference + page: docs/api/json-rpc.md +``` + +--- + +## 2. Repository Structure + +``` +viz-cpp-node/ +├── docs/ ← English source (primary) +│ ├── introduction/ +│ ├── node/ +│ ├── consensus/ +│ ├── p2p/ +│ ├── plugins/ +│ ├── protocol/ +│ │ └── operations/ +│ ├── storage/ +│ ├── governance/ +│ ├── development/ +│ ├── api/ +│ └── advanced/ +├── @l10n/ +│ ├── ru/docs/ ← Russian translation (mirrors docs/) +│ └── zh-CN/docs/ ← Chinese translation (mirrors docs/) +├── translations.yaml ← UI strings (buttons, nav labels) +└── redocly.yaml ← Site config +``` + +--- + +## 3. Content Plan + +Each row: **target file** | **primary source** | **supplementary sources** | **priority** + +### 3.1 Introduction + +| File | Primary Source | Supplementary | Priority | +|------|---------------|---------------|----------| +| `docs/introduction/what-is-viz.md` | `new-docs.md` (VIZ Ledger concept) | `repowiki/Project Overview.md` | P0 | +| `docs/introduction/architecture.md` | `repowiki/Architecture Overview/Architecture Overview.md` | `repowiki/Architecture Overview/Design Patterns*.md` | P0 | +| `docs/introduction/key-concepts.md` | `docs/data-types.md` (energy, shares, validators) | `repowiki/Core Libraries/Emergency Consensus*.md` | P1 | + +**`what-is-viz.md` key content:** +- VIZ is a hybrid DLT (not pure blockchain): Fair-DPOS consensus + snapshot-assisted state storage +- Nodes store recent blocks + periodic snapshots, not full history on every node +- Compare: "VIZ Ledger" vs "VIZ Blockchain" vs "VIZ DLT" — adopt "VIZ Ledger" +- Quote: *"VIZ Ledger uses a blockchain-based consensus mechanism with snapshot-assisted state storage, making it a hybrid DLT system."* + +--- + +### 3.2 Node Operation + +| File | Primary Source | Supplementary | Priority | +|------|---------------|---------------|----------| +| `docs/node/getting-started.md` | `repowiki/Getting Started.md` | `documentation/building.md` | P0 | +| `docs/node/configuration.md` | `repowiki/Configuration Management/Node Configuration.md` | `share/vizd/config/config.ini` | P0 | +| `docs/node/docker.md` | `repowiki/Development Tools/Build System/Docker Integration/` | `share/vizd/docker/` | P1 | +| `docs/node/building.md` | `repowiki/Development Tools/Build System/` | `documentation/building.md` | P1 | +| `docs/node/validator-node.md` | `docs/validator-plugin.md` | `repowiki/Validator.md`, `config_witness.ini` | P0 | +| `docs/node/validator-guard.md` | `docs/validator-guard.md` | `docs/witness-guard-spec.json` | P2 | +| `docs/node/monitoring.md` | `repowiki/Deployment and Operations/Monitoring and Maintenance.md` | `docs/dlt-p2p-stats-reference.md` | P2 | +| `docs/node/snapshot.md` | `docs/snapshot-plugin.md` | `docs/snapshot-pause-workflow.md` | P1 | + +--- + +### 3.3 Consensus + +| File | Primary Source | Supplementary | Priority | +|------|---------------|---------------|----------| +| `docs/consensus/fair-dpos.md` | `repowiki/Architecture Overview/Core Libraries/Chain Library/Fork Resolution and Consensus.md` | `repowiki/Project Overview.md` (Fair-DPOS section) | P0 | +| `docs/consensus/block-processing.md` | `docs/block-processing.md` | `repowiki/.../Block Processing and Validation.md` | P1 | +| `docs/consensus/fork-resolution.md` | `docs/fork-collision-hardfork-proposal.md` | `repowiki/.../Fork Resolution and Consensus.md` | P1 | +| `docs/consensus/emergency-consensus.md` | `docs/emergency-consensus-workflow.md` | `docs/consensus-emergency-params.md`, `docs/emergency-consensus-review.md` | P1 | +| `docs/consensus/hardforks.md` | `docs/hardfork-guide.md` | `docs/hf13-validator-reward-sharing.md`, `docs/dlt-hardfork-new-objects.md` | P2 | + +--- + +### 3.4 P2P Network + +| File | Primary Source | Supplementary | Priority | +|------|---------------|---------------|----------| +| `docs/p2p/overview.md` | `docs/p2p-sync-workflow.md` | `docs/dlt-p2p-network-redesign.md`, `repowiki/P2p Plugin.md` | P1 | +| `docs/p2p/messages.md` | `docs/p2p-messages.md` | `repowiki/.../Message Handling and Protocol.md` | P2 | +| `docs/p2p/stats-reference.md` | `docs/dlt-p2p-stats-reference.md` | `docs/dlt-p2p-stats-reference-ru.md` | P2 | +| `docs/p2p/sync-scenarios.md` | `docs/dlt-4-node-sync-scenarios.md` | — | P2 | +| `docs/p2p/forward-mode.md` | `docs/dlt-forward-mode.md` | — | P3 | + +--- + +### 3.5 Plugins + +| File | Primary Source | Supplementary | Priority | +|------|---------------|---------------|----------| +| `docs/plugins/overview.md` | `docs/plugins.md` | `repowiki/Plugin System/Plugin System.md` | P1 | +| `docs/plugins/validator.md` | `docs/validator-plugin.md` | `repowiki/Validator.md` | P0 | +| `docs/plugins/snapshot.md` | `docs/snapshot-plugin.md` | `docs/snapshot-pause-workflow.md` | P1 | +| `docs/plugins/chain.md` | `repowiki/Chain Plugin.md` | `repowiki/.../Chain Library.md` | P1 | +| `docs/plugins/database-api.md` | `repowiki/API Reference.md` | `docs/plugins.md` (JSON-RPC tables) | P1 | +| `docs/plugins/social-network.md` | `repowiki/Plugin System/Plugin System.md` | — | P2 | +| `docs/plugins/webserver.md` | `repowiki/Webserver Plugin.md` | — | P2 | + +--- + +### 3.6 Protocol — Data Types & Operations + +| File | Primary Source | Supplementary | Priority | +|------|---------------|---------------|----------| +| `docs/protocol/data-types.md` | `docs/data-types.md` | `docs/index.md` (serialization checklist) | P0 | +| `docs/protocol/operations/overview.md` | `docs/index.md` (Quick Reference tables) | — | P0 | +| `docs/protocol/operations/accounts.md` | `docs/op-account.md` | — | P1 | +| `docs/protocol/operations/transfers.md` | `docs/op-transfer-vesting.md` | — | P1 | +| `docs/protocol/operations/validators.md` | `docs/op-validator.md` | — | P1 | +| `docs/protocol/operations/content.md` | `docs/op-content.md` | — | P2 | +| `docs/protocol/operations/recovery.md` | `docs/op-recovery.md` | — | P2 | +| `docs/protocol/operations/escrow.md` | `docs/op-escrow.md` | — | P2 | +| `docs/protocol/operations/committee.md` | `docs/op-committee.md` | — | P2 | +| `docs/protocol/operations/invites.md` | `docs/op-invite.md` | — | P2 | +| `docs/protocol/operations/awards.md` | `docs/op-award.md` | — | P2 | +| `docs/protocol/operations/subscriptions.md` | `docs/op-subscription.md` | — | P2 | +| `docs/protocol/operations/account-market.md` | `docs/op-account-market.md` | — | P3 | +| `docs/protocol/operations/proposals.md` | `docs/op-proposal.md` | — | P2 | +| `docs/protocol/virtual-operations.md` | `docs/virtual-operations.md` | — | P2 | + +--- + +### 3.7 Storage + +| File | Primary Source | Supplementary | Priority | +|------|---------------|---------------|----------| +| `docs/storage/shared-memory.md` | `docs/shared-memory.md` | `repowiki/.../Memory Management System.md` | P1 | +| `docs/storage/block-log.md` | `docs/block-log-spec.md` | `docs/block-log-reader.js`, `docs/block-log-viewer.js` | P2 | +| `docs/storage/snapshots.md` | `docs/snapshot-plugin.md` | `docs/snapshot-pause-workflow.md` | P1 | + +--- + +### 3.8 Governance + +| File | Primary Source | Supplementary | Priority | +|------|---------------|---------------|----------| +| `docs/governance/chain-properties.md` | `docs/chain-properties-governance.md` | — | P2 | +| `docs/governance/staking-and-dao.md` | `docs/staking-and-dao-governance.md` | `repowiki/Advanced Topics/Database Schema Design.md` | P2 | +| `docs/governance/committee.md` | `docs/committee-dao-and-prediction-markets.md` | — | P2 | + +--- + +### 3.9 Development + +| File | Primary Source | Supplementary | Priority | +|------|---------------|---------------|----------| +| `docs/development/library-checklist.md` | `docs/index.md` (Library Implementation Master Checklist) | — | P1 | +| `docs/development/building.md` | `repowiki/Development Tools/Build System/` | `documentation/building.md` | P1 | +| `docs/development/testing.md` | `repowiki/Development Tools/Testing Framework/` | — | P2 | +| `docs/development/debugging.md` | `repowiki/Development Tools/Debugging Tools/` | — | P2 | +| `docs/development/plugin-development.md` | `repowiki/Advanced Topics/Advanced Plugin Development.md` | `repowiki/.../Plugin API Design Patterns.md` | P3 | + +--- + +### 3.10 API Reference + +| File | Primary Source | Supplementary | Priority | +|------|---------------|---------------|----------| +| `docs/api/json-rpc.md` | `docs/plugins.md` (all JSON-RPC method tables) | `repowiki/API Reference.md` | P1 | +| `docs/api/cli-wallet.md` | `docs/cli-wallet.md` | — | P1 | + +--- + +### 3.11 Advanced + +| File | Primary Source | Supplementary | Priority | +|------|---------------|---------------|----------| +| `docs/advanced/security.md` | `repowiki/Advanced Topics/Security Implementation.md` | `repowiki/Deployment and Operations/Node Deployment/Security Hardening.md` | P2 | +| `docs/advanced/database-schema.md` | `repowiki/Advanced Topics/Database Schema Design.md` | — | P3 | +| `docs/advanced/dlt-architecture.md` | `docs/dlt-p2p-network-redesign.md` | `docs/dlt-4-node-code-audit-2026-05-08.md` | P3 | +| `docs/advanced/hardfork-management.md` | `repowiki/Advanced Topics/Hardfork Management.md` | `docs/hardfork-guide.md` | P2 | + +--- + +## 4. Execution Phases + +### Phase 0 — Skeleton (now) +- [ ] Create all target directories under `docs/` +- [ ] Set up `redocly.yaml`, `package.json`, `translations.yaml` +- [ ] Create `docs/introduction/what-is-viz.md` (VIZ Ledger positioning) + +### Phase 1 — Core pages (P0 items) +All P0 files: `what-is-viz`, `architecture`, `getting-started`, `configuration`, `validator-node`, `fair-dpos`, `data-types`, `operations/overview`, `plugins/validator` + +### Phase 2 — Technical depth (P1 items) +Block processing, P2P overview, all P1 plugins/storage/protocol pages, `api/json-rpc`, `api/cli-wallet`, `development/library-checklist` + +### Phase 3 — Full coverage (P2 items) +All remaining operations, governance, advanced topics + +### Phase 4 — Translations +Copy EN structure to `@l10n/ru/docs/` and `@l10n/zh-CN/docs/`, translate P0+P1 pages first + +--- + +## 5. Naming & Terminology Rules + +| Old term | New term | Notes | +|----------|----------|-------| +| VIZ Blockchain | **VIZ Ledger** | Public name; explain hybrid DLT on intro page | +| witness | **validator** | Already renamed in codebase (branch `witness-rename`) | +| blockchain | ledger / chain | Context-dependent; "chain" OK in technical contexts | +| witness_update_operation | `validator_update_operation` | Use current operation name in docs | + +--- + +## 6. Source File Cross-Reference + +Quick lookup: internal `.qoder/docs/` files → docs target + +| Source | Target | +|--------|--------| +| `docs/data-types.md` | `docs/protocol/data-types.md` | +| `docs/plugins.md` | `docs/plugins/overview.md` + `docs/api/json-rpc.md` | +| `docs/block-processing.md` | `docs/consensus/block-processing.md` | +| `docs/shared-memory.md` | `docs/storage/shared-memory.md` | +| `docs/block-log-spec.md` | `docs/storage/block-log.md` | +| `docs/snapshot-plugin.md` + `docs/snapshot-pause-workflow.md` | `docs/storage/snapshots.md` + `docs/node/snapshot.md` | +| `docs/fork-collision-hardfork-proposal.md` | `docs/consensus/fork-resolution.md` | +| `docs/consensus-emergency-params.md` + workflow + review | `docs/consensus/emergency-consensus.md` | +| `docs/hardfork-guide.md` + `hf13-*.md` | `docs/consensus/hardforks.md` | +| `docs/p2p-messages.md` | `docs/p2p/messages.md` | +| `docs/p2p-sync-workflow.md` | `docs/p2p/overview.md` | +| `docs/dlt-p2p-stats-reference.md` | `docs/p2p/stats-reference.md` | +| `docs/dlt-4-node-sync-scenarios.md` | `docs/p2p/sync-scenarios.md` | +| `docs/dlt-forward-mode.md` | `docs/p2p/forward-mode.md` | +| `docs/validator-plugin.md` | `docs/plugins/validator.md` | +| `docs/validator-guard.md` | `docs/node/validator-guard.md` | +| `docs/cli-wallet.md` | `docs/api/cli-wallet.md` | +| `docs/chain-properties-governance.md` | `docs/governance/chain-properties.md` | +| `docs/staking-and-dao-governance.md` | `docs/governance/staking-and-dao.md` | +| `docs/committee-dao-and-prediction-markets.md` | `docs/governance/committee.md` | +| `docs/index.md` | `docs/protocol/operations/overview.md` + `docs/development/library-checklist.md` | +| `docs/op-*.md` (13 files) | `docs/protocol/operations/*.md` | +| `docs/virtual-operations.md` | `docs/protocol/virtual-operations.md` | +| `repowiki/Getting Started.md` | `docs/node/getting-started.md` | +| `repowiki/Architecture Overview/` | `docs/introduction/architecture.md` | +| `repowiki/Configuration Management/` | `docs/node/configuration.md` | +| `repowiki/Deployment and Operations/` | `docs/node/monitoring.md` + `docs/advanced/security.md` | +| `repowiki/Development Tools/` | `docs/development/` | +| `repowiki/API Reference.md` | `docs/api/json-rpc.md` | +| `repowiki/Validator.md` | supplement for `docs/plugins/validator.md` | +| `repowiki/Advanced Topics/` | `docs/advanced/` | diff --git a/docs/advanced/database-schema.md b/docs/advanced/database-schema.md new file mode 100644 index 0000000000..bd8fbf6149 --- /dev/null +++ b/docs/advanced/database-schema.md @@ -0,0 +1,299 @@ +# Database Schema + +VIZ Ledger uses ChainBase — a memory-mapped, multi-index persistent store built on Boost.Interprocess. All chain state lives in `shared_memory.bin`. Each object type is associated with a Boost.MultiIndex container that defines its primary and secondary indexes. + +--- + +## Object Type Registry + +Every persistent object has a unique numeric type ID declared in `chain_object_types.hpp`. The full set of tracked object types: + +| Object | Notes | +|--------|-------| +| `dynamic_global_property` | Singleton: current chain state, head block, LIB, inflation | +| `account` | All registered accounts | +| `account_authority` | master / active / regular authority sets | +| `witness` (validator) | Validator registrations, signing keys, vote counts | +| `transaction` | Pending / recent transactions (TAPOS window) | +| `block_summary` | 65536-slot TAPOS buffer of block IDs | +| `witness_schedule` | Singleton: active validator schedule | +| `content` | Posts and comments (deprecated) | +| `content_type` | Content title/body metadata | +| `content_vote` | Votes on content | +| `witness_vote` | Validator votes from accounts | +| `hardfork_property` | Singleton: current/next hardfork tracking | +| `withdraw_vesting_route` | Withdrawal routing rules | +| `master_authority_history` | Master key change history | +| `account_recovery_request` | Pending account recovery | +| `change_recovery_account_request` | Pending recovery account changes | +| `escrow` | Escrow transfers | +| `vesting_delegation` | Active SHARES delegations | +| `fix_vesting_delegation` | Delegation fix records | +| `vesting_delegation_expiration` | Delegations in return window | +| `account_metadata` | Account JSON metadata | +| `proposal` | Governance proposals | +| `required_approval` | Proposal approval requirements | +| `committee_request` | Committee funding requests | +| `committee_vote` | Committee votes | +| `invite` | Account invites | +| `award_shares_expire` | Expiring award shares | +| `paid_subscription` | Subscription offerings | +| `paid_subscribe` | Active subscriptions | +| `witness_penalty_expire` | Validator miss-penalty expirations | +| `block_post_validation` | Block post-validation records | + +--- + +## Account Object + +Accounts store balances, vesting state, delegation metrics, bandwidth, auction/sale flags, and governance participation. + +**Key fields:** `name`, `balance` (VIZ), `vesting_shares`, `delegated_vesting_shares`, `received_vesting_shares`, `energy`, `next_vesting_withdrawal`, `witnesses_voted_for`, `recovery_account`. + +**Indexes:** + +| Tag | Key | Type | +|-----|-----|------| +| `by_id` | `id` | unique | +| `by_name` | `name` | unique | +| `by_account_on_sale` | sale flag | non-unique | +| `by_account_on_auction` | auction flag | non-unique | +| `by_account_on_sale_start_time` | sale start time | non-unique | +| `by_subaccount_on_sale` | subaccount sale flag | non-unique | +| `by_next_vesting_withdrawal` | `(next_vesting_withdrawal, id)` | composite | + +The `by_next_vesting_withdrawal` composite index enables O(log N) batch processing of upcoming withdrawal installments. + +--- + +## Content Object + +Content objects represent posts and comments with voting, payout, and nesting metadata. **These objects are deprecated** — new applications should use `custom_operation` instead. + +**Indexes on `content`:** + +| Tag | Key | +|-----|-----| +| `by_id` | `id` | +| `by_cashout_time` | `(cashout_time, id)` | +| `by_permlink` | `(author, permlink)` | +| `by_root` | `(root_content, id)` | +| `by_parent` | `(parent_author, parent_permlink, id)` | +| `by_last_update` | `(parent_author, last_update, id)` — API-heavy | +| `by_author_last_update` | `(author, last_update, id)` — API-heavy | + +**Indexes on `content_vote`:** + +| Tag | Key | +|-----|-----| +| `by_id` | `id` | +| `by_content_voter` | `(content, voter)` — unique | +| `by_voter_content` | `(voter, content)` — unique | +| `by_voter_last_update` | `(voter, last_update, content)` | +| `by_content_weight_voter` | `(content, weight, voter)` — for leaderboards | + +--- + +## Validator (Witness) Objects + +**`witness_object` indexes:** + +| Tag | Key | +|-----|-----| +| `by_id` | `id` | +| `by_name` | `owner` — unique | +| `by_vote_name` | `(votes, owner)` | +| `by_counted_vote_name` | `(counted_votes, owner)` | +| `by_schedule_time` | `(virtual_scheduled_time, id)` — O(log N) slot scheduling | + +**`witness_vote_object` indexes:** + +| Tag | Key | +|-----|-----| +| `by_id` | `id` | +| `by_account_witness` | `(account, validator)` — unique | +| `by_witness_account` | `(validator, account)` — unique | + +The `by_schedule_time` index is how the block production scheduler picks the next validator in O(log N) time. + +--- + +## Proposal and Required Approval Objects + +**`proposal_object` indexes:** + +| Tag | Key | +|-----|-----| +| `by_id` | `id` | +| `by_account` | `(author, title)` — unique | +| `by_expiration` | `expiration` — non-unique | + +**`required_approval_object` indexes:** + +| Tag | Key | +|-----|-----| +| `by_id` | `id` | +| `by_account` | `(account, proposal)` | + +--- + +## Invite Object + +| Tag | Key | +|-----|-----| +| `by_id` | `id` | +| `by_invite_key` | public key — non-unique | +| `by_status` | status — non-unique | +| `by_creator` | creator — non-unique | +| `by_receiver` | receiver — non-unique | + +--- + +## Auxiliary Objects + +**`withdraw_vesting_route`:** + +| Tag | Key | +|-----|-----| +| `by_withdraw_route` | `(from_account, to_account)` — unique | +| `by_destination` | `(to_account, id)` | + +**`escrow`:** + +| Tag | Key | +|-----|-----| +| `by_from_id` | `(from, escrow_id)` — unique | +| `by_to` | `(to, id)` | +| `by_agent` | `(agent, id)` | +| `by_ratification_deadline` | `(is_approved, ratification_deadline, id)` | + +**`vesting_delegation`:** + +| Tag | Key | +|-----|-----| +| `by_delegation` | `(delegator, delegatee)` — unique | + +**`vesting_delegation_expiration`:** + +| Tag | Key | +|-----|-----| +| `by_expiration` | `expiration` — non-unique | +| `by_account_expiration` | `(delegator, expiration)` | + +--- + +## Fork Database + +The fork database (`fork_database`) maintains an in-memory tree of blocks for managing chain forks. It operates separately from the persistent chainbase store. + +**Linked index** — canonical chain blocks, indexed by block ID and block number. +**Unlinked index** — orphaned or out-of-order blocks whose parent is not yet known. + +``` +Push Block + ├── Parent known in linked index? + │ YES → link block, insert into linked index, update head + │ NO → insert into unlinked index + └── Attempt to link pending unlinked blocks +``` + +When a new block arrives and its ID matches the parent of an unlinked block, `_push_next()` cascades through the unlinked index and promotes those blocks to the linked chain. + +**Branch operations:** +- `fetch_branch_from(first, second)` — walks both branches to find their common ancestor. Returns `(first_branch, second_branch)` for fork switching. +- `set_max_size(n)` — prunes blocks older than n, caps memory usage. +- `walk_main_branch_to_num(n)` — iterates main chain to a specific block number. + +**Block validity:** Blocks flagged invalid are never promoted. Pushing a block outside the max reordering window raises an assertion. + +--- + +## Index Management + +Core indices are registered during `database::initialize_indexes()`. Plugins register additional indices via `add_plugin_index()` in their `plugin_startup()`. + +```cpp +// Core index registration (database.cpp) +add_core_index(); +add_core_index(); +// ... + +// Plugin index registration (plugin startup) +db.add_plugin_index(); +``` + +--- + +## Object Relationships + +``` +account ──(author)──► content ──► content_vote ◄──(voter)── account +account ──(delegator)──► vesting_delegation ──► account (delegatee) +account ──(account)──► witness_vote ──► witness (validator) +account ──(author)──► proposal ──► required_approval ◄──(account)── account +account ──(creator/receiver)──► invite +escrow: from + to + agent → escrow_object +``` + +--- + +## Query Optimization Guidelines + +**Fast lookups:** +- Account by name → `by_name` (unique, O(log N)) +- Validator schedule → `by_schedule_time` (ordered by virtual time) +- Content by author+permlink → `by_permlink` (unique composite) +- Votes by content+weight → `by_content_weight_voter` (leaderboards) + +**Batch processing:** +- Vesting withdrawals → iterate `by_next_vesting_withdrawal` forward +- Expiring delegations → iterate `by_expiration` forward +- Expiring proposals → iterate `by_expiration` forward + +**Avoid full scans:** always use an indexed tag. Composite indexes are ordered by the leftmost key first — put the most selective or frequently filtered field first. + +--- + +## Schema Extension for Plugins + +To add a custom object type: + +1. Define an object class inheriting from `chainbase::object`. +2. Declare a `chainbase::shared_multi_index_container` with the desired indexes. +3. Register via `db.add_plugin_index()` in `plugin_startup()`. +4. Add `FC_REFLECT` macros for serialization. + +```cpp +class my_object : public chainbase::object { + id_type id; + account_name_type account; + uint64_t value; +}; + +using my_index = chainbase::shared_multi_index_container< + my_object, + indexed_by< + ordered_unique, + member>, + ordered_unique, + member> + > +>; +``` + +--- + +## Schema Evolution + +New hardfork → new fields or objects. Guidelines: + +- Keep existing primary key semantics stable across hardforks. +- Add new fields as optional or with defaults; never change existing field layout. +- Gate new index usage behind `has_hardfork()` checks during replay. +- Add new MultiIndex tags alongside existing ones — never remove a tag that replaying nodes might query. + +See also: [Hardfork Management](./hardfork-management.md), [Shared Memory](../storage/shared-memory.md). + +--- + +See also: [Plugin Development](../development/plugin-development.md), [Virtual Operations](../protocol/virtual-operations.md), [Hardfork Management](./hardfork-management.md). diff --git a/docs/advanced/dlt-architecture.md b/docs/advanced/dlt-architecture.md new file mode 100644 index 0000000000..91e24e8627 --- /dev/null +++ b/docs/advanced/dlt-architecture.md @@ -0,0 +1,248 @@ +# DLT P2P Architecture + +VIZ Ledger's P2P layer was redesigned from the legacy Graphene synopsis-based protocol (`node.cpp`) to a dedicated DLT-native protocol (`dlt_p2p_node.cpp`). The public plugin API is unchanged — only the internal implementation was replaced. + +--- + +## Overview + +``` +Before: p2p_plugin → graphene::network::node (node.cpp, 6978 lines, STCP, inventory gossip) +After: p2p_plugin → dlt_p2p_node (dlt_p2p_node.cpp, 2627 lines, raw TCP, range-based sync) +``` + +The replacement is **in-place**: same plugin name `"p2p"`, same port (2001/4243), same public API. All dependent plugins (validator, snapshot, etc.) required zero changes. + +--- + +## Wire Protocol + +Raw TCP — no STCP encryption layer. Each message on the wire: + +``` +[4 bytes: data size (uint32_t)] [4 bytes: msg_type (uint32_t)] [N bytes: fc::raw::pack(T)] +``` + +### Message Types (5100–5116) + +| Type | ID | Description | +|------|----|-------------| +| `dlt_hello_message` | 5100 | Handshake: protocol version, head/LIB, DLT range, node/fork status | +| `dlt_hello_reply_message` | 5101 | Handshake reply: exchange_enabled, fork_alignment | +| `dlt_range_request_message` | 5102 | Request a range of block IDs | +| `dlt_range_reply_message` | 5103 | Reply with available block range | +| `dlt_get_block_range_message` | 5104 | Fetch blocks start..end with prev_block_id check | +| `dlt_block_range_reply_message` | 5105 | Reply: blocks vector + is_last flag | +| `dlt_get_block_message` | 5106 | Fetch a single block by ID | +| `dlt_block_reply_message` | 5107 | Reply: block + next_available + is_last | +| `dlt_not_available_message` | 5108 | Block not available | +| `dlt_fork_status_message` | 5109 | Broadcast current fork/node status to peers | +| `dlt_peer_exchange_request` | 5110 | Request known peers list | +| `dlt_peer_exchange_reply` | 5111 | Reply with peers | +| `dlt_peer_exchange_rate_limited` | 5112 | Rate limit notice: wait N seconds | +| `dlt_transaction_message` | 5113 | Broadcast a signed transaction | +| `dlt_soft_ban_message` | 5114 | Notification before disconnecting a banned peer | +| `dlt_gap_fill_request` | 5115 | Request specific block numbers to fill a gap | +| `dlt_gap_fill_reply` | 5116 | Reply with requested blocks | + +--- + +## Fiber Architecture + +All I/O runs on a single `fc::thread` using cooperative fibers — no mutexes needed for shared state: + +| Fiber | Role | +|-------|------| +| Accept loop | Waits for incoming connections; rejects duplicate IPs | +| Read loop (per peer) | Reads messages; dispatches to `on_message()` | +| Periodic task | Reconnects, checks stagnation, peer stats, mempool cleanup | + +Fibers yield on blocking I/O (`readsome()`, `writesome()`), allowing multiple peers on one thread without contention. + +--- + +## Node Status and Peer Lifecycle + +**Node statuses:** `SYNC` (catching up) / `FORWARD` (live, exchanging blocks) + +**Peer lifecycle states:** +``` +CONNECTING → HANDSHAKING → SYNCING → ACTIVE → DISCONNECTED → BANNED +``` + +Timeouts: connecting=5s, handshaking=10s. Reconnect backoff: 30s → 60s → … → 3600s with ±25% jitter, reset after 5 minutes stable uptime. Peers removed after 8 hours of non-response. + +--- + +## Block Sync: SYNC Mode + +A node in SYNC mode fetches blocks sequentially from a peer with a higher head: + +1. `request_blocks_from_peer()` — sends `dlt_get_block_range_message` for up to 200 blocks after our head. +2. `on_dlt_block_range_reply()` — validates `prev_block_id` hash chain, applies each block. +3. `check_sync_catchup()` — compares our head against all peers' heads; transitions to FORWARD when caught up. +4. `sync_stagnation_check()` — after 30s with no new block, retries up to 3 times then transitions to FORWARD with a warning. + +### Gap Fill + +When a contiguous gap exists between our head and the earliest available block on the syncing peer, `request_gap_fill()` sends a `dlt_gap_fill_request` (up to 100 blocks per request) to any peer whose DLT range covers the gap. Gap fill works in both SYNC and FORWARD modes: + +- Triggered from `on_dlt_block_reply()` (out-of-order block detected) and `periodic_task()` (every 5s). +- Falls back from exchange-enabled peers to any active peer with a higher head. +- Falls back to SYNC mode if no peer has the needed blocks. +- Large gaps handled in 100-block chunks with a 5s cooldown between requests. + +--- + +## Block Exchange: FORWARD Mode + +In FORWARD mode, peers exchange live blocks and transactions: + +- `exchange_enabled` flag controls whether a peer receives new blocks from us. +- On FORWARD transition, `dlt_fork_status_message` is sent to **all** peers (not just exchange-enabled) to notify them of our readiness. +- `on_dlt_fork_status()` re-evaluates `exchange_enabled` when a peer transitions from SYNC to FORWARD. +- `check_forward_stagnation()` — if head hasn't advanced in 30s AND at least one peer is ahead, transitions to SYNC. + +--- + +## Fork Alignment and Exchange Eligibility + +During the hello handshake, `check_fork_alignment()` performs multi-tier block ID matching to determine if peers are on the same fork: + +| Check | Condition | +|-------|-----------| +| Empty peer | `head_block_num == 0` → aligned (new node) | +| Range overlap | Our DLT log covers peer's head → `is_block_known(head_id)` | +| Boundary link | `peer_head + 1 == our_earliest` → check our earliest block's `previous == peer_head_id` | +| LIB fallback | Always check `is_block_known(lib_id)` | + +This multi-tier check prevents false "different fork" disconnections in DLT mode, where old blocks are pruned and the old single-ID check would fail for peers on the same chain. + +--- + +## Fork Resolution + +The fork resolution subsystem tracks competing chain tips: + +- **Threshold:** 42 blocks of divergence triggers `resolve_fork()` (= `CHAIN_MAX_WITNESSES × 2`, one full schedule rotation). +- **Selection:** Heaviest branch by vote weight. +- **Hysteresis:** 6 consecutive blocks as winner before switching (`CONFIRMATION_BLOCKS`). +- **Status:** `_fork_status` exposed via `is_on_majority_fork()` for the validator plugin to check before producing blocks. + +--- + +## Anti-Spam + +| Mechanism | Description | +|-----------|-------------| +| `spam_strikes` counter | Single counter per peer; reset on good packet; soft-ban at threshold=10 | +| Soft ban | Sets BANNED state for 3600s; sends `dlt_soft_ban_message` before closing | +| Per-IP dedup | Rejects duplicate connections from the same IP (both inbound and outbound) | +| Broadcast dedup | `send_to_all_our_fork_peers()` tracks `std::set` to skip duplicate IPs | + +Duplicate blocks and out-of-order blocks from range replies are silently skipped — not counted as spam. Deserialization errors do not increment spam strikes. + +--- + +## P2P Mempool + +A separate in-process mempool (distinct from the chain's `_pending_tx`) provides early transaction filtering before chain acceptance: + +- **Dedup** by `tx_id`. +- **Eviction** by oldest expiry when limits are reached. +- **Limits** (configurable): max 10,000 entries, 100 MB total, 64 KB per transaction. +- **Provisional entries** tagged during SYNC mode; revalidated on FORWARD transition. +- **Cleanup** on block receipt (`remove_transactions_in_block`) and fork switch (`prune_mempool_on_fork_switch`). + +--- + +## Peer Exchange + +Rate-limited peer discovery: + +- Max 3 requests per 5-minute window per peer. +- Subnet diversity filter: max 2 peers per `/24` prefix in each reply. +- Only peers with ≥600s uptime are shared. +- Inbound peers (ephemeral ports) excluded from exchange replies. + +--- + +## Recovery Mechanisms + +### Peer isolation (P53) + +When zero active peers exist for 60 seconds, `emergency_peer_reset()`: +- Clears all soft bans (BANNED → DISCONNECTED, resets spam strikes). +- Resets all disconnected peer backoffs to minimum with immediate reconnect. + +### Block processing pause/resume + +`pause_block_processing()` / `resume_block_processing()` allow the snapshot plugin to halt P2P block intake during state serialization. The periodic task skips DB-accessing operations while paused. + +### Startup grace period (P22) + +For the first 60 seconds after startup, blocks within 10 of the head are treated as `FORK_DB_ONLY` instead of `DEAD_FORK` — preventing cascade rejections while the fork_db rebuilds from the block log. + +--- + +## Block Accept Results + +`dlt_block_accept_result` enum replaces the old boolean return: + +| Value | Meaning | +|-------|---------| +| `ACCEPTED` | Block applied to chain (became new head) | +| `FORK_DB_ONLY` | Stored in fork_db but not applied (unlinkable, competing fork) | +| `DEAD_FORK` | Block at/below head from a dead fork — peer is soft-banned | +| `ALREADY_KNOWN` | Already have this block (duplicates, `block_too_old_exception`) | +| `REJECTED` | Failed validation entirely | + +--- + +## Configuration Reference + +| Option | Default | Description | +|--------|---------|-------------| +| `dlt-block-log-max-blocks` | 100,000 | Max blocks in DLT rolling block log | +| `dlt-peer-max-disconnect-hours` | 8 | Remove peer after this many hours non-response | +| `dlt-mempool-max-tx` | 10,000 | Hard cap on mempool entries | +| `dlt-mempool-max-bytes` | 100 MB | Hard cap on total mempool memory | +| `dlt-mempool-max-tx-size` | 64 KB | Reject oversized transactions | +| `dlt-mempool-max-expiration-hours` | 24 | Reject far-future expiration | +| `dlt-peer-exchange-max-per-reply` | 10 | Max peers per exchange reply | +| `dlt-peer-exchange-max-per-subnet` | 2 | Anti-sybil: max 2 peers per /24 | +| `dlt-peer-exchange-min-uptime-sec` | 600 | Min uptime before peer is shared | +| `dlt-stats-interval-sec` | 300 | Peer stats log interval (min 30) | + +--- + +## Color-Coded Logging + +| Color | Meaning | +|-------|---------| +| Green | Sync progress and block production | +| White | Normal block exchange | +| Red | Fork events | +| Dark gray | Transaction handling | +| Orange | Warnings (soft bans, stagnation, gaps) | +| Cyan | Peer statistics output | + +--- + +## Delegate Pattern + +The network library links only `fc` and `graphene_protocol` — not `graphene_chain`. The `dlt_p2p_delegate` abstract interface bridges this gap: + +``` +dlt_p2p_node (network lib) ←→ dlt_p2p_delegate (interface) ←→ dlt_delegate (p2p_plugin) +``` + +The `dlt_delegate` in `p2p_plugin.cpp` implements: +- `read_block_by_num()` — checks dlt_block_log, then fork_db. +- `accept_block()` — calls `push_block()`; catches `unlinkable_block_exception` → stores in fork_db. +- `get_fork_branch_tips()` — fetches from fork_db around current head. +- `is_tapos_block_known()` — delegates to `db.is_known_block()`. + +--- + +See also: [P2P Overview](../p2p/overview.md), [Sync Scenarios](../p2p/sync-scenarios.md), [Snapshot Plugin](../storage/snapshots.md), [Block Log](../storage/block-log.md). diff --git a/docs/advanced/hardfork-management.md b/docs/advanced/hardfork-management.md new file mode 100644 index 0000000000..4e2ecb9068 --- /dev/null +++ b/docs/advanced/hardfork-management.md @@ -0,0 +1,199 @@ +# Hardfork Management + +VIZ Ledger coordinates protocol upgrades through a deterministic hardfork system. Hardforks are defined at compile time, activated by validator consensus, and applied automatically during block processing — no node restart required. + +--- + +## How It Works + +### 1. Definition files + +Each hardfork has a dedicated `*.hf` file in `libraries/chain/hardfork.d/` that defines compile-time constants: + +```cpp +// Example: 9.hf +#define CHAIN_HARDFORK_9 9 +#define CHAIN_HARDFORK_9_VERSION version(1, 0, 9) +#define CHAIN_HARDFORK_9_TIME (fc::time_point_sec(1650000000)) +``` + +The preamble file (`0-preamble.hf`) declares the total count and the hardfork property object schema. Currently: `CHAIN_NUM_HARDFORKS = 12`. + +### 2. Validator consensus + +Validators publish their preferred next hardfork version via `versioned_chain_properties_update_operation`. During each validator schedule update, the node: + +1. Collects the hardfork version each active validator supports. +2. If a majority agree on a version ≥ next scheduled version, sets `next_hardfork` and `next_hardfork_time`. + +### 3. Activation during block processing + +When the head block time passes `next_hardfork_time` and sufficient validators support the version, the node calls `apply_hardfork(N)`. All subsequent behavior changes are gated by `has_hardfork(N)` checks in evaluators, inflation logic, and chain properties. + +--- + +## Hardfork History + +| HF | Key changes | +|----|------------| +| 1 | Median calculation fix | +| 2 | Committee approval threshold fix | +| 3 | Minor protocol corrections | +| 4 | Award operations, custom operation sequences | +| 5 | Bandwidth and authority fixes | +| 6 | Validator miss penalties, vote counting | +| 7 | Social/content adjustments | +| 8 | Protocol cleanup | +| 9 | Invite system, paid subscriptions, validator fees, withdraw_intervals | +| 10 | Inflation model | +| 11 | Emission model changes | +| 12 | Emergency consensus recovery (see below) | + +--- + +## HF12: Emergency Consensus Recovery + +HF12 introduces automatic network recovery when block production stalls. + +### Activation + +If the last irreversible block (LIB) timestamp is more than `CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC` (1 hour) behind wall clock, emergency mode activates automatically. An emergency validator account (`CHAIN_EMERGENCY_WITNESS_ACCOUNT = "committee"`) with a known public key (`CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY`) is created and inserted into the block production schedule. + +### Three-state safety enforcement + +| Network state | Condition | Behavior | +|--------------|-----------|----------| +| Healthy | Participation ≥ 33% | Enforces safe defaults; overrides manual config | +| Distressed | Participation < 33% | Honors manual config overrides for operator recovery | +| Emergency | Emergency mode active | Bypasses stale and participation checks automatically | + +### Enhanced validator scheduling + +- **Hybrid schedule**: Emergency validator fills unavailable slots while keeping real validator positions. +- **Vote-weighted fork switching**: Uses sum of raw votes from unique non-committee validators as the primary fork comparison criterion. +- **Median exclusion**: Emergency validator's property votes are excluded from median chain parameter computation. + +--- + +## Hardfork Property Object + +The persistent `hardfork_property_object` (singleton in chainbase) tracks: + +| Field | Description | +|-------|-------------| +| `processed_hardforks` | Vector of applied hardfork times | +| `last_hardfork` | ID of the last applied hardfork | +| `current_hardfork_version` | Protocol version currently enforced | +| `next_hardfork` | Next scheduled hardfork version | +| `next_hardfork_time` | When `next_hardfork` will activate | + +For HF12+, additional fields track emergency consensus state. + +--- + +## Database Lifecycle and Hardforks + +### On open + +``` +database::open() + → init_schema(), initialize_indexes(), initialize_evaluators() + → load hardfork_property from chainbase + → init_hardforks() ← populates _hardfork_times[] and _hardfork_versions[] arrays + → assert: chainbase revision == head_block_num +``` + +### On reindex + +Replays all blocks from block_log with skip flags (no signature checks, no merkle) for speed. `apply_hardfork()` fires at each hardfork boundary during replay, ensuring deterministic state reconstruction. + +### On block apply + +``` +process_hardforks() + → check if next_hardfork_time has passed + → check if validator consensus supports the version + → if yes: apply_hardfork(N) + → run version-specific state migrations + → update current_hardfork_version +``` + +--- + +## Rollback and Fork Switching + +The database uses undo sessions for atomic block application — partial failures roll back cleanly. + +For fork switches, `fetch_branch_from()` returns both branches to their common ancestor, pops the current branch, and reapplies the new one. HF12 adds vote-weighted chain comparison during this process. + +If block application fails, the fork database entry is removed and an exception is thrown. The P2P layer handles the exception by marking the sending peer appropriately. + +--- + +## Adding a New Hardfork + +1. **Create `N.hf`** in `libraries/chain/hardfork.d/`: + ```cpp + #define CHAIN_HARDFORK_N N + #define CHAIN_HARDFORK_N_VERSION version(1, 0, N) + #define CHAIN_HARDFORK_N_TIME (fc::time_point_sec(UNIX_TIMESTAMP)) + ``` + +2. **Increment `CHAIN_NUM_HARDFORKS`** in `0-preamble.hf` to N. + +3. **Gate new behavior** in evaluators and runtime logic: + ```cpp + if (db.has_hardfork(CHAIN_HARDFORK_N)) { + // new behavior + } else { + // legacy behavior + } + ``` + +4. **Add state migrations** in `apply_hardfork(N)` within `database.cpp`: + ```cpp + case CHAIN_HARDFORK_N: + // one-time migration code + break; + ``` + +5. **Consider emergency mode**: If the hardfork modifies validator scheduling or chain parameters, ensure the emergency validator is excluded from affected computations. + +6. **Test with reindex**: Run a full reindex against mainnet block data to confirm deterministic replay produces identical state. + +--- + +## Troubleshooting + +| Symptom | Likely cause | Resolution | +|---------|-------------|------------| +| Hardfork not triggering | Validator consensus not reached | Verify validators publish the target version; check `get_next_scheduled_hardfork()` API | +| Revision mismatch on open | chainbase revision ≠ head block num | Reindex from block log or restore from snapshot | +| Memory exhaustion during reindex | Shared memory too small | Increase `shared-file-size`; enable auto-resize | +| Emergency mode not activating | HF12 not applied yet | Verify `current_hardfork_version` ≥ 1.0.12 | +| State mismatch after reindex | Non-deterministic has_hardfork() branch | Audit `apply_hardfork()` for side effects | + +**Diagnostics:** + +```json +{ "method": "database_api.get_hardfork_version", "params": [] } +{ "method": "database_api.get_next_scheduled_hardfork", "params": [] } +``` + +--- + +## Upgrade Checklist + +- [ ] Define `N.hf` with realistic timestamp (coordinate with validators) +- [ ] Increment `CHAIN_NUM_HARDFORKS` in `0-preamble.hf` +- [ ] Implement `apply_hardfork(N)` migrations +- [ ] Gate behavior changes with `has_hardfork()` checks +- [ ] Back up database and block log before deploying +- [ ] Start node in read-only mode to verify compatibility +- [ ] Monitor logs for hardfork activation events +- [ ] Coordinate with validators to publish the new version +- [ ] Confirm `get_next_scheduled_hardfork()` shows the expected version/time + +--- + +See also: [Chain Properties](../governance/chain-properties.md), [Validators](../protocol/operations/validators.md), [Database Schema](./database-schema.md), [Database API](../plugins/database-api.md). diff --git a/docs/advanced/security.md b/docs/advanced/security.md new file mode 100644 index 0000000000..209c83ffec --- /dev/null +++ b/docs/advanced/security.md @@ -0,0 +1,162 @@ +# Security Implementation + +VIZ Ledger's security model rests on three pillars: threshold-based authority verification, deterministic signature validation, and encrypted peer transport. This page covers each subsystem and provides guidance for operators and plugin developers. + +--- + +## Authority Model + +Every account has three authority levels — master, active, and regular — each represented as a weighted set of keys and/or account references with a weight threshold. + +``` +Authority { + weight_threshold: uint32 + key_auths: { PublicKey → weight } + account_auths: { AccountName → weight } +} +``` + +An operation is authorized when the sum of weights of provided signatures (and recursively resolved account authorities) meets or exceeds `weight_threshold`. + +**Recursion depth** is bounded to prevent infinite loops in nested account authority chains. + +The same authority structure is stored in shared memory as `SharedAuthority`, which uses inter-process allocators compatible with Boost.Interprocess mapped files. + +--- + +## Signature Validation + +Transaction signature validation uses deterministic secp256k1 ECDSA: + +1. **Digest**: `sha256(chain_id || serialized_transaction)` +2. **Recovery**: `secp256k1_recover(signature, digest)` → public key +3. **Authority check**: `sign_state.check_authority(account, level)` walks the authority tree and verifies the recovered key set satisfies the threshold. + +The `sign_state` engine: +- Maintains a set of provided signatures and their recovered keys. +- Recursively resolves account authorities up to the maximum depth. +- Filters unused signatures after verification. + +**For plugin developers:** Use the `auth_util` plugin's `check_authority_signature` API to verify signatures before processing sensitive operations: + +```json +{ + "method": "auth_util.check_authority_signature", + "params": ["alice", "regular", "", ["", ""]] +} +``` + +Returns the set of verified signing keys if the authority is satisfied, or an error if it is not. + +--- + +## Peer Transport Encryption + +All peer-to-peer connections use ECDH key exchange + AES stream encryption: + +1. Each side generates an ephemeral key pair on connection. +2. ECDH produces a shared secret from the ephemeral keys. +3. AES encoder/decoder streams are initialized with the shared secret. +4. All subsequent messages are encrypted. + +This prevents passive eavesdropping. Every connection uses a fresh ephemeral key, so compromise of one session does not affect others. + +The implementation lives in `stcp_socket` (`libraries/network/stcp_socket.cpp`). + +--- + +## API Exposure + +The `webserver` plugin serves JSON-RPC over HTTP and WebSocket. Security configuration: + +```ini +# Bind to loopback for internal access only +webserver-http-endpoint = 127.0.0.1:8090 +webserver-ws-endpoint = 127.0.0.1:8091 + +# Use a reverse proxy (nginx, caddy) for public access with TLS +``` + +**Thread pool sizing:** The webserver runs a configurable number of threads. Set `webserver-thread-pool-size` to match expected concurrent request load. Undersizing causes request queuing; oversizing wastes resources. + +```ini +webserver-thread-pool-size = 4 +``` + +--- + +## Network Security Measures + +- **Encrypted channels**: All peer connections are encrypted (ECDH + AES). Passive eavesdropping is not possible. +- **Peer database**: The P2P node maintains a peer database and propagation timing metadata. +- **Soft bans**: Peers that behave incorrectly (send invalid blocks, fork-only data with no progress) receive temporary soft bans rather than permanent disconnection. +- **Bandwidth limits**: Configurable via `max-send-buffer-size` and related P2P options. + +--- + +## Vulnerability Assessment + +**Common risks:** + +| Risk | Mitigation | +|------|-----------| +| Authority bypass via malformed signatures | Bounded recursion depth; strict weight-threshold checking | +| Weak randomness in ephemeral keys | Uses secp256k1's deterministic key generation | +| MitM on unencrypted RPC | Bind webserver to loopback; use TLS reverse proxy for public endpoints | +| DoS via oversized payloads | JSON-RPC payload size limits; webserver thread pool controls concurrency | +| Nested authority exhaustion | Maximum recursion depth enforced in `sign_state` | + +**Penetration testing checklist:** +- Submit malformed or incomplete signatures to verify authority bypass protection. +- Test recursion depth limits with deeply nested account authority chains. +- Verify transport encryption with a network capture (no plaintext should appear on the wire). +- Stress-test the webserver endpoint for memory exhaustion and queue saturation. + +--- + +## Security Best Practices for Plugin Development + +**Input validation:** +- Reject malformed or oversized JSON-RPC payloads at plugin boundaries. +- Validate all parameters against their expected types and ranges before processing. + +**Authentication:** +- Always use `auth_util.check_authority_signature` before applying state changes that require authorization. +- Never trust account names or key references without verifying signatures. + +**Constant-time comparisons:** +- Use `fc::crypto::secure_compare` or equivalent for secret comparisons to prevent timing side-channels. + +**No plaintext credentials:** +- Never store private keys in plugin state or logs. +- Derive ephemeral keys per session; never reuse. + +**Threat model per authority level:** +- `regular` authority: social/content operations — lowest privilege. +- `active` authority: funds, staking, voting — medium privilege. +- `master` authority: key rotation, recovery — highest privilege. Require explicit user confirmation in any UI. + +--- + +## Monitoring and Incident Response + +**Metrics to monitor:** +- Peer connection count and churn rate. +- Webserver thread pool queue depth and response latency. +- Failed signature validation rate (visible in logs at `warn` level). +- Bandwidth per peer connection. + +**Incident response:** +1. Isolate the affected endpoint (restrict `webserver-http-endpoint` to loopback). +2. Rotate signing keys via the master authority if a validator key is compromised. +3. Re-validate account authorities after key rotation. +4. Review logs around `sign_state` failures and unusual authority chains. + +**Key rotation:** +- Validator signing key: `update_validator` operation with new signing key. +- Account keys: `update_account` with new master/active/regular keys. +- All key changes take effect immediately on the next block. + +--- + +See also: [Plugin Development](../development/plugin-development.md), [Data Types](../protocol/data-types.md), [Validators](../protocol/operations/validators.md), [Webserver Plugin](../plugins/webserver.md). diff --git a/docs/api/cli-wallet.md b/docs/api/cli-wallet.md new file mode 100644 index 0000000000..4726513bf7 --- /dev/null +++ b/docs/api/cli-wallet.md @@ -0,0 +1,251 @@ +# CLI Wallet + +The `cli_wallet` executable provides an interactive command-line interface for managing accounts, signing and broadcasting transactions, and querying the blockchain. + +--- + +## Connection + +```bash +cli_wallet --server-rpc-endpoint="ws://127.0.0.1:8091" +``` + +On first run, set a password: +``` +new >>> set_password "yourpassword" +``` + +Then unlock: +``` +locked >>> unlock "yourpassword" +``` + +--- + +## Wallet Management + +| Command | Description | +|---------|-------------| +| `is_new` | Returns `true` if no password set yet | +| `is_locked` | Returns `true` if wallet is locked | +| `lock` | Lock the wallet | +| `unlock "password"` | Unlock the wallet | +| `set_password "password"` | Set or change the password | +| `load_wallet_file "file.json"` | Load a wallet file (`""` = reload current) | +| `save_wallet_file "file.json"` | Save wallet to file | +| `set_transaction_expiration 60` | Set transaction TTL in seconds | +| `quit` | Exit the wallet | +| `help` | List all commands | +| `gethelp "command"` | Detailed help for one command | + +--- + +## Key Management + +| Command | Description | +|---------|-------------| +| `import_key "5K..."` | Import a WIF private key | +| `suggest_brain_key` | Generate a suggested brain key with public/private pair | +| `list_keys` | List all private keys (WIF) in the wallet | +| `get_private_key "VIZpubkey..."` | Get WIF for a known public key | +| `get_private_key_from_password "account" "role" "password"` | Derive key from credentials | +| `normalize_brain_key "words..."` | Normalize a brain key string | + +--- + +## Querying + +| Command | Description | +|---------|-------------| +| `info` | Current blockchain state | +| `database_info` | Database object statistics | +| `get_block 1000000` | Block data | +| `get_ops_in_block 1000000 false` | Operations in block (`true` = virtual only) | +| `get_active_validators` | Active validator set | +| `get_account "alice"` | Account object | +| `list_accounts "" 100` | Paginated account list | +| `list_my_accounts` | Accounts with keys in wallet | +| `get_account_history "alice" -1 100` | Last 100 operations for alice | +| `get_transaction "txid..."` | Transaction by ID | +| `get_master_history "alice"` | Master key change history | +| `get_withdraw_routes "alice" "all"` | Withdrawal routes (`"incoming"`, `"outgoing"`, `"all"`) | +| `get_proposed_transactions "alice" 0 100` | Proposals requiring alice's approval | + +--- + +## Account Operations + +| Command | Description | +|---------|-------------| +| `create_account "creator" "1.000 VIZ" "10.000000 SHARES" "newaccount" "{}" true` | Create account with auto-generated keys | +| `create_account_with_keys "creator" "1.000 VIZ" "10.000000 SHARES" "newaccount" "{}" "VIZmaster..." "VIZactive..." "VIZregular..." "VIZmemo..." true` | Create account with specified keys | +| `update_account "account" "{}" "VIZm..." "VIZa..." "VIZr..." "VIZmemo..." true` | Update all keys and metadata | +| `update_account_auth_key "account" "active" "VIZnewkey..." 1 true` | Add key to authority (weight 0 = remove) | +| `update_account_auth_account "account" "active" "guardian" 1 true` | Add account to authority | +| `update_account_auth_threshold "account" "active" 2 true` | Set authority weight threshold | +| `update_account_meta "account" "{\"key\":\"value\"}" true` | Update JSON metadata (regular auth) | +| `update_account_memo_key "account" "VIZnewmemo..." true` | Update memo key | +| `delegate_vesting_shares "alice" "bob" "100.000000 SHARES" true` | Delegate SHARES (0 = remove) | + +--- + +## Transfer and Vesting + +| Command | Description | +|---------|-------------| +| `transfer "alice" "bob" "10.000 VIZ" "memo" true` | Transfer VIZ (prefix memo with `#` for encrypted) | +| `transfer_to_vesting "alice" "alice" "100.000 VIZ" true` | Stake VIZ as SHARES | +| `withdraw_vesting "alice" "100.000000 SHARES" true` | Start power-down (0 = cancel) | +| `set_withdraw_vesting_route "alice" "bob" 5000 false true` | Route 50% of withdrawals to bob as VIZ | + +--- + +## Validator Operations + +| Command | Description | +|---------|-------------| +| `list_validators "" 100` | List validators | +| `get_validator "validatorname"` | Validator object | +| `update_validator "myvalidator" "https://url" "VIZsigningkey..." true` | Register/update validator | +| `update_chain_properties "myvalidator" {...} true` | Vote on chain properties (init format) | +| `versioned_update_chain_properties "myvalidator" {...} true` | Vote on versioned chain properties (hf9 format) | +| `vote_for_validator "alice" "myvalidator" true true` | Vote for validator (`false` = remove vote) | +| `set_voting_proxy "alice" "proxy" true` | Set validator vote proxy (`""` = remove) | + +--- + +## Escrow Operations + +| Command | Description | +|---------|-------------| +| `escrow_transfer "alice" "bob" "agent" 1 "100.000 VIZ" "1.000 VIZ" "2024-06-01T00:00:00" "2024-07-01T00:00:00" "{}" true` | Create escrow | +| `escrow_approve "alice" "bob" "agent" "bob" 1 true true` | Approve escrow (who = `"bob"` or `"agent"`) | +| `escrow_dispute "alice" "bob" "agent" "alice" 1 true` | Raise dispute (who = `"alice"` or `"bob"`) | +| `escrow_release "alice" "bob" "agent" "agent" "bob" 1 "100.000 VIZ" true` | Release funds | + +--- + +## Recovery Operations + +| Command | Description | +|---------|-------------| +| `request_account_recovery "recovery" "victim" {"weight_threshold":1,...} true` | Request recovery as recovery account | +| `recover_account "victim" {"recent_master_auth"} {"new_master_auth"} true` | Confirm recovery | +| `change_recovery_account "account" "new_recovery" true` | Change recovery account (30-day delay) | + +--- + +## Committee Operations + +| Command | Description | +|---------|-------------| +| `committee_worker_create_request "creator" "https://url" "worker" "100.000 VIZ" "500.000 VIZ" 604800 true` | Create funding request | +| `committee_worker_cancel_request "creator" 123 true` | Cancel request | +| `committee_vote_request "voter" 123 10000 true` | Vote (+10000 = full support, -10000 = full oppose, 0 = remove) | + +--- + +## Invite Operations + +| Command | Description | +|---------|-------------| +| `create_invite "creator" "10.000 VIZ" "VIZinvitekey..." true` | Create invite | +| `claim_invite_balance "initiator" "receiver" "5Kinvitesecret..." true` | Claim invite balance | +| `invite_registration "initiator" "newaccount" "5Kinvitesecret..." "VIZnewaccountkey..." true` | Create account from invite | +| `use_invite_balance "initiator" "receiver" "5Kinvitesecret..." true` | Use invite (may vest to SHARES) | + +--- + +## Award Operations + +| Command | Description | +|---------|-------------| +| `award "alice" "bob" 1000 0 "Great work!" [] true` | Award with energy-based reward | +| `fixed_award "alice" "bob" "10.000000 SHARES" 5000 0 "Reward" [] true` | Award fixed SHARES amount | + +Beneficiary format: `[{"account":"charlie","weight":2000}]` + +--- + +## Subscription Operations + +| Command | Description | +|---------|-------------| +| `set_paid_subscription "account" "https://url" 3 "10.000 VIZ" 30 true` | Create subscription (3 levels, 10 VIZ/period, 30-day period) | +| `paid_subscribe "subscriber" "account" 2 "20.000 VIZ" 1 true true` | Subscribe to level 2 | + +--- + +## Account Market + +| Command | Description | +|---------|-------------| +| `set_account_price "account" "account" "100.000 VIZ" true true` | List for sale | +| `set_subaccount_price "account" "account" "50.000 VIZ" true true` | List subaccount creation for sale | +| `buy_account "buyer" "account" "100.000 VIZ" "VIZnewkey..." "0.000 VIZ" true` | Buy account | +| `target_account_sale "account" "account" "targetbuyer" "100.000 VIZ" true true` | Targeted sale | + +--- + +## Custom Operation + +```bash +custom [] ["alice"] "my_app" "{\"action\":\"follow\",\"target\":\"bob\"}" true +``` + +Parameters: `required_active_auths` `required_regular_auths` `id` `json` `broadcast` + +--- + +## Transaction Builder + +Build and sign custom multi-operation transactions: + +```bash +begin_builder_transaction # Returns handle (e.g. 0) +add_operation_to_builder_transaction 0 [2,{"from":"alice","to":"bob","amount":"10.000 VIZ","memo":""}] +sign_builder_transaction 0 true # Sign and broadcast +``` + +| Command | Description | +|---------|-------------| +| `begin_builder_transaction` | Start a new transaction (returns handle) | +| `add_operation_to_builder_transaction handle [type_id, op]` | Add operation | +| `replace_operation_in_builder_transaction handle idx [type_id, op]` | Replace operation | +| `preview_builder_transaction handle` | Preview transaction JSON | +| `sign_builder_transaction handle broadcast` | Sign (and optionally broadcast) | +| `propose_builder_transaction handle author title memo expiry review broadcast` | Wrap in proposal | +| `remove_builder_transaction handle` | Discard | +| `get_prototype_operation "transfer_operation"` | Get empty operation template | +| `serialize_transaction {trx}` | Get hex serialization | +| `sign_transaction {trx} broadcast` | Sign arbitrary transaction | + +--- + +## NS DNS Helpers + +Store DNS records in account metadata: + +```bash +ns_set_records "myaccount" {"a_records":["188.120.231.153"],"ssl_hash":"4a4613...","ttl":28800} true +ns_get_summary "myaccount" +ns_extract_a_records "myaccount" +ns_remove_records "myaccount" true +``` + +Validation helpers: `ns_validate_ipv4`, `ns_validate_sha256_hash`, `ns_validate_ttl`, `ns_validate_ssl_txt_record`, `ns_validate_metadata`, `ns_create_metadata`. + +--- + +## Private Messaging + +```bash +get_encrypted_memo "alice" "bob" "#secret message" +decrypt_memo "#encrypteddata..." +get_inbox "myaccount" "2024-01-15T00:00:00" 100 0 +get_outbox "myaccount" "2024-01-15T00:00:00" 100 0 +``` + +--- + +See also: [JSON-RPC API](./json-rpc.md), [Operations Overview](../protocol/operations/overview.md), [Data Types](../protocol/data-types.md). diff --git a/docs/api/client-libraries.md b/docs/api/client-libraries.md new file mode 100644 index 0000000000..19bdbc4704 --- /dev/null +++ b/docs/api/client-libraries.md @@ -0,0 +1,140 @@ +# Client Libraries + +Official client libraries are available for Python, PHP, and JavaScript. All libraries communicate with a VIZ node over the JSON-RPC API and handle transaction signing locally. + +--- + +## Python — viz-python-lib + +**Repository:** https://github.com/VIZ-Blockchain/viz-python-lib + +### Installation + +```bash +pip install viz-python-lib +``` + +### Quick start + +```python +from viz import Client + +viz = Client( + node="wss://node.viz.cx/ws", + keys=["5...private_key..."] +) + +# Award energy to an account +viz.award("receiver_account", 10.5, "with love", None, "your_account") +``` + +### Features + +- WebSocket (`wss://`) and HTTP (`https://`) transport +- Full transaction broadcasting: all protocol operations +- Local key management — private keys never leave the client +- Automatic retry and failover across multiple nodes +- Python 3 compatible + +--- + +## PHP — viz-php-lib + +**Repository:** https://github.com/VIZ-Blockchain/viz-php-lib + +### Installation + +The library uses PSR-4 autoloading and does not require Composer. Clone or download the repository and include the autoloader: + +```php +require_once '/path/to/viz-php-lib/autoload.php'; +``` + +**PHP extension requirements:** `gmp` (preferred) or `bcmath` for big-integer arithmetic. + +### Quick start + +```php +$private_key = '5...your_private_key...'; +$tx = new VIZ\Transaction('https://api.viz.world/', $private_key); + +// Build and broadcast an award transaction +$tx_data = $tx->award($account, 'committee', 1000, 0, 'memo'); +$tx_status = $tx->execute($tx_data['json']); +``` + +### Features + +- All 40 protocol operations supported +- AES-256-CBC memo encryption/decryption +- No external dependencies — standard PHP extensions only +- Compatible with PHP 7.x and 8.x + +--- + +## JavaScript — viz-js-lib + +**Repository:** https://github.com/VIZ-Blockchain/viz-js-lib +**npm:** https://www.npmjs.com/package/viz-js-lib + +### Installation + +```bash +npm install viz-js-lib --save +``` + +### CDN (browser) + +```html + + + + + +``` + +### Quick start + +```js +const viz = require('viz-js-lib'); + +// Derive WIF key from credentials +var wif = viz.auth.toWif(username, password, 'regular'); + +// Broadcast a vote +viz.broadcast.vote(wif, voter, author, permlink, weight, function(err, result) { + console.log(err, result); +}); +``` + +### Node configuration + +```js +viz.config.set('websocket', 'wss://node.viz.cx/ws'); +// or HTTP: +viz.config.set('websocket', 'https://node.viz.cx/'); +``` + +### Features + +- WebSocket (`ws://` / `wss://`) and HTTP (`http://` / `https://`) transports +- Works in Node.js and modern browsers +- Key utilities: `toWif`, `toPublic`, `isWif`, `signTransaction` +- Full broadcast API covering all protocol operations +- MIT license + +--- + +## Choosing a Library + +| Criterion | Python | PHP | JavaScript | +|-----------|--------|-----|-----------| +| Install | `pip` | Manual PSR-4 | `npm` | +| Transport | WS / HTTP | HTTP | WS / HTTP | +| Runtime | Python 3 | PHP 7+ | Node.js / browser | +| Dependencies | minimal | GMP or BCMath | none (bundled) | +| License | MIT | MIT | MIT | + +--- + +See also: [JSON-RPC API](./json-rpc.md), [CLI Wallet](./cli-wallet.md). diff --git a/docs/api/json-rpc.md b/docs/api/json-rpc.md new file mode 100644 index 0000000000..4f510877d4 --- /dev/null +++ b/docs/api/json-rpc.md @@ -0,0 +1,228 @@ +# JSON-RPC API + +All VIZ node APIs use JSON-RPC 2.0 over HTTP POST or WebSocket. + +--- + +## Request Format + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "method": "api_name.method_name", + "params": [arg1, arg2] +} +``` + +- `id` may be any number or string; it is echoed in the response. +- `params` may be an array or object depending on the method. +- Both HTTP POST and WebSocket are supported. Subscriptions require WebSocket. + +--- + +## Response Format + +**Success:** +```json +{ "jsonrpc": "2.0", "id": 1, "result": { ... } } +``` + +**Error:** +```json +{ "jsonrpc": "2.0", "id": 1, "error": { "code": -32602, "message": "Invalid params" } } +``` + +### Error Codes + +| Code | Meaning | +|------|---------| +| `-32700` | Parse error — invalid JSON | +| `-32600` | Invalid request | +| `-32601` | Method not found | +| `-32602` | Invalid params | +| `-32603` | Internal error | +| `-32099` to `-32000` | Server error (exception from handler) | + +--- + +## Plugin Namespaces + +| Namespace | Status | Description | +|-----------|--------|-------------| +| `database_api` | Active | Block, account, chain state queries | +| `network_broadcast_api` | Active | Transaction and block broadcast | +| `witness_api` | Active | Validator queries | +| `account_by_key` | Active | Reverse key lookup | +| `account_history` | Active | Per-account operation history | +| `operation_history` | Active | Block operation queries | +| `committee_api` | Active | Committee request queries | +| `invite_api` | Active | Invite queries | +| `paid_subscription_api` | Active | Subscription queries | +| `custom_protocol_api` | Active | Custom protocol metadata | +| `auth_util` | Active | Authority verification | +| `block_info` | Active | Extended block info | +| `raw_block` | Active | Raw block export | +| `follow` | Deprecated | Follow/feed indexes | +| `tags` | Deprecated | Content discovery by tag | +| `social_network` | Deprecated | High-level content queries | +| `private_message` | Deprecated | Encrypted message index | +| `debug_node` | Dev only | Test/debug operations | + +--- + +## `database_api` Methods + +| Method | Description | +|--------|-------------| +| `get_block_header(block_num)` | Block header for given height | +| `get_block(block_num)` | Full signed block | +| `get_irreversible_block_header(block_num)` | Block header if irreversible | +| `get_irreversible_block(block_num)` | Full block if irreversible | +| `set_block_applied_callback(callback)` | WebSocket: subscribe to new blocks | +| `get_config()` | Compile-time chain constants | +| `get_dynamic_global_properties()` | Current chain state | +| `get_chain_properties()` | Median validator chain properties | +| `get_hardfork_version()` | Current hardfork version string | +| `get_next_scheduled_hardfork()` | Next pending hardfork info | +| `get_accounts(names[])` | Full account objects | +| `lookup_account_names(names[])` | Same as get_accounts but nullable | +| `lookup_accounts(lower_bound, limit)` | Paginated account name list | +| `get_account_count()` | Total registered accounts | +| `get_master_history(account)` | Master key change history | +| `get_recovery_request(account)` | Pending account recovery request | +| `get_escrow(from, escrow_id)` | Escrow object | +| `get_withdraw_routes(account, type)` | Vesting withdrawal routes (`"incoming"` / `"outgoing"` / `"all"`) | +| `get_vesting_delegations(account, from, limit, type)` | Delegations (`"delegated"` / `"received"`) | +| `get_expiring_vesting_delegations(account, from, limit)` | Delegations in return window | +| `get_transaction_hex(trx)` | Hex-encoded serialized transaction | +| `get_required_signatures(trx, available_keys[])` | Minimal key set to sign | +| `get_potential_signatures(trx)` | All keys that could sign | +| `verify_authority(trx)` | `true` if fully signed | +| `verify_account_authority(name, keys[])` | `true` if keys satisfy authority | +| `get_database_info()` | Chainbase memory usage stats | +| `get_proposed_transactions(account, from, limit)` | Proposals requiring account approval | +| `get_accounts_on_sale(from, limit)` | Accounts listed for direct sale | +| `get_accounts_on_auction(from, limit)` | Accounts listed for auction | +| `get_subaccounts_on_sale(from, limit)` | Subaccount creation rights for sale | + +--- + +## `network_broadcast_api` Methods + +| Method | Description | +|--------|-------------| +| `broadcast_transaction(trx)` | Broadcast (async) | +| `broadcast_transaction_synchronous(trx)` | Broadcast and wait for inclusion in a block | +| `broadcast_transaction_with_callback(callback, trx)` | Broadcast with WebSocket callback | +| `broadcast_block(block)` | Broadcast a signed block (validators) | + +--- + +## `witness_api` Methods + +| Method | Description | +|--------|-------------| +| `get_active_witnesses()` | Current active validator set (21 accounts) | +| `get_witness_schedule()` | Full validator schedule object | +| `get_witnesses(ids[])` | Validators by internal IDs | +| `get_witness_by_account(account)` | Validator object for an account | +| `get_witnesses_by_vote(lower_bound, limit)` | Validators ranked by vote weight | +| `get_witnesses_by_counted_vote(lower_bound, limit)` | Validators by counted votes | +| `get_witness_count()` | Total registered validators | +| `lookup_witness_accounts(lower_bound, limit)` | List validator account names | + +--- + +## `account_history` Methods + +### `get_account_history(account, from, limit)` + +Returns operations involving `account`. `from = -1` starts from the most recent. + +```json +{ + "method": "account_history.get_account_history", + "params": ["alice", -1, 100] +} +``` + +Returns a map of `{ sequence: { trx_id, block, op: [type_id, data] } }`. + +--- + +## `operation_history` Methods + +| Method | Description | +|--------|-------------| +| `get_ops_in_block(block_num, only_virtual)` | Operations in a block | +| `get_transaction(trx_id)` | Transaction by ID | + +--- + +## `committee_api` Methods + +| Method | Description | +|--------|-------------| +| `get_committee_request(request_id)` | Request details and status | +| `get_committee_request_votes(request_id)` | Votes on a request | +| `get_committee_requests_list(from, limit)` | List of request IDs | + +--- + +## `invite_api` Methods + +| Method | Description | +|--------|-------------| +| `get_invites_list(from, limit)` | All active invite IDs | +| `get_invite_by_id(id)` | Invite by internal ID | +| `get_invite_by_key(public_key)` | Invite by public key | + +--- + +## `paid_subscription_api` Methods + +| Method | Description | +|--------|-------------| +| `get_paid_subscriptions(from, limit)` | All subscription offerings | +| `get_paid_subscription_options(account)` | Subscription config for account | +| `get_paid_subscription_status(subscriber, account)` | Subscription status | +| `get_active_paid_subscriptions(subscriber, from, limit)` | Active subscriptions | +| `get_inactive_paid_subscriptions(subscriber, from, limit)` | Expired subscriptions | + +--- + +## WebSocket Subscriptions + +Only available over a persistent WebSocket connection. + +| Method | Callback data | +|--------|---------------| +| `database_api.set_block_applied_callback` | Block header on every applied block | +| `database_api.set_pending_transaction_callback` | Transaction when entering pending pool | +| `database_api.cancel_all_subscriptions` | Unsubscribe all | + +--- + +## Recommended Plugin Sets + +**Minimal API node:** +```ini +plugin = chain json_rpc webserver p2p +plugin = database_api network_broadcast_api +``` + +**Full API node (add):** +```ini +plugin = witness_api account_by_key account_history operation_history +plugin = committee_api invite_api paid_subscription_api +``` + +**Validator node (add):** +```ini +plugin = validator snapshot +``` + +--- + +See also: [Database API](../plugins/database-api.md), [Webserver Plugin](../plugins/webserver.md), [Operations Overview](../protocol/operations/overview.md). diff --git a/docs/consensus/block-processing.md b/docs/consensus/block-processing.md new file mode 100644 index 0000000000..c2917fcff2 --- /dev/null +++ b/docs/consensus/block-processing.md @@ -0,0 +1,259 @@ +# Block Processing + +Internal mechanics of block application, pending transaction management, and fork switching. + +--- + +## Overview + +When a node receives a new block via P2P, the chain plugin calls `database::push_block()`. The sequence is: + +1. Temporarily remove pending (mempool) transactions from the database. +2. Apply the incoming block. +3. Re-apply pending transactions not included in the block. + +This is managed by the `without_pending_transactions` helper in `db_with.hpp`. + +--- + +## Key Data Structures + +| Structure | Type | Purpose | +|-----------|------|---------| +| `_pending_tx` | `vector` | Mempool: received transactions waiting to be included in a block | +| `_popped_tx` | `deque` | Transactions from a popped block (during fork switch); re-applied after switch | +| `_pending_tx_session` | `optional` | Undo session covering all pending transaction state changes | + +--- + +## Block Application Flow + +``` +push_block(new_block) + └─ without_pending_transactions(db, skip, _pending_tx, callback) + ├─ pending_transactions_restorer ctor: clear_pending() + ├─ callback: _push_block(new_block) ← apply the incoming block + └─ ~pending_transactions_restorer() ← restore pending transactions +``` + +### Step-by-step inside `_push_block()` + +1. **Early rejection checks** (see below). +2. Push the block to `fork_db`. +3. If the new fork head directly extends the current head (`new_block.previous == head_block_id()`): + - Skip fork switch logic, fall through to `apply_block()`. +4. If the new head is higher and diverges from current head: + - **Vote-weighted fork comparison** (HF12) — see [Fork Resolution](./fork-resolution.md). + - Pop old-fork blocks until common ancestor. + - Apply new-fork blocks in order. +5. `apply_block()` runs transaction evaluators, updates dynamic global properties, processes virtual operations. +6. `update_last_irreversible_block()` — advances LIB if ≥14 validators have confirmed. + +--- + +## Pending Transaction Restoration + +The `~pending_transactions_restorer()` destructor processes two lists in order after the new block has been applied. + +### Step 1: Re-apply `_popped_tx` (from fork switch) + +``` +for each tx in _popped_tx: + if time_elapsed > 200ms → postpone (push back to _pending_tx) + else if is_known_transaction(tx) → skip (already in chain) + else → _push_transaction(tx) → applied_txs++ +``` + +### Step 2: Re-apply `_pending_transactions` (original mempool) + +``` +for each tx in _pending_transactions: + if time_elapsed > 200ms → postpone + else if is_known_transaction(tx) → skip + else → _push_transaction(tx) → applied_txs++ + on transaction_exception → discard (invalid) + on fc::exception → silently discard +``` + +### Step 3: Log summary + +If any transactions were postponed: +``` +Postponed N pending transactions. M were applied. +``` + +--- + +## Time Limit + +**`CHAIN_PENDING_TRANSACTION_EXECUTION_LIMIT` = 200 ms** + +Once elapsed since the start of restoration, all remaining transactions are pushed back to `_pending_tx` without being applied. This prevents the node from blocking on a large mempool. + +The limit triggers during: +- High-throughput blocks with many pending transactions +- CPU-intensive operations +- System under load + +--- + +## Block Size Limit During Generation + +**`CHAIN_BLOCK_GENERATION_POSTPONED_TX_LIMIT` = 5** + +During `_generate_block()`, transactions that would exceed `maximum_block_size` are skipped. After 5 consecutive oversized transactions the generation loop breaks. These remain in `_pending_tx` for the next block. + +Log: +``` +Postponed N transactions due to block size limit +``` + +--- + +## Fork DB Head-Seeding + +Before pushing a block, `_push_block()` ensures the current database head block is present in `fork_db`: + +``` +if new_block.previous == head_block_id() + AND head_block_id() NOT in fork_db: + fetch head block from block log (or dlt_block_log in DLT mode) + fork_db.start_block(head_block) +``` + +Without this seed, valid next-blocks would throw `unlinkable_block_exception` because their `previous` is not in `fork_db`. This also fixes validator nodes that generate their own blocks — `generate_block()` sets `pending_block.previous = head_block_id()`. + +--- + +## Direct-Extension Bypass + +After pushing a block to `fork_db`, if the block directly extends the database head: + +``` +if new_block.previous == head_block_id(): + → skip fork switch, fall through to apply_block() +``` + +This handles the case where `fork_db._head` points to a stale higher block from a previous failed sync cycle. Without this bypass, the stale head would trigger fork switch logic that silently drops the valid next-block. + +--- + +## Early Block Rejection + +`_push_block()` applies several early rejection checks to avoid unnecessary work and prevent infinite sync loops: + +| Check | Condition | Action | +|-------|-----------|--------| +| Already applied | `block.num ≤ head` and ID matches existing block | Silently ignore (duplicate) | +| Different fork | `block.num ≤ head`, different ID, parent not in fork_db | Silently reject | +| Far-ahead, gap > 100 | `block.num > head`, parent unknown, gap > 100 blocks | Silently reject (memory protection) | +| Far-ahead, gap ≤ 100 | `block.num > head`, parent unknown, gap ≤ 100 | Allow to fork_db (cached in unlinked index) | +| Direct next block | `block.previous == head_block_id()` | Always allowed | + +The 100-block gap threshold prevents memory bloat from dead-fork chains while allowing normal out-of-order block processing during P2P sync. + +--- + +## Fork Switch + +When the node switches to a different fork: + +1. `pop_block()` removes the current head block; its transactions move to `_popped_tx`. +2. Repeat until the common ancestor is reached. +3. Apply new-fork blocks in order from common ancestor to new head. +4. `~pending_transactions_restorer()` reapplies `_popped_tx` first, then the original mempool. + +Transactions already in the new chain are silently skipped via `is_known_transaction()`. + +### Linear extension vs. actual fork + +`_push_next()` in `fork_db` can auto-link multiple orphan blocks when their parent arrives, causing `fork_db._head` to jump several blocks ahead of the database head in one `push_block()` call. The code distinguishes: + +- **Linear extension** (`branches.second.size() == 1` and common ancestor == current head): no pop operations needed; blocks are applied directly. +- **Actual fork switch** (divergent branches): full pop-and-reapply sequence. + +This distinction is critical in DLT mode where LIB == head and undo sessions are committed — a pop loop on a linear extension would be infinite. + +--- + +## Orphan Block Handling (Unlinked Index) + +When a block arrives whose parent is unknown, `fork_db` stores it in `_unlinked_index`. When the missing parent later arrives: + +1. `_push_block(parent)` links the parent to the chain. +2. `_push_next(parent)` iterates `_unlinked_index` for children of `parent`. +3. Children are moved to `_index` and recursively linked. +4. `fork_db._head` may advance multiple blocks in one call (triggers linear extension path). + +--- + +## Peer Strike-Based Soft-Ban + +Peers are not immediately banned for sending unlinkable blocks. A counter accumulates: + +| Path | Threshold | Reset condition | +|------|-----------|----------------| +| Normal operation: unlinkable block at/below head | 20 strikes | Valid block accepted from same peer | +| Sync path: generic block rejection | 20 strikes | Valid block accepted from same peer | +| Dead fork / block too old | Immediate ban | — | + +Honest peers can recover from transient errors (snapshot reload, timing races, brief micro-forks). + +--- + +## Validator Block Production Timing + +The validator plugin uses a 250ms timer with 250ms look-ahead: + +1. Timer fires every **250ms** (aligned to 250ms wall-clock boundaries, minimum sleep 50ms). +2. `maybe_produce_block()` computes `now = NTP_time + 250ms`. +3. `get_slot_at_time(now)` finds the current slot. +4. If the slot belongs to a configured validator and `|scheduled_time - now| ≤ 500ms`, produce the block with the deterministic `scheduled_time` as the timestamp. + +``` +Slot at T=6.000, tick at T=5.750: + now = 5.750 + 0.250 = 6.000 → slot matched → produce +``` + +This yields a 500ms safety margin against the lag threshold. + +### Production conditions (checked in order) + +| Condition | Failure result | +|-----------|----------------| +| Chain is synced (or `enable-stale-production`) | `not_synced` | +| `get_slot_at_time(now) > 0` | `not_time_yet` | +| Scheduled validator is in our configured set | `not_my_turn` | +| Non-null signing key on chain | `not_my_turn` | +| Private key for signing key in memory | `no_private_key` | +| Network participation ≥ threshold (pre-HF12) | `low_participation` | +| `|scheduled_time - now| ≤ 500ms` | `lag` | +| No competing block at same height in fork_db | `fork_collision` | +| Last 21 blocks NOT all from our validators | `minority_fork` | + +--- + +## Configuration Constants + +| Constant | Value | Description | +|----------|-------|-------------| +| `CHAIN_PENDING_TRANSACTION_EXECUTION_LIMIT` | 200 ms | Max time to re-apply pending transactions after block push | +| `CHAIN_BLOCK_GENERATION_POSTPONED_TX_LIMIT` | 5 | Max consecutive oversized transactions skipped during generation | +| `CHAIN_BLOCK_SIZE` | 65536 bytes | Hard block size limit | +| `maximum_block_size` | Dynamic (validator median) | Soft block size limit | +| `CHAIN_BLOCK_INTERVAL` | 3 s | Block production interval | + +--- + +## Debug Log Prefixes + +| Prefix | Meaning | +|--------|---------| +| `FORK-SWITCH-POP: popping head #H` | Normal fork switch — popping old-fork block | +| `FORK-RECOVER-POP: popping head #H` | Error recovery — reverting a failed fork switch | +| `POP_BLOCK: db_head=#X fork_db_head=#Y` | State before every `pop_block()` call | +| `Fork switch: new_head=#X branches.first=N branches.second=M` | Branches before fork switch; `M=0` means linear extension | + +--- + +See also: [Fair-DPOS](./fair-dpos.md), [Fork Resolution](./fork-resolution.md), [Validator Node](../node/validator-node.md). diff --git a/docs/consensus/emergency-consensus.md b/docs/consensus/emergency-consensus.md new file mode 100644 index 0000000000..82b5a00544 --- /dev/null +++ b/docs/consensus/emergency-consensus.md @@ -0,0 +1,221 @@ +# Emergency Consensus Mode + +Emergency consensus mode (introduced in HF12) activates automatically when the network stalls for 1 hour. A special "committee" validator takes over block production to maintain chain continuity until real validators restore their signing keys. + +--- + +## Key Constants + +| Constant | Value | Meaning | +|----------|-------|---------| +| `CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC` | 3600 s | Inactivity time before activation | +| `CHAIN_EMERGENCY_WITNESS_ACCOUNT` | `"committee"` | Emergency block producer account | +| `CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY` | `VIZ75CR...` | Deterministic emergency signing key | +| `CHAIN_EMERGENCY_EXIT_NORMAL_BLOCKS` | 21 | Consecutive real-validator blocks for exit | +| `CHAIN_IRREVERSIBLE_THRESHOLD` | 75% | Fraction of schedule slots required to exit | +| `CHAIN_MAX_WITNESSES` | 21 | Maximum validator slots | + +### State fields in `dynamic_global_property_object` + +| Field | Default | Meaning | +|-------|---------|---------| +| `emergency_consensus_active` | `false` | Emergency mode is active | +| `emergency_consensus_start_block` | `0` | Block number at activation | + +--- + +## Activation + +`update_global_dynamic_data()` runs on every applied block and checks: + +1. **HF12 gate** — skip if the hardfork has not activated. +2. **Already active** — skip if emergency mode is already on. +3. **LIB block available in block log** — skip if the LIB block is not present in the block log (DLT nodes after snapshot restore have an empty block log; a missing LIB block would compute millions of stale seconds and trigger a false activation deadlock). +4. **Timeout** — compute `seconds_since_lib = current_block.timestamp − lib_block.timestamp`. If `< 3600`, skip. + +All checks use only block-embedded timestamps — no wall clock, no skip flags. This guarantees identical deterministic activation on every node, including replays. + +### Activation sequence + +When the timeout threshold is crossed: + +1. Set `dgp.emergency_consensus_active = true` and `dgp.emergency_consensus_start_block = block_num`. +2. Create or update the "committee" validator object: + - `signing_key = CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY` + - `props = current_median_props` + - Hardfork votes set to the currently applied version (neutral voter). +3. Disable ALL real validators: set `signing_key = zero`, reset `penalty_percent = 0`, `current_run = 0`. +4. Remove all `witness_penalty_expire` objects. +5. Override the validator schedule: all `CHAIN_MAX_WITNESSES` slots → "committee". +6. Notify fork DB: `_fork_db.set_emergency_mode(true)` (enables deterministic hash tie-breaking). +7. Log: `"EMERGENCY CONSENSUS MODE activated at block #N. No blocks for X seconds since LIB Y."` + +--- + +## Emergency Operation + +### Block production — master vs. follower + +Nodes with `emergency-private-key` configured in `config.ini` are **emergency masters**; all other nodes are **followers**. + +```ini +# Emergency master node only +emergency-private-key = 5Jzzz... # CHAIN_EMERGENCY_WITNESS_ACCOUNT private key +``` + +| Role | DLT sync check | Minority fork check | Production | +|------|----------------|---------------------|------------| +| Master | Bypassed (would deadlock) | Skipped (all blocks being "ours" is expected) | Produces blocks for all slots | +| Follower | Normal | 21-block check (1 full round) | Standard validator production for own scheduled slots | + +### Fork DB deterministic tie-breaking + +During emergency, multiple masters (geographic redundancy) may produce competing blocks at the same height with the same key. Arrival order varies by P2P topology. + +`fork_database::_push_block()` resolves ties: +``` +item->num == _head->num AND _emergency_consensus_active: + item->id < _head->id → _head = item (lower block hash wins) + else → keep current _head +``` + +All nodes converge on the same chain tip regardless of which block they saw first. + +### LIB advancement + +`update_last_irreversible_block()` computes LIB normally but **caps it at HEAD − 1** during emergency. Without the cap, since all 21 slots are "committee" and `committee.last_supported_block_num == HEAD`, the `nth_element` computation returns HEAD — which would commit the current block's undo session mid-apply, causing irreversible corruption. + +After 3 committee blocks (`CHAIN_IRREVERSIBLE_SUPPORT_MIN_RUN`), LIB advances every block, keeping the fork DB window small. + +### Hybrid validator schedule + +`update_witness_schedule()` builds a hybrid schedule each round: + +- Slots where the real validator's `signing_key` is non-zero: keep the real validator. +- Empty or zero-key slots: fill with "committee". + +This allows real validators to return incrementally. Each time a real validator restores their signing key (via `validator_update_operation`), the next schedule rebuild includes them. + +Committee is excluded from hardfork version tallying and median chain property computation (it copies the current median, so counting it per-slot would distort the median). + +--- + +## Deactivation + +Every schedule rebuild checks the exit condition: + +``` +real_witness_slots >= CHAIN_MAX_WITNESSES × 75% +``` + +With 21 validators: `21 × 0.75 = 15.75 → 15` real validator slots required. + +When the condition is met: +1. `dgp.emergency_consensus_active = false`. +2. `_fork_db.set_emergency_mode(false)`. +3. Log: `"EMERGENCY CONSENSUS MODE deactivated at block #N. R real validators active."`. + +The network resumes normal operation on the next schedule cycle. + +--- + +## Startup Recovery + +`database::open()` checks the validator schedule after replay. If any slot is empty (string `""`), the emergency was in progress when the node shut down: + +1. If `emergency_consensus_active` is not already set → set it and set fork DB emergency flag. +2. Fill all slots with "committee". +3. Log: `"schedule repaired, all N slots set to committee"`. + +This ensures the schedule is always consistent after an unclean shutdown during emergency mode. + +--- + +## Validator Guard Integration + +The `witness_guard` plugin continues to operate during emergency and is in fact more critical: + +- Real validators are disabled (signing key set to null) during activation. +- The validator guard automatically broadcasts `validator_update_operation` to restore each validator's signing key once the null key is detected on-chain. +- The `enable-stale-production` guard in the validator guard **does not block** key restoration during emergency mode ("Emergency consensus handles its own recovery and key restoration may still be needed"). +- Once 15 validators restore their keys, the exit condition triggers. + +See [Validator Guard](../node/validator-guard.md). + +--- + +## P2P Interaction Guards + +Several P2P safeguards are emergency-aware: + +| Guard | Behavior during emergency | +|-------|--------------------------| +| `resync_from_lib()` | **Skipped entirely** — popping blocks near LIB during emergency would crash | +| `stale_sync_check_task()` | If master's head is advancing → reset timer, skip recovery; if follower head is stuck → allow recovery | +| `handle_block()` (DLT, sync mode, gap 0–2) | Treated as normal (not sync) to prevent production loop disruption | +| Snapshot stalled sync detection | Same logic as stale sync check | + +The `resync_from_lib()` guard is the most critical: during emergency, LIB is close to HEAD. Popping blocks back to LIB and resetting the fork DB would cause peer blocks from the real network to link to the re-seeded LIB, trigger a fork switch, pop below the committed LIB, and either crash or corrupt state. + +--- + +## Block Validation — Relaxed Slot Mapping + +`verify_signing_witness()` normally asserts that the block producer matches the scheduled validator exactly. During emergency: + +``` +If block.validator != scheduled_witness: + dlog("Emergency mode: accepting block from BW at slot scheduled for SW") + → accept anyway (signature is still validated against block.validator's signing_key) +``` + +This allows emergency masters to produce blocks even if a few slots are still assigned to real validators in the pending schedule. + +--- + +## Snapshot Compatibility + +Emergency state fields are included in snapshots with forward-compatible defaults: + +- `emergency_consensus_active` missing in snapshot → defaults to `false`. +- `emergency_consensus_start_block` missing → defaults to `0`. + +Snapshots created during an active emergency preserve the state correctly; snapshots created before HF12 import as non-emergency. + +--- + +## Guard Summary + +| # | Location | Guard | +|---|----------|-------| +| 1 | `update_global_dynamic_data` | Only activate if HF12 + not already active + LIB block available | +| 2 | `update_witness_schedule` | Hybrid override + exit check at ≥75% real validators | +| 3 | `update_last_irreversible_block` | Cap LIB at HEAD − 1 during emergency | +| 4 | `verify_signing_witness` | Relax slot-to-validator mapping | +| 5 | `fork_db._push_block` | Deterministic hash tie-breaking | +| 6 | `maybe_produce_block` (master) | Bypass sync, stale, participation; skip minority fork | +| 7 | `maybe_produce_block` (follower) | Must sync first; 21-block isolation check | +| 8 | `resync_from_lib` | **Skip entirely** during emergency | +| 9 | `stale_sync_check_task` | Skip if master's head advancing; allow if follower stuck | +| 10 | `handle_block` | Near-caught-up blocks treated as normal in DLT emergency | +| 11 | `database::open` | Startup schedule repair | +| 12 | `witness_guard` | Do not suppress key restoration during emergency | +| 13 | `snapshot import` | Forward-compatible field handling | +| 14 | `update_witness_schedule` | Exclude committee from hardfork version tallying | +| 15 | `update_median_witness_props` | Exclude committee from median computation | + +--- + +## Key Invariants + +1. **Deterministic activation** — uses only block-embedded timestamps; identical on every node and every replay. +2. **DLT snapshot safety** — skips activation if LIB block is absent from block log. +3. **Emergency fork immutability** — `resync_from_lib()` refuses to execute during emergency. +4. **Master/follower distinction** — only nodes with `--emergency-private-key` are masters. +5. **Fork DB convergence** — deterministic hash tie-breaking ensures all nodes pick the same block. +6. **LIB safety** — capped at HEAD − 1 to preserve undo protection. +7. **Neutral committee voting** — committee votes for current applied hardfork version, copies median props. + +--- + +See also: [Fair-DPOS](./fair-dpos.md), [Fork Resolution](./fork-resolution.md), [Validator Node](../node/validator-node.md), [Validator Guard](../node/validator-guard.md). diff --git a/docs/consensus/fair-dpos.md b/docs/consensus/fair-dpos.md new file mode 100644 index 0000000000..c7102063b6 --- /dev/null +++ b/docs/consensus/fair-dpos.md @@ -0,0 +1,170 @@ +# Fair-DPOS Consensus + +VIZ Ledger uses **Fair Delegated Proof of Stake (Fair-DPOS)**, an extension of the classic DPoS algorithm that adds participation enforcement and miss penalties to prevent validators from collecting rewards without actually producing blocks. + +--- + +## How Classic DPoS Works + +In standard DPoS: +- Token holders vote for validator accounts (weighted by their SHARES). +- The top-voted validators are scheduled to produce blocks in a round-robin schedule. +- Every 3 seconds one slot fires; the scheduled validator either produces a block or misses the slot. + +VIZ runs with **21 active validators** per schedule round. + +--- + +## The "Fair" Extension + +In classic DPoS a validator can miss blocks indefinitely and still receive votes (and sometimes rewards). Fair-DPOS adds: + +1. **Participation tracking** — a 128-bit bitmask tracks the last 128 slots. Each slot is marked 1 (produced) or 0 (missed). +2. **Participation threshold** — the node will not produce if fewer than `required-participation`% of recent slots were filled by any validator (default 33%). This guards against a minority-fork scenario. +3. **Miss penalties** — validators that miss blocks accumulate a miss counter. On each hardfork evaluation the worst performers can be removed from the active set. +4. **Reward sharing** (HF13) — validator block rewards are partially redistributed to their voters, aligning delegator incentives with validator performance. + +--- + +## Validator Schedule + +### Building the schedule + +Every `CHAIN_WITNESS_SCHEDULE_BLOCK_NUM` blocks (21) the chain recomputes the active validator set: + +1. Take the top-21 validators by total vote weight (SHARES delegated to them). +2. Add the **time-share validators** — a rotating slot for lower-ranked validators to participate occasionally, preventing complete concentration. +3. Shuffle the resulting 21 slots using the head block ID as entropy seed (deterministic shuffle = same result on all nodes). + +The resulting ordered list becomes the **current schedule**. Each position corresponds to a 3-second slot. + +### Slot assignment + +Given a wall-clock time `T`: + +``` +slot_num = (T - genesis_time) / CHAIN_BLOCK_INTERVAL +scheduled = schedule[slot_num % num_scheduled_validators] +``` + +Block timestamps are always the **deterministic slot time**, never the raw clock: +``` +block_time = genesis_time + slot_num × 3s +``` + +### Missed slots + +When a validator misses their slot, `update_global_dynamic_data()` increments `current_aslot` and marks the slot as missed in the participation bitmask. Other validators do not fill in for a missed slot — the 3-second rhythm continues on the next slot regardless. + +--- + +## Participation Rate + +The participation rate is: + +``` +participation = popcount(recent_slots_filled) / 128 +``` + +where `recent_slots_filled` is the 128-bit sliding window of slot outcomes. + +**Validator production is blocked when participation drops below `required-participation`** (default 33%). This prevents a node on a minority fork from continuing to produce blocks when most of the network is unreachable. + +Config: +```ini +required-participation = 33 # minimum %, 0–99 +``` + +--- + +## Last Irreversible Block (LIB) + +A block is irreversible once more than 2/3 of active validators have built on top of it. The chain tracks this in `last_irreversible_block_num`. + +``` +irreversibility_threshold = ceil(num_scheduled_validators * 2 / 3) +``` + +With 21 validators: `ceil(21 × 2/3) = 14` confirmations. Once 14 validators have produced blocks descended from block N, block N becomes LIB. + +LIB advancement is **skipped during emergency consensus mode** (see [Emergency Consensus](./emergency-consensus.md)). + +--- + +## Hardfork Voting + +Validators participate in hardfork activation by setting their reported `hardfork_version_vote` via `validator_update_operation`. Hardfork N activates when: + +1. A supermajority (>80%) of the current validator set has signalled support. +2. The hardfork's scheduled activation timestamp has passed. + +Both conditions must be true. This allows network operators to block unwanted hardforks by withholding votes even after the scheduled time. + +--- + +## Minority Fork Guard + +If the last 21 consecutive blocks are all produced by validators that belong to this node's configured validator set, the validator plugin concludes the node is isolated and automatically rolls back to LIB. This is the **minority fork guard**. + +The check is bypassed when: +- `enable-stale-production = true` (development/testnet) +- Emergency consensus mode is active + +--- + +## Emergency Consensus Mode + +If no block has been produced for `CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC` (default 1 hour), the chain switches to **emergency mode**: + +- All 21 validator slots are assigned to `CHAIN_EMERGENCY_WITNESS_ACCOUNT` ("committee"). +- The emergency validator signs blocks using `CHAIN_EMERGENCY_WITNESS_PUBLIC_KEY`. +- All validator penalties are reset; shut-down validators are re-enabled. +- LIB advancement is paused during the emergency period. +- Emergency mode exits after `CHAIN_EMERGENCY_EXIT_NORMAL_BLOCKS` (21) consecutive normal blocks advance LIB past the emergency start block. + +See [Emergency Consensus](./emergency-consensus.md) for full details. + +--- + +## HF12: Vote-Weighted Fork Comparison + +Starting at hardfork 12, when two competing forks exist at the same block height, the chain uses **vote-weighted fork comparison** instead of simple longest-chain: + +1. For each fork branch, sum the total SHARES delegated to each unique validator that produced a block on that branch. +2. Apply a **+10% bonus** to the longer chain to break ties in favor of production continuity. +3. The fork with higher total vote weight wins. +4. If still tied, the current fork is maintained until the two-level fork collision resolver's 21-deferral timeout fires. + +See [Fork Resolution](./fork-resolution.md) for the full fork collision algorithm. + +--- + +## Configuration Summary + +| Setting | Default | Description | +|---------|---------|-------------| +| `required-participation` | `33` (33%) | Minimum participation to produce blocks | +| `enable-stale-production` | `false` | Bypass participation check (testnet only) | +| `emergency-private-key` | — | Optional emergency consensus signing key | +| Active validators | 21 | Hardcoded in `CHAIN_MAX_WITNESSES` | +| Block interval | 3 s | `CHAIN_BLOCK_INTERVAL` | +| LIB threshold | ⌈21 × 2/3⌉ = 14 | Blocks confirming irreversibility | +| Emergency timeout | 3600 s | `CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC` | + +--- + +## Key Source Locations + +| Component | File | +|-----------|------| +| Validator schedule construction | `libraries/chain/database.cpp` — `update_witness_schedule()` | +| Participation bitmask update | `libraries/chain/database.cpp` — `update_global_dynamic_data()` | +| LIB advancement | `libraries/chain/database.cpp` — `update_last_irreversible_block()` | +| Hardfork voting | `libraries/chain/database.cpp` — `process_hardforks()` | +| Production loop | `plugins/validator/validator.cpp` — `maybe_produce_block()` | +| Emergency mode activation | `libraries/chain/database.cpp` — `check_emergency_consensus()` | +| HF12 fork comparison | `libraries/chain/database.cpp` — `compare_fork_branches()` | + +--- + +See also: [Block Processing](./block-processing.md), [Fork Resolution](./fork-resolution.md), [Emergency Consensus](./emergency-consensus.md), [Validator Node](../node/validator-node.md). diff --git a/docs/consensus/fork-resolution.md b/docs/consensus/fork-resolution.md new file mode 100644 index 0000000000..779710fbd7 --- /dev/null +++ b/docs/consensus/fork-resolution.md @@ -0,0 +1,180 @@ +# Fork Resolution + +This page covers how VIZ Ledger detects, selects, and resolves competing forks — from the basic fork database to the HF12 vote-weighted collision resolver. + +--- + +## Fork Database + +The **fork database** (`fork_database`) is an in-memory tree of candidate chain tips. Every block received from P2P is inserted here before being applied to the chain state. + +Key operations: +- `push_block(b)` — link `b` to its parent; if the parent is unknown, cache in `_unlinked_index` +- `_push_next(item)` — when a parent arrives, iteratively link all cached children +- `fetch_branch_from(a, b)` — walk both branches back to their common ancestor +- `set_max_size(n)` — prune oldest blocks from both linked and unlinked indices + +### Duplicate detection + +Before inserting, the fork DB checks whether a block with the same ID already exists. If so, it is silently ignored. This prevents redundant processing of blocks re-broadcast during P2P sync. + +### Unlinked index + +Blocks whose parent is not yet in the fork DB are stored in `_unlinked_index`. When the parent later arrives: +1. `_push_block(parent)` links the parent. +2. `_push_next(parent)` iterates `_unlinked_index` for children of the parent. +3. Children are moved to `_index` and recursively linked. +4. The fork DB head may jump multiple blocks in one call. + +The gap threshold (100 blocks) prevents memory bloat: blocks more than 100 ahead of the database head with an unknown parent are silently rejected before reaching the fork DB. + +--- + +## Fork Selection: Longest-Chain Rule + +After inserting a block, the fork DB returns the new head. If the new head is higher than the database head and diverges from it, a **fork switch** is attempted. + +**Pre-HF12:** Simple longest-chain rule — the fork with the highest block number wins. + +--- + +## HF12: Vote-Weighted Fork Comparison + +Starting at hardfork 12, when two competing forks exist at the same block height, `compare_fork_branches()` is used instead of simple longest-chain: + +### Algorithm + +1. **Fetch branches** via `fetch_branch_from(fork_a_tip, fork_b_tip)` to the common ancestor. +2. **Sum vote weight per validator** for each branch — only unique validator accounts are counted once per branch. The emergency validator account (`"committee"`) is excluded. +3. **Apply +10% bonus** to the longer chain. +4. **Return**: `+1` if branch A is stronger, `-1` if B is stronger, `0` if tied. + +### Fork collision handling + +When `compare_fork_branches()` is called from the validator plugin: +- If one fork is clearly stronger → produce on that fork. +- If tied or inconclusive → defer (increment `fork_collision_defer_count_`). +- After **21 consecutive deferrals** (one full validator round) → timeout: call `remove_blocks_by_number(height)` to clear stale competing blocks, then produce on the canonical chain. + +| Condition | `peer_needs_sync_items_from_us` flag | +|-----------|-------------------------------------| +| Reply empty | `false` — our chain is empty | +| Reply = 1 item in synopsis | `false` — peer is caught up | +| Reply >1 item, `remaining == 0` | `false` — peer nearly caught up (switch to inventory) | +| Reply >1 item, `remaining > 0` | `true` — peer is far behind (stay in sync mode) | + +--- + +## Fork Switch Process + +When the node switches to a better fork: + +``` +1. fetch_branch_from(new_head, current_head) + → branches.first = [new_tip, ..., common_ancestor] + → branches.second = [current_tip, ..., common_ancestor] + +2. Linear extension check: + branches.second.size() == 1 AND common_ancestor == head + → Skip pop loop; apply branches.first directly. + +3. Actual fork switch: + for each block in branches.second (reverse): + FORK-SWITCH-POP: pop_block() ← save txs to _popped_tx + for each block in branches.first (reverse): + FORK-SWITCH-APPLY: apply_block() + +4. On exception: + for each block applied above: + FORK-RECOVER-POP: pop_block() ← undo partial apply + Invalidate the failed fork. + Re-raise the exception. +``` + +The **linear extension** distinction is critical in DLT mode where LIB == head: a pop loop would be infinite because undo sessions are already committed. + +--- + +## Irreversible Block Determination + +After each block application, `update_last_irreversible_block()` advances the Last Irreversible Block (LIB): + +1. Collect the `last_supported_block_num` for each of the 21 scheduled validators. +2. Sort and take the `⌈21 × 25%⌉ = 5`th from the bottom (i.e., the value where 75% of validators are at or above it). +3. The resulting block number becomes the new LIB. + +Once a block is LIB, it is written to `block_log` (or `dlt_block_log` in DLT mode) and its undo session is committed. + +**LIB is capped at HEAD − 1 during emergency consensus mode** to prevent committing the undo session of the block being currently applied. + +--- + +## Stale Fork Pruning + +Two mechanisms prevent stale data from accumulating: + +1. **`remove_blocks_by_number(num)`** — removes all blocks at a specific height. Called by the fork collision resolver after the 21-deferral timeout. +2. **`set_max_size(n)`** — prunes oldest blocks from both `_index` and `_unlinked_index` when the fork DB exceeds `n` entries. + +--- + +## Minority Fork Guard + +Before each block production, the validator plugin checks the last 21 blocks in the fork DB: + +- If all 21 were produced by this node's own configured validators → the node is isolated on a minority fork. +- Action (`enable-stale-production = false`): call `resync_from_lib()` — pop to LIB, reset fork DB, re-initiate P2P sync, reconnect seed nodes. +- Action (`enable-stale-production = true`): log warning, continue producing. +- Emergency consensus active → skip check (all slots being "ours" is expected for an emergency master). + +--- + +## Fork Collision Metrics (HF12) + +HF12 added two fields to `dynamic_global_property_object` for on-chain monitoring: + +| Field | Type | Description | +|-------|------|-------------| +| `fork_collision_count` | `uint32_t` | Cumulative count of fork collisions since genesis | +| `last_fork_collision_block_num` | `uint32_t` | Block number of the most recent collision | + +Read via `get_dynamic_global_properties`. + +--- + +## Fork DB Diagnostics + +The fork DB exposes O(1) accessors for monitoring: + +| Method | Returns | +|--------|---------| +| `linked_size()` | Number of blocks in the linked index | +| `unlinked_size()` | Number of blocks in the unlinked index | +| `linked_min_block_num()` | Lowest block number in linked index | +| `linked_max_block_num()` | Highest block number in linked index | +| `unlinked_min_block_num()` | Lowest block number in unlinked index | +| `unlinked_max_block_num()` | Highest block number in unlinked index | + +The P2P stats task logs these every 5 minutes: + +``` +Block storage | dlt_log: [79174319..79274318] | dlt_resizes: 412 | fork_db: linked=18 unlinked=0 +``` + +A growing `unlinked_size` that does not drain suggests a persistent gap in the received block stream (P2P connectivity issue or node on an isolated fork). + +--- + +## Troubleshooting + +| Symptom | Diagnosis | +|---------|-----------| +| `fork_collision` production result | Competing block at target height; wait for 21-deferral timeout or vote-weight resolution | +| `minority_fork` production result | Node is isolated; check P2P peers and seed connectivity | +| `unlinked_size` growing indefinitely | Parent blocks not arriving; check P2P connectivity | +| Repeated fork switches in logs | Network partition between two validator subsets; investigate connectivity between them | +| Head not advancing in DLT mode | Linear extension vs fork switch confusion; check `FORK-SWITCH-POP` logs | + +--- + +See also: [Fair-DPOS](./fair-dpos.md), [Block Processing](./block-processing.md), [Emergency Consensus](./emergency-consensus.md). diff --git a/docs/consensus/hardforks.md b/docs/consensus/hardforks.md new file mode 100644 index 0000000000..c03097432c --- /dev/null +++ b/docs/consensus/hardforks.md @@ -0,0 +1,171 @@ +# Hardforks + +A hardfork is a network upgrade that changes consensus rules. All nodes must upgrade before the scheduled activation timestamp; nodes running old software will diverge from the network after activation. + +--- + +## Activation Mechanism + +Each hardfork has: +- A **unique number** (N). +- A **Unix timestamp** — the earliest wall-clock time at which the hardfork can activate. +- A **validator vote supermajority** — >80% of the current validator set must signal the new hardfork version via `validator_update_operation`. + +Both conditions must be satisfied simultaneously. Validators can block an unwanted hardfork by withholding their version vote even after the scheduled timestamp. + +--- + +## Hardfork History + +| # | Version | Key changes | +|---|---------|-------------| +| 1–10 | 1.x – 2.x | Foundation, social graph, energy system, committee, subscriptions | +| 11 | 3.0.0 | — | +| 12 | 3.1.0 | Fork collision metrics, vote-weighted fork comparison, emergency consensus mode, NTP improvements | +| 13 | 3.2.0 | Validator reward sharing with vote-proportional distribution | + +--- + +## HF12 Summary + +HF12 (version 3.1.0) introduced: + +1. **Fork collision counter** — `fork_collision_count` and `last_fork_collision_block_num` added to `dynamic_global_property_object`. Observable via `get_dynamic_global_properties`. +2. **Vote-weighted fork comparison** (`compare_fork_branches()`) — fork selection uses total delegated SHARES per validator branch + 10% bonus for longer chain. +3. **Emergency consensus mode** — activates automatically after 1 hour with no blocks; "committee" account takes all 21 slots. See [Emergency Consensus](./emergency-consensus.md). +4. **Minority fork auto-resync** — validator plugin detects node isolation (21 consecutive own-blocks) and rolls back to LIB. +5. **NTP improvements** — dedicated NTP client with configurable servers, interval, and round-trip threshold. + +--- + +## HF13 Summary + +HF13 (version 3.2.0) introduced: + +**Validator reward sharing**: part of each block's validator reward is redistributed proportionally to the accounts that voted for that validator (by their SHARES vote weight). + +- New field on `witness_object`: `reward_percent` — fraction of block reward shared with voters (0–10000 basis points). +- New virtual operation: `validator_reward_virtual_operation` — fired once per reward distribution. +- Set via `validator_update_operation`. + +--- + +## Implementing a New Hardfork + +### Step 1: Create hardfork definition file + +`libraries/chain/hardfork.d/N.hf`: + +```cpp +#ifndef CHAIN_HARDFORK_N +#define CHAIN_HARDFORK_N N +#define CHAIN_HARDFORK_N_TIME 1234567890 // Unix timestamp — must be in the future +#define CHAIN_HARDFORK_N_VERSION hardfork_version(3, N, 0) +#endif +``` + +### Step 2: Bump constants + +`libraries/chain/hardfork.d/0-preamble.hf`: +```cpp +#define CHAIN_NUM_HARDFORKS N +``` + +`libraries/protocol/include/graphene/protocol/config.hpp` (if protocol-visible): +```cpp +#define CHAIN_VERSION (version(3, N, 0)) +``` + +### Step 3: Schema version + +If any chainbase object layout changes (new fields, removed fields, resized types), **increment `CHAIN_SCHEMA_VERSION`** in `config.hpp`: + +```cpp +#define CHAIN_SCHEMA_VERSION uint32_t(N) +``` + +The chain plugin checks this at startup. A mismatch wipes `shared_memory.bin` before opening, preventing corrupt reads from old layouts. + +New fields should always have **zero-value defaults** to avoid migration code: +```cpp +uint16_t my_new_field = 0; +``` + +### Step 4: Wire into database.cpp + +`init_hardforks()`: +```cpp +FC_ASSERT(CHAIN_HARDFORK_N == N); +_hardfork_times[N] = fc::time_point_sec(CHAIN_HARDFORK_N_TIME); +_hardfork_versions[N] = hardfork_version(CHAIN_HARDFORK_N_VERSION); +``` + +`apply_hardfork()` case: +```cpp +case CHAIN_HARDFORK_N: { + // Migration if any. Leave empty with a comment if zero defaults cover it. + break; +} +``` + +### Step 5: Operation and evaluator (if new op) + +1. Add struct to `chain_operations.hpp` with `validate()` and authority getters. +2. Add to the `static_variant` in `operations.hpp`. +3. Declare `DEFINE_EVALUATOR(my_new_op)` in `chain_evaluator.hpp`. +4. Implement `do_apply()` in a `.cpp` evaluator file — always check `ASSERT_REQ_HF(CHAIN_HARDFORK_N, ...)` first. +5. Register in `initialize_evaluators()` in `database.cpp`. + +### Step 6: Plugin updates + +| Plugin | What to update | +|--------|----------------| +| `account_history` | Add impact extractor for any new virtual operation | +| `witness_api` | Add new fields from `witness_object` to `witness_api_object` | +| `snapshot` | Add new chainbase objects to `serialize_state` / `load_snapshot` | + +--- + +## Schema Version Lifecycle + +``` +Fresh node (no existing data): + stored = 0, compiled = N → mismatch + wipe shared_memory (no-op if absent) + write schema_version = N + genesis → normal startup + +Upgrade (old binary had version M < N): + stored = M, compiled = N → mismatch + wipe shared_memory.bin + write schema_version = N + db.open() → revision mismatch exception + → auto-recovery: snapshot import + dlt_block_log replay + +Normal restart: + stored = N, compiled = N → match + db.open() proceeds normally +``` + +**Key files:** +- `config.hpp` — `CHAIN_SCHEMA_VERSION` +- `plugins/chain/plugin.cpp` — schema check and wipe logic +- `/schema_version` — plain text file with current version + +--- + +## Deployment Checklist + +- [ ] `CHAIN_NUM_HARDFORKS` incremented +- [ ] `CHAIN_VERSION` bumped (if protocol-visible) +- [ ] `CHAIN_SCHEMA_VERSION` incremented (if any chainbase object layout changed) +- [ ] Hardfork `.hf` file created with future activation timestamp +- [ ] All new fields have zero defaults; `apply_hardfork` comment explains why no migration needed +- [ ] New evaluator registered in `initialize_evaluators()` +- [ ] New virtual op registered in `account_history` plugin +- [ ] `witness_api_object` updated if `witness_object` changed +- [ ] Snapshot plugin updated if new chainbase objects added + +--- + +See also: [Fair-DPOS](./fair-dpos.md), [Emergency Consensus](./emergency-consensus.md), [Snapshots](../node/snapshot.md). diff --git a/docs/development/building.md b/docs/development/building.md new file mode 100644 index 0000000000..e80942e589 --- /dev/null +++ b/docs/development/building.md @@ -0,0 +1,196 @@ +# Building + +VIZ Ledger node uses CMake 3.16+ and requires Boost 1.71+ with the coroutine component. Supported platforms: Ubuntu 24.04+, macOS (Homebrew), Windows (MSVC or MinGW). + +--- + +## Linux (Ubuntu/Debian) + +### Step 1: Install dependencies (requires root) + +```bash +chmod +x install-deps-linux.sh +sudo ./install-deps-linux.sh +``` + +This installs: CMake, GCC/G++, Git, Make, ccache, OpenSSL, Boost 1.71 (all required components including coroutine/context), readline, and compression libraries. + +### Step 2: Build (run as regular user, not root) + +```bash +chmod +x build-linux.sh +./build-linux.sh +``` + +**Common options:** + +```bash +./build-linux.sh # Release build (default) +./build-linux.sh -l # LOW_MEMORY_NODE (validator nodes) +./build-linux.sh -n # Testnet build +./build-linux.sh -t Debug -j4 # Debug build with 4 parallel jobs +./build-linux.sh --skip-deps # Skip dependency installation +./build-linux.sh --install # Install to system after build + +# Custom dependency paths +./build-linux.sh --boost-root /opt/boost_1_74_0 --openssl-root /opt/openssl +``` + +### Fedora/RHEL + +The same scripts detect `dnf` automatically. Packages installed: `cmake`, `gcc-c++`, `git`, `ccache`, `boost-devel`, `openssl-devel`, `bzip2-devel`, `zstd-devel`. + +--- + +## macOS + +```bash +chmod +x build-mac.sh +./build-mac.sh +``` + +Requires Xcode Command Line Tools and Homebrew. The script installs: `boost`, `cmake`, `git`, `autoconf`, `automake`, `libtool`, `openssl`, `readline`. + +**Options:** + +```bash +./build-mac.sh -l # Low-memory node +./build-mac.sh -n # Testnet +./build-mac.sh --skip-deps # Skip Homebrew installs +./build-mac.sh --boost-root /opt/boost_1_74_0 +``` + +--- + +## Windows (MinGW) + +Set required environment variables, then run the batch script: + +```cmd +set BOOST_ROOT=C:\Boost +set OPENSSL_ROOT_DIR=C:\OpenSSL-Win64 +build-mingw.bat +``` + +**Optional variables:** + +| Variable | Default | Description | +|----------|---------|-------------| +| `VIZ_BUILD_TYPE` | Release | Release or Debug | +| `VIZ_LOW_MEMORY` | OFF | Enable low-memory node | +| `VIZ_BUILD_TESTNET` | OFF | Testnet build | +| `VIZ_FULL_STATIC` | OFF | Fully static binary | +| `VIZ_CMAKE_EXTRA` | — | Additional CMake flags | + +**Requirements:** MinGW-w64 with C++11 and SSE4.2, CMake 3.16+, Boost 1.71+ (static, `link=static threading=multi runtime-link=shared`), OpenSSL for Windows. + +--- + +## Windows (MSVC) + +```cmd +set BOOST_ROOT=C:\Boost +set OPENSSL_ROOT_DIR=C:\OpenSSL-Win64 +build-msvc.bat +``` + +**Optional variables:** + +| Variable | Default | Description | +|----------|---------|-------------| +| `VIZ_VS_VERSION` | "Visual Studio 17 2022" | Visual Studio generator | +| `VIZ_BUILD_TYPE` | Release | Build type | +| `VIZ_LOW_MEMORY` | OFF | Low-memory node | +| `VIZ_BUILD_TESTNET` | OFF | Testnet build | + +**Requirements:** Visual Studio 2019+ with "Desktop development with C++" workload, CMake 3.16+. + +--- + +## Docker + +The repository ships Dockerfiles for multiple configurations: + +| Dockerfile | Description | +|-----------|-------------| +| `Dockerfile-production` | Full node, Release, no MongoDB | +| `Dockerfile-lowmem` | Same but with `LOW_MEMORY_NODE=ON` | +| `Dockerfile-mongo` | MongoDB plugin enabled | +| `Dockerfile-testnet` | Testnet (`BUILD_TESTNET=ON`) | + +All Dockerfiles use a two-stage build to minimize image size and use Boost 1.71 packages (`libboost-coroutine-dev`, `libboost-context-dev`). + +--- + +## CMake Options + +| Option | Default | Description | +|--------|---------|-------------| +| `BUILD_TESTNET` | OFF | Build for testnet | +| `LOW_MEMORY_NODE` | OFF | Exclude non-consensus data (reduces RAM) | +| `CHAINBASE_CHECK_LOCKING` | OFF | Enable lock checking (development only) | +| `ENABLE_MONGO_PLUGIN` | OFF | Include MongoDB plugin | +| `BUILD_SHARED_LIBRARIES` | OFF | Build shared libraries | +| `USE_PCH` | OFF | Enable precompiled headers (faster rebuilds) | + +--- + +## Advanced: `configure_build.py` + +Wraps CMake with sensible defaults and cross-compilation support: + +```bash +# Release build +python3 programs/build_helpers/configure_build.py --release --src ../.. + +# Debug with low-memory +python3 programs/build_helpers/configure_build.py --debug --low-memory + +# Cross-compile for Windows with MinGW +python3 programs/build_helpers/configure_build.py --win --release + +# Custom dependency paths +python3 programs/build_helpers/configure_build.py \ + --boost-dir /opt/boost_1_74_0 \ + --openssl-dir /opt/openssl \ + --release +``` + +--- + +## New Plugin Scaffolding + +```bash +python3 programs/util/newplugin.py graphene myplugin +``` + +Generates: `CMakeLists.txt`, plugin header/implementation, API header/implementation under `libraries/plugins/myplugin/`. + +--- + +## Build Targets + +| Binary | Description | +|--------|-------------| +| `vizd` | Main node daemon | +| `cli_wallet` | Command-line wallet | +| `js_operation_serializer` | JavaScript operation serializer | +| `size_checker` | Size analysis utility | + +--- + +## Troubleshooting + +**Boost version below 1.71:** Install Boost 1.71+ from your package manager (Ubuntu 24.04 ships 1.74). On macOS, `brew install boost` provides a recent version. On Windows, build from source with the coroutine component. + +**`Do not run this script as root` error:** Use `sudo ./install-deps-linux.sh` for dependencies, then run `./build-linux.sh` as a regular user. + +**Coroutine component missing:** Ensure `libboost-coroutine-dev` and `libboost-context-dev` are installed on Ubuntu/Debian. + +**macOS OpenSSL not found:** Set `OPENSSL_ROOT_DIR` manually: `export OPENSSL_ROOT_DIR=$(brew --prefix openssl)`. + +**Windows MinGW missing variables:** Both `BOOST_ROOT` and `OPENSSL_ROOT_DIR` must be set before running `build-mingw.bat`. + +--- + +See also: [Plugin Development](./plugin-development.md), [Testing](./testing.md), [Plugins Overview](../plugins/overview.md). diff --git a/docs/development/debugging.md b/docs/development/debugging.md new file mode 100644 index 0000000000..e0b919ee2b --- /dev/null +++ b/docs/development/debugging.md @@ -0,0 +1,186 @@ +# Debugging + +VIZ Ledger node provides several debugging tools: the `debug_node` plugin for state manipulation and replay, transaction signing utilities for cryptographic diagnosis, and P2P plugin logging with ANSI color codes for network analysis. + +--- + +## Debug Node Plugin + +The `debug_node` plugin exposes a JSON-RPC API for: +- Replaying blocks from a block log or JSON array +- Generating blocks locally with a configurable signing key +- Popping blocks to roll back state +- Inspecting validator schedule and hardfork state +- Applying database edits at specific block heights + +**Enable with restricted RPC (localhost only):** + +```ini +plugin = debug_node +webserver-http-endpoint = 127.0.0.1:8090 +``` + +### API Reference + +| Method | Description | +|--------|-------------| +| `debug_push_blocks(src, count)` | Load blocks from a block log directory | +| `debug_push_json_blocks(file, count, skip)` | Load blocks from a JSON array file | +| `debug_generate_blocks(key, count, skip, miss, edit)` | Produce blocks with the given signing key | +| `debug_generate_blocks_until(key, time, sparse, skip)` | Advance chain to a target time | +| `debug_pop_block()` | Remove the head block, returning it | +| `debug_get_witness_schedule()` | Retrieve the current validator schedule object | +| `debug_set_hardfork(id)` | Set hardfork state programmatically | +| `debug_has_hardfork(id)` | Check whether a hardfork has been applied | + +### Usage Patterns + +```json +// Replay 100 blocks from a block log +{"method":"debug_node.debug_push_blocks","params":["/data/blockchain",100]} + +// Generate 10 blocks with a signing key (skip validation) +{"method":"debug_node.debug_generate_blocks","params":["5K...",10,2,0,{}]} + +// Inspect validator schedule +{"method":"debug_node.debug_get_witness_schedule","params":[]} + +// Activate hardfork 9 for testing +{"method":"debug_node.debug_set_hardfork","params":[9]} +``` + +**Block generation** temporarily modifies the active validator's signing key to accept self-signed blocks, then restores the original key. + +**Database update hooks** allow injecting state changes at specific block heights: + +```cpp +// From plugin code +debug_plugin.debug_update([&](database& db) { + // Modify db state here +}, skip_flags); +``` + +--- + +## Transaction Signing Utilities + +### sign_transaction + +Reads JSON signing requests from stdin (one per line), computes the transaction digest and signature, and prints results: + +```bash +echo '{"ref_block_num":1234,"ref_block_prefix":5678,...}' | ./sign_transaction +``` + +Output includes `digest`, `sig_digest`, `key` (public key), and `signature`. + +**Diagnosing signing failures:** +1. Compute `sig_digest` with `sign_transaction`. +2. Compare against the wallet's `sig_digest(chain_id)`. +3. Verify the WIF key corresponds to the claimed signing key. + +### sign_digest + +Signs a raw SHA-256 digest with a WIF key: + +```bash +echo '{"digest":"abc123...","wif":"5K..."}' | ./sign_digest +``` + +Useful for confirming chain ID correctness and isolating signature malleability issues. + +--- + +## Network Debugging (P2P Logs) + +The P2P plugin uses ANSI color codes for visual distinction in console output: + +| Color | ANSI Code | Content | +|-------|-----------|---------| +| White | `\033[97m` | Block processing: transaction count, latency | +| Cyan | `\033[96m` | Peer statistics: connection count, bytes, RTT | +| Gray | `\033[90m` | Detailed debug context: DLT mode, sync state | +| Orange | — | Connection warnings and termination notices | +| Red | — | Critical connection termination events | + +**Reading P2P logs:** +- **White**: Spot block processing activity and transaction volume at a glance. +- **Cyan**: Monitor peer count and connection health in real-time. +- **Gray**: Investigate DLT mode and sync protocol details. +- **Orange/Red**: Identify connection failures and peer blocking events. + +### Network-specific logger + +Sync negotiation messages go through the `"sync"` logger. Enable in `config.ini`: + +```ini +[logger.sync] +level = info +appenders = stderr +``` + +P2P node messages use the `"p2p"` logger (not the default logger): + +```ini +[logger.p2p] +level = info +appenders = stderr +``` + +--- + +## Debug Configuration + +`share/vizd/config/config_debug.ini` is a configuration template tuned for debugging: + +- Larger shared memory sizes and growth thresholds for long replays. +- Single write thread for deterministic block generation. +- Tuned read/write lock retry counts. + +Key settings: + +```ini +shared-file-size = 12G +shared-file-full-threshold = 97 +shared-file-scale-rate = 3 +chainbase-check-locking = 0 +``` + +--- + +## Debugging Workflows + +### Transaction validation failure + +1. Run `sign_transaction` on the failing transaction JSON. +2. Compare the computed `sig_digest` against the wallet-produced value. +3. Verify the WIF key corresponds to the account's authority. +4. Replay blocks containing the transaction with `debug_push_blocks` and observe logs. + +### Consensus stall + +1. Use `debug_generate_blocks` to advance the chain deterministically. +2. Inspect the validator schedule with `debug_get_witness_schedule`. +3. If needed, set hardfork state with `debug_set_hardfork` to test activation logic. + +### Network connectivity problems + +1. Check **cyan logs** for peer count and connection health. +2. Check **white logs** for block ingestion latency and gaps. +3. Check **gray logs** for DLT sync state during snapshot sync. +4. Check **orange/red logs** for termination events and peer bans. +5. Correlate block push exceptions with specific block numbers in the logs. + +### Integration test acceleration + +Replay blocks from a JSON log with skip flags to bypass expensive validation: + +```json +{"method":"debug_node.debug_push_json_blocks","params":["/tmp/blocks.json",100,2]} +``` + +Skip flags: `1` = skip undo session, `2` = skip witness signature, `4` = skip merkle check. + +--- + +See also: [Building](./building.md), [Testing](./testing.md), [P2P Overview](../p2p/overview.md), [Plugins Overview](../plugins/overview.md). diff --git a/docs/development/plugin-development.md b/docs/development/plugin-development.md new file mode 100644 index 0000000000..ffb2965679 --- /dev/null +++ b/docs/development/plugin-development.md @@ -0,0 +1,252 @@ +# Plugin Development + +VIZ Ledger's plugin system is built on AppBase. Every plugin follows the same lifecycle, registers its API with the JSON-RPC layer, and subscribes to chain database signals. + +--- + +## Plugin Structure + +A plugin consists of: + +- **Header** (`include/graphene/plugins//plugin.hpp`) — declares the plugin class and its API. +- **Implementation** (`plugin.cpp`) — lifecycle hooks, signal subscriptions, API method bodies. +- **CMakeLists.txt** — declares the target and links dependencies. + +### Scaffolding a new plugin + +```bash +python3 programs/util/newplugin.py graphene myplugin +``` + +This generates boilerplate under `plugins/myplugin/`: +- `CMakeLists.txt` +- `include/graphene/plugins/myplugin/plugin.hpp` +- `plugin.cpp` +- API header and implementation files + +--- + +## Lifecycle + +``` +plugin_initialize(options) + └── Register API factory + └── Parse options + +plugin_startup() + └── Connect to database signals + └── Start any background threads + +plugin_shutdown() + └── Disconnect signals + └── Stop background threads +``` + +All three methods are called by AppBase in dependency order. Never call `plugin_startup()` directly. + +--- + +## JSON-RPC API Registration + +Plugins register methods with the `json_rpc` plugin using a macro-driven visitor: + +```cpp +// In plugin.hpp — declare API +DECLARE_API( + (get_account_history) + (get_ops_in_block) +) + +// In plugin.cpp — startup +plugin_startup() { + auto& json_rpc = appbase::app().get_plugin(); + json_rpc.add_api( + MAKE_API(this, get_account_history) + MAKE_API(this, get_ops_in_block) + ); +} +``` + +Each API method accepts a single argument struct and returns a single result struct. Void methods use a dedicated empty result type. + +**Method naming:** The JSON-RPC method name is `.`. For example, `account_history.get_account_history`. + +--- + +## Database Signals + +The chain database emits signals that plugins subscribe to: + +| Signal | Trigger | +|--------|---------| +| `applied_block` | After a block is applied (post-state) | +| `pre_apply_operation` | Before each operation is applied | +| `on_applied_transaction` | After a transaction is applied | +| `post_apply_operation` | After each operation is applied | + +```cpp +// Connect in plugin_startup() +auto& db = appbase::app().get_plugin().db(); + +db.applied_block.connect([this](const signed_block& b) { + on_applied_block(b); +}); + +db.pre_apply_operation.connect([this](const operation_notification& note) { + on_pre_apply_operation(note); +}); +``` + +**Important:** Signal handlers run synchronously during block processing. Do not perform heavy work inside them — queue tasks to a background thread instead. + +--- + +## Database Access + +### Reading (from API methods) + +Use a weak read lock to minimize contention: + +```cpp +auto& db = appbase::app().get_plugin().db(); +// db is automatically locked for reading in API handlers +auto account = db.get_account("alice"); +``` + +### Writing (from signal handlers or evaluators) + +Only write inside signal handlers or evaluators — never from API methods. + +```cpp +// Inside an applied_block handler +db.modify(db.get_account("alice"), [](account_object& a) { + a.some_field = new_value; +}); +``` + +--- + +## Custom Database Indices + +Plugins can add their own indices to the database: + +```cpp +// In plugin_startup(), after chain is initialized +auto& db = appbase::app().get_plugin().db(); +db.add_plugin_index(); +``` + +Define the object and index in headers following the pattern of existing objects: + +```cpp +// Object definition +class my_object : public chainbase::object { + id_type id; + account_name_type account; + uint64_t some_field; +}; + +// MultiIndex container +using my_index = chainbase::shared_multi_index_container< + my_object, + indexed_by< + ordered_unique, member>, + ordered_unique, member> + > +>; +``` + +--- + +## Custom Operation Evaluators + +To handle new operation types: + +```cpp +// Define the operation in the protocol layer and register an evaluator +class my_operation_evaluator : public evaluator { +public: + void do_apply(const my_operation& op) { + // Validate and apply state changes + auto& db = this->db(); + // ... + } +}; + +// Register in database initialization +db.register_evaluator(); +``` + +Use `has_hardfork(CHAIN_HARDFORK_N)` checks to gate behavior changes for backward compatibility. + +--- + +## WebSocket Real-time Events + +To emit real-time notifications: + +```cpp +// During plugin_startup(), register a block callback with the webserver +auto& ws = appbase::app().get_plugin(); +ws.add_handler("my_stream", [this](const fc::variant& params, fc::variant& result) { + // Stream handler +}); +``` + +The webserver plugin runs its own `io_service` thread — post callbacks from any thread using `ws.post([]{...})`. + +--- + +## Dependency Declaration + +Declare dependencies in your plugin's `plugin_requires()`: + +```cpp +static std::vector plugin_requires() { + return { &appbase::app().get_plugin(), + &appbase::app().get_plugin() }; +} +``` + +AppBase resolves the initialization order automatically. + +--- + +## Performance Guidelines + +- **API methods**: Use indexed lookups, not full scans. Add plugin indices for hot access patterns. +- **Signal handlers**: Return quickly. Queue heavy processing to a dedicated `fc::thread`. +- **Caching**: Cache hot-path results in memory; invalidate on `applied_block`. +- **Pagination**: Always paginate large result sets rather than returning unbounded collections. + +--- + +## Testing Plugins + +Use the `debug_node` plugin to simulate chain conditions: + +```json +{"method":"debug_node.debug_generate_blocks","params":["5K...",10,0,0,{}]} +``` + +Write unit tests using Boost.Test and the existing test harness. Add tests to the appropriate category suite (`operation_tests`, `block_tests`, etc.). + +For integration tests, load your plugin alongside the chain and replay a known block sequence with `debug_push_blocks`. + +--- + +## Deployment + +Enable plugins in `config.ini`: + +```ini +plugin = myplugin +``` + +Some plugins require a full reindex when enabled on an existing chain (especially those that track historical operations). Document this requirement clearly. + +For external (third-party) plugins, place them in `plugins/external/` — CMake discovers them automatically. + +--- + +See also: [Plugins Overview](../plugins/overview.md), [Database API](../plugins/database-api.md), [Building](./building.md), [Debugging](./debugging.md). diff --git a/docs/development/testing.md b/docs/development/testing.md new file mode 100644 index 0000000000..99e741161b --- /dev/null +++ b/docs/development/testing.md @@ -0,0 +1,159 @@ +# Testing + +VIZ Ledger uses Boost.Test for unit tests and provides utility programs for integration-level testing. + +--- + +## Unit Tests + +The unit test binary is built via CMake as part of the `libraries/chain` target. + +### Test Categories + +| Suite | Description | +|-------|-------------| +| `basic_tests` | Core functionality validation | +| `block_tests` | Blockchain-specific logic | +| `live_tests` | Past hardfork scenario validation | +| `operation_tests` | Operation validation | +| `operation_time_tests` | Time-dependent operations (vesting withdrawals, etc.) | +| `serialization_tests` | Serialization roundtrip checks | + +### Running Tests + +```bash +# Run all tests +./tests/chain_test + +# Filter by suite +./tests/chain_test --run_test=basic_tests + +# Filter by specific test case +./tests/chain_test --run_test=basic_tests/my_test_case + +# Adjust verbosity +./tests/chain_test --log_level=all --report_level=detailed +``` + +### Boost.Test Runtime Options + +| Option | Values | Description | +|--------|--------|-------------| +| `--log_level` | all, success, test_suite, message, warning, error, cpp_exception, system_error, fatal_error, nothing | Log verbosity | +| `--report_level` | no, confirm, short, detailed | Report detail level | +| `--run_test` | `` or `/` | Filter which tests run | + +--- + +## Code Coverage + +Enable coverage in a Debug build: + +```bash +cmake -DCMAKE_BUILD_TYPE=Debug -DCOVERAGE=ON .. +make chain_test + +# Capture baseline +lcov --capture --initial --directory . --output-file base.info + +# Run tests +./tests/chain_test + +# Capture test tracefile +lcov --capture --directory . --output-file test.info + +# Merge and clean +lcov --add-tracefile base.info --add-tracefile test.info --output-file merged.info +lcov --remove merged.info '/usr/*' '*/tests/*' --output-file coverage.info + +# Generate HTML report +genhtml coverage.info --output-directory coverage_report +``` + +--- + +## Integration Utilities + +### Block Log Test Utility + +The `test_block_log` utility exercises block storage and retrieval: + +```bash +# Built at programs/util/test_block_log +./test_block_log /tmp/test_block_log_dir +``` + +Opens a block log, appends signed blocks, flushes, and reads them back. Useful for validating block storage logic. + +### Transaction Signing Utilities + +```bash +# Sign a transaction (JSON input per line) +echo '{"ref_block_num":...}' | ./sign_transaction + +# Sign a raw digest +echo '{"digest":"...","wif":"5K..."}' | ./sign_digest +``` + +Both utilities print computed `digest`, `sig_digest`, signing key, and signature. Useful for diagnosing signing failures by comparing sig_digest against wallet-produced signatures. + +--- + +## Test API Plugin + +The `test_api` plugin exposes two JSON-RPC APIs (`test_api_a` and `test_api_b`) for integration tests. It is registered in `programs/vizd/main.cpp` and loaded by the node process. + +--- + +## Testnet Environment + +For isolated testing, use the testnet configuration: + +```bash +# Start a testnet node +vizd --config share/vizd/config/config_testnet.ini + +# Or build a testnet Docker image +docker build -f share/vizd/docker/Dockerfile-testnet -t viz-testnet . +``` + +A `share/vizd/snapshot-testnet.json` snapshot is available for quick testnet initialization. + +--- + +## Continuous Integration + +The CI matrix builds Docker images for multiple variants: + +| Variant | Dockerfile | +|---------|-----------| +| Standard | `Dockerfile-production` | +| Low-memory | `Dockerfile-lowmem` | +| MongoDB | `Dockerfile-mongo` | +| Testnet | `Dockerfile-testnet` | + +Builds trigger per branch and tag, with artifact publishing when credentials are configured. + +--- + +## Writing New Tests + +Add new test suites to the existing test target using Boost.Test macros: + +```cpp +#include + +BOOST_AUTO_TEST_SUITE(my_feature_tests) + +BOOST_AUTO_TEST_CASE(basic_case) { + BOOST_CHECK_EQUAL(1 + 1, 2); +} + +BOOST_AUTO_TEST_SUITE_END() +``` + +Group tests into the appropriate category suite. Prefer integration tests that hit real chain state over mocked tests to catch divergences between mock and production behavior. + +--- + +See also: [Building](./building.md), [Debugging](./debugging.md), [Plugin Development](./plugin-development.md). diff --git a/docs/governance/chain-properties.md b/docs/governance/chain-properties.md new file mode 100644 index 0000000000..6549b119b7 --- /dev/null +++ b/docs/governance/chain-properties.md @@ -0,0 +1,158 @@ +# Chain Properties + +Chain properties are the network's governable parameters: fees, block size, inflation rates, penalty rules, and more. There is no central authority that sets them — every active validator publishes their preferred values and the blockchain applies the **median** across all active validators. + +--- + +## How It Works + +### 1. Validators Publish Preferences + +Each validator submits their preferred parameters via `versioned_chain_properties_update_operation`: + +```json +[46, { + "owner": "alice", + "props": [3, { + "account_creation_fee": "1.000 VIZ", + "maximum_block_size": 131072, + ... + }] +}] +``` + +The `[3, {...}]` indicates version 3 (`chain_properties_hf9`, the current format). + +### 2. Median Calculation + +On every validator schedule update, the blockchain calls `update_median_witness_props()`. For **each property independently**, it: +1. Collects values from every active validator. +2. Sorts them. +3. Picks the **median** (index `active.size() / 2`). + +``` +Example — 5 validators vote account_creation_fee: + 0.5, 1.0, 1.0, 2.0, 5.0 VIZ + ↑ + median = 1.0 VIZ +``` + +The median resists extremes: a single validator cannot cause a sudden large shift; a majority must agree to move any parameter significantly. + +### 3. Application + +The resulting `median_props` object is stored in the `witness_schedule_object` and enforced across all block processing. + +--- + +## All Governable Properties + +### Account and Delegation + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `account_creation_fee` | asset (VIZ) | 1.000 VIZ | Minimum fee to create a new account | +| `create_account_delegation_ratio` | uint32 | 10 | Required delegation = ratio × fee | +| `create_account_delegation_time` | uint32 (s) | 2592000 (30d) | How long creation delegation is locked | +| `min_delegation` | asset (VIZ) | 1.000 VIZ | Minimum amount for any SHARES delegation | + +### Block Size and Bandwidth + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `maximum_block_size` | uint32 (bytes) | 131072 | Maximum block size; controls throughput | +| `bandwidth_reserve_percent` | uint16 (bp) | 1000 (10%) | Extra bandwidth for small accounts | +| `bandwidth_reserve_below` | asset (SHARES) | 500.000000 | Threshold to qualify for bandwidth reserve | +| `data_operations_cost_additional_bandwidth` | uint32 (%) | 0 | Extra bandwidth multiplier for data operations (custom_operation) | + +### Inflation and Economics + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `inflation_witness_percent` | uint16 (bp) | 2000 (20%) | Validator share of block inflation | +| `inflation_ratio_committee_vs_reward_fund` | uint16 (bp) | 5000 (50%) | Split of remaining inflation: committee fund vs reward fund | +| `inflation_recalc_period` | uint32 (blocks) | 806400 (~28d) | How often inflation is recalculated | + +Inflation flow: `block_reward × inflation_witness_percent` → validator. Remainder split: `inflation_ratio_committee_vs_reward_fund` → committee fund; the rest → award reward fund. + +### Reward System + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `min_curation_percent` | uint16 (bp) | 500 (5%) | Minimum curation reward share from content payouts | +| `max_curation_percent` | uint16 (bp) | 500 (5%) | Maximum curation reward share | +| `vote_accounting_min_rshares` | uint32 | 5000000 | Minimum rshares for an award to produce non-zero reward | +| `flag_energy_additional_cost` | uint16 (bp) | 0 | Extra energy cost for downvotes/flags | + +### Validator Accountability + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `witness_miss_penalty_percent` | uint16 (bp) | 100 (1%) | Vote weight reduction on missed block | +| `witness_miss_penalty_duration` | uint32 (s) | 86400 (1d) | How long the miss penalty lasts | + +### Fees + +All fees go to the committee fund (DAO treasury). + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `committee_create_request_fee` | asset (VIZ) | 100.000 VIZ | Fee to create a committee funding request | +| `create_paid_subscription_fee` | asset (VIZ) | 100.000 VIZ | Fee to create a paid subscription | +| `account_on_sale_fee` | asset (VIZ) | 10.000 VIZ | Fee to list an account for sale | +| `subaccount_on_sale_fee` | asset (VIZ) | 100.000 VIZ | Fee to list subaccount creation rights for sale | +| `witness_declaration_fee` | asset (VIZ) | 10.000 VIZ | One-time fee for validator registration | +| `create_invite_min_balance` | asset (VIZ) | 10.000 VIZ | Minimum invite balance | + +### Vesting Withdrawal + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `withdraw_intervals` | uint16 | 28 | Number of daily installments for SHARES unstaking | + +--- + +## Property Versions + +Properties were introduced in hardfork stages: + +| Version | Index | Hardfork | Fields added | +|---------|-------|----------|--------------| +| `chain_properties_init` | 0 | Genesis | account_creation_fee, maximum_block_size, delegation params, curation, bandwidth, flag cost, vote min rshares, committee threshold | +| `chain_properties_hf4` | 1 | HF4 | inflation_witness_percent, inflation_ratio_committee_vs_reward_fund, inflation_recalc_period | +| `chain_properties_hf6` | 2 | HF6 | data_operations_cost_additional_bandwidth, witness_miss_penalty_percent, witness_miss_penalty_duration | +| `chain_properties_hf9` | 3 | HF9 | create_invite_min_balance, committee_create_request_fee, create_paid_subscription_fee, account_on_sale_fee, subaccount_on_sale_fee, witness_declaration_fee, withdraw_intervals | + +Use version index 3 (`chain_properties_hf9`) for all new validator property submissions. + +--- + +## Governance Loop + +``` +SHARES holders → vote for validators +Validators → publish preferred property values +Blockchain → takes median of active set +Median → applied as live network rules +``` + +Changing a parameter requires a **majority of active validators** to publish the new value. The process: +1. Community discusses desired change (e.g., lower fees). +2. Validators update their published properties. +3. Users shift votes toward validators publishing the desired values. +4. Once a majority of active validators publish the new value, the median shifts. +5. New value takes effect automatically — no hardfork or governance vote needed. + +--- + +## Reading Current Properties + +```json +{ "method": "database_api.get_chain_properties", "params": [] } +``` + +Returns the current median properties in effect. See [Database API](../plugins/database-api.md#get_chain_properties). + +--- + +See also: [Validators](../protocol/operations/validators.md), [Database API](../plugins/database-api.md), [Staking and DAO](./staking-and-dao.md). diff --git a/docs/governance/committee.md b/docs/governance/committee.md new file mode 100644 index 0000000000..fcc743ddc1 --- /dev/null +++ b/docs/governance/committee.md @@ -0,0 +1,116 @@ +# Committee DAO + +The committee is VIZ Ledger's decentralized funding mechanism. Any account can create a worker request (funding proposal); all SHARES holders vote to approve or reject it using stake-weighted bipolar voting. + +The committee fund accumulates from request creation fees and from the portion of block inflation allocated to it (governed by the `inflation_ratio_committee_vs_reward_fund` chain property). + +--- + +## Operations + +| Operation | ID | Auth | Description | +|-----------|----|------|-------------| +| `committee_worker_create_request_operation` | 35 | regular of `creator` | Create a funding request | +| `committee_worker_cancel_request_operation` | 36 | regular of `creator` | Cancel own active request | +| `committee_vote_request_operation` | 37 | regular of `voter` | Vote on a request | + +See [Committee Operations](../protocol/operations/committee.md) for full field details. + +--- + +## Voting Mechanics + +Each SHARES holder can vote with a `vote_percent` in the range **−10000 to +10000** (basis points): + +- Positive vote: supports the request. +- Negative vote: opposes the request. +- Zero: removes the vote. + +**Vote weight** = `effective_vesting_shares × vote_percent / 10000`. + +Votes can be changed at any time while the request is active. + +--- + +## Approval Calculation + +When the request's `end_time` is reached: + +``` +max_rshares = SUM(voter.effective_vesting_shares for all voters) +actual_rshares = SUM(voter.effective_vesting_shares × vote_percent / 10000) +``` + +**Three conditions for approval:** + +1. **Participation:** `max_rshares ≥ total_vesting_shares × committee_request_approve_min_percent / 10000` + (if not met → rejected, insufficient participation) + +2. **Consensus:** `actual_rshares > 0` + (if negative → rejected, community opposed) + +3. **Minimum payout:** + ``` + payout = required_amount_max × (actual_rshares / max_rshares) + ``` + (if `payout < required_amount_min` → rejected) + +If all conditions pass → **approved**, payout = calculated value. + +--- + +## Request Lifecycle + +``` +[Created, status=0] + └── voting period (5–30 days) + ├── Insufficient participation → [Rejected, status=2] + ├── Net negative / below min → [Expired, status=3] + ├── Creator cancels → [Canceled, status=1] + └── Approved → [Approved, status=4] + └── payouts (every 200 blocks) + └── [Completed, status=5] +``` + +--- + +## Payout Processing + +Approved requests (status 4) receive payouts from the committee fund every 200 blocks (~10 minutes). The fund is split equally across all currently approved requests: + +``` +payment = min(committee_fund / count_approved_requests, remain_payout_amount) +``` + +Virtual operations fired during lifecycle: + +| Virtual Op | Trigger | +|-----------|---------| +| `committee_cancel_request_operation` (ID 38) | Request expires without approval | +| `committee_approve_request_operation` (ID 39) | Approval threshold reached | +| `committee_payout_request_operation` (ID 40) | Payout processed | +| `committee_pay_request_operation` (ID 41) | Worker receives payment | + +--- + +## Querying Committee State + +```json +{ "method": "committee_api.get_committee_request", "params": [42] } +{ "method": "committee_api.get_committee_request_votes", "params": [42] } +{ "method": "committee_api.get_committee_requests_list", "params": [0, 100] } +``` + +--- + +## Key Properties + +- **Bipolar**: every voter expresses support or opposition with fine-grained intensity. +- **Stake-weighted**: votes are weighted by locked tokens, making manipulation expensive. +- **Proportional payout**: stronger consensus → higher payout (up to `required_amount_max`). +- **Self-funding**: creation fees flow into the committee fund. +- **Non-custodial**: no trusted intermediary; the protocol enforces all rules automatically. + +--- + +See also: [Committee Operations](../protocol/operations/committee.md), [Staking and DAO](./staking-and-dao.md), [Chain Properties](./chain-properties.md), [Virtual Operations](../protocol/virtual-operations.md). diff --git a/docs/governance/staking-and-dao.md b/docs/governance/staking-and-dao.md new file mode 100644 index 0000000000..f10c15f3b7 --- /dev/null +++ b/docs/governance/staking-and-dao.md @@ -0,0 +1,166 @@ +# Staking and DAO Governance + +## Staking (SHARES) + +Staking converts liquid VIZ tokens into **SHARES** (vesting shares). Staked tokens are locked and cannot be transferred directly, but they grant governance power proportional to the amount staked. + +Every account has three vesting fields: + +| Field | Meaning | +|-------|---------| +| `vesting_shares` | SHARES owned by the account | +| `delegated_vesting_shares` | SHARES delegated out to others (reduces power) | +| `received_vesting_shares` | SHARES received via delegation (increases power) | + +**Effective vesting shares** — the governance power used in all weighted operations: + +``` +effective_vesting_shares = vesting_shares − delegated_vesting_shares + received_vesting_shares +``` + +--- + +## Staking Operations + +### Stake: `transfer_to_vesting_operation` (ID 3) + +Converts liquid VIZ to SHARES. Can vest into another account's balance. + +```json +[3, {"from": "alice", "to": "alice", "amount": "1000.000 VIZ"}] +``` + +### Unstake: `withdraw_vesting_operation` (ID 4) + +Initiates gradual withdrawal over `withdraw_intervals` daily installments (chain-governed, default 28 days). Set to `"0.000000 SHARES"` to cancel. + +```json +[4, {"account": "alice", "vesting_shares": "1000.000000 SHARES"}] +``` + +Virtual `fill_vesting_withdraw_operation` fires once per interval as tokens are released. + +### Withdrawal Routes: `set_withdraw_vesting_route_operation` (ID 11) + +Routes a percentage of withdrawals to another account, optionally re-vesting immediately. + +```json +[11, {"from_account": "alice", "to_account": "bob", "percent": 5000, "auto_vest": true}] +``` + +Up to 10 routes per account; total percent across all routes must not exceed 10000. + +### Delegate: `delegate_vesting_shares_operation` (ID 19) + +Transfers governance power (not ownership) to another account. Set to `"0.000000 SHARES"` to remove. + +```json +[19, {"delegator": "alice", "delegatee": "bob", "vesting_shares": "500.000000 SHARES"}] +``` + +When removing delegation, SHARES enter a 7-day return window. Virtual `return_vesting_delegation_operation` fires when they return. + +--- + +## Where SHARES Are Used + +SHARES are the universal governance token. Every meaningful action is weighted by `effective_vesting_shares`. + +### 1. Validator Voting (Fair-DPOS) + +```json +[7, {"account": "alice", "witness": "bob", "approve": true}] +``` + +Vote weight is **divided equally** among all validators the account votes for: + +``` +fair_weight = effective_vesting_shares / witnesses_voted_for +``` + +This prevents concentration — voting for 10 validators gives each 1/10 of your weight. Accounts may also set a proxy (`account_witness_proxy_operation`) to delegate all validator voting to another account. + +### 2. Committee DAO Voting + +```json +[37, {"voter": "alice", "request_id": 42, "vote_percent": 7500}] +``` + +Vote weight: `effective_vesting_shares × vote_percent / 10000`. +Range: −10000 (strong oppose) to +10000 (strong support). + +### 3. Awards (Social Reward Distribution) + +```json +[47, {"initiator": "alice", "receiver": "bob", "energy": 1000, ...}] +``` + +Reward size is proportional to: +``` +rshares = effective_vesting_shares × energy / 10000 +``` + +An account with 10× more SHARES creates 10× more reward for the same energy. + +### 4. Chain Parameter Governance + +Validators publish preferred chain parameters; the blockchain applies the median. Since validators are elected by stake-weighted votes, all chain parameters are indirectly governed by SHARES holders. + +### 5. Transaction Bandwidth + +Network bandwidth is allocated proportionally to `effective_vesting_shares`. Accounts below 500 SHARES get an additional 10% bandwidth reserve. + +### 6. Account Creation via Delegation + +New accounts can be bootstrapped by delegating SHARES to them at a 10× ratio (locked for 30 days), making account creation accessible without liquid tokens. + +--- + +## VIZ as a DAO + +| Traditional DAO | VIZ Blockchain | +|-----------------|----------------| +| DAO treasury | Committee fund + reward fund | +| Governance tokens | SHARES | +| Proposal voting | Committee worker requests | +| Board of directors | Elected validators | +| Director elections | Validator voting (Fair-DPOS) | +| Dividend distribution | Award mechanism (reward fund) | +| Bylaws / rules | Chain properties (median governance) | +| Proxy voting | `delegate_vesting_shares` + validator proxy | + +### Governance Properties + +1. **Proportional representation**: 1 SHARES = 1 unit of influence everywhere. +2. **Bipolar voting**: negative votes actively oppose, not just abstain. +3. **Continuous governance**: no fixed voting seasons — votes can be changed anytime. +4. **Skin in the game**: SHARES are locked; exiting takes 28 days. Long-term alignment. +5. **No trusted intermediaries**: all rules enforced by protocol code. +6. **Delegation without custody**: governance power can be lent and revoked at any time. + +### Governance Cycle + +``` +Stake VIZ → Get SHARES → Governance Power + ├── Vote for validators → Block production & chain parameters + ├── Vote on committee → Treasury spending + ├── Award other accounts → Distribute value from reward fund + └── Delegate to others → Amplify allies' governance power +``` + +--- + +## Key Constants + +| Constant | Value | Description | +|----------|-------|-------------| +| `CHAIN_VESTING_WITHDRAW_INTERVALS` | 28 | Daily withdrawal installments | +| `CHAIN_VESTING_WITHDRAW_INTERVAL_SECONDS` | 86400 (1d) | Time between installments | +| `CHAIN_MAX_WITHDRAW_ROUTES` | 10 | Max withdrawal routes per account | +| `CHAIN_ENERGY_REGENERATION_SECONDS` | 432000 (5d) | Full energy regeneration | +| `CHAIN_100_PERCENT` | 10000 | Basis points denominator | +| `CHAIN_MAX_ACCOUNT_WITNESS_VOTES` | 100 | Max validators per account | + +--- + +See also: [Chain Properties](./chain-properties.md), [Committee DAO](./committee.md), [Awards](../protocol/operations/awards.md), [Transfers](../protocol/operations/transfers.md). diff --git a/docs/introduction/architecture.md b/docs/introduction/architecture.md new file mode 100644 index 0000000000..c269787e22 --- /dev/null +++ b/docs/introduction/architecture.md @@ -0,0 +1,177 @@ +# Architecture Overview + +VIZ Ledger is implemented as a modular C++ daemon (`vizd`) composed of layered libraries and a plugin system. This page describes the structural layers, design patterns, and component interactions. + +--- + +## Layered Structure + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Programs │ +│ vizd (node daemon) cli_wallet (CLI wallet) │ +├─────────────────────────────────────────────────────────────────┤ +│ Plugins │ +│ chain │ validator │ p2p │ webserver │ json_rpc │ database_api │ +│ social_network │ snapshot │ committee_api │ invite_api │ ... │ +├─────────────────────────────────────────────────────────────────┤ +│ Core Libraries │ +│ libraries/chain — blockchain state machine, fork db │ +│ libraries/protocol — operation types, transactions │ +│ libraries/network — peer-to-peer messaging │ +│ libraries/api — shared API property types │ +│ libraries/wallet — transaction building helpers │ +│ libraries/time — NTP-aware time utilities │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### Libraries + +| Library | Key file | Purpose | +|---------|----------|---------| +| `libraries/chain` | `database.hpp` | Blockchain state: accounts, blocks, objects, fork DB, shared memory | +| `libraries/protocol` | `operations.hpp` | `static_variant` union of all 64+ operation types | +| `libraries/network` | `node.hpp` | P2P engine: peer connections, sync, message propagation | +| `libraries/api` | `chain_api_properties.hpp` | Shared types returned by API plugins | +| `libraries/wallet` | `wallet.hpp` | Remote node API calls, transaction construction | +| `libraries/time` | `time.hpp` | NTP synchronisation for block slot timing | + +### Plugins + +Plugins are registered with the `AppBase` framework at startup and implement lifecycle hooks (`plugin_initialize`, `plugin_startup`, `plugin_shutdown`). + +| Plugin | Role | +|--------|------| +| `chain` | Opens the database, validates and applies blocks/transactions | +| `validator` | Produces blocks on schedule (Fair-DPOS), manages NTP and watchdog | +| `p2p` | Manages peer connections, syncs blocks, propagates transactions | +| `webserver` | HTTP and WebSocket server for API access | +| `json_rpc` | Routes JSON-RPC requests to registered API plugins | +| `database_api` | Read queries: accounts, blocks, transactions, globals | +| `social_network` | Indexes and queries content, votes, replies | +| `snapshot` | Creates and restores state snapshots | +| `committee_api` | Committee worker request queries | +| `invite_api` | Invite object queries | +| `paid_subscription_api` | Paid subscription queries | +| `account_history` | Per-account operation history index | +| `account_by_key` | Lookup accounts by public key | +| `follow` | Follow/ignore relationship index | +| `tags` | Tag-based content indexing | +| `witness_api` | Validator schedule and signing key queries | +| `debug_node` | Test utilities: inject blocks, set time | + +--- + +## Design Patterns + +### Event-Driven Observer (Signals) + +The chain `Database` emits `fc::signal` events at key points. Plugins subscribe to these signals to implement indexing, history, and notifications without coupling to the core chain. + +``` +push_block() / push_transaction() + │ + ├── pre_apply_operation ──► subscriber plugins (pre-hooks) + ├── [evaluator applies state change] + ├── post_apply_operation ──► subscriber plugins (post-hooks) + └── applied_block ──► subscriber plugins (block finalized) +``` + +### Factory + Strategy (Evaluator Registry) + +Every operation type has a dedicated **evaluator** class. The `EvaluatorRegistry` maps operation type IDs to evaluator instances. When a transaction is applied: + +1. `Database` extracts the operation type tag from the `static_variant`. +2. Registry returns the registered evaluator. +3. Evaluator's `do_apply(op)` mutates the database state. + +Adding a new operation requires only registering a new evaluator — no changes to the dispatch loop. + +### Plugin-Based Architecture (AppBase) + +`vizd/main.cpp` registers all plugins with `AppBase` before calling `app().exec()`. Each plugin declares its options and dependencies; AppBase handles ordering and lifecycle. + +``` +main() ──► register_plugin() + ──► register_plugin() + ──► register_plugin() + ──► ... + ──► app().initialize(argc, argv) + ──► app().startup() + ──► app().exec() ← event loop runs until SIGINT/SIGTERM +``` + +### MVC Separation + +| Layer | Component | Responsibility | +|-------|-----------|---------------| +| Data | `libraries/chain/database` | State persistence, validation, signals | +| Control | Plugins (`chain`, `validator`, `p2p`) | Lifecycle, block/tx acceptance, coordination | +| View | API plugins (`database_api`, `social_network`, …) | Read-only query endpoints | + +--- + +## Data Flow: Incoming Block + +``` +Peer (P2P) ──► p2p_plugin::handle_block() + ──► chain_plugin::accept_block() + ──► database::push_block() + ├── validate block header, signature + ├── for each transaction: + │ ├── validate authorities + │ └── evaluator->do_apply(operation) + ├── process virtual operations (rewards, cashouts) + ├── emit applied_block signal + └── update fork DB / LIB +``` + +## Data Flow: API Request + +``` +Client (HTTP/WS) ──► webserver_plugin + ──► json_rpc_plugin::call() + ──► registry.find_api_method(api, method) + ──► database_api / social_network / ... + ──► database::get_*(...) + ──► return JSON result +``` + +--- + +## Concurrency Model + +| Concern | Approach | +|---------|---------| +| Write operations | Single write thread (optional `single-write-thread` config) | +| Read operations | Multiple concurrent readers via `chainbase` shared memory | +| P2P I/O | Dedicated `boost::asio::io_service` thread pool | +| Block production timer | Isolated `io_service` + thread in validator plugin to prevent P2P delays | +| RPC serving | Configurable thread pool (`rpc-endpoint-thread-pool-size`) | + +The most important invariant: **only one thread may hold a write lock on the database at a time.** All evaluators and block-processing code runs under this lock. + +--- + +## Shared Memory Database + +State is stored in a memory-mapped file (`shared_memory.bin`) managed by `chainbase`. Key properties: + +- All object indexes (accounts, blocks, content, validators, …) live in this file. +- The file is resized incrementally when free space drops below a threshold. +- On a clean shutdown the file is consistent; a crash may require replay from the block log. +- Nodes can export a **snapshot** of the shared memory state at a block boundary — see [Snapshots](../storage/snapshots.md). + +--- + +## Source Map + +| File | Role in architecture | +|------|---------------------| +| `programs/vizd/main.cpp` | Plugin registration and startup | +| `libraries/chain/include/graphene/chain/database.hpp` | Core database interface and signals | +| `libraries/chain/include/graphene/chain/evaluator_registry.hpp` | Factory for operation evaluators | +| `libraries/network/include/graphene/network/node.hpp` | P2P node delegate interface | +| `libraries/protocol/include/graphene/protocol/operations.hpp` | Operation type union | +| `plugins/chain/plugin.cpp` | Chain plugin: block/tx acceptance | +| `plugins/json_rpc/plugin.cpp` | JSON-RPC dispatch | diff --git a/docs/introduction/key-concepts.md b/docs/introduction/key-concepts.md new file mode 100644 index 0000000000..bc551a49cc --- /dev/null +++ b/docs/introduction/key-concepts.md @@ -0,0 +1,161 @@ +# Key Concepts + +This page explains the fundamental concepts you need to understand before working with VIZ Ledger as a developer, node operator, or application builder. + +--- + +## Accounts + +Every actor on VIZ Ledger is an **account**. Accounts hold balances, produce content, vote for validators, and interact with all protocol features. + +### Account Name Rules + +- Length: 3–16 characters total +- Dot-separated labels: each label ≥ 3 characters +- Each label starts with a letter, ends with a letter or digit +- Only lowercase letters (`a-z`), digits (`0-9`), hyphens (`-`) +- Example valid names: `alice`, `alice.bob`, `viz-user1` + +### Authority Levels + +Each account has three authority levels, each holding a set of keys or account delegates: + +| Level | Used for | Key to use | +|-------|---------|-----------| +| `master` | Changing keys, account recovery, high-security ops | Master key (keep offline) | +| `active` | Token transfers, vesting, validator voting | Active key | +| `regular` | Content, awards, committee voting, social ops | Regular key | + +An authority is a multi-sig structure: `{ weight_threshold, account_auths[], key_auths[] }`. A transaction is authorized when the sum of weights of provided signatures meets or exceeds `weight_threshold`. + +--- + +## Tokens + +### VIZ — Liquid Token + +- 3 decimal places: `"10.000 VIZ"` +- Used for transfers, fees, and funding operations +- Can be converted to SHARES via `transfer_to_vesting_operation` + +### SHARES — Staked Token + +- 6 decimal places: `"10.000000 SHARES"` +- Represents voting power and energy capacity +- Created by staking VIZ; withdrawn back to VIZ over 28 intervals (≈28 days) +- Not directly transferable; can be delegated to other accounts + +--- + +## Energy System + +Energy is the resource that controls the impact of social actions (awards, votes) relative to an account's SHARES. + +| Property | Value | +|----------|-------| +| Unit | Basis points: `0` = 0%, `10000` = 100% | +| Regeneration rate | Full recovery in 24 hours (86400 seconds) | +| Regeneration formula | `current_energy = min(10000, last_energy + elapsed_sec * 10000 / 86400)` | + +When an account performs an award with `energy = 500` (5%), that fraction of the account's SHARES is used to determine the reward pool distribution. Spending energy does not "destroy" tokens — it determines weight in the reward pool. + +--- + +## Validators (Block Producers) + +**Validators** (previously called "witnesses") are accounts that produce blocks and maintain the network. + +- Any account can register as a validator via `validator_update_operation`. +- Token holders vote for validators using their SHARES weight. +- The top validators by vote are scheduled round-robin to produce blocks. +- Each block slot is exactly 3 seconds. +- A round of 21 validators = 21 blocks = 63 seconds. + +### Fair-DPOS Participation + +Unlike standard DPOS, VIZ Ledger penalizes inactivity: +- Each validator has a **participation score** based on recent block production. +- If network-wide participation drops below `required-participation` (default 33%), block production pauses. +- Validators that miss too many blocks receive a vote penalty applied for `witness_miss_penalty_duration` seconds. + +--- + +## Blocks and Transactions + +### Block + +A signed bundle of transactions produced by a validator at its scheduled slot. Contains: +- `previous`: hash of the previous block (chain linkage) +- `timestamp`: the exact slot time +- `witness`: name of the producing validator +- `transactions[]`: list of signed transactions +- `witness_signature`: validator's signature + +### Transaction + +One or more operations grouped and signed. Properties: +- `ref_block_num = head_block_number & 0xFFFF` +- `ref_block_prefix` = bytes 4–7 of the referenced block ID (little-endian uint32) +- `expiration`: must be within 60 seconds of current time (recommended) +- `operations[]`: 1 or more operations +- `signatures[]`: ECDSA signatures satisfying all required authorities + +### Operation + +The atomic unit of state change. Serialized as `[type_id, operation_object]` when inside a transaction. There are 64+ operation types covering transfers, social actions, governance, and more — see [Operations Overview](../protocol/operations/overview.md). + +--- + +## Reward Pool + +Inflation is continuously added to the reward pool. Validators and content creators draw from this pool: + +| Recipient | Source | +|-----------|--------| +| Validators | `inflation_witness_percent` of block reward | +| Committee | `inflation_ratio_committee_vs_reward_fund` fraction | +| Reward fund | Remainder — distributed via awards and content votes | + +The exact percentages are set by validator consensus via `versioned_chain_properties_update_operation` and voted on by the top validators. + +--- + +## Fork and LIB + +**Fork database** (`fork_db`): in-memory tree of recently received blocks that may not yet be part of the canonical chain. The node tracks all candidate forks and always extends the heaviest (most approved) fork. + +**LIB (Last Irreversible Block)**: the most recent block that has been confirmed by more than 2/3 of validators. Blocks at or below LIB can never be reorganised. Once a block is below LIB, it is written to the permanent block log. + +--- + +## Snapshot + +A snapshot is a binary dump of the entire database state at a specific block number. It allows a new node to: +1. Download the snapshot file +2. Load it in seconds (rather than replaying the entire block history) +3. Resume syncing from the snapshot block height + +Snapshots are created by the `snapshot` plugin and have no effect on the canonical chain — they are purely an operational tool. + +--- + +## Chain Properties (Governance Parameters) + +On-chain consensus parameters are controlled by validators via `versioned_chain_properties_update_operation`. The active parameters are the **median** of the values submitted by the top 21 validators. + +Key parameters include: +- `account_creation_fee` — cost to create a new account +- `maximum_block_size` — max bytes per block +- `inflation_witness_percent` — validator share of block reward +- `witness_miss_penalty_percent` / `witness_miss_penalty_duration` — miss penalty +- `withdraw_intervals` — number of vesting withdrawal intervals + +See [Chain Properties Governance](../governance/chain-properties.md) for the full parameter list. + +--- + +## Hardforks + +Protocol upgrades are deployed as **hardforks** — scheduled activations at a specific block number. Once ≥17/21 validators signal support for a hardfork, it activates at the next scheduled block. Hardforks can add new operation types, change consensus rules, or introduce new chain properties. + +See [Hardforks](../consensus/hardforks.md) for the history and upgrade process. diff --git a/docs/introduction/what-is-viz.md b/docs/introduction/what-is-viz.md new file mode 100644 index 0000000000..614c43478a --- /dev/null +++ b/docs/introduction/what-is-viz.md @@ -0,0 +1,121 @@ +# What is VIZ Ledger? + +VIZ Ledger is a distributed ledger technology (DLT) built on the **Fair-DPOS** (Fair Delegated Proof of Stake) consensus algorithm. It is designed for decentralized social, financial, and governance applications where fairness, transparency, and efficiency are critical. + +> *VIZ Ledger uses a blockchain-based consensus mechanism with snapshot-assisted state storage, making it a hybrid DLT system.* + +--- + +## VIZ Ledger vs. Traditional Blockchain + +Traditional blockchains require every full node to store the complete history of all transactions from the genesis block. VIZ Ledger takes a different approach: + +| Property | Traditional Blockchain | VIZ Ledger | +|----------|----------------------|------------| +| State storage | Full history on every node | Recent blocks + periodic snapshots | +| Sync method | Replay all blocks from genesis | Load snapshot, replay recent blocks | +| Storage requirement | Grows unboundedly | Bounded by snapshot interval | +| Security model | Full chain verification | Snapshot + consensus verification | +| Consensus | Varies | Fair-DPOS | + +This architecture is closer to what the industry calls **DLT** in the broad sense — similar to Hedera Hashgraph or Corda — rather than a classical blockchain where every node keeps the full ledger history. + +### Why "VIZ Ledger"? + +The name follows the same pattern as [XRP Ledger](https://xrpl.org): +- It is neutral with respect to the underlying storage mechanism. +- It accurately reflects the core function: maintaining a distributed ledger of accounts, transactions, and state. +- It leaves room for architectural evolution without renaming. + +In technical documentation the full description is used where needed: *"VIZ Ledger is a Fair-DPOS distributed ledger with snapshot-assisted state storage."* + +--- + +## Core Properties + +### Fair-DPOS Consensus + +VIZ Ledger uses **Fair Delegated Proof of Stake**, an evolution of standard DPOS: + +- Token holders vote for **validators** (block producers) using their staked SHARES. +- The top validators by vote weight are scheduled to produce blocks in round-robin order. +- **Fairness enforcement**: a validator that misses blocks has its participation score reduced. If participation drops below the required threshold, block production is paused until enough validators are active again. +- There are no unbounded rewards for inactive validators — production requires actual participation. + +### Snapshot-Assisted State + +- Nodes store the current state (accounts, balances, content, votes) in shared memory. +- Periodic snapshots capture the full state at a specific block height. +- New nodes can fast-sync by loading a recent snapshot and replaying only the blocks since the snapshot, instead of the entire chain history. +- The block log (binary format) stores all blocks for full nodes that need historical access. + +### Social & Governance Primitives + +VIZ Ledger embeds social and governance features directly into the protocol — they are not an application layer: + +- **Energy system**: accounts have an energy pool (0–100%) that regenerates over 24 hours. Energy is spent to perform social actions (awards, votes) proportional to the account's stake impact. +- **Awards**: any account can award any other account using energy, distributing reward pool tokens. +- **Committee DAO**: on-chain committee worker requests, funding proposals, and voting. +- **Invites**: on-chain invite mechanism for bootstrapping new accounts. +- **Paid subscriptions**: on-chain subscription contracts between accounts. + +--- + +## Architecture at a Glance + +``` +┌─────────────────────────────────────────────────────────────┐ +│ vizd process │ +│ │ +│ ┌──────────┐ ┌──────────┐ ┌────────────┐ ┌─────────┐ │ +│ │ chain │ │validator │ │database_api│ │ p2p │ │ +│ │ plugin │ │ plugin │ │ plugin │ │ plugin │ │ +│ └────┬─────┘ └────┬─────┘ └─────┬──────┘ └────┬────┘ │ +│ │ │ │ │ │ +│ ┌────▼──────────────▼──────────────▼───────────────▼────┐ │ +│ │ libraries/chain (database) │ │ +│ └────────────────────────────────────────────────────────┘ │ +│ ┌────────────────────┐ ┌──────────────────────────────┐ │ +│ │ libraries/network │ │ libraries/protocol │ │ +│ └────────────────────┘ └──────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ + │ │ + Peer nodes Wallets / Apps + (P2P port 2001) (HTTP/WS ports 8090/8091) +``` + +Key components: + +| Component | Role | +|-----------|------| +| `chain plugin` | Opens the database, coordinates block and transaction processing | +| `validator plugin` | Produces blocks on schedule using Fair-DPOS rules | +| `database_api plugin` | Exposes JSON-RPC read queries for wallets and apps | +| `p2p plugin` | Manages peer connections, block and transaction propagation | +| `webserver plugin` | HTTP and WebSocket transport for JSON-RPC | +| `snapshot plugin` | Creates and loads state snapshots | + +--- + +## Token System + +VIZ Ledger has two native tokens: + +| Token | Purpose | Decimals | +|-------|---------|----------| +| `VIZ` | Liquid token for transfers and fees | 3 (`10.000 VIZ`) | +| `SHARES` | Staked token representing voting power and energy capacity | 6 (`10.000000 SHARES`) | + +VIZ can be converted to SHARES via `transfer_to_vesting_operation`. SHARES can be withdrawn back to VIZ over 28 withdrawal intervals. + +--- + +## Who Is This Documentation For? + +| Audience | Start here | +|----------|-----------| +| Node operators | [Getting Started](../node/getting-started.md) | +| Validator operators | [Running a Validator Node](../node/validator-node.md) | +| Application developers | [JSON-RPC API](../api/json-rpc.md) | +| Wallet / library developers | [Data Types](../protocol/data-types.md) · [Operations](../protocol/operations/overview.md) | +| Protocol contributors | [Architecture](./architecture.md) · [Consensus](../consensus/fair-dpos.md) | diff --git a/docs/node/building.md b/docs/node/building.md new file mode 100644 index 0000000000..532daaba9c --- /dev/null +++ b/docs/node/building.md @@ -0,0 +1,190 @@ +# Building from Source + +VIZ Ledger uses a CMake-based build system with dedicated build scripts for each platform. The two-script Linux process (`install-deps-linux.sh` + `build-linux.sh`) separates dependency installation (needs root) from the actual build (runs as a regular user). + +--- + +## Requirements + +| Component | Version | +|-----------|---------| +| CMake | 3.16+ | +| GCC | 4.8+ | +| Clang | 3.3+ | +| Boost | 1.71+ (with `coroutine` component) | +| OpenSSL | Any recent version | + +--- + +## Linux (Ubuntu 20.04 / 22.04 / 24.04) + +### Step 1 — Clone the repository + +```bash +git clone --recursive https://github.com/VIZ-Blockchain/viz-cpp-node +cd viz-cpp-node +``` + +### Step 2 — Install dependencies (as root) + +```bash +chmod +x install-deps-linux.sh +sudo ./install-deps-linux.sh +``` + +Installs: cmake, gcc/g++, git, boost (all components including coroutine/context), openssl, readline, ccache, and compression libraries. + +### Step 3 — Build (as regular user) + +```bash +chmod +x build-linux.sh +./build-linux.sh +``` + +Output binary: `build/programs/vizd/vizd` + +### Common build flags + +```bash +# Low-memory node (validators/seed nodes — excludes history indexing) +./build-linux.sh -l + +# Testnet build +./build-linux.sh -n + +# Debug build +./build-linux.sh -t Debug + +# Parallel jobs +./build-linux.sh -j 8 + +# Skip dependency installation (already installed) +./build-linux.sh --skip-deps + +# Custom Boost / OpenSSL paths +./build-linux.sh --boost-root /opt/boost_1_74_0 --openssl-root /opt/openssl +``` + +--- + +## macOS + +```bash +chmod +x build-mac.sh +./build-mac.sh +``` + +The script installs Xcode Command Line Tools (if needed) and Homebrew dependencies automatically, then configures and builds. + +```bash +# With custom Boost path +./build-mac.sh --boost-root /opt/homebrew/opt/boost + +# Skip dependency installation +./build-mac.sh --skip-deps +``` + +--- + +## Windows (MinGW) + +```cmd +set BOOST_ROOT=C:\Boost +set OPENSSL_ROOT_DIR=C:\OpenSSL-Win64 +build-mingw.bat +``` + +Optional environment variables: + +| Variable | Default | Description | +|----------|---------|-------------| +| `VIZ_BUILD_TYPE` | `Release` | `Release` or `Debug` | +| `VIZ_LOW_MEMORY` | `OFF` | `ON` to build low-memory node | +| `VIZ_BUILD_TESTNET` | `OFF` | `ON` for testnet build | +| `VIZ_FULL_STATIC` | `OFF` | `ON` for fully static binary | + +--- + +## Windows (MSVC) + +Requires Visual Studio 2019+ with "Desktop development with C++" workload: + +```cmd +set BOOST_ROOT=C:\Boost +set OPENSSL_ROOT_DIR=C:\OpenSSL-Win64 +build-msvc.bat +``` + +--- + +## CMake Options + +For direct CMake usage (advanced): + +| Option | Default | Description | +|--------|---------|-------------| +| `BUILD_TESTNET` | `OFF` | Enable testnet-specific code | +| `LOW_MEMORY_NODE` | `OFF` | Exclude history/indexing plugins | +| `CHAINBASE_CHECK_LOCKING` | `OFF` | Enable lock assertion checks (debug) | +| `ENABLE_MONGO_PLUGIN` | `OFF` | Build MongoDB plugin | +| `BUILD_SHARED_LIBRARIES` | `OFF` | Build shared libraries | +| `USE_PCH` | `OFF` | Enable precompiled headers (faster rebuilds) | + +Example: + +```bash +mkdir build && cd build +cmake -DCMAKE_BUILD_TYPE=Release \ + -DLOW_MEMORY_NODE=ON \ + -DCMAKE_INSTALL_PREFIX=/usr/local \ + .. +make -j$(nproc) +``` + +Or use the helper: + +```bash +python3 programs/build_helpers/configure_build.py --release +``` + +--- + +## Build Targets + +| Target | Binary | Description | +|--------|--------|-------------| +| `vizd` | `programs/vizd/vizd` | Main node daemon | +| `cli_wallet` | `programs/cli_wallet/cli_wallet` | Command-line wallet | + +--- + +## Docker Builds + +The repository ships four Dockerfiles: + +| File | Purpose | +|------|---------| +| `Dockerfile-production` | Full mainnet node (Release) | +| `Dockerfile-lowmem` | Low-memory node (`LOW_MEMORY_NODE=ON`) | +| `Dockerfile-mongo` | Node with MongoDB plugin | +| `Dockerfile-testnet` | Testnet node (`BUILD_TESTNET=ON`) | + +Build example: + +```bash +docker build -f share/vizd/docker/Dockerfile-production -t vizd:local . +``` + +See [Docker](./docker.md) for full production Docker setup. + +--- + +## Troubleshooting + +| Problem | Solution | +|---------|---------| +| `boost/coroutine.hpp` not found | Install `libboost-coroutine-dev` (Ubuntu) or Boost 1.71+ | +| CMake < 3.16 | Install newer CMake from `cmake.org` or Kitware PPA | +| `do not run as root` error | Run `build-linux.sh` as a normal user, not `sudo` | +| Link error on macOS (OpenSSL) | `export OPENSSL_ROOT_DIR=$(brew --prefix openssl)` | +| Out of memory during compilation | Use `-j 2` to reduce parallel jobs | diff --git a/docs/node/configuration.md b/docs/node/configuration.md new file mode 100644 index 0000000000..ca64ff6bb2 --- /dev/null +++ b/docs/node/configuration.md @@ -0,0 +1,223 @@ +# Node Configuration + +VIZ Ledger nodes are configured via an INI file. The repository ships several templates in `share/vizd/config/`: + +| Template | Use case | +|----------|---------| +| `config.ini` | Full mainnet node with public RPC | +| `config_witness.ini` | Validator node (localhost RPC, block production) | +| `config_testnet.ini` | Testnet / development | +| `config_mongo.ini` | Node with MongoDB history backend | +| `config_lowmem.ini` | Low-memory consensus/seed node | +| `config_stock_exchange.ini` | Market data consumer (minimal plugins) | +| `config_debug.ini` | Debug mode | + +--- + +## Network & P2P + +```ini +# Listen address for P2P connections (port 2001 standard) +p2p-endpoint = 0.0.0.0:2001 + +# Maximum peer connections (unlimited if unset) +p2p-max-connections = 200 + +# Seed nodes to bootstrap connectivity (repeatable) +p2p-seed-node = seed1.viz.media:2001 +p2p-seed-node = seed2.viz.media:2001 + +# Checkpoints: trusted (block_num, block_id) pairs (repeatable) +# checkpoint = [12345,"0003039..." ] +``` + +--- + +## Webserver & RPC + +```ini +# HTTP JSON-RPC endpoint +webserver-http-endpoint = 0.0.0.0:8090 + +# WebSocket JSON-RPC endpoint +webserver-ws-endpoint = 0.0.0.0:8091 + +# RPC thread pool size +webserver-thread-pool-size = 2 +``` + +> **Security note:** For validator nodes, bind to `127.0.0.1` to prevent external access: +> ```ini +> webserver-http-endpoint = 127.0.0.1:8090 +> webserver-ws-endpoint = 127.0.0.1:8091 +> ``` + +--- + +## Locking & Concurrency + +```ini +# Microseconds to wait for a read lock before retrying +read-wait-micro = 500000 + +# Maximum read lock retry attempts +max-read-wait-retries = 2 + +# Microseconds to wait for a write lock before retrying +write-wait-micro = 500000 + +# Maximum write lock retry attempts +max-write-wait-retries = 3 + +# Serialize all write operations on a single thread (recommended) +single-write-thread = true + +# Run plugin notifications on push_transaction (adds latency; default false) +enable-plugins-on-push-transaction = false +``` + +--- + +## Shared Memory (Database) + +The blockchain state is stored in a memory-mapped file (`shared_memory.bin`). + +```ini +# Initial size of the shared memory file +shared-file-size = 4G + +# Minimum free space before triggering a resize +min-free-shared-file-size = 500M + +# Amount to grow the file by on resize +inc-shared-file-size = 2G + +# Check free space every N blocks +block-num-check-free-size = 1000 +``` + +Tune `shared-file-size` based on chain size. For mainnet, start at `4G` and monitor growth. + +--- + +## Plugin Activation + +```ini +# Each 'plugin' line adds a plugin (repeatable) +# Minimum set for a full API node: +plugin = chain p2p webserver json_rpc database_api network_broadcast_api + +# Additional indexing plugins (comment out on low-memory nodes): +plugin = social_network tags follow account_history account_by_key +plugin = committee_api invite_api paid_subscription_api custom_protocol_api + +# For validator nodes only: +plugin = validator witness_api +``` + +### Plugin sets by node type + +| Node type | Plugins | +|-----------|---------| +| Full node | All above | +| Validator | `chain p2p webserver json_rpc database_api network_broadcast_api validator witness_api` | +| Low-memory seed | `chain p2p` | +| Stock exchange | `chain p2p webserver json_rpc database_api network_broadcast_api account_history` | + +--- + +## History & Tracking + +```ini +# Discard vote objects before this block (saves memory, default 0 = keep all) +clear-votes-before-block = 0 + +# Skip indexing virtual operations (saves memory on validators) +skip-virtual-ops = false + +# Track account history only for accounts in range (optional) +# track-account-range = ["alice","alice.zzz"] + +# Whitelist/blacklist specific operation types for history +# history-whitelist-ops = transfer_operation +# history-blacklist-ops = custom_operation + +# Start indexing history from this block number +# history-start-block = 1000000 + +# Maximum feed entries per account (follow plugin) +follow-max-feed-size = 500 + +# Private message tracking range (optional) +# pm-account-range = ["alice","alice.zzz"] +``` + +--- + +## Validator (Block Production) + +Leave these unset on non-validator nodes. + +```ini +# Allow production even if chain is stale (development/testnet only) +enable-stale-production = false + +# Minimum participation % required to produce blocks (0–99) +required-participation = 33 + +# Validator account name (repeatable for multiple validators on one node) +validator = alice + +# WIF signing key for the validator +private-key = 5JxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxWIF + +# Emergency consensus private key (optional) +# emergency-private-key = 5Jxxx... +``` + +See [Validator Node](./validator-node.md) for full validator configuration. + +--- + +## Logging + +```ini +# Console logger (outputs to stderr) +log.console_appender.stderr.stream = std_error + +# File logger for P2P messages +log.file_appender.p2p.filename = logs/p2p/p2p.log + +# Log level: all, debug, info, warn, error, off +logger.default.level = warn +logger.default.appenders = stderr + +logger.p2p.level = warn +logger.p2p.appenders = p2p +``` + +--- + +## MongoDB (Optional) + +Only relevant when the node is built with `ENABLE_MONGO_PLUGIN=ON`: + +```ini +plugin = mongo_db +mongodb-uri = mongodb://localhost:27017/vizd +``` + +--- + +## Complete Reference + +All options listed by source file: + +| Source | Options covered | +|--------|----------------| +| `plugins/chain/plugin.hpp` | `shared-file-size`, `min-free-shared-file-size`, `inc-shared-file-size`, `block-num-check-free-size`, `single-write-thread`, `enable-plugins-on-push-transaction`, `read-wait-micro`, `max-read-wait-retries`, `write-wait-micro`, `max-write-wait-retries`, `skip-virtual-ops`, `clear-votes-before-block`, `track-account-range`, `history-whitelist-ops`, `history-blacklist-ops`, `history-start-block` | +| `plugins/p2p/p2p_plugin.hpp` | `p2p-endpoint`, `p2p-max-connections`, `p2p-seed-node`, `checkpoint` | +| `plugins/webserver/webserver_plugin.hpp` | `webserver-http-endpoint`, `webserver-ws-endpoint`, `webserver-thread-pool-size` | +| `plugins/validator/validator.hpp` | `enable-stale-production`, `required-participation`, `validator`, `private-key`, `emergency-private-key`, `fork-collision-timeout-blocks`, `ntp-server`, `ntp-request-interval`, `debug-block-production` | +| `plugins/follow/` | `follow-max-feed-size` | +| `plugins/private_message/` | `pm-account-range` | diff --git a/docs/node/docker.md b/docs/node/docker.md new file mode 100644 index 0000000000..6dd1858ae8 --- /dev/null +++ b/docs/node/docker.md @@ -0,0 +1,183 @@ +# Docker Deployment + +VIZ Ledger ships four Docker images for different deployment profiles. All use a two-stage build: the builder stage compiles the binary; the runtime stage contains only the binary and configuration. + +--- + +## Available Images + +| Dockerfile | Tag | Description | +|-----------|-----|-------------| +| `Dockerfile-production` | `latest` | Full mainnet node (Release, all plugins) | +| `Dockerfile-lowmem` | `lowmem` | Low-memory node (`LOW_MEMORY_NODE=ON`, no history indexes) | +| `Dockerfile-mongo` | `mongo` | Full node with MongoDB history plugin | +| `Dockerfile-testnet` | `testnet` | Testnet node (`BUILD_TESTNET=ON`) | + +--- + +## Quick Start + +```bash +docker run -d \ + --name vizd \ + --restart unless-stopped \ + -p 2001:2001 \ + -p 8090:8090 \ + -p 8091:8091 \ + -v /data/vizd:/var/lib/vizd \ + vizblockchain/vizd:latest +``` + +Follow logs: + +```bash +docker logs -f vizd +``` + +--- + +## Volumes + +| Container path | Purpose | +|----------------|---------| +| `/var/lib/vizd` | Blockchain data, shared memory, block log | +| `/etc/vizd` | Configuration files and seed node list | + +Always mount `/var/lib/vizd` to persist state across container restarts. + +To use a custom config: + +```bash +docker run -d \ + -v /data/vizd:/var/lib/vizd \ + -v /my/config.ini:/etc/vizd/config.ini \ + vizblockchain/vizd:latest +``` + +--- + +## Environment Variables + +The entry script (`vizd.sh`) reads these environment variables: + +| Variable | Description | Example | +|----------|-------------|---------| +| `VIZD_SEED_NODES` | Space-delimited seed node list (overrides `/etc/vizd/seednodes`) | `seed1.viz.media:2001 seed2.viz.media:2001` | +| `VIZD_RPC_ENDPOINT` | Override HTTP RPC endpoint | `0.0.0.0:8090` | +| `VIZD_P2P_ENDPOINT` | Override P2P endpoint | `0.0.0.0:2001` | +| `VIZD_WITNESS` | Validator account name (enables block production) | `alice` | +| `VIZD_PRIVATE_KEY` | Validator signing key in WIF format | `5J...` | + +--- + +## Ports + +| Port | Protocol | Purpose | +|------|----------|---------| +| 2001 | TCP | P2P peer connections | +| 8090 | TCP | HTTP JSON-RPC | +| 8091 | TCP | WebSocket JSON-RPC | + +--- + +## Validator Node (Docker) + +```bash +docker run -d \ + --name vizd-validator \ + --restart unless-stopped \ + -p 2001:2001 \ + -v /data/vizd:/var/lib/vizd \ + -e VIZD_WITNESS=myvalidator \ + -e VIZD_PRIVATE_KEY=5Jxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx \ + vizblockchain/vizd:latest +``` + +For validators, do **not** expose ports 8090/8091 publicly — bind to localhost only: + +```bash +-e VIZD_RPC_ENDPOINT=127.0.0.1:8090 +``` + +--- + +## Testnet Node + +```bash +docker run -d \ + --name vizd-testnet \ + -p 2001:2001 \ + -p 8090:8090 \ + -v /data/vizd-testnet:/var/lib/vizd \ + vizblockchain/vizd:testnet +``` + +--- + +## Building Images Locally + +```bash +# Production +docker build \ + -f share/vizd/docker/Dockerfile-production \ + -t vizd:local \ + . + +# Low-memory +docker build \ + -f share/vizd/docker/Dockerfile-lowmem \ + -t vizd:lowmem \ + . +``` + +### CMake flags per image + +| Image | `LOW_MEMORY_NODE` | `ENABLE_MONGO_PLUGIN` | `BUILD_TESTNET` | +|-------|:-----------------:|:---------------------:|:---------------:| +| production | OFF | OFF | OFF | +| lowmem | ON | OFF | OFF | +| mongo | OFF | ON | OFF | +| testnet | OFF | OFF | ON | + +--- + +## CI/CD (GitHub Actions) + +The repository ships `.github/workflows/docker-main.yml` which builds and pushes the production image tagged `latest` on every push to `master`. + +```yaml +- name: Build and push + uses: docker/build-push-action@v2 + with: + file: share/vizd/docker/Dockerfile-production + tags: vizblockchain/vizd:latest + push: true +``` + +--- + +## Resource Sizing + +| Node type | RAM | Disk | +|-----------|-----|------| +| Full node (mainnet) | 8 GB+ | 50 GB+ | +| Low-memory / validator | 4 GB | 20 GB | +| Testnet | 4 GB | 10 GB | + +Start with a shared memory size that fits comfortably in RAM. In `config.ini`: + +```ini +shared-file-size = 4G +``` + +--- + +## Troubleshooting + +| Symptom | Cause | Fix | +|---------|-------|-----| +| Container exits immediately | Bad config or missing volume | `docker logs vizd` — check for startup errors | +| Port 8090 unreachable | RPC bound to localhost | Remove `127.0.0.1:` prefix or use reverse proxy | +| No peers | Firewall blocking port 2001 | Open port 2001 TCP inbound | +| Slow sync | No snapshot loaded | Provide snapshot in volume before first start | +| `Permission denied` on `/var/lib/vizd` | Volume ownership mismatch | `chown -R 1000:1000 /data/vizd` | diff --git a/docs/node/getting-started.md b/docs/node/getting-started.md new file mode 100644 index 0000000000..eed02087e9 --- /dev/null +++ b/docs/node/getting-started.md @@ -0,0 +1,184 @@ +# Getting Started + +This guide covers everything needed to run a VIZ Ledger node — from installing dependencies to initial synchronization. + +--- + +## Prerequisites + +| Requirement | Minimum | Recommended | +|-------------|---------|-------------| +| OS | Ubuntu 20.04 LTS | Ubuntu 24.04 LTS | +| RAM | 4 GB | 8 GB+ | +| Disk | 20 GB | 50 GB+ SSD | +| CPU | 2 cores | 4+ cores | +| Network | Public IP, open port 2001 | Stable connection | + +**Ports used:** + +| Port | Protocol | Purpose | +|------|----------|---------| +| 2001 | TCP | P2P peer connections | +| 8090 | TCP | HTTP JSON-RPC | +| 8091 | TCP | WebSocket JSON-RPC | + +--- + +## Option A: Docker (Recommended for Quick Start) + +### 1. Pull the production image + +```bash +docker pull vizblockchain/vizd:latest +``` + +### 2. Run the node + +```bash +docker run -d \ + --name vizd \ + -p 2001:2001 \ + -p 8090:8090 \ + -p 8091:8091 \ + -v /data/vizd:/var/lib/vizd \ + vizblockchain/vizd:latest +``` + +### 3. Follow logs + +```bash +docker logs -f vizd +``` + +You should see peer connections and block sync progress within a few minutes. + +### Environment variables (Docker) + +| Variable | Purpose | Example | +|----------|---------|---------| +| `VIZD_SEED_NODES` | Override default seed nodes | `node1.viz.media:2001` | +| `VIZD_WITNESS` | Validator name (if validator node) | `alice` | +| `VIZD_PRIVATE_KEY` | Validator signing key (WIF) | `5J...` | + +--- + +## Option B: Build from Source + +### 1. Install dependencies (Linux) + +```bash +git clone --recursive https://github.com/VIZ-Blockchain/viz-cpp-node +cd viz-cpp-node +chmod +x install-deps-linux.sh +sudo ./install-deps-linux.sh +``` + +### 2. Build + +```bash +chmod +x build-linux.sh +./build-linux.sh +``` + +For a low-memory build (validators and seed nodes — excludes indexing plugins): + +```bash +./build-linux.sh -l +``` + +The binary is placed at `build/programs/vizd/vizd`. + +### 3. macOS + +```bash +chmod +x build-mac.sh +./build-mac.sh +``` + +### 4. Windows (MinGW) + +```cmd +set BOOST_ROOT=C:\Boost +set OPENSSL_ROOT_DIR=C:\OpenSSL-Win64 +build-mingw.bat +``` + +See [Building](./building.md) for detailed platform instructions and CMake options. + +--- + +## Initial Configuration + +Copy the mainnet config template: + +```bash +cp share/vizd/config/config.ini /data/vizd/config.ini +``` + +Minimum edits for a public node: + +```ini +# P2P +p2p-endpoint = 0.0.0.0:2001 +p2p-seed-node = seed1.viz.media:2001 +p2p-seed-node = seed2.viz.media:2001 + +# RPC +webserver-http-endpoint = 0.0.0.0:8090 +webserver-ws-endpoint = 0.0.0.0:8091 + +# Shared memory — adjust to available disk +shared-file-size = 4G + +# Plugins (full node) +plugin = chain p2p webserver json_rpc database_api network_broadcast_api +plugin = social_network tags follow account_history +``` + +For a validator node, see [Validator Node](./validator-node.md). + +--- + +## Starting the Node + +```bash +./vizd --config-file /data/vizd/config.ini --data-dir /data/vizd +``` + +Or with Docker, pass the data directory as a volume (see Option A above). + +--- + +## Verifying Sync + +Query the node via HTTP RPC: + +```bash +curl -s -X POST http://localhost:8090 \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"call","params":["database_api","get_dynamic_global_properties",[]],"id":1}' \ + | python3 -m json.tool +``` + +Check `head_block_number` — it should increase every 3 seconds once synced. + +--- + +## Node Types + +| Type | Config template | Description | +|------|----------------|-------------| +| Full node | `config.ini` | All plugins, public RPC endpoints | +| Validator | `config_witness.ini` | Block production, RPC on localhost only | +| Testnet | `config_testnet.ini` | Development and testing | +| Low-memory | `config.ini` + `LOW_MEMORY_NODE` build flag | Consensus only, no history indexes | +| MongoDB | `config_mongo.ini` | Full history in MongoDB | + +--- + +## Next Steps + +- [Configuration reference](./configuration.md) — all config options explained +- [Docker deployment](./docker.md) — production Docker setup +- [Validator node](./validator-node.md) — run a block-producing validator +- [Snapshots](./snapshot.md) — fast-sync using state snapshots diff --git a/docs/node/monitoring.md b/docs/node/monitoring.md new file mode 100644 index 0000000000..24f4f80784 --- /dev/null +++ b/docs/node/monitoring.md @@ -0,0 +1,313 @@ +# Monitoring + +This page covers health checks, log patterns, P2P peer statistics, and integration with external monitoring stacks for VIZ Ledger nodes. + +--- + +## Health Check: Node Sync + +Query the node's dynamic global properties to verify it is running and syncing: + +```bash +curl -s -X POST http://localhost:8090 \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"call","params":["database_api","get_dynamic_global_properties",[]],"id":1}' \ + | python3 -m json.tool +``` + +Check `head_block_number` — it must increase every 3 seconds while synced. Check `time` — it must be within a few seconds of wall clock. + +A simple liveness probe script: + +```bash +#!/bin/bash +RESPONSE=$(curl -sf -X POST http://localhost:8090 \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"call","params":["database_api","get_dynamic_global_properties",[]],"id":1}') +if [ $? -ne 0 ]; then echo "CRIT: RPC unreachable"; exit 2; fi + +HEAD=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['result']['head_block_number'])") +echo "OK: head_block_number=$HEAD" +``` + +--- + +## Log Patterns + +### Block production (validator nodes) + +``` +# Good: slot produced +produced block #123456 @2025-01-01T00:00:03 validator=alice sz=2048 + +# Missed slot +MISSED-SLOT-OUR-validator: alice missed slot at 2025-01-01T00:00:06 + +# Minority fork detected +MINORITY FORK DETECTED: rolling back to LIB #123400 + +# Watchdog fired +WATCHDOG: no production for 180s, clearing flags +``` + +### P2P connectivity + +``` +# New peer connected +New peer is connected (203.0.113.10:2001), now 8 active peers + +# Peer soft-banned +soft-banning peer 203.0.113.10:2001 for 300s: reason=only_fork_db_blocks_no_progress + +# Sync complete +Sync: peer 203.0.113.10 says we're up-to-date +``` + +### Snapshot and recovery + +``` +# Snapshot created +Snapshot created at block 5000000 in 14.2s: /data/snapshots/snapshot-block-5000000.json + +# Auto-recovery triggered +shared_memory_corruption_exception detected — starting auto-recovery +Recovery complete. Resumed from block 4999500 +``` + +### Sync logger (DLT mode) + +Enable the `sync` logger to see sync negotiation details: + +```ini +[logger.sync] +level = info +appenders = stderr +``` + +Key messages: +- `Starting sync with peer ...` — sync initiated +- `on_blockchain_item_ids_inventory: ...` — block ID batch received +- `Sync: peer X says we're up-to-date` — sync complete for this peer +- `DEFERRED_RESIZE: sync block #N deferred` — shared memory resize delayed a block +- `auto-clearing stuck peer_needs_sync_items_from_us` — 30s safety net cleared a stuck flag + +--- + +## Log Configuration + +Logs are configured in `config.ini`: + +```ini +# Console output +log.console_appender.stderr.stream = std_error + +# P2P log file +log.file_appender.p2p.filename = logs/p2p/p2p.log + +# Log levels: all, debug, info, warn, error, off +logger.default.level = warn +logger.default.appenders = stderr + +logger.p2p.level = warn +logger.p2p.appenders = p2p +``` + +> **Note:** `node.cpp` routes all its `ilog`/`wlog` calls to the `p2p` logger. To see P2P messages, configure the `p2p` logger level to `info`. + +Log rotation via `logrotate` (example `/etc/logrotate.d/vizd`): + +``` +/data/vizd/logs/p2p/p2p.log { + daily + rotate 14 + compress + missingok + copytruncate +} +``` + +--- + +## P2P Peer Statistics + +The P2P plugin logs peer health metrics every 5 minutes (configurable). Enable in `config.ini`: + +```ini +p2p-stats-enabled = true +p2p-stats-interval = 300 # seconds +``` + +Sample log output: + +``` +P2P peer | ip: 203.0.113.10 | port: 2001 | latency: 45ms | bytes_in: 12345 | blocked: false | reason: +P2P peer | ip: 198.51.100.5 | port: 2001 | latency: 120ms | bytes_in: 8765 | blocked: true | reason: soft_ban +Block storage | dlt_log: [79174319..79274318] | dlt_resizes: 412 | fork_db: linked=18 unlinked=0 +``` + +Fields: +- `latency` — round-trip delay in ms +- `bytes_in` — delta bytes received since last measurement +- `blocked` / `reason` — soft-ban or inhibition status and cause +- `Block storage` — DLT block log range, resize count, fork_db state + +A high `dlt_resizes` count combined with a shrinking `dlt_log` range may indicate the mapped-file self-heal ran. A `reason: soft_ban` peer may be on a fork or sending only stale data. + +--- + +## Prometheus & Grafana + +The node does not expose a Prometheus endpoint natively. Use [Node Exporter](https://github.com/prometheus/node_exporter) for OS-level metrics and scrape the JSON-RPC endpoint with a custom exporter: + +```python +# minimal example: scrape head_block_number +import requests, time +from prometheus_client import Gauge, start_http_server + +g = Gauge('viz_head_block_number', 'Current head block') + +def collect(): + r = requests.post('http://localhost:8090', json={ + "jsonrpc": "2.0", "method": "call", + "params": ["database_api", "get_dynamic_global_properties", []], + "id": 1 + }, timeout=5) + g.set(r.json()['result']['head_block_number']) + +start_http_server(9100) +while True: + collect() + time.sleep(3) +``` + +**Recommended dashboard panels:** + +| Panel | Metric / Source | +|-------|----------------| +| Head block | `viz_head_block_number` (increases every 3 s when synced) | +| Block lag | `time() - viz_head_block_time` (seconds behind wall clock) | +| Peer count | Parsed from P2P stats log | +| Peer latency | P2P stats log, by peer IP | +| Shared memory free | `viz_shared_memory_free_mb` from custom exporter | +| CPU / RAM | Node Exporter standard metrics | +| Disk I/O | Node Exporter `node_disk_*` | + +--- + +## ELK / Centralized Logging + +Forward node logs to a central collector. Example with Filebeat: + +```yaml +# filebeat.yml +filebeat.inputs: + - type: log + paths: + - /data/vizd/logs/p2p/p2p.log + fields: + service: vizd + node: validator-1 + +output.logstash: + hosts: ["logstash:5044"] +``` + +Parse key fields (Logstash grok or Elasticsearch ingest pipeline): + +``` +MISSED-SLOT-OUR-validator: %{WORD:validator} missed slot at %{TIMESTAMP_ISO8601:slot_time} +produced block #%{NUMBER:block_num} @%{TIMESTAMP_ISO8601:block_time} validator=%{WORD:producer} +``` + +--- + +## Validator-Specific Monitoring + +### Key metrics to alert on + +| Condition | Severity | Action | +|-----------|----------|--------| +| `MISSED-SLOT-OUR-validator` in logs | Warning | Check NTP, network latency, CPU load | +| `MINORITY FORK DETECTED` | Critical | Verify P2P connectivity to seed nodes | +| `WATCHDOG: no production for 180s` | Critical | Check validator key and node health | +| `no_private_key` result code | Critical | Signing key mismatch — check config | +| `low_participation` result code | Warning | Network health degraded | +| Head block stopped advancing | Critical | Node may be stalled | +| Peer count drops to 0 | Critical | Network partition or firewall issue | + +### NTP check + +```bash +chronyc tracking | grep "System time" +# or +timedatectl | grep "NTP synchronized" +``` + +The validator plugin uses its own NTP client (configurable via `ntp-server` in config), but OS clock sync is also important. A drift >200ms can cause missed slots. + +--- + +## Database Maintenance + +### Shared memory sizing + +Monitor free space warnings in logs: + +``` +chainbase: shared memory low — resizing from 4G to 6G +``` + +Proactively configure growth parameters in `config.ini`: + +```ini +shared-file-size = 4G +min-free-shared-file-size = 500M +inc-shared-file-size = 2G +block-num-check-free-size = 1000 +``` + +### Snapshot backup verification + +After creating a snapshot, verify it loads cleanly on a test node: + +```bash +vizd --create-snapshot /tmp/verify-snap.json --plugin snapshot +# Expected: exits cleanly with "Snapshot created at block N" +``` + +Periodically test crash recovery: + +```bash +vizd --replay-from-snapshot --snapshot-auto-latest --plugin snapshot +# Expected: imports snapshot, replays dlt_block_log, emits "Recovery complete" +``` + +--- + +## Incident Response Checklist + +**Node not syncing:** +1. Check peer count (`p2p-stats-enabled` logs or RPC `get_info`). +2. Verify firewall allows TCP port 2001 inbound. +3. Check `p2p-seed-node` settings — try alternate seeds. +4. Look for `soft_ban` entries in P2P stats — the node may be on a fork. + +**Validator not producing:** +1. Check `validator` and `private-key` in `config.ini` match on-chain signing key. +2. Verify `low_participation` is not the cause (network health). +3. Check NTP synchronization. +4. Look for `MINORITY FORK DETECTED` — node may need to resync. + +**Node crashed / shared memory corrupted:** +1. If `--auto-recover-from-snapshot` is enabled (default) and snapshots exist, the node recovers automatically — check logs. +2. Manual recovery: `vizd --replay-from-snapshot --snapshot-auto-latest --plugin snapshot`. +3. If no snapshots exist: `vizd --replay-blockchain` (requires full block log; unavailable in DLT mode). + +**RPC unreachable:** +1. Check `webserver-http-endpoint` binding — validators default to `127.0.0.1:8090`. +2. Check firewall or reverse proxy configuration. +3. Verify plugin list includes `webserver json_rpc database_api`. + +--- + +See also: [Validator Node](./validator-node.md), [Validator Guard](./validator-guard.md), [Snapshots](./snapshot.md), [Configuration](./configuration.md). diff --git a/docs/node/snapshot.md b/docs/node/snapshot.md new file mode 100644 index 0000000000..a9acb2c1ce --- /dev/null +++ b/docs/node/snapshot.md @@ -0,0 +1,377 @@ +# Snapshots + +The snapshot plugin enables near-instant node startup by serializing the full blockchain state to a JSON file. Instead of replaying millions of blocks from the block log, a node loads a snapshot and syncs only the blocks produced since the snapshot was taken. + +Nodes loaded from a snapshot run in **DLT mode**: the main block log stays empty and a compact **DLT rolling block log** (`dlt_block_log`) holds the most recent irreversible blocks. + +--- + +## Quick Reference + +| Goal | Command / Config | +|------|-----------------| +| Create snapshot once (stop node) | `vizd --create-snapshot /path/snap.json --plugin snapshot` | +| Create snapshot at block N | `snapshot-at-block = N` + `snapshot-dir = /path` | +| Create snapshot every N blocks | `snapshot-every-n-blocks = N` + `snapshot-dir = /path` | +| Bootstrap new node from file | `vizd --snapshot /path/snap.json --plugin snapshot` | +| Crash recovery | `vizd --replay-from-snapshot --snapshot-auto-latest --plugin snapshot` | +| P2P auto-bootstrap | `sync-snapshot-from-trusted-peer = true` + `trusted-snapshot-peer = host:8092` | + +--- + +## Enabling the Plugin + +```ini +plugin = snapshot +``` + +--- + +## Configuration Reference + +### CLI-only flags + +| Flag | Description | +|------|-------------| +| `--snapshot ` | Bootstrap from a snapshot file (DLT mode). Skipped if `shared_memory.bin` already exists or the file was already imported (renamed to `.used`). | +| `--snapshot-auto-latest` | Auto-discover the latest snapshot in `snapshot-dir` by parsing block numbers from filenames. Use with `--replay-from-snapshot`. Ignored if `--snapshot` is set. | +| `--replay-from-snapshot` | Crash recovery: always wipes shared memory, imports snapshot, replays `dlt_block_log`. Does not rename the snapshot file. Requires `--snapshot` or `--snapshot-auto-latest`. | +| `--auto-recover-from-snapshot` | (default: `true`) Automatic runtime recovery when shared memory corruption is detected — no restart required. Disable with `--no-auto-recover-from-snapshot`. | +| `--create-snapshot ` | Create a snapshot at the given path using the current database, then exit. | +| `--sync-snapshot-from-trusted-peer` | (default: `false`) Download snapshot from trusted peers when state is empty. Must be explicitly enabled. | + +### Config file options + +| Option | Default | Description | +|--------|---------|-------------| +| `snapshot-at-block` | `0` | Create a snapshot when this block number is reached (0 = disabled). | +| `snapshot-every-n-blocks` | `0` | Create a snapshot every N blocks (0 = disabled). Only fires on live blocks — skipped during P2P catchup. | +| `snapshot-dir` | — | Directory for auto-generated snapshots. Created automatically if absent. | +| `snapshot-max-age-days` | `90` | Delete snapshots older than N days after a new one is created (0 = disabled). | +| `allow-snapshot-serving` | `false` | Serve snapshots to other nodes over TCP. | +| `allow-snapshot-serving-only-trusted` | `false` | Restrict serving to trusted peers only. | +| `snapshot-serve-endpoint` | `0.0.0.0:8092` | TCP endpoint for the snapshot server. | +| `trusted-snapshot-peer` | — | Trusted peer address for P2P snapshot sync (repeatable). | +| `dlt-block-log-max-blocks` | `100000` | Rolling block log size in DLT mode (chain plugin option). 0 = disabled. | + +--- + +## Creating Snapshots + +### Method 1: One-shot (stop node, create file, exit) + +Stop the running node first, then: + +```bash +vizd --create-snapshot /data/snapshots/viz-snapshot.json --plugin snapshot +``` + +The node opens the existing database, replays if needed, writes the snapshot, and exits before P2P or validator plugins activate. + +### Method 2: Snapshot at a specific block (no downtime) + +```ini +plugin = snapshot +snapshot-at-block = 5000000 +snapshot-dir = /data/snapshots +``` + +When block 5,000,000 is applied, the snapshot is written to `/data/snapshots/snapshot-block-5000000.json` without stopping the node. + +### Method 3: Periodic snapshots (recommended for production) + +```ini +plugin = snapshot +snapshot-every-n-blocks = 28800 # ~24 hours at 3 s/block +snapshot-dir = /data/snapshots +snapshot-max-age-days = 90 +``` + +Files are named `snapshot-block-.json` automatically. Snapshot creation is asynchronous: + +- **Phase 1** (read lock, ~1 s): serialize all database objects into memory. +- **Phase 2** (no lock, ~2 s): compress, checksum, write to disk. + +Block processing is paused only during Phase 1; API and P2P reads are unaffected throughout. + +**Recommended intervals:** + +| Frequency | Blocks | Approx. time | +|-----------|--------|--------------| +| Frequent | 10,000 | ~8 hours | +| Daily | 28,800 | ~24 hours | +| Weekly | 100,000 | ~3.5 days | + +### Method 4: Combining at-block and periodic + +Both settings can be active simultaneously: + +```ini +snapshot-at-block = 5000000 +snapshot-every-n-blocks = 100000 +snapshot-dir = /data/snapshots +``` + +--- + +## Bootstrap: Loading from a Snapshot (DLT Mode) + +Transfer a snapshot file to the new node, then: + +```bash +vizd \ + --snapshot /data/snapshots/viz-snapshot.json \ + --plugin snapshot \ + --plugin p2p \ + --p2p-seed-node seed1.viz.media:2001 +``` + +The node loads state in seconds and begins P2P sync from the snapshot's block height. + +### What happens during load + +1. `chain::plugin_startup()` detects `--snapshot`. +2. Three safety checks (in order): shared memory already exists → skip; file not found (already `.used`) → skip; otherwise proceed. +3. Database opened via `open_from_snapshot()` (wipes and re-initializes chainbase). +4. Snapshot JSON validated (format version, chain ID, SHA-256 checksum), all 32 object types imported. +5. Snapshot file renamed to `.used` to prevent re-import on restart. +6. LIB promoted to `head_block_num` so P2P synopsis starts from the snapshot head. +7. Fork database seeded with the snapshot's head block. +8. P2P plugin starts and syncs blocks from `LIB + 1` onward. + +### Restart safety + +| Scenario | Result | +|----------|--------| +| First start (no `shared_memory.bin`, file present) | Imports snapshot, renames to `.used` | +| Restart (shared_memory exists) | Skips import — uses existing state | +| Restart (shared_memory wiped, file already `.used`) | Skips import — file not found | +| Force re-import | `--resync-blockchain` + a fresh snapshot file | + +No need to remove `--snapshot` from the command line or Docker `VIZD_EXTRA_OPTS` after the initial import. + +--- + +## Snapshot File Format + +The snapshot is a single JSON file: + +```json +{ + "header": { + "version": 1, + "chain_id": "...", + "snapshot_block_num": 12345678, + "snapshot_block_id": "...", + "snapshot_block_time": "2025-01-01T00:00:00", + "last_irreversible_block_num": 12345660, + "payload_checksum": "sha256...", + "object_counts": { "account": 50000, ... } + }, + "state": { + "dynamic_global_property": [ ... ], + "account": [ ... ], + ... + } +} +``` + +### 32 included object types + +**Critical (11)** — consensus-essential: +`dynamic_global_property`, `witness_schedule`, `hardfork_property`, `account`, `account_authority`, `validator`, `witness_vote`, `block_summary`, `content`, `content_vote`, `block_post_validation` + +**Important (15)** — needed for full operation: +`transaction`, `vesting_delegation`, `vesting_delegation_expiration`, `fix_vesting_delegation`, `withdraw_vesting_route`, `escrow`, `proposal`, `required_approval`, `committee_request`, `committee_vote`, `invite`, `award_shares_expire`, `paid_subscription`, `paid_subscribe`, `witness_penalty_expire` + +**Optional (5)** — metadata and recovery: +`content_type`, `account_metadata`, `master_authority_history`, `account_recovery_request`, `change_recovery_account_request` + +--- + +## DLT Rolling Block Log + +In DLT mode the main `block_log` stays empty. The `dlt_block_log` (files `dlt_block_log.log` + `dlt_block_log.index`) stores recent irreversible blocks for: + +- **P2P block serving** — peers can request recent blocks for fork resolution. +- **API access** — `get_block` works for blocks in the rolling window. + +```ini +dlt-block-log-max-blocks = 100000 # keep last ~3.5 days of blocks +``` + +When the log exceeds this size, old blocks are pruned from the front. The implementation tracks logical file sizes independently of the memory-mapped file to prevent stale-size bugs after thousands of resize cycles. + +--- + +## Crash Recovery: `--replay-from-snapshot` + +Use this when `shared_memory.bin` is corrupted (unclean shutdown, disk full, hardware fault). Normal `--replay-blockchain` is not available in DLT mode because `block_log` is empty. + +```bash +# Specify snapshot path explicitly +vizd --replay-from-snapshot --snapshot /data/snapshots/snapshot-block-79273800.json --plugin snapshot + +# Or auto-discover the latest snapshot +vizd --replay-from-snapshot --snapshot-auto-latest --plugin snapshot +``` + +Recovery steps: +1. Always wipes `shared_memory.bin` (assumes corruption). +2. Imports the snapshot state. +3. Replays `dlt_block_log` from `snapshot_head + 1` onward. +4. Emits `on_sync` — P2P fills the remaining gap to the live chain head. + +The snapshot file is **not** renamed to `.used` (it may be needed again). + +### Example recovery scenario + +A DLT node crashes at block 79,274,318 with `snapshot-every-n-blocks = 100000` and `dlt-block-log-max-blocks = 100000`: + +``` +/data/viz-snapshots/snapshot-block-79273800.json ← latest snapshot +/blockchain/dlt_block_log.* ← contains blocks 79174319..79274318 +/blockchain/shared_memory.bin ← CORRUPTED +``` + +```bash +vizd --replay-from-snapshot --snapshot-auto-latest --plugin snapshot +``` + +``` +Loading state from snapshot: .../snapshot-block-79273800.json (12.3 s) +Replaying dlt_block_log from block 79273801 to 79274318... + 100% 518 of 518 (block 79274318, elapsed 7.2 s) +Recovery complete. Started on blockchain with 79274318 blocks. +``` + +The node is now at block 79,274,318; P2P sync delivers the rest. + +--- + +## Automatic Runtime Recovery: `--auto-recover-from-snapshot` + +Enabled by default (`true`). When corruption is detected during block processing or generation at runtime, the node: + +1. Finds the latest snapshot in `snapshot-dir`. +2. Closes the database. +3. Wipes and re-imports using the same code path as `--replay-from-snapshot`. +4. Resumes P2P sync — no restart required. + +**Prerequisites:** `plugin = snapshot` enabled and snapshots present in `snapshot-dir`. + +To disable (for debugging): + +```bash +vizd --no-auto-recover-from-snapshot +``` + +--- + +## P2P Snapshot Sync + +Nodes can download snapshots from trusted peers over a custom TCP protocol, eliminating manual file transfers. + +### Snapshot server + +```ini +plugin = snapshot +allow-snapshot-serving = true +snapshot-serve-endpoint = 0.0.0.0:8092 +snapshot-every-n-blocks = 28800 +snapshot-dir = /data/viz-snapshots +``` + +### New node bootstrap + +```ini +plugin = snapshot +trusted-snapshot-peer = seed1.viz.media:8092 +trusted-snapshot-peer = seed2.viz.media:8092 +sync-snapshot-from-trusted-peer = true +``` + +When the node starts with 0 blocks and `sync-snapshot-from-trusted-peer = true`, it queries all trusted peers, selects the one with the highest snapshot, downloads it in 1 MB chunks, verifies the SHA-256 checksum, and imports — all before P2P or validator plugins activate. + +### Security + +- Downloads exceeding 2 GB are rejected. +- Checksum verified via streaming SHA-256 (never fully loaded into memory). +- Rate limiting, max 5 concurrent connections, 60-second per-connection deadline. +- `allow-snapshot-serving-only-trusted = true` restricts to the `trusted-snapshot-peer` list. + +--- + +## Docker + +Set `VIZD_EXTRA_OPTS` to pass snapshot flags: + +```bash +# Bootstrap from snapshot +docker run -e VIZD_EXTRA_OPTS="--snapshot /var/lib/vizd/snapshots/snap.json --plugin snapshot" ... + +# Crash recovery +docker run -e VIZD_EXTRA_OPTS="--replay-from-snapshot --snapshot-auto-latest --plugin snapshot" ... +``` + +Periodic snapshots via `config.ini` (no `VIZD_EXTRA_OPTS` needed): + +```ini +plugin = snapshot +snapshot-every-n-blocks = 28800 +snapshot-dir = /var/lib/vizd/snapshots +``` + +Snapshot files are accessible on the host at the mounted volume path. + +| Task | Method | +|------|--------| +| Periodic snapshots | `snapshot-every-n-blocks` in config | +| One-shot snapshot | `--create-snapshot` via `VIZD_EXTRA_OPTS` | +| Bootstrap new node | `--snapshot` via `VIZD_EXTRA_OPTS` | +| Crash recovery | `--replay-from-snapshot --snapshot-auto-latest` via `VIZD_EXTRA_OPTS` | +| Auto-recovery | Default — ensure `plugin = snapshot` and `snapshot-dir` set | +| P2P auto-bootstrap | `sync-snapshot-from-trusted-peer = true` + `trusted-snapshot-peer` in config | + +--- + +## Recommended Production Config + +```ini +plugin = snapshot + +# Snapshot every ~24 hours +snapshot-every-n-blocks = 28800 +snapshot-dir = /data/viz-snapshots +snapshot-max-age-days = 90 + +# DLT rolling block log: keep last ~3.5 days +dlt-block-log-max-blocks = 100000 + +shared-file-size = 4G +plugin = p2p +p2p-seed-node = seed1.viz.media:2001 +``` + +--- + +## Stale Snapshot Detection + +If a serving node's latest snapshot is older than the `dlt_block_log` start block, new nodes downloading the snapshot cannot sync the missing blocks. At startup the plugin detects this and automatically creates a fresh snapshot on the next live block — no manual intervention required. + +--- + +## Troubleshooting + +| Problem | Check | +|---------|-------| +| Node re-imports snapshot every restart | Snapshot file not renaming to `.used` — check write permissions on snapshot directory | +| `item_not_available` from peers | DLT block log may not cover the advertised blocks — verify `dlt-block-log-max-blocks` is large enough | +| P2P sync stalls after snapshot load | Check `[logger.sync]` in config; verify LIB was promoted to head after import | +| Snapshot creation fails | Check disk space in `snapshot-dir`; node continues running on failure | +| Auto-recovery fires unexpectedly | Check for disk errors; inspect logs for `shared_memory_corruption_exception` | +| P2P download rejected (>2 GB) | Snapshot too large — increase `dlt-block-log-max-blocks` on the serving node to reduce per-snapshot size | + +--- + +See also: [Snapshot Plugin](../plugins/snapshot.md) for the complete implementation reference, and [P2P Overview](../p2p/overview.md) for DLT sync protocol details. diff --git a/docs/node/validator-guard.md b/docs/node/validator-guard.md new file mode 100644 index 0000000000..d053f212ed --- /dev/null +++ b/docs/node/validator-guard.md @@ -0,0 +1,138 @@ +# Validator Guard + +The `witness_guard` plugin automates signing-key restoration for validator accounts. When a validator's signing key is reset to null (disabling block production), the plugin detects the change and broadcasts a `witness_update_operation` to restore the key — without manual intervention. + +--- + +## When to Use This Plugin + +- Your validator account can have its signing key nulled by an emergency consensus master, a security protocol, or manual action. +- Without this plugin you must monitor the on-chain key and restore it by hand before your scheduled slot. +- With this plugin the node watches for null keys every N blocks and restores them automatically. + +--- + +## Enabling the Plugin + +```ini +plugin = witness_guard +``` + +--- + +## Configuration + +| Option | Default | Description | +|--------|---------|-------------| +| `validator-guard-enabled` | `true` | Enable or disable the plugin globally. | +| `validator-guard-interval` | `20` | Check interval in blocks (~60 s at 3 s/block). | +| `validator-guard-validator` | — | JSON triplet `[name, signing_wif, active_wif]`. Repeatable. | +| `validator-guard-disable` | `5` | Consecutive blocks produced by a single validator before auto-disabling it. `0` = disabled. | + +The plugin also reads `enable-stale-production` from the validator plugin config. + +### Example + +```ini +plugin = witness_guard + +# Monitor one validator +validator-guard-validator = ["alice", "5K_SIGNING_WIF", "5K_ACTIVE_WIF"] + +# Monitor a second validator +validator-guard-validator = ["alice.backup", "5J_SIGNING_WIF", "5J_ACTIVE_WIF"] + +# Check every 10 blocks +validator-guard-interval = 10 +``` + +> **Security:** The active private key is stored in plain text in `config.ini`. Restrict file permissions (`chmod 600 config.ini`) and avoid exposing the file to untrusted processes. + +--- + +## How It Works + +### Startup + +1. Parses and validates all configured WIF keys. +2. If `enable-stale-production = true`, auto-restore starts disabled (see Safety Guards). +3. After the chain database opens, verifies each configured active key against on-chain authority. Validators whose accounts are not found or whose keys do not match are removed from monitoring with a warning. +4. Runs an immediate check; caches the result to align with the periodic schedule. + +### Per-Block Handler + +On every block: + +1. **Consecutive-block auto-disable**: If a monitored validator produced `validator-guard-disable` consecutive blocks, a `witness_update_operation` with a null key is broadcast to disable it, and the validator is flagged as auto-disabled. Any block by a *different* validator resets all consecutive counters. +2. **Transaction confirmation**: Scans for pending restore transaction IDs in the block. On match, marks the restore confirmed and clears tracking state. +3. **Look-ahead scheduling**: If any monitored validator is scheduled within the next 3 slots, triggers an immediate check so the key can be restored before the slot arrives. +4. **Periodic check**: Otherwise runs the core check every `validator-guard-interval` blocks. While the node is still catching up after startup, checks run every 10 blocks. + +### Core Check + +Each check (in order): + +1. **Stale production guard**: If `enable-stale-production` is active and network participation < 33%, skips all restoration. Auto-clears when participation reaches ≥ 33%. +2. **Sync check**: Skips if head block time is more than 2 block intervals behind wall clock. +3. **Long fork safety**: Skips if LIB is older than 200 seconds. +4. **Expiry cleanup**: Expires stale in-flight restore attempts so they can be retried. +5. **Key check per validator**: Reads the on-chain signing key. + - Key present → clears pending restore state and auto-disabled flag. + - Key null + validator was auto-disabled → skips auto-restore (operator must investigate). + - Key null + no restore in-flight → calls `send_witness_update`. + +### Restore Transaction + +1. Builds a `witness_update_operation` preserving the current on-chain URL and setting the signing key to the configured public key. +2. Wraps in a `signed_transaction` with 30-second expiration and current head block reference. +3. Signs with the configured active private key. +4. Broadcasts via P2P. +5. Tracks the transaction ID in `_pending_confirmations` to prevent duplicate sends. + +--- + +## Safety Guards + +| Guard | Behavior | +|-------|----------| +| **Stale production** | When `enable-stale-production = true`, auto-restore is disabled to avoid broadcasting on a minority fork. Auto-cleared when participation ≥ 33%. | +| **Emergency mode** | During emergency consensus, the stale production guard is bypassed — key restoration may be needed for recovery. | +| **Sync check** | Only runs when the node is synchronized. | +| **Long fork detection** | Skips if LIB is older than 200 seconds. | +| **Authority validation** | Active keys verified against on-chain authority at startup. | +| **Consecutive-block auto-disable** | Automatically nulls the signing key after N consecutive blocks from the same validator. Auto-restore suppressed until the operator manually fixes the key. | +| **Duplicate prevention** | In-flight restores tracked with expiration; no duplicate transactions sent. | + +--- + +## Log Messages + +| Message | Meaning | +|---------|---------| +| `monitoring validator 'alice' (signing key: VIZ...)` | Plugin started for this validator | +| `enable-stale-production detected — auto-restore is DISABLED` | Stale production mode active; restore suppressed | +| `network is healthy (XX%), auto-clearing stale production override` | Stale guard lifted | +| `'alice' has null signing key on-chain — initiating restore` | Null key detected, about to broadcast | +| `broadcasting witness_update [ID: ...] for 'alice' — restoring key to VIZ...` | Restore transaction sent | +| `CONFIRMED restoration for 'alice' in block #N` | Restore confirmed on-chain | +| `POTENTIAL LONG FORK DETECTED! LIB #N is Xs old. Skipping restoration.` | Restoration skipped due to stale LIB | +| `validator 'alice' produced N consecutive blocks — auto-disabling` | Consecutive-block threshold reached | +| `'alice' was auto-disabled (consecutive block limit), skipping auto-restore` | Auto-restore suppressed after auto-disable | +| `witness_update FAILED for 'alice': [error]` | Broadcast failed | + +--- + +## Troubleshooting + +| Problem | Check | +|---------|-------| +| Restore not triggering | Verify `validator-guard-enabled = true`; ensure node is synced; confirm account is a registered validator | +| Disabled when `enable-stale-production = true` | Expected — waits for network participation ≥ 33% | +| Transaction failed | Verify `active_wif` matches the account's active authority. Check for startup warning about mismatched keys | +| Config parse error | Each entry must be a valid 3-element JSON array: `["name", "signing_wif", "active_wif"]` | +| Validator auto-disabled and not restoring | Consecutive-block threshold was hit. Investigate the cause, manually restore the signing key on-chain; the auto-disabled flag clears once the key is detected as non-null | +| Authority warning at startup | `WARNING: Configured active key ... does NOT have authority on-chain` — update the key in config | + +--- + +See also: [Validator Node](./validator-node.md) for signing key setup and [Validator Plugin](../plugins/validator.md) for the production loop internals. diff --git a/docs/node/validator-node.md b/docs/node/validator-node.md new file mode 100644 index 0000000000..9e28ada060 --- /dev/null +++ b/docs/node/validator-node.md @@ -0,0 +1,235 @@ +# Running a Validator Node + +Validators (block producers) are accounts scheduled by the Fair-DPOS algorithm to produce blocks every 3 seconds. Running a validator node requires a registered validator account, a signing key, and a properly configured node. + +--- + +## Prerequisites + +1. A VIZ Ledger account registered as a validator via `validator_update_operation`. +2. The WIF private key corresponding to the signing key registered on-chain. +3. A synced full node (validator plugin requires chain + p2p + snapshot plugins). + +--- + +## Configuration + +Use `share/vizd/config/config_witness.ini` as the base template. + +Key settings: + +```ini +# P2P — allow public inbound connections for block propagation +p2p-endpoint = 0.0.0.0:2001 +p2p-seed-node = seed1.viz.media:2001 + +# RPC — bind to localhost for security (validators don't need public API) +webserver-http-endpoint = 127.0.0.1:8090 +webserver-ws-endpoint = 127.0.0.1:8091 + +# Required plugins for a validator +plugin = chain p2p webserver json_rpc database_api network_broadcast_api validator witness_api + +# Skip virtual-op indexing to save memory (validators don't need it) +skip-virtual-ops = true + +# Shared memory — 2G is enough for low-memory validator builds +shared-file-size = 2G + +# ─── Validator identity ─────────────────────────────────────── +# Your validator account name +validator = myvalidator + +# Your signing private key (WIF format) +private-key = 5JxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxWIF + +# Participation threshold — stop producing if network drops below this +required-participation = 33 + +# Do NOT enable this on mainnet (only for testnet/bootstrap) +# enable-stale-production = false +``` + +--- + +## NTP Synchronization + +Accurate time is critical for block production. The validator plugin maintains its own NTP client: + +```ini +# NTP servers (defaults are pool.ntp.org, time.google.com, time.cloudflare.com) +ntp-server = pool.ntp.org +ntp-server = time.cloudflare.com + +# Check NTP every 15 minutes +ntp-request-interval = 900 + +# Discard NTP replies with round-trip > 150ms +ntp-round-trip-threshold = 150 +``` + +Ensure the server OS clock is also synchronized (via `chrony` or `systemd-timesyncd`). + +--- + +## Starting the Node + +```bash +./vizd --config-file /data/vizd/config.ini --data-dir /data/vizd +``` + +### Docker + +```bash +docker run -d \ + --name vizd-validator \ + --restart unless-stopped \ + -p 2001:2001 \ + -v /data/vizd:/var/lib/vizd \ + -e VIZD_WITNESS=myvalidator \ + -e VIZD_PRIVATE_KEY=5Jxxx... \ + vizblockchain/vizd:lowmem +``` + +Use the `lowmem` image for validators — it excludes unnecessary indexing plugins. + +--- + +## Registering / Updating Your Validator + +Use `cli_wallet` or any compatible wallet to broadcast a `validator_update_operation`: + +```json +{ + "type": "validator_update_operation", + "value": { + "owner": "myvalidator", + "url": "https://mysite.example/validator", + "block_signing_key": "VIZ5hqSa...", + "props": [3, { + "account_creation_fee": "1.000 VIZ", + "maximum_block_size": 65536 + }] + } +} +``` + +The `block_signing_key` must match the `private-key` configured in the node. + +To disable your validator (remove from schedule), set `block_signing_key` to the null key: + +```json +"block_signing_key": "VIZ1111111111111111111111111111111114T1Anm" +``` + +--- + +## Production Loop: What the Node Does + +The validator plugin runs a 250ms production timer on a **dedicated thread** (isolated from P2P I/O). On each tick it calls `maybe_produce_block()` which checks (in order): + +1. **Sync gate** (DLT mode): Not producing if catching up from peers. +2. **Snapshot gate**: Not producing if snapshot creation is in progress. +3. **Participation check**: Network must have ≥33% validator participation. +4. **Slot assignment**: Is this node's validator scheduled for the current slot? +5. **Key check**: Does the node have the correct private key? +6. **Minority fork detection**: If the last 21 blocks are all from this node's validators — rollback and resync. +7. **Fork collision resolution**: If another block exists at the same height, apply vote-weight comparison. +8. **Lag check**: If the node is >500ms past the slot boundary — skip. +9. **Generate and broadcast** the block. + +See [Validator Plugin](../plugins/validator.md) for the complete execution flow. + +--- + +## Production Results (Log Messages) + +| Result | Meaning | +|--------|---------| +| `produced` | Block produced and broadcast successfully | +| `not_synced` | Node still catching up or snapshot in progress | +| `not_time_yet` | No slot available or NTP drift | +| `not_my_turn` | Another validator is scheduled for this slot | +| `no_private_key` | Configured validator scheduled but private key missing | +| `low_participation` | Network participation below threshold | +| `lag` | Woke up >500ms past slot — slot missed | +| `fork_collision` | Competing block at next height — waiting | +| `minority_fork` | Node is on isolated fork — rolling back | + +--- + +## Safety Mechanisms + +### Network Partition Guard +If fewer than 33% of validators are participating, production stops to prevent split-brain scenarios. Override with `enable-stale-production = true` only for bootstrap/testnet. + +### Minority Fork Detection +If the node's fork database shows 21+ consecutive blocks all from this node's own validators, it automatically rolls back to LIB and resyncs. This catches network isolation. + +### Production Watchdog +If no block has been produced for 180 seconds (60s for emergency master) while `should_be_producing` is true, the watchdog automatically clears stuck flags (`minority_fork_recovering`, P2P catchup, chain syncing) and attempts to resume. + +### Snapshot Safety +Block production is paused while a snapshot is being created to avoid write-lock conflicts. + +--- + +## Monitoring + +Watch for these log patterns: + +``` +# Good: block produced +produced block #123456 ... validator=myvalidator + +# Warning: missed slot +MISSED-SLOT-OUR-validator: ... + +# Warning: minority fork +MINORITY FORK DETECTED: rolling back to LIB + +# Warning: watchdog fires +WATCHDOG: no production for 180s, clearing flags +``` + +Also see [Monitoring](./monitoring.md) and [Validator Guard](./validator-guard.md) for automated alerting. + +--- + +## Multiple Validators on One Node + +The `validator` and `private-key` options are repeatable: + +```ini +validator = alice +validator = alice.backup +private-key = 5Jxxx... # Alice's key +private-key = 5Jyyy... # Alice.backup's key +``` + +The node will produce blocks for any of the configured validators when scheduled. + +--- + +## Emergency Consensus Key + +For nodes involved in emergency consensus recovery: + +```ini +emergency-private-key = 5Jzzz... # Committee emergency key +``` + +When set, the node automatically adds `CHAIN_EMERGENCY_WITNESS_ACCOUNT` to its validator set and participates in emergency block production. See [Emergency Consensus](../consensus/emergency-consensus.md). + +--- + +## Troubleshooting + +| Problem | Check | +|---------|-------| +| Not producing | Verify `validator` and `private-key` in config; check signing key registered on-chain matches config | +| `no_private_key` in logs | On-chain signing key doesn't match any `private-key` in config | +| `low_participation` | Network health issue — check peer count and other validators | +| `minority_fork` | Network isolation — verify connectivity to seed nodes | +| NTP stall warnings | Check OS NTP sync: `chronyc tracking` or `timedatectl` | +| Slot hijacks | Signing key may have been blanked by emergency master; restore via `validator_update_operation` | diff --git a/docs/p2p/forward-mode.md b/docs/p2p/forward-mode.md new file mode 100644 index 0000000000..1b4f8d7a06 --- /dev/null +++ b/docs/p2p/forward-mode.md @@ -0,0 +1,214 @@ +# Forward Mode — Block and Transaction Exchange + +Forward mode (`DLT_NODE_STATUS_FORWARD`) is the normal operating state once a node has caught up with the network. Instead of pulling block ranges from peers, the node **pushes** new blocks and transactions to all fork-aligned peers as they arrive. + +--- + +## Delivery Gate: `exchange_enabled` + +All forward-mode traffic is filtered by two flags per peer: + +| Flag | What it means | +|------|--------------| +| `exchange_enabled == true` | Peer is fork-aligned — its head block is known to us (or ours to it) | +| `lifecycle_state == ACTIVE` | Peer has completed handshake | + +Both must be true for a peer to receive block and transaction broadcasts. + +The central broadcast function, `send_to_all_our_fork_peers()`, iterates all connected peers and skips those that fail either check. + +--- + +## Setting `exchange_enabled` + +### Initial setting (hello handshake) + +During hello, the acceptor calls `check_fork_alignment()` — a multi-tier DLT-range-aware check: + +| Case | Check | +|------|-------| +| Peer has no blocks (`head_num == 0`) | → aligned | +| Peer head in our DLT range | `is_block_known(peer.head_id)` | +| Peer head + 1 == our DLT earliest | Read our earliest block; verify `previous == peer.head_id` | +| LIB fallback | `is_block_known(peer.lib_id)` | + +If any check passes → `exchange_enabled = true`. + +### OR combination + +Both sides send hello messages to each other and each independently computes `exchange_enabled`. The final value for a peer is the **logical OR** of both sides' determinations. If either side recognizes the other's chain, exchange is enabled. + +A slave whose head is behind the master's DLT range fails its own `check_fork_alignment` (it hasn't yet applied the master's blocks), but the master's check succeeds (it knows the slave's head). The OR ensures exchange is enabled even in this asymmetric case. + +### Re-evaluation triggers + +`exchange_enabled` is re-evaluated whenever the node's knowledge of peer blocks changes: + +| Trigger | When | +|---------|------| +| `transition_to_forward()` | On every peer with `exchange_enabled=false`; re-checks `is_block_known(peer_head_id)` | +| `on_dlt_fork_status()` | Peer transitions SYNC → FORWARD; re-checks fork alignment | +| Block accepted from peer | If block applies to our chain, immediately enables exchange for that peer | + +--- + +## Block Broadcasting + +### Self-produced blocks + +When a validator produces a block: + +``` +validator.cpp → p2p_plugin.broadcast_block(block) + → dlt_p2p_node.broadcast_block(block) + → send_to_all_our_fork_peers(dlt_block_reply_message, exclude=none, block_id=block.id()) +``` + +The block is sent to **all** ACTIVE exchange-enabled peers. Echo suppression prevents re-sending to peers that already have the block. + +### Relaying received blocks + +When a block arrives from peer X: + +1. Record that X has this block (`state.record_known_block(block.id())`). +2. Apply the block to the chain. +3. `send_to_all_our_fork_peers(block_reply, exclude=X, block_id=block.id())` — sends to all other exchange-enabled ACTIVE peers. + +--- + +## Block Echo Suppression + +Without suppression, blocks loop back to their producer through relay chains: + +``` +A produces block N → sends to B, C +B relays N to A, C +C relays N to A, B +A receives its own block N back from B and C — wasted bandwidth +``` + +Each peer state maintains a **ring buffer of 20 recent block IDs** (`known_blocks`). Before sending a block to a peer, the node checks `peer.has_block(block_id)`. If already known, the send is skipped. + +A peer is recorded as "having" a block in two cases: +- **We just sent it to them** — recorded in `send_to_all_our_fork_peers` after the send. +- **They sent it to us** — recorded in `on_dlt_block_reply` on receipt. + +The relay log shows echo-filtered counts: +``` +Relay block_reply to 3 peers (0 skipped: no_exchange, 0 skipped: not_active, 1 skipped: echo) +``` + +--- + +## Transaction Broadcasting + +### Self-originated (via API) + +Transaction submitted via `network_broadcast_api` → added to P2P mempool → `dlt_transaction_message` sent to all exchange-enabled ACTIVE peers. + +### Relaying received transactions + +Transaction arrives from peer X → added to mempool → relayed to all exchange-enabled ACTIVE peers **except X**. + +### Mempool pre-filter + +Before a transaction is accepted into the mempool or forwarded, it must pass: + +| Check | Failure | +|-------|---------| +| Duplicate (`trx_id` already in mempool) | Silently skip | +| Expired (`expiration < now`) | Reject; increment spam strike if from peer | +| Expiration too far (>24 h in future) | Reject; increment spam strike | +| Oversized (>`dlt-mempool-max-tx-size`, default 64 KB) | Reject; increment spam strike | +| TaPoS invalid (reference block unknown) | Reject; increment spam strike | +| Mempool full | Evict oldest-expiry entry, then add | + +**Provisional entries:** Transactions received during SYNC mode are tagged `is_provisional = true` — stored locally but not forwarded to peers. On transition to FORWARD, provisional entries are revalidated against the current head and invalid ones are purged. + +--- + +## SYNC → FORWARD Transition + +### Triggers + +| Trigger | Condition | +|---------|-----------| +| Block range reply with `is_last=true` | AND at least one block was applied (not all dead-fork) | +| `check_sync_catchup()` | `our_head >= all active peer heads` AND at least one active peer | +| Stagnation timeout | 30 s without a block, 3 retries exhausted | + +`check_sync_catchup()` runs after each block acceptance and every 5 seconds from the periodic task. + +**Isolation guard:** `check_sync_catchup()` does NOT claim caught up when zero active peers exist. Instead it starts a 60-second isolation timer; after expiry, `emergency_peer_reset()` fires (see below). + +### Actions on transition + +1. Notify all connected peers: broadcast `dlt_fork_status_message` with `node_status=FORWARD` to every active/syncing peer (not just exchange-enabled ones). This lets peers re-evaluate `exchange_enabled` for us immediately. +2. Re-evaluate `exchange_enabled` for all peers. +3. Revalidate and purge invalid provisional mempool entries. +4. Reset `_sync_stagnation_retries = 0`. +5. Reset `_last_block_received_time = now` so the forward stagnation timer starts fresh. + +--- + +## FORWARD → SYNC Fallback + +If blocks stop arriving in FORWARD mode, the node falls back to SYNC: + +| Trigger | Condition | +|---------|-----------| +| Hello reply shows peer far ahead | `peer_head_num > our_head + 2` on receiving hello_reply | +| Periodic check | `check_forward_behind()`: any active peer has `peer_head_num > our_head + 2` (skipped for 15 s after entering FORWARD) | +| Stagnation | `check_forward_stagnation()`: head stuck for 30 s AND at least one peer is ahead | + +**No-op when no peer is ahead:** `check_forward_stagnation()` does NOT transition to SYNC when all connected peers have the same head. There is nothing to sync from; transitioning would just cause oscillation. The stagnation timer resets and the node stays in FORWARD. + +On transition to SYNC, `_last_block_received_time` is reset to `now` so the sync stagnation timer starts fresh (not inherited from the FORWARD phase). + +--- + +## Peer Isolation Recovery + +When all peers are disconnected or banned (e.g., after a snapshot pause), the normal SYNC/FORWARD mode transitions loop pointlessly. After **60 seconds** with zero active connections: + +`emergency_peer_reset()`: +1. Moves all BANNED peers back to DISCONNECTED state; clears `spam_strikes`. +2. Resets all DISCONNECTED peers' backoffs to 30 s (`INITIAL_RECONNECT_BACKOFF_SEC`) with `next_reconnect_attempt = now`. +3. Clears stagnation retry counters. +4. On the next periodic task tick (~5 s), `periodic_reconnect_check()` immediately reconnects. + +--- + +## What Is Not Forwarded + +| Scenario | Traffic | +|----------|---------| +| Peer has `exchange_enabled=false` | No blocks, no transactions | +| Node is in SYNC mode | No broadcasts; only range requests and gap fill requests | +| Block processing paused (`_block_processing_paused=true`) | Blocks are received and queued but periodic DB-accessing tasks are skipped | + +--- + +## Delivery Summary + +| Event | Recipients | Excluded | Echo-filtered | +|-------|-----------|----------|--------------| +| Node produces block | All ACTIVE `exchange_enabled=true` peers | (none) | Peers with block in `known_blocks` | +| Node receives block from X | All ACTIVE `exchange_enabled=true` peers | X | Peers with block in `known_blocks` | +| Node originates transaction | All ACTIVE `exchange_enabled=true` peers | (none) | (none) | +| Node receives transaction from X | All ACTIVE `exchange_enabled=true` peers | X | (none) | + +--- + +## `peer_head_num` Is a Stale Snapshot + +The `peer_head_num` shown in [stats](./stats-reference.md) is updated from: +- hello handshake +- `dlt_fork_status_message` exchanges +- Block relay (receiving block N implies `peer_head_num ≥ N`) + +Between these events the peer's actual chain head may be significantly higher. Do not treat `peer_head_num` as real-time. + +--- + +See also: [P2P Overview](./overview.md), [Sync Scenarios](./sync-scenarios.md), [Stats Reference](./stats-reference.md), [Messages](./messages.md). diff --git a/docs/p2p/messages.md b/docs/p2p/messages.md new file mode 100644 index 0000000000..0dad5c4bcd --- /dev/null +++ b/docs/p2p/messages.md @@ -0,0 +1,459 @@ +# P2P Message Reference + +DLT P2P uses a binary protocol over raw TCP. Each message is framed with a 4-byte little-endian header containing the message type ID, followed by a length-prefixed data payload serialized with FC reflection. + +**Header file:** [libraries/network/include/graphene/network/dlt_p2p_messages.hpp](../../libraries/network/include/graphene/network/dlt_p2p_messages.hpp) + +--- + +## Message Type Summary + +| Type ID | Name | Direction | Purpose | +|---------|------|-----------|---------| +| 5100 | `dlt_hello_message` | initiator → acceptor | Initial handshake — chain state and capabilities | +| 5101 | `dlt_hello_reply_message` | acceptor → initiator | Handshake reply — fork alignment, exchange status | +| 5102 | `dlt_range_request_message` | any | Ask peer if it has a specific block | +| 5103 | `dlt_range_reply_message` | any | Response: block availability range | +| 5104 | `dlt_get_block_range_message` | syncing → peer | Request a range of blocks (bulk sync) | +| 5105 | `dlt_block_range_reply_message` | peer → syncing | Bulk block delivery | +| 5106 | `dlt_get_block_message` | any | Request a single block | +| 5107 | `dlt_block_reply_message` | any | Single block delivery | +| 5108 | `dlt_not_available_message` | any | Requested block not available | +| 5109 | `dlt_fork_status_message` | any | Live chain state update (head, LIB, fork, DLT range) | +| 5110 | `dlt_peer_exchange_request` | any | Request peer address list | +| 5111 | `dlt_peer_exchange_reply` | any | Peer address list response | +| 5112 | `dlt_peer_exchange_rate_limited` | any | Rate-limit response to peer exchange request | +| 5113 | `dlt_transaction_message` | any | Transaction broadcast | +| 5114 | `dlt_soft_ban_message` | any → banned | Soft-ban notification before disconnect | +| 5115 | `dlt_gap_fill_request` | any | Request specific blocks to fill a gap | +| 5116 | `dlt_gap_fill_reply` | any | Gap fill block delivery | + +--- + +## Enumerations + +### `dlt_node_status` + +| Value | Meaning | +|-------|---------| +| `DLT_NODE_STATUS_SYNC` (0) | Node is behind; actively pulling blocks from peers | +| `DLT_NODE_STATUS_FORWARD` (1) | Node is caught up; exchanges blocks via broadcast | + +### `dlt_fork_status` + +| Value | Meaning | +|-------|---------| +| `DLT_FORK_STATUS_NORMAL` (0) | On the majority fork | +| `DLT_FORK_STATUS_LOOKING_RESOLUTION` (1) | Fork detected; running resolution algorithm | +| `DLT_FORK_STATUS_MINORITY` (2) | Confirmed on a minority fork | + +### `dlt_peer_lifecycle_state` + +| Value | Meaning | +|-------|---------| +| `DLT_PEER_LIFECYCLE_CONNECTING` (0) | TCP connect in progress (5 s timeout) | +| `DLT_PEER_LIFECYCLE_HANDSHAKING` (1) | Hello exchange in progress (10 s timeout) | +| `DLT_PEER_LIFECYCLE_SYNCING` (2) | Exchanging sync blocks (`we_need_sync_items` or peer needs blocks from us) | +| `DLT_PEER_LIFECYCLE_ACTIVE` (3) | Fully synchronized; normal block/transaction exchange | +| `DLT_PEER_LIFECYCLE_DISCONNECTED` (4) | Not connected; eligible for reconnect after backoff | +| `DLT_PEER_LIFECYCLE_BANNED` (5) | Soft-banned; no reconnect until ban expires | + +--- + +## Detailed Message Reference + +### 5100 — `dlt_hello_message` + +Sent immediately after TCP connection is established. Carries the initiating node's complete chain state and capabilities. + +```cpp +struct dlt_hello_message { + uint16_t protocol_version; // currently 1 + block_id_type head_block_id; + uint32_t head_block_num; + block_id_type lib_block_id; + uint32_t lib_block_num; + uint32_t dlt_earliest_block; // oldest block in our rolling DLT log (0 if none) + uint32_t dlt_latest_block; // most recent block in our DLT log + bool emergency_active; // emergency consensus is currently active + bool has_emergency_key; // we hold the emergency committee private key + uint8_t fork_status; // dlt_fork_status enum + uint8_t node_status; // dlt_node_status enum (SYNC or FORWARD) +}; +``` + +**Notes:** +- `dlt_earliest_block` is critical for DLT-range-aware fork alignment. The acceptor uses it to avoid requesting blocks that are no longer in the initiator's rolling window. +- `has_emergency_key` identifies the emergency master node. Other nodes may prioritize syncing from this peer during emergency consensus mode. + +--- + +### 5101 — `dlt_hello_reply_message` + +Sent by the acceptor in response to 5100. Completes the handshake. + +```cpp +struct dlt_hello_reply_message { + bool exchange_enabled; // true if we consider the initiator caught up + bool fork_alignment; // true if the initiator is on the same fork + block_id_type initiator_head_seen; // echo: initiator's head_block_id as we see it + block_id_type initiator_lib_seen; // echo: initiator's lib_block_id as we see it + uint32_t our_dlt_earliest; // our earliest DLT block + uint32_t our_dlt_latest; // our latest DLT block + uint8_t our_fork_status; // dlt_fork_status enum + uint8_t our_node_status; // dlt_node_status enum +}; +``` + +**Fork alignment check is multi-tiered** to handle DLT-pruned block ranges: + +| Case | Check performed | +|------|----------------| +| Peer has no blocks (`head_num == 0`) | → aligned | +| Peer head is in our DLT range | `is_block_known(peer.head_block_id)` | +| Peer head + 1 == our earliest | Read `our_earliest_block.previous == peer.head_block_id` | +| Fallback | `is_block_known(peer.lib_block_id)` | + +**`exchange_enabled`** is `true` when the acceptor's fork_db contains the initiator's head block (i.e., the initiator is within the exchange window and on the same fork). Only exchange-enabled peers receive block and transaction broadcasts. + +--- + +### 5102 — `dlt_range_request_message` + +Asks a peer whether it has a specific block by number and/or ID. + +```cpp +struct dlt_range_request_message { + uint32_t block_num; + block_id_type block_id; // hash of the block being asked about +}; +``` + +--- + +### 5103 — `dlt_range_reply_message` + +Response to 5102. Returns the serving range available from the peer. + +```cpp +struct dlt_range_reply_message { + uint32_t range_start; // earliest block the peer can serve + uint32_t range_end; // latest block the peer can serve + bool has_blocks; // false if the peer has no blocks at all +}; +``` + +--- + +### 5104 — `dlt_get_block_range_message` + +Requests a contiguous range of blocks during SYNC mode. Maximum 200 blocks per request. + +```cpp +struct dlt_get_block_range_message { + uint32_t start_block_num; + uint32_t end_block_num; + block_id_type prev_block_id; // hash of block (start_block_num - 1); used to verify chain continuity +}; +``` + +**Notes:** +- The serving peer validates that `blocks[0].previous == prev_block_id` before sending. +- The gap between `start_block_num` and the serving peer's `dlt_earliest_block` may require a bridging peer (see [P2P Overview — Gap Detection](./overview.md#sync-mode)). + +--- + +### 5105 — `dlt_block_range_reply_message` + +Response to 5104. Contains up to 200 blocks. + +```cpp +struct dlt_block_range_reply_message { + std::vector blocks; + uint32_t last_block_next_available; // next available block after this batch + bool is_last; // true if no more blocks are available on this peer +}; +``` + +**`is_last = true`** triggers `transition_to_forward()` on the receiving side if the node has caught up. + +--- + +### 5106 — `dlt_get_block_message` + +Requests a single block by number. + +```cpp +struct dlt_get_block_message { + uint32_t block_num; + block_id_type prev_block_id; // hash of (block_num - 1) for chain link verification +}; +``` + +--- + +### 5107 — `dlt_block_reply_message` + +Response to 5106. + +```cpp +struct dlt_block_reply_message { + signed_block block; + uint32_t next_available; // next block number the peer can serve (0 if at head) + bool is_last; // true if this is the peer's head block +}; +``` + +--- + +### 5108 — `dlt_not_available_message` + +Sent when the peer cannot serve a requested block (block outside DLT log range, or block unknown). + +```cpp +struct dlt_not_available_message { + uint32_t block_num; +}; +``` + +The requesting node should look for another peer with the block in range or trigger gap fill / SYNC mode. + +--- + +### 5109 — `dlt_fork_status_message` + +Live chain state update. Sent when a node's head, LIB, DLT window, or fork status changes, and on SYNC → FORWARD transition. + +```cpp +struct dlt_fork_status_message { + uint8_t fork_status; // dlt_fork_status enum + block_id_type head_block_id; + uint32_t head_block_num; + block_id_type lib_block_id; + uint32_t lib_block_num; + uint32_t dlt_earliest_block; + uint32_t dlt_latest_block; + uint8_t node_status; // dlt_node_status enum +}; +``` + +**Key use case:** When a node transitions SYNC → FORWARD, it broadcasts this message to all connected peers so they can re-evaluate `exchange_enabled` for this node immediately rather than waiting for the next hello cycle. + +The receiving node updates its local `dlt_peer_state` for the sender and re-checks fork alignment + exchange eligibility. + +--- + +### 5110 — `dlt_peer_exchange_request` + +Empty message requesting a list of known peers. + +```cpp +struct dlt_peer_exchange_request { + // empty +}; +``` + +Rate limit: **3 requests per 5-minute sliding window** per peer. Violators receive `dlt_peer_exchange_rate_limited` (5112). + +--- + +### 5111 — `dlt_peer_exchange_reply` + +Response to 5110. Contains endpoint information for known peers. + +```cpp +struct dlt_peer_endpoint_info { + fc::ip::endpoint endpoint; + node_id_t node_id; +}; + +struct dlt_peer_exchange_reply { + std::vector peers; +}; +``` + +**Filters applied before inclusion in the reply:** +- Peer uptime ≥ `dlt-peer-exchange-min-uptime-sec` (default 600 s) +- Maximum `dlt-peer-exchange-max-per-subnet` (default 2) peers per /24 subnet +- `is_incoming` peers excluded (ephemeral source ports) +- Maximum `dlt-peer-exchange-max-per-reply` (default 10) peers total + +--- + +### 5112 — `dlt_peer_exchange_rate_limited` + +Sent instead of 5111 when the request rate limit is exceeded. + +```cpp +struct dlt_peer_exchange_rate_limited { + uint32_t wait_seconds; // how long the requester should wait before retrying +}; +``` + +--- + +### 5113 — `dlt_transaction_message` + +Carries a signed transaction for mempool propagation. + +```cpp +struct dlt_transaction_message { + signed_transaction trx; +}; +``` + +Received transactions are added to the P2P mempool (filtered by expiry, TaPoS, size) before being pushed to the chain's `_pending_tx`. Successfully accepted transactions are relayed to all exchange-enabled peers. + +--- + +### 5114 — `dlt_soft_ban_message` + +Sent to a peer just before the connection is closed due to spam or protocol violation. The receiving peer enters BANNED state for `ban_duration_sec` and will not attempt to reconnect until the ban expires. + +```cpp +struct dlt_soft_ban_message { + uint32_t ban_duration_sec; // ban duration in seconds + std::string reason; // human-readable reason +}; +``` + +Common reasons logged (color-coded orange/yellow on both sides): +- `"spam_strikes_exceeded"` — 10 invalid-packet strikes +- `"dead_fork_blocks"` — peer repeatedly sent blocks from a dead fork +- `"protocol_violation"` — unexpected message type or malformed data + +--- + +### 5115 — `dlt_gap_fill_request` + +Requests specific blocks to fill a gap detected in the node's block stream. Works in both SYNC and FORWARD modes. + +```cpp +struct dlt_gap_fill_request { + std::vector block_nums; // specific block numbers requested +}; +``` + +**Constraints:** +- Maximum **100 blocks** per request (`GAP_FILL_MAX_BLOCKS`). +- **5-second cooldown** between requests from the same node (`GAP_FILL_COOLDOWN_SEC`). +- Larger gaps are requested in 100-block chunks; subsequent chunks are triggered on the next periodic task cycle. +- The serving peer reads blocks from its `dlt_block_log`; block numbers outside the serving range result in 5108. +- Serving accepts requests from any active peer (not only exchange-enabled ones). + +Triggered from three locations: +1. `on_dlt_block_reply()` — out-of-order block detected +2. `periodic_task()` — proactive every-5s gap check +3. `resume_block_processing()` — after snapshot pause completes + +--- + +### 5116 — `dlt_gap_fill_reply` + +Response to 5115. Contains the requested blocks (may be a subset if some were unavailable). + +```cpp +struct dlt_gap_fill_reply { + std::vector blocks; // requested blocks; may be partial +}; +``` + +--- + +## Handshake Flow + +``` +Initiator Acceptor + │ │ + │──TCP connect──────────────────────►│ + │ │ + │ 5100 dlt_hello_message │ + │──────────────────────────────────►│ + │ (head/lib, DLT range, │ + │ emergency_active, node_status) │ + │ │ check fork alignment + │ │ set exchange_enabled + │ 5101 dlt_hello_reply_message │ + │◄──────────────────────────────────│ + │ (exchange_enabled, fork_alignment, │ + │ our DLT range, our node_status) │ + │ │ + ├── exchange_enabled = true ──────────┼── FORWARD or sync exchange begins + └── exchange_enabled = false ─────────┴── initiator enters SYNC mode +``` + +--- + +## Sync Mode Flow + +``` +Syncing Node Serving Peer + │ │ + │ 5104 dlt_get_block_range │ + │ (start=our_head+1, end=+200, │ + │ prev_block_id=our_head_id) │ + │──────────────────────────────────►│ + │ │ reads dlt_block_log + │ 5105 dlt_block_range_reply │ + │◄──────────────────────────────────│ + │ (blocks=[N+1..N+200], is_last) │ + │ │ + │ apply each block │ + │ if is_last → transition_to_forward │ + │ else → request next batch │ +``` + +If a gap exists between `our_head + 1` and the serving peer's `dlt_earliest_block`, the node searches for a bridging peer before requesting. + +--- + +## Forward Mode Broadcast + +``` +block producer Peer A Peer B (exchange-enabled) + │ │ │ + │ produce block │ │ + │ ─5109 fork_status──────►│ │ + │ (via transition notify) │ │ + │ │ │ + │ ─block broadcast ───────►│ (exchange-enabled) │ + │ │ ─block broadcast ─────►│ + │ │ │ push_block() + │ │ │ ─5109 fork_status─► peers +``` + +Only exchange-enabled peers in `DLT_FORK_STATUS_NORMAL` or `DLT_FORK_STATUS_LOOKING_RESOLUTION` receive block broadcasts. + +--- + +## Gap Fill Flow + +``` +Node (FORWARD, gap detected) Peer (has gap blocks) + │ │ + │ 5115 dlt_gap_fill_request │ + │ (block_nums=[N+1, N+2, N+3]) │ + │──────────────────────────────────►│ + │ │ reads dlt_block_log + │ 5116 dlt_gap_fill_reply │ + │◄──────────────────────────────────│ + │ (blocks=[N+1, N+2, N+3]) │ + │ │ + │ apply blocks → head advances │ +``` + +--- + +## Wire Format + +Each message is written as: + +``` +[ 4 bytes: type ID (uint32 LE) ][ 4 bytes: payload length (uint32 LE) ][ N bytes: FC-serialized payload ] +``` + +Reading uses `fc::tcp_socket::readsome()` / `writesome()` (non-blocking, fiber-yielding). There is no encryption layer — all peers on the same chain share a common network identity and messages are not signed per-message. + +--- + +See also: [P2P Overview](./overview.md), [Sync Scenarios](./sync-scenarios.md), [Stats Reference](./stats-reference.md). diff --git a/docs/p2p/overview.md b/docs/p2p/overview.md new file mode 100644 index 0000000000..6e849c2437 --- /dev/null +++ b/docs/p2p/overview.md @@ -0,0 +1,287 @@ +# P2P Network Overview + +VIZ Ledger uses a custom DLT P2P protocol that replaced the legacy graphene synopsis-based networking layer. The new design is optimized for DLT mode (snapshot-based nodes with rolling block logs) and removes the complex graphene ancestry synopsis in favour of a simpler range-based block exchange. + +--- + +## Architecture + +``` +┌─────────────────────────────────────────────────────────┐ +│ p2p_plugin (AppBase plugin) │ +│ └─ dlt_delegate (implements dlt_p2p_delegate) │ +│ └─ bridges chain state: db(), fork_db, block_log │ +├─────────────────────────────────────────────────────────┤ +│ dlt_p2p_node │ +│ ├─ accept loop (incoming TCP connections) │ +│ ├─ periodic task (5s tick: reconnect, stats, gaps) │ +│ └─ dlt_peer_state × N (one per connected peer) │ +├─────────────────────────────────────────────────────────┤ +│ Wire format: raw TCP, header (type + length) + data │ +│ Fiber model: all I/O on one fc::thread, cooperative │ +└─────────────────────────────────────────────────────────┘ +``` + +### Design decisions + +| Decision | Rationale | +|----------|-----------| +| Delegate pattern | `dlt_p2p_node` links only `fc` + `graphene_protocol`. Direct chain access is exposed via `dlt_p2p_delegate` to avoid circular dependency. | +| Raw TCP (no STCP encryption) | DLT emergency mode flips all validators simultaneously — no backward-compatible encryption needed. Simpler wire protocol. | +| Cooperative fibers (fc::thread) | All I/O uses `readsome()`/`writesome()` which yield the fiber. Multiple peers on one thread without mutexes. | +| Separate P2P mempool | Chain's `_pending_tx` only takes effect after acceptance. The P2P mempool filters by expiry, TaPoS, and size before pushing to chain, reducing wasted evaluation. | +| In-place plugin replacement | Plugin name is still `"p2p"`, port still `2001`/`4243`, public API unchanged. Old and new protocols are incompatible; dual-mode creates isolated sub-networks. | + +--- + +## Peer Lifecycle + +Each peer connection goes through the following states: + +``` +CONNECTING ──(TCP established)──► HANDSHAKING + 5s timeout ↓ ↓ 10s timeout + DISCONNECTED hello/hello_reply + ▲ ↓ + │ SYNCING ──(caught up)──► ACTIVE + │ │ + └──(disconnect/error)──────────────────┘ + │ + BANNED ◄──(spam_strikes ≥ 10)──────────┘ +``` + +**Timeout values:** +- Connecting → DISCONNECTED: **5 seconds** +- Handshaking → DISCONNECTED: **10 seconds** + +**Reconnection backoff:** 30 s → 60 s → … → 3600 s with ±25% jitter. Backoff resets after a stable connection of >5 minutes. Peers with no response for 8 hours are permanently removed. + +**Emergency peer reset:** If all peers are isolated (zero active connections) for 60 seconds, `emergency_peer_reset()` clears all soft bans and resets all backoffs to their initial value with immediate reconnect attempts. + +--- + +## Hello Handshake + +On connection the initiating peer sends `dlt_hello_message` containing: + +- `head_block_num` / `head_block_id` +- `lib_block_num` / `lib_block_id` +- `dlt_earliest_block_num` — oldest block available in the peer's rolling DLT block log +- `node_status` — SYNC or FORWARD + +The receiving peer responds with `dlt_hello_reply_message` containing: + +- `fork_alignment` — whether the blocks overlap on the same fork +- `exchange_enabled` — whether the responding peer considers the sender caught up + +### Fork alignment check (DLT-range-aware) + +Because DLT nodes prune old blocks, naive head-ID comparison would falsely flag same-chain peers as "different fork". The check is multi-tiered: + +| Case | Check | +|------|-------| +| Peer has no blocks (`head_num == 0`) | Aligned | +| Peer head is in our DLT range | `is_block_known(peer.head_id)` | +| Peer head + 1 == our earliest block | Read our earliest block, verify `previous == peer.head_id` | +| Fallback | `is_block_known(peer.lib_id)` | + +--- + +## Sync Modes + +Each node is in one of two modes at any time: + +### SYNC mode (pull-based) + +Used when the node is behind the network. The node requests blocks in ranges of up to **200 blocks** from a peer: + +``` +us peer + │──dlt_get_block_range──►│ + │◄──dlt_block_range_reply─│ + │ (up to 200 blocks) │ + │──apply each block──►chain│ + │ │ + │ (when is_last=true) │ + │──transition_to_forward │ +``` + +**Gap detection:** If `our_head + 1 < peer.dlt_earliest` (the missing blocks are no longer in the peer's rolling log), the node searches for another peer that can bridge the gap. If no peer can serve the gap, a snapshot import is recommended. + +**Stagnation protection:** If no block is received for 30 seconds, the node retries up to 3 times, then transitions to FORWARD mode with a warning. + +### FORWARD mode (push-based) + +Used when the node is caught up. Blocks are gossiped via `dlt_block_message`. Each block is broadcast to all **exchange-enabled** peers that share the same fork. + +**FORWARD → SYNC fallback:** If the node's head does not advance for **30 seconds** (`check_forward_stagnation`) and at least one peer is ahead, the node re-enters SYNC mode. + +### SYNC ↔ FORWARD transitions + +| Transition | Trigger | +|------------|---------| +| SYNC → FORWARD | Block range reply with `is_last=true` | +| SYNC → FORWARD | `check_sync_catchup()`: our head ≥ all peers | +| SYNC → FORWARD | Stagnation after 3 retries | +| FORWARD → SYNC | `check_forward_stagnation()`: head stuck for 30s and a peer is ahead | +| FORWARD → SYNC | `check_forward_behind()`: peer ahead by >2 blocks (15 s grace after entering FORWARD) | +| FORWARD → SYNC | Gap fill fails and no peer is available | + +On SYNC → FORWARD, the node broadcasts a `dlt_fork_status_message` with `node_status=FORWARD` to all connected peers, enabling them to re-evaluate `exchange_enabled` for this node. + +--- + +## Gap Fill + +Gap fill is a lightweight mechanism to fetch a small number of specific blocks without entering full SYNC mode. It uses two dedicated message types (`dlt_gap_fill_request` / `dlt_gap_fill_reply`) and triggers in three places: + +1. When an out-of-order block arrives (`on_dlt_block_reply`) +2. Every 5 seconds from `periodic_task()` +3. After snapshot pause completes (`resume_block_processing()`) + +**Rules:** +- Maximum **100 blocks per request** (`GAP_FILL_MAX_BLOCKS`); larger gaps use chunked requests. +- **5-second cooldown** between gap fill requests. +- The requesting peer selects the active peer with the highest head block number. +- The serving peer reads blocks from its DLT block log; requests outside the log range are rejected. +- SYNCING lifecycle peers are eligible candidates (not just ACTIVE). +- If no suitable peer is found, the node transitions immediately to SYNC mode. + +--- + +## Mempool + +The DLT P2P layer maintains its own mempool separate from the chain's `_pending_tx`. This allows early filtering before pushing transactions to the chain evaluator. + +**Admission checks:** +- Duplicate by `tx_id` — deduplicated on receipt +- Expiry — reject if already expired +- TaPoS (`tapos_block_num`) — reject if reference block is unknown +- Size — reject if `tx.size > dlt-mempool-max-tx-size` (default 64 KB) +- Expiration horizon — reject if expiry is more than `dlt-mempool-max-expiration-hours` (default 24 h) in the future + +**Eviction:** When the mempool exceeds `dlt-mempool-max-tx` (default 10 000) or `dlt-mempool-max-bytes` (default 100 MB), the entry with the nearest expiration is evicted first. + +**Lifecycle:** +- Transactions received during SYNC are tagged **provisional** and revalidated on transition to FORWARD (TaPoS blocks may now be known). +- On block application, included transactions are pruned (`remove_transactions_in_block`). +- On fork switch, TaPoS-invalid entries are pruned (`prune_mempool_on_fork_switch`). +- `periodic_mempool_cleanup()` removes expired and TaPoS-invalid entries every cycle. + +--- + +## Fork Resolution + +The DLT P2P layer tracks fork state with a **42-block threshold** (2 full validator rounds = `CHAIN_MAX_WITNESSES × 2`). + +`track_fork_state()` is called after each block application. When a competing fork is detected and sustained for ≥ 42 blocks, `resolve_fork()` computes the **heaviest branch** by total vote weight. A candidate branch must accumulate **6 consecutive confirmation blocks** (`dlt_fork_resolution_state::CONFIRMATION_BLOCKS`) before the node switches to it (hysteresis). + +The current fork status is exposed via `is_on_majority_fork()`, which the Validator Plugin uses to decide whether to produce blocks. + +--- + +## Anti-Spam + +Each peer has a single **`spam_strikes`** counter: + +- Incremented on: invalid block, invalid transaction, protocol violation +- Reset on: any valid packet +- Soft-ban threshold: **10 strikes** + +A soft-banned peer receives `dlt_soft_ban_message` (containing `ban_duration_sec` and a human-readable reason) before the connection is closed. The banned peer enters BANNED state for the specified duration and will not reconnect until it expires. + +**Per-IP connection dedup** prevents multiple connections from the same node: +- `accept_loop()` rejects incoming connections from IPs with an existing active entry. +- `connect_to_peer()` skips outbound connections if the target IP already has an active entry. +- Broadcast (`send_to_all_our_fork_peers`) tracks a `set` and skips IPs already sent to in that broadcast. + +**Duplicate / out-of-order block tolerance:** +- Already-applied blocks are silently skipped (not counted as spam). +- Out-of-order blocks in range replies fall through to `fork_db` instead of triggering a soft ban. +- Deserialization errors do not increment spam strikes. +- Oversized messages from old-protocol peers trigger a disconnect without increasing backoff. + +--- + +## Peer Exchange + +Nodes share peer addresses to assist discovery. + +**Rate limit:** **3 requests per 5-minute window** per peer. + +**Filters applied before sharing a peer address:** +- Minimum uptime: **600 seconds** +- Subnet diversity: maximum **2 peers per /24** subnet +- Ephemeral-port exclusion: `is_incoming` peers are never shared (their port is temporary) + +**Limits per reply:** `dlt-peer-exchange-max-per-reply` (default 10). + +--- + +## Block Processing Pause/Resume + +The snapshot plugin (and other plugins requiring exclusive access) can halt P2P block intake via `pause_block_processing()`. While paused: + +- `periodic_task()` skips operations that need database read locks: `sync_stagnation_check()`, `periodic_peer_exchange()`, `log_peer_stats()`. +- Stale sync and forward stagnation timers are reset so the node does not enter unnecessary mode transitions. +- Non-DB housekeeping continues: reconnect, lifecycle management, mempool cleanup, banned-peer unban. + +On `resume_block_processing()`, the node attempts gap fill before falling back to SYNC mode. + +--- + +## Configuration + +| Option | Default | Description | +|--------|---------|-------------| +| `p2p-endpoint` | `0.0.0.0:2001` | Listen address and port | +| `seed-node` | — | Static seed peer address(es) | +| `p2p-max-connections` | — | Maximum simultaneous peer connections | +| `dlt-block-log-max-blocks` | 100000 | Rolling DLT block log capacity | +| `dlt-peer-max-disconnect-hours` | 8 | Remove non-responding peer after N hours | +| `dlt-mempool-max-tx` | 10000 | Hard cap on mempool entry count | +| `dlt-mempool-max-bytes` | 104857600 | Hard cap on total mempool memory (100 MB) | +| `dlt-mempool-max-tx-size` | 65536 | Reject transactions larger than this (64 KB) | +| `dlt-mempool-max-expiration-hours` | 24 | Reject transactions expiring more than N hours in future | +| `dlt-peer-exchange-max-per-reply` | 10 | Max addresses returned per peer-exchange reply | +| `dlt-peer-exchange-max-per-subnet` | 2 | Max peers shared per /24 subnet | +| `dlt-peer-exchange-min-uptime-sec` | 600 | Min peer uptime before sharing address | +| `dlt-stats-interval-sec` | 300 | Interval between peer stats log output (min 30 s) | + +--- + +## Peer Statistics Log + +Every `dlt-stats-interval-sec` (default 5 minutes) the node logs a peer statistics summary: + +``` +[DLT-P2P] node=FORWARD head=#79274318 lib=#79274297 fork=MAJORITY + peer 192.168.1.10:2001 ACTIVE head=#79274318 exch=YES dlt=[79174319..79274318] strikes=0 + peer 192.168.1.11:2001 SYNCING head=#79274100 exch=no dlt=[79174319..79274100] strikes=0 + peer 192.168.1.12:2001 BANNED ban_remaining=3540s +``` + +Fields: +- `exch=YES/no` — whether block/transaction exchange is enabled with this peer +- `dlt=[min..max]` — DLT block log range the peer can serve +- `strikes` — current spam strike count (resets on any valid packet) +- `ban_remaining` — seconds until soft ban expires + +The stats interval can be updated at runtime via `set_stats_log_interval()`. + +--- + +## Diagnostic Summary + +| Symptom | Likely cause | +|---------|--------------| +| Node stuck in SYNC, head not advancing | Gap between our head and peer's DLT range — peer cannot bridge; consider snapshot import | +| Rapid SYNC ↔ FORWARD oscillation | No peer is ahead, or all peers isolated — check `emergency_peer_reset` log entries | +| All peers show `exch=no` | FORWARD transition did not notify peers; should self-resolve on next `broadcast_chain_status` cycle | +| `spam_strikes` growing on all peers | Likely fork divergence — check fork alignment via hello logs | +| `unlinked_size` growing in fork_db | Parent blocks are not arriving; gap fill should recover within 5s | +| `peer_head_num` appears stale in stats | Expected — `peer_head_num` is a snapshot from the last hello/fork_status exchange, not real-time | + +--- + +See also: [Messages](./messages.md), [Sync Scenarios](./sync-scenarios.md), [Forward Mode](./forward-mode.md), [Stats Reference](./stats-reference.md), [Snapshot](../node/snapshot.md), [Fork Resolution](../consensus/fork-resolution.md). diff --git a/docs/p2p/stats-reference.md b/docs/p2p/stats-reference.md new file mode 100644 index 0000000000..35b119ebdc --- /dev/null +++ b/docs/p2p/stats-reference.md @@ -0,0 +1,234 @@ +# P2P Stats Reference + +The DLT P2P layer emits two periodic log lines for monitoring: + +| Log prefix | Default interval | Purpose | +|------------|-----------------|---------| +| `DLT Status \|` | ~30 s | Compact one-liner for tail/grep health monitoring | +| `=== DLT P2P Stats \|` | ~120 s (configurable via `dlt-stats-interval-sec`) | Full per-peer detail | + +--- + +## Compact Status Line + +``` +DLT Status | FORWARD | head=#79881136 lib=#79881130 | dlt_range=79000000-79881136 | peers=6active/8conn | uptime=2h15m43s | flags=... +``` + +| Field | Example | Meaning | +|-------|---------|---------| +| Mode | `FORWARD` | Node operating mode (`SYNC` or `FORWARD`) | +| `head=#N` | `head=#79881136` | Current head block number | +| `lib=#N` | `lib=#79881130` | Last irreversible block number | +| `dlt_range=A-B` | `dlt_range=79000000-79881136` | Block range stored in the rolling DLT block log | +| `peers=Xactive/Yconn` | `peers=6active/8conn` | Exchange-enabled peers / total TCP connections | +| `uptime` | `2h15m43s` | Time since node startup | +| `flags` | various | Active flags (snapshot, paused, catchup, etc.) | + +--- + +## Full Stats — Header Line + +``` +=== DLT P2P Stats | status=FWD fork=NORMAL head=79881136 lib=79881130 peers=6 conn=4 paused=no uptime=0h20m30s === +``` + +### `status` — Node Operating Mode + +| Value | Meaning | +|-------|---------| +| `SYNC` | Catching up — pulling blocks from peers; does not broadcast transactions | +| `FWD` | Caught up — producing and relaying blocks and transactions in real time | + +**Why `SYNC`:** Node just started; fell behind during downtime; detected minority fork and is re-syncing; head stagnated for >30 seconds with a peer ahead. + +**Why `FWD`:** Node has caught up to the network head; all blocks arrive via real-time broadcast. + +### `fork` — Fork Status + +| Value | Meaning | +|-------|---------| +| `NORMAL` | On the majority fork — no conflict | +| `LOOKING` | Competing tips detected; comparing branches (threshold: 42 blocks = 2 full rounds) | +| `MINORITY` | Confirmed on a minority fork; waiting to switch | + +**Why not `NORMAL`:** Two validators produced at the same slot; network partition split validators across tips; an alternative-fork block arrived. + +### `head` and `lib` + +- **`head`** — block number of the current chain tip +- **`lib`** — Last Irreversible Block; blocks at or below this are finalized + +The head-to-lib gap is typically 1–10 blocks in normal DLT operation. + +### `peers` and `conn` + +- **`peers`** — total peer entries in the peer table (active + connecting + disconnected, tracked for reconnection) +- **`conn`** — current live TCP connections + +When `peers` significantly exceeds `conn`, the node has disconnected peers waiting in backoff queues. + +### `paused` + +| Value | Meaning | +|-------|---------| +| `no` | Block processing active | +| `YES` | Block intake temporarily halted (snapshot export in progress) | + +While paused, P2P connections continue normally. Received blocks are queued in the fork DB and applied on resume. Stale-sync and forward-stagnation timers reset so no spurious mode transitions occur during the pause. + +--- + +## Full Stats — Per-Peer Lines + +### Active Peer + +``` +62.109.17.82:2001 | ACTIVE | exch=YES | head=79881136 lib=79880729 | range=79869724-79880729 | peer_fork=NORM peer_node=FWD | spam=0 | +align+emrg+sync +``` + +#### Lifecycle State + +| Label | Meaning | +|-------|---------| +| `CONNECT` | TCP connect in progress (5 s timeout) | +| `HANDSHAKE` | Hello/hello-reply exchange in progress (10 s timeout) | +| `SYNCING` | Downloading a block range from this peer | +| `ACTIVE` | Fully operational; exchange established | +| `DISC` | Disconnected; will reconnect after backoff | +| `BANNED` | Soft-banned; no reconnect until ban expires | + +#### `exch` — Exchange Status + +| Value | Meaning | +|-------|---------| +| `YES` | Block and transaction exchange enabled; both sides share the same fork | +| `no` | Exchange disabled; fork alignment not confirmed | + +**Why `no`:** Handshake just completed and fork alignment not yet verified; peer's head/LIB not in our fork DB; peer reported fork mismatch; SYNC → FORWARD transition not yet propagated. + +**How exchange becomes `YES`:** +1. Hello handshake: acceptor calls `is_block_known(peer.head_block_id)` — on match, sets `exchange_enabled=true` in the hello reply. +2. Block acceptance: when a block from this peer applies to our chain, exchange is enabled. +3. FORWARD transition: peer broadcasts `dlt_fork_status_message` with `node_status=FORWARD`, triggering a fork-alignment re-check. + +#### `head` / `lib` (peer values) + +The peer's last reported head and LIB block numbers — a **snapshot** from the last hello, fork_status message, or block relay. The peer's actual chain may be ahead of these values, especially in FORWARD mode during rapid block production. + +#### `range` — DLT Block Log Range + +`earliest-latest`: block numbers available in the peer's rolling DLT block log. + +If blocks needed for gap fill or initial sync are below `earliest`, this peer cannot serve them. The node searches for another peer whose range covers the missing blocks. + +#### `peer_fork` — Peer's Self-Reported Fork Status + +| Label | Meaning | +|-------|---------| +| `NORM` | Peer reports being on the majority fork | +| `LOOK` | Peer is resolving a fork conflict | +| `MINO` | Peer reports being on a minority fork | + +Peers reporting `MINO` are likely in the process of switching forks and may soon change their head. A block from them should not be treated as canonical. + +#### `peer_node` — Peer's Operating Mode + +| Label | Meaning | +|-------|---------| +| `SYNC` | Peer is catching up; will not broadcast transactions | +| `FWD` | Peer is caught up and relaying real-time blocks | + +#### `spam` — Anti-Spam Strike Counter + +Strikes accumulated since the last valid packet. Soft-ban triggers at **10 strikes**. Resets on any valid packet, successful reconnection, or ban expiry. + +**Strike triggers:** Deserialization failure; protocol violation (unexpected message in current state); dead-fork blocks (after the grace period). + +**Note:** Duplicate blocks and out-of-order blocks within a range reply do **not** increment the counter. + +#### Flags + +| Flag | Meaning | +|------|---------| +| `+align` | Fork alignment verified — blocks from this peer apply cleanly to our chain | +| `+emrg` | Peer reports emergency consensus is active | +| `+ekey` | Peer holds the emergency committee private key (emergency master candidate) | +| `+sync` | Block range sync pending or in progress with this peer | + +--- + +### Disconnected Peer + +``` +138.201.117.201:2001 | DISC | disconnected=74s | backoff=480s | reconnect_in=502s | spam=0 +``` + +| Field | Meaning | +|-------|---------| +| `disconnected` | Seconds since the connection was lost | +| `backoff` | Current reconnect interval; doubles on each failure: 30 → 60 → 120 → … → 3600 s | +| `reconnect_in` | Seconds until the next reconnect attempt | +| `spam` | Residual strike count from the previous session | + +Backoff resets to the initial value (30 s) when a connection remains stable for >5 minutes. + +--- + +### Banned Peer + +``` +1.2.3.4:2001 | BANNED | ban_remaining=1800s | reason=spam strike threshold exceeded +``` + +| Field | Meaning | +|-------|---------| +| `ban_remaining` | Seconds until the ban expires (default ban: 3600 s) | +| `reason` | Human-readable ban reason sent in `dlt_soft_ban_message` | + +After expiry the entry reverts to DISCONNECTED and normal backoff reconnection resumes. + +--- + +## Scenario Interpretations + +| Symptom | Likely cause | Action | +|---------|-------------|--------| +| All peers `exch=no` | Handshake just completed; fork mismatch; node in SYNC with unrecognized peers | Wait for FORWARD transition to trigger re-evaluation; check `fork` status | +| `status=SYNC` not advancing | Gap to peer's DLT range; no bridging peer available | Check `range` on peers; may need snapshot import | +| `peer_fork=MINO` on multiple peers | Network-wide fork split | Monitor; protocol converges automatically | +| High `backoff` on disconnected peers | Repeated connection failures; network instability | Check connectivity on port 2001; high backoff is expected and resets on success | +| `paused=YES` unexpectedly | Snapshot stuck or crashed during export | Check snapshot plugin logs | +| `fork=LOOKING` not resolving | Fork persists > 42 blocks without clear majority | Check validator connectivity; inspect chain on both tips | +| `spam` growing on one peer | Protocol mismatch; peer on incompatible fork | Will auto-ban at 10 strikes; check peer software version | +| Rapid SYNC ↔ FWD oscillation | No peer ahead; all peers at same head | `emergency_peer_reset()` fires after 60 s of isolation; also check P53 fix in logs | + +--- + +## Protocol Constants + +| Constant | Value | Description | +|----------|-------|-------------| +| `SPAM_STRIKE_THRESHOLD` | 10 | Strikes before soft-ban | +| `BAN_DURATION_SEC` | 3600 | Default soft-ban duration (1 h) | +| `INITIAL_RECONNECT_BACKOFF_SEC` | 30 | First reconnect delay | +| `MAX_RECONNECT_BACKOFF_SEC` | 3600 | Maximum reconnect delay (1 h) | +| `STABLE_CONNECTION_RESET_SEC` | 300 | Connection duration before backoff resets (5 min) | +| `PEER_EXCHANGE_MAX_REQUESTS` | 3 | Max peer-exchange requests per sliding window | +| `PEER_EXCHANGE_WINDOW_SEC` | 300 | Peer-exchange rate-limit window (5 min) | +| `CONNECTING_TIMEOUT` | 5 s | TCP connect timeout | +| `HANDSHAKING_TIMEOUT` | 10 s | Hello exchange timeout | +| `PEER_REMOVAL_HOURS` | 8 h | Remove non-responding peer after this long | +| `ISOLATION_RESET_SEC` | 60 | Seconds with zero active peers before `emergency_peer_reset()` | +| `GAP_FILL_MAX_BLOCKS` | 100 | Max blocks per gap fill request | +| `GAP_FILL_COOLDOWN_SEC` | 5 | Minimum interval between gap fill requests | +| `GAP_FILL_TIMEOUT_SEC` | 15 | Gap fill in-progress flag timeout | +| `FORWARD_STAGNATION_SEC` | 30 | Head-not-advancing threshold in FORWARD mode | +| `FORWARD_BEHIND_GRACE_SEC` | 15 | Grace period after SYNC→FORWARD before `check_forward_behind()` acts | +| `SYNC_STAGNATION_SEC` | 30 | No-block-received threshold in SYNC mode | +| `FORK_RESOLUTION_BLOCK_THRESHOLD` | 42 | Blocks before fork resolution triggers (2 × CHAIN_MAX_WITNESSES) | +| `FORK_RESOLUTION_CONFIRMATION_BLOCKS` | 6 | Consecutive blocks to confirm fork resolution | + +--- + +See also: [P2P Overview](./overview.md), [Messages](./messages.md), [Sync Scenarios](./sync-scenarios.md). diff --git a/docs/p2p/sync-scenarios.md b/docs/p2p/sync-scenarios.md new file mode 100644 index 0000000000..8a26d5838f --- /dev/null +++ b/docs/p2p/sync-scenarios.md @@ -0,0 +1,218 @@ +# P2P Sync Scenarios + +This page describes how the DLT P2P layer handles common sync situations: initial startup, catching up after downtime, DLT range gaps, fork recovery, and emergency consensus. + +--- + +## Node Classification + +The scenarios below use a 4-node reference setup: + +| Role | Description | +|------|-------------| +| **Master** | FORWARD mode; DLT block log `[A..B]`; holds emergency private key | +| **Slave (NEAR)** | Head at `A-1` (exactly adjacent to master's DLT range) | +| **Slave (FAR)** | Head significantly below `A` (not in master's DLT range) | +| **Fresh node** | No blocks; genesis state only | + +--- + +## Scenario 1: NEAR Slave (head adjacent to master's DLT range) + +**Setup:** Master DLT range `[1000-2000]`. Slave head = 999. + +### Hello handshake + +1. Slave sends hello: `head_num=999, head_id=H999`. +2. Master's `check_fork_alignment` — multi-tier check: + - `head_num=999` is below `dlt_earliest=1000` — not in range. + - `head_num + 1 == dlt_earliest (1000)` → **boundary link check**: reads block 1000, verifies `block_1000.previous == H999`. + - Match → `fork_alignment = true`, `exchange_enabled = true`. +3. Master replies: `exchange_enabled=true, fork_alignment=true`. +4. Slave enters **SYNCING** lifecycle state on master. + +### Block sync + +Slave requests `dlt_get_block_range(start=1000, end=1199, prev=H999)`. Master responds with blocks 1000–1199 from its DLT log. Slave applies each block. This repeats in batches of 200 until the slave reaches block 2000 and `is_last=true` fires `transition_to_forward()`. + +**Result:** Clean P2P sync without any snapshot download. No backoff penalties. + +--- + +## Scenario 2: FAR Slave (head far below master's DLT range) + +**Setup:** Master DLT range `[1000-2000]`. Slave head = 800. + +### Hello handshake + +1. Slave sends hello: `head_num=800, head_id=H800`. +2. Master's fork alignment check: `800 < 1000`, boundary link fails (`800 + 1 ≠ 1000`), LIB fallback also fails (LIB ID pruned). +3. `fork_alignment = false`, but `exchange_enabled = false`. +4. Master **does not disconnect** the slave because `hello.node_status == SYNC` — SYNC peers always advance to ACTIVE lifecycle state. + +### Sync attempt + +Slave enters ACTIVE lifecycle state on master. Since `exchange_enabled = false`, master does not send forward blocks. Slave attempts block range request: `request_blocks_from_peer` detects `our_head+1 (801) < peer_dlt_earliest (1000)` — **gap detected**. + +The node searches all connected peers for one whose DLT range covers block 801. If found, that peer is used as the bridging sync source. If no peer can bridge the gap: + +``` +[P2P] Gap detected: our_head=800, nearest_peer_dlt_earliest=1000 + No peer can serve blocks 801-999. Snapshot import may be required. +``` + +After ~90 seconds with no head progress, the snapshot plugin's stalled-sync detector fires and initiates a snapshot download from trusted peers (configured via `trusted-peer-for-snapshot`). After importing a snapshot at block 1500, the slave re-enters SYNC mode and catches up normally. + +--- + +## Scenario 3: Fresh Node (no blocks) + +**Setup:** Node has no blocks; `head_num=0, head_id=zero_id`. + +### Hello handshake + +1. Fresh node sends hello: `head_num=0`. +2. Master's fork alignment: `head_num == 0` → **empty peer** → `fork_alignment = true` (treated as "new node, not yet on any fork"). +3. `exchange_enabled = true` (master will accept blocks from this node). +4. Fresh node advances to ACTIVE lifecycle state on master. + +### Sync attempt + +In `request_blocks_from_peer`, `our_head=0` and `peer_dlt_latest=2000`. However `peer_dlt_earliest=1000`, so the earliest available is block 1000. The request starts from `max(our_head+1, peer_dlt_earliest) = 1000`. The node receives blocks 1000+ but cannot apply them because the chain database has no state before block 1000. + +The snapshot plugin detects the stall and downloads a snapshot (e.g., at block 1500). After import, the fresh node catches up from block 1500 → 2000 normally. + +--- + +## Scenario 4: Node Restart After Crash + +**Setup:** Node was at head 1912, DLT range `[1750-1912]`. After restart, peers are at block 2000. + +### Startup recovery + +1. `database::open()` checks DLT block log consistency: if the log's head matches the database head → consistent; otherwise reset the log. +2. The last **100 blocks** from the DLT block log are seeded into `fork_db` (blocks 1813–1912). This gives newly arriving blocks a 100-block parent window without requiring them to be fetched first. +3. A **60-second grace period** applies: for the first 60s after startup, blocks within 10 of the head are treated as `FORK_DB_ONLY` instead of `DEAD_FORK`. This prevents "rejection cascade" when peers replay blocks near the head that the fresh fork_db doesn't yet know about. + +### Catching up + +The node re-enters SYNC mode and requests blocks from 1913 onward. Peers with DLT range `[1800-2000]` can serve all needed blocks. The node catches up to 2000 and transitions to FORWARD. + +--- + +## Scenario 5: Fork Switch + +**Setup:** Node at head `H` on fork A. Peer has fork B head `H'` where `H' > H` and fork B has more vote weight. + +### Fork detection + +1. Block from fork B arrives via broadcast. Fork DB links it to its parent chain. +2. `track_fork_state()` is called after each block. When fork B sustains its lead for **42 blocks** (2 full validator rounds), `resolve_fork()` runs. +3. `resolve_fork()` computes the total vote weight (SHARES delegated) of validators on each branch. Fork B must maintain a 6-consecutive-block confirmation before the switch is committed. + +### Fork switch execution + +1. `pop_block()` removes blocks from fork A back to the common ancestor. Popped transactions go to `_popped_tx`. +2. Blocks from fork B are applied from the common ancestor to the new head. +3. `_popped_tx` and `_pending_tx` are reapplied; transactions already in fork B's chain are silently skipped. + +**Fork status in stats:** transitions `NORMAL → LOOKING → NORMAL` (or `MINORITY` if this node is on the losing branch). + +--- + +## Scenario 6: Emergency Consensus Sync + +**Setup:** Network has been stalled for >3600 seconds. Emergency consensus is active. + +### Master operation + +The emergency master (node with `emergency-private-key` in config) produces all 21 blocks per round using the "committee" signing key. In stats: `+emrg +ekey`. + +### Slave sync during emergency + +1. Slave connects to master. Master's hello includes `emergency_active=true, has_emergency_key=true`. +2. Slave's fork alignment still proceeds normally — committee blocks are regular signed blocks from the perspective of the P2P layer. +3. Slave enters SYNC mode and requests committee-produced blocks from master. +4. Block validation: `verify_signing_witness()` relaxes the slot-producer mapping check during emergency — if the block producer doesn't match the exact scheduled slot, it is accepted as long as the signature validates against the producer's `signing_key`. + +### Validator key restoration + +When real validators restore their signing keys (via `validator_update_operation`), the schedule rebuild includes them in the hybrid schedule. Once **15 of 21** validator slots are real (non-committee), emergency mode deactivates. Subsequent blocks are produced by real validators and synced normally. + +--- + +## Scenario 7: Stale Sync Recovery + +**Condition:** SYNC mode, no block received for 30 seconds. + +1. `sync_stagnation_check()` fires: retry 1 of 3 — re-requests blocks from all active exchange-enabled peers. +2. 30 seconds later: retry 2 of 3. +3. 30 seconds later: retry 3 of 3. +4. After the third retry: `transition_to_forward()` with a stagnation warning. + +If the node was still behind when it transitioned to FORWARD, `check_forward_stagnation()` will detect no head progress after 30 seconds and transition back to SYNC mode, starting a new cycle. + +--- + +## Scenario 8: Gap Fill + +**Condition:** FORWARD mode; 1–100 blocks missing in the block stream. + +Gap fill triggers automatically when: +- An out-of-order block is received (block N+2 arrives before N+1). +- `periodic_task()` detects `highest_seen_block_num > our_head + 1`. +- `resume_block_processing()` is called after a snapshot pause. + +**Protocol:** +1. Select the peer with the highest `peer_head_num` among active peers. +2. Send `dlt_gap_fill_request(block_nums=[N+1, N+2, ...])` (max 100 blocks). +3. Wait up to **15 seconds** for the reply. +4. On receipt, apply the returned blocks. If blocks are still missing, trigger another gap fill on the next periodic cycle. + +**If no peer can serve the gap** (no exchange-enabled or SYNCING peer with a higher head), the node immediately transitions to SYNC mode. + +--- + +## Scenario 9: SYNC ↔ FORWARD Oscillation Prevention + +**Root cause of oscillation:** After transitioning FORWARD→SYNC, the sync stagnation timer inherits a stale timestamp, fires immediately, and `check_sync_catchup` sees zero peers ahead → transitions back to FORWARD. Loop continues. + +Another variant: `sync_stagnation_check()` transitions to FORWARD, then `check_forward_behind()` runs on the **same periodic tick** and immediately transitions back to SYNC because the peer is still ahead — instant SYNC→FORWARD→SYNC oscillation with no progress. + +**Fixes in place:** +- `transition_to_sync()` resets `_last_block_received_time` to `now`, so stagnation timers start fresh. +- `check_forward_stagnation()` does NOT transition to SYNC when all connected peers have the same head as our node — no point syncing when nobody is ahead. +- `check_sync_catchup()` does NOT claim "caught up" when zero active peers exist; instead it starts the 60-second isolation timer. +- After 60 seconds of isolation, `emergency_peer_reset()` clears all soft-bans and backoffs, forcing immediate reconnect to all known peers. +- `check_forward_behind()` has a **15-second grace period** after entering FORWARD (`FORWARD_BEHIND_GRACE_SEC`). During this window the behind-peer check is skipped, giving broadcast blocks time to arrive before re-evaluating whether a SYNC fallback is needed. + +--- + +## Scenario 10: Dead Fork Blocks + +**Condition:** A peer sends blocks from a chain that diverged before the node's fork DB window. `push_block()` throws `unlinkable_block_exception` and the block number is ≤ `head_block_num`. + +**Behavior:** +1. `dlt_delegate::accept_block()` returns `DEAD_FORK`. +2. The block is NOT stored in `fork_db._unlinked_index` (prevents memory growth). +3. The peer accumulates a spam strike per dead-fork block. +4. After 10 strikes the peer is soft-banned for 3600 s. +5. The sync loop breaks out — no further blocks from this peer are processed in the current batch. + +**Grace period (P22 fix):** For the first 60 seconds after node startup, blocks within 10 of the current head that fail with `unlinkable_block_exception` are returned as `FORK_DB_ONLY` (not `DEAD_FORK`). This prevents false banning of legitimate peers sending blocks near the head before fork_db is fully rebuilt from the last 100-block seed. + +--- + +## Configuration Relevant to Sync + +| Setting | Default | Effect | +|---------|---------|--------| +| `seed-node` | — | Static peers; reconnected after `emergency_peer_reset()` | +| `dlt-block-log-max-blocks` | 100000 | DLT log capacity; affects how far back peers can bridge gaps | +| `trusted-peer-for-snapshot` | — | Peers from which snapshot download is accepted | +| `stalled-sync-timeout-minutes` | 2 | Minutes before snapshot plugin triggers recovery | +| `enable-stale-production` | false | Allow validator to produce without being synced (development only) | + +--- + +See also: [P2P Overview](./overview.md), [Forward Mode](./forward-mode.md), [Emergency Consensus](../consensus/emergency-consensus.md), [Snapshot](../node/snapshot.md). diff --git a/docs/plugins/chain.md b/docs/plugins/chain.md new file mode 100644 index 0000000000..0cf72896ba --- /dev/null +++ b/docs/plugins/chain.md @@ -0,0 +1,125 @@ +# Chain Plugin + +The chain plugin is the core component of every VIZ Ledger node. It manages the chainbase shared-memory database, accepts and validates blocks and transactions, maintains fork database and block log state, and coordinates startup with the snapshot and P2P plugins. All other plugins depend on it. + +**Source:** [plugins/chain/plugin.cpp](../../plugins/chain/plugin.cpp) + +--- + +## Dependencies + +``` +json_rpc::plugin +``` + +The chain plugin must be the first domain plugin initialized; `json_rpc` is its only formal dependency and is loaded first by the AppBase framework. + +--- + +## Configuration + +### CLI-only flags + +These are one-time recovery or maintenance operations; they cause the node to perform a specific action on startup and cannot be set in `config.ini`. + +| Flag | Description | +|------|-------------| +| `--replay-blockchain` | Wipe chainbase shared memory and replay the full block log from block 1. | +| `--force-replay-blockchain` | Same as `--replay-blockchain` but skips the corruption check. Use when the block log is intact but chainbase is unreadable. | +| `--replay-from-snapshot ` | Crash recovery for DLT nodes: wipe shared memory, import a snapshot, then replay the DLT rolling block log. See [Snapshot Plugin](./snapshot.md). | +| `--snapshot-auto-latest` | With `--replay-from-snapshot`: auto-discover the latest snapshot in `snapshot-dir` instead of specifying the path manually. | +| `--auto-recover-from-snapshot` | Default `true`. Automatically recover at runtime when shared memory corruption is detected during block processing or generation, without a restart. Disable with `--no-auto-recover-from-snapshot`. | +| `--resync-blockchain` | Wipe both shared memory and the block log; start from genesis or from a snapshot. Destructive — use only when recovering from complete data loss. | +| `--check-locks` | Validate lock ordering (development only). | +| `--validate-database-invariants` | Run database consistency checks on every block (very slow; development only). | + +### Config file options + +#### Shared memory + +| Option | Default | Description | +|--------|---------|-------------| +| `shared-file-dir` | `state` | Directory for the shared memory file (absolute path, or relative to the data directory). | +| `shared-file-size` | `2G` | Initial shared memory size. Use `4G`–`16G` for production nodes depending on chain age and object counts. | +| `inc-shared-file-size` | `2G` | Growth increment when free space falls below the minimum threshold. | +| `min-free-shared-file-size` | `500M` | Auto-grow when free shared memory falls below this value. | +| `block-num-check-free-size` | `1000` | Check free space every N blocks. | +| `flush-state-interval` | `10000` | Force a full flush to disk every N blocks. Higher values improve throughput at the cost of more data to replay after an unclean shutdown. | + +#### Block log and DLT + +| Option | Default | Description | +|--------|---------|-------------| +| `dlt-block-log-max-blocks` | `100000` | Number of recent blocks to keep in the DLT rolling block log (`dlt_block_log.log`). Only active in DLT mode (after snapshot import). Set to `0` to disable. | +| `checkpoint` | — | Block-number/block-ID pairs that must match during replay; can be specified multiple times. | + +#### Performance + +| Option | Default | Description | +|--------|---------|-------------| +| `single-write-thread` | `false` | Route all write operations through a dedicated io_service thread. Improves consistency under high concurrency; slight throughput cost. | +| `skip-virtual-ops` | `false` | Skip virtual operation processing. Reduces memory use; breaks plugins that index virtual ops (`account_history`, `operation_history`). | +| `enable-plugins-on-push-transaction` | `false` | Notify observer plugins when transactions enter the pending pool (before block application). | +| `read-wait-micro` | *(db default)* | Read lock timeout in microseconds. | +| `max-read-wait-retries` | *(db default)* | Retry attempts before a read lock timeout is fatal. | +| `write-wait-micro` | *(db default)* | Write lock timeout in microseconds. | +| `max-write-wait-retries` | *(db default)* | Retry attempts before a write lock timeout is fatal. | + +--- + +## Startup Sequence + +``` +plugin_initialize() ← parse CLI and config options; validate snapshot path +plugin_startup() ← open or create database + ├─ --resync → wipe shared memory + block log; init genesis + ├─ --replay → wipe shared memory; replay from block log + ├─ --snapshot → import snapshot; start DLT mode + ├─ --replay-from-snapshot → import snapshot; replay dlt_block_log + └─ normal restart → open existing shared memory; replay if revision mismatch +emit on_sync() ← P2P and validator plugins activate +``` + +All snapshot loading happens inside `plugin_startup()`, before P2P or validator ever see the database. + +--- + +## Block Acceptance + +`chain::plugin::accept_block()` is the entry point for all incoming blocks (from P2P and from the validator). It: + +1. Validates the block timestamp is not too far in the future. +2. Under a write lock, calls `database::push_block()`. +3. Updates the fork database and block log. +4. Emits the `applied_block` signal to all subscriber plugins. +5. On `shared_memory_corruption_exception`, calls `attempt_auto_recovery()` if auto-recovery is enabled. + +Transaction acceptance (`accept_transaction()`) follows the same path via `database::push_transaction()`. + +--- + +## Shared Memory + +The chainbase database lives in a single memory-mapped file (`shared_memory.bin`) in `shared-file-dir`. Key sizing guidance: + +- Start with `shared-file-size = 4G` for a node loading from a recent snapshot. +- The database auto-grows by `inc-shared-file-size` when free space drops below `min-free-shared-file-size`. +- After a clean shutdown, the file shrinks back to actual used size. +- After a crash, run with `--replay-blockchain` or `--replay-from-snapshot` to rebuild consistent state. + +--- + +## Troubleshooting + +| Symptom | Action | +|---------|--------| +| `FC_ASSERT` or `database_revision_exception` on startup | Revision mismatch — run `--replay-blockchain` | +| Chainbase open fails with corruption error | Run `--replay-from-snapshot --snapshot-auto-latest` (DLT nodes) or `--replay-blockchain` (full nodes) | +| Node stuck at genesis after `--resync-blockchain` | Block log was also wiped; provide `--snapshot` to load state from a snapshot | +| Shared memory grows unbounded | Check `inc-shared-file-size` and `min-free-shared-file-size` settings; verify chain is applying blocks normally | +| `write lock timeout` errors | Another process holds the write lock; check for stale `vizd` processes | +| Auto-recovery fires repeatedly | Underlying storage may have hardware faults; check disk health; also verify `snapshot-every-n-blocks` is configured so fresh snapshots exist | + +--- + +See also: [Snapshot Plugin](./snapshot.md), [Validator Plugin](./validator.md), [P2P Overview](../p2p/overview.md), [Block Processing](../consensus/block-processing.md). diff --git a/docs/plugins/database-api.md b/docs/plugins/database-api.md new file mode 100644 index 0000000000..200cbfd68f --- /dev/null +++ b/docs/plugins/database-api.md @@ -0,0 +1,408 @@ +# Database API + +The `database_api` plugin exposes read-only JSON-RPC methods for querying blockchain state: blocks, accounts, chain properties, delegation, authority verification, and governance. + +**Source:** [plugins/database_api/](../../plugins/database_api/) + +--- + +## Dependencies + +``` +json_rpc::plugin, chain::plugin +``` + +--- + +## Request Format + +All methods use JSON-RPC 2.0 over HTTP POST or WebSocket: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "method": "database_api.", + "params": [, , ...] +} +``` + +--- + +## Blocks and Transactions + +### `get_block_header(block_num)` + +Returns the block header for the given block number, or `null` if not found. + +```json +{ "method": "database_api.get_block_header", "params": [12345678] } +``` + +**Returns:** `block_header` — `previous`, `timestamp`, `witness`, `transaction_merkle_root`, `extensions`. + +--- + +### `get_block(block_num)` + +Returns the full signed block including all transactions. + +```json +{ "method": "database_api.get_block", "params": [12345678] } +``` + +**Returns:** `signed_block` — all header fields plus `transactions[]` with full operation data, and the response additionally includes `block_id`, `signing_key`, `transaction_ids[]`. + +--- + +### `get_irreversible_block_header(block_num)` + +Same as `get_block_header` but only returns the block if it has reached LIB (is irreversible). + +--- + +### `get_irreversible_block(block_num)` + +Same as `get_block` but only returns the block if it has reached LIB. + +--- + +### `set_block_applied_callback(callback)` + +Register a WebSocket callback to be notified on every applied block. The callback receives the block header as a JSON variant. + +**WebSocket only.** Unsubscribe with `cancel_all_subscriptions`. + +--- + +## Chain Globals + +### `get_config()` + +Returns compile-time constants: chain ID, token symbols, block interval, max block size, voting periods, and all `CHAIN_*` constants. + +```json +{ "method": "database_api.get_config", "params": [] } +``` + +--- + +### `get_dynamic_global_properties()` + +Returns live chain state: current head block number and ID, time, head validator, total vesting shares, participation rate, DPO fund balances, and more. + +```json +{ "method": "database_api.get_dynamic_global_properties", "params": [] } +``` + +Key fields: `head_block_number`, `head_block_id`, `time`, `current_witness`, `total_vesting_shares`, `total_vesting_fund_viz`, `committee_fund`, `last_irreversible_block_num`, `participation_count`. + +--- + +### `get_chain_properties()` + +Returns the current on-chain governance parameters (set via `chain_properties_update_operation`): minimum account creation fee, maximum block size, create account with viz fee, bandwidth reserve percent, and reward parameters. + +```json +{ "method": "database_api.get_chain_properties", "params": [] } +``` + +--- + +### `get_hardfork_version()` + +Returns the currently active hardfork version string (e.g., `"1.0.0"`). + +```json +{ "method": "database_api.get_hardfork_version", "params": [] } +``` + +--- + +### `get_next_scheduled_hardfork()` + +Returns the version and scheduled live time of the next pending hardfork. + +```json +{ "method": "database_api.get_next_scheduled_hardfork", "params": [] } +``` + +**Returns:** `{ "hf_version": "1.0.0", "live_time": "2025-01-01T00:00:00" }` + +--- + +## Accounts + +### `get_accounts(account_names)` + +Returns full account objects for the given list of account names. + +```json +{ "method": "database_api.get_accounts", "params": [["alice", "bob"]] } +``` + +**Returns:** Array of `account_api_object` — name, balance, vesting shares, received vesting, delegated vesting, keys, recovery account, created, post count, voting power, and more. + +--- + +### `lookup_account_names(account_names)` + +Same as `get_accounts` but returns `null` for accounts that do not exist. + +```json +{ "method": "database_api.lookup_account_names", "params": [["alice", "nonexistent"]] } +``` + +**Returns:** Array of `optional` — `null` for missing accounts. + +--- + +### `lookup_accounts(lower_bound_name, limit)` + +Returns a set of account names starting from `lower_bound_name`, up to `limit` results (max 1000). Useful for paginated account enumeration. + +```json +{ "method": "database_api.lookup_accounts", "params": ["alice", 100] } +``` + +**Returns:** Set of account name strings. + +--- + +### `get_account_count()` + +Returns the total number of registered accounts. + +```json +{ "method": "database_api.get_account_count", "params": [] } +``` + +--- + +## Account State + +### `get_master_history(account)` + +Returns the history of master authority changes for the given account. + +```json +{ "method": "database_api.get_master_history", "params": ["alice"] } +``` + +**Returns:** Array of `master_authority_history_api_object` — `account`, `previous_master_authority`, `last_valid_time`. + +--- + +### `get_recovery_request(account)` + +Returns the pending account recovery request for the given account, if any. + +```json +{ "method": "database_api.get_recovery_request", "params": ["alice"] } +``` + +**Returns:** `optional` — `account_to_recover`, `new_master_authority`, `expires`. + +--- + +### `get_escrow(from, escrow_id)` + +Returns the escrow transfer object for the given sender and escrow ID. + +```json +{ "method": "database_api.get_escrow", "params": ["alice", 1] } +``` + +**Returns:** `optional` — all escrow fields including `from`, `to`, `agent`, `ratification_deadline`, `escrow_expiration`, amounts, and approval status. + +--- + +### `get_withdraw_routes(account, type)` + +Returns the vesting power withdrawal routes for an account. `type` is one of `"incoming"`, `"outgoing"`, or `"all"`. + +```json +{ "method": "database_api.get_withdraw_routes", "params": ["alice", "outgoing"] } +``` + +**Returns:** Array of `{ "from_account", "to_account", "percent", "auto_vest" }`. + +--- + +### `get_vesting_delegations(account, from, limit, type)` + +Returns vesting delegations for an account. `type` is `"delegated"` (delegations made by this account) or `"received"` (delegations received). + +```json +{ "method": "database_api.get_vesting_delegations", "params": ["alice", "", 100, "delegated"] } +``` + +**Returns:** Array of `vesting_delegation_api_object` — `delegator`, `delegatee`, `vesting_shares`, `min_delegation_time`. + +--- + +### `get_expiring_vesting_delegations(account, from, limit)` + +Returns vesting delegation expiration entries for an account — delegations that have been retracted and are waiting for the return window. + +```json +{ "method": "database_api.get_expiring_vesting_delegations", "params": ["alice", "1970-01-01T00:00:00", 100] } +``` + +**Returns:** Array of `vesting_delegation_expiration_api_object` — `delegator`, `vesting_shares`, `expiration`. + +--- + +## Authority and Transaction Validation + +### `get_transaction_hex(trx)` + +Returns the hex-encoded serialized binary form of a transaction. Useful for signing and broadcasting. + +```json +{ "method": "database_api.get_transaction_hex", "params": [{ ...transaction object... }] } +``` + +**Returns:** Hex string. + +--- + +### `get_required_signatures(trx, available_keys)` + +Given a partially-signed transaction and the set of public keys available to the signer, returns the minimal subset of keys that must sign to authorize the transaction. + +```json +{ + "method": "database_api.get_required_signatures", + "params": [{ ...trx... }, ["VIZ5...", "VIZ6..."]] +} +``` + +**Returns:** Set of public key strings. + +--- + +### `get_potential_signatures(trx)` + +Returns all public keys that could potentially sign the transaction (across all involved accounts and authority levels). Use this to pre-filter a wallet's key set before calling `get_required_signatures`. + +```json +{ "method": "database_api.get_potential_signatures", "params": [{ ...trx... }] } +``` + +**Returns:** Set of public key strings. + +--- + +### `verify_authority(trx)` + +Returns `true` if the transaction has all required signatures; throws an error with a description of what is missing otherwise. + +```json +{ "method": "database_api.verify_authority", "params": [{ ...signed_trx... }] } +``` + +--- + +### `verify_account_authority(name, signers)` + +Returns `true` if the given set of public keys has sufficient authority to act on behalf of `name`. + +```json +{ "method": "database_api.verify_account_authority", "params": ["alice", ["VIZ5..."]] } +``` + +--- + +## Database Info + +### `get_database_info()` + +Returns chainbase shared memory usage statistics. + +```json +{ "method": "database_api.get_database_info", "params": [] } +``` + +**Returns:** +```json +{ + "total_size": 4294967296, + "free_size": 1073741824, + "reserved_size": 0, + "used_size": 3221225472, + "index_list": [ + { "name": "account_object", "record_count": 52341 }, + ... + ] +} +``` + +--- + +## Governance + +### `get_proposed_transactions(account, from, limit)` + +Returns governance proposals that require approval from `account`. + +```json +{ "method": "database_api.get_proposed_transactions", "params": ["alice", 0, 100] } +``` + +**Returns:** Array of `proposal_api_object` — full proposal details including required approvals, expiration, and operation list. + +--- + +## Account Market + +### `get_accounts_on_sale(from, limit)` + +Returns accounts currently listed for sale (direct sale, not auction). + +```json +{ "method": "database_api.get_accounts_on_sale", "params": [0, 100] } +``` + +**Returns:** Array of `account_on_sale_api_object` — `account`, `account_seller`, `account_offer_price`, `account_on_sale_start_time`, `target_buyer`. + +--- + +### `get_accounts_on_auction(from, limit)` + +Returns accounts listed for auction. + +```json +{ "method": "database_api.get_accounts_on_auction", "params": [0, 100] } +``` + +**Returns:** Array of `account_on_sale_api_object` — same as above plus `current_bid`, `current_bidder`, `current_bidder_key`, `last_bid`. + +--- + +### `get_subaccounts_on_sale(from, limit)` + +Returns account namespace registrations available for sale (subaccount creation rights). + +```json +{ "method": "database_api.get_subaccounts_on_sale", "params": [0, 100] } +``` + +**Returns:** Array of `subaccount_on_sale_api_object` — `account`, `subaccount_seller`, `subaccount_offer_price`. + +--- + +## Error Codes + +| Code | Meaning | +|------|---------| +| `-32700` | Parse error — invalid JSON | +| `-32600` | Invalid request — missing required fields | +| `-32601` | Method not found | +| `-32602` | Invalid params | +| `-32603` | Internal error | +| `-32099` to `-32000` | Server error (exception from method handler) | + +--- + +See also: [Plugin Overview](./overview.md), [witness_api methods](./overview.md#witness_api), [JSON-RPC API](../api/json-rpc.md). diff --git a/docs/plugins/overview.md b/docs/plugins/overview.md new file mode 100644 index 0000000000..5de2a3e49b --- /dev/null +++ b/docs/plugins/overview.md @@ -0,0 +1,379 @@ +# Plugin Overview + +VIZ Ledger uses the **AppBase** plugin framework. Each plugin has a lifecycle (`plugin_initialize` → `plugin_startup` → `plugin_shutdown`), may register JSON-RPC methods, may store data in the chainbase database, and may subscribe to chain signals (e.g., `applied_block`). + +--- + +## Plugin Categories + +| Category | Description | +|----------|-------------| +| **Core** | Required for any node operation | +| **Infrastructure** | Networking, web server, snapshots | +| **API** | Expose JSON-RPC endpoints for clients | +| **Index** | Index chain data into chainbase for fast queries | +| **Producer** | Block signing and production | +| **External** | Integration with external systems (MongoDB) | +| **Debug/Test** | Development only; not for production | + +--- + +## Plugin Inventory + +### Core + +| Plugin | Status | Deps | JSON-RPC | +|--------|--------|------|---------| +| `chain` | Required | `json_rpc` | — | +| `json_rpc` | Required | — | — | + +### Infrastructure + +| Plugin | Status | Deps | JSON-RPC | +|--------|--------|------|---------| +| `webserver` | Required for API | `json_rpc` | — | +| `p2p` | Required for network | `chain` | — | +| `snapshot` | Recommended | `chain` | — | +| `witness_guard` | Recommended for validators | `chain`, `p2p` | — | + +### API + +| Plugin | Status | Deps | JSON-RPC | +|--------|--------|------|---------| +| `database_api` | Active | `json_rpc`, `chain` | Yes | +| `network_broadcast_api` | Active | `json_rpc`, `chain`, `p2p` | Yes | +| `witness_api` | Active | `json_rpc`, `chain` | Yes | +| `account_by_key` | Active | `json_rpc`, `chain` | Yes | +| `account_history` | Active | `json_rpc`, `chain`, `operation_history` | Yes | +| `operation_history` | Active | `json_rpc`, `chain` | Yes | +| `committee_api` | Active | `json_rpc`, `chain` | Yes | +| `invite_api` | Active | `json_rpc`, `chain` | Yes | +| `paid_subscription_api` | Active | `json_rpc`, `chain` | Yes | +| `custom_protocol_api` | Active | `json_rpc`, `chain` | Yes | +| `auth_util` | Active | `json_rpc`, `chain` | Yes | +| `block_info` | Active | `json_rpc`, `chain` | Yes | +| `raw_block` | Active | `json_rpc`, `chain` | Yes | +| `follow` | Deprecated | `json_rpc`, `chain` | Yes | +| `tags` | Deprecated | `json_rpc`, `chain`, `follow` | Yes | +| `social_network` | Deprecated | `json_rpc`, `chain` | Yes | +| `private_message` | Deprecated | `json_rpc`, `chain` | Yes | + +### Producer + +| Plugin | Status | Deps | JSON-RPC | +|--------|--------|------|---------| +| `validator` | Active | `chain`, `p2p` | — | + +### External + +| Plugin | Status | Deps | JSON-RPC | +|--------|--------|------|---------| +| `mongo_db` | Active | `chain` | — | + +### Debug / Test + +| Plugin | Status | Deps | JSON-RPC | +|--------|--------|------|---------| +| `debug_node` | Dev only | `chain` | Yes | +| `test_api` | Test only | `json_rpc` | — | + +--- + +## Core Plugins + +### `chain` + +Manages the chainbase database, applies blocks and transactions, and emits signals to all other plugins. + +**Key config options:** + +| Option | Default | Description | +|--------|---------|-------------| +| `shared-file-size` | `2G` | Initial shared memory file size | +| `shared-file-dir` | `state` | Directory for shared memory files | +| `inc-shared-file-size` | `2G` | Growth increment when space is low | +| `min-free-shared-file-size` | `500M` | Trigger auto-grow below this threshold | +| `flush-state-interval` | `10000` | Force flush to disk every N blocks | +| `skip-virtual-ops` | `false` | Skip virtual operations (reduces memory) | +| `dlt-block-log-max-blocks` | `100000` | Rolling DLT block log capacity | + +**Key CLI flags:** + +| Flag | Description | +|------|-------------| +| `--replay-blockchain` | Wipe chainbase and replay from block log | +| `--force-replay-blockchain` | Same as above, ignores corruption check | +| `--replay-from-snapshot` | Import snapshot then replay DLT block log (crash recovery) | +| `--auto-recover-from-snapshot` | Enable automatic recovery from shared memory corruption | +| `--resync-blockchain` | Wipe chainbase and block log; start from genesis or snapshot | + +--- + +### `json_rpc` + +Framework plugin; no config. Registers and dispatches all JSON-RPC methods. Must be loaded first. + +All JSON-RPC requests use the 2.0 format: +```json +{ + "jsonrpc": "2.0", + "method": "api_name.method_name", + "params": {}, + "id": 1 +} +``` + +--- + +## Infrastructure Plugins + +### `webserver` + +HTTP and WebSocket server that forwards requests to `json_rpc`. Includes a read-only response cache. + +**Key config options:** + +| Option | Default | Description | +|--------|---------|-------------| +| `webserver-http-endpoint` | — | HTTP listen address (e.g., `0.0.0.0:8090`) | +| `webserver-ws-endpoint` | — | WebSocket listen address (e.g., `0.0.0.0:8091`) | +| `webserver-thread-pool-size` | `32` | Worker threads for HTTP/WS handling | +| `webserver-cache-enabled` | `true` | Enable response caching | +| `webserver-cache-size` | `10000` | Maximum cached entries | + +Cache keys are derived from `method + params` (not `id`), preventing bypass by rotating the request `id`. Mutating methods (`network_broadcast_api.*`, `debug_node.*`) are never cached. The cache clears on each new applied block. + +See [Webserver](./webserver.md) for full details. + +--- + +### `p2p` + +DLT P2P networking — block and transaction propagation, peer management, minority fork recovery. + +**Key config options:** + +| Option | Default | Description | +|--------|---------|-------------| +| `p2p-endpoint` | — | Listen address (e.g., `0.0.0.0:2001`) | +| `seed-node` | — | Static seed peer(s) | +| `p2p-max-connections` | — | Maximum simultaneous connections | +| `dlt-block-log-max-blocks` | `100000` | Rolling DLT log capacity | +| `dlt-stats-interval-sec` | `300` | Peer stats log interval | + +See [P2P Overview](../p2p/overview.md) for the full P2P architecture. + +--- + +### `snapshot` + +Snapshot creation, loading, and P2P snapshot sync for fast bootstrap and crash recovery. + +See [Snapshot](../node/snapshot.md) and [Plugin: Snapshot](./snapshot.md) for details. + +--- + +## API Plugins + +### `database_api` + +Primary read API. Query blocks, transactions, accounts, chain state, hardfork version, delegations, proposals. + +See [Database API](./database-api.md) for the full method reference. + +--- + +### `network_broadcast_api` + +Submit and broadcast signed transactions and blocks. + +| Method | Description | +|--------|-------------| +| `broadcast_transaction` | Submit a transaction (async) | +| `broadcast_transaction_synchronous` | Submit and wait for inclusion in a block | +| `broadcast_transaction_with_callback` | Submit with callback on inclusion or expiry | +| `broadcast_block` | Submit a signed block (validators only) | + +--- + +### `witness_api` + +Query validator state: active set, schedule, individual validators, vote rankings. + +| Method | Description | +|--------|-------------| +| `get_active_witnesses` | Current 21-validator active set | +| `get_witness_schedule` | Full schedule object | +| `get_witnesses` | Validators by database IDs | +| `get_witness_by_account` | Single validator by account name | +| `get_witnesses_by_vote` | Validators sorted by total vote weight | +| `get_witnesses_by_counted_vote` | Validators sorted by counted vote weight | +| `get_witness_count` | Total number of registered validators | +| `lookup_witness_accounts` | List validator account names by prefix | + +--- + +### `account_by_key` + +Reverse-lookup accounts by public key. + +| Method | Description | +|--------|-------------| +| `get_key_references` | Get account names that use given public keys | + +--- + +### `account_history` + +Per-account operation history, paginated. + +| Method | Description | +|--------|-------------| +| `get_account_history(account, from, limit)` | Get operations; `from=-1` returns newest; max 1000 per call | + +**Config options:** +- `track-account-range` — account name range to index (default: all accounts) +- `history-count-blocks` — retain N blocks of history + +--- + +### `operation_history` + +All-operations index for block-level and transaction queries. + +| Method | Description | +|--------|-------------| +| `get_ops_in_block(block_num, virtual_ops)` | Operations in a block; `virtual_ops=true` includes virtual ops | +| `get_transaction(tx_id)` | Transaction by ID | + +**Config options:** +- `history-whitelist-ops` / `history-blacklist-ops` — filter which op types are stored +- `history-start-block` — start indexing from this block number +- `history-count-blocks` — retain N blocks of history + +--- + +### `committee_api` + +Query committee worker requests and votes. + +| Method | Description | +|--------|-------------| +| `get_committee_request(id)` | Request by ID | +| `get_committee_request_votes(id)` | Votes on a request | +| `get_committee_requests_list(from, limit, status)` | Paginated request list | + +--- + +### `invite_api` + +Query active invite codes. + +| Method | Description | +|--------|-------------| +| `get_invites_list` | All invite IDs | +| `get_invite_by_id(id)` | Invite by database ID | +| `get_invite_by_key(pub_key)` | Invite by public key | + +--- + +### `paid_subscription_api` + +Query subscription offerings and subscriber status. + +| Method | Description | +|--------|-------------| +| `get_paid_subscriptions` | All active subscription offerings | +| `get_paid_subscription_options(account)` | Subscription config for an account | +| `get_paid_subscription_status(subscriber, account)` | Status of a specific subscription | +| `get_active_paid_subscriptions(subscriber)` | Active subscriptions for a subscriber | +| `get_inactive_paid_subscriptions(subscriber)` | Expired subscriptions | + +--- + +### Deprecated API Plugins + +| Plugin | Methods | Notes | +|--------|---------|-------| +| `follow` | Followers/following, feeds, blog, reblogs | Still functional; not recommended for new integrations | +| `tags` | Trending/hot/new content by tag | Still functional; not recommended for new integrations | +| `social_network` | Content, votes, replies | Wraps committee/invite queries; still functional | +| `private_message` | Inbox/outbox for encrypted messages | `custom_operation`-based; still functional | + +--- + +## Producer Plugin + +### `validator` + +Block signing and production. Runs a 250 ms timer loop; produces when the next slot is assigned to a configured validator account. + +**Key config options:** + +| Option | Default | Description | +|--------|---------|-------------| +| `validator` | — | Validator account name(s) | +| `private-key` | — | WIF private key(s) for signing | +| `emergency-private-key` | — | Emergency consensus signing key | +| `enable-stale-production` | `false` | Produce even when chain is stale (testnet only) | +| `required-participation` | `3300` | Min participation in basis points (3300 = 33%) | +| `fork-collision-timeout-blocks` | `21` | Deferrals before forcing production past a collision | + +`required-participation` is always in **basis points** (0–10000 = 0%–100%). + +See [Validator Plugin](./validator.md) for full production timing and fork handling details. + +--- + +## Recommended Plugin Sets + +### Minimal API node + +```ini +plugin = chain +plugin = json_rpc +plugin = webserver +plugin = p2p +plugin = database_api +plugin = network_broadcast_api +``` + +### Full API node + +```ini +plugin = chain +plugin = json_rpc +plugin = webserver +plugin = p2p +plugin = database_api +plugin = network_broadcast_api +plugin = witness_api +plugin = account_by_key +plugin = account_history +plugin = operation_history +plugin = committee_api +plugin = invite_api +plugin = paid_subscription_api +``` + +### Validator node + +```ini +plugin = chain +plugin = p2p +plugin = validator +plugin = json_rpc +plugin = webserver +plugin = database_api +plugin = network_broadcast_api +plugin = witness_api +plugin = snapshot + +snapshot-every-n-blocks = 28800 +snapshot-dir = /data/snapshots +dlt-block-log-max-blocks = 100000 +``` + +--- + +See also: [Chain Plugin](./chain.md), [Validator Plugin](./validator.md), [Snapshot Plugin](./snapshot.md), [P2P Overview](../p2p/overview.md), [JSON-RPC API](../api/json-rpc.md). diff --git a/docs/plugins/snapshot.md b/docs/plugins/snapshot.md new file mode 100644 index 0000000000..0fb52ab162 --- /dev/null +++ b/docs/plugins/snapshot.md @@ -0,0 +1,347 @@ +# Snapshot Plugin + +The snapshot plugin enables near-instant node startup by serializing and restoring the full blockchain state as a JSON file. Instead of replaying millions of blocks from the block log, a node loads a pre-built snapshot and begins syncing from the snapshot's block height via P2P. + +**Source:** [plugins/snapshot/snapshot.cpp](../../plugins/snapshot/snapshot.cpp) + +--- + +## Dependencies + +``` +chain::plugin +``` + +--- + +## Configuration + +### CLI-only options + +| Option | Default | Description | +|--------|---------|-------------| +| `--snapshot ` | — | Load state from a snapshot file (DLT mode). Skips import if `shared_memory.bin` already exists; renames the file to `.used` after successful import. | +| `--snapshot-auto-latest` | `false` | Auto-discover the latest snapshot in `snapshot-dir` by block number from filename. Ignored if `--snapshot` is also specified. | +| `--replay-from-snapshot` | `false` | Crash recovery: import a snapshot then replay blocks from the DLT rolling block log. Always wipes shared memory; does NOT rename the snapshot file to `.used`. Requires `--snapshot` or `--snapshot-auto-latest`. | +| `--auto-recover-from-snapshot` | `true` | Automatic runtime recovery from shared memory corruption — no restart required. Requires `plugin = snapshot` and snapshots in `snapshot-dir`. Disable with `--no-auto-recover-from-snapshot`. | +| `--create-snapshot ` | — | Create a snapshot at the given path using the current database state, then exit. Runs before P2P or validator activate. | +| `--sync-snapshot-from-trusted-peer` | `false` | Download and load a snapshot from trusted peers when state is empty. Requires `trusted-snapshot-peer`. Opt-in to prevent accidental state wipe. | + +### Config file options + +| Option | Default | Description | +|--------|---------|-------------| +| `snapshot-at-block` | `0` | Create a snapshot when this block number is reached (0 = disabled). | +| `snapshot-every-n-blocks` | `0` | Create a snapshot every N blocks (0 = disabled). Only fires on live blocks — skipped during initial P2P sync. | +| `snapshot-dir` | — | Directory for auto-generated snapshot files. Auto-created if absent. | +| `snapshot-max-age-days` | `90` | Delete snapshots older than N days after creating a new one (0 = disabled). | +| `allow-snapshot-serving` | `false` | Enable serving snapshots over TCP to other nodes. | +| `allow-snapshot-serving-only-trusted` | `false` | Restrict snapshot serving to configured trusted peers only. | +| `snapshot-serve-endpoint` | `0.0.0.0:8092` | TCP endpoint for the snapshot serving listener. | +| `trusted-snapshot-peer` | — | Trusted peer endpoint for P2P snapshot sync (`IP:port`); may be repeated. | + +The `dlt-block-log-max-blocks` option (in the `chain` plugin config section) controls the rolling DLT block log size and is closely related to snapshot operation — see [DLT Rolling Block Log](#dlt-rolling-block-log) below. + +--- + +## Creating Snapshots + +### Method 1: One-shot (node stopped) + +Stop the node, then restart it with `--create-snapshot`. The node opens the existing database (replaying from the block log if needed), writes the snapshot, and exits before P2P or validator activate: + +```bash +vizd --create-snapshot /data/snapshots/viz-snapshot.json --plugin snapshot +``` + +### Method 2: At a specific block (no downtime) + +```ini +plugin = snapshot +snapshot-at-block = 5000000 +snapshot-dir = /data/snapshots +``` + +When block 5,000,000 is applied, the node writes `/data/snapshots/snapshot-block-5000000.json` without interrupting operation. + +### Method 3: Periodic (recommended) + +```ini +plugin = snapshot +snapshot-every-n-blocks = 28800 +snapshot-dir = /data/snapshots +snapshot-max-age-days = 90 +``` + +Files are named `snapshot-block-.json`. Recommended intervals: + +| Interval | Blocks | Time (at 3 s/block) | +|----------|--------|---------------------| +| Frequent | 10,000 | ~8 h | +| Daily | 28,800 | ~24 h | +| Weekly | 100,000 | ~3.5 days | + +### Method 4: Combining at-block and periodic + +Both options can be set together; `snapshot-at-block` fires once and `snapshot-every-n-blocks` fires repeatedly. + +### How snapshot creation works + +Snapshot creation is asynchronous and split into two phases to minimize impact: + +- **Phase 1 (read lock, ~1 s):** All 32 tracked object types are serialized into memory. Block processing waits during this phase; API and P2P reads proceed concurrently. +- **Phase 2 (no lock, ~2 s):** Compression, SHA-256 checksum computation, and file I/O. Normal node operation resumes. + +If creation fails (e.g., disk full), the error is logged and the node continues running. + +--- + +## Loading from a Snapshot (DLT Mode) + +### First-time startup + +```bash +vizd --snapshot /path/to/snapshot.json --plugin snapshot +``` + +On first load: +1. The snapshot plugin validates the file header (format version, chain ID, SHA-256 checksum). +2. Shared memory is wiped and re-initialized from snapshot state. +3. All 32 object types are imported. +4. LIB is promoted to `head_block_num` so P2P sync starts from the snapshot point. +5. The snapshot file is renamed to `.used` to prevent re-import on restart. +6. P2P plugin starts and syncs forward from the snapshot head. + +### Restart safety + +The node is safe to restart with `--snapshot` still on the command line (e.g., via `VIZD_EXTRA_OPTS`): + +| Scenario | Behavior | +|----------|----------| +| First start (no shared memory, file exists) | Imports snapshot; renames to `.used` | +| Restart (shared memory exists) | Skips import — uses existing state | +| Restart (shared memory wiped, file already `.used`) | Skips import — "snapshot file not found" | +| Force re-import | Use `--resync-blockchain` + provide a fresh snapshot file | + +### DLT mode + +After snapshot import the node runs in **DLT mode**: the main `block_log` is empty. A separate [DLT rolling block log](#dlt-rolling-block-log) stores recent blocks for P2P serving and local block queries. The mode is detected automatically on subsequent restarts (empty `block_log` + existing chainbase state). + +--- + +## Snapshot File Format + +Each snapshot is a single JSON file: + +```json +{ + "header": { + "version": 1, + "chain_id": "...", + "snapshot_block_num": 12345678, + "snapshot_block_id": "...", + "snapshot_block_time": "2025-01-01T00:00:00", + "last_irreversible_block_num": 12345660, + "payload_checksum": "sha256...", + "object_counts": { "account": 50000, ... } + }, + "state": { + "dynamic_global_property": [...], + "account": [...], + ... + } +} +``` + +### Included object types (32 total) + +**Critical (11)** — consensus-essential: +`dynamic_global_property`, `witness_schedule`, `hardfork_property`, `account`, `account_authority`, `validator`, `witness_vote`, `block_summary`, `content`, `content_vote`, `block_post_validation` + +**Important (15)** — required for full operation: +`transaction`, `vesting_delegation`, `vesting_delegation_expiration`, `fix_vesting_delegation`, `withdraw_vesting_route`, `escrow`, `proposal`, `required_approval`, `committee_request`, `committee_vote`, `invite`, `award_shares_expire`, `paid_subscription`, `paid_subscribe`, `witness_penalty_expire` + +**Optional (5)** — metadata and recovery: +`content_type`, `account_metadata`, `master_authority_history`, `account_recovery_request`, `change_recovery_account_request` + +--- + +## DLT Rolling Block Log + +In DLT mode the main `block_log` is empty. The **DLT rolling block log** (`dlt_block_log.log` + `dlt_block_log.index`) stores the most recent irreversible blocks. + +This enables: +- **P2P block serving** — peers requesting recent blocks for fork resolution and sync catch-up. +- **Local block queries** — API calls like `get_block` work for the stored range. + +### Configuration + +```ini +# Keep the last 100,000 blocks (default) +dlt-block-log-max-blocks = 100000 + +# Disable the DLT block log entirely +# dlt-block-log-max-blocks = 0 +``` + +The log uses a rolling window: when it exceeds `dlt-block-log-max-blocks`, old blocks are pruned from the front. On restart, the log is preserved and new blocks are appended from where they left off. + +### Stale snapshot detection + +If the latest snapshot's block number is less than the DLT log's start block, downloading nodes cannot bridge the gap (snapshot at block N, DLT log starts at M > N, blocks N+1..M-1 missing). The plugin detects this at startup and creates an urgent fresh snapshot on the first live block after sync completes. + +--- + +## Crash Recovery: `--replay-from-snapshot` + +When `shared_memory.bin` becomes corrupted and the node cannot start normally, `--replay-from-snapshot` combines snapshot import with DLT block log replay: + +```bash +# Specify the snapshot explicitly +vizd --replay-from-snapshot --snapshot /data/snapshots/snapshot-block-79273800.vizjson --plugin snapshot + +# Or auto-discover the latest snapshot +vizd --replay-from-snapshot --snapshot-auto-latest --plugin snapshot +``` + +**Recovery steps:** +1. Wipes shared memory (always — assumes corruption). +2. Imports the snapshot. +3. Replays blocks from `dlt_block_log` beyond the snapshot head, restoring up to the latest available state. +4. Resumes P2P sync from the replayed head block. + +### Comparison: `--snapshot` vs `--replay-from-snapshot` + +| Aspect | `--snapshot` | `--replay-from-snapshot` | +|--------|-------------|--------------------------| +| Purpose | Bootstrap a new node | Recover from corruption | +| Shared memory check | Skips if already exists | Always wipes and re-imports | +| Snapshot file rename | Renames to `.used` | Does NOT rename | +| DLT block log replay | No | Yes | +| Typical use | First-time setup | Crash recovery | + +### Example recovery + +A DLT node crashes at block 79,274,318. State: +``` +snapshots/snapshot-block-79273800.vizjson ← 518 blocks behind crash point +blockchain/dlt_block_log.log ← contains blocks 79174319..79274318 +blockchain/shared_memory.bin ← CORRUPTED +``` + +Recovery command: `vizd --replay-from-snapshot --snapshot-auto-latest --plugin snapshot` + +``` +Loading state from snapshot: snapshot-block-79273800.vizjson +Snapshot loaded at block 79273800, elapsed time 12.3 sec +Replaying dlt_block_log from block 79273801 to 79274318 (518 blocks)... +Done replaying, head_block=79274318, elapsed time: 7.3 sec +``` + +P2P sync fills the gap to the live chain head. + +--- + +## Automatic Runtime Recovery: `--auto-recover-from-snapshot` + +Enabled by default. When shared memory corruption is detected during block processing or block generation, the node: + +1. Closes the database. +2. Finds the latest snapshot in `snapshot-dir`. +3. Wipes shared memory, imports the snapshot, replays `dlt_block_log`. +4. Resumes P2P sync — no restart required. + +**Prerequisites:** +- `plugin = snapshot` must be enabled. +- Snapshots must exist in `snapshot-dir`. Use `snapshot-every-n-blocks` to create them automatically. +- `dlt_block_log` should cover blocks beyond the snapshot for minimal data loss. + +If recovery fails (no snapshot found, import error), the node logs an error and shuts down cleanly. + +To disable: `--no-auto-recover-from-snapshot` + +--- + +## P2P Snapshot Sync + +Nodes can serve and download snapshots over TCP, enabling fully automated bootstrap without manual file transfers. + +### Server configuration + +```ini +plugin = snapshot +allow-snapshot-serving = true +snapshot-serve-endpoint = 0.0.0.0:8092 +snapshot-every-n-blocks = 28800 +snapshot-dir = /data/snapshots + +# Optional: restrict to trusted peers only +# allow-snapshot-serving-only-trusted = true +# trusted-snapshot-peer = 1.2.3.4:8092 +``` + +### Client configuration + +```ini +plugin = snapshot +trusted-snapshot-peer = seed1.viz.world:8092 +trusted-snapshot-peer = seed2.viz.world:8092 +sync-snapshot-from-trusted-peer = true +``` + +`sync-snapshot-from-trusted-peer` defaults to `false` — must be explicitly enabled to prevent accidental state wipe. + +### How it works + +1. **Query:** Connects to each trusted peer, sends a `snapshot_info_request`, collects metadata (block number, checksum, size). +2. **Select:** Picks the peer with the highest block number. +3. **Download:** Downloads in 1 MB chunks; progress is logged to console every 5%. +4. **Verify:** SHA-256 checksum verified via streaming (no full-file load into memory). +5. **Import:** Wipes state, loads the verified snapshot, initializes hardforks. + +All operations happen inside `chain::plugin_startup()`, before P2P and validator activate. The node is fully blocked until import completes. + +### Security + +- Maximum download size: 2 GB. +- Max 5 concurrent connections per server, each with a 60-second connection deadline. +- Rate limiting per IP. +- Control messages limited to 64 KB; only data replies allow up to 64 MB. +- The TCP server runs on a dedicated thread, independent of the main I/O loop. + +--- + +## Recommended Production Config + +```ini +plugin = snapshot + +# Daily snapshots (~24 h at 3 s/block) +snapshot-every-n-blocks = 28800 +snapshot-dir = /data/viz-snapshots + +# Keep 90 days of snapshots (default) +snapshot-max-age-days = 90 + +# DLT rolling block log: last 100k blocks (default) +dlt-block-log-max-blocks = 100000 +``` + +--- + +## Troubleshooting + +| Symptom | Check | +|---------|-------| +| Node re-imports snapshot on every restart | `shared_memory.bin` is being deleted between restarts; or snapshot file was never renamed to `.used` | +| `Snapshot file not found` on start | File was already renamed to `.used` from a previous successful import; or wrong path | +| `Chain ID mismatch` on snapshot load | Snapshot was created from a different chain; cannot import | +| `Checksum mismatch` | Snapshot file is corrupt or partially transferred | +| Snapshot creation never fires | `snapshot-dir` not set, or node is still in P2P sync (periodic snapshots skip sync mode) | +| Stale snapshot warning at startup | Latest snapshot is older than `dlt_block_log` start; node will create a fresh snapshot after the next live block | +| Auto-recovery fires but fails | No snapshots in `snapshot-dir`; check that `snapshot-every-n-blocks` is configured | +| P2P snapshot download fails | Check `trusted-snapshot-peer` is reachable on port 8092; check `allow-snapshot-serving = true` on server | + +--- + +See also: [Snapshot](../node/snapshot.md), [Validator Plugin](./validator.md), [Chain Plugin](./chain.md), [P2P Overview](../p2p/overview.md). diff --git a/docs/plugins/validator.md b/docs/plugins/validator.md new file mode 100644 index 0000000000..c6202b8098 --- /dev/null +++ b/docs/plugins/validator.md @@ -0,0 +1,226 @@ +# Validator Plugin + +The validator plugin handles block signing and production. It runs a dedicated 250 ms timer loop on its own OS thread, executes a series of safety checks on every tick, and calls `database::generate_block()` when all conditions are met. + +**Source:** [plugins/validator/validator.cpp](../../plugins/validator/validator.cpp) + +--- + +## Dependencies + +``` +chain::plugin, p2p::p2p_plugin, snapshot::snapshot_plugin +``` + +--- + +## Configuration + +### Block production + +| Option | Default | Description | +|--------|---------|-------------| +| `validator` / `-w` | — | Validator account name(s); may be repeated | +| `private-key` | — | WIF private key(s) for signing; may be repeated | +| `emergency-private-key` | — | WIF key for emergency consensus; auto-adds `CHAIN_EMERGENCY_WITNESS_ACCOUNT` to the validator set | +| `enable-stale-production` | `false` | Bypass participation and sync checks (testnet / network recovery only) | +| `required-participation` | `3300` | Minimum validator participation in **basis points** (3300 = 33%) | +| `fork-collision-timeout-blocks` | `21` | Consecutive fork-collision deferrals before forcing production (one full validator round) | + +### NTP synchronization + +| Option | Default | Description | +|--------|---------|-------------| +| `ntp-server` | `pool.ntp.org`, `time.google.com`, `time.cloudflare.com` | NTP servers; may be repeated | +| `ntp-request-interval` | `900` | Normal sync interval in seconds | +| `ntp-retry-interval` | `300` | Retry interval when no NTP reply | +| `ntp-round-trip-threshold` | `150` | Discard NTP replies with round-trip > N ms | +| `ntp-history-size` | `5` | Moving-average window for NTP delta smoothing | + +### Debug + +| Option | Default | Description | +|--------|---------|-------------| +| `debug-block-production` | `false` | Enable verbose debug logging in the chain database | + +--- + +## Production Timer + +The production loop runs on a **dedicated `production_io_service_`** and its own OS thread — completely separate from the AppBase/P2P shared io_service. This prevents P2P activity (peer disconnects, TLS handshakes, send-queue drains) from delaying the 250 ms timer callback. + +**Tick alignment:** +``` +timer tick every 250 ms aligned to wall-clock 250 ms boundaries +minimum sleep: 50 ms (to absorb OS jitter) +``` + +**Look-ahead:** `now = ntp_time + 250 ms` — shifts the production decision forward so the tick at `T_slot - 250 ms` aligns exactly to the slot boundary: +``` +Slot at T=6.000s: + Tick at T=5.750 → now=6.000 → slot matched → produce at lag=0ms + Tick at T=6.000 → now=6.250 → lag=250ms → still within 500ms threshold +``` + +**Lag skip:** After a `lag` result, the same slot would re-trigger every tick for the rest of the 3-second slot interval. A guard skips ahead to the next slot boundary so the loop yields CPU instead of spinning. + +--- + +## `maybe_produce_block()` — Safety Check Sequence + +The following checks run **in order** on every tick where `slot > 0`. + +| # | Check | Failure result | +|---|-------|---------------| +| 1 | DLT sync gate (DLT mode only): `chain().is_syncing()` is false, or this node is the emergency master | `not_synced` | +| 2 | Snapshot pause gate: `snapshot().is_snapshot_in_progress()` is false | `not_synced` | +| 3 | P2P catchup gate: `p2p().is_catching_up_after_pause()` is false | `not_synced` | +| 4 | HF12 three-state safety (see below) | `not_synced` / `low_participation` | +| 5 | `slot = db.get_slot_at_time(now) > 0` | `not_time_yet` | +| 6 | Scheduled validator is in our configured set | `not_my_turn` | +| 7 | Slot not already filled (`scheduled_time > head_block_time`) | `not_time_yet` | +| 8 | Validator's on-chain `signing_key` is non-zero | `not_my_turn` | +| 9 | Private key for `signing_key` is loaded | `no_private_key` | +| 10 | Pre-HF12: participation ≥ threshold | `low_participation` | +| 11 | `|scheduled_time - now| ≤ 500 ms` | `lag` | +| 12 | Fork collision check (see below) | `fork_collision` | +| 13 | Second snapshot pause check (race window) | `not_time_yet` | +| 14 | `db.generate_block()` + `p2p().broadcast_block()` | `produced` | + +### HF12 three-state safety (check #4) + +**Emergency consensus active:** +- Emergency master (has `emergency-private-key` + committee in schedule): proceeds unconditionally. +- Slave: requires `get_slot_time(1) >= now` (chain not stale) before producing. + +**Normal mode (HF12+):** +- Participation ≥ 33%: healthy network; sync check via `get_slot_time(1)`. +- Participation < 33%: distressed network; participation vs `required-participation` threshold applies. +- `enable-stale-production=true`: bypasses both participation and sync checks. + +**Pre-HF12:** Simple sync check via `get_slot_time(1)`. + +### Fork collision resolution (check #12) + +When a competing block exists at `head_block_num + 1`: + +1. **Vote-weighted comparison (HF12+):** `compare_fork_branches()` computes total SHARES delegated to each branch. If our branch is heavier, proceed and remove the competing block. If tied or lighter, defer. +2. **Stuck-head timeout:** After `fork-collision-timeout-blocks` consecutive deferrals (default 21 = 63 seconds), the competing block is removed and production resumes. This handles dead-fork blocks from disconnected peers. + +**Emergency mode:** Any competing block triggers a defer; the vote-weight path is not taken. + +--- + +## Minority Fork Detection + +Before each production attempt (after HF12 safety checks), the plugin walks the last 21 blocks in `fork_db`. If all 21 were produced by the node's own configured validators, the node is isolated on a minority fork. + +- **Default action:** Call `p2p().resync_from_lib()` — pop blocks to LIB, reset fork DB, re-initiate P2P sync, reconnect seed nodes. Returns `minority_fork`. +- **With `enable-stale-production=true`:** Log a warning, continue producing. +- **Skipped when:** Emergency consensus is active (committee blocks would always match our configured set). A DLT-specific slave isolation check replaces it in emergency mode. + +--- + +## NTP Stall Detection + +If `get_slot_at_time(now)` returns 0 (NTP behind chain time), the `_slot_zero_streak` counter increments: + +| Streak | Time | Action | +|--------|------|--------| +| 3 | ~750 ms | Warning | +| 10 | ~2.5 s | Force NTP resync | +| 60 | ~15 s | Prolonged stall warning | +| 120 | ~30 s | Critical error | + +The counter resets on any non-zero slot result. + +--- + +## Production Watchdog + +If the node has ever produced a block and `should_be_producing` is true (derived from live chain state: participation ≥ 33% or emergency consensus active with our key), but no block has been produced for: +- Emergency master: **60 seconds** +- Regular validator: **180 seconds** + +The watchdog fires every 30 seconds and logs a diagnostic. If recovery conditions are met (head advancing in the last 30 s, not syncing, has peer connections, has non-zero signing keys on-chain), it force-clears blocking conditions: + +1. Clears `_minority_fork_recovering` flag. +2. Calls `p2p().clear_catchup_flag()` — clears the P2P post-pause catchup flag. +3. Calls `chain().clear_syncing()` — clears the chain sync flag. + +Production resumes automatically on the next tick. + +--- + +## `on_block_applied()` — Signal Handler + +Connected to `database::applied_block`. Runs for every incoming block. + +### Missed slot detection + +When `block_num > prev_num + 1` (gap in block stream), the handler determines whether our validator was scheduled for any of the missed slots and logs full diagnostic state (production flags, NTP offset, sync status, signing key status, next slot time). + +### Slot hijack detection (DLT emergency mode) + +When emergency consensus is active, the emergency master may blank our validator's signing key and produce committee blocks in our scheduled slots. The handler tracks this via `_slot_hijack_count`. Resets when one of our own validators produces a block. + +--- + +## Public API + +### `is_witness_scheduled_soon()` + +Returns `true` if a locally-controlled validator is scheduled to produce in the next 4 slots (~12 seconds). The snapshot plugin calls this before scheduling a snapshot to defer if production is imminent. + +### `is_emergency_master()` + +Returns `true` when: +1. `emergency-private-key` is configured (`CHAIN_EMERGENCY_WITNESS_ACCOUNT` in `_witnesses`). +2. The "committee" account is in the current validator schedule. + +Only nodes where both conditions hold should produce solo during emergency mode; others are followers and must sync first. + +### `is_emergency_key_configured()` + +Returns `true` if `emergency-private-key` is configured, regardless of the current schedule. Used in P2P hello messages (`has_emergency_key` field). + +### `get_production_diagnostics()` + +Returns a compact diagnostic string: +``` +validator[skip_flags=0x0 catching_up=0 head=#79881136 last_prod=45s_ago minority_rcv=0 slot_hijacks=0] +``` +Included in P2P FORWARD stagnation logs when the node is stuck with no peer ahead. + +--- + +## Key Invariants + +1. **Never produce in DLT mode while syncing** — creates blocks on a stale head, causing fork oscillation. +2. **Never produce while snapshot is in progress** — write-lock deadlock. +3. **Never produce if the slot is already filled** — creates a micro-fork. +4. **Emergency master must always produce** — it is the sole block producer; waiting would deadlock. +5. **Slaves must sync before producing in emergency mode** — producing on stale head = minority fork. +6. **Participation < 33% stops production** — network partition guard (overridable). +7. **21 consecutive own-validator blocks → rollback to LIB** — minority fork recovery. +8. **All database reads are fresh** — no state caching; emergency mode can activate/deactivate any block. + +--- + +## Troubleshooting + +| Symptom | Check | +|---------|-------| +| `not_synced` logs | DLT sync active or snapshot in progress — wait; watchdog will auto-clear if stuck | +| `not_time_yet` repeated | NTP behind chain time; check `_slot_zero_streak` warnings and NTP offset | +| `not_my_turn` on our slot | Signing key blanked on-chain; send `validator_update_operation` to restore | +| `no_private_key` | Config missing `private-key` for the signing key that's registered on-chain | +| `low_participation` | Network participation < 33%; check peer connectivity or set `enable-stale-production=true` | +| `fork_collision` | Competing block at next height; wait for vote-weight resolution or 21-deferral timeout | +| `minority_fork` | Isolated; plugin auto-resyncs to LIB | +| Watchdog fires repeatedly | Sync or catchup flag stuck; watchdog auto-clears if head is advancing | +| `SLOT-HIJACK` logs | Emergency master blanked our key; restore via `validator_update_operation` | + +--- + +See also: [Validator Guard](../node/validator-guard.md), [Fair-DPOS](../consensus/fair-dpos.md), [Emergency Consensus](../consensus/emergency-consensus.md), [Block Processing](../consensus/block-processing.md). diff --git a/docs/plugins/webserver.md b/docs/plugins/webserver.md new file mode 100644 index 0000000000..59fe4c630b --- /dev/null +++ b/docs/plugins/webserver.md @@ -0,0 +1,117 @@ +# Webserver Plugin + +The webserver plugin exposes HTTP and WebSocket endpoints that forward JSON-RPC requests to the `json_rpc` plugin. It includes a response cache keyed on `method + params` (not `id`) that is invalidated on each applied block, and a thread pool for concurrent request handling. + +**Source:** [plugins/webserver/webserver_plugin.cpp](../../plugins/webserver/webserver_plugin.cpp) + +--- + +## Dependencies + +``` +json_rpc::plugin +``` + +--- + +## Configuration + +| Option | Default | Description | +|--------|---------|-------------| +| `webserver-http-endpoint` | — | HTTP listen address, e.g. `0.0.0.0:8090` | +| `webserver-ws-endpoint` | — | WebSocket listen address, e.g. `0.0.0.0:8091` | +| `webserver-thread-pool-size` | `256` | Worker threads for handling HTTP and WebSocket requests. | +| `webserver-cache-enabled` | `true` | Enable the response cache. | +| `webserver-cache-size` | `10000` | Maximum number of cached responses. Cache is evicted entirely when this limit is reached. | + +At least one of `webserver-http-endpoint` or `webserver-ws-endpoint` must be set for the plugin to do anything. Both can be enabled simultaneously. + +--- + +## Minimal config.ini + +```ini +plugin = webserver + +webserver-http-endpoint = 0.0.0.0:8090 +webserver-ws-endpoint = 0.0.0.0:8091 +``` + +--- + +## Response Cache + +### What is cached + +Every read-only JSON-RPC response is eligible for caching. The cache key is a SHA-256 hash of `method + params` — deliberately **excluding `id`** so that rotating the request `id` cannot bypass the cache. + +### What is never cached + +| Namespace | Reason | +|-----------|--------| +| `network_broadcast_api.*` | State-changing (transaction/block broadcast) | +| `debug_node.*` | State-changing debug operations | +| Malformed / unparseable requests | No reliable key can be derived | + +Batch requests (JSON array) are treated as a single atomic cache entry keyed on the full array hash. + +### Invalidation + +The cache is cleared on every applied block. Responses are never served stale beyond one block interval (3 seconds). + +### Disabling the cache + +```ini +webserver-cache-enabled = false +``` + +Disable for nodes that serve highly latency-sensitive or real-time clients where a 3-second cache window is unacceptable. + +--- + +## Thread Pool + +HTTP and WebSocket servers each run on a dedicated `io_service` instance. Incoming requests are dispatched to a shared thread pool of `webserver-thread-pool-size` workers. + +**Sizing guidance:** +- Public API node with mixed read/write traffic: `256` (default) is sufficient for most workloads. +- High-throughput nodes: increase to `512` or more. Monitor CPU saturation — more threads than CPU cores do not help for CPU-bound operations. +- Development / local node: `4`–`8` is adequate. + +--- + +## WebSocket Subscriptions + +WebSocket clients can register callbacks: + +| Method | Description | +|--------|-------------| +| `database_api.set_block_applied_callback` | Called on every applied block with the block header | +| `database_api.set_pending_transaction_callback` | Called when a transaction enters the pending pool | +| `database_api.cancel_all_subscriptions` | Unsubscribe from all callbacks | + +Subscriptions require a persistent WebSocket connection. They are not available over plain HTTP. + +--- + +## Security + +- **Bind to localhost** (`127.0.0.1`) and use a reverse proxy (nginx/Caddy) for public exposure. Binding to `0.0.0.0` exposes the RPC directly to the network. +- The plugin has no built-in authentication or rate limiting. Apply those at the reverse proxy layer. +- Mutating methods (`network_broadcast_api`, `debug_node`) are blocked from cache poisoning by design, but they remain callable from any connected client — restrict access at the network level if needed. + +--- + +## Troubleshooting + +| Symptom | Check | +|---------|-------| +| Port already in use on startup | Another process is bound to the configured port; change the port or kill the conflicting process | +| High memory usage | Reduce `webserver-cache-size` or disable caching | +| Slow responses under load | Increase `webserver-thread-pool-size`; check CPU saturation | +| WebSocket subscriptions not firing | Subscriptions require a WebSocket connection, not HTTP | +| Stale responses | If `webserver-cache-enabled = true`, responses are fresh within one block interval (~3 s); for real-time use disable the cache | + +--- + +See also: [Plugin Overview](./overview.md), [Database API](./database-api.md), [JSON-RPC API](../api/json-rpc.md). diff --git a/docs/protocol/data-types.md b/docs/protocol/data-types.md new file mode 100644 index 0000000000..8dd90ae123 --- /dev/null +++ b/docs/protocol/data-types.md @@ -0,0 +1,257 @@ +# Common Data Types + +All shared data types used across VIZ Ledger protocol operations and virtual operations. + +--- + +## Primitive Types + +| C++ type | JSON representation | Description | +|----------|---------------------|-------------| +| `string` | `string` | UTF-8 string | +| `bool` | `boolean` | `true` / `false` | +| `uint8_t` | `integer` | Unsigned 8-bit integer | +| `uint16_t` | `integer` | Unsigned 16-bit integer (0–65535) | +| `int16_t` | `integer` | Signed 16-bit integer (−32768–32767) | +| `uint32_t` | `integer` | Unsigned 32-bit integer | +| `int32_t` | `integer` | Signed 32-bit integer | +| `uint64_t` | `string` or `integer` | Unsigned 64-bit integer — use string in JavaScript to avoid overflow | +| `int64_t` | `string` or `integer` | Signed 64-bit integer | +| `share_type` | `integer` | Alias for `safe` — token amount in satoshi units | +| `time_point_sec` | `string` | ISO 8601 UTC datetime: `"2024-01-15T12:00:00"` (no timezone suffix) | + +--- + +## `account_name_type` + +A fixed-length string (max 16 bytes) identifying an account. Rules: + +- Dot-separated labels; each label is at least 3 characters. +- Begins with a letter, ends with a letter or digit. +- Only lowercase letters (`a`–`z`), digits (`0`–`9`), hyphens (`-`). +- Minimum length: 2 characters (`CHAIN_MIN_ACCOUNT_NAME_LENGTH`). +- Maximum length: 16 characters (`CHAIN_MAX_ACCOUNT_NAME_LENGTH`). + +**JSON:** plain string — `"alice"`, `"alice.bob"` + +--- + +## `public_key_type` + +A secp256k1 compressed public key encoded as base58check with a `VIZ` prefix. + +**JSON:** string — `"VIZ5hqSa4NkEZGAMUpoH5EaEr64mBJuMcPpGjvk8qb7hcPFTbXSQ9"` + +- Prefix must be `VIZ` (not `STM`, `GLS`, or any other). +- Encoded from a 33-byte compressed public key + 4-byte checksum = 37 bytes total, base58-encoded. + +--- + +## `asset` + +Represents a token amount with its symbol. In JSON API responses and operation parameters, serialized as a human-readable string: + +``` +"10.000 VIZ" +"5.000000 SHARES" +``` + +### Token symbols + +| Symbol | String | Decimals | Description | +|--------|--------|----------|-------------| +| `TOKEN_SYMBOL` | `VIZ` | 3 | Main liquid token | +| `SHARES_SYMBOL` | `SHARES` | 6 | Vesting shares (staked VIZ) | + +When constructing operations, always use the string format. Parse by splitting on the space character: amount part left, symbol right. VIZ uses 3 decimal places; SHARES uses 6. + +--- + +## `authority` + +Multi-signature authority structure controlling an account permission level. + +```json +{ + "weight_threshold": 1, + "account_auths": [ + ["alice", 1] + ], + "key_auths": [ + ["VIZ5hqSa4NkEZGAMUpoH5EaEr64mBJuMcPpGjvk8qb7hcPFTbXSQ9", 1] + ] +} +``` + +| Field | Type | Description | +|-------|------|-------------| +| `weight_threshold` | `uint32_t` | Minimum total weight required to satisfy authority | +| `account_auths` | `[[account_name, weight], ...]` | Account-based signers | +| `key_auths` | `[[public_key, weight], ...]` | Key-based signers | + +The sum of weights for the satisfied signers must be ≥ `weight_threshold`. An empty authority is `{ "weight_threshold": 0, "account_auths": [], "key_auths": [] }`. + +### Authority levels + +| Level | Used for | +|-------|---------| +| `master` | Highest security — changing keys, account recovery | +| `active` | Token operations — transfer, vesting, validator voting | +| `regular` | Social operations — content, awards, committee voting | + +--- + +## `beneficiary_route_type` + +Specifies a beneficiary and their reward share for content payouts. + +```json +{ "account": "alice", "weight": 2500 } +``` + +| Field | Type | Description | +|-------|------|-------------| +| `account` | `account_name_type` | Beneficiary account | +| `weight` | `uint16_t` | Share in basis points (10000 = 100%) | + +- The sum of all beneficiary weights must not exceed 10000. +- Beneficiaries must be sorted by account name (ascending) in the array. +- Each beneficiary account must exist on-chain. + +--- + +## `extensions_type` + +Currently unused — always serialized as an empty array. + +```json +"extensions": [] +``` + +--- + +## `versioned_chain_properties` + +A static variant holding one of the chain properties versions. Serialized as a 2-element array `[type_index, object]`. + +| Index | Type | +|-------|------| +| 0 | `chain_properties_init` | +| 1 | `chain_properties_hf4` | +| 2 | `chain_properties_hf6` | +| 3 | `chain_properties_hf9` (current) | + +See [Chain Properties](../governance/chain-properties.md) for the full field reference per version. + +--- + +## `operation` (static variant) + +Every operation is serialized as a 2-element array: `[type_id, operation_object]`. + +### Regular operations (user-broadcast) + +| ID | Operation | +|----|-----------| +| 0 | `vote_operation` *(deprecated)* | +| 1 | `content_operation` *(deprecated)* | +| 2 | `transfer_operation` | +| 3 | `transfer_to_vesting_operation` | +| 4 | `withdraw_vesting_operation` | +| 5 | `account_update_operation` | +| 6 | `witness_update_operation` | +| 7 | `account_witness_vote_operation` | +| 8 | `account_witness_proxy_operation` | +| 9 | `delete_content_operation` *(deprecated)* | +| 10 | `custom_operation` | +| 11 | `set_withdraw_vesting_route_operation` | +| 12 | `request_account_recovery_operation` | +| 13 | `recover_account_operation` | +| 14 | `change_recovery_account_operation` | +| 15 | `escrow_transfer_operation` | +| 16 | `escrow_dispute_operation` | +| 17 | `escrow_release_operation` | +| 18 | `escrow_approve_operation` | +| 19 | `delegate_vesting_shares_operation` | +| 20 | `account_create_operation` | +| 21 | `account_metadata_operation` | +| 22 | `proposal_create_operation` | +| 23 | `proposal_update_operation` | +| 24 | `proposal_delete_operation` | +| 25 | `chain_properties_update_operation` | +| 35 | `committee_worker_create_request_operation` | +| 36 | `committee_worker_cancel_request_operation` | +| 37 | `committee_vote_request_operation` | +| 43 | `create_invite_operation` | +| 44 | `claim_invite_balance_operation` | +| 45 | `invite_registration_operation` | +| 46 | `versioned_chain_properties_update_operation` | +| 47 | `award_operation` | +| 50 | `set_paid_subscription_operation` | +| 51 | `paid_subscribe_operation` | +| 54 | `set_account_price_operation` | +| 55 | `set_subaccount_price_operation` | +| 56 | `buy_account_operation` | +| 58 | `use_invite_balance_operation` | +| 60 | `fixed_award_operation` | +| 61 | `target_account_sale_operation` | + +### Virtual operations (blockchain-generated, not broadcastable) + +| ID | Operation | +|----|-----------| +| 26 | `author_reward_operation` | +| 27 | `curation_reward_operation` | +| 28 | `content_reward_operation` | +| 29 | `fill_vesting_withdraw_operation` | +| 30 | `shutdown_witness_operation` | +| 31 | `hardfork_operation` | +| 32 | `content_payout_update_operation` | +| 33 | `content_benefactor_reward_operation` | +| 34 | `return_vesting_delegation_operation` | +| 38 | `committee_cancel_request_operation` | +| 39 | `committee_approve_request_operation` | +| 40 | `committee_payout_request_operation` | +| 41 | `committee_pay_request_operation` | +| 42 | `witness_reward_operation` | +| 48 | `receive_award_operation` | +| 49 | `benefactor_award_operation` | +| 52 | `paid_subscription_action_operation` | +| 53 | `cancel_paid_subscription_operation` | +| 57 | `account_sale_operation` | +| 59 | `expire_escrow_ratification_operation` | +| 62 | `bid_operation` | +| 63 | `outbid_operation` | + +--- + +## Transaction Construction + +A signed transaction contains: + +| Field | Value | +|-------|-------| +| `ref_block_num` | `head_block_number & 0xFFFF` | +| `ref_block_prefix` | bytes 4–7 of `block_id` as little-endian `uint32` | +| `expiration` | UTC time string; max ~60 s from broadcast time recommended | +| `operations` | Array of `[type_id, object]` pairs | +| `extensions` | Always `[]` | +| `signatures` | Array of compact hex-encoded ECDSA signatures | + +**Signing:** `sha256(chain_id + serialized_transaction_body)` → compact ECDSA signature over secp256k1. + +**Private keys:** WIF format (base58check, version byte `0x80`). + +--- + +## Energy System + +Energy is used by award-type operations. + +- Stored in basis points: 0–10000 (0%–100%). +- Regenerates at 100% per 24 hours (`CHAIN_ENERGY_REGENERATION_SECONDS = 86400`). +- Current energy: `min(10000, last_energy + elapsed_seconds × 10000 / 86400)`. + +--- + +See also: [Operations Overview](./operations/overview.md), [Virtual Operations](./virtual-operations.md), [Chain Properties](../governance/chain-properties.md). diff --git a/docs/protocol/operations/account-market.md b/docs/protocol/operations/account-market.md new file mode 100644 index 0000000000..4dd007d84d --- /dev/null +++ b/docs/protocol/operations/account-market.md @@ -0,0 +1,120 @@ +# Account Market Operations + +The account market allows accounts and subaccount namespaces to be listed for sale and purchased on-chain. + +--- + +## `set_account_price_operation` (ID 54) + +**Auth:** `master` of `account` + +Lists an account for public sale or updates the listing. An `account_on_sale_fee` is charged on listing. + +| Field | Type | Description | +|-------|------|-------------| +| `account` | `account_name_type` | Account being listed | +| `account_seller` | `account_name_type` | Account to receive the payment (may differ from `account`) | +| `account_offer_price` | `asset` (VIZ) | Asking price | +| `account_on_sale` | `bool` | `true` to list; `false` to delist | + +```json +[54, { + "account": "alice", + "account_seller": "alice", + "account_offer_price": "1000.000 VIZ", + "account_on_sale": true +}] +``` + +- `account_on_sale: false` delists without a fee refund. +- `account_seller` can be any account — useful for brokered sales. + +--- + +## `set_subaccount_price_operation` (ID 55) + +**Auth:** `master` of `account` + +Lists the right to create subaccounts (e.g., `account.childname`) for sale. A `subaccount_on_sale_fee` is charged on listing. + +| Field | Type | Description | +|-------|------|-------------| +| `account` | `account_name_type` | Parent account | +| `subaccount_seller` | `account_name_type` | Account to receive payments | +| `subaccount_offer_price` | `asset` (VIZ) | Price per subaccount creation right | +| `subaccount_on_sale` | `bool` | `true` to list; `false` to delist | + +```json +[55, { + "account": "alice", + "subaccount_seller": "alice", + "subaccount_offer_price": "50.000 VIZ", + "subaccount_on_sale": true +}] +``` + +- Buyers purchase the right to create one subaccount under `account`'s namespace per transaction. + +--- + +## `buy_account_operation` (ID 56) + +**Auth:** `active` of `buyer` + +Purchases an account currently listed for sale. All authorities are transferred to the buyer. + +| Field | Type | Description | +|-------|------|-------------| +| `buyer` | `account_name_type` | Purchasing account | +| `account` | `account_name_type` | Account being purchased | +| `account_offer_price` | `asset` (VIZ) | Purchase price (must exactly match the listing) | +| `account_authorities_key` | `public_key_type` | New key set as master, active, regular, and memo of the purchased account | +| `tokens_to_shares` | `asset` (VIZ) | Additional VIZ to convert to SHARES for the bought account (may be `"0.000 VIZ"`) | + +```json +[56, { + "buyer": "bob", + "account": "alice", + "account_offer_price": "1000.000 VIZ", + "account_authorities_key": "VIZ5newowner...", + "tokens_to_shares": "0.000 VIZ" +}] +``` + +- `account_offer_price` must exactly match the price in `set_account_price_operation`. +- `account_authorities_key` is applied to all four authority slots simultaneously. +- Payment is sent to `account_seller` as specified in the listing. +- Virtual `account_sale_operation` fires on successful purchase. + +--- + +## `target_account_sale_operation` (ID 61) + +**Auth:** `master` of `account` + +Lists an account for private (targeted) sale to a specific buyer only. A buyer other than `target_buyer` cannot purchase this listing. + +| Field | Type | Description | +|-------|------|-------------| +| `account` | `account_name_type` | Account being listed | +| `account_seller` | `account_name_type` | Account to receive payment | +| `target_buyer` | `account_name_type` | Only this account may buy | +| `account_offer_price` | `asset` (VIZ) | Asking price | +| `account_on_sale` | `bool` | `true` to list; `false` to delist | + +```json +[61, { + "account": "alice", + "account_seller": "alice", + "target_buyer": "charlie", + "account_offer_price": "500.000 VIZ", + "account_on_sale": true +}] +``` + +- `account_on_sale: false` cancels the targeted listing. +- The buyer uses the standard `buy_account_operation` to complete the purchase. + +--- + +See also: [Data Types](../data-types.md), [Operations Overview](./overview.md), [Virtual Operations](../virtual-operations.md), [Database API — Account Market](../../plugins/database-api.md#account-market). diff --git a/docs/protocol/operations/accounts.md b/docs/protocol/operations/accounts.md new file mode 100644 index 0000000000..79a834fedd --- /dev/null +++ b/docs/protocol/operations/accounts.md @@ -0,0 +1,96 @@ +# Account Operations + +--- + +## `account_create_operation` (ID 20) + +**Auth:** `active` of `creator` + +Creates a new blockchain account. The fee is converted to SHARES for the new account. + +| Field | Type | Description | +|-------|------|-------------| +| `fee` | `asset` (VIZ) | Creation fee ≥ chain `account_creation_fee` | +| `delegation` | `asset` (SHARES) | Initial SHARES delegation to new account | +| `creator` | `account_name_type` | Account paying the fee | +| `new_account_name` | `account_name_type` | Name for the new account | +| `master` | `authority` | Master authority | +| `active` | `authority` | Active authority | +| `regular` | `authority` | Regular authority | +| `memo_key` | `public_key_type` | Memo public key | +| `json_metadata` | `string` | JSON metadata (may be `""`) | +| `referrer` | `account_name_type` | Referrer account (may be `""`) | +| `extensions` | `extensions_type` | Always `[]` | + +```json +[20, { + "fee": "1.000 VIZ", + "delegation": "10.000000 SHARES", + "creator": "alice", + "new_account_name": "bob", + "master": { "weight_threshold": 1, "account_auths": [], "key_auths": [["VIZ5...", 1]] }, + "active": { "weight_threshold": 1, "account_auths": [], "key_auths": [["VIZ5...", 1]] }, + "regular": { "weight_threshold": 1, "account_auths": [], "key_auths": [["VIZ5...", 1]] }, + "memo_key": "VIZ5...", + "json_metadata": "", + "referrer": "", + "extensions": [] +}] +``` + +- All three authorities are required (even if identical keys are used). +- `fee.symbol` must be `VIZ`; `delegation.symbol` must be `SHARES`. + +--- + +## `account_update_operation` (ID 5) + +**Auth:** `master` of `account` (if `master` field present), otherwise `active` + +Updates an account's keys and metadata. + +| Field | Type | Description | +|-------|------|-------------| +| `account` | `account_name_type` | Account to update | +| `master` | `optional` | New master authority (omit if not changing) | +| `active` | `optional` | New active authority | +| `regular` | `optional` | New regular authority | +| `memo_key` | `public_key_type` | New memo key (required even if unchanged) | +| `json_metadata` | `string` | New JSON metadata | + +```json +[5, { + "account": "alice", + "active": { "weight_threshold": 1, "account_auths": [], "key_auths": [["VIZ5new...", 1]] }, + "memo_key": "VIZ5new...", + "json_metadata": "{\"profile\":\"updated\"}" +}] +``` + +- If `master` is present → sign with current **master** key. +- If `master` is absent → sign with current **active** key. +- `memo_key` is always required. + +--- + +## `account_metadata_operation` (ID 21) + +**Auth:** `regular` of `account` + +Updates only the account's JSON metadata. Lower bandwidth cost than `account_update`. + +| Field | Type | Description | +|-------|------|-------------| +| `account` | `account_name_type` | Account to update | +| `json_metadata` | `string` | New JSON metadata string | + +```json +[21, { + "account": "alice", + "json_metadata": "{\"name\":\"Alice\",\"about\":\"Hello!\"}" +}] +``` + +--- + +See also: [Data Types](../data-types.md), [Operations Overview](./overview.md). diff --git a/docs/protocol/operations/awards.md b/docs/protocol/operations/awards.md new file mode 100644 index 0000000000..f49faf7a46 --- /dev/null +++ b/docs/protocol/operations/awards.md @@ -0,0 +1,94 @@ +# Award Operations + +Awards are the primary social reward mechanism. An account spends *energy* to award SHARES directly to another account from the reward pool. Award size is proportional to the initiator's energy expenditure and SHARES stake. + +**Energy:** stored as basis points 0–10000; regenerates at 100% per 24 hours (`CHAIN_ENERGY_REGENERATION_SECONDS = 86400`). + +--- + +## `award_operation` (ID 47) + +**Auth:** `regular` of `initiator` + +Awards SHARES to `receiver`, spending a specified percentage of energy. The actual SHARES amount is determined by the initiator's stake and pool depth. + +| Field | Type | Description | +|-------|------|-------------| +| `initiator` | `account_name_type` | Account giving the award | +| `receiver` | `account_name_type` | Account receiving the award | +| `energy` | `uint16_t` | Energy to spend in basis points (1–10000) | +| `custom_sequence` | `uint64_t` | Application-defined sequence number (may be 0) | +| `memo` | `string` | Optional message or reason | +| `beneficiaries` | `vector` | Optional beneficiaries receiving a share of the award | + +```json +[47, { + "initiator": "alice", + "receiver": "bob", + "energy": 1000, + "custom_sequence": 0, + "memo": "great article!", + "beneficiaries": [] +}] +``` + +With beneficiaries: +```json +[47, { + "initiator": "alice", + "receiver": "bob", + "energy": 1000, + "custom_sequence": 1, + "memo": "", + "beneficiaries": [ + {"account": "charlie", "weight": 2000} + ] +}] +``` + +- `energy` = 10000 uses 100% of current energy. +- If beneficiaries are present, `receiver` gets `(10000 − sum_of_weights) / 10000` of the award. +- Beneficiary weights must sum to ≤ 10000; beneficiaries must be sorted by account name ascending. +- Virtual `receive_award_operation` fires for `receiver`. +- Virtual `benefactor_award_operation` fires for each beneficiary. + +--- + +## `fixed_award_operation` (ID 60) + +**Auth:** `regular` of `initiator` + +Awards a **fixed amount** of SHARES to `receiver`. Energy is consumed in proportion to the desired award size; `max_energy` caps the expenditure. + +| Field | Type | Description | +|-------|------|-------------| +| `initiator` | `account_name_type` | Account giving the award | +| `receiver` | `account_name_type` | Account receiving the award | +| `reward_amount` | `asset` (SHARES) | Fixed amount of SHARES to award | +| `max_energy` | `uint16_t` | Maximum energy to spend (basis points; 0 = no cap) | +| `custom_sequence` | `uint64_t` | Application-defined sequence number | +| `memo` | `string` | Optional message or reason | +| `beneficiaries` | `vector` | Optional beneficiaries | + +```json +[60, { + "initiator": "alice", + "receiver": "bob", + "reward_amount": "10.000000 SHARES", + "max_energy": 5000, + "custom_sequence": 1, + "memo": "fixed reward", + "beneficiaries": [ + {"account": "charlie", "weight": 1000} + ] +}] +``` + +- `reward_amount.symbol` must be `SHARES`. +- `max_energy = 0` means no energy cap — the operation will consume whatever energy is required. +- Actual energy consumed depends on the initiator's stake and the current reward pool depth. +- Beneficiary rules are identical to `award_operation`. + +--- + +See also: [Data Types](../data-types.md), [Operations Overview](./overview.md), [Virtual Operations](../virtual-operations.md). diff --git a/docs/protocol/operations/committee.md b/docs/protocol/operations/committee.md new file mode 100644 index 0000000000..7571cc1ed7 --- /dev/null +++ b/docs/protocol/operations/committee.md @@ -0,0 +1,103 @@ +# Committee Operations + +The committee (worker proposal) system lets community members request funding from the committee fund. SHARES holders vote to approve or reject requests; approved requests receive a payout from the fund. + +--- + +## `committee_worker_create_request_operation` (ID 35) + +**Auth:** `regular` of `creator` + +Creates a new funding request. A `committee_create_request_fee` is charged to the creator on submission. + +| Field | Type | Description | +|-------|------|-------------| +| `creator` | `account_name_type` | Account creating the request | +| `url` | `string` | URL describing the proposal (non-empty, max 255 bytes) | +| `worker` | `account_name_type` | Account that will receive the payout | +| `required_amount_min` | `asset` (VIZ) | Minimum acceptable payout | +| `required_amount_max` | `asset` (VIZ) | Maximum acceptable payout | +| `duration` | `uint32_t` | Request duration in seconds | + +```json +[35, { + "creator": "alice", + "url": "https://alice.example.com/proposal", + "worker": "alice", + "required_amount_min": "100.000 VIZ", + "required_amount_max": "500.000 VIZ", + "duration": 604800 +}] +``` + +**Constraints:** + +| Parameter | Value | +|-----------|-------| +| Minimum duration | 5 days (432000 s) | +| Maximum duration | 30 days (2592000 s) | +| `required_amount_max` | Must be > `required_amount_min` | + +- `required_amount_min` ≥ 0; `required_amount_max` > `required_amount_min`. +- `worker` may differ from `creator`. + +--- + +## `committee_worker_cancel_request_operation` (ID 36) + +**Auth:** `regular` of `creator` + +Cancels an existing funding request before it expires. + +| Field | Type | Description | +|-------|------|-------------| +| `creator` | `account_name_type` | Creator of the request | +| `request_id` | `uint32_t` | ID of the request to cancel | + +```json +[36, { + "creator": "alice", + "request_id": 42 +}] +``` + +- Only the `creator` of the request can cancel it. +- `request_id` must refer to an existing active request. + +--- + +## `committee_vote_request_operation` (ID 37) + +**Auth:** `regular` of `voter` + +Votes on a funding request. Voting power is proportional to the voter's SHARES stake. + +| Field | Type | Description | +|-------|------|-------------| +| `voter` | `account_name_type` | Account casting the vote | +| `request_id` | `uint32_t` | ID of the request | +| `vote_percent` | `int16_t` | Vote weight in basis points (−10000 to 10000) | + +```json +[37, { + "voter": "bob", + "request_id": 42, + "vote_percent": 10000 +}] +``` + +- `vote_percent` > 0 → support; `vote_percent` < 0 → oppose; `vote_percent` = 0 → remove vote. +- A request is approved when weighted net vote percent ≥ `committee_request_approve_min_percent` chain property. + +**Virtual operations triggered by committee lifecycle:** + +| Virtual Op | Trigger | +|-----------|---------| +| `committee_cancel_request_operation` | Request expires without approval | +| `committee_approve_request_operation` | Request reaches approval threshold | +| `committee_payout_request_operation` | Payout is processed | +| `committee_pay_request_operation` | Worker receives payment | + +--- + +See also: [Data Types](../data-types.md), [Operations Overview](./overview.md), [Virtual Operations](../virtual-operations.md), [Committee Governance](../../governance/committee.md). diff --git a/docs/protocol/operations/content.md b/docs/protocol/operations/content.md new file mode 100644 index 0000000000..8ecd08c2bd --- /dev/null +++ b/docs/protocol/operations/content.md @@ -0,0 +1,104 @@ +# Content Operations + +> **Deprecation notice:** `vote_operation` (ID 0), `content_operation` (ID 1), and `delete_content_operation` (ID 9) are **deprecated**. They remain in the operation variant for historical and archive compatibility but should not be used in new code. `custom_operation` (ID 10) is the active general-purpose data channel. + +--- + +## `content_operation` *(deprecated)* (ID 1) + +**Auth:** `regular` of `author` + +Creates or updates a content object (post or comment). Content is addressed by `author` + `permlink`. + +| Field | Type | Description | +|-------|------|-------------| +| `parent_author` | `account_name_type` | Author of parent content; `""` for a root post | +| `parent_permlink` | `string` | Permlink of parent; acts as category tag for root posts | +| `author` | `account_name_type` | Content author | +| `permlink` | `string` | Unique identifier within the author's namespace | +| `title` | `string` | Post title | +| `body` | `string` | Post body (Markdown) | +| `curation_percent` | `int16_t` | Curation reward share in basis points (0–10000) | +| `json_metadata` | `string` | JSON metadata string | +| `extensions` | `content_extensions_type` | Optional beneficiary list | + +Beneficiary extension format (inside `extensions`): +```json +[[0, { + "beneficiaries": [ + {"account": "bob", "weight": 2500} + ] +}]] +``` + +- `parent_author == ""` → root post; otherwise a comment. +- `permlink` must be unique per author. +- `curation_percent` must be within the chain's `[min_curation_percent, max_curation_percent]` range. +- Beneficiary weights must sum to ≤ 10000 and be sorted by account name ascending. + +--- + +## `vote_operation` *(deprecated)* (ID 0) + +**Auth:** `regular` of `voter` + +Casts a weighted vote on a piece of content. + +| Field | Type | Description | +|-------|------|-------------| +| `voter` | `account_name_type` | Voting account | +| `author` | `account_name_type` | Content author | +| `permlink` | `string` | Content permlink | +| `weight` | `int16_t` | Vote weight: negative = flag, positive = upvote, 0 = remove vote | + +- `weight` range: −10000 to 10000. +- Flag votes may incur extra energy cost (`flag_energy_additional_cost` chain property). + +--- + +## `delete_content_operation` *(deprecated)* (ID 9) + +**Auth:** `regular` of `author` + +Deletes a content object. + +| Field | Type | Description | +|-------|------|-------------| +| `author` | `account_name_type` | Content author | +| `permlink` | `string` | Content permlink to delete | + +- Content with a pending payout cannot be deleted. + +--- + +## `custom_operation` (ID 10) + +**Auth:** `active` or `regular` of signers (at least one) + +Posts arbitrary JSON data to the blockchain. Used by applications to build custom on-chain protocols. + +| Field | Type | Description | +|-------|------|-------------| +| `required_active_auths` | `flat_set` | Accounts requiring active-key signatures | +| `required_regular_auths` | `flat_set` | Accounts requiring regular-key signatures | +| `id` | `string` | Application-defined namespace identifier (max 32 characters) | +| `json` | `string` | Valid UTF-8 JSON payload | + +```json +[10, { + "required_active_auths": [], + "required_regular_auths": ["alice"], + "id": "my_app", + "json": "{\"action\":\"follow\",\"target\":\"bob\"}" +}] +``` + +- At least one of `required_active_auths` or `required_regular_auths` must be non-empty. +- Accounts in `required_active_auths` must sign with their **active** key. +- Accounts in `required_regular_auths` must sign with their **regular** key. +- Both sets may be populated simultaneously for multi-authority operations. +- The `json` field is counted as a data operation — may incur additional bandwidth cost (`data_operations_cost_additional_bandwidth` chain property). + +--- + +See also: [Data Types](../data-types.md), [Operations Overview](./overview.md), [Awards](./awards.md). diff --git a/docs/protocol/operations/escrow.md b/docs/protocol/operations/escrow.md new file mode 100644 index 0000000000..bfc405d3c3 --- /dev/null +++ b/docs/protocol/operations/escrow.md @@ -0,0 +1,158 @@ +# Escrow Operations + +Escrow holds VIZ tokens in a conditional transfer: funds are released only after approval by both the recipient and a neutral agent, or arbitrated by the agent in case of dispute. + +**Escrow flow:** +``` +escrow_transfer → escrow_approve (by both "to" and "agent") + → escrow_release (by "from" or "to") + → [escrow_dispute] → escrow_release (by "agent" only) + ↓ + (ratification deadline missed) + → expire_escrow_ratification_operation [virtual — funds return] +``` + +--- + +## `escrow_transfer_operation` (ID 15) + +**Auth:** `active` of `from` + +Creates an escrow. Funds leave `from` immediately into escrow balance; both `agent` and `to` must approve before release is possible. + +| Field | Type | Description | +|-------|------|-------------| +| `from` | `account_name_type` | Sender | +| `to` | `account_name_type` | Intended recipient | +| `agent` | `account_name_type` | Neutral escrow agent (arbitrator) | +| `escrow_id` | `uint32_t` | Unique ID chosen by sender (default 30) | +| `token_amount` | `asset` (VIZ) | Amount to hold in escrow | +| `fee` | `asset` (VIZ) | Agent fee — paid to agent upon approval | +| `ratification_deadline` | `time_point_sec` | Deadline for both parties to approve | +| `escrow_expiration` | `time_point_sec` | Expiry if escrow is never released | +| `json_metadata` | `string` | Optional terms / metadata | + +```json +[15, { + "from": "alice", + "to": "bob", + "agent": "charlie", + "escrow_id": 1001, + "token_amount": "100.000 VIZ", + "fee": "1.000 VIZ", + "ratification_deadline": "2024-06-01T00:00:00", + "escrow_expiration": "2024-07-01T00:00:00", + "json_metadata": "{\"description\":\"payment for work\"}" +}] +``` + +- `ratification_deadline` must be before `escrow_expiration`. +- Both timestamps must be in the future at broadcast time. +- `escrow_id` must be unique per `from` account. +- If not approved before `ratification_deadline`, the virtual `expire_escrow_ratification_operation` fires and funds return to `from`. + +--- + +## `escrow_approve_operation` (ID 18) + +**Auth:** `active` of `who` + +Approves or rejects the escrow. Both `to` and `agent` must approve for the escrow to become active. + +| Field | Type | Description | +|-------|------|-------------| +| `from` | `account_name_type` | Original escrow sender | +| `to` | `account_name_type` | Original escrow recipient | +| `agent` | `account_name_type` | Escrow agent | +| `who` | `account_name_type` | Who is approving: must be `to` or `agent` | +| `escrow_id` | `uint32_t` | Escrow ID | +| `approve` | `bool` | `true` to approve, `false` to reject | + +```json +[18, { + "from": "alice", + "to": "bob", + "agent": "charlie", + "who": "bob", + "escrow_id": 1001, + "approve": true +}] +``` + +- `who` must be either `to` or `agent`. +- Once approved, cannot be revoked. +- If `approve: false` — escrow is cancelled and funds return to `from`. +- Must be broadcast before `ratification_deadline`. + +--- + +## `escrow_dispute_operation` (ID 16) + +**Auth:** `active` of `who` + +Raises a dispute on an approved escrow. After a dispute, only the `agent` can release funds. + +| Field | Type | Description | +|-------|------|-------------| +| `from` | `account_name_type` | Original escrow sender | +| `to` | `account_name_type` | Original escrow recipient | +| `agent` | `account_name_type` | Escrow agent | +| `who` | `account_name_type` | Who is disputing: must be `from` or `to` | +| `escrow_id` | `uint32_t` | Escrow ID | + +```json +[16, { + "from": "alice", + "to": "bob", + "agent": "charlie", + "who": "alice", + "escrow_id": 1001 +}] +``` + +- Can only be raised on an **approved** escrow (both `to` and `agent` have approved). +- Must be raised before `escrow_expiration`. + +--- + +## `escrow_release_operation` (ID 17) + +**Auth:** `active` of `who` + +Releases escrow funds to `receiver`. Partial releases are allowed. + +| Field | Type | Description | +|-------|------|-------------| +| `from` | `account_name_type` | Original escrow sender | +| `to` | `account_name_type` | Original escrow recipient | +| `agent` | `account_name_type` | Escrow agent | +| `who` | `account_name_type` | Account releasing the funds | +| `receiver` | `account_name_type` | Account receiving the funds (must be `from` or `to`) | +| `escrow_id` | `uint32_t` | Escrow ID | +| `token_amount` | `asset` (VIZ) | Amount to release (may be partial) | + +```json +[17, { + "from": "alice", + "to": "bob", + "agent": "charlie", + "who": "alice", + "receiver": "bob", + "escrow_id": 1001, + "token_amount": "100.000 VIZ" +}] +``` + +**Release permission rules:** + +| State | Who can release | To whom | +|-------|----------------|---------| +| No dispute, before expiration | `from` or `to` | The other party | +| No dispute, after expiration | `from` or `to` | Either party | +| Disputed | `agent` only | Either party | + +- Partial releases are allowed; the remainder stays in escrow. + +--- + +See also: [Data Types](../data-types.md), [Operations Overview](./overview.md), [Virtual Operations](../virtual-operations.md). diff --git a/docs/protocol/operations/invites.md b/docs/protocol/operations/invites.md new file mode 100644 index 0000000000..a65dd462fb --- /dev/null +++ b/docs/protocol/operations/invites.md @@ -0,0 +1,117 @@ +# Invite Operations + +Invites allow existing VIZ users to onboard new accounts without the recipient needing a prior account. An invite is a one-time-use keypair funded with VIZ tokens; the private key serves as the invite secret. + +**Invite flow:** +``` +create_invite → invite_registration_operation (create new account) + → claim_invite_balance_operation (credit balance to existing account) + → use_invite_balance_operation (alternative claim) +``` + +--- + +## `create_invite_operation` (ID 43) + +**Auth:** `active` of `creator` + +Creates an invite by generating a key and locking VIZ tokens in it. + +| Field | Type | Description | +|-------|------|-------------| +| `creator` | `account_name_type` | Account creating the invite | +| `balance` | `asset` (VIZ) | VIZ to lock in the invite | +| `invite_key` | `public_key_type` | Public key of the invite keypair | + +```json +[43, { + "creator": "alice", + "balance": "5.000 VIZ", + "invite_key": "VIZ5invite..." +}] +``` + +- Generate a random secp256k1 keypair. The **public key** goes into `invite_key`; the **private key** (WIF) becomes the invite secret to share. +- `balance` must be ≥ `create_invite_min_balance` chain property. + +--- + +## `claim_invite_balance_operation` (ID 44) + +**Auth:** `active` of `initiator` + +Claims the VIZ balance from an invite, transferring it to `receiver`. The invite is consumed and cannot be reused. + +| Field | Type | Description | +|-------|------|-------------| +| `initiator` | `account_name_type` | Existing account claiming the invite | +| `receiver` | `account_name_type` | Account to receive the VIZ balance | +| `invite_secret` | `string` | WIF private key of the invite | + +```json +[44, { + "initiator": "bob", + "receiver": "bob", + "invite_secret": "5Ky1MXn..." +}] +``` + +- `receiver` may differ from `initiator` — the balance can be redirected. +- `invite_secret` is the WIF-encoded private key of the invite keypair. + +--- + +## `invite_registration_operation` (ID 45) + +**Auth:** `active` of `initiator` + +Uses an invite to create a new blockchain account. The invite balance is converted to SHARES and assigned to the new account. + +| Field | Type | Description | +|-------|------|-------------| +| `initiator` | `account_name_type` | Existing account triggering registration | +| `new_account_name` | `account_name_type` | Name for the new account | +| `invite_secret` | `string` | WIF private key of the invite | +| `new_account_key` | `public_key_type` | Key set as master, active, regular, and memo for the new account | + +```json +[45, { + "initiator": "bob", + "new_account_name": "carol", + "invite_secret": "5Ky1MXn...", + "new_account_key": "VIZ5newacct..." +}] +``` + +- `new_account_key` is applied to all four authority slots (master, active, regular, memo). +- The invite balance is converted to SHARES (not liquid VIZ) for the new account. +- Invite is consumed after use. + +--- + +## `use_invite_balance_operation` (ID 58) + +**Auth:** `active` of `initiator` + +Alternative invite claim that may convert the balance to SHARES for the receiver rather than liquid VIZ. + +| Field | Type | Description | +|-------|------|-------------| +| `initiator` | `account_name_type` | Account using the invite | +| `receiver` | `account_name_type` | Existing account receiving the balance | +| `invite_secret` | `string` | WIF private key of the invite | + +```json +[58, { + "initiator": "bob", + "receiver": "bob", + "invite_secret": "5Ky1MXn..." +}] +``` + +- `receiver` must be an existing account. +- Invite is consumed after use. + +--- + +See also: [Data Types](../data-types.md), [Operations Overview](./overview.md), [Accounts](./accounts.md). diff --git a/docs/protocol/operations/overview.md b/docs/protocol/operations/overview.md new file mode 100644 index 0000000000..64ebf87e9e --- /dev/null +++ b/docs/protocol/operations/overview.md @@ -0,0 +1,111 @@ +# Operations Overview + +VIZ Ledger operations are the atomic state-changing actions included in transactions. Every operation is serialized as a 2-element array `[type_id, object]` inside a signed transaction. + +--- + +## Regular Operations + +These are user-initiated operations that can be broadcast to the network. + +| ID | Operation | Auth level | Reference | +|----|-----------|-----------|-----------| +| 0 | `vote_operation` *(deprecated)* | regular | [Content](./content.md) | +| 1 | `content_operation` *(deprecated)* | regular | [Content](./content.md) | +| 2 | `transfer_operation` | active (VIZ) / master (SHARES) | [Transfers](./transfers.md) | +| 3 | `transfer_to_vesting_operation` | active | [Transfers](./transfers.md) | +| 4 | `withdraw_vesting_operation` | active | [Transfers](./transfers.md) | +| 5 | `account_update_operation` | master / active | [Accounts](./accounts.md) | +| 6 | `witness_update_operation` | active | [Validators](./validators.md) | +| 7 | `account_witness_vote_operation` | active | [Validators](./validators.md) | +| 8 | `account_witness_proxy_operation` | active | [Validators](./validators.md) | +| 9 | `delete_content_operation` *(deprecated)* | regular | [Content](./content.md) | +| 10 | `custom_operation` | active / regular | [Content](./content.md) | +| 11 | `set_withdraw_vesting_route_operation` | active | [Transfers](./transfers.md) | +| 12 | `request_account_recovery_operation` | active | [Recovery](./recovery.md) | +| 13 | `recover_account_operation` | master (×2) | [Recovery](./recovery.md) | +| 14 | `change_recovery_account_operation` | master | [Recovery](./recovery.md) | +| 15 | `escrow_transfer_operation` | active | [Escrow](./escrow.md) | +| 16 | `escrow_dispute_operation` | active | [Escrow](./escrow.md) | +| 17 | `escrow_release_operation` | active | [Escrow](./escrow.md) | +| 18 | `escrow_approve_operation` | active | [Escrow](./escrow.md) | +| 19 | `delegate_vesting_shares_operation` | active | [Transfers](./transfers.md) | +| 20 | `account_create_operation` | active | [Accounts](./accounts.md) | +| 21 | `account_metadata_operation` | regular | [Accounts](./accounts.md) | +| 22 | `proposal_create_operation` | active | [Proposals](./proposals.md) | +| 23 | `proposal_update_operation` | varies | [Proposals](./proposals.md) | +| 24 | `proposal_delete_operation` | active | [Proposals](./proposals.md) | +| 25 | `chain_properties_update_operation` | active | [Validators](./validators.md) | +| 35 | `committee_worker_create_request_operation` | regular | [Committee](./committee.md) | +| 36 | `committee_worker_cancel_request_operation` | regular | [Committee](./committee.md) | +| 37 | `committee_vote_request_operation` | regular | [Committee](./committee.md) | +| 43 | `create_invite_operation` | active | [Invites](./invites.md) | +| 44 | `claim_invite_balance_operation` | active | [Invites](./invites.md) | +| 45 | `invite_registration_operation` | active | [Invites](./invites.md) | +| 46 | `versioned_chain_properties_update_operation` | active | [Validators](./validators.md) | +| 47 | `award_operation` | regular | [Awards](./awards.md) | +| 50 | `set_paid_subscription_operation` | active | [Subscriptions](./subscriptions.md) | +| 51 | `paid_subscribe_operation` | active | [Subscriptions](./subscriptions.md) | +| 54 | `set_account_price_operation` | master | [Account Market](./account-market.md) | +| 55 | `set_subaccount_price_operation` | master | [Account Market](./account-market.md) | +| 56 | `buy_account_operation` | active | [Account Market](./account-market.md) | +| 58 | `use_invite_balance_operation` | active | [Invites](./invites.md) | +| 60 | `fixed_award_operation` | regular | [Awards](./awards.md) | +| 61 | `target_account_sale_operation` | master | [Account Market](./account-market.md) | + +--- + +## Virtual Operations + +Virtual operations are generated by the blockchain itself during block processing. They are never broadcast by users — they appear in account history and block data for informational purposes only. + +| ID | Operation | Trigger | Reference | +|----|-----------|---------|-----------| +| 26 | `author_reward_operation` | Content payout | [Virtual Ops](../virtual-operations.md) | +| 27 | `curation_reward_operation` | Content payout | [Virtual Ops](../virtual-operations.md) | +| 28 | `content_reward_operation` | Content payout | [Virtual Ops](../virtual-operations.md) | +| 29 | `fill_vesting_withdraw_operation` | Withdrawal interval fires | [Virtual Ops](../virtual-operations.md) | +| 30 | `shutdown_witness_operation` | Validator deactivated | [Virtual Ops](../virtual-operations.md) | +| 31 | `hardfork_operation` | Hardfork activation | [Virtual Ops](../virtual-operations.md) | +| 32 | `content_payout_update_operation` | Content payout update | [Virtual Ops](../virtual-operations.md) | +| 33 | `content_benefactor_reward_operation` | Content payout | [Virtual Ops](../virtual-operations.md) | +| 34 | `return_vesting_delegation_operation` | Delegation return window ends | [Virtual Ops](../virtual-operations.md) | +| 38 | `committee_cancel_request_operation` | Committee request expires | [Virtual Ops](../virtual-operations.md) | +| 39 | `committee_approve_request_operation` | Committee request approved | [Virtual Ops](../virtual-operations.md) | +| 40 | `committee_payout_request_operation` | Committee payout processed | [Virtual Ops](../virtual-operations.md) | +| 41 | `committee_pay_request_operation` | Committee worker paid | [Virtual Ops](../virtual-operations.md) | +| 42 | `witness_reward_operation` | Block produced | [Virtual Ops](../virtual-operations.md) | +| 48 | `receive_award_operation` | Award received | [Virtual Ops](../virtual-operations.md) | +| 49 | `benefactor_award_operation` | Award with beneficiary | [Virtual Ops](../virtual-operations.md) | +| 52 | `paid_subscription_action_operation` | Subscription payment | [Virtual Ops](../virtual-operations.md) | +| 53 | `cancel_paid_subscription_operation` | Subscription cancelled/expired | [Virtual Ops](../virtual-operations.md) | +| 57 | `account_sale_operation` | Account sold | [Virtual Ops](../virtual-operations.md) | +| 59 | `expire_escrow_ratification_operation` | Escrow deadline missed | [Virtual Ops](../virtual-operations.md) | +| 62 | `bid_operation` | Auction bid placed | [Virtual Ops](../virtual-operations.md) | +| 63 | `outbid_operation` | Auction outbid | [Virtual Ops](../virtual-operations.md) | + +--- + +## Transaction Construction + +```json +{ + "ref_block_num": 12345, + "ref_block_prefix": 678901234, + "expiration": "2024-01-15T12:01:00", + "operations": [ + [2, { "from": "alice", "to": "bob", "amount": "1.000 VIZ", "memo": "" }] + ], + "extensions": [], + "signatures": ["1f2a3b..."] +} +``` + +- `ref_block_num` = `head_block_number & 0xFFFF` +- `ref_block_prefix` = bytes 4–7 of `block_id` as little-endian `uint32` +- `expiration` = current UTC time + TTL (max recommended: 60 seconds) +- Sign: `sha256(chain_id || serialized_tx)` → compact secp256k1 ECDSA signature + +--- + +See also: [Data Types](../data-types.md), [Virtual Operations](../virtual-operations.md), [JSON-RPC API](../../api/json-rpc.md). diff --git a/docs/protocol/operations/proposals.md b/docs/protocol/operations/proposals.md new file mode 100644 index 0000000000..8203002236 --- /dev/null +++ b/docs/protocol/operations/proposals.md @@ -0,0 +1,125 @@ +# Proposal Operations + +Proposals enable multi-signature governance: one account creates a bundle of operations that require approval from a defined set of signatories before execution. The proposal executes automatically when approval is complete. + +--- + +## `proposal_create_operation` (ID 22) + +**Auth:** `active` of `author` + +Creates a transaction proposal. The proposal is identified by the `author` + `title` pair. + +| Field | Type | Description | +|-------|------|-------------| +| `author` | `account_name_type` | Account creating the proposal | +| `title` | `string` | Unique title per author (used as proposal ID) | +| `memo` | `string` | Human-readable description | +| `expiration_time` | `time_point_sec` | Proposal expiration time | +| `proposed_operations` | `vector` | Operations to execute upon approval | +| `review_period_time` | `optional` | Optional: no new approvals accepted after this time | +| `extensions` | `extensions_type` | Always `[]` | + +Each entry in `proposed_operations` is an `operation_wrapper`: +```json +{"op": [type_id, operation_object]} +``` + +```json +[22, { + "author": "alice", + "title": "transfer-proposal-001", + "memo": "Joint transfer to shared fund", + "expiration_time": "2024-12-31T23:59:59", + "proposed_operations": [ + { + "op": [2, { + "from": "multisig-wallet", + "to": "fund", + "amount": "1000.000 VIZ", + "memo": "" + }] + } + ], + "review_period_time": null, + "extensions": [] +}] +``` + +- `title` must be unique per `author`. +- `expiration_time` must be in the future. +- If `review_period_time` is set, it must be before `expiration_time`; no new approvals are accepted after this point. +- `proposed_operations` may contain multiple operations of any type. + +--- + +## `proposal_update_operation` (ID 23) + +**Auth:** Varies — determined by which approval sets are modified + +Adds or removes approvals. The proposal executes automatically as soon as sufficient approvals are collected. + +| Field | Type | Description | +|-------|------|-------------| +| `author` | `account_name_type` | Author of the proposal | +| `title` | `string` | Title of the proposal | +| `active_approvals_to_add` | `flat_set` | Accounts granting active approval | +| `active_approvals_to_remove` | `flat_set` | Accounts revoking active approval | +| `master_approvals_to_add` | `flat_set` | Accounts granting master approval | +| `master_approvals_to_remove` | `flat_set` | Accounts revoking master approval | +| `regular_approvals_to_add` | `flat_set` | Accounts granting regular approval | +| `regular_approvals_to_remove` | `flat_set` | Accounts revoking regular approval | +| `key_approvals_to_add` | `flat_set` | Public keys granting approval | +| `key_approvals_to_remove` | `flat_set` | Public keys revoking approval | +| `extensions` | `extensions_type` | Always `[]` | + +```json +[23, { + "author": "alice", + "title": "transfer-proposal-001", + "active_approvals_to_add": ["bob"], + "active_approvals_to_remove": [], + "master_approvals_to_add": [], + "master_approvals_to_remove": [], + "regular_approvals_to_add": [], + "regular_approvals_to_remove": [], + "key_approvals_to_add": [], + "key_approvals_to_remove": [], + "extensions": [] +}] +``` + +- The transaction must be signed by the keys corresponding to the approvals being added or removed. +- All `*_to_add` and `*_to_remove` fields default to `[]` when not needed. +- After execution the proposal is resolved; further updates are rejected. + +--- + +## `proposal_delete_operation` (ID 24) + +**Auth:** `active` of `requester` + +Permanently deletes (vetoes) a proposal. Can be invoked by any required authority on the proposal. + +| Field | Type | Description | +|-------|------|-------------| +| `author` | `account_name_type` | Author of the proposal | +| `title` | `string` | Title of the proposal | +| `requester` | `account_name_type` | Account requesting deletion | +| `extensions` | `extensions_type` | Always `[]` | + +```json +[24, { + "author": "alice", + "title": "transfer-proposal-001", + "requester": "bob", + "extensions": [] +}] +``` + +- `requester` must be a required authority on the proposal. +- Deletion is permanent and cannot be undone. + +--- + +See also: [Data Types](../data-types.md), [Operations Overview](./overview.md), [Database API — get_proposed_transactions](../../plugins/database-api.md#get_proposed_transactionsaccount-from-limit). diff --git a/docs/protocol/operations/recovery.md b/docs/protocol/operations/recovery.md new file mode 100644 index 0000000000..9cc6e4abd1 --- /dev/null +++ b/docs/protocol/operations/recovery.md @@ -0,0 +1,106 @@ +# Account Recovery Operations + +The recovery mechanism allows a pre-designated trusted account (the *recovery account*) to help restore access to a compromised account using a recent valid master authority. + +**Recovery flow:** +``` +request_account_recovery → recover_account (within 24 hours) +change_recovery_account (30-day delay before taking effect) +``` + +--- + +## `request_account_recovery_operation` (ID 12) + +**Auth:** `active` of `recovery_account` + +Initiates an account recovery request. The recovery account proposes a new master authority for the compromised account; the account owner has 24 hours to confirm with `recover_account_operation`. + +| Field | Type | Description | +|-------|------|-------------| +| `recovery_account` | `account_name_type` | The trusted recovery account | +| `account_to_recover` | `account_name_type` | Compromised account to recover | +| `new_master_authority` | `authority` | New master authority to assign after confirmation | +| `extensions` | `extensions_type` | Always `[]` | + +```json +[12, { + "recovery_account": "recover-service", + "account_to_recover": "alice", + "new_master_authority": { + "weight_threshold": 1, + "account_auths": [], + "key_auths": [["VIZ5newkey...", 1]] + }, + "extensions": [] +}] +``` + +- Only the designated recovery account of `account_to_recover` may send this. +- Only one active recovery request per account is permitted; sending again updates the request and resets the 24-hour window. +- To cancel: set `new_master_authority.weight_threshold` to `0`. + +--- + +## `recover_account_operation` (ID 13) + +**Auth:** Signatures satisfying **both** `new_master_authority` AND `recent_master_authority` + +Confirms recovery by proving past ownership. Must be broadcast within 24 hours of the recovery request. + +| Field | Type | Description | +|-------|------|-------------| +| `account_to_recover` | `account_name_type` | Account being recovered | +| `new_master_authority` | `authority` | New master authority (must exactly match the recovery request) | +| `recent_master_authority` | `authority` | A master authority that was valid within the past 30 days | +| `extensions` | `extensions_type` | Always `[]` | + +```json +[13, { + "account_to_recover": "alice", + "new_master_authority": { + "weight_threshold": 1, + "account_auths": [], + "key_auths": [["VIZ5newkey...", 1]] + }, + "recent_master_authority": { + "weight_threshold": 1, + "account_auths": [], + "key_auths": [["VIZ5oldkey...", 1]] + }, + "extensions": [] +}] +``` + +- The transaction must be signed by keys satisfying **both** the new and recent authorities simultaneously. +- `new_master_authority` must exactly match the one in the pending recovery request. +- After recovery the old master key is invalidated. + +--- + +## `change_recovery_account_operation` (ID 14) + +**Auth:** `master` of `account_to_recover` + +Changes the recovery account. The change takes effect after a **30-day delay** to prevent attackers from substituting the recovery account during an active attack. + +| Field | Type | Description | +|-------|------|-------------| +| `account_to_recover` | `account_name_type` | Account changing its recovery account | +| `new_recovery_account` | `account_name_type` | New recovery account name | +| `extensions` | `extensions_type` | Always `[]` | + +```json +[14, { + "account_to_recover": "alice", + "new_recovery_account": "new-recovery-service", + "extensions": [] +}] +``` + +- `new_recovery_account` must be an existing account. +- If `new_recovery_account` is `""`, the top-voted validator becomes the recovery account. + +--- + +See also: [Data Types](../data-types.md), [Operations Overview](./overview.md), [Accounts](./accounts.md). diff --git a/docs/protocol/operations/subscriptions.md b/docs/protocol/operations/subscriptions.md new file mode 100644 index 0000000000..f665c9905c --- /dev/null +++ b/docs/protocol/operations/subscriptions.md @@ -0,0 +1,77 @@ +# Paid Subscription Operations + +Paid subscriptions allow accounts to offer tiered recurring services payable in VIZ tokens with optional auto-renewal. + +--- + +## `set_paid_subscription_operation` (ID 50) + +**Auth:** `active` of `account` + +Creates or updates a subscription offering. A `create_paid_subscription_fee` is charged on first creation. + +| Field | Type | Description | +|-------|------|-------------| +| `account` | `account_name_type` | Account offering the subscription | +| `url` | `string` | URL with subscription details | +| `levels` | `uint16_t` | Number of subscription tiers (≥ 1) | +| `amount` | `asset` (VIZ) | Base price per period per unit level | +| `period` | `uint16_t` | Subscription period in days (≥ 1) | + +```json +[50, { + "account": "alice", + "url": "https://alice.example.com/subscribe", + "levels": 3, + "amount": "10.000 VIZ", + "period": 30 +}] +``` + +- Actual cost for a subscriber = `amount × level`. +- `levels = 3` with `amount = "10.000 VIZ"` → tier 1 costs 10 VIZ, tier 2 costs 20 VIZ, tier 3 costs 30 VIZ per period. +- Updating this operation changes the parameters for future subscriptions; existing active subscriptions continue at the previous terms until renewal. + +--- + +## `paid_subscribe_operation` (ID 51) + +**Auth:** `active` of `subscriber` + +Subscribes to or renews a paid subscription. Tokens transfer immediately from `subscriber` to `account`. + +| Field | Type | Description | +|-------|------|-------------| +| `subscriber` | `account_name_type` | Subscribing account | +| `account` | `account_name_type` | Account offering the subscription | +| `level` | `uint16_t` | Subscription tier (1 – `levels`) | +| `amount` | `asset` (VIZ) | Payment amount | +| `period` | `uint16_t` | Number of periods to pay for | +| `auto_renewal` | `bool` | Enable automatic renewal each period | + +```json +[51, { + "subscriber": "bob", + "account": "alice", + "level": 2, + "amount": "20.000 VIZ", + "period": 1, + "auto_renewal": true +}] +``` + +- `amount` must match `subscription.amount × level × period` exactly. +- `level` must be in range [1, `subscription.levels`]. +- `auto_renewal: true` — tokens are deducted automatically each period while sufficient balance exists. +- `auto_renewal: false` — one-time subscription; expires after the paid period. + +**Virtual operations:** + +| Virtual Op | Trigger | +|-----------|---------| +| `paid_subscription_action_operation` | Payment processed | +| `cancel_paid_subscription_operation` | Subscription expires or is cancelled | + +--- + +See also: [Data Types](../data-types.md), [Operations Overview](./overview.md), [Virtual Operations](../virtual-operations.md). diff --git a/docs/protocol/operations/transfers.md b/docs/protocol/operations/transfers.md new file mode 100644 index 0000000000..9bcfb986a2 --- /dev/null +++ b/docs/protocol/operations/transfers.md @@ -0,0 +1,136 @@ +# Transfer Operations + +--- + +## `transfer_operation` (ID 2) + +**Auth:** `active` of `from` (VIZ) / `master` of `from` (SHARES) + +Transfers VIZ or SHARES tokens between accounts. + +| Field | Type | Description | +|-------|------|-------------| +| `from` | `account_name_type` | Sending account | +| `to` | `account_name_type` | Receiving account | +| `amount` | `asset` | Amount to transfer (VIZ or SHARES) | +| `memo` | `string` | Memo text (plain or encrypted; may be `""`) | + +```json +[2, { + "from": "alice", + "to": "bob", + "amount": "10.000 VIZ", + "memo": "payment for services" +}] +``` + +- `amount.symbol` must be `VIZ` or `SHARES`. +- VIZ transfers require **active** authority; SHARES transfers require **master** authority. +- Encrypted memo format: `#` followed by base58-encoded ciphertext. + +--- + +## `transfer_to_vesting_operation` (ID 3) + +**Auth:** `active` of `from` + +Converts liquid VIZ into SHARES (staking). The SHARES can be credited to a different account. + +| Field | Type | Description | +|-------|------|-------------| +| `from` | `account_name_type` | Account providing VIZ | +| `to` | `account_name_type` | Account receiving SHARES (may equal `from`) | +| `amount` | `asset` (VIZ) | Amount of VIZ to stake | + +```json +[3, { + "from": "alice", + "to": "alice", + "amount": "100.000 VIZ" +}] +``` + +- `amount.symbol` must be `VIZ`. +- `to` may be any existing account — useful for gifting staked shares. + +--- + +## `withdraw_vesting_operation` (ID 4) + +**Auth:** `active` of `account` + +Initiates a gradual withdrawal of SHARES back to liquid VIZ over multiple intervals. + +| Field | Type | Description | +|-------|------|-------------| +| `account` | `account_name_type` | Account initiating withdrawal | +| `vesting_shares` | `asset` (SHARES) | Total SHARES to withdraw; `0.000000 SHARES` cancels | + +```json +[4, { + "account": "alice", + "vesting_shares": "1000.000000 SHARES" +}] +``` + +- Withdrawal is spread over `withdraw_intervals` intervals (chain property, default 28). +- Each interval: `vesting_shares / withdraw_intervals` SHARES are converted. +- Set to `"0.000000 SHARES"` to cancel an active withdrawal. + +--- + +## `set_withdraw_vesting_route_operation` (ID 11) + +**Auth:** `active` of `from_account` + +Routes a percentage of vesting withdrawals to a specified account, optionally re-vesting the routed portion. + +| Field | Type | Description | +|-------|------|-------------| +| `from_account` | `account_name_type` | Account whose withdrawals are routed | +| `to_account` | `account_name_type` | Destination account | +| `percent` | `uint16_t` | Percentage to route (0–10000 basis points) | +| `auto_vest` | `bool` | If `true`, routed tokens are immediately re-vested in `to_account` | + +```json +[11, { + "from_account": "alice", + "to_account": "bob", + "percent": 5000, + "auto_vest": false +}] +``` + +- `percent` = 0 deletes this route to `to_account`. +- The sum of all routes from `from_account` must not exceed 10000. +- Multiple routes to different accounts are allowed. + +--- + +## `delegate_vesting_shares_operation` (ID 19) + +**Auth:** `active` of `delegator` + +Delegates SHARES to another account. The delegatee gains bandwidth and voting power; ownership stays with the delegator. + +| Field | Type | Description | +|-------|------|-------------| +| `delegator` | `account_name_type` | Account delegating SHARES | +| `delegatee` | `account_name_type` | Account receiving the delegation | +| `vesting_shares` | `asset` (SHARES) | Amount to delegate; `0.000000 SHARES` removes delegation | + +```json +[19, { + "delegator": "alice", + "delegatee": "bob", + "vesting_shares": "500.000000 SHARES" +}] +``` + +- `vesting_shares` must be ≥ `min_delegation` chain property, or exactly `0.000000 SHARES` to remove. +- When delegation is removed, the SHARES enter a 7-day return window before crediting back. +- Virtual `return_vesting_delegation_operation` fires when the return window ends. + +--- + +See also: [Data Types](../data-types.md), [Operations Overview](./overview.md). diff --git a/docs/protocol/operations/validators.md b/docs/protocol/operations/validators.md new file mode 100644 index 0000000000..32bda4066f --- /dev/null +++ b/docs/protocol/operations/validators.md @@ -0,0 +1,166 @@ +# Validator Operations + +--- + +## `witness_update_operation` (ID 6) + +**Auth:** `active` of `owner` + +Registers or updates a validator node. Setting `block_signing_key` to the null key removes the validator from block production. + +| Field | Type | Description | +|-------|------|-------------| +| `owner` | `account_name_type` | Validator account name | +| `url` | `string` | Validator website or info URL (non-empty, max 256 bytes) | +| `block_signing_key` | `public_key_type` | Key used to sign produced blocks | + +```json +[6, { + "owner": "alice", + "url": "https://alice.example.com", + "block_signing_key": "VIZ5hqSa4NkEZGAMUpoH5EaEr64mBJuMcPpGjvk8qb7hcPFTbXSQ9" +}] +``` + +- **Null key** (deactivate): `"VIZ1111111111111111111111111111111114T1Anm"` — removes from block production without deleting the validator record. +- Broadcasting this operation requires `witness_declaration_fee` (paid to the committee fund). + +--- + +## `chain_properties_update_operation` (ID 25) + +**Auth:** `active` of `owner` + +Votes on base chain properties (`chain_properties_init` format). The on-chain value is the median across all active validators. + +| Field | Type | Description | +|-------|------|-------------| +| `owner` | `account_name_type` | Validator casting the vote | +| `props` | `chain_properties_init` | Proposed chain parameters | + +```json +[25, { + "owner": "alice", + "props": { + "account_creation_fee": "1.000 VIZ", + "maximum_block_size": 65536, + "create_account_delegation_ratio": 10, + "create_account_delegation_time": 2592000, + "min_delegation": "1.000 VIZ", + "min_curation_percent": 0, + "max_curation_percent": 10000, + "bandwidth_reserve_percent": 1000, + "bandwidth_reserve_below": "1.000000 SHARES", + "flag_energy_additional_cost": 1000, + "vote_accounting_min_rshares": 0, + "committee_request_approve_min_percent": 1000 + } +}] +``` + +- All percent fields are in basis points (0–10000). +- `min_curation_percent` must be ≤ `max_curation_percent`. +- Use `versioned_chain_properties_update_operation` (ID 46) for extended HF9+ properties. + +--- + +## `versioned_chain_properties_update_operation` (ID 46) + +**Auth:** `active` of `owner` + +Votes on versioned chain properties supporting all hardfork extensions. Preferred over `chain_properties_update_operation` for current nodes. + +| Field | Type | Description | +|-------|------|-------------| +| `owner` | `account_name_type` | Validator casting the vote | +| `props` | `versioned_chain_properties` | Versioned props serialized as `[index, object]` | + +```json +[46, { + "owner": "alice", + "props": [3, { + "account_creation_fee": "1.000 VIZ", + "maximum_block_size": 65536, + "create_account_delegation_ratio": 10, + "create_account_delegation_time": 2592000, + "min_delegation": "1.000 VIZ", + "min_curation_percent": 0, + "max_curation_percent": 10000, + "bandwidth_reserve_percent": 1000, + "bandwidth_reserve_below": "1.000000 SHARES", + "flag_energy_additional_cost": 1000, + "vote_accounting_min_rshares": 0, + "committee_request_approve_min_percent": 1000, + "inflation_witness_percent": 2000, + "inflation_ratio_committee_vs_reward_fund": 1000, + "inflation_recalc_period": 28800, + "data_operations_cost_additional_bandwidth": 0, + "witness_miss_penalty_percent": 100, + "witness_miss_penalty_duration": 86400, + "create_invite_min_balance": "1.000 VIZ", + "committee_create_request_fee": "1.000 VIZ", + "create_paid_subscription_fee": "1.000 VIZ", + "account_on_sale_fee": "10.000 VIZ", + "subaccount_on_sale_fee": "1.000 VIZ", + "witness_declaration_fee": "1.000 VIZ", + "withdraw_intervals": 28 + }] +}] +``` + +- `props` is a static variant: use index `3` for `chain_properties_hf9` (current). +- See [Data Types](../data-types.md#versioned_chain_properties) for the full field list per version index. + +--- + +## `account_witness_vote_operation` (ID 7) + +**Auth:** `active` of `account` + +Votes for or removes a vote from a validator. The top 21 validators by cumulative vote weight produce blocks. + +| Field | Type | Description | +|-------|------|-------------| +| `account` | `account_name_type` | Voting account | +| `witness` | `account_name_type` | Validator to vote for | +| `approve` | `bool` | `true` to add vote, `false` to remove vote | + +```json +[7, { + "account": "alice", + "witness": "bob", + "approve": true +}] +``` + +- Voting weight is proportional to the voter's SHARES stake. +- `approve: false` removes a previously cast vote. + +--- + +## `account_witness_proxy_operation` (ID 8) + +**Auth:** `active` of `account` + +Delegates all validator voting to a proxy account. All existing direct votes are removed when a proxy is set. + +| Field | Type | Description | +|-------|------|-------------| +| `account` | `account_name_type` | Account setting the proxy | +| `proxy` | `account_name_type` | Proxy account; `""` removes the proxy | + +```json +[8, { + "account": "alice", + "proxy": "bob" +}] +``` + +- `proxy: ""` (empty string) removes the proxy and restores direct voting. +- Cannot set proxy to self. +- Proxy chains are resolved transitively (A→B→C); maximum chain depth is enforced. +- Setting a proxy removes all direct validator votes. + +--- + +See also: [Data Types](../data-types.md), [Operations Overview](./overview.md), [Chain Properties](../../governance/chain-properties.md). diff --git a/docs/protocol/virtual-operations.md b/docs/protocol/virtual-operations.md new file mode 100644 index 0000000000..9493436d75 --- /dev/null +++ b/docs/protocol/virtual-operations.md @@ -0,0 +1,354 @@ +# Virtual Operations + +Virtual operations are generated by the blockchain itself during block processing. They are **never broadcast by users** — they appear only in account history and block data for informational purposes. + +Virtual operations share the same operation variant as regular operations but carry higher type IDs. They can be observed via the `account_history` API or block streaming. + +--- + +## Content Payouts *(deprecated)* + +### `author_reward_operation` (ID 26) + +**Trigger:** Content post payout + +Fired when an author receives their reward share from a content payout. + +| Field | Type | Description | +|-------|------|-------------| +| `author` | `account_name_type` | Content author | +| `permlink` | `string` | Content permlink | +| `token_payout` | `asset` (VIZ) | Liquid VIZ portion | +| `vesting_payout` | `asset` (SHARES) | Vested portion | + +--- + +### `curation_reward_operation` (ID 27) + +**Trigger:** Content post payout + +Fired when a curator receives their curation reward. + +| Field | Type | Description | +|-------|------|-------------| +| `curator` | `account_name_type` | Curator account | +| `reward` | `asset` (SHARES) | Curation reward | +| `content_author` | `account_name_type` | Author of curated content | +| `content_permlink` | `string` | Permlink of curated content | + +--- + +### `content_reward_operation` (ID 28) + +**Trigger:** Content post payout + +Fired when a post reaches its payout time. + +| Field | Type | Description | +|-------|------|-------------| +| `author` | `account_name_type` | Content author | +| `permlink` | `string` | Content permlink | +| `payout` | `asset` | Total payout amount | + +--- + +### `content_payout_update_operation` (ID 32) + +**Trigger:** Content payout recalculation (e.g. after vote changes) + +| Field | Type | Description | +|-------|------|-------------| +| `author` | `account_name_type` | Content author | +| `permlink` | `string` | Content permlink | + +--- + +### `content_benefactor_reward_operation` (ID 33) + +**Trigger:** Content post payout — fires for each beneficiary + +| Field | Type | Description | +|-------|------|-------------| +| `benefactor` | `account_name_type` | Beneficiary account | +| `author` | `account_name_type` | Content author | +| `permlink` | `string` | Content permlink | +| `reward` | `asset` | Beneficiary reward share | + +--- + +## Vesting Withdrawals + +### `fill_vesting_withdraw_operation` (ID 29) + +**Trigger:** Each vesting withdrawal interval fires + +Fired once per interval for each active withdrawal route. + +| Field | Type | Description | +|-------|------|-------------| +| `from_account` | `account_name_type` | Account withdrawing | +| `to_account` | `account_name_type` | Destination account (may differ via withdrawal route) | +| `withdrawn` | `asset` (SHARES) | SHARES amount withdrawn this interval | +| `deposited` | `asset` | Deposited to `to_account` (VIZ, or SHARES if `auto_vest = true`) | + +```json +[29, { + "from_account": "alice", + "to_account": "alice", + "withdrawn": "35.714285 SHARES", + "deposited": "10.000 VIZ" +}] +``` + +--- + +### `return_vesting_delegation_operation` (ID 34) + +**Trigger:** End of 7-day return window after `delegate_vesting_shares_operation` with zero amount + +| Field | Type | Description | +|-------|------|-------------| +| `account` | `account_name_type` | Account receiving returned SHARES | +| `vesting_shares` | `asset` (SHARES) | SHARES returned from limbo | + +--- + +## Validator Operations + +### `shutdown_witness_operation` (ID 30) + +**Trigger:** Validator deactivated due to insufficient vote weight + +| Field | Type | Description | +|-------|------|-------------| +| `owner` | `account_name_type` | Validator that was shut down | + +--- + +### `witness_reward_operation` (ID 42) + +**Trigger:** Block produced — validator receives block reward + +| Field | Type | Description | +|-------|------|-------------| +| `witness` | `account_name_type` | Validator account | +| `shares` | `asset` (SHARES) | Block reward | + +```json +[42, { + "witness": "alice", + "shares": "1.234567 SHARES" +}] +``` + +--- + +## Network Events + +### `hardfork_operation` (ID 31) + +**Trigger:** Network hardfork activates + +| Field | Type | Description | +|-------|------|-------------| +| `hardfork_id` | `uint32_t` | Hardfork number | + +--- + +## Awards + +### `receive_award_operation` (ID 48) + +**Trigger:** `award_operation` or `fixed_award_operation` + +Fired for the primary receiver of an award. + +| Field | Type | Description | +|-------|------|-------------| +| `initiator` | `account_name_type` | Account that gave the award | +| `receiver` | `account_name_type` | Account that received the award | +| `custom_sequence` | `uint64_t` | App-defined sequence from the award operation | +| `memo` | `string` | Memo from the award operation | +| `shares` | `asset` (SHARES) | SHARES received | + +```json +[48, { + "initiator": "alice", + "receiver": "bob", + "custom_sequence": 0, + "memo": "great article!", + "shares": "5.000000 SHARES" +}] +``` + +--- + +### `benefactor_award_operation` (ID 49) + +**Trigger:** `award_operation` or `fixed_award_operation` with beneficiaries + +Fired once per beneficiary. + +| Field | Type | Description | +|-------|------|-------------| +| `initiator` | `account_name_type` | Account that gave the award | +| `benefactor` | `account_name_type` | Beneficiary account | +| `receiver` | `account_name_type` | Primary receiver of the award | +| `custom_sequence` | `uint64_t` | App-defined sequence | +| `memo` | `string` | Memo from the award operation | +| `shares` | `asset` (SHARES) | SHARES received by beneficiary | + +--- + +## Committee + +### `committee_cancel_request_operation` (ID 38) + +**Trigger:** Committee funding request expires without reaching approval threshold + +| Field | Type | Description | +|-------|------|-------------| +| `request_id` | `uint32_t` | ID of the cancelled request | + +--- + +### `committee_approve_request_operation` (ID 39) + +**Trigger:** Committee funding request reaches the required approval threshold + +| Field | Type | Description | +|-------|------|-------------| +| `request_id` | `uint32_t` | ID of the approved request | + +--- + +### `committee_payout_request_operation` (ID 40) + +**Trigger:** Committee request payout is processed + +| Field | Type | Description | +|-------|------|-------------| +| `request_id` | `uint32_t` | ID of the paid request | + +--- + +### `committee_pay_request_operation` (ID 41) + +**Trigger:** Worker receives payment from the committee fund + +| Field | Type | Description | +|-------|------|-------------| +| `worker` | `account_name_type` | Worker account | +| `request_id` | `uint32_t` | Committee request ID | +| `tokens` | `asset` (VIZ) | Amount paid to worker | + +```json +[41, { + "worker": "alice", + "request_id": 42, + "tokens": "250.000 VIZ" +}] +``` + +--- + +## Paid Subscriptions + +### `paid_subscription_action_operation` (ID 52) + +**Trigger:** `paid_subscribe_operation` executed, or auto-renewal payment processed + +| Field | Type | Description | +|-------|------|-------------| +| `subscriber` | `account_name_type` | Subscriber account | +| `account` | `account_name_type` | Subscription provider | +| `level` | `uint16_t` | Subscription tier | +| `amount` | `asset` (VIZ) | Payment amount | +| `period` | `uint16_t` | Number of periods | +| `summary_duration_sec` | `uint64_t` | Cumulative subscription duration (seconds) | +| `summary_amount` | `asset` (VIZ) | Total amount paid to date | + +--- + +### `cancel_paid_subscription_operation` (ID 53) + +**Trigger:** Subscription expires, or insufficient balance for auto-renewal + +| Field | Type | Description | +|-------|------|-------------| +| `subscriber` | `account_name_type` | Subscriber account | +| `account` | `account_name_type` | Subscription provider | + +--- + +## Account Market + +### `account_sale_operation` (ID 57) + +**Trigger:** `buy_account_operation` completes successfully + +| Field | Type | Description | +|-------|------|-------------| +| `account` | `account_name_type` | Account that was sold | +| `price` | `asset` (VIZ) | Sale price | +| `buyer` | `account_name_type` | Buyer account | +| `seller` | `account_name_type` | Seller (payment recipient) | + +```json +[57, { + "account": "alice", + "price": "1000.000 VIZ", + "buyer": "bob", + "seller": "alice" +}] +``` + +--- + +### `bid_operation` (ID 62) + +**Trigger:** New bid placed on an account listed for auction + +| Field | Type | Description | +|-------|------|-------------| +| `account` | `account_name_type` | Account being bid on | +| `bidder` | `account_name_type` | Account placing the bid | +| `bid` | `asset` (VIZ) | Bid amount | + +--- + +### `outbid_operation` (ID 63) + +**Trigger:** A previous bid is replaced by a higher bid + +Fired for the outbid account; the previous bid amount is returned. + +| Field | Type | Description | +|-------|------|-------------| +| `account` | `account_name_type` | Account being bid on | +| `bidder` | `account_name_type` | Account that was outbid | +| `bid` | `asset` (VIZ) | Returned bid amount | + +--- + +## Escrow + +### `expire_escrow_ratification_operation` (ID 59) + +**Trigger:** Escrow `ratification_deadline` missed — neither `to` nor `agent` approved in time + +All locked funds return to `from`. + +| Field | Type | Description | +|-------|------|-------------| +| `from` | `account_name_type` | Original escrow sender | +| `to` | `account_name_type` | Intended recipient | +| `agent` | `account_name_type` | Escrow agent | +| `escrow_id` | `uint32_t` | Escrow ID | +| `token_amount` | `asset` (VIZ) | Returned token amount | +| `fee` | `asset` (VIZ) | Returned fee (agent not paid since escrow was not ratified) | +| `ratification_deadline` | `time_point_sec` | Deadline that was missed | + +--- + +See also: [Operations Overview](./operations/overview.md), [Awards](./operations/awards.md), [Committee](./operations/committee.md). diff --git a/docs/storage/block-log.md b/docs/storage/block-log.md new file mode 100644 index 0000000000..d59079060b --- /dev/null +++ b/docs/storage/block-log.md @@ -0,0 +1,166 @@ +# Block Log + +VIZ stores blocks in binary log files. Two variants exist: + +| Variant | Files | Purpose | +|---------|-------|---------| +| `block_log` | `block_log` + `block_log.index` | Full history (archive nodes) | +| `dlt_block_log` | `dlt_block_log` + `dlt_block_log.index` | Rolling window (DLT/snapshot nodes) | + +Both share the same data-file format; the index format differs slightly. + +--- + +## Binary Serialization (`fc::raw`) + +All data uses little-endian encoding. + +| Type | Format | +|------|--------| +| `uint8_t` – `uint64_t` | Fixed-width little-endian | +| `fc::unsigned_int` | Variable-length (varint): 7 data bits + 1 continuation bit per byte | +| `string` | `[varint: length][UTF-8 bytes]` | +| `vector` | `[varint: count][elements...]` | +| `optional` | `[uint8: 0 or 1][value if 1]` | +| `static_variant` | `[varint: type_index][serialized value]` | + +--- + +## Data File Layout + +Both `block_log` and `dlt_block_log` use the same format: + +``` +[block 1 binary][uint64 LE: position of block 1] +[block 2 binary][uint64 LE: position of block 2] +... +``` + +Each entry = serialized `signed_block` followed by its own start offset as a `uint64_t`. + +**Reading the head block:** seek to the last 8 bytes, read the offset, seek there, deserialize. + +--- + +## Index Files + +### `block_log.index` + +Each entry is an 8-byte `uint64_t` offset into `block_log`. + +``` +offset = 8 × (block_num − 1) +``` + +### `dlt_block_log.index` + +Starts with an 8-byte header: + +``` +[uint64 LE: start_block_num][uint64 LE: offset of start_block_num][...] +``` + +``` +offset = 8 + 8 × (block_num − start_block_num) +``` + +--- + +## `signed_block` Structure + +``` +block_header: + [20 bytes: previous block ID (ripemd160)] + [4 bytes: timestamp (uint32 Unix seconds)] + [varint + string: witness account name] + [20 bytes: transaction_merkle_root (ripemd160)] + [varint + vector: extensions] + +signed_block_header (appended): + [65 bytes: witness_signature (1 recovery byte + 32 r + 32 s)] + +signed_block (appended): + [varint + vector: transactions] +``` + +Block number is **not stored directly**. Derive it as: +``` +block_num = num_from_id(previous) + 1 +num_from_id = first 4 bytes of block_id as uint32_t LE +``` +(Genesis: `previous` all zeros → `block_num = 1`.) + +--- + +## `signed_transaction` Structure + +``` +transaction: + [2 bytes: ref_block_num (uint16 LE)] + [4 bytes: ref_block_prefix (uint32 LE)] + [4 bytes: expiration (uint32 Unix seconds)] + [varint + vector: operations] + [varint + extensions_type: extensions] + +signed_transaction (appended): + [varint + vector: signatures] +``` + +--- + +## Operation Serialization + +Each operation in the `operations` vector is a static variant: + +``` +[varint: type_id][operation-specific fields...] +``` + +Type IDs: see [Operations Overview](../protocol/operations/overview.md). + +**Asset wire format:** +``` +[int64: amount][uint64: symbol] +``` +Symbol is a packed `uint64`: byte 0 = decimal places, bytes 1–6 = ASCII name, byte 7 = 0x00. + +| Symbol | Hex (LE) | +|--------|----------| +| VIZ (3 decimals) | `03 56 49 5A 00 00 00 00` | +| SHARES (6 decimals) | `06 53 48 41 52 45 53 00` | + +**Public key wire format:** 33 raw bytes (compressed secp256k1): `[0x02 or 0x03][32-byte x]`. + +--- + +## Block Header Extensions + +| Index | Type | Data | +|-------|------|------| +| 0 | `void_t` | (none) | +| 1 | `version` | `uint32_t` version number (major 8 \| hf 8 \| release 16 bits) | +| 2 | `hardfork_version_vote` | `uint32_t` hf_version + `uint32_t` hf_time | + +--- + +## `dlt_block_log` Rolling Window + +The DLT log keeps only a recent window of blocks; older blocks are pruned. It starts at `start_block_num > 1`. Nodes using snapshots use this file for crash recovery (replay from snapshot + dlt_block_log). + +--- + +## Block Log Viewer + +A terminal block log viewer is included in the tooling (`block-log-viewer.js`): + +``` +node block-log-viewer.js [--dlt] +``` + +Key commands: `f` first, `l` last, `n`/`p` next/prev, `g ` go to block N, `o` show operations, `s ` search by operation type, `S ` search by content, `scan` build fast-navigation bitmask. + +The `scan` command builds a bitmask file (`block_log.bitmask`) that marks which blocks contain non-empty operations, enabling instant `N`/`P` jumps. + +--- + +See also: [Shared Memory](./shared-memory.md), [Snapshots](./snapshots.md), [Chain Plugin](../plugins/chain.md). diff --git a/docs/storage/shared-memory.md b/docs/storage/shared-memory.md new file mode 100644 index 0000000000..4b1d2359c7 --- /dev/null +++ b/docs/storage/shared-memory.md @@ -0,0 +1,150 @@ +# Shared Memory + +All blockchain state in a VIZ node is stored in a single memory-mapped file (`shared_memory.bin`) managed by the **chainbase** library. The node cannot operate without this file. + +--- + +## Architecture + +``` +vizd process +├── block_log / dlt_block_log — raw block bytes (disk) +└── shared_memory.bin (mmap) — all chain state (chainbase) + ├── account_index + ├── witness_index + ├── transaction_index + └── ... (all other object indices) +``` + +API threads (webserver thread pool) acquire **shared read locks**; block application holds an **exclusive write lock**. Multiple readers can coexist; a writer blocks all readers. + +--- + +## Configuration + +All options go in `config.ini`. + +### Size Options + +| Option | Default | Description | +|--------|---------|-------------| +| `shared-file-dir` | `state` | Directory for `shared_memory.bin` (relative to data dir or absolute) | +| `shared-file-size` | `2G` | Initial allocation. If file exists and this is larger, file grows. Does not trigger replay. | +| `inc-shared-file-size` | `2G` | Auto-growth step when free space falls below threshold | +| `min-free-shared-file-size` | `500M` | Free-space threshold that triggers auto-growth | + +**Rule:** `min-free-shared-file-size` must be less than `inc-shared-file-size`, otherwise cascading resizes occur. + +### Lock Timeout Options + +| Option | Default | Description | +|--------|---------|-------------| +| `read-wait-micro` | `500000` (500 ms) | Timeout per read lock attempt | +| `max-read-wait-retries` | `3` | Max read attempts before error | +| `write-wait-micro` | `500000` (500 ms) | Timeout per write lock attempt | +| `max-write-wait-retries` | `3` | Max write attempts before error | + +### Performance Options + +| Option | Default | Description | +|--------|---------|-------------| +| `single-write-thread` | `false` | Serialize all block/transaction pushes. **Recommended for production.** | +| `block-num-check-free-size` | `1000` | Check free space every N blocks | +| `flush-state-interval` | — | Flush shared memory to disk every N blocks | +| `clear-votes-before-block` | `0` | Drop votes older than this block (0 = keep all). Reduces memory. | +| `skip-virtual-ops` | `false` | Skip virtual operation notifications. Saves CPU during replay. | + +--- + +## Recommended Configurations + +**Validator node (production):** +```ini +shared-file-size = 4G +inc-shared-file-size = 2G +min-free-shared-file-size = 500M +single-write-thread = true +``` + +**API node (high read throughput):** +```ini +shared-file-size = 8G +inc-shared-file-size = 2G +min-free-shared-file-size = 500M +single-write-thread = true +read-wait-micro = 1000000 +max-read-wait-retries = 10 +webserver-thread-pool-size = 256 +``` + +**Replay / initial sync:** +```ini +shared-file-size = 8G +inc-shared-file-size = 4G +min-free-shared-file-size = 500M +block-num-check-free-size = 10 +skip-virtual-ops = true +``` + +--- + +## Auto-Resize + +The database auto-grows when free space drops below `min-free-shared-file-size`. Each resize: + +1. Pauses all operations (including block production and API requests). +2. Destroys the current memory mapping. +3. Extends the file by `inc-shared-file-size`. +4. Re-maps the file and rebuilds all index pointers. + +Pre-allocate `shared-file-size` generously to minimize resize frequency. Each resize causes a latency spike. + +--- + +## Size Planning + +Approximate usage for a VIZ mainnet full node: + +| Component | Estimated Size | +|-----------|---------------| +| Account index (~14 K accounts) | ~50 MB | +| Validator index | ~5 MB | +| Operation history (operation_history plugin) | 200–500 MB | +| Account history (account_history plugin) | 100–300 MB | +| Other indexes | 100–200 MB | +| **Recommended starting size** | **4–8 GB** | + +--- + +## Startup Sequence + +``` +1. Open shared_memory.bin (grow if shared-file-size is larger) +2. Acquire exclusive file lock +3. Initialize indices +4. If genesis missing → init_genesis() +5. Open block_log or dlt_block_log +6. undo_all() → rewind to last irreversible block +7. Verify head block matches block log +``` + +--- + +## Recovery + +| Symptom | Action | +|---------|--------| +| `CRITICAL: validator X account object MISSING` | Corruption — use `--replay-blockchain` | +| `Could not modify object, uniqueness constraint violated` | Corruption — replay or resync | +| `Unable to acquire READ lock` | Lock contention — increase `read-wait-micro` / enable `single-write-thread` | +| Node crashes in a loop on startup | Corrupted file — `--replay-blockchain` or `--snapshot` | + +Recovery options: + +- `--replay-blockchain` — delete shared memory, replay from block log. +- `--resync-blockchain` — delete shared memory and block log, sync from peers. +- `--snapshot ` — load from snapshot, replay dlt_block_log on top. + +--- + +See also: [Chain Plugin](../plugins/chain.md), [Snapshot Plugin](../plugins/snapshot.md), [Block Log](./block-log.md). diff --git a/docs/storage/snapshots.md b/docs/storage/snapshots.md new file mode 100644 index 0000000000..1d89610d9d --- /dev/null +++ b/docs/storage/snapshots.md @@ -0,0 +1,286 @@ +# Snapshot Plugin + +The snapshot plugin enables near-instant node startup by serializing and restoring the full blockchain state as a JSON snapshot file. Instead of replaying millions of blocks from the block log, a node can load a pre-built snapshot and begin syncing from that point via P2P. + +--- + +## Enabling + +```ini +plugin = snapshot +``` + +Or on the command line: + +```bash +vizd --plugin snapshot +``` + +--- + +## Configuration Reference + +### CLI-only Options + +| Option | Type | Description | +|--------|------|-------------| +| `--snapshot ` | string | Load state from a snapshot file (DLT mode). Skips import if `shared_memory.bin` already exists; renames file to `.used` after successful import. | +| `--snapshot-auto-latest` | bool | Auto-discover the latest snapshot in `snapshot-dir` by block number in filename. Ignored if `--snapshot` is set. | +| `--replay-from-snapshot` | bool | Crash recovery: import snapshot then replay `dlt_block_log` to reach latest available state. Always wipes shared memory; does not rename the snapshot. | +| `--auto-recover-from-snapshot` | bool (default: `true`) | Automatic runtime recovery when shared memory corruption is detected. Closes database, finds latest snapshot, wipes shared memory, imports and replays — without a restart. | +| `--create-snapshot ` | string | Create a snapshot from current database state, then exit. | +| `--sync-snapshot-from-trusted-peer` | bool (default: `false`) | Download and load snapshot from trusted peers when state is empty. Must be explicitly enabled. | + +### Config File Options + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `snapshot-at-block` | uint32 | `0` | Create snapshot when this block number is reached | +| `snapshot-every-n-blocks` | uint32 | `0` | Create snapshot every N blocks (0 = disabled) | +| `snapshot-dir` | string | `""` | Directory for auto-generated snapshot files | +| `snapshot-max-age-days` | uint32 | `90` | Delete snapshots older than N days (0 = disabled) | +| `allow-snapshot-serving` | bool | `false` | Enable serving snapshots over TCP to other nodes | +| `allow-snapshot-serving-only-trusted` | bool | `false` | Restrict serving to trusted peers only | +| `snapshot-serve-endpoint` | string | `0.0.0.0:8092` | TCP endpoint for snapshot serving | +| `trusted-snapshot-peer` | string (multi) | — | Trusted peer for snapshot sync (`IP:port`). Repeatable. | +| `dlt-block-log-max-blocks` | uint32 | `100000` | Recent blocks to keep in DLT rolling block log (chain plugin) | + +--- + +## Creating Snapshots + +### Method 1: One-shot (node stops and exits) + +```bash +vizd --create-snapshot /data/snapshots/viz-snapshot.json --plugin snapshot +``` + +The node opens the existing database, replays if needed, creates the snapshot, and exits before P2P activates. + +### Method 2: At a specific block (no downtime) + +```ini +plugin = snapshot +snapshot-at-block = 5000000 +snapshot-dir = /data/snapshots +``` + +When block 5,000,000 is applied, the snapshot is written to `/data/snapshots/snapshot-block-5000000.json` on a background thread — block processing is only briefly paused. + +### Method 3: Periodic automatic snapshots (recommended) + +```ini +plugin = snapshot +snapshot-every-n-blocks = 100000 +snapshot-dir = /data/snapshots +``` + +Snapshots are created every 100,000 blocks (~3.5 days). Snapshots are skipped while syncing — they only trigger on live blocks. + +**Recommended intervals:** + +| Interval | Blocks | Approximate time | +|----------|--------|-----------------| +| Frequent | 10,000 | ~8 hours | +| Daily | 28,800 | ~24 hours | +| Weekly | 100,000 | ~3.5 days | +| Rare | 1,000,000 | ~35 days | + +### Snapshot Rotation + +Old snapshots are automatically deleted after each new snapshot if older than `snapshot-max-age-days` (default 90). To disable: + +```ini +snapshot-max-age-days = 0 +``` + +--- + +## Loading from a Snapshot (DLT Mode) + +```bash +vizd --snapshot /path/to/snapshot.json --plugin snapshot +``` + +What happens during load: + +1. Snapshot plugin registers a load callback on the chain plugin. +2. Chain plugin checks: if `shared_memory.bin` already exists → skips import (restart safety). If snapshot file not found → skips import. +3. Database is opened via `open_from_snapshot()` — shared memory is wiped, chainbase initialized. +4. Snapshot is validated (format version, chain ID, SHA-256 checksum) and all 32 object types imported. +5. Snapshot file is renamed to `.used`. +6. LIB is promoted to `head_block_num` so P2P synopsis starts from the snapshot head. +7. Fork database is seeded with the snapshot head block. +8. P2P starts syncing from LIB + 1. + +### Restart Safety + +| Restart scenario | What happens | +|-----------------|--------------| +| 1st start (no shared_memory, file exists) | Imports snapshot, renames to `.used` | +| Restart (shared_memory exists) | Skips import | +| Restart (shared_memory wiped, file is `.used`) | Skips import | +| Force re-import | `--resync-blockchain` + fresh snapshot file | + +The `--snapshot` flag is safe to leave on the command line permanently. + +--- + +## DLT Rolling Block Log + +When running in DLT mode (loaded from snapshot), the main `block_log` is empty. A separate **DLT rolling block log** (`dlt_block_log`) stores the most recent irreversible blocks. + +- Enables P2P block serving (peers can request recent blocks for fork resolution). +- Enables API calls like `get_block` for recent blocks. +- Stored in `dlt_block_log.log` and `dlt_block_log.index` in the blockchain data directory. +- Rolling window: when the log exceeds `dlt-block-log-max-blocks`, old blocks are truncated from the front. + +```ini +dlt-block-log-max-blocks = 100000 +``` + +--- + +## Crash Recovery: `--replay-from-snapshot` + +When `shared_memory.bin` is corrupted after an unclean shutdown, use this mode: + +```bash +# Specify snapshot path explicitly +vizd --replay-from-snapshot --snapshot /data/snapshots/snapshot-block-79273800.vizjson --plugin snapshot + +# Auto-discover the latest snapshot +vizd --replay-from-snapshot --snapshot-auto-latest --plugin snapshot +``` + +Recovery steps: +1. Wipes `shared_memory.bin` (always — assumes corruption). +2. Imports snapshot state. +3. Replays `dlt_block_log` blocks beyond the snapshot head. +4. Resumes P2P sync from the replayed head. + +| Aspect | `--snapshot` | `--replay-from-snapshot` | +|--------|-------------|--------------------------| +| Shared memory check | Skips if exists | Always wipes | +| Snapshot rename | Renames to `.used` | Does not rename | +| DLT block log replay | No | Yes | +| Use case | Bootstrap | Crash recovery | + +--- + +## Automatic Runtime Recovery: `--auto-recover-from-snapshot` + +Enabled by default (`true`). When shared memory corruption is detected during block processing or block generation, the node: + +1. Closes the database. +2. Finds the latest snapshot in `snapshot-dir`. +3. Wipes shared memory, imports snapshot, replays `dlt_block_log`. +4. Resumes P2P sync — **no restart required**. + +Prerequisites: +- `plugin = snapshot` must be enabled. +- Snapshots must exist in `snapshot-dir`. + +To disable (e.g., for debugging): + +```bash +vizd --no-auto-recover-from-snapshot +``` + +--- + +## P2P Snapshot Sync + +Nodes can download snapshots directly from trusted peers over TCP. + +### Server configuration + +```ini +plugin = snapshot +allow-snapshot-serving = true +snapshot-serve-endpoint = 0.0.0.0:8092 +snapshot-every-n-blocks = 28800 +snapshot-dir = /data/viz-snapshots +``` + +### Client configuration + +```ini +trusted-snapshot-peer = seed1.viz.world:8092 +trusted-snapshot-peer = seed2.viz.world:8092 +sync-snapshot-from-trusted-peer = true +``` + +The client connects to each trusted peer, selects the one with the highest block number, downloads the snapshot in 1 MB chunks, verifies the SHA-256 checksum, and imports it. + +Security features: max 2 GB snapshot size, trusted peer list, rate limiting, 60-second connection deadline, streaming checksum verification. + +--- + +## Snapshot File Format + +```json +{ + "header": { + "version": 1, + "chain_id": "...", + "snapshot_block_num": 12345678, + "snapshot_block_id": "...", + "snapshot_block_time": "2025-01-01T00:00:00", + "last_irreversible_block_num": 12345660, + "payload_checksum": "sha256...", + "object_counts": { "account": 50000, ... } + }, + "state": { + "dynamic_global_property": [ ... ], + "account": [ ... ], + ... + } +} +``` + +### Included Object Types (32 total) + +**Critical (11):** `dynamic_global_property`, `witness_schedule`, `hardfork_property`, `account`, `account_authority`, `validator`, `witness_vote`, `block_summary`, `content`, `content_vote`, `block_post_validation` + +**Important (15):** `transaction`, `vesting_delegation`, `vesting_delegation_expiration`, `fix_vesting_delegation`, `withdraw_vesting_route`, `escrow`, `proposal`, `required_approval`, `committee_request`, `committee_vote`, `invite`, `award_shares_expire`, `paid_subscription`, `paid_subscribe`, `witness_penalty_expire` + +**Optional (5):** `content_type`, `account_metadata`, `master_authority_history`, `account_recovery_request`, `change_recovery_account_request` + +--- + +## Recommended Production Config + +```ini +plugin = snapshot + +# Create a snapshot every ~24 hours +snapshot-every-n-blocks = 28800 +snapshot-dir = /data/viz-snapshots + +# Auto-delete snapshots older than 90 days +snapshot-max-age-days = 90 + +# DLT rolling block log: keep last 100k blocks +dlt-block-log-max-blocks = 100000 + +shared-file-size = 4G +plugin = p2p +p2p-seed-node = seed1.viz.world:2001 +``` + +--- + +## Docker Quick Reference + +| Task | Command | +|------|---------| +| Start with periodic snapshots | Add `snapshot-every-n-blocks` to config, restart container | +| One-shot snapshot | `VIZD_EXTRA_OPTS="--create-snapshot /var/lib/vizd/snapshots/snap.json --plugin snapshot"` | +| Load from snapshot | `VIZD_EXTRA_OPTS="--snapshot /var/lib/vizd/snapshots/snap.json --plugin snapshot"` | +| Crash recovery | `VIZD_EXTRA_OPTS="--replay-from-snapshot --snapshot-auto-latest --plugin snapshot"` | +| Auto-recovery (default) | Enabled by default; ensure `plugin = snapshot` and `snapshot-every-n-blocks` are set | + +--- + +See also: [Chain Plugin](../plugins/chain.md), [Shared Memory](./shared-memory.md), [Block Log](./block-log.md), [P2P Sync Scenarios](../p2p/sync-scenarios.md). diff --git a/package.json b/package.json new file mode 100644 index 0000000000..ba9eef9738 --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "name": "viz-ledger-docs", + "version": "1.0.0", + "private": true, + "scripts": { + "start": "realm dev", + "build": "realm build", + "preview": "realm preview" + }, + "devDependencies": { + "@redocly/realm": "latest" + } +} diff --git a/redocly.yaml b/redocly.yaml new file mode 100644 index 0000000000..b8f283fcab --- /dev/null +++ b/redocly.yaml @@ -0,0 +1,189 @@ +seo: + title: VIZ Ledger Documentation + description: Official documentation for VIZ Ledger — hybrid DLT with Fair-DPOS consensus + +l10n: + defaultLocale: en-US + locales: + - code: en-US + name: English + - code: ru + name: Русский + - code: zh-CN + name: 中文 + +navbar: + items: + - label: Introduction + page: docs/introduction/what-is-viz.md + - label: Run a Node + page: docs/node/getting-started.md + - label: Protocol + page: docs/protocol/data-types.md + - label: API + page: docs/api/json-rpc.md + - label: GitHub + href: https://github.com/VIZ-Blockchain/viz-cpp-node + external: true + +footer: + copyrightText: VIZ Ledger + +sidebars: + docs: + - group: Introduction + items: + - label: What is VIZ Ledger? + page: docs/introduction/what-is-viz.md + - label: Architecture + page: docs/introduction/architecture.md + - label: Key Concepts + page: docs/introduction/key-concepts.md + + - group: Run a Node + items: + - label: Getting Started + page: docs/node/getting-started.md + - label: Configuration + page: docs/node/configuration.md + - label: Building from Source + page: docs/node/building.md + - label: Docker + page: docs/node/docker.md + - label: Validator Node + page: docs/node/validator-node.md + - label: Validator Guard + page: docs/node/validator-guard.md + - label: Snapshots + page: docs/node/snapshot.md + - label: Monitoring + page: docs/node/monitoring.md + + - group: Consensus + items: + - label: Fair-DPOS + page: docs/consensus/fair-dpos.md + - label: Block Processing + page: docs/consensus/block-processing.md + - label: Fork Resolution + page: docs/consensus/fork-resolution.md + - label: Emergency Consensus + page: docs/consensus/emergency-consensus.md + - label: Hardforks + page: docs/consensus/hardforks.md + + - group: P2P Network + items: + - label: Overview + page: docs/p2p/overview.md + - label: Messages + page: docs/p2p/messages.md + - label: Sync Scenarios + page: docs/p2p/sync-scenarios.md + - label: Forward Mode + page: docs/p2p/forward-mode.md + - label: Stats Reference + page: docs/p2p/stats-reference.md + + - group: Plugins + items: + - label: Overview + page: docs/plugins/overview.md + - label: Validator + page: docs/plugins/validator.md + - label: Snapshot + page: docs/plugins/snapshot.md + - label: Chain + page: docs/plugins/chain.md + - label: Database API + page: docs/plugins/database-api.md + - label: Webserver + page: docs/plugins/webserver.md + + - group: Protocol + items: + - label: Data Types + page: docs/protocol/data-types.md + - label: Virtual Operations + page: docs/protocol/virtual-operations.md + - group: Operations + items: + - label: Overview + page: docs/protocol/operations/overview.md + - label: Accounts + page: docs/protocol/operations/accounts.md + - label: Transfers & Vesting + page: docs/protocol/operations/transfers.md + - label: Validators + page: docs/protocol/operations/validators.md + - label: Content + page: docs/protocol/operations/content.md + - label: Recovery + page: docs/protocol/operations/recovery.md + - label: Escrow + page: docs/protocol/operations/escrow.md + - label: Committee + page: docs/protocol/operations/committee.md + - label: Invites + page: docs/protocol/operations/invites.md + - label: Awards + page: docs/protocol/operations/awards.md + - label: Subscriptions + page: docs/protocol/operations/subscriptions.md + - label: Account Market + page: docs/protocol/operations/account-market.md + - label: Proposals + page: docs/protocol/operations/proposals.md + + - group: Storage + items: + - label: Shared Memory + page: docs/storage/shared-memory.md + - label: Block Log + page: docs/storage/block-log.md + - label: Snapshots + page: docs/storage/snapshots.md + + - group: Governance + items: + - label: Chain Properties + page: docs/governance/chain-properties.md + - label: Staking & DAO + page: docs/governance/staking-and-dao.md + - label: Committee + page: docs/governance/committee.md + + - group: API Reference + items: + - label: JSON-RPC + page: docs/api/json-rpc.md + - label: CLI Wallet + page: docs/api/cli-wallet.md + - label: Client Libraries + page: docs/api/client-libraries.md + + - group: Development + items: + - label: Building + page: docs/development/building.md + - label: Testing + page: docs/development/testing.md + - label: Debugging + page: docs/development/debugging.md + - label: Plugin Development + page: docs/development/plugin-development.md + + - group: Advanced + items: + - label: Security + page: docs/advanced/security.md + - label: Database Schema + page: docs/advanced/database-schema.md + - label: DLT Architecture + page: docs/advanced/dlt-architecture.md + - label: Hardfork Management + page: docs/advanced/hardfork-management.md + +sidebarAssociations: + - sidebarName: docs + basePath: / From d32a5cf37b881d6fea734f80be1bd2fcf7eabacb Mon Sep 17 00:00:00 2001 From: Anatoly Piskunov Date: Tue, 19 May 2026 19:38:12 +0400 Subject: [PATCH 28/29] docs(api): add Swift client library to official client libraries section - Introduce viz-swift-lib as a new low-level Swift client library - Provide installation instructions using Swift Package Manager and Xcode - Include usage examples for key derivation, transactions, and JSON-RPC calls - Detail features like full operation coverage, multi-sig support, async/await concurrency - Update client library comparison tables to include Swift with relevant details - Extend documentation in English, Russian, and Chinese localization files --- @l10n/ru/docs/api/client-libraries.md | 103 +++++++++++++++++++++-- @l10n/zh-CN/docs/api/client-libraries.md | 103 +++++++++++++++++++++-- docs/api/client-libraries.md | 103 +++++++++++++++++++++-- 3 files changed, 285 insertions(+), 24 deletions(-) diff --git a/@l10n/ru/docs/api/client-libraries.md b/@l10n/ru/docs/api/client-libraries.md index 7f2d4b3097..edb0cee40b 100644 --- a/@l10n/ru/docs/api/client-libraries.md +++ b/@l10n/ru/docs/api/client-libraries.md @@ -1,6 +1,6 @@ # Клиентские библиотеки -Официальные клиентские библиотеки доступны для Python, PHP и JavaScript. Все библиотеки взаимодействуют с узлом VIZ через JSON-RPC API и выполняют подписание транзакций локально. +Официальные клиентские библиотеки доступны для Python, PHP, JavaScript и Swift. Все библиотеки взаимодействуют с узлом VIZ через JSON-RPC API и выполняют подписание транзакций локально. --- @@ -125,15 +125,102 @@ viz.config.set('websocket', 'https://node.viz.cx/'); --- +## Swift — viz-swift-lib + +**Репозиторий:** https://github.com/VIZ-Blockchain/viz-swift-lib + +Низкоуровневая, минималистичная Swift-библиотека: предоставляет примитивы — операции, транзакции, подписание, JSON-RPC — без лишних абстракций. Подходит для мобильных кошельков, бэкенд-сервисов и ботов, где нужен полный контроль. + +### Установка + +Swift Package Manager — добавьте в `Package.swift`: + +```swift +dependencies: [ + .package( + url: "https://github.com/viz-blockchain/viz-swift-lib.git", + .upToNextMinor(from: "0.1.0") + ) +] +``` + +Или в Xcode: **File → Add Packages…** и введите URL репозитория. + +### Быстрый старт + +```swift +import VIZ + +let client = VIZ.Client(address: URL(string: "https://node.viz.cx")!) + +// Получить параметры цепочки для reference block +let props = try await client.send(VIZ.API.GetDynamicGlobalProperties()) + +// Получить ключ подписания +let key = VIZ.PrivateKey(seed: "alice" + "active" + "password")! + +// Создать операцию +let transfer = VIZ.Operation.Transfer( + from: "alice", + to: "bob", + amount: VIZ.Asset(10.0, .viz), + memo: "Спасибо!" +) + +// Обернуть в транзакцию, подписать и транслировать +let tx = VIZ.Transaction( + refBlockNum: UInt16(props.headBlockNumber & 0xFFFF), + refBlockPrefix: props.headBlockId.prefix, + expiration: props.time.addingTimeInterval(60), + operations: [transfer] +) +let signed = try tx.sign(usingKey: key) +let confirmation = try await client.send( + VIZ.API.BroadcastTransaction(transaction: signed) +) +``` + +### Работа с ключами + +```swift +// Вывод ключей из seed +let privateKey = VIZ.PrivateKey(seed: "username" + "active" + "password")! +let publicKey = privateKey.createPublic() +print(publicKey.address) // VIZ7… +print(privateKey.wif) // 5K… + +// Импорт из WIF +let imported = VIZ.PrivateKey("5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3")! + +// Активы +let amount = VIZ.Asset(100.5, .viz) // "100.500 VIZ" +let shares = VIZ.Asset(1000.0, .vests) // "1000.000000 VESTS" +``` + +### Возможности + +- Полное покрытие операций: переводы, награды, создание/обновление аккаунтов, vesting, escrow, восстановление и другие +- Компонуемые транзакции — несколько операций в одной подписанной транзакции +- Pure-Swift `secp256k1` ECDSA подписание; вывод ключей из seed; импорт/экспорт WIF +- JSON-RPC клиент на `async/await` с `actor`-архитектурой, типы `Sendable` +- Составные multi-sig authority +- Делегированное подписание через `viz://` URL +- Кроссплатформенность: iOS 13+, macOS 10.15+, tvOS 13+, watchOS 6+, Linux +- Нет скрытого состояния — управление ключами, повторы и трансляция остаются на стороне разработчика +- Лицензия MIT + +--- + ## Выбор библиотеки -| Критерий | Python | PHP | JavaScript | -|----------|--------|-----|-----------| -| Установка | `pip` | Ручная PSR-4 | `npm` | -| Транспорт | WS / HTTP | HTTP | WS / HTTP | -| Среда | Python 3 | PHP 7+ | Node.js / браузер | -| Зависимости | минимальные | GMP или BCMath | нет (bundled) | -| Лицензия | MIT | MIT | MIT | +| Критерий | Python | PHP | JavaScript | Swift | +|----------|--------|-----|-----------|-------| +| Установка | `pip` | Ручная PSR-4 | `npm` / CDN | Swift PM / Xcode | +| Транспорт | WS / HTTP | HTTP | WS / HTTP | HTTP (async/await) | +| Среда | Python 3 | PHP 7+ | Node.js / браузер | iOS / macOS / Linux | +| Зависимости | минимальные | GMP или BCMath | нет (bundled) | secp256k1, OrderedDictionary | +| Уровень | высокоуровневый | высокоуровневый | высокоуровневый | низкоуровневые примитивы | +| Лицензия | MIT | MIT | MIT | MIT | --- diff --git a/@l10n/zh-CN/docs/api/client-libraries.md b/@l10n/zh-CN/docs/api/client-libraries.md index 71076a0f17..95978527bb 100644 --- a/@l10n/zh-CN/docs/api/client-libraries.md +++ b/@l10n/zh-CN/docs/api/client-libraries.md @@ -1,6 +1,6 @@ # 客户端库 -Python、PHP 和 JavaScript 均有官方客户端库。所有库通过 JSON-RPC API 与 VIZ 节点通信,并在本地完成交易签名。 +Python、PHP、JavaScript 和 Swift 均有官方客户端库。所有库通过 JSON-RPC API 与 VIZ 节点通信,并在本地完成交易签名。 --- @@ -125,15 +125,102 @@ viz.config.set('websocket', 'https://node.viz.cx/'); --- +## Swift — viz-swift-lib + +**仓库:** https://github.com/VIZ-Blockchain/viz-swift-lib + +底层、无强制约定的 Swift 库,提供操作、交易、签名、JSON-RPC 等原语,不隐藏任何细节。适用于需要完全控制的移动钱包、后端服务和机器人应用。 + +### 安装 + +Swift Package Manager — 添加到 `Package.swift`: + +```swift +dependencies: [ + .package( + url: "https://github.com/viz-blockchain/viz-swift-lib.git", + .upToNextMinor(from: "0.1.0") + ) +] +``` + +或在 Xcode 中:**File → Add Packages…** 并输入仓库 URL。 + +### 快速开始 + +```swift +import VIZ + +let client = VIZ.Client(address: URL(string: "https://node.viz.cx")!) + +// 获取链头参数用于引用区块 +let props = try await client.send(VIZ.API.GetDynamicGlobalProperties()) + +// 派生签名密钥 +let key = VIZ.PrivateKey(seed: "alice" + "active" + "password")! + +// 构建操作 +let transfer = VIZ.Operation.Transfer( + from: "alice", + to: "bob", + amount: VIZ.Asset(10.0, .viz), + memo: "谢谢!" +) + +// 封装为交易、签名并广播 +let tx = VIZ.Transaction( + refBlockNum: UInt16(props.headBlockNumber & 0xFFFF), + refBlockPrefix: props.headBlockId.prefix, + expiration: props.time.addingTimeInterval(60), + operations: [transfer] +) +let signed = try tx.sign(usingKey: key) +let confirmation = try await client.send( + VIZ.API.BroadcastTransaction(transaction: signed) +) +``` + +### 密钥工具 + +```swift +// 从种子派生密钥 +let privateKey = VIZ.PrivateKey(seed: "username" + "active" + "password")! +let publicKey = privateKey.createPublic() +print(publicKey.address) // VIZ7… +print(privateKey.wif) // 5K… + +// 从 WIF 导入 +let imported = VIZ.PrivateKey("5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3")! + +// 资产 +let amount = VIZ.Asset(100.5, .viz) // "100.500 VIZ" +let shares = VIZ.Asset(1000.0, .vests) // "1000.000000 VESTS" +``` + +### 功能特性 + +- 完整操作覆盖:转账、奖励、账户创建/更新、vesting、escrow、账户恢复等 +- 可组合交易——一笔已签名交易中包含多个操作 +- 纯 Swift `secp256k1` ECDSA 签名;从种子派生密钥;WIF 导入/导出 +- 基于 `async/await` 的 JSON-RPC 客户端,`actor` 并发架构,全 `Sendable` 类型 +- 多签 authority 组合 +- 通过 `viz://` URL 进行委托签名 +- 跨平台:iOS 13+、macOS 10.15+、tvOS 13+、watchOS 6+、Linux +- 无隐藏状态——密钥管理、重试和广播均由调用方负责 +- MIT 许可证 + +--- + ## 如何选择库 -| 标准 | Python | PHP | JavaScript | -|------|--------|-----|-----------| -| 安装方式 | `pip` | 手动 PSR-4 | `npm` | -| 传输协议 | WS / HTTP | HTTP | WS / HTTP | -| 运行环境 | Python 3 | PHP 7+ | Node.js / 浏览器 | -| 依赖项 | 极少 | GMP 或 BCMath | 无(已打包) | -| 许可证 | MIT | MIT | MIT | +| 标准 | Python | PHP | JavaScript | Swift | +|------|--------|-----|-----------|-------| +| 安装方式 | `pip` | 手动 PSR-4 | `npm` / CDN | Swift PM / Xcode | +| 传输协议 | WS / HTTP | HTTP | WS / HTTP | HTTP(async/await) | +| 运行环境 | Python 3 | PHP 7+ | Node.js / 浏览器 | iOS / macOS / Linux | +| 依赖项 | 极少 | GMP 或 BCMath | 无(已打包) | secp256k1、OrderedDictionary | +| 抽象层次 | 高层 | 高层 | 高层 | 底层原语 | +| 许可证 | MIT | MIT | MIT | MIT | --- diff --git a/docs/api/client-libraries.md b/docs/api/client-libraries.md index 19bdbc4704..33d5673cae 100644 --- a/docs/api/client-libraries.md +++ b/docs/api/client-libraries.md @@ -1,6 +1,6 @@ # Client Libraries -Official client libraries are available for Python, PHP, and JavaScript. All libraries communicate with a VIZ node over the JSON-RPC API and handle transaction signing locally. +Official client libraries are available for Python, PHP, JavaScript, and Swift. All libraries communicate with a VIZ node over the JSON-RPC API and handle transaction signing locally. --- @@ -125,15 +125,102 @@ viz.config.set('websocket', 'https://node.viz.cx/'); --- +## Swift — viz-swift-lib + +**Repository:** https://github.com/VIZ-Blockchain/viz-swift-lib + +Low-level, unopinionated Swift library providing primitives — operations, transactions, signing, JSON-RPC — without hiding any details. Suitable for mobile wallets, backend services, and bots where full control is required. + +### Installation + +Swift Package Manager — add to `Package.swift`: + +```swift +dependencies: [ + .package( + url: "https://github.com/viz-blockchain/viz-swift-lib.git", + .upToNextMinor(from: "0.1.0") + ) +] +``` + +Or in Xcode: **File → Add Packages…** and enter the repository URL. + +### Quick start + +```swift +import VIZ + +let client = VIZ.Client(address: URL(string: "https://node.viz.cx")!) + +// Fetch chain head for the reference block +let props = try await client.send(VIZ.API.GetDynamicGlobalProperties()) + +// Derive the signing key +let key = VIZ.PrivateKey(seed: "alice" + "active" + "password")! + +// Build the operation +let transfer = VIZ.Operation.Transfer( + from: "alice", + to: "bob", + amount: VIZ.Asset(10.0, .viz), + memo: "Thanks!" +) + +// Wrap in a transaction, sign, and broadcast +let tx = VIZ.Transaction( + refBlockNum: UInt16(props.headBlockNumber & 0xFFFF), + refBlockPrefix: props.headBlockId.prefix, + expiration: props.time.addingTimeInterval(60), + operations: [transfer] +) +let signed = try tx.sign(usingKey: key) +let confirmation = try await client.send( + VIZ.API.BroadcastTransaction(transaction: signed) +) +``` + +### Key utilities + +```swift +// Derive keys from seed +let privateKey = VIZ.PrivateKey(seed: "username" + "active" + "password")! +let publicKey = privateKey.createPublic() +print(publicKey.address) // VIZ7… +print(privateKey.wif) // 5K… + +// Import from WIF +let imported = VIZ.PrivateKey("5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3")! + +// Assets +let amount = VIZ.Asset(100.5, .viz) // "100.500 VIZ" +let shares = VIZ.Asset(1000.0, .vests) // "1000.000000 VESTS" +``` + +### Features + +- Full operation coverage: transfers, awards, account create/update, vesting, escrow, recovery, and more +- Composable transactions — multiple operations in one signed transaction +- Pure-Swift `secp256k1` ECDSA signing; key derivation from seed; WIF import/export +- `async/await` JSON-RPC client with `actor`-based concurrency, `Sendable` types throughout +- Multi-signature authority composition +- Delegated signing via `viz://` URLs +- Cross-platform: iOS 13+, macOS 10.15+, tvOS 13+, watchOS 6+, Linux +- No hidden state — key management, retries, and broadcasting are left to the caller +- MIT license + +--- + ## Choosing a Library -| Criterion | Python | PHP | JavaScript | -|-----------|--------|-----|-----------| -| Install | `pip` | Manual PSR-4 | `npm` | -| Transport | WS / HTTP | HTTP | WS / HTTP | -| Runtime | Python 3 | PHP 7+ | Node.js / browser | -| Dependencies | minimal | GMP or BCMath | none (bundled) | -| License | MIT | MIT | MIT | +| Criterion | Python | PHP | JavaScript | Swift | +|-----------|--------|-----|-----------|-------| +| Install | `pip` | Manual PSR-4 | `npm` / CDN | Swift PM / Xcode | +| Transport | WS / HTTP | HTTP | WS / HTTP | HTTP (async/await) | +| Runtime | Python 3 | PHP 7+ | Node.js / browser | iOS / macOS / Linux | +| Dependencies | minimal | GMP or BCMath | none (bundled) | secp256k1, OrderedDictionary | +| Level | high-level | high-level | high-level | low-level primitives | +| License | MIT | MIT | MIT | MIT | --- From e488f5a22e127e269756f1b482fc6f8db2c89e43 Mon Sep 17 00:00:00 2001 From: Anatoly Piskunov Date: Tue, 19 May 2026 20:25:59 +0400 Subject: [PATCH 29/29] chore(thirdparty): update fc submodule commit reference - Updated fc submodule to latest commit 8e974c2c4a57fd7a60971bd379a0d9cd857eb6ae - Replaced previous commit cf033fee1b802a39fe11de4cc1e311faae29f168 with new reference - Ensured thirdparty/fc pointer is aligned with upstream repository changes --- thirdparty/fc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/thirdparty/fc b/thirdparty/fc index cf033fee1b..8e974c2c4a 160000 --- a/thirdparty/fc +++ b/thirdparty/fc @@ -1 +1 @@ -Subproject commit cf033fee1b802a39fe11de4cc1e311faae29f168 +Subproject commit 8e974c2c4a57fd7a60971bd379a0d9cd857eb6ae