This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
|
dido:public:ra:1.2_views:3_taxonomic:4_data_tax:10_errors:start [2022/02/03 13:00] nick |
dido:public:ra:1.2_views:3_taxonomic:4_data_tax:10_errors:start [2022/03/16 15:55] (current) char |
||
|---|---|---|---|
| Line 39: | Line 39: | ||
| [[dido:public:ra:1.2_views:3_taxonomic:4_data_tax:10_errors:start | Return to Top]] | [[dido:public:ra:1.2_views:3_taxonomic:4_data_tax:10_errors:start | Return to Top]] | ||
| - | Solidity Syntax errors are detected within [[dido:public:ra:xapend:xapend.a_glossary:s:syntax_highlighting]] the [[dido:public:ra:xapend:xapend.b_stds:defact:ethereum:remix]] or other [[dido:public:ra:xapend:xapend.a_glossary:i:ide | Integrated Development Environments (IDEs) ]] such as [[dido:public:ra:xapend:xapend.b_stds:defact:microsoft:visual_studio_code]] with the Solidity [[dido:public:ra:xapend:xapend.a_glossary:p:plug_in]]. | + | Solidity Syntax errors are detected within [[dido:public:ra:xapend:xapend.a_glossary:s:syntax_highlighting]] the [[dido:public:ra:xapend:xapend.b_stds:defact:ethereum:remix:start]] or other [[dido:public:ra:xapend:xapend.a_glossary:i:ide | Integrated Development Environments (IDEs) ]] such as [[dido:public:ra:xapend:xapend.b_stds:defact:microsoft:visual_studio_code]] with the Solidity [[dido:public:ra:xapend:xapend.a_glossary:p:plug_in]]. |
| A very important aspect of **Syntax Errors** caught within the IDE is that these errors are NOT caught on the blockchain, but rather just inside the IDE. This means there is no expenditure of Gas to catch these errors. | A very important aspect of **Syntax Errors** caught within the IDE is that these errors are NOT caught on the blockchain, but rather just inside the IDE. This means there is no expenditure of Gas to catch these errors. | ||
| Line 155: | Line 155: | ||
| : **Note:** This is caused by malformed inputs, usually during contract creation. | : **Note:** This is caused by malformed inputs, usually during contract creation. | ||
| </WRAP>| | </WRAP>| | ||
| - | ^ Stack Overflow | <WRAP> | + | ^ Stack Overflow / Underflow | <WRAP> |
| - | </WRAP>| | + | EVM is a stack-based machine, and thus performs all computations in a data area called the stack. All in-memory values are also stored in the stack. It has a maximum depth of 1024 elements and supports the word size of 256 bits. [[https://www.datasciencecentral.com/the-ethereum-virtual-machine-evm/]]. An **Overflow** occurs when the stack exceeds the 1024 elements maximum size. |
| - | ^ Stack Underflow | <WRAP> | + | |
| + | An **Underflow** occurs when the stack is empty and a **''pop''** is called. | ||
| </WRAP>| | </WRAP>| | ||
| ===== Logic Errors ===== | ===== Logic Errors ===== | ||
| Line 191: | Line 192: | ||
| Logic Errors are the hardest to fix becuase there are no tools that can examine a Smart Contract and find the Logic Errors. There are efforts underway at Ethereum called the [[https://github.com/leonardoalt/ethereum_formal_verification_overview | Ethereum Formal Verification]]. | Logic Errors are the hardest to fix becuase there are no tools that can examine a Smart Contract and find the Logic Errors. There are efforts underway at Ethereum called the [[https://github.com/leonardoalt/ethereum_formal_verification_overview | Ethereum Formal Verification]]. | ||
| + | The Solidity [[dido:public:ra:xapend:xapend.b_stds:defact:ethereum:remix:start| Remix Project]] [[dido:public:ra:xapend:xapend.a_glossary:i:ide]] has a [[dido:public:ra:xapend:xapend.a_glossary:p:plug_in]] for [[dido:public:ra:xapend:xapend.a_glossary:s:static_code_analysis]] called the **Remix-analyzer**. | ||
| + | |||
| + | **remix-analyzer** is the library which works underneath of **Remix-IDE Solidity Static Analysis** plugin. | ||
| + | |||
| + | remix-analyzer is an NPM package. It can be used as a library in a solution supporting [[dido:public:ra:xapend:xapend.a_glossary:n:nodejs]]. Find more information about this type of usage in the remix-analyzer repository | ||
| + | |||
| + | |||
| + | <table staticAnalysis> | ||
| + | <caption>Solidity Static Analysis(( | ||
| + | __Solidity Static Analysis__, | ||
| + | Remix-IDE, | ||
| + | Accessed: 4 February 2022 | ||
| + | ))</caption> | ||
| + | |||
| + | ^ Category ^ Name of Weakness ^ Description | | ||
| + | | **Security** | Transaction origin: **''tx.origin''** is used | <WRAP> | ||
| + | **''tx.origin''** is useful only in very exceptional cases. If you use it for authentication, you usually want to replace it by “msg.sender”, because otherwise any contract you call can act on your behalf. | ||
| + | |||
| + | Example: | ||
| + | |||
| + | <Code solidity linenums:1> | ||
| + | require ( tx.origin == owner ); | ||
| + | </Code> | ||
| + | </WRAP> | | ||
| + | ^ | Check effects: Potential reentrancy bugs | <WRAP> | ||
| + | Potential Violation of Checks-Effects-Interaction pattern can lead to [[dido:public:ra:xapend:xapend.a_glossary:r:reentrancy_attack]] vulnerability. | ||
| + | |||
| + | Example: | ||
| + | |||
| + | <Code Solidity linenums:1> | ||
| + | // sending ether first | ||
| + | msg.sender.transfer ( amount ); | ||
| + | |||
| + | // updating state afterwards | ||
| + | balances [ msg.sender ] -= amount; | ||
| + | </Code> | ||
| + | |||
| + | </WRAP> | | ||
| + | ^ | Inline assembly: Inline assembly used | <WRAP> | ||
| + | Use of inline assembly is advised only in rare cases. | ||
| + | |||
| + | Example: | ||
| + | <Code solidity linenums:1> | ||
| + | assembly | ||
| + | { // retrieve the size of the code, this needs assembly | ||
| + | let size := extcodesize(_addr) | ||
| + | } // End assembly | ||
| + | </Code> | ||
| + | |||
| + | </WRAP>| | ||
| + | ^ | Block timestamp: Semantics maybe unclear | <WRAP> | ||
| + | **''now''** does not mean current time. **''now''** is an alias for **''block.timestamp''**. **''block.timestamp''** can be influenced by miners to a certain degree, be careful. | ||
| + | |||
| + | Example: | ||
| + | |||
| + | <Code solidity linenums:1> | ||
| + | // using now for date comparison | ||
| + | if ( startDate > now ) | ||
| + | { isStarted = true; | ||
| + | } // End if | ||
| + | // using block.timestamp | ||
| + | uint c = block.timestamp; | ||
| + | </Code> | ||
| + | |||
| + | </WRAP> | | ||
| + | ^ | Low level calls: Semantics maybe unclear | <WRAP> | ||
| + | Use of low level **''call''**, **''callcode''** or **''delegatecall''** should be avoided whenever possible. **''send''** does not throw an exception when not successful, make sure you deal with the failure case accordingly. Use **''transfer''** whenever failure of the ether transfer should rollback the whole transaction. | ||
| + | |||
| + | Example: | ||
| + | |||
| + | <Code solidity linenums:1> | ||
| + | x.call ( 'something' ); | ||
| + | x.send ( 1 wei ); | ||
| + | </Code> | ||
| + | |||
| + | </WRAP> | | ||
| + | ^ | Blockhash usage: Semantics maybe unclear | <WRAP> | ||
| + | **''blockhash''** is used to access the last 256 block hashes. A miner computes the block hash by “summing up” the information in the current block mined. By summing up the information in a clever way a miner can try to influence the outcome of a transaction in the current block. | ||
| + | |||
| + | Example: | ||
| + | <Code solidity linenums:1> | ||
| + | bytes32 b = blockhash(100); | ||
| + | </Code> | ||
| + | |||
| + | </WRAP> | | ||
| + | ^ | Selfdestruct: Beware of caller contracts | <WRAP> | ||
| + | selfdestruct can block calling contracts unexpectedly. Be especially careful if this contract is planned to be used by other contracts (i.e. library contracts, interactions). Selfdestruction of the callee contract can leave callers in an inoperable state. | ||
| + | |||
| + | Example: | ||
| + | <Code solidity linenums:1> | ||
| + | selfdestruct(address(0x123abc..)); | ||
| + | </Code> | ||
| + | |||
| + | </WRAP>| | ||
| + | ^**Gas & Economy**| Gas costs: Too high gas requirement of functions | <WRAP> | ||
| + | Never use **''this''** to call functions in the same contract, it only consumes more gas than normal local calls. | ||
| + | |||
| + | Example: | ||
| + | |||
| + | <Code Solidity linenums:1> | ||
| + | contract test | ||
| + | { function callb() public | ||
| + | { address x; | ||
| + | this.b(x); | ||
| + | } // End function callb | ||
| + | | ||
| + | function b (address a ) | ||
| + | public | ||
| + | returns ( bool ) | ||
| + | {} // End function b | ||
| + | } | ||
| + | </Code> | ||
| + | </WRAP>| | ||
| + | ^ | Delete on dynamic Array: Use require/assert appropriately| <WRAP> | ||
| + | The **''delete''** operation when applied to a dynamically sized array in Solidity generates code to delete each of the elements contained. If the array is large, this operation can surpass the block gas limit and raise an OOG exception. Also nested dynamically sized objects can produce the same results. | ||
| + | |||
| + | Example: | ||
| + | <Code solidity linenums:1> | ||
| + | contract arr | ||
| + | { uint[] users; | ||
| + | function resetState() | ||
| + | public | ||
| + | { delete users; | ||
| + | } // End function resetState | ||
| + | } // End contract arr | ||
| + | </Code> | ||
| + | |||
| + | </WRAP>| | ||
| + | ^ | For loop over dynamic array: Iterations depend on dynamic array’s size | <WRAP> | ||
| + | Loops that do not have a fixed number of iterations, for example, loops that depend on storage values, have to be used carefully: Due to the block gas limit, transactions can only consume a certain amount of gas. The number of iterations in a loop can grow beyond the block gas limit, which can stall the complete contract at a certain point. Additionally, using unbounded loops can incur in a lot of avoidable gas costs. Carefully test how many items at maximum you can pass to such functions to make it successful. | ||
| + | |||
| + | Example: | ||
| + | <Code Solidity linenums:1> | ||
| + | contract forLoopArr | ||
| + | { uint[] array; | ||
| + | |||
| + | function shiftArrItem | ||
| + | ( uint index ) | ||
| + | public | ||
| + | returns ( uint[] memory ) | ||
| + | { for ( uint i = index; | ||
| + | i < array.length; | ||
| + | i++ | ||
| + | ) | ||
| + | { array [ i ] = array [i + 1 ]; | ||
| + | } // End for i | ||
| + | return array; | ||
| + | } // End function shiftArrItem | ||
| + | } // End contract forLoopArr | ||
| + | </Code> | ||
| + | |||
| + | </WRAP>| | ||
| + | ^ | Ether transfer in loop: Transferring Ether in a for/while/do-while loop | <WRAP> | ||
| + | Ether payout should not be done in a loop. Due to the block gas limit, transactions can only consume a certain amount of gas. The number of iterations in a loop can grow beyond the block gas limit, which can cause the complete contract to be stalled at a certain point. If required, make sure that the number of iterations are low, and you trust each address involved. | ||
| + | |||
| + | Example: | ||
| + | <Code Solidity linenums:1> | ||
| + | contract etherTransferInLoop | ||
| + | { address payable owner; | ||
| + | |||
| + | function transferInForLoop | ||
| + | ( uint index ) | ||
| + | public | ||
| + | { for ( uint i = index; | ||
| + | i < 100; | ||
| + | i++ | ||
| + | ) | ||
| + | { owner.transfer ( i ); | ||
| + | } // End for i | ||
| + | } // End function transferInForLoop | ||
| + | |||
| + | function transferInWhileLoop | ||
| + | ( uint index ) | ||
| + | public | ||
| + | { uint i = index; | ||
| + | while ( i < 100 ) | ||
| + | { owner.transfer(i); | ||
| + | i++; | ||
| + | } // End while loop | ||
| + | } // End function transferInWhileLoop | ||
| + | } // End contract etherTransferInLoop | ||
| + | </Code> | ||
| + | </WRAP>| | ||
| + | ^ **ERC** | ERC20: ‘decimals’ should be ‘uint8’ | <WRAP> | ||
| + | [[dido:public:ra:xapend:xapend.b_stds:defact:ethereum:eip:erc_0020 | ERC20]] Contracts **''decimals''** function should have **''uint8''** as return type. | ||
| + | |||
| + | Example: | ||
| + | <Code Solidity linenums:1> | ||
| + | contract EIP20 | ||
| + | { uint public decimals = 12; | ||
| + | } // End contract EIP20 | ||
| + | </Code> | ||
| + | |||
| + | </WRAP>| | ||
| + | ^ **Miscellaneous** | Constant/View/Pure functions: Potentially constant/view/pure functions | <WRAP> | ||
| + | It warns for the methods which potentially should be constant/view/pure but are not. | ||
| + | |||
| + | Example: | ||
| + | <Code Solidity linenums:1> | ||
| + | function b | ||
| + | ( address a ) | ||
| + | public | ||
| + | returns ( bool ) | ||
| + | { return true; | ||
| + | } // End function b | ||
| + | </Code> | ||
| + | </WRAP>| | ||
| + | ^ | Similar variable names: Variable names are too similar | <WRAP> | ||
| + | It warns on the usage of similar variable names. | ||
| + | |||
| + | Example: | ||
| + | <Code Solidity linenums:1> | ||
| + | // Variables have very similar names voter and voters. | ||
| + | function giveRightToVote | ||
| + | ( address voter ) | ||
| + | public | ||
| + | { require ( voters [ voter ].weight == 0 ); | ||
| + | voters [ voter ].weight = 1; | ||
| + | } // End function giveRightToVote | ||
| + | </Code> | ||
| + | |||
| + | </WRAP>| | ||
| + | ^ | No return: Function with ‘returns’ not returning | <WRAP> | ||
| + | It warns for the methods which define a return type but never explicitly return a value. | ||
| + | |||
| + | Example: | ||
| + | <Code Solidity linenums:1> | ||
| + | function noreturn | ||
| + | ( string memory _dna ) | ||
| + | public | ||
| + | returns (bool) | ||
| + | { dna = _dna; | ||
| + | } // End function noreturn | ||
| + | </Code> | ||
| + | </WRAP>| | ||
| + | ^ | Guard conditions: Use ‘require’ and ‘assert’ appropriately | <WRAP> | ||
| + | Use **''assert(x)''** if you never ever want **''x''** to be **''false''**, not in any circumstance (apart from a bug in your code). Use **''require(x)''** if **''x''** can be **''false''**, due to e.g. invalid input or a failing external component. | ||
| + | |||
| + | Example: | ||
| + | <Code Solidity linenums:1> | ||
| + | assert(a.balance == 0); | ||
| + | </Code> | ||
| + | </WRAP>| | ||
| + | ^ | Result not used: The result of an operation not used | <WRAP> | ||
| + | A binary operation yields a value that is not used in the following code. This is often caused by confusing assignment (**''=''**) and comparison (**''==''**). | ||
| + | |||
| + | Example: | ||
| + | <Code Solidity linenums:1> | ||
| + | c == 5; | ||
| + | // or | ||
| + | a + b; | ||
| + | </Code> | ||
| + | </WRAP>| | ||
| + | ^ | String Length: Bytes length != String length | <WRAP> | ||
| + | Bytes and string length are not the same since strings are assumed to be UTF-8 encoded (according to the ABI definition) therefore one character is not necessarily encoded in one byte of data. | ||
| + | |||
| + | Example: | ||
| + | <Code Solidity linenums:1> | ||
| + | function length | ||
| + | ( string memory a ) | ||
| + | public | ||
| + | pure | ||
| + | returns ( uint ) | ||
| + | { bytes memory x = bytes ( a ); | ||
| + | return x.length; | ||
| + | } // End function length | ||
| + | </Code> | ||
| + | </WRAP>| | ||
| + | ^ | Delete from dynamic array: ‘delete’ on an array leaves a gap | <WRAP> | ||
| + | Using **''delete''** on an array leaves a gap. The length of the array remains the same. If you want to remove the empty position you need to shift items manually and update the length property. | ||
| + | |||
| + | Example: | ||
| + | <Code Solidity linenums:1> | ||
| + | contract arr | ||
| + | { uint[] array = [ 1, 2, 3 ]; | ||
| + | function removeAtIndex() | ||
| + | public | ||
| + | returns ( uint[] memory ) | ||
| + | { delete array[1]; | ||
| + | return array; | ||
| + | } // End function removeAtIndex | ||
| + | } // End contract arr | ||
| + | </Code> | ||
| + | </WRAP>| | ||
| + | ^ | Data Truncated: Division on int/uint values truncates the result | <WRAP> | ||
| + | Division of integer values yields an integer value again. That means e.g. ''10 / 100 = 0'' instead of ''0.1'' since the result is an integer again. This does not hold for division of (only) literal values since those yield rational constants. | ||
| + | |||
| + | Example: | ||
| + | <Code Solidity linenums:1> | ||
| + | function contribute() | ||
| + | payable | ||
| + | public | ||
| + | { uint fee = msg.value * uint256 ( feePercentage / 100 ); | ||
| + | fee = msg.value * ( p2 / 100 ); | ||
| + | } // End function contribute | ||
| + | </Code> | ||
| + | </WRAP>| | ||
| + | </table> | ||
| ===== DIDO Specifics ===== | ===== DIDO Specifics ===== | ||
| [[dido:public:ra:1.2_views:3_taxonomic:4_data_tax:10_errors:start | Return to Top]] | [[dido:public:ra:1.2_views:3_taxonomic:4_data_tax:10_errors:start | Return to Top]] | ||
| Line 203: | Line 502: | ||
| + | <color darkred><todo @char>Review </todo></color> | ||