diff options
author | Chris Ward <chris.ward@ethereum.org> | 2019-01-08 01:05:27 +0800 |
---|---|---|
committer | Chris Ward <chris.ward@ethereum.org> | 2019-01-08 01:13:32 +0800 |
commit | 13cd96136aa499303a4c2ed92848366ebd0b9343 (patch) | |
tree | 89b7bbcd9c4d1984abbef0511d73c0dedc4f0a32 /docs/contracts/libraries.rst | |
parent | a2926cd9dcdddf681b87471cef8ed0c83c3aefd3 (diff) | |
download | dexon-solidity-13cd96136aa499303a4c2ed92848366ebd0b9343.tar.gz dexon-solidity-13cd96136aa499303a4c2ed92848366ebd0b9343.tar.zst dexon-solidity-13cd96136aa499303a4c2ed92848366ebd0b9343.zip |
Split libraries into new doc
Diffstat (limited to 'docs/contracts/libraries.rst')
-rw-r--r-- | docs/contracts/libraries.rst | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/docs/contracts/libraries.rst b/docs/contracts/libraries.rst new file mode 100644 index 00000000..0cabe18a --- /dev/null +++ b/docs/contracts/libraries.rst @@ -0,0 +1,230 @@ +.. index:: ! library, callcode, delegatecall + +.. _libraries: + +********* +Libraries +********* + +Libraries are similar to contracts, but their purpose is that they are deployed +only once at a specific address and their code is reused using the ``DELEGATECALL`` +(``CALLCODE`` until Homestead) +feature of the EVM. This means that if library functions are called, their code +is executed in the context of the calling contract, i.e. ``this`` points to the +calling contract, and especially the storage from the calling contract can be +accessed. As a library is an isolated piece of source code, it can only access +state variables of the calling contract if they are explicitly supplied (it +would have no way to name them, otherwise). Library functions can only be +called directly (i.e. without the use of ``DELEGATECALL``) if they do not modify +the state (i.e. if they are ``view`` or ``pure`` functions), +because libraries are assumed to be stateless. In particular, it is +not possible to destroy a library. + +.. note:: + Until version 0.4.20, it was possible to destroy libraries by + circumventing Solidity's type system. Starting from that version, + libraries contain a :ref:`mechanism<call-protection>` that + disallows state-modifying functions + to be called directly (i.e. without ``DELEGATECALL``). + +Libraries can be seen as implicit base contracts of the contracts that use them. +They will not be explicitly visible in the inheritance hierarchy, but calls +to library functions look just like calls to functions of explicit base +contracts (``L.f()`` if ``L`` is the name of the library). Furthermore, +``internal`` functions of libraries are visible in all contracts, just as +if the library were a base contract. Of course, calls to internal functions +use the internal calling convention, which means that all internal types +can be passed and types :ref:`stored in memory <data-location>` will be passed by reference and not copied. +To realize this in the EVM, code of internal library functions +and all functions called from therein will at compile time be pulled into the calling +contract, and a regular ``JUMP`` call will be used instead of a ``DELEGATECALL``. + +.. index:: using for, set + +The following example illustrates how to use libraries (but manual method +be sure to check out :ref:`using for <using-for>` for a +more advanced example to implement a set). + +:: + + pragma solidity >=0.4.22 <0.6.0; + + library Set { + // We define a new struct datatype that will be used to + // hold its data in the calling contract. + struct Data { mapping(uint => bool) flags; } + + // Note that the first parameter is of type "storage + // reference" and thus only its storage address and not + // its contents is passed as part of the call. This is a + // special feature of library functions. It is idiomatic + // to call the first parameter `self`, if the function can + // be seen as a method of that object. + function insert(Data storage self, uint value) + public + returns (bool) + { + if (self.flags[value]) + return false; // already there + self.flags[value] = true; + return true; + } + + function remove(Data storage self, uint value) + public + returns (bool) + { + if (!self.flags[value]) + return false; // not there + self.flags[value] = false; + return true; + } + + function contains(Data storage self, uint value) + public + view + returns (bool) + { + return self.flags[value]; + } + } + + contract C { + Set.Data knownValues; + + function register(uint value) public { + // The library functions can be called without a + // specific instance of the library, since the + // "instance" will be the current contract. + require(Set.insert(knownValues, value)); + } + // In this contract, we can also directly access knownValues.flags, if we want. + } + +Of course, you do not have to follow this way to use +libraries: they can also be used without defining struct +data types. Functions also work without any storage +reference parameters, and they can have multiple storage reference +parameters and in any position. + +The calls to ``Set.contains``, ``Set.insert`` and ``Set.remove`` +are all compiled as calls (``DELEGATECALL``) to an external +contract/library. If you use libraries, be aware that an +actual external function call is performed. +``msg.sender``, ``msg.value`` and ``this`` will retain their values +in this call, though (prior to Homestead, because of the use of ``CALLCODE``, ``msg.sender`` and +``msg.value`` changed, though). + +The following example shows how to use :ref:`types stored in memory <data-location>` and +internal functions in libraries in order to implement +custom types without the overhead of external function calls: + +:: + + pragma solidity >=0.4.16 <0.6.0; + + library BigInt { + struct bigint { + uint[] limbs; + } + + function fromUint(uint x) internal pure returns (bigint memory r) { + r.limbs = new uint[](1); + r.limbs[0] = x; + } + + function add(bigint memory _a, bigint memory _b) internal pure returns (bigint memory r) { + r.limbs = new uint[](max(_a.limbs.length, _b.limbs.length)); + uint carry = 0; + for (uint i = 0; i < r.limbs.length; ++i) { + uint a = limb(_a, i); + uint b = limb(_b, i); + r.limbs[i] = a + b + carry; + if (a + b < a || (a + b == uint(-1) && carry > 0)) + carry = 1; + else + carry = 0; + } + if (carry > 0) { + // too bad, we have to add a limb + uint[] memory newLimbs = new uint[](r.limbs.length + 1); + uint i; + for (i = 0; i < r.limbs.length; ++i) + newLimbs[i] = r.limbs[i]; + newLimbs[i] = carry; + r.limbs = newLimbs; + } + } + + function limb(bigint memory _a, uint _limb) internal pure returns (uint) { + return _limb < _a.limbs.length ? _a.limbs[_limb] : 0; + } + + function max(uint a, uint b) private pure returns (uint) { + return a > b ? a : b; + } + } + + contract C { + using BigInt for BigInt.bigint; + + function f() public pure { + BigInt.bigint memory x = BigInt.fromUint(7); + BigInt.bigint memory y = BigInt.fromUint(uint(-1)); + BigInt.bigint memory z = x.add(y); + assert(z.limb(1) > 0); + } + } + +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` 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 +``Set`` is the name of the library). The address can be filled +manually by replacing all those 40 symbols by the hex +encoding of the address of the library contract. + +.. note:: + Manually linking libraries on the generated bytecode is discouraged, because + it is restricted to 36 characters. + You should ask the compiler to link the libraries at the time + a contract is compiled by either using + the ``--libraries`` option of ``solc`` or the ``libraries`` key if you use + the standard-JSON interface to the compiler. + +Restrictions for libraries in comparison to contracts: + +- No state variables +- Cannot inherit nor be inherited +- Cannot receive Ether + +(These might be lifted at a later point.) + +.. _call-protection: + +Call Protection For Libraries +============================= + +As mentioned in the introduction, if a library's code is executed +using a ``CALL`` instead of a ``DELEGATECALL`` or ``CALLCODE``, +it will revert unless a ``view`` or ``pure`` function is called. + +The EVM does not provide a direct way for a contract to detect +whether it was called using ``CALL`` or not, but a contract +can use the ``ADDRESS`` opcode to find out "where" it is +currently running. The generated code compares this address +to the address used at construction time to determine the mode +of calling. + +More specifically, the runtime code of a library always starts +with a push instruction, which is a zero of 20 bytes at +compilation time. When the deploy code runs, this constant +is replaced in memory by the current address and this +modified code is stored in the contract. At runtime, +this causes the deploy time address to be the first +constant to be pushed onto the stack and the dispatcher +code compares the current address against this constant +for any non-view and non-pure function. |