diff options
Diffstat (limited to 'docs/introduction-to-smart-contracts.rst')
-rw-r--r-- | docs/introduction-to-smart-contracts.rst | 124 |
1 files changed, 70 insertions, 54 deletions
diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index d1789c44..7de0d19b 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -33,14 +33,14 @@ Storage The first line simply tells that the source code is written for Solidity version 0.4.0 or anything newer that does not break functionality (up to, but not including, version 0.5.0). This is to ensure that the -contract does not suddenly behave differently with a new compiler version. The keyword ``pragma`` is called that way because, in general, -pragmas are instructions for the compiler about how to treat the +contract is not compilable with a new (breaking) compiler version, where it could behave differently. +So-called pragmas are common instrutions for compilers about how to treat the source code (e.g. `pragma once <https://en.wikipedia.org/wiki/Pragma_once>`_). A contract in the sense of Solidity is a collection of code (its *functions*) and data (its *state*) that resides at a specific address on the Ethereum blockchain. The line ``uint storedData;`` declares a state variable called ``storedData`` of -type ``uint`` (unsigned integer of 256 bits). You can think of it as a single slot +type ``uint`` (*u*nsigned *int*eger of *256* bits). You can think of it as a single slot in a database that can be queried and altered by calling functions of the code that manages the database. In the case of Ethereum, this is always the owning contract. And in this case, the functions ``set`` and ``get`` can be used to modify @@ -49,8 +49,8 @@ or retrieve the value of the variable. To access a state variable, you do not need the prefix ``this.`` as is common in other languages. -This contract does not do much yet (due to the infrastructure -built by Ethereum) apart from allowing anyone to store a single number that is accessible by +This contract does not do much yet apart from (due to the infrastructure +built by Ethereum) allowing anyone to store a single number that is accessible by anyone in the world without a (feasible) way to prevent you from publishing this number. Of course, anyone could just call ``set`` again with a different value and overwrite your number, but the number will still be stored in the history @@ -62,7 +62,7 @@ so that only you can alter the number. the ASCII character set. It is possible to store UTF-8 encoded data in string variables. .. warning:: - Be careful with using Unicode text as similarly looking (or even identical) characters can + Be careful with using Unicode text, as similar looking (or even identical) characters can have different code points and as such will be encoded as a different byte array. .. index:: ! subcurrency @@ -72,39 +72,40 @@ Subcurrency Example The following contract will implement the simplest form of a cryptocurrency. It is possible to generate coins out of thin air, but -only the person that created the contract will be able to do that (it is trivial +only the person that created the contract will be able to do that (it is easy to implement a different issuance scheme). -Furthermore, anyone can send coins to each other without any need for -registering with username and password - all you need is an Ethereum keypair. +Furthermore, anyone can send coins to each other without a need for +registering with username and password — all you need is an Ethereum keypair. :: - pragma solidity ^0.4.21; + pragma solidity >0.4.24; contract Coin { // The keyword "public" makes those variables - // readable from outside. + // easily readable from outside. address public minter; mapping (address => uint) public balances; - // Events allow light clients to react on + // Events allow light clients to react to // changes efficiently. event Sent(address from, address to, uint amount); // This is the constructor whose code is // run only when the contract is created. - function Coin() public { + constructor() public { minter = msg.sender; } function mint(address receiver, uint amount) public { - if (msg.sender != minter) return; + require(msg.sender == minter); + require(amount < 1e60); balances[receiver] += amount; } function send(address receiver, uint amount) public { - if (balances[msg.sender] < amount) return; + require(amount <= balances[msg.sender], "Insufficient balance."); balances[msg.sender] -= amount; balances[receiver] += amount; emit Sent(msg.sender, receiver, amount); @@ -116,15 +117,15 @@ 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 does not allow any arithmetic operations. It is suitable for -storing addresses of contracts or keypairs belonging to external +storing addresses of contracts or of keypairs belonging to external persons. The keyword ``public`` automatically generates a function that allows you to access the current value of the state variable from outside of the contract. Without this keyword, other contracts have no way to access the variable. The code of the function generated by the compiler is roughly equivalent -to the following:: +to the following (ignore ``external`` and ``view`` for now):: - function minter() returns (address) { return minter; } + function minter() external view returns (address) { return minter; } Of course, adding a function exactly like that will not work because we would have a @@ -137,17 +138,17 @@ The next line, ``mapping (address => uint) public balances;`` also creates a public state variable, but it is a more complex datatype. The type maps addresses to unsigned integers. Mappings can be seen as `hash tables <https://en.wikipedia.org/wiki/Hash_table>`_ which are -virtually initialized such that every possible key exists and is mapped to a +virtually initialized such that every possible key exists from the start and is mapped to a value whose byte-representation is all zeros. This analogy does not go too far, though, as it is neither possible to obtain a list of all keys of a mapping, nor a list of all values. So either keep in mind (or better, keep a list or use a more advanced data type) what you -added to the mapping or use it in a context where this is not needed, -like this one. The :ref:`getter function<getter-functions>` created by the ``public`` keyword +added to the mapping or use it in a context where this is not needed. +The :ref:`getter function<getter-functions>` created by the ``public`` keyword is a bit more complex in this case. It roughly looks like the following:: - function balances(address _account) public view returns (uint) { + function balances(address _account) external view returns (uint) { return balances[_account]; } @@ -162,7 +163,9 @@ a so-called "event" which is emitted in the last line of the function listen for those events being emitted on the blockchain without much cost. As soon as it is emitted, the listener will also receive the arguments ``from``, ``to`` and ``amount``, which makes it easy to track -transactions. In order to listen for this event, you would use :: +transactions. In order to listen for this event, you would use the following +JavaScript code (which assumes that ``Coin`` is a contract object created via +web3.js or a similar module):: Coin.Sent().watch({}, '', function(error, result) { if (!error) { @@ -180,23 +183,34 @@ the user interface. .. index:: coin -The special function ``Coin`` is the -constructor which is run during creation of the contract and +The constructor is a special function which is run during creation of the contract and cannot be called afterwards. It permanently stores the address of the person creating the -contract: ``msg`` (together with ``tx`` and ``block``) is a magic global variable that +contract: ``msg`` (together with ``tx`` and ``block``) is a special global variable that contains some properties which allow access to the blockchain. ``msg.sender`` is always the address where the current (external) function call came from. Finally, the functions that will actually end up with the contract and can be called by users and contracts alike are ``mint`` and ``send``. If ``mint`` is called by anyone except the account that created the contract, -nothing will happen. On the other hand, ``send`` can be used by anyone (who already -has some of these coins) to send coins to anyone else. Note that if you use -this contract to send coins to an address, you will not see anything when you -look at that address on a blockchain explorer, because the fact that you sent -coins and the changed balances are only stored in the data storage of this -particular coin contract. By the use of events it is relatively easy to create -a "blockchain explorer" that tracks transactions and balances of your new coin. +nothing will happen. This is ensured by the special function ``require`` which +causes all changes to be reverted if its argument evaluates to false. +The second call to ``require`` ensures that there will not be too many coins, +which could cause overflow errors later. + +On the other hand, ``send`` can be used by anyone (who already +has some of these coins) to send coins to anyone else. If you do not have +enough coins to send, the ``require`` call will fail and also provide the +user with an appropriate error message string. + +.. note:: + If you use + this contract to send coins to an address, you will not see anything when you + look at that address on a blockchain explorer, because the fact that you sent + coins and the changed balances are only stored in the data storage of this + particular coin contract. By the use of events it is relatively easy to create + a "blockchain explorer" that tracks transactions and balances of your new coin, + but you have to inspect the coin contract address and not the addresses of the + coin owners. .. _blockchain-basics: @@ -260,6 +274,10 @@ blocks that are added on top, the less likely it is. So it might be that your tr are reverted and even removed from the blockchain, but the longer you wait, the less likely it will be. +.. note:: + Transactions are not guaranteed to happen on the next block or any future specific block, since it is up to the miners to include transactions and not the submitter of the transaction. This applies to function calls and contract creation transactions alike. + + If you want to schedule future calls of your contract, you can use the `alarm clock <http://www.ethereum-alarm-clock.com/>`_ service. .. _the-ethereum-virtual-machine: @@ -352,19 +370,21 @@ If the gas is used up at any point (i.e. it is negative), an out-of-gas exception is triggered, which reverts all modifications made to the state in the current call frame. +Any unused gas is refunded at the end of the transaction. + .. index:: ! storage, ! memory, ! stack Storage, Memory and the Stack ============================= -Each account has a persistent memory area which is called **storage**. +The Ethereum Virtual Machine has three areas where it can store data. + +Each account has a data area called **storage**, which is persistent between function calls. 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 -from its own. +It is not possible to enumerate storage from within a contract and it is comparatively costly to read, and even more to modify storage. +A contract can neither read nor write to any storage apart from its own. -The second memory area is called **memory**, of which a contract obtains +The second data area is called **memory**, of which a contract obtains a freshly cleared instance for each message call. Memory is linear and can be addressed at byte level, but reads are limited to a width of 256 bits, while writes can be either 8 bits or 256 bits wide. Memory is expanded by a word (256-bit), when @@ -373,7 +393,7 @@ within a word). At the time of expansion, the cost in gas must be paid. Memory i costly the larger it grows (it scales quadratically). The EVM is not a register machine but a stack machine, so all -computations are performed on an area called the **stack**. It has a maximum size of +computations are performed on an data area called the **stack**. It has a maximum size of 1024 elements and contains words of 256 bits. Access to the stack is limited to the top end in the following way: It is possible to copy one of @@ -412,7 +432,7 @@ a top-level message call which in turn can create further message calls. A contract can decide how much of its remaining **gas** should be sent with the inner message call and how much it wants to retain. If an out-of-gas exception happens in the inner call (or any -other exception), this will be signalled by an error value put onto the stack. +other exception), this will be signaled by an error value put onto the stack. In this case, only the gas sent together with the call is used up. In Solidity, the calling contract causes a manual exception by default in such situations, so that exceptions "bubble up" the call stack. @@ -470,21 +490,17 @@ these **create calls** and normal message calls is that the payload data is executed and the result stored as code and the caller / creator receives the address of the new contract on the stack. -.. index:: selfdestruct +.. index:: selfdestruct, self-destruct, deactivate -Self-destruct -============= +Deactivate and Self-destruct +============================ -The only possibility that code is removed from the blockchain is -when a contract at that address performs the ``selfdestruct`` operation. -The remaining Ether stored at that address is sent to a designated -target and then the storage and code is removed from the state. +The only way to remove code from the blockchain is when a contract at that address performs the ``selfdestruct`` operation. The remaining Ether stored at that address is sent to a designated target and then the storage and code is removed from the state. Removing the contract in theory sounds like a good idea, but it is potentially dangerous, as if someone sends Ether to removed contracts, the Ether is forever lost. -.. warning:: Even if a contract's code does not contain a call to ``selfdestruct``, - it can still perform that operation using ``delegatecall`` or ``callcode``. +.. note:: + Even if a contract's code does not contain a call to ``selfdestruct``, it can still perform that operation using ``delegatecall`` or ``callcode``. -.. note:: The pruning of old contracts may or may not be implemented by Ethereum - clients. Additionally, archive nodes could choose to keep the contract storage - and code indefinitely. +If you want to deactivate your contracts, you should instead **disable** them by changing some internal state which causes all functions to revert. This makes it impossible to use the contract, as it returns Ether immediately. -.. note:: Currently **external accounts** cannot be removed from the state. +.. warning:: + Even if a contract is removed by "selfdestruct", it is still part of the history of the blockchain and probably retained by most Ethereum nodes. So using "selfdestruct" is not the same as deleting data from a hard disk. |