Douglas Horn

On Saturday, July 27, when the first Telos ratification ballot had been completed, an exploit utilized by one Telos account prompted a closer look at how the voting code for the Telos Amend voting (eosio.amend) and the Worker Proposal System voting (eosio.saving) are calculated. Errors were found in both the configuration settings of eosio.amend and in the figures used to calculate the voting thresholds required to pass a ballot or to refund the deposit from one. The nature of both the incorrect configuration settings and code errors were that they did not adhere as closely as possible to the governance stated in the TBNOA (Telos Blockchain Network Operating Agreement).

What the Governance Documents Say

Paragraph 38 of the TBNOA addresses governance amendments. It reads:

“The Telos Governance Documents may be amended by a vote of the TLOS token holders using the “ratifyamend” contract. To ratify or amend any Telos Governance Document, any user may execute the “ratifyamend” contract, paying its contract fee of 700 TLOS (the “Ratify/Amend Contract Fee”), which may be returned if the contract receives a minimum of 1% of votes from all TLOS voters. This fee may be paid by one Member or collected from many Members over time to execute when the full cost has been collected. Once the fee has been fully paid, the full text of the proposed new document, or the existing document in the case of ratification, shall be recorded to the Telos blockchain. No Telos Governance Documents shall be ratified or amended except by a vote of the TLOS token holders, as recorded by the “ratifyamend” contract with no less than 15% vote participation among votable TLOS tokens and no fewer than 10% more Yes than No votes, at the end of a 5,000,000 block voting period (approximately 29 days).”

The TBNOA was ratified prior to completion of code development for these additional voting features. As a result, there is an inconsequential mismatch between the name of contract listed (ratifyamend) and the actual contract name (eosio.amend).

The important settings named in this paragraph are meant to be held in the eosio.amend ‘configs’ table. The threshold of tokens voted among all votable tokens that is required to pass a proposal is ‘threshold_pass_voters’ which should be set to 15.00, meaning 15%. The amount of tokens voted as Yes compared to the total number of tokens voted Yes or No (but not Abstain) that is required to pass a proposal is ‘threshold_pass_votes’ which should be set to 55.00, meaning 55% or 10% more Yes votes than No votes.

Similarly, the threshold of tokens voted among all voteable tokens that is required to have the deposit fee refunded for a proposal is ‘threshold_fee_voters’ which should be set to 1.00, meaning 1%. The amount of tokens voted as Yes compared to the total number of tokens voted Yes or No (but not Abstain) that is required to have the deposit fee refunded for a proposal is ‘threshold_fee_votes’ which should be set to 0.00, meaning that there is no requirement to have more Yes votes than No votes.

Figure 1. The eosio.amend ‘configs’ table holds the values for Telos Amend ballots.

The Configuration Errors

The figures for ‘threshold_pass_voters’, ‘threshold_pass_votes’, ‘threshold_fee_voters’, and ‘threshold_fee_votes’ were set incorrectly to 5.00, 66.67, 4.00, and 25.00 respectively instead of to 15.00, 55.00, 1.00, and 0.00. The most consequential impact of these errors would be that a measure could pass with just 5% of the amount of voteable TLOS tokens instead of the required 15%. Additionally, the requirement to pass would be 66.67% Yes votes instead of 55.00% Yes votes. While the TEDP would have passed both of these requirements, it is still important to have the configuration settings match the levels mentioned in the governance documents. Fortunately, the calculation is not performed until the TED Plan ballot is closed (when the original proposer calls the ‘closeprop’ action) and these configuration errors can be resolved before that occurs.

The Code Errors

“Voteable TLOS tokens” as referenced in the TBNOA refers to the tokens in Telos accounts that have registered to vote using the eosio.trail ‘regvoter’ action. These tokens are recorded on the chain as VOTE tokens in the eosio.trail ‘registries’ table. The total number of “voteable TLOS tokens” is the same as the ‘supply’ entry on the table for VOTE tokens. Currently, this is 26,149,527.9088. The ‘total_voters’ entry on this table records the number of unique accounts that are registered to vote. That number is 5,398.

The code for eosio.amend’s ‘closeprop’ action contains an error in how it counts the number of voters. The existing version counts the number of unique accounts voting as a percentage of the ‘total_voters’ figure, using the number of accounts that have registered to vote no matter how many TLOS tokens they may hold. However, this is not in line with what the TBNOA calls for. This was a known error that was intended to be corrected by this time. An update to the Trail voting system has been planned for some time but has not been implemented yet due to tight programmer resources.

The Exploit

Using the total number of accounts holding voteable TLOS tokens as the determination for whether the participation threshold had been met allowed an exploit where one account could create a large number of accounts and register them to vote even though they held few TLOS. Done in sufficiently large numbers, this would mean that all future Telos Amend proposals could be made to fail based on the account number threshold and therefore no governance correction could be made because a nearly limitless amount of accounts could be easily created. This would create an intractable situation that was certainly never intended by the authors or adopters of the TBNOA.

In the last hours before the TEDP ballot was about to expire with about 97% Yes votes (compared to No votes) and over 17 million total votes cast, the Telos account, ‘arfasys11111’ created hundreds of new Telos accounts with sequential names and identical keys. These accounts were all registered to vote, thus increasing the amount of ‘total_voters’ that would be used to assess whether the TEDP ballot would pass the threshold under the previous version of the code. Due to the speed of the account creation and registry, it was almost certainly performed by a bot or script for just this purpose.

The Outcome

It is fortunate that this exploit came to light before the ballot was closed or the TEDP would have failed despite the obvious intent of the voters. The Telos block producers and core developers consulted the program code, governance documents, and voting results and have determined that the only reasonable course of action is to correct the code errors (the same error exists twice, once to calculate passage of the ballot and once to determine if the depositor’s fee will be returned) and the configuration settings prior to executing the eosio.amend ‘closeprop’ action.

This voting error also exists on the WPS voting system (eosio.saving) and will be similarly corrected. The code changes for these (about 4 lines of code each) have already been pushed to the Telos Foundation Github repository, where they have been reviewed and merged. We are currently running unit tests on this code and hope to move it to the testnet soon. After it is successfully tested on the testnet, we will create a multisig transaction for the block producers to bring this code to the Telos mainnet. Once this is done, we can close the ballot and execute the TEDP.

About the author: Douglas Horn is the Telos architect and whitepaper author, and the founder of GoodBlock, a block producer and app developer for the Telos Blockchain Network.

More about GoodBlock can be found at:

Join us on Twitter @GoodBlockio

Vote for GoodBlock on the Telos Blockchain Network @goodblocktls