diff options
Diffstat (limited to 'docs')
-rw-r--r-- | docs/common-patterns.rst | 104 | ||||
-rw-r--r-- | docs/contracts.rst | 11 | ||||
-rw-r--r-- | docs/control-structures.rst | 27 | ||||
-rw-r--r-- | docs/index.rst | 2 | ||||
-rw-r--r-- | docs/installing-solidity.rst | 6 | ||||
-rw-r--r-- | docs/introduction-to-smart-contracts.rst | 16 | ||||
-rw-r--r-- | docs/layout-of-source-files.rst | 10 | ||||
-rw-r--r-- | docs/miscellaneous.rst | 2 | ||||
-rw-r--r-- | docs/security-considerations.rst | 8 | ||||
-rw-r--r-- | docs/solidity-by-example.rst | 4 |
10 files changed, 152 insertions, 38 deletions
diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst index 322be3ef..74f9c078 100644 --- a/docs/common-patterns.rst +++ b/docs/common-patterns.rst @@ -2,6 +2,110 @@ Common Patterns ############### +.. index:: withdrawal + +.. _withdrawal_pattern: + +************************* +Withdrawal from Contracts +************************* + +The recommended method of sending funds after an effect +is using the withdrawal pattern. Although the most intuitive +method of sending Ether, as a result of an effect, is a +direct ``send`` call, this is not recommended as it +introduces a potential security risk. You may read +more about this on the :ref:`security_considerations` page. + +This is an example of the withdrawal pattern in practice in +a contract where the goal is to send the most money to the +contract in order to become the "richest", inspired by +`King of the Ether <https://www.kingoftheether.com/>`_. + +In the following contract, if you are usurped as the richest, +you will recieve the funds of the person who has gone on to +become the new richest. + +:: + + contract WithdrawalContract { + address public richest; + uint public mostSent; + + mapping (address => uint) pendingWithdrawals; + + function WithdrawalContract() { + richest = msg.sender; + mostSent = msg.value; + } + + function becomeRichest() returns (bool) { + if (msg.value > mostSent) { + pendingWithdrawals[richest] += msg.value; + richest = msg.sender; + mostSent = msg.value; + return true; + } + else { + return false; + } + } + + function withdraw() returns (bool) { + uint amount = pendingWithdrawals[msg.sender]; + // Remember to zero the pending refund before + // sending to prevent re-entrancy attacks + pendingWithdrawals[msg.sender] = 0; + if (msg.sender.send(amount)) { + return true; + } + else { + pendingWithdrawals[msg.sender] = amount; + return false; + } + } + } + +This is as opposed to the more intuitive sending pattern. + +:: + + contract SendContract { + address public richest; + uint public mostSent; + + function SendContract() { + richest = msg.sender; + mostSent = msg.value; + } + + function becomeRichest() returns (bool) { + if (msg.value > mostSent) { + // Check if call succeeds to prevent an attacker + // from trapping the previous person's funds in + // this contract through a callstack attack + if (!richest.send(msg.value)) { + throw; + } + richest = msg.sender; + mostSent = msg.value; + return true; + } + else { + return false; + } + } + } + +Notice that, in this example, an attacker could trap the +contract into an unusable state by causing ``richest`` to be +the address of a contract that has a fallback function +which consumes more than the 2300 gas stipend. That way, +whenever ``send`` is called to deliver funds to the +"poisoned" contract, it will cause execution to always fail +because there will not be enough gas to finish the execution +of the fallback function. + .. index:: access;restricting ****************** diff --git a/docs/contracts.rst b/docs/contracts.rst index 5f370951..911bfc0d 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -25,7 +25,7 @@ API, this is done as follows:: // Need to specify some source including contract name for the data param below var source = "contract CONTRACT_NAME { function CONTRACT_NAME(unit a, uint b) {} }"; - + // The json abi array generated by the compiler var abiArray = [ { @@ -922,7 +922,7 @@ custom types without the overhead of external function calls: As the compiler cannot know where the library will be deployed at, these addresses have to be filled into the final bytecode by a linker -(see :ref:`commandline-compiler`) on how to use the +(see :ref:`commandline-compiler` for how to use the commandline compiler for linking). If the addresses are not given as arguments to the compiler, the compiled hex code will contain placeholders of the form ``__Set______`` (where @@ -932,10 +932,11 @@ encoding of the address of the library contract. Restrictions for libraries in comparison to contracts: -- no state variables -- cannot inherit nor be inherited +- No state variables +- Cannot inherit nor be inherited +- Cannot recieve Ether -(these might be lifted at a later point) +(These might be lifted at a later point.) .. index:: ! using for, library diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 5b78d099..b3940f72 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -127,11 +127,11 @@ Those names will still be present on the stack, but they are inaccessible. .. _creating-contracts: -Creating Contracts via new -========================== +Creating Contracts via ``new`` +============================== A contract can create a new contract using the ``new`` keyword. The full -code of the contract to be created has to be known and thus recursive +code of the contract being created has to be known and, thus, recursive creation-dependencies are now possible. :: @@ -142,6 +142,8 @@ creation-dependencies are now possible. x = a; } } + + contract C { D d = new D(4); // will be executed as part of C's constructor @@ -299,11 +301,14 @@ In the following example, we show how ``throw`` can be used to easily revert an } } -Currently, there are three situations, where exceptions happen automatically in Solidity: +Currently, there are six situations, where exceptions happen automatically in Solidity: -1. If you access an array beyond its length (i.e. ``x[i]`` where ``i >= x.length``) +1. If you access an array beyond its length (i.e. ``x[i]`` where ``i >= x.length``). 2. If a function called via a message call does not finish properly (i.e. it runs out of gas or throws an exception itself). 3. If a non-existent function on a library is called or Ether is sent to a library. +4. If you divide or modulo by zero (e.g. ``5 / 0`` or ``23 % 0``). +5. If you perform an external function call targeting a contract that contains no code. +6. If a contract-creation call using the ``new`` keyword fails. Internally, Solidity performs an "invalid jump" when an exception is thrown and thus causes the EVM to revert all changes made to the state. The reason for this is that there is no safe way to continue execution, because an expected effect did not occur. Because we want to retain the atomicity of transactions, the safest thing to do is to revert all changes and make the whole transaction (or at least call) without effect. @@ -500,9 +505,9 @@ The opcodes ``pushi`` and ``jumpdest`` cannot be used directly. +-------------------------+------+-----------------------------------------------------------------+ | callvalue | | wei sent together with the current call | +-------------------------+------+-----------------------------------------------------------------+ -| calldataload(p) | | call data starting from position p (32 bytes) | +| calldataload(p) | | calldata starting from position p (32 bytes) | +-------------------------+------+-----------------------------------------------------------------+ -| calldatasize | | size of call data in bytes | +| calldatasize | | size of calldata in bytes | +-------------------------+------+-----------------------------------------------------------------+ | calldatacopy(t, f, s) | `-` | copy s bytes from calldata at position f to mem at position t | +-------------------------+------+-----------------------------------------------------------------+ @@ -517,14 +522,14 @@ The opcodes ``pushi`` and ``jumpdest`` cannot be used directly. | create(v, p, s) | | create new contract with code mem[p..(p+s)) and send v wei | | | | and return the new address | +-------------------------+------+-----------------------------------------------------------------+ -| call(g, a, v, in, | | call contract at address a with input mem[in..(in+insize)] | +| call(g, a, v, in, | | call contract at address a with input mem[in..(in+insize)) | | insize, out, outsize) | | providing g gas and v wei and output area | -| | | mem[out..(out+outsize)] returting 1 on error (out of gas) | +| | | mem[out..(out+outsize)) returning 1 on error (out of gas) | +-------------------------+------+-----------------------------------------------------------------+ -| callcode(g, a, v, in, | | identical to call but only use the code from a and stay | +| callcode(g, a, v, in, | | identical to `call` but only use the code from a and stay | | insize, out, outsize) | | in the context of the current contract otherwise | +-------------------------+------+-----------------------------------------------------------------+ -| delegatecall(g, a, in, | | identical to callcode but also keep ``caller`` | +| delegatecall(g, a, in, | | identical to `callcode` but also keep ``caller`` | | insize, out, outsize) | | and ``callvalue`` | +-------------------------+------+-----------------------------------------------------------------+ | return(p, s) | `*` | end execution, return data mem[p..(p+s)) | diff --git a/docs/index.rst b/docs/index.rst index 4b3ace89..a330172e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -70,7 +70,7 @@ Solidity Tools * `Solidity REPL <https://github.com/raineorshine/solidity-repl>`_ Try Solidity instantly with a command-line Solidity console. - + * `solgraph <https://github.com/raineorshine/solgraph>`_ Visualize Solidity control flow and highlight potential security vulnerabilities. diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index 47388c25..ba40c99f 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -73,7 +73,7 @@ to compile Solidity on Ubuntu 14.04 (Trusty Tahr). sudo apt-get -y install build-essential git cmake libgmp-dev libboost-all-dev \ libjsoncpp-dev - + sudo add-apt-repository -y ppa:ethereum/ethereum sudo add-apt-repository -y ppa:ethereum/ethereum-dev sudo apt-get -y update @@ -94,14 +94,14 @@ installed either by adding the Ethereum PPA (Option 1) or by backporting sudo apt-get -y install build-essential git cmake libgmp-dev libboost-all-dev \ libjsoncpp-dev - + # (Option 1) For those willing to add the Ethereum PPA: sudo add-apt-repository -y ppa:ethereum/ethereum sudo add-apt-repository -y ppa:ethereum/ethereum-dev sudo apt-get -y update sudo apt-get -y upgrade sudo apt-get -y install libcryptopp-dev - + ## (Option 2) For those willing to backport libcrypto++: #sudo apt-get -y install ubuntu-dev-tools #sudo pbuilder create diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 0122387b..fbce4244 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -95,7 +95,7 @@ registering with username and password - all you need is an Ethereum keypair. This contract introduces some new concepts, let us go through them one by one. The line ``address public minter;`` declares a state variable of type address -that is publicly accessible. The ``address`` type is a 160 bit value +that is publicly accessible. The ``address`` type is a 160-bit value that does not allow any arithmetic operations. It is suitable for storing addresses of contracts or keypairs belonging to external persons. The keyword ``public`` automatically generates a function that @@ -220,7 +220,7 @@ only the person holding the keys to the account can transfer money from it. Blocks ====== -One major obstacle to overcome is what in bitcoin terms is called "double-spend attack": +One major obstacle to overcome is what, in Bitcoin terms, is called "double-spend attack": What happens if two transactions exist in the network that both want to empty an account, a so-called conflict? @@ -236,7 +236,7 @@ Ethereum this is roughly every 17 seconds. As part of the "order selection mechanism" (which is called "mining") it may happen that blocks are reverted from time to time, but only at the "tip" of the chain. The more -blocks are reverted the less likely it is. So it might be that your transactions +blocks that are added on top, the less likely it is. So it might be that your transactions are reverted and even removed from the blockchain, but the longer you wait, the less likely it will be. @@ -277,7 +277,7 @@ of transactions sent from that address, the so-called "nonce"). Apart from the fact whether an account stores code or not, the EVM treats the two types equally, though. -Every account has a persistent key-value store mapping 256 bit words to 256 bit +Every account has a persistent key-value store mapping 256-bit words to 256-bit words called **storage**. Furthermore, every account has a **balance** in @@ -300,7 +300,7 @@ If the target account is the zero-account (the account with the address ``0``), the transaction creates a **new contract**. As already mentioned, the address of that contract is not the zero address but an address derived from the sender and -its number of transaction sent (the "nonce"). The payload +its number of transactions sent (the "nonce"). The payload of such a contract creation transaction is taken to be EVM bytecode and executed. The output of this execution is permanently stored as the code of the contract. @@ -332,7 +332,7 @@ Storage, Memory and the Stack ============================= Each account has a persistent memory area which is called **storage**. -Storage is a key-value store that maps 256 bit words to 256 bit words. +Storage is a key-value store that maps 256-bit words to 256-bit words. It is not possible to enumerate storage from within a contract and it is comparatively costly to read and even more so, to modify storage. A contract can neither read nor write to any storage apart @@ -340,7 +340,7 @@ from its own. The second memory area is called **memory**, of which a contract obtains a freshly cleared instance for each message call. Memory can be -addressed at byte level, but read and written to in 32 byte (256 bit) +addressed at byte level, but read and written to in 32 byte (256-bit) chunks. Memory is more costly the larger it grows (it scales quadratically). @@ -364,7 +364,7 @@ Instruction Set The instruction set of the EVM is kept minimal in order to avoid incorrect implementations which could cause consensus problems. -All instructions operate on the basic data type, 256 bit words. +All instructions operate on the basic data type, 256-bit words. The usual arithmetic, bit, logical and comparison operations are present. Conditional and unconditional jumps are possible. Furthermore, contracts can access relevant properties of the current block diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst index ef6fd656..6ef06961 100644 --- a/docs/layout-of-source-files.rst +++ b/docs/layout-of-source-files.rst @@ -61,7 +61,7 @@ It depends on the compiler (see below) how to actually resolve the paths. In general, the directory hierarchy does not need to strictly map onto your local filesystem, it can also map to resources discovered via e.g. ipfs, http or git. -Use in actual Compilers +Use in Actual Compilers ----------------------- When the compiler is invoked, it is not only possible to specify how to @@ -101,7 +101,7 @@ and then run the compiler as As a more complex example, suppose you rely on some module that uses a very old version of dapp-bin. That old version of dapp-bin is checked -out at ``/usr/local/dapp-bin_old``, then you can use +out at ``/usr/local/dapp-bin_old``, then you can use .. code-block:: bash @@ -171,9 +171,9 @@ for the two input parameters and two returned values. * @return s The calculated surface. * @return p The calculated perimeter. */ - function rectangle(uint w, uint h) returns (uint s, uint p){ - s = w*h; - p = 2*(w+h); + function rectangle(uint w, uint h) returns (uint s, uint p) { + s = w * h; + p = 2 * (w + h); } } diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 804d69ef..882a6002 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -66,7 +66,7 @@ Calling ``select(false, x)`` will compute ``x * x`` and ``select(true, x)`` will .. index:: optimizer, common subexpression elimination, constant propagation ************************* -Internals - the Optimizer +Internals - The Optimizer ************************* The Solidity optimizer operates on assembly, so it can be and also is used by other languages. It splits the sequence of instructions into basic blocks at JUMPs and JUMPDESTs. Inside these blocks, the instructions are analysed and every modification to the stack, to memory or storage is recorded as an expression which consists of an instruction and a list of arguments which are essentially pointers to other expressions. The main idea is now to find expressions that are always equal (on every input) and combine them into an expression class. The optimizer first tries to find each new expression in a list of already known expressions. If this does not work, the expression is simplified according to rules like ``constant + constant = sum_of_constants`` or ``X * 1 = X``. Since this is done recursively, we can also apply the latter rule if the second factor is a more complex expression where we know that it will always evaluate to one. Modifications to storage and memory locations have to erase knowledge about storage and memory locations which are not known to be different: If we first write to location x and then to location y and both are input variables, the second could overwrite the first, so we actually do not know what is stored at x after we wrote to y. On the other hand, if a simplification of the expression x - y evaluates to a non-zero constant, we know that we can keep our knowledge about what is stored at x. diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index bae6e20b..f8d099e4 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -1,3 +1,5 @@ +.. _security_considerations: + ####################### Security Considerations ####################### @@ -124,7 +126,7 @@ Sending and Receiving Ether because the operation is just too expensive) - it "runs out of gas" (OOG). If the return value of ``send`` is checked, this might provide a means for the recipient to block progress in the sending contract. Again, the best practice here is to use - a "withdraw" pattern instead of a "send" pattern. + a :ref:`"withdraw" pattern instead of a "send" pattern <withdrawal_pattern>`. Callstack Depth =============== @@ -145,7 +147,7 @@ Never use tx.origin for authorization. Let's say you have a wallet contract like :: - contract TxUserWallet { + contract TxUserWallet { address owner; function TxUserWallet() { @@ -162,7 +164,7 @@ Now someone tricks you into sending ether to the address of this attack wallet: :: - contract TxAttackWallet { + contract TxAttackWallet { address owner; function TxAttackWallet() { diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index 4051cd94..61589b2e 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -191,6 +191,8 @@ contract into a blind auction where it is not possible to see the actual bid until the bidding period ends. +.. _simple_auction: + Simple Open Auction =================== @@ -269,7 +271,7 @@ activate themselves. // highestBidder.send(highestBid) is a security risk // because it can be prevented by the caller by e.g. // raising the call stack to 1023. It is always safer - // to let the recipient withdraw their money themselves. + // to let the recipient withdraw their money themselves. pendingReturns[highestBidder] += highestBid; } highestBidder = msg.sender; |