From df0b54d7c76e80d510247095ba3487c633e01b37 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Mon, 16 Sep 2019 19:55:17 +0200 Subject: [PATCH] [DOCS] Clearing mappings --- docs/security-considerations.rst | 55 ++++++++++++++++++++++++++++++++ docs/types/mapping-types.rst | 3 +- 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index 989a6d830..3f18bf3b5 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -254,6 +254,61 @@ if you want all overflows to cause a revert. Code such as ``require((balanceOf[_to] + _value) >= balanceOf[_to])`` can also help you check if values are what you expect. +.. _clearing-mappings: + +Clearing Mappings +================= + +The Solidity type ``mapping`` (see :ref:`mapping-types`) is a storage-only key-value data structure that +does not keep track of the keys that were assigned a non-zero value. +Because of that, cleaning a mapping without extra information about the written +keys is not possible. +If a ``mapping`` is used as the base type of a dynamic storage array, deleting +or popping the array will have no effect over the ``mapping`` elements. +The same happens, for example, if a ``mapping`` is used as the type of a member +field of a ``struct`` that is the base type of a dynamic storage array. +The ``mapping`` is also ignored in assignments of structs or arrays containing +a ``mapping``. + +:: + + pragma solidity >=0.5.0 <0.7.0; + + contract Map { + mapping (uint => uint)[] array; + + function allocate(uint _newMaps) public { + array.length += _newMaps; + } + + function writeMap(uint _map, uint _key, uint _value) public { + array[_map][_key] = _value; + } + + function readMap(uint _map, uint _key) public view returns (uint) { + return array[_map][_key]; + } + + function eraseMaps() public { + delete array; + } + } + +Consider the example above and the following sequence of calls: ``allocate(10)``, +``writeMap(4, 128, 256)``. +At this point, calling ``readMap(4, 128)`` returns 256. +If we call ``eraseMaps``, the length of state variable ``array`` is zeroed, but +since its ``mapping`` elements cannot be zeroed, their information stays alive +in the contract's storage. +After deleting ``array``, calling ``allocate(5)`` allows us to access +``array[4]`` again, and calling ``readMap(4, 128)`` returns 256 even without +another call to ``writeMap``. + +If your ``mapping`` information must be deleted, consider using a library similar to +`iterable mapping `_, +allowing you to traverse the keys and delete their values in the appropriate +``mapping``. + Minor Details ============= diff --git a/docs/types/mapping-types.rst b/docs/types/mapping-types.rst index afee61202..a7d607786 100644 --- a/docs/types/mapping-types.rst +++ b/docs/types/mapping-types.rst @@ -17,7 +17,8 @@ byte-representation is all zeros, a type's :ref:`default value `. mapping, only its ``keccak256`` hash is used to look up the value. Because of this, mappings do not have a length or a concept of a key or -value being set. +value being set, and therefore cannot be erased without extra information +regarding the assigned keys (see :ref:`clearing-mappings`). Mappings can only have a data location of ``storage`` and thus are allowed for state variables, as storage reference types