mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
commit
145d3e4da9
@ -10,12 +10,16 @@ Layout of State Variables in Storage
|
||||
|
||||
.. _storage-inplace-encoding:
|
||||
|
||||
Statically-sized variables (everything except mapping and dynamically-sized array types) are laid out contiguously in storage starting from position ``0``. Multiple, contiguous items that need less than 32 bytes are packed into a single storage slot if possible, according to the following rules:
|
||||
Statically-sized variables (everything except mapping and dynamically-sized
|
||||
array types) are laid out contiguously in storage starting from position ``0``.
|
||||
Multiple, contiguous items that need less than 32 bytes are packed into a single
|
||||
storage slot if possible, according to the following rules:
|
||||
|
||||
- The first item in a storage slot is stored lower-order aligned.
|
||||
- Elementary types use only as many bytes as are necessary to store them.
|
||||
- If an elementary type does not fit the remaining part of a storage slot, it is moved to the next storage slot.
|
||||
- Structs and array data always start a new slot and occupy whole slots (but items inside a struct or array are packed tightly according to these rules).
|
||||
- Structs and array data always start a new slot and occupy whole slots
|
||||
(but items inside a struct or array are packed tightly according to these rules).
|
||||
|
||||
For contracts that use inheritance, the ordering of state variables is determined by the
|
||||
C3-linearized order of contracts starting with the most base-ward contract. If allowed
|
||||
@ -54,17 +58,22 @@ Mappings and Dynamic Arrays
|
||||
.. _storage-hashed-encoding:
|
||||
|
||||
Due to their unpredictable size, mapping and dynamically-sized array types use a Keccak-256 hash
|
||||
computation to find the starting position of the value or the array data. These starting positions are always full stack slots.
|
||||
computation to find the starting position of the value or the array data.
|
||||
These starting positions are always full stack slots.
|
||||
|
||||
The mapping or the dynamic array itself occupies a slot in storage at some position ``p``
|
||||
according to the above rule (or by recursively applying this rule for mappings of mappings or arrays of arrays). For dynamic arrays,
|
||||
this slot stores the number of elements in the array (byte arrays and strings are an exception, see :ref:`below <bytes-and-string>`).
|
||||
according to the above rule (or by recursively applying this rule for
|
||||
mappings of mappings or arrays of arrays). For dynamic arrays,
|
||||
this slot stores the number of elements in the array (byte arrays and
|
||||
strings are an exception, see :ref:`below <bytes-and-string>`).
|
||||
For mappings, the slot is unused (but it is needed so that two equal mappings after each other will use a different
|
||||
hash distribution). Array data is located at ``keccak256(p)`` and the value corresponding to a mapping key
|
||||
``k`` is located at ``keccak256(k . p)`` where ``.`` is concatenation. If the value is again a
|
||||
non-elementary type, the positions are found by adding an offset of ``keccak256(k . p)``.
|
||||
|
||||
So for the following contract snippet::
|
||||
So for the following contract snippet
|
||||
the position of ``data[4][9].b`` is at ``keccak256(uint256(9) . keccak256(uint256(4) . uint256(1))) + 1``::
|
||||
|
||||
|
||||
pragma solidity >=0.4.0 <0.7.0;
|
||||
|
||||
@ -75,8 +84,6 @@ So for the following contract snippet::
|
||||
mapping(uint => mapping(uint => S)) data;
|
||||
}
|
||||
|
||||
The position of ``data[4][9].b`` is at ``keccak256(uint256(9) . keccak256(uint256(4) . uint256(1))) + 1``.
|
||||
|
||||
.. _bytes-and-string:
|
||||
|
||||
``bytes`` and ``string``
|
||||
@ -97,7 +104,8 @@ JSON Output
|
||||
|
||||
.. _storage-layout-top-level:
|
||||
|
||||
The storage layout of a contract can be requested via the :ref:`standard JSON interface <compiler-api>`. The output is a JSON object containing two keys,
|
||||
The storage layout of a contract can be requested via
|
||||
the :ref:`standard JSON interface <compiler-api>`. The output is a JSON object containing two keys,
|
||||
``storage`` and ``types``. The ``storage`` object is an array where each
|
||||
element has the following form:
|
||||
|
||||
@ -114,7 +122,7 @@ element has the following form:
|
||||
"type": "t_uint256"
|
||||
}
|
||||
|
||||
where the example above is the storage layout of ``contract A { uint x; }`` from source unit ``fileA``
|
||||
The example above is the storage layout of ``contract A { uint x; }`` from source unit ``fileA``
|
||||
and
|
||||
|
||||
- ``astId`` is the id of the AST node of the state variable's declaration
|
||||
@ -148,7 +156,8 @@ where
|
||||
- ``bytes``: single slot or Keccak-256 hash-based depending on the data size (see :ref:`above <bytes-and-string>`).
|
||||
|
||||
- ``label`` is the canonical type name.
|
||||
- ``numberOfBytes`` is the number of used bytes (as a decimal string). Note that if ``numberOfBytes > 32`` this means that more than one slot is used.
|
||||
- ``numberOfBytes`` is the number of used bytes (as a decimal string).
|
||||
Note that if ``numberOfBytes > 32`` this means that more than one slot is used.
|
||||
|
||||
Some types have extra information besides the four above. Mappings contain
|
||||
its ``key`` and ``value`` types (again referencing an entry in this mapping
|
||||
@ -369,7 +378,8 @@ Scratch space can be used between statements (i.e. within inline assembly). The
|
||||
is used as initial value for dynamic memory arrays and should never be written to
|
||||
(the free memory pointer points to ``0x80`` initially).
|
||||
|
||||
Solidity always places new objects at the free memory pointer and memory is never freed (this might change in the future).
|
||||
Solidity always places new objects at the free memory pointer and
|
||||
memory is never freed (this might change in the future).
|
||||
|
||||
Elements in memory arrays in Solidity always occupy multiples of 32 bytes (this
|
||||
is even true for ``byte[]``, but not for ``bytes`` and ``string``).
|
||||
@ -378,9 +388,16 @@ dynamic array is stored at the first slot of the array and followed by the array
|
||||
elements.
|
||||
|
||||
.. warning::
|
||||
There are some operations in Solidity that need a temporary memory area larger than 64 bytes and therefore will not fit into the scratch space. They will be placed where the free memory points to, but given their short lifetime, the pointer is not updated. The memory may or may not be zeroed out. Because of this, one shouldn't expect the free memory to point to zeroed out memory.
|
||||
There are some operations in Solidity that need a temporary memory area
|
||||
larger than 64 bytes and therefore will not fit into the scratch space.
|
||||
They will be placed where the free memory points to, but given their
|
||||
short lifetime, the pointer is not updated. The memory may or may not
|
||||
be zeroed out. Because of this, one should not expect the free memory
|
||||
to point to zeroed out memory.
|
||||
|
||||
While it may seem like a good idea to use ``msize`` to arrive at a definitely zeroed out memory area, using such a pointer non-temporarily without updating the free memory pointer can have adverse results.
|
||||
While it may seem like a good idea to use ``msize`` to arrive at a
|
||||
definitely zeroed out memory area, using such a pointer non-temporarily
|
||||
without updating the free memory pointer can have unexpected results.
|
||||
|
||||
.. index: calldata layout
|
||||
|
||||
@ -404,11 +421,11 @@ data to the code.
|
||||
Internals - Cleaning Up Variables
|
||||
*********************************
|
||||
|
||||
When a value is shorter than 256-bit, in some cases the remaining bits
|
||||
When a value is shorter than 256 bit, in some cases the remaining bits
|
||||
must be cleaned.
|
||||
The Solidity compiler is designed to clean such remaining bits before any operations
|
||||
that might be adversely affected by the potential garbage in the remaining bits.
|
||||
For example, before writing a value to the memory, the remaining bits need
|
||||
For example, before writing a value to memory, the remaining bits need
|
||||
to be cleared because the memory contents can be used for computing
|
||||
hashes or sent as the data of a message call. Similarly, before
|
||||
storing a value in the storage, the remaining bits need to be cleaned
|
||||
@ -452,13 +469,54 @@ Different types have different rules for cleaning up invalid values:
|
||||
Internals - The Optimiser
|
||||
*************************
|
||||
|
||||
The Solidity optimiser operates on assembly so that other languages can use it. It splits the sequence of instructions into basic blocks at ``JUMPs`` and ``JUMPDESTs``. Inside these blocks, the optimiser analyses the instructions and records every modification to the stack, memory, or storage as an expression which consists of an instruction and a list of arguments which are pointers to other expressions. The optimiser uses a component called "CommonSubexpressionEliminator" that amongst other tasks, finds expressions that are always equal (on every input) and combines them into an expression class. The optimiser first tries to find each new expression in a list of already known expressions. If this does not work, it simplifies the expression according to rules like ``constant + constant = sum_of_constants`` or ``X * 1 = X``. Since this is a recursive process, we can also apply the latter rule if the second factor is a more complex expression where we know that it always evaluates 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 do not know what is stored at x after we wrote to y. If 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.
|
||||
This section discusses the optimiser that was first added to Solidity,
|
||||
which operates on opcode streams. For information on the new Yul-based optimiser,
|
||||
please see the `readme on github <https://github.com/ethereum/solidity/blob/develop/libyul/optimiser/README.md>`_.
|
||||
|
||||
After this process, we know which expressions have to be on the stack at the end, and have a list of modifications to memory and storage. This information is stored together with the basic blocks and is used to link them. Furthermore, knowledge about the stack, storage and memory configuration is forwarded to the next block(s). If we know the targets of all ``JUMP`` and ``JUMPI`` instructions, we can build a complete control flow graph of the program. If there is only one target we do not know (this can happen as in principle, jump targets can be computed from inputs), we have to erase all knowledge about the input state of a block as it can be the target of the unknown ``JUMP``. If the optimiser finds a ``JUMPI`` whose condition evaluates to a constant, it transforms it to an unconditional jump.
|
||||
The Solidity optimiser operates on assembly. It splits the sequence of instructions into basic blocks
|
||||
at ``JUMPs`` and ``JUMPDESTs``. Inside these blocks, the optimiser
|
||||
analyses the instructions and records every modification to the stack,
|
||||
memory, or storage as an expression which consists of an instruction and
|
||||
a list of arguments which are pointers to other expressions. The optimiser
|
||||
uses a component called "CommonSubexpressionEliminator" that amongst other
|
||||
tasks, finds expressions that are always equal (on every input) and combines
|
||||
them into an expression class. The optimiser first tries to find each new
|
||||
expression in a list of already known expressions. If this does not work,
|
||||
it simplifies the expression according to rules like
|
||||
``constant + constant = sum_of_constants`` or ``X * 1 = X``. Since this is
|
||||
a recursive process, we can also apply the latter rule if the second factor
|
||||
is a more complex expression where we know that it always evaluates 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 do not know what is stored at x after
|
||||
we wrote to y. If 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.
|
||||
|
||||
As the last step, the code in each block is re-generated. The optimiser creates a dependency graph from the expressions on the stack at the end of the block, and it drops every operation that is not part of this graph. It generates code that applies the modifications to memory and storage in the order they were made in the original code (dropping modifications which were found not to be needed). Finally, it generates all values that are required to be on the stack in the correct place.
|
||||
After this process, we know which expressions have to be on the stack at
|
||||
the end, and have a list of modifications to memory and storage. This information
|
||||
is stored together with the basic blocks and is used to link them. Furthermore,
|
||||
knowledge about the stack, storage and memory configuration is forwarded to
|
||||
the next block(s). If we know the targets of all ``JUMP`` and ``JUMPI`` instructions,
|
||||
we can build a complete control flow graph of the program. If there is only
|
||||
one target we do not know (this can happen as in principle, jump targets can
|
||||
be computed from inputs), we have to erase all knowledge about the input state
|
||||
of a block as it can be the target of the unknown ``JUMP``. If the optimiser
|
||||
finds a ``JUMPI`` whose condition evaluates to a constant, it transforms it
|
||||
to an unconditional jump.
|
||||
|
||||
These steps are applied to each basic block and the newly generated code is used as replacement if it is smaller. If a basic block is split at a ``JUMPI`` and during the analysis, the condition evaluates to a constant, the ``JUMPI`` is replaced depending on the value of the constant. Thus code like
|
||||
As the last step, the code in each block is re-generated. The optimiser creates
|
||||
a dependency graph from the expressions on the stack at the end of the block,
|
||||
and it drops every operation that is not part of this graph. It generates code
|
||||
that applies the modifications to memory and storage in the order they were
|
||||
made in the original code (dropping modifications which were found not to be
|
||||
needed). Finally, it generates all values that are required to be on the
|
||||
stack in the correct place.
|
||||
|
||||
These steps are applied to each basic block and the newly generated code
|
||||
is used as replacement if it is smaller. If a basic block is split at a
|
||||
``JUMPI`` and during the analysis, the condition evaluates to a constant,
|
||||
the ``JUMPI`` is replaced depending on the value of the constant. Thus code like
|
||||
|
||||
::
|
||||
|
||||
@ -545,13 +603,18 @@ Tips and Tricks
|
||||
***************
|
||||
|
||||
* Use ``delete`` on arrays to delete all its elements.
|
||||
* Use shorter types for struct elements and sort them such that short types are grouped together. This can lower the gas costs as multiple ``SSTORE`` operations might be combined into a single (``SSTORE`` costs 5000 or 20000 gas, so this is what you want to optimise). Use the gas price estimator (with optimiser enabled) to check!
|
||||
* Use shorter types for struct elements and sort them such that short types are
|
||||
grouped together. This can lower the gas costs as multiple ``SSTORE`` operations
|
||||
might be combined into a single (``SSTORE`` costs 5000 or 20000 gas, so this is
|
||||
what you want to optimise). Use the gas price estimator (with optimiser enabled) to check!
|
||||
* Make your state variables public - the compiler creates :ref:`getters <visibility-and-getters>` for you automatically.
|
||||
* If you end up checking conditions on input or state a lot at the beginning of your functions, try using :ref:`modifiers`.
|
||||
* Initialize storage structs with a single assignment: ``x = MyStruct({a: 1, b: 2});``
|
||||
|
||||
.. note::
|
||||
If the storage struct has tightly packed properties, initialize it with separate assignments: ``x.a = 1; x.b = 2;``. In this way it will be easier for the optimizer to update storage in one go, thus making assignment cheaper.
|
||||
If the storage struct has tightly packed properties, initialize it with separate
|
||||
assignments: ``x.a = 1; x.b = 2;``. In this way it will be easier for the
|
||||
optimizer to update storage in one go, thus making assignment cheaper.
|
||||
|
||||
**********
|
||||
Cheatsheet
|
||||
@ -627,12 +690,16 @@ The following is the order of precedence for operators, listed in order of evalu
|
||||
Global Variables
|
||||
================
|
||||
|
||||
- ``abi.decode(bytes memory encodedData, (...)) returns (...)``: :ref:`ABI <ABI>`-decodes the provided data. The types are given in parentheses as second argument. Example: ``(uint a, uint[2] memory b, bytes memory c) = abi.decode(data, (uint, uint[2], bytes))``
|
||||
- ``abi.decode(bytes memory encodedData, (...)) returns (...)``: :ref:`ABI <ABI>`-decodes
|
||||
the provided data. The types are given in parentheses as second argument.
|
||||
Example: ``(uint a, uint[2] memory b, bytes memory c) = abi.decode(data, (uint, uint[2], bytes))``
|
||||
- ``abi.encode(...) returns (bytes memory)``: :ref:`ABI <ABI>`-encodes the given arguments
|
||||
- ``abi.encodePacked(...) returns (bytes memory)``: Performs :ref:`packed encoding <abi_packed_mode>` of the given arguments. Note that this encoding can be ambiguous!
|
||||
- ``abi.encodeWithSelector(bytes4 selector, ...) returns (bytes memory)``: :ref:`ABI <ABI>`-encodes the given arguments
|
||||
starting from the second and prepends the given four-byte selector
|
||||
- ``abi.encodeWithSignature(string memory signature, ...) returns (bytes memory)``: Equivalent to ``abi.encodeWithSelector(bytes4(keccak256(bytes(signature)), ...)```
|
||||
- ``abi.encodePacked(...) returns (bytes memory)``: Performs :ref:`packed encoding <abi_packed_mode>` of
|
||||
the given arguments. Note that this encoding can be ambiguous!
|
||||
- ``abi.encodeWithSelector(bytes4 selector, ...) returns (bytes memory)``: :ref:`ABI <ABI>`-encodes
|
||||
the given arguments starting from the second and prepends the given four-byte selector
|
||||
- ``abi.encodeWithSignature(string memory signature, ...) returns (bytes memory)``: Equivalent
|
||||
to ``abi.encodeWithSelector(bytes4(keccak256(bytes(signature)), ...)```
|
||||
- ``block.coinbase`` (``address payable``): current block miner's address
|
||||
- ``block.difficulty`` (``uint``): current block difficulty
|
||||
- ``block.gaslimit`` (``uint``): current block gaslimit
|
||||
@ -646,22 +713,28 @@ Global Variables
|
||||
- ``tx.gasprice`` (``uint``): gas price of the transaction
|
||||
- ``tx.origin`` (``address payable``): sender of the transaction (full call chain)
|
||||
- ``assert(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for internal error)
|
||||
- ``require(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for malformed input or error in external component)
|
||||
- ``require(bool condition, string memory message)``: abort execution and revert state changes if condition is ``false`` (use for malformed input or error in external component). Also provide error message.
|
||||
- ``require(bool condition)``: abort execution and revert state changes if condition is ``false`` (use
|
||||
for malformed input or error in external component)
|
||||
- ``require(bool condition, string memory message)``: abort execution and revert state changes if
|
||||
condition is ``false`` (use for malformed input or error in external component). Also provide error message.
|
||||
- ``revert()``: abort execution and revert state changes
|
||||
- ``revert(string memory message)``: abort execution and revert state changes providing an explanatory string
|
||||
- ``blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent blocks
|
||||
- ``keccak256(bytes memory) returns (bytes32)``: compute the Keccak-256 hash of the input
|
||||
- ``sha256(bytes memory) returns (bytes32)``: compute the SHA-256 hash of the input
|
||||
- ``ripemd160(bytes memory) returns (bytes20)``: compute the RIPEMD-160 hash of the input
|
||||
- ``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``: recover address associated with the public key from elliptic curve signature, return zero on error
|
||||
- ``addmod(uint x, uint y, uint k) returns (uint)``: compute ``(x + y) % k`` where the addition is performed with arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0.
|
||||
- ``mulmod(uint x, uint y, uint k) returns (uint)``: compute ``(x * y) % k`` where the multiplication is performed with arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0.
|
||||
- ``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``: recover address associated with
|
||||
the public key from elliptic curve signature, return zero on error
|
||||
- ``addmod(uint x, uint y, uint k) returns (uint)``: compute ``(x + y) % k`` where the addition is performed with
|
||||
arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0.
|
||||
- ``mulmod(uint x, uint y, uint k) returns (uint)``: compute ``(x * y) % k`` where the multiplication is performed
|
||||
with arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0.
|
||||
- ``this`` (current contract's type): the current contract, explicitly convertible to ``address`` or ``address payable``
|
||||
- ``super``: the contract one level higher in the inheritance hierarchy
|
||||
- ``selfdestruct(address payable recipient)``: destroy the current contract, sending its funds to the given address
|
||||
- ``<address>.balance`` (``uint256``): balance of the :ref:`address` in Wei
|
||||
- ``<address payable>.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, returns ``false`` on failure
|
||||
- ``<address payable>.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`,
|
||||
returns ``false`` on failure
|
||||
- ``<address payable>.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure
|
||||
- ``type(C).name`` (``string``): the name of the contract
|
||||
- ``type(C).creationCode`` (``bytes memory``): creation bytecode of the given contract, see :ref:`Type Information<meta-type>`.
|
||||
@ -717,16 +790,20 @@ Modifiers
|
||||
- ``constant`` for state variables: Disallows assignment (except initialisation), does not occupy storage slot.
|
||||
- ``anonymous`` for events: Does not store event signature as topic.
|
||||
- ``indexed`` for event parameters: Stores the parameter as topic.
|
||||
- ``virtual`` for functions and modifiers: Allows the function's or modifier's
|
||||
behaviour to be changed in derived contracts.
|
||||
- ``override``: States that this function, modifier or public state variable changes
|
||||
the behaviour of a function or modifier in a base contract.
|
||||
|
||||
Reserved Keywords
|
||||
=================
|
||||
|
||||
These keywords are reserved in Solidity. They might become part of the syntax in the future:
|
||||
|
||||
``abstract``, ``after``, ``alias``, ``apply``, ``auto``, ``case``, ``catch``, ``copyof``, ``default``,
|
||||
``after``, ``alias``, ``apply``, ``auto``, ``case``, ``copyof``, ``default``,
|
||||
``define``, ``final``, ``immutable``, ``implements``, ``in``, ``inline``, ``let``, ``macro``, ``match``,
|
||||
``mutable``, ``null``, ``of``, ``partial``, ``promise``, ``reference``, ``relocatable``,
|
||||
``sealed``, ``sizeof``, ``static``, ``supports``, ``switch``, ``try``, ``typedef``, ``typeof``,
|
||||
``sealed``, ``sizeof``, ``static``, ``supports``, ``switch``, ``typedef``, ``typeof``,
|
||||
``unchecked``.
|
||||
|
||||
Language Grammar
|
||||
|
Loading…
Reference in New Issue
Block a user