Skip to content

Commit

Permalink
Merge #2267: [Doc] Introducing Deterministic Masternodes
Browse files Browse the repository at this point in the history
9f43405 [Doc] Add DIP3 rpc commands to the release notes (random-zebra)

Pull request description:

  This is the current state of my DMN branch.
  Deterministic masternodes fully working on regtest (except for proof of service, which will come later), with a good number of tests covering registration, update, revocation, revival, collaterals auto-locking, consistency after reorgs, compatibility with the old system, payments, and governance voting. ☕

  There are several differences with Dash code, mostly in the RPC, mempool, block assembler, compatibility code, wallet, deployment logic, and unit/functional testing (which led to the discovery of a few bugs, including a non-trivial one, reported upstream).
  But, overall, this remains respectful of the specification given here (at least for now... we'll have to modify the spec a bit when introducing shield rewards later):
  - [DIP3 — Deterministic Masternode Lists](https://github.com/dashpay/dips/blob/master/dip-0003.md)

  Huge props and thanks to Codablock and the Dash Core developers for the design of this awesome protocol.

  In order to follow the specification, implementing it in our PoS system, while still allowing the cold-stake script, a critical consensus change was needed: the masternode/budget payment is no longer included in the *coinstake* transaction. It is, instead, an output of the *coinbase* transaction, which, in turn, is no longer required to be empty.
  As a side effect, this enables the definition and use of a new P2CS script, with no "free" outputs.
  The block version is bumped to `10`, with the new rules.

  This PR serves as general container for tracking, and will now be divided in more manageable sub-PRs, for better review.

  ### Introduction

  Deterministic Masternode lists are lists of masternodes, built at every block, relying only on on-chain data (previous list, and transactions included in the current block).
  All nodes derive (and verify) their masternode lists independently, from the same on-chain transactions, thus they immediately reach consensus on the tier-two state (number of masternodes, properties and status of each one).
  As clearly explained in the "motivation" part of the DIP document, this is crucially different from the previous system:

  > The previous system was maintained with consensus mechanisms that predated Satoshi Nakamoto’s solution to the Byzantine Generals Problem. This meant that each node needed to maintain their own individual masternode list with P2P messages and not a blockchain based solution. Due to the nature of the P2P system, there was no guarantee that nodes would come to the same conclusion on what the masternode list ought to look like. Discrepancies might, for example, occur due to a different order of message reception or if messages had not been received at all. This posed some risks in regard to consensus and limited the possible uses of quorums by the system.
  >
  > As a concrete example, the previous system required implementing workarounds such as "masternode reward voting" which was performed multiple blocks in advance for each block to make sure that consensus would be found and agreed on. Enforcing this consensus however still posed a risk which could have resulted in network wide forking, so a spork to turn off masternode payment enforcement was added to prevent this issue from occurring. The spork was used sporadically after major network updates.

  This is a major overhaul, which brings also a good number of improvements in the user experience, while removing the shortcomings of the previous system.
  All reviewers are encouraged to take a deep dive in the DIP3 document, which describes perfectly the advantages of the new system.

  ### New Roles

  For each masternode, three different "roles" are defined. Each role is represented by a private/public keypair.

  1. **Owner**: Must be unique on the network. Can update the other two roles, and the masternode payout address.
  2. **Operator**: Must be unique on the network. The operator key is saved in the `pivx.conf` of the remote node, and it is used to sign masternode-related P2P messages (e.g. budget finalisations, or masternode winners in the compatibility code). It can also be used to update the masternode IP-address, or the operator payout address (if the masternode is configured to allow a percentage of the reward to be paid to operator).
  3. **Voting**: Doesn't have to be unique (multiple masternodes can share the same voting key). It is used to cast budget votes.

  The same keypair can be used for all three roles (at least for now, the operator key will be changed to a BLS key soon), but they must be different from the key of the collateral address.

  ### New Transaction Types

  Special transactions ([DIP2](https://github.com/dashpay/dips/blob/master/dip-0002.md)) were introduced in #1966, but no new transaction type has been defined in PIVX yet.
  Here we introduce four new types, each identifying a particular transaction payload, with its own validation rules:

  - `PROREG` (*provider-register*): this is the main special transaction. Used for the registration of a new masternode, setting all of its properties (such as the keys for each role). It creates the masternode collateral, as one of its outputs, or it references a 10000 PIV unspent output on chain (in which case, it must include a signature with its keys, as proof of ownership).
  - `PROUPSERV` (*provider-update-service*): sent by the mn **operator** to update the properties related to the service (IP address, operator payout address)
  - `PROUPREG` (*provider-update-registrar*): sent by the mn **owner** to update the operator key, the voting key, or the payout address.
  - `PROUPREV` (*provider-update-revoke*): sent by the mn **operator** to revoke the service, and put the mn in PoSe-banned state (e.g. in case of compromised keys). The masternode can be "revived" later, by sending a ProUpReg tx, which sets a new operator key, and then a ProUpServ tx (signed with the new key), which sets the new IP address for the masternode.

  ### Code Architecture

  Deterministic masternodes are represented as objects of the class `CDeterministicMN`.
  This class includes a member variable that stores a shared pointer to a constant `CDeterministicMNState` object, which encapsulates the dmn state (updated properties and status).

  A list of masternodes is represented by the class `CDeterministicMNList`, which uses immutable functional maps (https://github.com/arximboldi/immer) to hold the actual information about each entry.
  A new list is built at every block and mantained by `CDeterministicMNManager`.

  The use of immutable functional maps is, in my opinion, an elegant solution, devised by Codablock, to reduce the memory overhead required for the masternode list update at every block, by adopting a [copy-on-write](https://en.wikipedia.org/wiki/Copy-on-write) approach.
  Immutable data structures are provided by default on functional-programming oriented languages, such as Clojure or Scala, but for C++ we unfortunately need to rely on third party libraries.
  We could switch to an implementation based on `std::map`s, but that would severly impact the performance and require hundreds of MB in the ram, just for MN list housekeeping.

  The code still contains a lot of ugly parts, which we need to keep for compatibility with the old system. Most of them are properly commented. My first task, after the full deployment on mainnet, will be to do a general cleanup and refactoring, removing all the legacy system code.

  ### Deployment

  This an hard-forking update, slated for **PIVX v6.0**.
  It will be deployed with a combination of `nuparams` activation height, and a spork message.
  For this purpose, a new height-based (rather than time-based) spork, `SPORK_21_LEGACY_MNS_MAX_HEIGHT`, is introduced.
  It will be used only during the transition to the new system, and can be removed afterwards.

  As in the previous mandatory upgrades, there will be a protocol bump, shortly before the enforcement block.
  All nodes must be updated before the protobump.
  Masternodes, as usual, will need to send a new MNB start message, after upgrading, with the new protocol number.

  After the enforcement, new consensus rules apply (such as block v10), and new special transactions are accepted on the network (with the exception of `PROUPREG`).
  Deterministic masternodes can be registered, but have a few limitations, in order to be compatible with the legacy system (which is still active): for example, they cannot pay a portion of the reward to the operator, yet.
  The system still uses the old logic for masternode payments (mnw signed messages), with new compatibility code, in order to take into account also nodes from the deterministic list.

  After the activation of `SPORK_21_LEGACY_MNS_MAX_HEIGHT`, the legacy system is disabled, and the transition to deterministic masternode lists is complete.
  At this point, mno can also send `PROUPREG` transactions, and use all the features of DMNs. The new payment logic applies.

  The reasons for using a spork, instead of pre-set number of blocks after enforcement, are multiple:

  - It allows for easier testing on testnet, with the possibility of disabling and re-enabling legacy masternodes multiple times, in order to test different scenarios
  - It allows for safer deployment on mainnet: we might want to wait until the network has a good percentage of dmn, before disabling the legacy system. Or, conversely, there could be an unforseen bug in the compatibility code, which would require a transition faster than expected.

  ### TODO

  These are the next milestones:

  - Proof of service for masternodes (with [DIP6](https://github.com/dashpay/dips/blob/master/dip-0006.md) and [DIP7](https://github.com/dashpay/dips/blob/master/dip-0007.md))
  - kickass GUI
  - Budget system overhaul
  - Chain-locks ([DIP8](https://github.com/dashpay/dips/blob/master/dip-0008.md))
  - SHIELD masternode rewards

ACKs for top commit:
  furszy:
    utACK 9f43405
  Fuzzbawls:
    utACK 9f43405

Tree-SHA512: 0c23228522cf021147cb66affcceb8b453c952e9db433c4513ebfcdc3328243383742e8a18eea985036a107506ceb10b39e96d4e0fde87ea16686d2f7039e0bc
  • Loading branch information
random-zebra committed Jul 18, 2021
2 parents ce3ac37 + 9f43405 commit 152d244
Showing 1 changed file with 226 additions and 6 deletions.
232 changes: 226 additions & 6 deletions doc/release-notes.md
Expand Up @@ -45,15 +45,235 @@ Notable Changes
(Developers: add your notes here as part of your pull requests whenever possible)


Cold-Staking Re-Activation
--------------------------
PIVX Core v6.0.0 includes a fix for the vulnerability identified within the cold-staking protocol (see PR [#2258](https://github.com/PIVX-Project/PIVX/pull/2258)).
Therefore the feature will be re-enabled on the network, via `SPORK_19`, shortly after the upgrade enforcement.
Deterministic Masternode Lists
------------------------------

PIVX v6.0.0 introduces on-chain consensus for masternode lists, which allow for deterministic quorum derivation, implementing Dash's [DIP-0003](https://github.com/dashpay/dips/blob/master/dip-0003.md).

In the previous masternode system, each node needed to maintain their own individual masternode list with P2P messages, thus discrepancies might occur, for example, due to a different order of message reception.
Deterministic Masternode lists are lists of masternodes, built at every block, relying only on on-chain data (previous list, and transactions included in the current block).
All nodes derive (and verify) their masternode lists independently, from the same on-chain transactions, thus they immediately reach consensus on the tier-two state (number of masternodes, properties and status of each one).

Masternodes are "registered" by special transactions called ProTx, and removed only by spending the collateral.
A ProTx either creates a 10000-PIV collateral as tx output, or includes a reference to an unspent 10000-PIV utxo on chain (and a proof of ownership).
See PR [#2267](https://github.com/PIVX-Project/PIVX/pull/2267) for more information.

Upgrade instructions: !TODO

### New RPC commands

* `protx_list`
```
protx_list (detailed wallet_only valid_only height)
Lists all ProTxs.
Arguments:
1. detailed (bool, optional, default=true) Return detailed information about each protx.
If set to false, return only the list of txids.
2. wallet_only (bool, optional, default=false) If set to true, return only protx which involves
keys from this wallet (collateral, owner, operator, voting, or payout addresses).
3. valid_only (bool, optional, default=false) If set to true, return only ProTx which are active/valid
at the height specified.
4. height (numeric, optional) If height is not specified, it defaults to the current chain-tip.
Result:
[...] (list) List of protx txids or, if detailed=true, list of json objects.
```

* `protx_register`
```
protx_register "collateralHash" collateralIndex "ipAndPort" "ownerAddress" "operatorPubKey" "votingAddress" "payoutAddress" (operatorReward "operatorPayoutAddress")
Creates and sends a ProTx to the network. The collateral is specified through "collateralHash" and collateralIndex, and must be an unspent
transaction output spendable by this wallet. It must also not be used by any other masternode.
Arguments:
1. "collateralHash" (string, required) The collateral transaction hash.
2. collateralIndex (numeric, required) The collateral transaction output index.
3. "ipAndPort" (string, required) IP and port in the form "IP:PORT".
Must be unique on the network. Can be set to 0, which will require a ProUpServTx afterwards.
4. "ownerAddress" (string, required) The PIVX address to use for payee updates and proposal voting.
The private key belonging to this address must be known in your wallet, in order to send updates.
The address must not be already registered, and must differ from the collateralAddress
5. "operatorPubKey" (string, required) The operator BLS public key. The private BLS key does not have to be known.
It has to match the BLS private key which is later used when operating the masternode.
6. "votingAddress" (string, required) The voting key address. The private key does not have to be known by your wallet.
It has to match the private key which is later used when voting on proposals.
If set to an empty string, ownerAddress will be used.
7. "payoutAddress" (string, required) The PIVX address to use for masternode reward payments.
8. "operatorReward" (numeric, optional) The fraction in % to share with the operator. The value must be
between 0.00 and 100.00. If not set, it takes the default value of 0.0
9. "operatorPayoutAddress" (string, optional) The address used for operator reward payments.
Only allowed when the ProRegTx had a non-zero operatorReward value.
If set to an empty string, the operatorAddress is used.
Result:
"txid" (string) The transaction id.
```

* `protx_register_fund`
```
protx_register_fund "collateralAddress" "ipAndPort" "ownerAddress" "operatorPubKey" "votingAddress" "payoutAddress" (operatorReward "operatorPayoutAddress")
Creates, funds and sends a ProTx to the network. The resulting transaction will move 10000 PIV
to the address specified by collateralAddress and will then function as masternode collateral.
Arguments:
1. "collateralAddress" (string, required) The PIVX address to send the collateral to.
2. "ipAndPort" (string, required) IP and port in the form "IP:PORT".
Must be unique on the network. Can be set to 0, which will require a ProUpServTx afterwards.
3. "ownerAddress" (string, required) The PIVX address to use for payee updates and proposal voting.
The private key belonging to this address must be known in your wallet, in order to send updates.
The address must not be already registered, and must differ from the collateralAddress
4. "operatorPubKey" (string, required) The operator BLS public key. The private BLS key does not have to be known.
It has to match the BLS private key which is later used when operating the masternode.
5. "votingAddress" (string, required) The voting key address. The private key does not have to be known by your wallet.
It has to match the private key which is later used when voting on proposals.
If set to an empty string, ownerAddress will be used.
6. "payoutAddress" (string, required) The PIVX address to use for masternode reward payments.
7. "operatorReward" (numeric, optional) The fraction in % to share with the operator. The value must be
between 0.00 and 100.00. If not set, it takes the default value of 0.0
8. "operatorPayoutAddress" (string, optional) The address used for operator reward payments.
Only allowed when the ProRegTx had a non-zero operatorReward value.
If set to an empty string, the operatorAddress is used.
Result:
"txid" (string) The transaction id.
```

* `protx_register_prepare`
```
protx_register_prepare "collateralHash" collateralIndex "ipAndPort" "ownerAddress" "operatorPubKey" "votingAddress" "payoutAddress" (operatorReward "operatorPayoutAddress")
Creates an unsigned ProTx and returns it. The ProTx must be signed externally with the collateral
key and then passed to "protx_register_submit".
The collateral is specified through "collateralHash" and "collateralIndex" and must be an unspent transaction output.
Arguments:
1. "collateralHash" (string, required) The collateral transaction hash.
2. collateralIndex (numeric, required) The collateral transaction output index.
3. "ipAndPort" (string, required) IP and port in the form "IP:PORT".
Must be unique on the network. Can be set to 0, which will require a ProUpServTx afterwards.
4. "ownerAddress" (string, required) The PIVX address to use for payee updates and proposal voting.
The private key belonging to this address must be known in your wallet, in order to send updates.
The address must not be already registered, and must differ from the collateralAddress
5. "operatorPubKey" (string, required) The operator BLS public key. The private BLS key does not have to be known.
It has to match the BLS private key which is later used when operating the masternode.
6. "votingAddress" (string, required) The voting key address. The private key does not have to be known by your wallet.
It has to match the private key which is later used when voting on proposals.
If set to an empty string, ownerAddress will be used.
7. "payoutAddress" (string, required) The PIVX address to use for masternode reward payments.
8. "operatorReward" (numeric, optional) The fraction in % to share with the operator. The value must be
between 0.00 and 100.00. If not set, it takes the default value of 0.0
9. "operatorPayoutAddress" (string, optional) The address used for operator reward payments.
Only allowed when the ProRegTx had a non-zero operatorReward value.
If set to an empty string, the operatorAddress is used.
Result:
{ (json object)
"tx" : (string) The serialized ProTx in hex format.
"collateralAddress" : (string) The collateral address.
"signMessage" : (string) The string message that needs to be signed with the collateral key
}
```

* `protx_register_submit`
```
protx_register_submit "tx" "sig"
Submits the specified ProTx to the network. This command will also sign the inputs of the transaction
which were previously added by "protx_register_prepare" to cover transaction fees
Arguments:
1. "tx" (string, required) The serialized transaction previously returned by "protx_register_prepare"
2. "sig" (string, required) The signature signed with the collateral key. Must be in base64 format.
Result:
"txid" (string) The transaction id.
```

* `protx_revoke`
```
protx_revoke \"proTxHash\" (\"operatorKey\" reason)\n"
Creates and sends a ProUpRevTx to the network. This will revoke the operator key of the masternode and
put it into the PoSe-banned state. It will also set the service field of the masternode
to zero. Use this in case your operator key got compromised or you want to stop providing your service
to the masternode owner.
Arguments:
1. "proTxHash" (string, required) The hash of the initial ProRegTx.
2. "operatorKey" (string, optional) The operator BLS private key associated with the registered operator public key.
If not specified, or set to an empty string, then this command must be performed on the active
masternode with the corresponding operator key.
3 reason (numeric, optional) The reason for masternode service revocation. Default: 0.
0=not_specified, 1=service_termination, 2=compromised_keys, 3=keys_change.
Result:
"txid" (string) The transaction id.
```

* `protx_update_registrar`
```
protx update_registrar \"proTxHash\" \"operatorPubKey\" \"votingAddress\" \"payoutAddress\" (\"ownerKey\")
Creates and sends a ProUpRegTx to the network. This will update the operator key, voting key and payout
address of the masternode specified by \"proTxHash\".
The owner key of this masternode must be known to your wallet.
Creates and sends a ProUpServTx to the network. This will update the IP address
of a masternode, and/or the operator payout address.
If the IP is changed for a masternode that got PoSe-banned, the ProUpServTx will also revive this masternode.
Arguments:
1. "proTxHash" (string, required) The hash of the initial ProRegTx.
2. "operatorPubKey (string, required) The operator BLS public key. The private BLS key does not have to be known.
It has to match the BLS private key which is later used when operating the masternode.
If set to an empty string, the currently active operator BLS public key is reused.
3. "votingAddress" (string, required) The voting key address. The private key does not have to be known by your wallet.
It has to match the private key which is later used when voting on proposals.
If set to an empty string, the currently active voting key address is reused.
4. "payoutAddress" (string, required) The PIVX address to use for masternode reward payments.
If set to an empty string, the currently active payout address is reused.
5. "ownerKey" (string, optional) The owner key associated with the operator address of the masternode.
If not specified, or set to an empty string, then the mn key must be known by your wallet,
in order to sign the tx.
Result:
"txid" (string) The transaction id.
```

* `protx_update_service`
```
protx_update_service "proTxHash" "ipAndPort" ("operatorPayoutAddress" "operatorKey")
Creates and sends a ProUpServTx to the network. This will update the IP address
of a masternode, and/or the operator payout address.
If the IP is changed for a masternode that got PoSe-banned, the ProUpServTx will also revive this masternode.
Arguments:
1. "proTxHash" (string, required) The hash of the initial ProRegTx.
2. "ipAndPort" (string, required) IP and port in the form "IP:PORT".
If set to an empty string, the currently active ip is reused.
3. "operatorPayoutAddress" (string, optional) The address used for operator reward payments.
Only allowed when the ProRegTx had a non-zero operatorReward value.
If set to an empty string, the currently active one is reused.
4. "operatorKey" (string, optional) The operator BLS private key associated with the registered operator public key.
If not specified, or set to an empty string, then this command must be performed on the active
masternode with the corresponding operator key.
Result:
"txid" (string) The transaction id.
```

### GUI changes

!TODO

### Protocol changes

A new opcode (`0xd2`) is introduced (see PR [#2275](https://github.com/PIVX-Project/PIVX/pull/2275)). It enforces the same rules as the legacy cold-staking opcode, but without allowing a "free" script for the last output of the transaction.
This is in accord with the consensus change introduced with the "Deterministic Masternodes" update, as masternode/budget payments are now outputs of the *coinbase* transaction (rather than the *coinstake*), therefore a "free" output for the coinstake is no longer needed.
Starting with the enforcement block, masternode rewards and budget payments are paid as outputs of the coinbase transaction, instead of the coinstake transaction.
With this rule, a new opcode (`0xd2`) is introduced (see PR [#2275](https://github.com/PIVX-Project/PIVX/pull/2275)).
It enforces the same rules as the legacy cold-staking opcode, but without allowing a "free" script for the last output of the transaction.
The new opcode takes the name of `OP_CHECKCOLDSTAKEVERIFY`, and the legacy opcode (`0xd1`) is renamed to `OP_CHECKCOLDSTAKEVERIFY_LOF` (last-output-free).
Scripts with the old opcode are still accepted on the network (the restriction on the last-output is enforced after the script validation in this case), but the client creates new delegations with the new opcode, by default, after the upgrade enforcement.

Expand Down

0 comments on commit 152d244

Please sign in to comment.