When the world’s biggest ICO was launched back in June 2018 and got freezed for two days because of a software bug, it made the crypto community skeptical. Leaving that behind, four months after now accounts of EOS have more transactions than Ethereum. Therefore the promise of faster and free transactions, EOS has about 13,000 daily active users compared to Ethereum which has only 2,000 on top most Dapp.
A Smart Contract Security of EOS
It’s pretty clear that a smart contract has vulnerabilities which are applied to all platforms. As we have Ethereum, smart contracts written on EOS require an audition before steps live on the mainnet. Sometimes fatal bugs in the agreement can get exploited if the deals are not battle tested enough. With this guide, we’re looking to help you avoid common pitfalls if you are working on the dApp on EOS.
Before you begin reading, you’ll need to know some prerequisite information about EOS development. Some knowledge of C++ is a must. You can start with the smart contract development of EOSIO’s documentation.
How to deal with the ABI Dispatcher
extern “C” {
void apply(uint64_t receiver, uint64_t code, uint64_t action) {
class_name thiscontract(receiver);
if ((code == N(eosio.token)) && (action == N(transfer))) {
execute_action(&thiscontract, &class_name::transfer);
return;
}
if (code != receiver) return;
switch (action) { EOSIO_API(class_name, (action_1)(action_n))};
eosio_exit(0);
}
}
The above sample is a code of a modified ABI dispatcher. Below is a simpler ABI dispatcher used for much simpler action handling the contract.
EOSIO_ABI( class_name, (action_1)(action_n) );
The ABI forwarder/dispatcher lets you listen to incoming EOSIO. Token transfers and even regular interaction with the smart contract. To avoid abnormal and illegal calls is essential to bind each key action and the code to meet all the requirements.
A perfect example is a hack which happened to dApp EOSBet Casino due to a bug while forwarding source code in their ABI.
EOSIO_ABI( class_name, (action_1)(action_n) );
The above check allowed an attacker in the apply action handler of ABI forwarding source code to bypass the EOSIO token. Then the transfer duty completely then straight call contract:: transfer duty without any EOS to transfer to the contract. All of this before even placing the bet. For the loss, he didn’t receive a thing and also didn’t lose. Still for the wins as paid with real EOS from the contract.
They solved the above bug by adding a check of eosio. Token contract transfer action. All of this to happen before the incoming action request going to the contract.
Remember that is very important to use the statement require_auth(account); only to the actions, you desire to the authorized account to execute. require_auth(_self) this one is for allowing the owner of the contract to sin the transaction.
if( code == self || code == N(eosio.token) ) {
if( action == N(transfer) ){
eosio_assert( code == N(eosio.token), “Must transfer EOS”);
}
TYPE thiscontract( self );
switch( action ) {
EOSIO_API( TYPE, MEMBERS )
}
}
Remember that is very important to use the statement require_auth(account); only to the actions, you desire to the authorized account to execute. require_auth(_self) this one is for permitting the owner of the contract to sin the transaction.
The Authorization in Actions
void token::transfer( account_name from, account_name to, asset quantity) { auto sym = quantity.symbol.name(); require_recipient( from ); require_recipient( to ); auto payer = has_auth( to ) ? to : from; sub_balance( from, quantity ); add_balance( to, quantity, payer ); }
The above sample code lets anyone call the action. To solve this issue use require_auth(from); a statement used to authorize the payer for the call the work.
Avoid modifying the EOSIO Token contract
Recently a smart white hat hacker claimed a billion tokens due to a weak tested method call to their eosio. Token settlement. The inactive Dapp Se7ens told it came with a new method inside the eosio. Token contract. By airdropping their tokens in the user accounts. The good part is, the agreement didn’t call the problem or the transfer move of the eosio. Token contract.
The exciting part, regarding the change and the hence, the fund came back magically in the account users. The second thing, they forgot the verify the amount, in the previous method before the transfer. The moment when the hacker claimed about 1 billion of their available tokens in the process.
Moving from this, changing the token symbol and the maximum supply its indicated to avoid modifying it for making some custom functions, as the bugs in the eosio.token, which can be a fatal contract. For serving a secure airdrop, transfer the airdrop tokens to a different account then move it from there. Changing of the multi-index table properties
Now, EOS keeps the data into a shared memory database to manage the sharing through actions.
void transfer(symbol_name symbol, account_name from, account_names to, uint64_t balance) { require_auth(from); account fromaccount; eosio_assert(is_balance_within_range(balance), "invalid balance"); eosio_assert(balance > 0, "must transfer positive balance"); uint64_t amount = balance * 4; //Multiplication overflow }
The above sample code makes a multi_inde table named people which are nicely structured on a single row of that table, by only using the struct person. At this moment EOS doesn’t allow any type of modifications of the table features once it gets deployed. The eosio_assert_message assertion is the failure the error which is thrown. So far, proprieties need to be made thought out right after deployment of the table. If not, a new table with a different name must be made and requires tons of care while migrating from an old to a new table. In case of failing to make can turn to a loss of data.
Check of the Numerical Overflow
While doing an arithmetic operation, sometimes values might overflow in case boundary conditions are not correctly checked, causing loss of users assets.
void transfer(symbol_name symbol, account_name from, account_names to, uint64_t balance) { require_auth(from); account fromaccount; eosio_assert(is_balance_within_range(balance), "invalid balance"); eosio_assert(balance > 0, "must transfer positive balance"); uint64_t amount = balance * 4; //Multiplication overflow }
The above sample code, using uint64_t presenting user balance can result into the overflow when the value is multiplied. So, avoid the use of uint64_t, so you denote balances and making arithmetic operations on it. Make use of the asset structure correctly defined in the eosiolib for any actions, then that balance which is carrying of the overflow conditions.
Some care for the Assumptions in the Contract
In the process will be assumptions which need assertions when executing a contract. If using eosio_assert will care of the conditions beforehand and avoid any contention fails. For example:
void assert_roll_under(const uint8_t& roll_under) { eosio_assert(roll_under >= 2 && roll_under <= 96, "roll under overflow, must be greater than 2 and less than 96"); }
The above assert statement creates an assumption which roll_under integer is better than 2 and less than 96. If the above cover case is not discovered can turn into a small disaster for the house which sets the rules.
Creating True Random numbers
Generating True Random numbers for the EOS Blockchain can be a real risk it is not perfect. If is not done correctly will turn into an adversary predicting the outcomes, setting all the system in the process. There are services as Oracle.it existing to share random numbers from an external source.
However, they are very expansive, and they have only a single point of failure. Individuals used the Blockchain’s contextual variables (bloc stamp, block numbers, etc.) in the back days so they could create the random number in the Ethereum smart contract. However, it has been gamed before. To build it correctly, this program has to provide combined randomness, which non-party could control alone. For now, the best way is one suggested by Dan Larimar:
string sha256_to_hex(const checksum256& sha256) { return to_hex((char*)sha256.hash, sizeof(sha256.hash)); } string sha1_to_hex(const checksum160& sha1) { return to_hex((char*)sha1.hash, sizeof(sha1.hash)); } template <class T> Inline void hash_combine(std::size_t& seed, const T& v) { std::hash<T> hasher; seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); }
The above code provides an updated random number generation from 1 to 100. seed1 is the house seed, and seed2 is the individual seed right above. If you need a reference check, EOSBetCasino, and Dappub which have an open sourced their complete contracts. They managed to use a random number generator implementing a good dice game between the player and the source house (developer).
Sadly, EOSBet got hacked again of only 65,000 EOS, when an opponent tricked their eosio. Token contract to send EOS to his contract everything when a transaction was happening. Still, eosio.token contract code informs the sender and the receiver of the incoming tokens. So to imitate this behavior and facilitate the hack, the hacker made two accounts, we can assume A&B. A, had a nice, smart contract having an action statement require_recipient(N(eosbetdice11)).
When A helps the transaction from A to B using the action call, if inform the transfer function from the contract. Similar to the case when the call came from the eosio. Token settlement. Since there’s not a real transfer of EOS into the contract, each time the hacker lost a bet, he dropped like nothing. Still, he received a reward when won the chance. Again, is not enough to only check the action name and the contract name.
Some checks in the notifications from Contracts
To check the issue, the function needs to mitigate all the time the contract is indeed, then the receiver of the tokens not.
eosio_assert(transfer_data.from == _self || transfer_data.to == _self, "Must be incoming or outgoing transfer");
Thinking of some of the best practices while developing a smart contract on EOS?
For sure bugs are inevitable for a software. The consequences come amplified in a more decentralized environment mostly when involves the transaction of value. Far from these EOS guards the above things discuses. But, here are some of the precautions and good practices the new smart contract developers have to keep in mind.
1 – Independently audit the contract coming from the third party smart contract before releasing any mainnet.
2 – Do the Caveman debugging (the only way of debugging the contract) of the contract right before the release of the testnet. A good point, EOSIO has a nice documentation guide for it.
3 – Set your limit transfer rate when withdrawing, so you avoid excessive losses, when are the initial days of mainnet launch. Make use of a bug bounty program, perfect for disclosure by the white hat hackers.
4 – Make sure you have a killswitch to freeze the contract when you notice a bug.
For implementation, we persist a flag in the multi_idex table. To set the flag, we use an action called only by the owner of the contract. Then we check each public work if the flag is set to frozen or not. A sample you can find right below.
struct st_frozen { uint64_t frozen; }; typedef singleton<N(freeze), st_frozen> tb_frozen; tb_frozen _frozen; uint64_t getFreezeFlag() { st_frozen frozen_st{.frozen = 0}; return _frozen.get_or_create(_self, frozen_st); } void setFreezeFlag(const uint64_t& pFrozen) { st_frozen frozen_st = getFreezeFlag(); frozen_st.frozen = pFrozen; _frozen.set(frozen_st, _self); } // public Action void freeze() { require_auth(_self); setFreezeFlag(1); } // public Action void unfreeze() { require_auth(_self); setFreezeFlag(0); } // any public action void action(...) { eosio_assert(getFreezeFlag().frozen == 1, "Contract is frozen!"); ... }
5 – Keep yourself updated to the security enhancements, in vulnerabilities or libraries disclosures on the platform. Make sure you update your libraries if necessary immediately.
6 – Open source the contract code at least, in this way fairness is kept in the game, and indie developers can sense bugs way quicker.
Conclusion: EOS Smart Contract Security
With only five months since EOS launched, it grew more than expected. Mutable smart contracts, trade-offs made, 21 mining nodes and many more, came with a lot of criticism from decentralization minimalist. However, it didn’t stop the dApps on Ethereum to shift to EOS given the scalability which the platform shares them today. Even if it’s EOS or the Ethereum who wins the war, will see in the future. However, EOS surely won a battle and will stay like this until Ethereum manages to reach the scalability which the world needs now.
For real-time trade alerts and a daily breakdown of the crypto markets, sign up for Elite membership!
Disclaimer: This article should not be taken as, and is not intended to provide, investment advice. Global Coin Report and its affiliates, employees, writers, and subcontractors are cryptocurrency investors and from time to time may or may not have holdings in some of the coins or tokens they cover. Please conduct your own thorough research before investing in any cryptocurrency and read our full disclaimer.
Image Courtesy of Pixabay.
The post The Only EOS Smart Contract Guide You’d Need to Read appeared first on Global Coin Report.
Read more at https://globalcoinreport.com/the-only-eos-smart-contract-guide-youd-need-to-read/
Globalcoinreport.com/ is author of this content, TheBitcoinNews.com is is not responsible for the content of external sites.
Our Social Networks: Facebook Instagram Pinterest Reddit Telegram Twitter Youtube