diff --git a/roadmap/ipfs/README.md b/roadmap/ipfs/README.md new file mode 100644 index 0000000000..f8dc842612 --- /dev/null +++ b/roadmap/ipfs/README.md @@ -0,0 +1,44 @@ +# IPFS - Integration + +IPFS (Interplanetary File System) is used by Ravencoin for issuance meta-data, messaging, and transaction meta-data. + +IPFS hashes are stored on-chain. +* Issuance transaction for issuance meta-data. +* Send channel token or ownership token to the same address to "broadcast" a message. +* Any other transaction can optionally include transaction meta-data. + +Ravencoin must interact with IPFS in order to show messages because the message content is stored on IPFS. IPFS access should be on by default, but should be able to be turned off with a flag. -noipfs Seed nodes, back-end systems, etc. will not need IPFS. + +### Phased approach to IPFS integration + +The two phase approach allows Ravencoin to use IPFS natively, but also use existing IPFS proxies. + +Add a class of IPFS functions for init, get, and add, where these functions will either get from ifps daemon, ipfs proxies, or NOOP if -noipfs is set. + + +#### Phase 1 +* Use https://cloudflare-ipfs.com/ipfs/ for download + * Allow this to be configured in the Network tab under Preferences. +* Use https://globalupload.io/ for IPFS upload (meta-data, message send, transaction meta-data) + + +#### Phase 2 +* A button that downloads and installs IPFS for the given platform (Mac, Windows, Linux) +* Ravencoin client detects when ipfs daemon is installed. +* Use ipfs get for download +* Use ipfs add for upload +* Option to act as a IPFS pinning node for Ravencoin assets. -ipfsnode + +### Mobile Wallet + +The mobile wallet will use IPFS proxies. + +### Web Wallet + +The web wallet will use IPFS proxies. + +### IPFS Node +If -ipfsnode is set, then all ipfs hashes for Ravencoin that are less than 16000 characters and are valid JSON are pinned. + +If -ipfsnofilter is also set, then all ipfs hashes for Ravencoin are pinned. This may take a lot of disk space. This removes the 16000 character and json data type restriction. + diff --git a/roadmap/messaging-antispam/README.md b/roadmap/messaging-antispam/README.md new file mode 100644 index 0000000000..d33b278f6a --- /dev/null +++ b/roadmap/messaging-antispam/README.md @@ -0,0 +1,34 @@ +# Messaging - Anti-spam + +Ravencoin will display messages for all tokens that you own. But wait, what if I get an unsolicited HERPES token because they found my crypto address on-chain? Maybe I don’t want messages from the HERPES token issuer. + +The holder of the owner token, e.g. ATOKEN! can send messages to holders their token ATOKEN. The owner token has an exclamation point. The owner token holder can also create channel tokens like ATOKEN\~News, or ATOKEN\~VoteInfo, or ATOKEN~Emergency These are message channel tokens and the holder of these tokens can broadcast a message by attaching a special file (IPFS) to the transaction while sending the message channel token to themselves. + +By default, if you own a token, you will get all the messages that are sent by channel owners, and the token owner. Message channel tokens can be easily recognized because they contain a tilde ( ~ ) character. No other tokens can contain a tilde character. + +The Problem: +There is a problem with building a messaging system. Perhaps one that the early designers of the e-mail system didn’t anticipate, and that's spam. Spam is just an unwanted message. Ravencoin users don’t want to get spam messages. + +Messages for tokens that you bought, or acquired through your actions are wanted. If that isn’t true, then just turn off the channel for that token, and Ravencoin will stop showing messages for that token. + +The solution: +Messages for tokens that you didn’t acquire, but were just sent to you unsolicited are probably spam. + +The best practice is to provide a brand new, never used address when you receive new tokens. If you strictly adhere to this practice, then you’ll get messages for tokens you bought or requested, and never get messages for tokens that were sent to you unsolicited. + +The only reason spam is possible is that your crypto addresses are public, even if nobody knows the address belongs to you. + +Here are the simple steps that Ravencoin takes to prevent spam. +* A channel that you choose to mute will be silenced. Maybe you don’t want messages from the token owner or their designated channel token holders. +* A token that is sent to an address that has already been used will be considered as potentially spam and all of its channel silenced. It may not be, but it will be treated as such. Why? Because your address is public and someone can send a SPAM token or a SPAM/SUBSPAM token to it. Tokens can be created in bulk for very little cost, and then sent to everyone at very little cost. The spam prevention system is to preemptively prevent this type of spam behavior. + +The technical: +* Every token sent to an address that hasn’t ever been used will allow messages to be sent by the token owner token, and its channel tokens. +* If a token is sent to an address that has been used before, then the message channel will be muted (turned off). You can always turn it on if you wish to receive messages for the new token you’ve been sent. +* If a message channel is added to a token that was sent to an already used address, then it will also be muted because the token channel was already muted. In other words, it will inherit the state of the primary channel. + +Testing: +* Send a token to a newly never seen address. Send a message using the owner token. The message should be seen. +* Send a token to a newly never seen address. Create a channel token. Send a message using the new channel token. The message should be seen. +* Send a token to an address that already has a token in it. Send a message using the owner token. The message should not be seen. +* Send a token to an address that already has a token in it. Create a channel token. Send a message using the new channel token. The message should not be seen. diff --git a/roadmap/messaging/README.md b/roadmap/messaging/README.md new file mode 100644 index 0000000000..1dbc5a9467 --- /dev/null +++ b/roadmap/messaging/README.md @@ -0,0 +1,75 @@ +# Messaging + +Messaging is described here in the [KAAAWWWW! protocol](https://medium.com/@tronblack/ravencoin-kaaawww-2f72077aece). + +### Messaging Components +#### Protocol +This requires only the addition of an IPFS for each transaction. + +A message is "broadcast" if an owner token or channel token is sent in a transaction to the same address with the addition of an IPFS hash and an optional expiration date. The message isn't really broadcast in the sense of being transmitted to nodes, but rather each node will independently detect the special transaction type and display the message. Message display is subject to some heuristic anti-spam rules. + +#### Channels +Channels are special Ravencoin unique asset tokens that are created by asset owners. The channel tokens are similar to unique assets in that there is only one with a given name. They can be uniquely identified by having a ~ (tilde) in the name. They are limited to twelve characters, and can use uppercase, lowercase, and digits. Example: TRONCO~Alert + +Sending these special channel tokens from one address to the same address will "broadcast" a message on the channel, which is named the same as the token. + +Users can mute channels. Some channels will be automatically muted by the anti-spam system. + +#### GUI - Desktop + +Create a broadcast message with optional expiration date. +* Phase 1 - Enter IPFS hash of message +* Phase 2 - Enter message, submit via POST to publish IPFS hash, submit message transaction by sending owner or channel token to same address. + +#### GUI - Mobile + +Create a broadcast message with optional expiration date. +* Phase 1 - Enter IPFS hash of message +* Phase 2 - Enter message, submit via POST to publish IPFS hash, submit message transaction by sending owner or channel token to same address. + +#### RPC + +These rpc calls are added in support of messaging: + +issue_channel TOKEN CHANNEL_NAME +send_message TOKEN IPFS_HASH + +You must hold the owner token for TOKEN. + + +#### Message Queue + +The ZMQ message adds an additional queue for messaging 'pubmessage' + +Only broadcast messages should be published via zmq. + +Transaction messages can be obtained by watching zmq message queue 'pubrawtx' and decoding to get the ipfshash. + +The ZMQ will return a json reponse. +``` +{ + 'asset': 'TRONCO', + 'block_height':483294, + 'txid': '8851dcf27271721f7eb5712eb49e092acfc4866e76a8e85fe6a33bb237501f9a', + 'vout': 2 + 'ipfs_hash':'QmTqu3Lk3gmTsQVtjU7rYYM37EAW4xNmbuEAp2Mjr4AV7E', + 'expires': 1545343682 +} +``` + +#### DevKit + +The DevKit adds messaging support by including a ipfs_hash field for every transaction that includes an IPFS hash. The ipfs_hash can be used with + +https://cloudflare-ipfs.com/ipfs/QmTqu3Lk3gmTsQVtjU7rYYM37EAW4xNmbuEAp2Mjr4AV7E + +Replace the hash with your hash to look up the message on cloudflare. +If the IPFS daemon is installed, you can also use ipfs get [ipfs_hash] + + + + + + + + diff --git a/roadmap/rewards/README.md b/roadmap/rewards/README.md new file mode 100644 index 0000000000..f733e1c3d5 --- /dev/null +++ b/roadmap/rewards/README.md @@ -0,0 +1,193 @@ +# Rewards + +Rewards, also sometimes called dividends, provides a way to send tokenized assets or RVN to token holders. This can be used to reward shareholders with profits (denominated in RVN), or to reward membership holders, or to reward those that contributed the most to a shared project and earned special tokens. + +Rewards do not require a consensus protocol change, and the rpc calls exist to be able do rewards already. + +These capabilities just make it native and easy-to-use from the client. + +Example that rewards TRONCO holders with RVN: +```reward 10000 RVN TRONCO``` + +## Reward calculation + +First, the QTY of TARGET_TOKEN is calculated. This is the total issuance, minus the qty held by the exception addresses. + +Next, the reward calculation takes the qty to send. This must be specified, and will usually be RVN. + +PER_TOKEN_AMOUNT_IN_SATOSHIS = [QTY TO SEND_IN_SATOSHIS] / [QTY OF TARGET_TOKEN] + +For RVN this must send an equal number of satoshis to every TARGET_TOKEN. Remainder satoshis should be sent to the miners. + +For a token, this must send an equal number of the token to every TARGET_TOKEN. The calculation will need to factor in the units. For example, if you attempted to send 7 non-divisible (units=0) of SEND_TOKEN to every holder of TARGET_TOKEN, but there were 8 or more TARGET_TOKEN holders, then the 'reward' call would fail because it is impossible to reward the TARGET_TOKEN holders equally. + +Example: 10 RVN to 3 TRONCO holders. 10 * 100,000,000 sats = 1,000,000,000 RVN sats. +PER_TOKEN_AMOUNT_IN_SATOSHIS = 1,000,000,000 / 3 = 333333333.33333333 (repeating) per TRONCO holder. The remainder of .3333 (repeating) satoshis per holder will not be sent as an output, and therefore will be given to the miners. This is 1 sat once multiplied by the 3 TRONCO holders. Each TRONCO holder will receive exactly 333333333 RVN sats. + +One special case - Paying TRONCO to TRONCO. This special case would require an exception address, and the source of the TRONCO would need to come from one or more of the exception addresses. +reward 400000 TRONCO TRONCO ['exception address'] + +### Rewards Components +#### Protocol + +No protocol change needed. + +#### GUI - Desktop + +Select a token or RVN to send. +Set the QTY to send. +Select the target token +* If you are sending another token, you must have the owner token for the TARGET_TOKEN. +* If you are sending RVN, you do not need the owner token for the TARGET_TOKEN + +GUI will show the exact amount being sent to each TARGET_TOKEN. It will also calculate and show the remaining which must be returned as change. If RVN is being sent, it will show the remainder that is being sent to the miners. + +#### GUI - Mobile + +Mobile will not initially have the rewards feature. + +#### RPC + +These rpc calls are added in support of rewards: + +reward QTY [RVN|TOKEN] TARGET_TOKEN [exception address list] + +#### Examples + +Example 1 (simple): +``` +4 holders of TRONCO (unit 0) (100 Issued) +RBQ5A9wYKcebZtTSrJ5E4bKgPRbNmr8M2H 10 TRONCO +RPsCVwsq8Uf2dcUSXcYPzVnsAMZtAHw6sV 20 TRONCO +RBp5woWDU8TRMz1TPeemyLxxLL3xsCnQgh 30 TRONCO +RFMD7ZJzexAmiLA9BHxwFCPVeiuAgdVjcP 40 TRONCO + +reward 100 RVN TRONCO + +Takes 100 RVN (10,000,000,000 sats) +RBQ5A9wYKcebZtTSrJ5E4bKgPRbNmr8M2H gets 10 RVN (10,000,000,000 RVN sats) +RPsCVwsq8Uf2dcUSXcYPzVnsAMZtAHw6sV gets 20 RVN (20,000,000,000 RVN sats) +RBp5woWDU8TRMz1TPeemyLxxLL3xsCnQgh gets 30 RVN (30,000,000,000 RVN sats) +RFMD7ZJzexAmiLA9BHxwFCPVeiuAgdVjcP gets 40 RVN (40,000,000,000 RVN sats) +``` + +Example 2 (simple with 2 exception addresses) +``` +5 holders of TRONCO (unit 0) (10000 Issued) +RBQ5A9wYKcebZtTSrJ5E4bKgPRbNmr8M2H 9900 TRONCO (exception) +RCqsnXo2Uc1tfNxwnFzkTYXfjKP21VX5ZD 50 TRONCO (exception) +RPsCVwsq8Uf2dcUSXcYPzVnsAMZtAHw6sV 5 TRONCO +RBp5woWDU8TRMz1TPeemyLxxLL3xsCnQgh 15 TRONCO +RFMD7ZJzexAmiLA9BHxwFCPVeiuAgdVjcP 30 TRONCO + +reward 1000 RVN TRONCO ['RBQ5A9wYKcebZtTSrJ5E4bKgPRbNmr8M2H','RCqsnXo2Uc1tfNxwnFzkTYXfjKP21VX5ZD'] + +Takes 1000 RVN (100,000,000,000 sats) +RPsCVwsq8Uf2dcUSXcYPzVnsAMZtAHw6sV gets 100 RVN (10,000,000,000 RVN sats) +RBp5woWDU8TRMz1TPeemyLxxLL3xsCnQgh gets 300 RVN (30,000,000,000 RVN sats) +RFMD7ZJzexAmiLA9BHxwFCPVeiuAgdVjcP gets 600 RVN (60,000,000,000 RVN sats) +``` + +Example 3 (Payment of BLACKCO to TRONCO holders with 2 exception addresses) +TRONCO and BLACKCO is set to units 0. +Note: Can only make this call if you hold TRONCO! -- the ownership token. +``` +5 holders of TRONCO (unit 0) (10000 Issued) +RBQ5A9wYKcebZtTSrJ5E4bKgPRbNmr8M2H 9900 TRONCO (exception) +RCqsnXo2Uc1tfNxwnFzkTYXfjKP21VX5ZD 50 TRONCO (exception) +RPsCVwsq8Uf2dcUSXcYPzVnsAMZtAHw6sV 5 TRONCO +RBp5woWDU8TRMz1TPeemyLxxLL3xsCnQgh 15 TRONCO +RFMD7ZJzexAmiLA9BHxwFCPVeiuAgdVjcP 30 TRONCO + +reward 1000 BLACKCO TRONCO ['RBQ5A9wYKcebZtTSrJ5E4bKgPRbNmr8M2H','RCqsnXo2Uc1tfNxwnFzkTYXfjKP21VX5ZD'] + +Takes 1000 BLACKCO and distributes them according to TRONCO holdings (minus holdings by exception addresses) +RPsCVwsq8Uf2dcUSXcYPzVnsAMZtAHw6sV gets 100 BLACKCO +RBp5woWDU8TRMz1TPeemyLxxLL3xsCnQgh gets 300 BLACKCO +RFMD7ZJzexAmiLA9BHxwFCPVeiuAgdVjcP gets 600 BLACKCO +``` + +Example 4 (Payment of DOLLARTOKEN to TRONCO holders with 1 exception address) +DOLLARTOKEN - a fictional stablecoin is set to units 2. +Note: Can only make this call if you hold TRONCO! -- the ownership token. +``` +5 holders of TRONCO (unit 0) (10000 Issued) +RBQ5A9wYKcebZtTSrJ5E4bKgPRbNmr8M2H 9900 TRONCO (exception) +RCqsnXo2Uc1tfNxwnFzkTYXfjKP21VX5ZD 50 TRONCO +RPsCVwsq8Uf2dcUSXcYPzVnsAMZtAHw6sV 5 TRONCO +RBp5woWDU8TRMz1TPeemyLxxLL3xsCnQgh 15 TRONCO +RFMD7ZJzexAmiLA9BHxwFCPVeiuAgdVjcP 30 TRONCO + +reward 1000.00 DOLLARTOKEN TRONCO ['RBQ5A9wYKcebZtTSrJ5E4bKgPRbNmr8M2H'] + +Takes 1000.00 DOLLARTOKEN and distributes them according to TRONCO holdings (minus holdings by exception addresses) +RCqsnXo2Uc1tfNxwnFzkTYXfjKP21VX5ZD gets 500.00 DOLLARTOKEN +RPsCVwsq8Uf2dcUSXcYPzVnsAMZtAHw6sV gets 50.00 DOLLARTOKEN +RBp5woWDU8TRMz1TPeemyLxxLL3xsCnQgh gets 150.00 DOLLARTOKEN +RFMD7ZJzexAmiLA9BHxwFCPVeiuAgdVjcP gets 300.00 DOLLARTOKEN +``` + +Example 5 (Payment of INDIVISIBLE to TRONCO holders) +INDIVISIBLE - a token with units set to 0 (therefore indivisible). +Note: Can only make this call if you hold TRONCO! -- the ownership token. +Note: Failure occurs because of indivisibility of INDIVISIBLE token. Unable to reward equally. +``` +5 holders of TRONCO (unit 0) (10000 Issued) +RBQ5A9wYKcebZtTSrJ5E4bKgPRbNmr8M2H 9900 TRONCO +RCqsnXo2Uc1tfNxwnFzkTYXfjKP21VX5ZD 50 TRONCO +RPsCVwsq8Uf2dcUSXcYPzVnsAMZtAHw6sV 5 TRONCO +RBp5woWDU8TRMz1TPeemyLxxLL3xsCnQgh 15 TRONCO +RFMD7ZJzexAmiLA9BHxwFCPVeiuAgdVjcP 30 TRONCO + +reward 9999 INDIVISIBLE TRONCO + +Takes 9999 INDIVISIBLE and attempts to distribute equally according to TRONCO holdings + +Results in FAILURE - Error "Unable to reward evenly" +``` + +Example 6 (Payment of INDIVISIBLE to TRONCO holders) +INDIVISIBLE - a token with units set to 0 (therefore indivisible). +Note: Can only make this call if you hold TRONCO! -- the ownership token. +Note: Remainder is sent back to sending address. +``` +5 holders of TRONCO (unit 0) (10000 Issued) +RBQ5A9wYKcebZtTSrJ5E4bKgPRbNmr8M2H 9900 TRONCO +RCqsnXo2Uc1tfNxwnFzkTYXfjKP21VX5ZD 50 TRONCO +RPsCVwsq8Uf2dcUSXcYPzVnsAMZtAHw6sV 5 TRONCO +RBp5woWDU8TRMz1TPeemyLxxLL3xsCnQgh 15 TRONCO +RFMD7ZJzexAmiLA9BHxwFCPVeiuAgdVjcP 30 TRONCO + +reward 10001 INDIVISIBLE TRONCO + +Takes 10001 INDIVISIBLE and attempts to distribute equally according to TRONCO holdings +RBQ5A9wYKcebZtTSrJ5E4bKgPRbNmr8M2H gets 9900 INDIVISIBLE +RCqsnXo2Uc1tfNxwnFzkTYXfjKP21VX5ZD gets 50 INDIVISIBLE +RPsCVwsq8Uf2dcUSXcYPzVnsAMZtAHw6sV gets 5 INDIVISIBLE +RBp5woWDU8TRMz1TPeemyLxxLL3xsCnQgh gets 15 INDIVISIBLE +RFMD7ZJzexAmiLA9BHxwFCPVeiuAgdVjcP gets 30 INDIVISIBLE +Remaining 1 INDIVISIBLE sent back to the first sending address. +``` + +Example 7 (Payment of VERYDIVISIBLE to TRONCO holders) +VERYDIVISIBLE - a token with units set to 8 (therefore divisible to 8 decimal places). +Note: Can only make this call if you hold TRONCO! -- the ownership token. +Note: Remainder is sent back to sending address. +``` +2 holders of TRONCO (unit 0) (3 Issued) +RBQ5A9wYKcebZtTSrJ5E4bKgPRbNmr8M2H 1 TRONCO +RCqsnXo2Uc1tfNxwnFzkTYXfjKP21VX5ZD 2 TRONCO + +reward 1 VERYDIVISIBLE TRONCO + +Takes 1 VERYDIVISIBLE and attempts to distribute equally according to TRONCO holdings +RBQ5A9wYKcebZtTSrJ5E4bKgPRbNmr8M2H gets 0.33333333 VERYDIVISIBLE +RCqsnXo2Uc1tfNxwnFzkTYXfjKP21VX5ZD gets 0.66666666 VERYDIVISIBLE +Remaining 0.00000001 VERYDIVISIBLE sent back to the first sending address. +``` + + + + + + diff --git a/roadmap/voting/README.md b/roadmap/voting/README.md new file mode 100644 index 0000000000..123821cbb3 --- /dev/null +++ b/roadmap/voting/README.md @@ -0,0 +1,78 @@ +# Voting + +Voting for Ravencoin is a protocol level solution, which allows UTXOs that expire. + +The advantage to expiring UTXOs is that they do not need to be held in memory after the expiration because the right to vote has expired, and therefore the expired votes are worthless. + +Layered on top of the voting is: +* Vote description - built on top of messaging. +* Vote ballot specification - JSON spec that defines vote addresses for each vote option. +* Vote distribution - built on top of rewards. +* Vote tracking. +* Voting with RPC calls. +* Voting in UI. + +## Voting - protocol layer + +Creating a vote will create vote tokens. These vote tokens are identical to normally issued tokens, except that they are created in exact qty and units as an already issued token. + +## RPC + +```issuevote BASETOKEN NAME ipfs_hash lastblocktovote [optional exempt address list]``` + +```vote VOTETOKEN
[optional qty]``` + +Creates a BASETOKEN^NAME token in exactly the same qty and units as BASETOKEN. IPFS hash is the meta-data message that defines what the vote is for - see vote message spec. The lastblocktovote is a block height which is the last block a vote is allowed into. After this block, the UTXOs for the un-voted tokens can be expired and removed. If left blank, lastblocktovote will be set to approximately 30 days or 43200 blocks from current block height. Exempt tokens are immediately burned at the same time that the vote tokens are distributed to the BASETOKEN holders. + +Vote tokens will be distributed in exact amounts to holders of BASETOKEN. + +Voting without specifying the qty in the RPC call will vote all the tokens. Partial votes are allowed, so that proxy voting can be done by custodial holders of BASETOKEN. + +## Delegative or Liquid voting +Vote tokens move just like regular tokens up until the block height when the vote expires. This allows vote token holders to send their vote to a delegate that might have better information about the topic and therefore cast a more informed vote. + +Ravencoin voting supports this type of vote while still protecting against counterfeit votes and ensuring votes can't be cast twice, and transparently tracking every vote that is cast or not cast. + +By issuing EXACTLY the same number of vote tokens as the BASETOKEN and automatically burning the exempted votes, it is easy to do a full audit of all votes. Unused votes that expire from the mempool can be easily calculated by subtracting votes from issued vote tokens once the vote expiration block height has passed. This number can also be audited by the Ravencoin software at the time of UTXO expiry to ensure the UTXO vote count exactly matches issuance before removal. Note: If UTXO optimization is done for burn addresses, this will need to factor into the UTXO audit. + +## Vote message specification (in IPFS) +``` +{ +"subject":"Vote for membership expansion", +"message": "Members of our community have requested that we increase membership. To this end, we are holding a vote. You have until Thursday to register your vote.", +"ballot": { "RBQ5A9wYKcebZtTSrJ5E4bKgPRbNmr8M2H":"Yes, expand membership.", + "RCqsnXo2Uc1tfNxwnFzkTYXfjKP21VX5ZD":"No, do not expand membership." + } +} +``` + +Voting addresses may be set to vanity burn addresses like: +``` +RVoteYesXXXXXXXXXXXXXXXXXXXXbDU8Rx +RVoteNoXXXXXXXXXXXXXXXXXXXXXWdk9zp +RAddMembershipXXXXXXXXXXXXXXWSzhgw +RYesToFranchiseXXXXXXXXXXXXXamSLQv +RAbstainXXXXXXXXXXXXXXXXXXXXaHW7mE +``` + +## Vote Token Creation in UI +The UI and RPC would let only the token owner create a vote. Votes must be created in exact proportion to the issued tokens. If exempt addresses are specified, then those tokens are immediately sent to a vote Burn address. + +## Voting in UI +The presence of a vote token allows the UI to present a voting interface that presents the ballot options and sends the vote tokens to the correct voting address. + +For simplification, all vote tokens will be voted for the selected option when using the UI. + +The normal transfer RPC call would allow vote splitting by transferring some votes to one address and other votes to another address. + +## Vote tracking +Vote tracking adds up the votes in the addresses that match those in the ballot and presents the results. + +## Vote by percentage +This is an optional way of allocating vote tokens. Instead of issuing a vote token in the same qty and units as the underlying BASETOKEN, allocate 100 vote tokens with units 8. Then allocate the tokens as a percentage of ownership. + +```issue_percent_vote BASETOKEN NAME ipfs_hash lastblocktovote [optional exempt address list]``` + +The vote mechanism is identical, but this allocates votes as a percentage. The total BASETOKEN amount minus the exemption addresses is calculated. Then each address gets QTY_OF_BASETOKEN_HELD / TOTAL_BASETOKEN_NOT_INCLUDING_EXEMPTED_ADDRESSES. This is calculated down to the satoshi. The amount of vote tokens must sum up to 100.00000000, so any remaining satoshis (caused by rounding errors at the 10^-8 precision) need to be accounted for and either sent to a burn address or allocated to a BASETOKEN holder. + +The advantage of using percentage voting is that it simplifies some vote calculations as a simple asset explorer will allow anyone to see the votes cast and view it as a percentage without knowing the total qty of tokens issued.