From 160bde7d39c558b068fed7c0f5b6a4096ccc099c Mon Sep 17 00:00:00 2001 From: thephez Date: Wed, 8 Nov 2017 12:30:29 -0500 Subject: [PATCH] MN Sync - Add ongoing sync drawing - Re-arrange layout / rename some sections - Add additional details about ongoing sync Correct issue w/ mnget P2P message in dev ref Add masternode sync drawing --- _includes/devdoc/guide_p2p_network.md | 147 ++--- _includes/devdoc/ref_p2p_networking.md | 4 +- img/dev/en-masternode-sync-ongoing.svg | 765 +++++++++++++++++++++++++ img/dev/en-masternode-sync.odg | Bin 0 -> 17100 bytes 4 files changed, 850 insertions(+), 66 deletions(-) create mode 100644 img/dev/en-masternode-sync-ongoing.svg create mode 100644 img/dev/en-masternode-sync.odg diff --git a/_includes/devdoc/guide_p2p_network.md b/_includes/devdoc/guide_p2p_network.md index ac0b41c4..713f73cf 100644 --- a/_includes/devdoc/guide_p2p_network.md +++ b/_includes/devdoc/guide_p2p_network.md @@ -674,75 +674,15 @@ several conditions that initiate a start/restart the sync process: * A failure occurred during the last sync attempt (after a 1 minute cooldown before sync restarts) * Issuing a `mnsync reset` RPC command -Once a masternode completes an initial full sync, continuing synchronization is -maintained by the exchange of P2P messages with other nodes. Each masternode -issues a ping (`mnp` message) periodically to notify others that it is still -online. Masternodes that do not issue a ping for 3 hours will be put into the -`MASTERNODE_NEW_START_REQUIRED` state and will need to issue a masternode -announce (`mnb` message). - - -*Masternode List* - -After the initial masternode list has been received, it is kept current by a -combination of the periodic `mnp` messages received from other masternodes and -the `mnb` messages sent by masternodes as they come online. Also, `dseg` messages -can be sent to request masternode info when messages are received containing -unrecognized masternode `vin` entries (most masternode/governance messages -include a `vin` value that can be used to verify the masternode's unspent 1000 -Dash). - -*Masternode Payment* - -After the initial masternode payment synchronization, payment information is -kept current via the `mnw` messages relayed on the network. - -#### Masternode Sync Status - -There are several status values used to track masternode synchronization. They -are used in both `ssc` messages and the `mnsync` RPC. - -| **Value** | **Status** | **Description** | -| -1 | `MASTERNODE_SYNC_FAILED` | Synchronization failed | -| 0 | `MASTERNODE_SYNC_INITIAL` | Synchronization just started, was reset recently, or is still in IBD | -| 1 | `MASTERNODE_SYNC_WAITING` | Synchronization pending - waiting after initial to check for more headers/blocks | -| 2 | `MASTERNODE_SYNC_LIST` | Synchronizing masternode list | -| 3 | `MASTERNODE_SYNC_MNW` | Synchronizing masternode payments | -| 4 | `MASTERNODE_SYNC_GOVERNANCE` | Synchronizing governance objects | -| 999 | `MASTERNODE_SYNC_FINISHED` | Synchronization finished | - - -#### Masternode Sync Schedule - -The following tables detail the timing of various functions used to keep the -masternodes in sync with each other. This information is derived from -`ThreadCheckPrivateSend` in `src/privatesend.cpp`. - -| **Period (seconds)** | **Action** | **Description** | -| 6 | MN Sync | Synchronizes sporks, masternode list, masternode payments, and governance objects | - -The following actions only run when the masternode sync is past `MASTERNODE_SYNC_WAITING` status. - -| **Period (seconds)** | **Action** | **Description** | -| 1 | MN Check | Check the state of each masternode that is still funded and not banned. The action occurs once per second, but individual masternodes are only checked at most every 5 seconds (only a subset of masternodes are checked each time it runs) | -| 60 | Process MN Connections | Disconnects some masternodes | -| 60 | MN Check/Remove | Remove spent masternodes and check the state of inactive ones | -| 60 | MN Payment Check/Remove | Remove old masternode payment votes/blocks | -| 60 | InstantSend Check/Remove | Remove expired/orphaned/invalid InstantSend candidates and votes | -| 300 | Full verification | Verify masternodes via direct requests (`mnv` messages - note time constraints in the Developer Reference section) | -| 300 | Maintenance | Check/remove/reprocess governance objects | -| 600 | Manage State | Sends masternode pings (`mnp` message). Also sends initial masternode broadcast (`mnb` message) for local masternodes. | - - -#### Masternode Sync Data Flow +#### Initial Masternode Sync This diagram shows the order in which P2P messages are sent to perform masternode synchronization initially after startup. ![Masternode Sync (Initial)](/img/dev/en-masternode-sync-initial.svg) -The following table details the exchanged of P2P messages during masternode -synchronization. +The following table details the data flow of P2P messages exchanged during +initial masternode synchronization. | **Syncing Node Message** | **Direction** | **Masternode Response** | **Description** | | **1. Sporks** | | | | @@ -765,8 +705,87 @@ synchronization. | | ← | `mnw` message(s) | (If requested) Masternode payment vote message | **4. Governance** | | | See [Governance sync](#governance) | -{% endautocrossref %} +*Masternode Sync Status* + +There are several status values used to track masternode synchronization. They +are used in both `ssc` messages and the `mnsync` RPC. + +| **Value** | **Status** | **Description** | +| -1 | `MASTERNODE_SYNC_FAILED` | Synchronization failed | +| 0 | `MASTERNODE_SYNC_INITIAL` | Synchronization just started, was reset recently, or is still in IBD | +| 1 | `MASTERNODE_SYNC_WAITING` | Synchronization pending - waiting after initial to check for more headers/blocks | +| 2 | `MASTERNODE_SYNC_LIST` | Synchronizing masternode list | +| 3 | `MASTERNODE_SYNC_MNW` | Synchronizing masternode payments | +| 4 | `MASTERNODE_SYNC_GOVERNANCE` | Synchronizing governance objects | +| 999 | `MASTERNODE_SYNC_FINISHED` | Synchronization finished | + + +#### Ongoing Masternode Sync + +Once a masternode completes an initial full sync, continuing synchronization is +maintained by the exchange of P2P messages with other nodes. This diagram shows +an overview of the messages exchanged to keep the masternode list, masternode +payments, and governance objects synchronized between masternodes. + +![Masternode Sync (Ongoing)](/img/dev/en-masternode-sync-ongoing.svg) + +**Recurring Ping** + +Each masternode issues a ping (`mnp` message) periodically to notify the network +that it is still online. Masternodes that do not issue a ping for 3 hours will +be put into the `MASTERNODE_NEW_START_REQUIRED` state and will need to issue a +masternode announce (`mnb` message). + +**Masternode List** + +After the initial masternode list has been received, it is kept current by a +combination of the periodic `mnp` messages received from other masternodes, +the `mnb` messages sent by masternodes as they come online, and `mnv` messages +to verify that other masternodes are valid. + +Also, `dseg` messages can be sent to request masternode info when messages are +received that have been signed by an unrecognized masternode (most masternode/governance +messages include a `vin` value that can be used to verify the masternode's +unspent 1000 Dash). + +Unsynchronized peers may send a `dseg` message to request the entire masternode list. + +**Masternode Payment** + +After the initial masternode payment synchronization, payment information is +kept current via the `mnw` messages relayed on the network. Unsynchronized peers +may send a `mnget` message to request masternode payment sync. + +**Governance** + +After the initial governance synchronization, governance information is kept +current by the `govobj` messages and `govobjvote` messages relayed on the +network. Unsynchronized peers may send `govsync` messages to request governance +sync. + +#### Masternode Sync Schedule + +The following tables detail the timing of various functions used to keep the +masternodes in sync with each other. This information is derived from +`ThreadCheckPrivateSend` in `src/privatesend.cpp`. + +| **Period (seconds)** | **Action** | **Description** | +| 6 | MN Sync | Synchronizes sporks, masternode list, masternode payments, and governance objects | + +The following actions only run when the masternode sync is past `MASTERNODE_SYNC_WAITING` status. + +| **Period (seconds)** | **Action** | **Description** | +| 1 | MN Check | Check the state of each masternode that is still funded and not banned. The action occurs once per second, but individual masternodes are only checked at most every 5 seconds (only a subset of masternodes are checked each time it runs) | +| 60 | Process MN Connections | Disconnects some masternodes | +| 60 | MN Check/Remove | Remove spent masternodes and check the state of inactive ones | +| 60 | MN Payment Check/Remove | Remove old masternode payment votes/blocks | +| 60 | InstantSend Check/Remove | Remove expired/orphaned/invalid InstantSend candidates and votes | +| 300 | Full verification | Verify masternodes via direct requests (`mnv` messages - note time constraints in the Developer Reference section) | +| 300 | Maintenance | Check/remove/reprocess governance objects | +| 600 | Manage State | Sends masternode pings (`mnp` message). Also sends initial masternode broadcast (`mnb` message) for local masternodes. | + +{% endautocrossref %} ### Governance diff --git a/_includes/devdoc/ref_p2p_networking.md b/_includes/devdoc/ref_p2p_networking.md index c906c877..a03f63f6 100644 --- a/_includes/devdoc/ref_p2p_networking.md +++ b/_includes/devdoc/ref_p2p_networking.md @@ -2311,8 +2311,8 @@ Masternode Ping Message {% autocrossref %} The `mnget` message requests masternode payment sync. The response to an -`mnget` message is an `mnb` message inventory and an `mnp` message inventory for -each requested masternode. Masternodes ignore this request if they are not fully synced. +`mnget` message is `mnw` message inventories (up to the number asked for in the +request). Masternodes ignore this request if they are not fully synced. | Bytes | Name | Data type | Required | Description | | ---------- | ----------- | --------- | -------- | -------- | diff --git a/img/dev/en-masternode-sync-ongoing.svg b/img/dev/en-masternode-sync-ongoing.svg new file mode 100644 index 00000000..0d47c1b0 --- /dev/null +++ b/img/dev/en-masternode-sync-ongoing.svg @@ -0,0 +1,765 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Masternode Synchronization (Ongoing) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MasternodePayments + + + + + + + + + + MasternodeList + + + + + + + + + + Governance + + + + + + PaymentVotes + + + + + + + + + + + + + + + + + + + + + + mnw + + + + + + PaymentVotes + + + + + + + + mnw + + + + + + BroadcastsfromStarting MNs + + + + + + Recurring Ping(15 min) + + + + + + + + mnb + + + + + + Relayed MNBroadcasts + + + + + + Relayed MNPings + + + + + + Relayed MNVerifications + + + + + + MN ListRequests + + + + + + + + + dseg + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + mnb + + + + + + + + + + + + + + + + + + + mnp + + + + + + + + + + + + + + + + + + + + + + + + + + + + + mnp + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + mnv + + + + + + MN Pings + + + + + + + + mnp + + + + + + PaymentSync Request + + + + + + VerificationRequests + + + + + + + + + mnv + + + + + + + + mnget + + + + + + Governance SyncRequest + + + + + + Governance Object + + + + + + GovernanceObject Vote + + + + + + GovernanceObject + + + + + + + + govsync + + + + + + + + govobj + + + + + + + + govobjvote + + + + + + GovernanceVote + + + + + + + + + + + + + + + + + + + + + + govobjvote + + + + + + + + + + + + + + + + + + + + + + + govobj + + + + + + + + Relayed message (to all peers) or sync response + + + + + + + + + + + + + + + + + + + + + + + Communication with an individual peer + + + + + + + + + + + + + + Periodic send to all peers + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/img/dev/en-masternode-sync.odg b/img/dev/en-masternode-sync.odg new file mode 100644 index 0000000000000000000000000000000000000000..a3c285f0eabe554872381d22df49796a516caf00 GIT binary patch literal 17100 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