Hello,
I’ve written up a proposal for implementing motions in Peercoin. I would like to submit this for discussion. If people are generally happy with the proposal then I plan to create a peer4commit project for the implementation of this proposal which would most likely be for Peerunity. Criticisms and suggestions are most welcome.
Matthew
[hr]
[hr]
[size=16pt]Overview[/size]
This proposal is for a protocol extension that will allow Peercoin minters to make a single yes or no vote for up-to 10 motions per block. The yes and no votes for these motions will be kept for 12,970 blocks, and vote results can be calculated over any time period up to ~90 days. A motion might for instance have 450 yes votes, 215 no votes, and 335 abstentions over a 1,000 block period. This would give 45% in favour and 21.5% against over a period of roughly 7 days. Each motion will also have a title and details that are shared by the p2p network. This would allow clients to retrieve and display the contents for each motion.
The votes of the 10 most recent Proof-of-Stake blocks will not be counted to account for potential fork scenarios and missing motion information. Each motion will have a title of up-to 50 characters, and details of up-to 5,000 characters which shall be shared by the P2P network. The motion details will use the mime type “text/plain” but different types may be supported in the future. In particular a lightweight markup language such as markdown may be used to provide better document structure to motion details with plaintext readability, simplicity and style consistency.
Proof-of-Stake blocks will include a 20 byte hash in the coinbase. This will serve as a method for minters to set their voting intention, and shall be a hash of a new “vote” message which lists votes for individual motions. Votes for up-to 10 motions inside a block will be supported. The network protocol will be extended with new messages which share votes and motion details. Compliance with the extended protocol will be signified with a new services flag.
Yes and no counts of up to 12,960 blocks will be producible for each motion. These numbers will have no effect on the protocol. They are purely informative and there is no number by which a motion is “passed”. These numbers may be used to approximate user consensus for decision making.
The terms “MUST”, “MUST NOT”, “SHOULD”, “SHOULD NOT” and “MAY” are to be interpreted according to RFC2119: http://www.ietf.org/rfc/rfc2119.txt
Occurrences of MUST should absolutely be implemented to ensure interoperability with other implementations.
Occurrences of SHOULD are not necessary for interoperability but are necessary to conform to the intended functionality of this protocol.
[hr]
[size=16pt]Rationale[/size]
There are four benefits to this proposal:
[ol][li]By introducing motions into Peercoin, it would provide a mechanism to measure user-support for controversial changes or ideas. For instance, a motion could be raised to disable Proof-of-Work in Peercoin. This would be a controversial change that may disappoint certain users, but by using a motion it would be possible to ascertain an estimate for user support.[/li]
[li]Motions would also provide user accountability for any changes. Users would have to review every motion before deciding to vote yes, voting no or to abstain. Votes would act as a useful source of feedback and may highlight ideas that would need changing to obtain enough support.[/li]
[li]By requiring a block for a vote, it would encourage user’s to mint, thus providing a potential side benefit for the health of the network.[/li]
[li]By sharing motion details between peers, reducing blockchain bloat, and providing a distinction between “no” votes and abstentions, this proposal is arguably more powerful than the motions in NuBits. This would demonstrate an additional level of innovation into Peercoin and may spark new interest.[/li][/ol]
Bitcoin has had controversial changes and proposals, especially with P2SH and increasing the blocksize limit. The bitcoin developers measure miner support to decide protocol changes. By only taking into account miner votes, the day to day users of bitcoin are alienated from the decisions of big protocol changes. It has been demonstrated with the likes of NuBits and BitShares that it is possible to utilise a user’s stake to vote instead.
NuBits uses a Proof-of-Stake system derived from Peercoin and currently measures motion votes through the number of supporting blocks and number of Share Days Destroyed (SDD) used in minting blocks. A motion is said to be passed if both the number of supporting blocks and the number of SDD surpasses 50%. It is planned for SDD to be removed from NuBits voting however (link). This is because it is possible for a user to save up share days to give unfair weight to future motions. If coin-days were similarly used to measure support for Peercoin motions a similar problem may arise. As minting probability is proportional to coin ownership, one-vote-per-block would be sufficient, as it has been for NuBits. Therefore only block votes are used in this proposal.
Motions for Peercoin was initially suggested by [member=17446]Jordan Lee[/member] in this topic (link). It was suggested that both the motion and transaction fee voting mechanisms in Nu be ported into Peercoin. This proposal is only concerned with motions. There are several reasons why the Nu motion voting code should not be used:
[ol][li]Vote information is stored in the NuBits blockchain. To prevent blockchain bloat this proposal suggests that the vote and motion information is stored outside of the blockchain. Only a 20 byte hash will be stored in each coinbase scriptSig. Only the most recent voting information needs to be kept, making this proposal space efficient.[/li]
[li]NuBits does not distinguish between “no” votes and abstentions. For decision making it is helpful to know the extent to which users are explicitly against a motion. If a motion has 70% support and only 2% against, it may be considered low in controversy, but if a motion had 70% support but 20% against, that would imply a large amount of disapproval. The only way to measure this is to have an explicit “no” vote.[/li]
[li]NuBits provides only motion hashes over the p2p network. The details corresponding to these hashes are stored on the NuBits forums. Whilst there is no problem with this, providing the motion details over the p2p network would enable powerful uses of the protocol. Motion details can be verified and displayed within the client to a user, so that they can read and then decide to vote for a motion within the same application.[/li][/ol]
As a result, motions for Peercoin should be implemented from scratch. As this votes are designed to be informational, votes may be counted over any period of up-to 90-days.
[hr]
[size=16pt]Issues[/size]
When outputs are spent, the coins lose their age and prevent the ability to mint blocks and therefore vote. Potential solutions for this include:
[ul][li]Maintaining coin age for outputs that send to an input address.[/li]
[li]Reducing the minimum coin age, so that minting becomes possible earlier.[/li]
[li]Reducing the maximum coin age, so that the highest probability for minting comes earlier.[/li][/ul]
[hr]
[size=16pt]New and Changed Protocol Messages[/size]
The messages are described using the data types used by the C++ client. This section contains some protocol rules for messages.
version: Add a new services flag with 1 << 3, named NODE_MOTIONS. This flag MUST be set in “version” messages sent to peers that an implementation intends to communicate with according to this protocol. An implementation SHOULD prioritise NODE_MOTIONS peers for at least two connections.
block: When providing a vote inside a block, the block MUST be a Proof-of-Stake block, the version MUST be set to 2 or higher, and the coinbase transaction MUST have a uint160 added to the scriptSig after the block height. This MUST be the RIPEMD-160 hash of the SHA-256 hash of the “vote” message associated with this block, ie. RIPEMD160(SHA256(votemessage)). No additional block validation rules are defined in this protocol, but note that version 2 blocks will require block heights in coinbase transactions, and a vote is invalid if these rules are not followed.
votes:
[ul][li]vVote (vector): A vector of vote messages, for each requested block. This MUST NOT exceed 12,970 votes and MUST contain at least 1.[/li][/ul]
vote:
[ul][li]vMotionVotes (vector): A vector of “motionvote” messages. All motions MUST have unique hashContents. This MUST NOT exceed 10 “motionvotes” messages, and MUST contain at least 1.[/li][/ul]
motionvote:
[ul][li]hashMotion (uint160): Hash of the motion message. When creating a new “motionvote” message an implementation MUST be able to produce the “motion” message corresponding to this hash.[/li]
[li]fFavour (bool): true if voting yes, false if voting no.[/li][/ul]
getvotes:
[ul][li]setVoteHashes (set): A set of the hashes of required vote messages. This MUST NOT exceed 12,970 hashes and MUST contain at least 1.[/li][/ul]
motion:
[ul][li]strTitle (string): A short title for the motion. This MUST NOT exceed 50 characters and MUST be 5 characters or more.[/li]
[li]nContentType (unsigned short): 0 explicitly for text/plain. For all other numbers an implementation SHOULD assume text/plain. Other types may be added in the future.[/li]
[li]strContent (string): The motion details. This MUST NOT exceed 5,000 characters and MAY be empty[/li][/ul]
There is a new inventory type named MSG_MOTION with the value 4. This MUST be used to acquire motion details only. An implementation MUST respond with known motions in response to “getdata” for every MSG_MOTION inventory item.
[hr]
[size=16pt]Protocol Logic[/size]
When Counting Votes
An implementation SHOULD be able to count the votes over a window of 1-12,960 blocks starting from the block that is 10 blocks back from the blockchain head block. The most recent 10 blocks SHOULD not be counted.
Votes for each motion can be counted by using the “vote” message corresponding to the vote hash in the coinbase scriptSig of a block. The vote hash is the second push operation in the scriptSig. Votes SHOULD not be counted for vote hashes without a corresponding “vote” message, or for blocks without a vote hash. Votes SHOULD be counted over a number of Proof-of-Stake blocks, including Proof-of-Stake blocks without a vote. Proof-of-Work blocks SHOULD NOT be counted.
After Connecting a Proof-of-Stake Block
The inventory for blocks containing a vote MUST be broadcast to all NODE_MOTIONS peers.
After Disconnecting a Proof-of-Stake Block
An implementation MUST keep at least 10 of the most recent vote messages that are no longer found in the last 1,010 blocks due to a block disconnections. This is for the case that blocks are reconnected again.
“vote” messages that are not contained in the last 12,970 blocks and are not retained due to block disconnections, MAY be discarded. “motion” messages that no longer exist inside any retained “vote” messages MAY be discarded.
After Connecting the Best Block With Full Synchronisation
An implementation MUST send a “getvotes” message containing the vote hashes of ALL missing “vote” messages. If the peer that provided the best block has NODE_MOTIONS set, the message MUST be sent to that peer. Otherwise the message MUST be sent to another peer with NODE_MOTIONS, if such a peer exists.
“vote” messages MUST NOT be requested for blocks that do not conform to the protocol rules. For example a uint160 after a block height in a coinbase scriptSig does not constitute as a vote hash if the block has the version 1 or if the block is a Proof-of-Work block.
When Receiving a “votes” Message
An implementation MAY ignore the message if it does not conform to the protocol rules, and MAY penalise the peer for this. Otherwise an implementation MUST read each vote in the message. An implementation MUST retain “vote” messages which correspond to vote hashes in any of the last 12,970 Proof-of-Stake blocks.
If there are any new motions found in the necessarily retained “vote” messages, then an implementation MUST ask for these motions by sending as many “getdata” messages as required to the peer that provided the “votes” message.
When Receiving a “getvotes” Message
An implementation MAY ignore the message if it does not conform to the protocol rules, and MAY penalise the peer for this. Otherwise an implementation MUST respond with a “votes” message for every “vote” message that exists and corresponds to a hash inside the “getvotes” message. If there are no “vote” messages to provide, then a “votes” message MUST not be sent.
When Receiving a “motion” Message
An implementation MAY ignore the message if it does not conform to the protocol rules, and MAY penalise the peer for this. Otherwise an implementation MUST retain the message if it corresponds to a motion hash contained in a retained “vote” message.
When Minting a Block
The version for new blocks SHOULD be 2, and MUST be 2 when voting. A correctly formatted “vote” message MUST be created when voting and MUST follow the protocol rules. Every included motion hash MUST have a corresponding “motion” message and these “motion” messages MUST be kept for as long as they exist in one or more retained “vote” messages. The “vote” message for a block vote MUST be retained as if the message was given by another peer. When voting, the hash of the vote message MUST be placed after the block height in the coinbase scriptSig, and the push operation SHOULD be as compact as possible. This hash MUST be calculated by first calculating the SHA-256 hash of the entire “vote” message and then calculating the RIPEMD-160 hash of the first hash.
Votes will only be counted for Proof-of-Stake blocks.
Every 30 Seconds After Requesting Votes/Motions
If there is one or more missing “vote” messages, an implementation SHOULD send a “getvotes” message with the missing vote hashes to a peer that has NODE_MOTIONS. The peer SHOULD be different to the previous used peer if possible, and the best block height at the time that the message was last sent to this peer SHOULD be lower than the most recent block which contains a missing “vote” message.
If there is one or more missing “motion” messages, an implementation SHOULD send a “getdata” message with the missing motion hashes to a peer that has NODE_MOTIONS. The peer SHOULD be different to the previous used peer if possible, and each peer SHOULD only be asked once until new motions are discovered.
[hr]
[size=16pt]RPC Commands[/size]
setvote “motionHash” favour (“title” “contents” “contentType”)
Used to vote yes or no to a motion whilst minting. If this is a new motion, the motion details can be provided. If no details are provided then the motion must have been included in the last 12,970 blocks.
Arguments:
- motionHash (string, required): Hex of the motion hash.
- favour (boolean, required): If true, vote yes for this motion. If false, vote no for this motion.
- title (string, optional): Title of the motion, with 5-50 characters.
- contents (string, optional): Contents of the motion, up to 5,000 characters. Can be empty.
- contentType (string, optional, default=“text/plain”): The mime type. Only text/plain is currently supported. Any other input will be assumed text/plain.
Errors:
-31: Too many votes.
-5: The motion does not exist.
-25: Motion details do not match hash.
unsetvote “motionHash”
Removes a vote for a motion with a particular hash.
Arguments:
- motionHash (string, required): Hex of the motion hash.
Errors:
-5: motionHash was not set.
clearvotes
Removes all motions from voting.
getselfvote
Gives a list of motions that are being voted for.
Result:
[
{
“motionHash”: (string) Hex of the motion hash.
“favour”: (bool) true if voting yes, false is voting no.
}
,…
]
getvotes (numBlocks)
Gives a list of all motions and their vote counts.
Arguments:
- numBlocks (numerical, optional, default=4320): The number of blocks to count. The default gives roughly 30 days of data.
Result:
[
{
“motionHash”: (string) Hex of the motion hash
“yes”: (numeric) The number of yes votes out of numBlocks.
“no”: (numeric) The number of no votes out of numBlocks.
}
,…
]
getmotiondetails “motionHash”
Gives the details for a motion.
Arguments:
- motionHash (string, required): Hex of the motion hash.
Result:
{
“title”: (string) The title of the motion.
“content”: (string) The content of the motion.
“contentType”: (string) “text/plain”
}
Errors:
-5: The motion does not exist.