diff options
53 files changed, 4690 insertions, 2436 deletions
diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..07458841 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,11 @@ +# This is used to verify that the documentation compiles. +language: python +python: + - "2.7" +# command to install dependencies +install: "pip install -q Sphinx==1.1.3 --use-mirrors" +# command to run tests +script: cd docs && sphinx-build -nW -b html -d _build/doctrees . _build/html +# Flags used here, not in `make html`: +# -n Run in nit-picky mode. Currently, this generates warnings for all missing references. +# -W Turn warnings into errors. This means that the build stops at the first warning and sphinx-build exits with exit status 1. diff --git a/CMakeLists.txt b/CMakeLists.txt index 8120847b..6d5bd606 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.3.2") +set(PROJECT_VERSION "0.3.5") project(solidity VERSION ${PROJECT_VERSION}) # Let's find our dependencies diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 20efd1b3..00000000 --- a/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 00000000..10926e87 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,675 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. + @@ -4,9 +4,7 @@ To get started you can find a basic introduction to the language in the [Solidity Documentation](https://solidity.readthedocs.org). -You can start using [Solidity in your browser](https://chriseth.github.io/browser-solidity/) with no need to download or compile anything. This application only supports compilation - if you want to inject it into the blockchain, you have to use a client like [Geth](https://github.com/ethereum/go-ethereum/wiki) or [AlethZero](https://github.com/ethereum/alethzero). - -Also check out more documentations for [Solidity ABI](https://github.com/ethereum/wiki/wiki/Solidity,-Docs-and-ABI) and a crowdfunding [example contract](https://github.com/chriseth/cpp-ethereum/wiki/Crowdfunding-example-contract-in-Solidity) written in Solidity. +You can start using [Solidity in your browser](https://ethereum.github.io/browser-solidity/) with no need to download or compile anything. [Changelog](https://github.com/ethereum/wiki/wiki/Solidity-Changelog) @@ -20,16 +18,6 @@ See the [Wiki](https://github.com/ethereum/webthree-umbrella/wiki) for build ins ## How to Contribute -### External Contributors - -I hereby place all my contributions in this codebase under an MIT -licence, as specified [here](http://opensource.org/licenses/MIT). -- *Name Surname* (**email@domain**) - -### Contribution guideline - -Please add yourself in the `@author` doxygen section of the file your are adding/editing -with the same wording as the one you listed yourself in the external contributors section above, -only replacing the word **contribution** by **file** - -Please read [CodingStandards.txt](https://github.com/ethereum/webthree-umbrella/blob/develop/CodingStandards.txt) thoroughly before making alterations to the code base. Please do *NOT* use an editor that automatically reformats whitespace away from astylerc or the formatting guidelines as described in [CodingStandards.txt](https://github.com/ethereum/webthree-umbrella/blob/develop/CodingStandards.txt). +This repository uses the same [coding style](https://github.com/ethereum/webthree-umbrella/blob/develop/CodingStandards.txt) as +all of the cpp-ethereum projects. Please try to align with us in the gitter channel before making larger changes. +Any contributions are welcome! diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst index f4794221..9096571e 100644 --- a/docs/common-patterns.rst +++ b/docs/common-patterns.rst @@ -17,7 +17,7 @@ to read the data, so will everyone else. You can restrict read access to your contract's state by **other contracts**. That is actually the default -unless you declare make your state variables `public`. +unless you declare make your state variables ``public``. Furthermore, you can restrict who can make modifications to your contract's state or call your contract's @@ -28,8 +28,6 @@ functions and this is what this page is about. The use of **function modifiers** makes these restrictions highly readable. -.. {% include open_link gist="fe4ef267cbdeac151b98" %} - :: contract AccessRestriction { @@ -140,11 +138,11 @@ Example ======= In the following example, -the modifier `atStage` ensures that the function can +the modifier ``atStage`` ensures that the function can only be called at a certain stage. Automatic timed transitions -are handled by the modifier `timeTransitions`, which +are handled by the modifier ``timeTransitions``, which should be used for all functions. .. note:: @@ -154,7 +152,7 @@ should be used for all functions. it after the latter, so that the new stage is taken into account. -Finally, the modifier `transitionNext` can be used +Finally, the modifier ``transitionNext`` can be used to automatically go to the next stage when the function finishes. @@ -167,8 +165,6 @@ function finishes. return. If you want to do that, make sure to call nextStage manually from those functions. -.. {% include open_link gist="0a221eaceb6d708bf271" %} - :: contract StateMachine { @@ -179,6 +175,7 @@ function finishes. AreWeDoneYet, Finished } + // This is the current stage. Stages public stage = Stages.AcceptingBlindedBids; @@ -188,9 +185,11 @@ function finishes. if (stage != _stage) throw; _ } + function nextStage() internal { stage = Stages(uint(stage) + 1); } + // Perform timed transitions. Be sure to mention // this modifier first, otherwise the guards // will not take the new stage into account. @@ -203,7 +202,7 @@ function finishes. nextStage(); // The other stages transition by transaction } - + // Order of the modifiers matters here! function bid() timedTransitions @@ -211,6 +210,7 @@ function finishes. { // We will not implement that here } + function reveal() timedTransitions atStage(Stages.RevealBids) @@ -227,6 +227,7 @@ function finishes. _ nextStage(); } + function g() timedTransitions atStage(Stages.AnotherStage) @@ -235,12 +236,14 @@ function finishes. // If you want to use `return` here, // you have to call `nextStage()` manually. } + function h() timedTransitions atStage(Stages.AreWeDoneYet) transitionNext { } + function i() timedTransitions atStage(Stages.Finished) diff --git a/docs/conf.py b/docs/conf.py index 48664344..8776ec43 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -134,7 +134,7 @@ html_theme = 'default' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = [] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied diff --git a/docs/contracts.rst b/docs/contracts.rst index 68905fa4..fbcc858d 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -20,39 +20,40 @@ Contracts can be created "from outside" or from Solidity contracts. When a contract is created, its constructor (a function with the same name as the contract) is executed once. -From `web3.js`, i.e. the JavaScript +From ``web3.js``, i.e. the JavaScript API, this is done as follows:: // The json abi array generated by the compiler var abiArray = [ - { - "inputs":[ - {"name":"x","type":"uint256"}, - {"name":"y","type":"uint256"} - ], - "type":"constructor" - }, - { - "constant":true, - "inputs":[], - "name":"x", - "outputs":[{"name":"","type":"bytes32"}], - "type":"function" - } + { + "inputs":[ + {"name":"x","type":"uint256"}, + {"name":"y","type":"uint256"} + ], + "type":"constructor" + }, + { + "constant":true, + "inputs":[], + "name":"x", + "outputs":[{"name":"","type":"bytes32"}], + "type":"function" + } ]; var MyContract = web3.eth.contract(abiArray); // deploy new contract var contractInstance = MyContract.new( - 10, - {from: myAccount, gas: 1000000} + 10, + 11, + {from: myAccount, gas: 1000000} ); .. index:: constructor;arguments Internally, constructor arguments are passed after the code of the contract itself, but you do not have to care about this -if you use `web3.js`. +if you use ``web3.js``. If a contract wants to create another contract, the source code (and the binary) of the created contract has to be known to the creator. @@ -84,7 +85,8 @@ This means that cyclic creation dependencies are impossible. // Only the creator can alter the name -- // the comparison is possible since contracts // are implicitly convertible to addresses. - if (msg.sender == creator) name = newName; + if (msg.sender == creator) + name = newName; } function transfer(address newOwner) { @@ -141,38 +143,38 @@ a "message call") and external ones that do), there are four types of visibilities for functions and state variables. -Functions can be specified as being `external`, -`public`, `internal` or `private`, where the default is -`public`. For state variables, `external` is not possible -and the default is `internal`. +Functions can be specified as being ``external``, +``public``, ``internal`` or ``private``, where the default is +``public``. For state variables, ``external`` is not possible +and the default is ``internal``. -`external`: +``external``: External functions are part of the contract interface, which means they can be called from other contracts and - via transactions. An external function `f` cannot be called - internally (i.e. `f()` does not work, but `this.f()` works). + via transactions. An external function ``f`` cannot be called + internally (i.e. ``f()`` does not work, but ``this.f()`` works). External functions are sometimes more efficient when they receive large arrays of data. -`public`: +``public``: Public functions are part of the contract interface and can be either called internally or via messages. For public state variables, an automatic accessor function (see below) is generated. -`internal`: +``internal``: Those functions and state variables can only be accessed internally (i.e. from within the current contract - or contracts deriving from it), without using `this`. + or contracts deriving from it), without using ``this``. -`private`: +``private``: Private functions and state variables are only visible for the contract they are defined in and not in derived contracts. .. note:: Everything that is inside a contract is visible to - all external observers. Making something `private` + all external observers. Making something ``private`` only prevents other contract from accessing and modifying the information, but it will still be visible to the whole world outside of the blockchain. @@ -183,15 +185,15 @@ return parameter list for functions. :: - contract c { + contract C { function f(uint a) private returns (uint b) { return a + 1; } function setData(uint a) internal { data = a; } uint public data; } -Other contracts can call `c.data()` to retrieve the value of data in state -storage, but are not able to call `f`. Contracts derived from `c` can call -`setData` to alter the value of `data` (but only in their own state). +Other contracts can call ``c.data()`` to retrieve the value of data in state +storage, but are not able to call ``f``. Contracts derived from ``c`` can call +``setData`` to alter the value of ``data`` (but only in their own state). .. index:: ! accessor;function, ! function;accessor @@ -200,19 +202,19 @@ Accessor Functions The compiler automatically creates accessor functions for all public state variables. The contract given below will -have a function called `data` that does not take any +have a function called ``data`` that does not take any arguments and returns a uint, the value of the state -variable `data`. The initialization of state variables can +variable ``data``. The initialization of state variables can be done at declaration. The accessor functions have external visibility. If the -symbol is accessed internally (i.e. without `this.`), +symbol is accessed internally (i.e. without ``this.``), it is a state variable and if it is accessed externally -(i.e. with `this.`), it is a function. +(i.e. with ``this.``), it is a function. :: - contract test { + contract Test { uint public data = 42; } @@ -220,9 +222,13 @@ The next example is a bit more complex: :: - contract complex { - struct Data { uint a; bytes3 b; mapping(uint => uint) map; } - mapping(uint => mapping(bool => Data[])) public data; + contract Complex { + struct Data { + uint a; + bytes3 b; + mapping (uint => uint) map; + } + mapping (uint => mapping(bool => Data[])) public data; } It will generate a function of the following form:: @@ -260,16 +266,20 @@ inheritable properties of contracts and may be overridden by derived contracts. // This means that if the owner calls this function, the // function is executed and otherwise, an exception is // thrown. - modifier onlyowner { if (msg.sender != owner) throw; _ } + modifier onlyOwner { + if (msg.sender != owner) + throw; + _ + } } contract mortal is owned { - // This contract inherits the "onlyowner"-modifier from + // This contract inherits the "onlyOwner"-modifier from // "owned" and applies it to the "close"-function, which // causes that calls to "close" only have an effect if // they are made by the stored owner. - function close() onlyowner { + function close() onlyOwner { selfdestruct(owner); } } @@ -277,18 +287,25 @@ inheritable properties of contracts and may be overridden by derived contracts. contract priced { // Modifiers can receive arguments: - modifier costs(uint price) { if (msg.value >= price) _ } + modifier costs(uint price) { + if (msg.value >= price) { + _ + } + } } contract Register is priced, owned { mapping (address => bool) registeredAddresses; uint price; + function Register(uint initialPrice) { price = initialPrice; } + function register() costs(price) { registeredAddresses[msg.sender] = true; } - function changePrice(uint _price) onlyowner { + + function changePrice(uint _price) onlyOwner { price = _price; } } @@ -338,7 +355,7 @@ functions matches the given function identifier (or if no data was supplied at all). Furthermore, this function is executed whenever the contract receives plain -Ether (witout data). In such a context, there is very little gas available to +Ether (without data). In such a context, there is very little gas available to the function call, so it is important to make fallback functions as cheap as possible. @@ -359,13 +376,13 @@ possible. contract Caller { - function callTest(address testAddress) { - Test(testAddress).call(0xabcdef01); // hash does not exist - // results in Test(testAddress).x becoming == 1. - Rejector r = Rejector(0x123); - r.send(2 ether); - // results in r.balance == 0 - } + function callTest(address testAddress) { + Test(testAddress).call(0xabcdef01); // hash does not exist + // results in Test(testAddress).x becoming == 1. + Rejector r = Rejector(0x123); + r.send(2 ether); + // results in r.balance == 0 + } } .. index:: ! event @@ -397,15 +414,15 @@ ultimately, also the block headers have to be supplied because the contract can only see the last 256 block hashes). Up to three parameters can -receive the attribute `indexed` which will cause the respective arguments +receive the attribute ``indexed`` which will cause the respective arguments to be searched for: It is possible to filter for specific values of indexed arguments in the user interface. -If arrays (including `string` and `bytes`) are used as indexed arguments, the +If arrays (including ``string`` and ``bytes``) are used as indexed arguments, the sha3-hash of it is stored as topic instead. The hash of the signature of the event is one of the topics except if you -declared the event with `anonymous` specifier. This means that it is +declared the event with ``anonymous`` specifier. This means that it is not possible to filter for specific anonymous events by name. All non-indexed arguments will be stored in the data part of the log. @@ -458,8 +475,8 @@ Low-Level Interface to Logs =========================== It is also possible to access the low-level interface to the logging -mechanism via the functions `log0`, `log1`, `log2`, `log3` and `log4`. -`logi` takes `i + 1` parameter of type `bytes32`, where the first +mechanism via the functions ``log0``, ``log1``, ``log2``, ``log3`` and ``log4``. +``logi`` takes ``i + 1`` parameter of type ``bytes32``, where the first argument will be used for the data part of the log and the others as topics. The event call above can be performed in the same way as @@ -473,7 +490,7 @@ as topics. The event call above can be performed in the same way as ); where the long hexadecimal number is equal to -`sha3("Deposit(address,hash256,uint256)")`, the signature of the event. +``sha3("Deposit(address,hash256,uint256)")``, the signature of the event. Additional Resources for Understanding Events ============================================== @@ -574,7 +591,7 @@ Details are given in the following example. uint info; } -Note that above, we call `mortal.kill()` to "forward" the +Note that above, we call ``mortal.kill()`` to "forward" the destruction request. The way this is done is problematic, as seen in the following example:: @@ -598,10 +615,10 @@ seen in the following example:: contract Final is Base1, Base2 { } -A call to `Final.kill()` will call `Base2.kill` as the most +A call to ``Final.kill()`` will call ``Base2.kill`` as the most derived override, but this function will bypass -`Base1.kill`, basically because it does not even know about -`Base1`. The way around this is to use `super`:: +``Base1.kill``, basically because it does not even know about +``Base1``. The way around this is to use ``super``:: contract mortal is owned { function kill() { @@ -623,10 +640,10 @@ derived override, but this function will bypass contract Final is Base2, Base1 { } -If `Base1` calls a function of `super`, it does not simply +If ``Base1`` calls a function of ``super``, it does not simply call this function on one of its base contracts, it rather calls this function on the next base contract in the final -inheritance graph, so it will call `Base2.kill()` (note that +inheritance graph, so it will call ``Base2.kill()`` (note that the final inheritance sequence is -- starting with the most derived contract: Final, Base1, Base2, mortal, owned). The actual function that is called when using super is @@ -653,9 +670,9 @@ the base constructors. This can be done at two places:: } } -Either directly in the inheritance list (`is Base(7)`) or in +Either directly in the inheritance list (``is Base(7)``) or in the way a modifier would be invoked as part of the header of -the derived constructor (`Base(_y * _y)`). The first way to +the derived constructor (``Base(_y * _y)``). The first way to do it is more convenient if the constructor argument is a constant and defines the behaviour of the contract or describes it. The second way has to be used if the @@ -674,7 +691,7 @@ Solidity follows the path of Python and uses "`C3 Linearization <https://en.wiki to force a specific order in the DAG of base classes. This results in the desirable property of monotonicity but disallows some inheritance graphs. Especially, the order in -which the base classes are given in the `is` directive is +which the base classes are given in the ``is`` directive is important. In the following code, Solidity will give the error "Linearization of inheritance graph impossible". @@ -684,9 +701,9 @@ error "Linearization of inheritance graph impossible". contract A is X {} contract C is A, X {} -The reason for this is that `C` requests `X` to override `A` -(by specifying `A, X` in this order), but `A` itself -requests to override `X`, which is a contradiction that +The reason for this is that ``C`` requests ``X`` to override ``A`` +(by specifying ``A, X`` in this order), but ``A`` itself +requests to override ``X``, which is a contradiction that cannot be resolved. A simple rule to remember is to specify the base classes in @@ -698,15 +715,15 @@ the order from "most base-like" to "most derived". Abstract Contracts ****************** -Contract functions can lack an implementation as in the following example (note that the function declaration header is terminated by `;`):: +Contract functions can lack an implementation as in the following example (note that the function declaration header is terminated by ``;``):: - contract feline { + contract Feline { function utterance() returns (bytes32); } Such contracts cannot be compiled (even if they contain implemented functions alongside non-implemented functions), but they can be used as base contracts:: - contract Cat is feline { + contract Cat is Feline { function utterance() returns (bytes32) { return "miaow"; } } @@ -721,26 +738,26 @@ 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) +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 +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 to way to name them, otherwise). +would have no way to name them, otherwise). 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 +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 memory types will be passed by reference and not copied. In order to realise this in the EVM, code of internal library functions (and all functions called from therein) will be pulled into the calling -contract and a regular `JUMP` call will be used instead of a `DELEGATECALL`. +contract and a regular ``JUMP`` call will be used instead of a ``DELEGATECALL``. .. index:: using for, set @@ -806,13 +823,13 @@ data types, functions also work without any storage reference parameters, 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`s) to an external +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, take care 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, `msg.sender` and -`msg.value` changed, though). +``msg.sender``, ``msg.value`` and ``this`` will retain their values +in this call, though (prior to Homestead, ``msg.sender`` and +``msg.value`` changed, though). The following example shows how to use memory types and internal functions in libraries in order to implement @@ -820,65 +837,66 @@ custom types without the overhead of external function calls: :: - library bigint { - struct bigint { - uint[] limbs; - } - - function fromUint(uint x) internal returns (bigint r) { - r.limbs = new uint[](1); - r.limbs[0] = x; - } - - function add(bigint _a, bigint _b) internal returns (bigint 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); - for (i = 0; i < r.limbs.length; ++i) - newLimbs[i] = r.limbs[i]; - newLimbs[i] = carry; - r.limbs = newLimbs; - } - } - - function limb(bigint _a, uint _limb) internal returns (uint) { - return _limb < _a.limbs.length ? _a.limbs[_limb] : 0; - } - - function max(uint a, uint b) private returns (uint) { - return a > b ? a : b; - } - } - - - contract C { - using bigint for bigint.bigint; - function f() { - var x = bigint.fromUint(7); - var y = bigint.fromUint(uint(-1)); - var z = x.add(y); - } - } + library BigInt { + struct bigint { + uint[] limbs; + } + + function fromUint(uint x) internal returns (bigint r) { + r.limbs = new uint[](1); + r.limbs[0] = x; + } + + function add(bigint _a, bigint _b) internal returns (bigint 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); + for (i = 0; i < r.limbs.length; ++i) + newLimbs[i] = r.limbs[i]; + newLimbs[i] = carry; + r.limbs = newLimbs; + } + } + + function limb(bigint _a, uint _limb) internal returns (uint) { + return _limb < _a.limbs.length ? _a.limbs[_limb] : 0; + } + + function max(uint a, uint b) private returns (uint) { + return a > b ? a : b; + } + } + + + contract C { + using BigInt for BigInt.bigint; + + function f() { + var x = bigint.fromUint(7); + var y = bigint.fromUint(uint(-1)); + var z = x.add(y); + } + } 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 [Using the Commandline -Compiler](#using-the-commandline-compiler) on how to use the +final bytecode by a linker +(see :ref:`commandline-compiler`) on 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 +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. @@ -897,14 +915,14 @@ Restrictions for libraries in comparison to contracts: Using For ********* -The directive `using A for B;` can be used to attach library -functions (from the library `A`) to any type (`B`). +The directive ``using A for B;`` can be used to attach library +functions (from the library ``A``) to any type (``B``). These functions will receive the object they are called on -as their first parameter (like the `self` variable in +as their first parameter (like the ``self`` variable in Python). -The effect of `using A for *;` is that the functions from -the library `A` are attached to any type. +The effect of ``using A for *;`` is that the functions from +the library ``A`` are attached to any type. In both situations, all functions, even those where the type of the first parameter does not match the type of @@ -912,7 +930,7 @@ the object, are attached. The type is checked at the point the function is called and function overload resolution is performed. -The `using A for B;` directive is active for the current +The ``using A for B;`` directive is active for the current scope, which is limited to a contract for now but will be lifted to the global scope later, so that by including a module, its data types including library functions are @@ -996,5 +1014,5 @@ It is also possible to extend elementary types in that way:: Note that all library calls are actual EVM function calls. This means that if you pass memory or value types, a copy will be performed, even of the -`self` variable. The only situation where no copy will be performed +``self`` variable. The only situation where no copy will be performed is when storage reference variables are used. diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 2d959d1d..2f867cb0 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -8,15 +8,15 @@ Control Structures =================== Most of the control structures from C/JavaScript are available in Solidity -except for `switch` and `goto`. So -there is: `if`, `else`, `while`, `for`, `break`, `continue`, `return`, `? :`, with +except for ``switch`` and ``goto``. So +there is: ``if``, ``else``, ``while``, ``for``, ``break``, ``continue``, ``return``, ``? :``, with the usual semantics known from C / JavaScript. Parentheses can *not* be omitted for conditionals, but curly brances can be omitted around single-statement bodies. Note that there is no type conversion from non-boolean to boolean types as -there is in C and JavaScript, so `if (1) { ... }` is *not* valid Solidity. +there is in C and JavaScript, so ``if (1) { ... }`` is *not* valid Solidity. .. index:: ! function;call, function;internal, function;external @@ -31,9 +31,9 @@ Internal Function Calls Functions of the current contract can be called directly ("internally"), also recursively, as seen in this nonsensical example:: - contract c { - function g(uint a) returns (uint ret) { return f(); } - function f() returns (uint ret) { return g(7) + f(); } + contract C { + function g(uint a) returns (uint ret) { return f(); } + function f() returns (uint ret) { return g(7) + f(); } } These function calls are translated into simple jumps inside the EVM. This has @@ -44,26 +44,28 @@ contract can be called internally. External Function Calls ----------------------- -The expression `this.g(8);` is also a valid function call, but this time, the function +The expression ``this.g(8);`` is also a valid function call, but this time, the function will be called "externally", via a message call and not directly via jumps. Functions of other contracts have to be called externally. For an external call, all function arguments have to be copied to memory. When calling functions of other contracts, the amount of Wei sent with the call and the gas can be specified:: - + contract InfoFeed { - function info() returns (uint ret) { return 42; } + function info() returns (uint ret) { return 42; } } + + contract Consumer { - InfoFeed feed; - function setFeed(address addr) { feed = InfoFeed(addr); } - function callFeed() { feed.info.value(10).gas(800)(); } + InfoFeed feed; + function setFeed(address addr) { feed = InfoFeed(addr); } + function callFeed() { feed.info.value(10).gas(800)(); } } -Note that the expression `InfoFeed(addr)` performs an explicit type conversion stating -that "we know that the type of the contract at the given address is `InfoFeed`" and -this does not execute a constructor. We could also have used `function setFeed(InfoFeed _feed) { feed = _feed; }` directly. Be careful about the fact that `feed.info.value(10).gas(800)` +Note that the expression ``InfoFeed(addr)`` performs an explicit type conversion stating +that "we know that the type of the contract at the given address is ``InfoFeed``" and +this does not execute a constructor. We could also have used ``function setFeed(InfoFeed _feed) { feed = _feed; }`` directly. Be careful about the fact that ``feed.info.value(10).gas(800)`` only (locally) sets the value and amount of gas sent with the function call and only the parentheses at the end perform the actual call. @@ -75,16 +77,18 @@ of unused parameters (especially return parameters) can be omitted. :: - contract c { - function f(uint key, uint value) { ... } - function g() { - // named arguments - f({value: 2, key: 3}); - } - // omitted parameters - function func(uint k, uint) returns(uint) { - return k; - } + contract C { + function f(uint key, uint value) { ... } + + function g() { + // named arguments + f({value: 2, key: 3}); + } + + // omitted parameters + function func(uint k, uint) returns(uint) { + return k; + } } Order of Evaluation of Expressions @@ -109,51 +113,110 @@ Destructuring Assignments and Returning Multiple Values Solidity internally allows tuple types, i.e. a list of objects of potentially different types whose size is a constant at compile-time. Those tuples can be used to return multiple values at the same time and also assign them to multiple variables (or LValues in general) at the same time:: contract C { - uint[] data; - function f() returns (uint, bool, uint) { - return (7, true, 2); - } - function g() { - // Declares and assigns the variables. Specifying the type explicitly is not possible. - var (x, b, y) = f(); - // Assigns to a pre-existing variable. - (x, y) = (2, 7); - // Common trick to swap values -- does not work for non-value storage types. - (x, y) = (y, x); - // Components can be left out (also for variable declarations). - // If the tuple ends in an empty component, - // the rest of the values are discarded. - (data.length,) = f(); // Sets the length to 7 - // The same can be done on the left side. - (,data[3]) = f(); // Sets data[3] to 2 - // Components can only be left out at the left-hand-side of assignments, with - // one exception: - (x,) = (1,); - // (1,) is the only way to specify a 1-component tuple, because (1) is - // equivalent to 1. - } + uint[] data; + + function f() returns (uint, bool, uint) { + return (7, true, 2); + } + + function g() { + // Declares and assigns the variables. Specifying the type explicitly is not possible. + var (x, b, y) = f(); + // Assigns to a pre-existing variable. + (x, y) = (2, 7); + // Common trick to swap values -- does not work for non-value storage types. + (x, y) = (y, x); + // Components can be left out (also for variable declarations). + // If the tuple ends in an empty component, + // the rest of the values are discarded. + (data.length,) = f(); // Sets the length to 7 + // The same can be done on the left side. + (,data[3]) = f(); // Sets data[3] to 2 + // Components can only be left out at the left-hand-side of assignments, with + // one exception: + (x,) = (1,); + // (1,) is the only way to specify a 1-component tuple, because (1) is + // equivalent to 1. + } } Complications for Arrays and Structs ------------------------------------ The semantics of assignment are a bit more complicated for non-value types like arrays and structs. -Assigning *to* a state variable always creates an independent copy. On the other hand, assigning to a local variable creates an independent copy only for elementary types, i.e. static types that fit into 32 bytes. If structs or arrays (including `bytes` and `string`) are assigned from a state variable to a local variable, the local variable holds a reference to the original state variable. A second assignment to the local variable does not modify the state but only changes the reference. Assignments to members (or elements) of the local variable *do* change the state. +Assigning *to* a state variable always creates an independent copy. On the other hand, assigning to a local variable creates an independent copy only for elementary types, i.e. static types that fit into 32 bytes. If structs or arrays (including ``bytes`` and ``string``) are assigned from a state variable to a local variable, the local variable holds a reference to the original state variable. A second assignment to the local variable does not modify the state but only changes the reference. Assignments to members (or elements) of the local variable *do* change the state. .. index:: ! exception, ! throw +Scoping and Declarations +======================== + +.. index:: ! scoping, ! declarations + +In Solidity, a variable declared anywhere within a function will be in scope for the *entire function*, regardless of where it is declared. +This happens because Solidity inherits its scoping rules from JavaScript. +This is in contrast to many languages where variables are only scoped where they are declared until the end of the semantic block. +As a result, the following code is illegal and cause the compiler to throw an error, ``Identifier already declared``:: + + contract ScopingErrors { + function scoping() { + uint i = 0; + + while (i++ < 1) { + uint same1 = 0; + } + + while (i++ < 2) { + uint same1 = 0;// Illegal, second declaration of same1 + } + } + + function minimalScoping() { + { + uint same2 = 0; + } + + { + uint same2 = 0;// Illegal, second declaration of same2 + } + } + + function forLoopScoping() { + for (uint same3 = 0; same3 < 1; same3++) { + } + + for (uint same3 = 0; same3 < 1; same3++) {// Illegal, second declaration of same3 + } + } + } + +In addition to this, if a variable is declared, it will be initialized at the beginning of the function to its default value. +As a result, the following code is legal, despite being poorly written:: + + function foo() returns (uint) { + // baz is implicitly initialized as 0 + uint bar = 5; + if (true) { + bar += baz; + } + else { + uint baz = 10;// never executes + } + return bar;// returns 5 + } + Exceptions ========== -There are some cases where exceptions are thrown automatically (see below). You can use the `throw` instruction to throw an exception manually. The effect of an exception is that the currently executing call is stopped and reverted (i.e. all changes to the state and balances are undone) and the exception is also "bubbled up" through Solidity function calls (exceptions are `send` and the low-level functions `call`, `delegatecall` and `callcode`, those return `false` in case of an exception). +There are some cases where exceptions are thrown automatically (see below). You can use the ``throw`` instruction to throw an exception manually. The effect of an exception is that the currently executing call is stopped and reverted (i.e. all changes to the state and balances are undone) and the exception is also "bubbled up" through Solidity function calls (exceptions are ``send`` and the low-level functions ``call``, ``delegatecall`` and ``callcode``, those return ``false`` in case of an exception). Catching exceptions is not yet possible. -In the following example, we show how `throw` can be used to easily revert an Ether transfer and also how to check the return value of `send`:: +In the following example, we show how ``throw`` can be used to easily revert an Ether transfer and also how to check the return value of ``send``:: contract Sharer { function sendHalf(address addr) returns (uint balance) { - if (!addr.send(msg.value/2)) + if (!addr.send(msg.value / 2)) throw; // also reverts the transfer to Sharer return this.balance; } @@ -161,7 +224,7 @@ In the following example, we show how `throw` can be used to easily revert an Et Currently, there are three 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. @@ -179,83 +242,87 @@ often hard to address the correct stack slot and provide arguments to opcodes at point on the stack. Solidity's inline assembly tries to facilitate that and other issues arising when writing manual assembly by the following features: -* functional-style opcodes: `mul(1, add(2, 3))` instead of `push1 3 push1 2 add push1 1 mul` -* assembly-local variables: `let x := add(2, 3) let y := mload(0x40) x := add(x, y)` -* access to external variables: `function f(uint x) { assembly { x := sub(x, 1) } }` -* labels: `let x := 10 repeat: x := sub(x, 1) jumpi(repeat, eq(x, 0))` +* functional-style opcodes: ``mul(1, add(2, 3))`` instead of ``push1 3 push1 2 add push1 1 mul`` +* assembly-local variables: ``let x := add(2, 3) let y := mload(0x40) x := add(x, y)`` +* access to external variables: ``function f(uint x) { assembly { x := sub(x, 1) } }`` +* labels: ``let x := 10 repeat: x := sub(x, 1) jumpi(repeat, eq(x, 0))`` We now want to describe the inline assembly language in detail. .. warning:: - Inline assembly is still a relatively new feature and might change if it does not prove useful, - so please try to keep up to date. + Inline assembly is still a relatively new feature and might change if it does not prove useful, + so please try to keep up to date. Example ------- The following example provides library code to access the code of another contract and -load it into a `bytes` variable. This is not possible at all with "plain Solidity" and the +load it into a ``bytes`` variable. This is not possible at all with "plain Solidity" and the idea is that assembly libraries will be used to enhance the language in such ways. .. code:: - library GetCode { - function at(address _addr) returns (bytes o_code) { - assembly { - // retrieve the size of the code, this needs assembly - let size := extcodesize(_addr) - // allocate output byte array - this could also be done without assembly - // by using o_code = new bytes(size) - o_code := mload(0x40) - // new "memory end" including padding - mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), bnot(0x1f)))) - // store length in memory - mstore(o_code, size) - // actually retrieve the code, this needs assembly - extcodecopy(_addr, add(o_code, 0x20), 0, size) - } - } - } - -Inline assemmbly could also be beneficial in cases where the optimizer fails to produce + library GetCode { + function at(address _addr) returns (bytes o_code) { + assembly { + // retrieve the size of the code, this needs assembly + let size := extcodesize(_addr) + // allocate output byte array - this could also be done without assembly + // by using o_code = new bytes(size) + o_code := mload(0x40) + // new "memory end" including padding + mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f)))) + // store length in memory + mstore(o_code, size) + // actually retrieve the code, this needs assembly + extcodecopy(_addr, add(o_code, 0x20), 0, size) + } + } + } + +Inline assembly could also be beneficial in cases where the optimizer fails to produce efficient code. Please be aware that assembly is much more difficult to write because the compiler does not perform checks, so you should use it for complex things only if you really know what you are doing. .. code:: - library VectorSum { - // This function is less efficient because the optimizer currently fails to - // remove the bounds checks in array access. - function sumSolidity(uint[] _data) returns (uint o_sum) { - for (uint i = 0; i < _data.length; ++i) - o_sum += _data[i]; - } - // We know that we only access the array in bounds, so we can avoid the check. - // 0x20 needs to be added to an array because the first slot contains the - // array length. - function sumAsm(uint[] _data) returns (uint o_sum) { - for (uint i = 0; i < _data.length; ++i) - assembly { o_sum := mload(add(add(_data, 0x20), i)) } - } - } + library VectorSum { + // This function is less efficient because the optimizer currently fails to + // remove the bounds checks in array access. + function sumSolidity(uint[] _data) returns (uint o_sum) { + for (uint i = 0; i < _data.length; ++i) + o_sum += _data[i]; + } + + // We know that we only access the array in bounds, so we can avoid the check. + // 0x20 needs to be added to an array because the first slot contains the + // array length. + function sumAsm(uint[] _data) returns (uint o_sum) { + for (uint i = 0; i < _data.length; ++i) { + assembly { + o_sum := mload(add(add(_data, 0x20), i)) + } + } + } + } Syntax ------ Inline assembly parses comments, literals and identifiers exactly as Solidity, so you can use the -usual `//` and `/* */` comments. Inline assembly is initiated by `assembly { ... }` and inside +usual ``//`` and ``/* */`` comments. Inline assembly is initiated by ``assembly { ... }`` and inside these curly braces, the following can be used (see the later sections for more details) - - literals, i.e. `0x123`, `42` or `"abc"` (strings up to 32 characters) - - opcodes (in "instruction style"), e.g. `mload sload dup1 sstore`, for a list see below - - opcode in functional style, e.g. `add(1, mlod(0))` - - labels, e.g. `name:` - - variable declarations, e.g. `let x := 7` or `let x := add(y, 3)` - - identifiers (externals, labels or assembly-local variables), e.g. `jump(name)`, `3 x add` - - assignments (in "instruction style"), e.g. `3 =: x` - - assignments in functional style, e.g. `x := add(y, 3)` - - blocks where local variables are scoped inside, e.g. `{ let x := 3 { let y := add(x, 1) } }` + - literals, i.e. ``0x123``, ``42`` or ``"abc"`` (strings up to 32 characters) + - opcodes (in "instruction style"), e.g. ``mload sload dup1 sstore``, for a list see below + - opcode in functional style, e.g. ``add(1, mlod(0))`` + - labels, e.g. ``name:`` + - variable declarations, e.g. ``let x := 7`` or ``let x := add(y, 3)`` + - identifiers (externals, labels or assembly-local variables), e.g. ``jump(name)``, ``3 x add`` + - assignments (in "instruction style"), e.g. ``3 =: x`` + - assignments in functional style, e.g. ``x := add(y, 3)`` + - blocks where local variables are scoped inside, e.g. ``{ let x := 3 { let y := add(x, 1) } }`` Opcodes ------- @@ -265,177 +332,177 @@ following list can be used as a reference of its opcodes. If an opcode takes arguments (always from the top of the stack), they are given in parentheses. Note that the order of arguments can be seed to be reversed in non-functional style (explained below). -Opcodes marked with `-` do not push an item onto the stack, those marked with `*` are +Opcodes marked with ``-`` do not push an item onto the stack, those marked with ``*`` are special and all others push exactly one item onte the stack. -In the following, `mem[a...b)` signifies the bytes of memory starting at position `a` up to -(excluding) position `b` and `storage[p]` signifies the storage contents at position `p`. - -The opcodes `pushi` and `jumpdest` cannot be used directly. - -+-----------------------+------+---------------------------------------------------------------+ -| stop + `-` | stop execution, identical to return(0,0) | -+-----------------------+------+---------------------------------------------------------------+ -| add(x, y) | | x + y | -+-----------------------+------+---------------------------------------------------------------+ -| sub(x, y) | | x - y | -+-----------------------+------+---------------------------------------------------------------+ -| mul(x, y) | | x * y | -+-----------------------+------+---------------------------------------------------------------+ -| div(x, y) | | x / y | -+-----------------------+------+---------------------------------------------------------------+ -| sdiv(x, y) | | x / y, for signed numbers in two's complement | -+-----------------------+------+---------------------------------------------------------------+ -| mod(x, y) | | x % y | -+-----------------------+------+---------------------------------------------------------------+ -| smod(x, y) | | x % y, for signed numbers in two's complement | -+-----------------------+------+---------------------------------------------------------------+ -| exp(x, y) | | x to the power of y | -+-----------------------+------+---------------------------------------------------------------+ -| bnot(x) | | ~x, every bit of x is negated | -+-----------------------+------+---------------------------------------------------------------+ -| lt(x, y) | | 1 if x < y, 0 otherwise | -+-----------------------+------+---------------------------------------------------------------+ -| gt(x, y) | | 1 if x > y, 0 otherwise | -+-----------------------+------+---------------------------------------------------------------+ -| slt(x, y) | |1 if x < y, 0 otherwise, for signed numbers in two's complement| -+-----------------------+------+---------------------------------------------------------------+ -| sgt(x, y) | |1 if x > y, 0 otherwise, for signed numbers in two's complement| -+-----------------------+------+---------------------------------------------------------------+ -| eq(x, y) | | 1 if x == y, 0 otherwise | -+-----------------------+------+---------------------------------------------------------------+ -| not(x) | | 1 if x == 0, 0 otherwise | -+-----------------------+------+---------------------------------------------------------------+ -| and(x, y) | | bitwise and of x and y | -+-----------------------+------+---------------------------------------------------------------+ -| or(x, y) | | bitwise or of x and y | -+-----------------------+------+---------------------------------------------------------------+ -| xor(x, y) | | bitwise xor of x and y | -+-----------------------+------+---------------------------------------------------------------+ -| byte(n, x) | | nth byte of x, where the most significant byte is the 0th byte| -+-----------------------+------+---------------------------------------------------------------+ -| addmod(x, y, m) | | (x + y) % m with arbitrary precision arithmetics | -+-----------------------+------+---------------------------------------------------------------+ -| mulmod(x, y, m) | | (x * y) % m with arbitrary precision arithmetics | -+-----------------------+------+---------------------------------------------------------------+ -| signextend(i, x) | | sign extend from (i*8+7)th bit counting from least significant| -+-----------------------+------+---------------------------------------------------------------+ -| sha3(p, n) | | keccak(mem[p...(p+n))) | -+-----------------------+------+---------------------------------------------------------------+ -| jump(label) | `-` | jump to label / code position | -+-----------------------+------+---------------------------------------------------------------+ -| jumpi(label, cond) | `-` | jump to label if cond is nonzero | -+-----------------------+------+---------------------------------------------------------------+ -| pc | | current position in code | -+-----------------------+------+---------------------------------------------------------------+ -| pop | `*` | remove topmost stack slot | -+-----------------------+------+---------------------------------------------------------------+ -| dup1 ... dup16 | | copy ith stack slot to the top (counting from top) | -+-----------------------+------+---------------------------------------------------------------+ -| swap1 ... swap1 | `*` | swap topmost and ith stack slot below it | -+-----------------------+------+---------------------------------------------------------------+ -| mload(p) | | mem[p..(p+32)) | -+-----------------------+------+---------------------------------------------------------------+ -| mstore(p, v) | `-` | mem[p..(p+32)) := v | -+-----------------------+------+---------------------------------------------------------------+ -| mstore8(p, v) | `-` | mem[p] := v & 0xff - only modifies a single byte | -+-----------------------+------+---------------------------------------------------------------+ -| sload(p) | | storage[p] | -+-----------------------+------+---------------------------------------------------------------+ -| sstore(p, v) | `-` | storage[p] := v | -+-----------------------+------+---------------------------------------------------------------+ -| msize | | size of memory, i.e. largest accessed memory index | -+-----------------------+------+---------------------------------------------------------------+ -| gas | | gas still available to execution | -+-----------------------+------+---------------------------------------------------------------+ -| address | | address of the current contract / execution context | -+-----------------------+------+---------------------------------------------------------------+ -| balance(a) | | wei balance at address a | -+-----------------------+------+---------------------------------------------------------------+ -| caller | | call sender (excluding delegatecall) | -+-----------------------+------+---------------------------------------------------------------+ -| callvalue | | wei sent together with the current call | -+-----------------------+------+---------------------------------------------------------------+ -| calldataload(p) | | call data starting from position p (32 bytes) | -+-----------------------+------+---------------------------------------------------------------+ -| calldatasize | | size of call data in bytes | -+-----------------------+------+---------------------------------------------------------------+ -| calldatacopy(t, f, s) | `-` | copy s bytes from calldata at position f to mem at position t | -+-----------------------+------+---------------------------------------------------------------+ -| codesize | | size of the code of the current contract / execution context | -+-----------------------+------+---------------------------------------------------------------+ -| codecopy(t, f, s) | `-` | copy s bytes from code at position f to mem at position t | -+-----------------------+------+---------------------------------------------------------------+ -| extcodesize(a) | | size of the code at address a | -+-----------------------+------+---------------------------------------------------------------+ -|extcodecopy(a, t, f, s)| `-` | like codecopy(t, f, s) but take code at address a | -+-----------------------+------+---------------------------------------------------------------+ -| 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)] | -| insize, out, outsize) | | providing g gas and v wei and output area | -| | | mem[out..(out+outsize)] returting 1 on error (out of gas) | -+-----------------------+------+---------------------------------------------------------------+ -| 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` and `callvalue` | -| insize, out, outsize) | | | -+-----------------------+------+---------------------------------------------------------------+ -| return(p, s) | `*` | end execution, return data mem[p..(p+s)) | -+-----------------------+------+---------------------------------------------------------------+ -| selfdestruct(a) | `*` | end execution, destroy current contract and send funds to a | -+-----------------------+------+---------------------------------------------------------------+ -| log0(p, s) | `-` | log without topics and data mem[p..(p+s)) | -+-----------------------+------+---------------------------------------------------------------+ -| log1(p, s, t1) | `-` | log with topic t1 and data mem[p..(p+s)) | -+-----------------------+------+---------------------------------------------------------------+ -| log2(p, s, t1, t2) | `-` | log with topics t1, t2 and data mem[p..(p+s)) | -+-----------------------+------+---------------------------------------------------------------+ -| log3(p, s, t1, t2, t3)| `-` | log with topics t1, t2, t3 and data mem[p..(p+s)) | -+-----------------------+------+---------------------------------------------------------------+ -| log4(p, s, t1, t2, t3,| `-` | log with topics t1, t2, t3, t4 and data mem[p..(p+s)) | -| t4) | | | -+-----------------------+------+---------------------------------------------------------------+ -| origin | | transaction sender | -+-----------------------+------+---------------------------------------------------------------+ -| gasprice | | gas price of the transaction | -+-----------------------+------+---------------------------------------------------------------+ -| blockhash(b) | |hash of block nr b - only for last 256 blocks excluding current| -+-----------------------+------+---------------------------------------------------------------+ -| coinbase | | current mining beneficiary | -+-----------------------+------+---------------------------------------------------------------+ -| timestamp | | timestamp of the current block in seconds since the epoch | -+-----------------------+------+---------------------------------------------------------------+ -| number | | current block number | -+-----------------------+------+---------------------------------------------------------------+ -| difficulty | | difficulty of the current block | -+-----------------------+------+---------------------------------------------------------------+ -| gaslimit | | block gas limit of the current block | -+-----------------------+------+---------------------------------------------------------------+ +In the following, ``mem[a...b)`` signifies the bytes of memory starting at position ``a`` up to +(excluding) position ``b`` and ``storage[p]`` signifies the storage contents at position ``p``. + +The opcodes ``pushi`` and ``jumpdest`` cannot be used directly. + ++-------------------------+------+-----------------------------------------------------------------+ +| stop + `-` | stop execution, identical to return(0,0) | ++-------------------------+------+-----------------------------------------------------------------+ +| add(x, y) | | x + y | ++-------------------------+------+-----------------------------------------------------------------+ +| sub(x, y) | | x - y | ++-------------------------+------+-----------------------------------------------------------------+ +| mul(x, y) | | x * y | ++-------------------------+------+-----------------------------------------------------------------+ +| div(x, y) | | x / y | ++-------------------------+------+-----------------------------------------------------------------+ +| sdiv(x, y) | | x / y, for signed numbers in two's complement | ++-------------------------+------+-----------------------------------------------------------------+ +| mod(x, y) | | x % y | ++-------------------------+------+-----------------------------------------------------------------+ +| smod(x, y) | | x % y, for signed numbers in two's complement | ++-------------------------+------+-----------------------------------------------------------------+ +| exp(x, y) | | x to the power of y | ++-------------------------+------+-----------------------------------------------------------------+ +| not(x) | | ~x, every bit of x is negated | ++-------------------------+------+-----------------------------------------------------------------+ +| lt(x, y) | | 1 if x < y, 0 otherwise | ++-------------------------+------+-----------------------------------------------------------------+ +| gt(x, y) | | 1 if x > y, 0 otherwise | ++-------------------------+------+-----------------------------------------------------------------+ +| slt(x, y) | | 1 if x < y, 0 otherwise, for signed numbers in two's complement | ++-------------------------+------+-----------------------------------------------------------------+ +| sgt(x, y) | | 1 if x > y, 0 otherwise, for signed numbers in two's complement | ++-------------------------+------+-----------------------------------------------------------------+ +| eq(x, y) | | 1 if x == y, 0 otherwise | ++-------------------------+------+-----------------------------------------------------------------+ +| iszero(x) | | 1 if x == 0, 0 otherwise | ++-------------------------+------+-----------------------------------------------------------------+ +| and(x, y) | | bitwise and of x and y | ++-------------------------+------+-----------------------------------------------------------------+ +| or(x, y) | | bitwise or of x and y | ++-------------------------+------+-----------------------------------------------------------------+ +| xor(x, y) | | bitwise xor of x and y | ++-------------------------+------+-----------------------------------------------------------------+ +| byte(n, x) | | nth byte of x, where the most significant byte is the 0th byte | ++-------------------------+------+-----------------------------------------------------------------+ +| addmod(x, y, m) | | (x + y) % m with arbitrary precision arithmetics | ++-------------------------+------+-----------------------------------------------------------------+ +| mulmod(x, y, m) | | (x * y) % m with arbitrary precision arithmetics | ++-------------------------+------+-----------------------------------------------------------------+ +| signextend(i, x) | | sign extend from (i*8+7)th bit counting from least significant | ++-------------------------+------+-----------------------------------------------------------------+ +| sha3(p, n) | | keccak(mem[p...(p+n))) | ++-------------------------+------+-----------------------------------------------------------------+ +| jump(label) | `-` | jump to label / code position | ++-------------------------+------+-----------------------------------------------------------------+ +| jumpi(label, cond) | `-` | jump to label if cond is nonzero | ++-------------------------+------+-----------------------------------------------------------------+ +| pc | | current position in code | ++-------------------------+------+-----------------------------------------------------------------+ +| pop | `*` | remove topmost stack slot | ++-------------------------+------+-----------------------------------------------------------------+ +| dup1 ... dup16 | | copy ith stack slot to the top (counting from top) | ++-------------------------+------+-----------------------------------------------------------------+ +| swap1 ... swap1 | `*` | swap topmost and ith stack slot below it | ++-------------------------+------+-----------------------------------------------------------------+ +| mload(p) | | mem[p..(p+32)) | ++-------------------------+------+-----------------------------------------------------------------+ +| mstore(p, v) | `-` | mem[p..(p+32)) := v | ++-------------------------+------+-----------------------------------------------------------------+ +| mstore8(p, v) | `-` | mem[p] := v & 0xff - only modifies a single byte | ++-------------------------+------+-----------------------------------------------------------------+ +| sload(p) | | storage[p] | ++-------------------------+------+-----------------------------------------------------------------+ +| sstore(p, v) | `-` | storage[p] := v | ++-------------------------+------+-----------------------------------------------------------------+ +| msize | | size of memory, i.e. largest accessed memory index | ++-------------------------+------+-----------------------------------------------------------------+ +| gas | | gas still available to execution | ++-------------------------+------+-----------------------------------------------------------------+ +| address | | address of the current contract / execution context | ++-------------------------+------+-----------------------------------------------------------------+ +| balance(a) | | wei balance at address a | ++-------------------------+------+-----------------------------------------------------------------+ +| caller | | call sender (excluding delegatecall) | ++-------------------------+------+-----------------------------------------------------------------+ +| callvalue | | wei sent together with the current call | ++-------------------------+------+-----------------------------------------------------------------+ +| calldataload(p) | | call data starting from position p (32 bytes) | ++-------------------------+------+-----------------------------------------------------------------+ +| calldatasize | | size of call data in bytes | ++-------------------------+------+-----------------------------------------------------------------+ +| calldatacopy(t, f, s) | `-` | copy s bytes from calldata at position f to mem at position t | ++-------------------------+------+-----------------------------------------------------------------+ +| codesize | | size of the code of the current contract / execution context | ++-------------------------+------+-----------------------------------------------------------------+ +| codecopy(t, f, s) | `-` | copy s bytes from code at position f to mem at position t | ++-------------------------+------+-----------------------------------------------------------------+ +| extcodesize(a) | | size of the code at address a | ++-------------------------+------+-----------------------------------------------------------------+ +| extcodecopy(a, t, f, s) | `-` | like codecopy(t, f, s) but take code at address a | ++-------------------------+------+-----------------------------------------------------------------+ +| 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)] | +| insize, out, outsize) | | providing g gas and v wei and output area | +| | | mem[out..(out+outsize)] returting 1 on error (out of gas) | ++-------------------------+------+-----------------------------------------------------------------+ +| 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`` | +| insize, out, outsize) | | and ``callvalue`` | ++-------------------------+------+-----------------------------------------------------------------+ +| return(p, s) | `*` | end execution, return data mem[p..(p+s)) | ++-------------------------+------+-----------------------------------------------------------------+ +| selfdestruct(a) | `*` | end execution, destroy current contract and send funds to a | ++-------------------------+------+-----------------------------------------------------------------+ +| log0(p, s) | `-` | log without topics and data mem[p..(p+s)) | ++-------------------------+------+-----------------------------------------------------------------+ +| log1(p, s, t1) | `-` | log with topic t1 and data mem[p..(p+s)) | ++-------------------------+------+-----------------------------------------------------------------+ +| log2(p, s, t1, t2) | `-` | log with topics t1, t2 and data mem[p..(p+s)) | ++-------------------------+------+-----------------------------------------------------------------+ +| log3(p, s, t1, t2, t3) | `-` | log with topics t1, t2, t3 and data mem[p..(p+s)) | ++-------------------------+------+-----------------------------------------------------------------+ +| log4(p, s, t1, t2, t3, | `-` | log with topics t1, t2, t3, t4 and data mem[p..(p+s)) | +| t4) | | | ++-------------------------+------+-----------------------------------------------------------------+ +| origin | | transaction sender | ++-------------------------+------+-----------------------------------------------------------------+ +| gasprice | | gas price of the transaction | ++-------------------------+------+-----------------------------------------------------------------+ +| blockhash(b) | | hash of block nr b - only for last 256 blocks excluding current | ++-------------------------+------+-----------------------------------------------------------------+ +| coinbase | | current mining beneficiary | ++-------------------------+------+-----------------------------------------------------------------+ +| timestamp | | timestamp of the current block in seconds since the epoch | ++-------------------------+------+-----------------------------------------------------------------+ +| number | | current block number | ++-------------------------+------+-----------------------------------------------------------------+ +| difficulty | | difficulty of the current block | ++-------------------------+------+-----------------------------------------------------------------+ +| gaslimit | | block gas limit of the current block | ++-------------------------+------+-----------------------------------------------------------------+ Literals -------- You can use integer constants by typing them in decimal or hexadecimal notation and an -appropriate `PUSHi` instruction will automatically be generated. The following creates code +appropriate ``PUSHi`` instruction will automatically be generated. The following creates code to add 2 and 3 resulting in 5 and then computes the bitwise and with the string "abc". Strings are stored left-aligned and cannot be longer than 32 bytes. .. code:: - assembly { 2 3 add "abc" and } + assembly { 2 3 add "abc" and } Functional Style ----------------- You can type opcode after opcode in the same way they will end up in bytecode. For example -adding `3` to the contents in memory at position `0x80` would be +adding ``3`` to the contents in memory at position ``0x80`` would be .. code:: - 3 0x80 mload add 0x80 mstore + 3 0x80 mload add 0x80 mstore As it is often hard to see what the actual arguments for certain opcodes are, Solidity inline assembly also provides a "functional style" notation where the same code @@ -443,7 +510,7 @@ would be written as follows .. code:: - mstore(0x80, add(mload(0x80), 3)) + mstore(0x80, add(mload(0x80), 3)) Functional style and instructional style can be mixed, but any opcode inside a functional style expression has to return exactly one stack slot (most of the opcodes do). @@ -452,8 +519,8 @@ Note that the order of arguments is reversed in functional-style as opposed to t way. If you use functional-style, the first argument will end up on the stack top. -Access to External Variables ----------------------------- +Access to External Variables and Functions +------------------------------------------ Solidity variables and other identifiers can be accessed by simply using their name. For storage and memory variables, this will push the address and not the value onto the @@ -461,43 +528,54 @@ stack. Also note that non-struct and non-array storage variable addresses occupy on the stack: One for the address and one for the byte offset inside the storage slot. In assignments (see below), we can even use local Solidity variables to assign to. +Functions external to inline assembly can also be accessed: The assembly will +push their entry label (with virtual function resolution applied). The calling semantics +in solidity are: + + - the caller pushes return label, arg1, arg2, ..., argn + - the call returns with ret1, ret2, ..., retn + +This feature is still a bit cumbersome to use, because the stack offset essentially +changes during the call, and thus references to local variables will be wrong. +It is planned that the stack height changes can be specified in inline assembly. + .. code:: - contract c { - uint b; - function f(uint x) returns (uint r) { - assembly { - b pop // remove the offset, we know it is zero - sload - x - mul - =: r // assign to return variable r - } - } - } + contract C { + uint b; + function f(uint x) returns (uint r) { + assembly { + b pop // remove the offset, we know it is zero + sload + x + mul + =: r // assign to return variable r + } + } + } Labels ------ -Another problem in EVM assembly is that `jump` and `jumpi` use absolute addresses +Another problem in EVM assembly is that ``jump`` and ``jumpi`` use absolute addresses which can change easily. Solidity inline assembly provides labels to make the use of jumps easier. The following code computes an element in the Fibonacci series. .. code:: - { - let n := calldataload(4) - let a := 1 - let b := a - loop: - jumpi(loopend, eq(n, 0)) - a add swap1 - n := sub(n, 1) - jump(loop) - loopend: - mstore(0, a) - return(0, 0x20) - } + { + let n := calldataload(4) + let a := 1 + let b := a + loop: + jumpi(loopend, eq(n, 0)) + a add swap1 + n := sub(n, 1) + jump(loop) + loopend: + mstore(0, a) + return(0, 0x20) + } Please note that automatically accessing stack variables can only work if the assembler knows the current stack height. This fails to work if the jump source @@ -506,50 +584,50 @@ you should just not access any stack variables (even assembly variables) in that Furthermore, the stack height analyser goes through the code opcode by opcode (and not according to control flow), so in the following case, the assembler -will have a wrong impression about the stack height at label `two`: +will have a wrong impression about the stack height at label ``two``: .. code:: - { - jump(two) - one: - // Here the stack height is 1 (because we pushed 7), - // but the assembler thinks it is 0 because it reads - // from top to bottom. - // Accessing stack variables here will lead to errors. - jump(three) - two: - 7 // push something onto the stack - jump(one) - three: - } + { + jump(two) + one: + // Here the stack height is 1 (because we pushed 7), + // but the assembler thinks it is 0 because it reads + // from top to bottom. + // Accessing stack variables here will lead to errors. + jump(three) + two: + 7 // push something onto the stack + jump(one) + three: + } Declaring Assembly-Local Variables ---------------------------------- -You can use the `let` keyword to declare variables that are only visible in -inline assembly and actually only in the current `{...}`-block. What happens -is that the `let` instruction will create a new stack slot that is reserved +You can use the ``let`` keyword to declare variables that are only visible in +inline assembly and actually only in the current ``{...}``-block. What happens +is that the ``let`` instruction will create a new stack slot that is reserved for the variable and automatically removed again when the end of the block is reached. You need to provide an initial value for the variable which can -be just `0`, but it can also be a complex functional-style expression. +be just ``0``, but it can also be a complex functional-style expression. .. code:: - contract c { - function f(uint x) returns (uint b) { - assembly { - let v := add(x, 1) - mstore(0x80, v) - { - let y := add(sload(v), 1) - b := y - } // y is "deallocated" here - b := add(b, v) - } // v is "deallocated" here - } - } + contract C { + function f(uint x) returns (uint b) { + assembly { + let v := add(x, 1) + mstore(0x80, v) + { + let y := add(sload(v), 1) + b := y + } // y is "deallocated" here + b := add(b, v) + } // v is "deallocated" here + } + } Assignments @@ -560,19 +638,19 @@ variables. Take care that when you assign to variables that point to memory or storage, you will only change the pointer and not the data. There are two kinds of assignments: Functional-style and instruction-style. -For functionaly-style assignments (`variable := value`), you need to provide a value in a +For functionaly-style assignments (``variable := value``), you need to provide a value in a functional-style expression that results in exactly one stack value -and for instruction-style (`=: variable`), the value is just taken from the stack top. +and for instruction-style (``=: variable``), the value is just taken from the stack top. For both ways, the colon points to the name of the variable. .. code:: - assembly { - let v := 0 // functional-style assignment as part of variable declaration - let g := add(v, 2) - sload(10) - =: v // instruction style assignment, puts the result of sload(10) into v - } + assembly { + let v := 0 // functional-style assignment as part of variable declaration + let g := add(v, 2) + sload(10) + =: v // instruction style assignment, puts the result of sload(10) into v + } Things to Avoid @@ -591,7 +669,7 @@ Conventions in Solidity ----------------------- In contrast to EVM assembly, Solidity knows types which are narrower than 256 bits, -e.g. `uint24`. In order to make them more efficient, most arithmetic operations just +e.g. ``uint24``. In order to make them more efficient, most arithmetic operations just treat them as 256 bit numbers and the higher-order bits are only cleaned at the point where it is necessary, i.e. just shortly before they are written to memory or before comparisons are performed. This means that if you access such a variable @@ -599,15 +677,15 @@ from within inline assembly, you might have to manually clean the higher order b first. Solidity manages memory in a very simple way: There is a "free memory pointer" -at position `0x40` in memory. If you want to allocate memory, just use the memory +at position ``0x40`` in memory. If you want to allocate memory, just use the memory from that point on and update the pointer accordingly. Elements in memory arrays in Solidity always occupy multiples of 32 bytes (yes, this is -even true for `byte[]`, but not for `bytes` and `string`). Multi-dimensional memory +even true for ``byte[]``, but not for ``bytes`` and ``string``). Multi-dimensional memory arrays are pointers to memory arrays. The length of a dynamic array is stored at the first slot of the array and then only the array elements follow. .. warning:: - Statically-sized memory arrays do not have a length field, but it will be added soon - to allow better convertibility between statically- and dynamically-sized arrays, so - please do not rely on that. + Statically-sized memory arrays do not have a length field, but it will be added soon + to allow better convertibility between statically- and dynamically-sized arrays, so + please do not rely on that. diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index ea8b6a67..b3667a11 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -2,7 +2,7 @@ Frequently Asked Questions ########################### -This list was originally compiled by [fivedogit](mailto:fivedogit@gmail.com). +This list was originally compiled by `fivedogit <mailto:fivedogit@gmail.com>`_. *************** @@ -10,7 +10,7 @@ Basic Questions *************** What is Solidity? -============================= +================= Solidity is the DEV-created (i.e. Ethereum Foundation-created), Javascript-inspired language that can be used to create smart contracts @@ -28,11 +28,11 @@ There are some `contract examples <https://github.com/fivedogit/solidity-baby-st there should be a `test contract <https://github.com/ethereum/solidity/blob/develop/test/libsolidity/SolidityEndToEndTest.cpp>`_ for every single feature of Solidity. How do I compile contracts? -============================= +=========================== -Probably the fastest way is the `online compiler <https://chriseth.github.io/browser-solidity/>`_. +Probably the fastest way is the `online compiler <https://ethereum.github.io/browser-solidity/>`_. -You can also use the `solc` binary which comes with cpp-ethereum to compile +You can also use the ``solc`` binary which comes with cpp-ethereum to compile contracts or an emerging option is to use Mix, the IDE. @@ -71,8 +71,8 @@ several blockchain explorers. Contracts on the blockchain should have their original source code published if they are to be used by third parties. -Does selfdestruct() free up space in the blockchain? -==================================================== +Does ``selfdestruct()`` free up space in the blockchain? +======================================================== It removes the contract bytecode and storage from the current block into the future, but since the blockchain stores every single block (i.e. @@ -89,97 +89,97 @@ If you want to deactivate your contracts, it is preferable to **disable** them b internal state which causes all functions to throw. This will make it impossible to use the contract and ether sent to the contract will be returned automatically. -Now to answering the question: Inside a constructor, `msg.sender` is the -creator. Save it. Then `selfdestruct(creator);` to kill and return funds. +Now to answering the question: Inside a constructor, ``msg.sender`` is the +creator. Save it. Then ``selfdestruct(creator);`` to kill and return funds. `example <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/05_greeter.sol>`_ -Note that if you `import "mortal"` at the top of your contracts and declare -`contract SomeContract is mortal { ...` and compile with a compiler that already -has it (which includes `browser-solidity <https://chriseth.github.io/browser-solidity/>`_), then -`kill()` is taken care of for you. Once a contract is "mortal", then you can -`contractname.kill.sendTransaction({from:eth.coinbase})`, just the same as my +Note that if you ``import "mortal"`` at the top of your contracts and declare +``contract SomeContract is mortal { ...`` and compile with a compiler that already +has it (which includes `browser-solidity <https://ethereum.github.io/browser-solidity/>`_), then +``kill()`` is taken care of for you. Once a contract is "mortal", then you can +``contractname.kill.sendTransaction({from:eth.coinbase})``, just the same as my examples. Store Ether in a contract ========================= -The trick is to create the contract with `{from:someaddress, value: web3.toWei(3,"ether")...}` +The trick is to create the contract with ``{from:someaddress, value: web3.toWei(3,"ether")...}`` See `endowment_retriever.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/30_endowment_retriever.sol>`_. -Use a non-constant function (req sendTransaction) to increment a variable in a contract -======================================================================================= +Use a non-constant function (req ``sendTransaction``) to increment a variable in a contract +=========================================================================================== See `value_incrementer.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/20_value_incrementer.sol>`_. Get contract address in Solidity ================================ -Short answer: The global variable `this` is the contract address. +Short answer: The global variable ``this`` is the contract address. See `basic_info_getter <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/15_basic_info_getter.sol>`_. -Long answer: `this` is a variable representing the current contract. +Long answer: ``this`` is a variable representing the current contract. Its type is the type of the contract. Since any contract type basically inherits from the -`address` type, `this` is always convertible to `address` and in this case contains +``address`` type, ``this`` is always convertible to ``address`` and in this case contains its own address. -What is the difference between a function marked constant and one that is not? -============================================================================== +What is the difference between a function marked ``constant`` and one that is not? +================================================================================== -`constant` functions can perform some action and return a value, but cannot +``constant`` functions can perform some action and return a value, but cannot change state (this is not yet enforced by the compiler). In other words, a constant function cannot save or update any variables within the contract or wider -blockchain. These functions are called using `c.someFunction(...)` from +blockchain. These functions are called using ``c.someFunction(...)`` from geth or any other web3.js environment. -"non-constant" functions (those lacking the `constant` specifier) must be called -with `c.someMethod.sendTransaction({from:eth.accounts[x], gas: 1000000});` +"non-constant" functions (those lacking the ``constant`` specifier) must be called +with ``c.someMethod.sendTransaction({from:eth.accounts[x], gas: 1000000});`` That is, because they can change state, they have to have a gas payment sent along to get the work done. -Get a contract to return its funds to you (not using selfdestruct(...)). -======================================================================== +Get a contract to return its funds to you (not using ``selfdestruct(...)``). +============================================================================ -This example demonstrates how to send funds from a contract to an address. +This example demonstrates how to send funds from a contract to an address. See `endowment_retriever <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/30_endowment_retriever.sol>`_. -What is a mapping and how do we use them? -========================================= +What is a ``mapping`` and how do we use them? +============================================= A mapping is very similar to a K->V hashmap. -If you have a state variable of type `mapping (string -> uint) x;`, then you can -access the value by `x["somekeystring"]`. +If you have a state variable of type ``mapping (string -> uint) x;``, then you can +access the value by ``x["somekeystring"]``. -How can I get the length of a mapping? -====================================== +How can I get the length of a ``mapping``? +========================================== Mappings are a rather low-level data structure. It does not store the keys and it is not possible to know which or how many values are "set". Actually, all values to all possible keys are set by default, they are just initialised with the zero value. -In this sense, the attribute `length` for a mapping does not really apply. +In this sense, the attribute ``length`` for a mapping does not really apply. If you want to have a "sized mapping", you can use the iterable mapping (see below) or just a dynamically-sized array of structs. -Are mappings iterable? -====================== +Are ``mapping``'s iterable? +=========================== Mappings themselves are not iterable, but you can use a higher-level datastructure on top of it, for example the `iterable mapping <https://github.com/ethereum/dapp-bin/blob/master/library/iterable_mapping.sol>`_. -Can I put arrays inside of a mapping? How do I make a mapping of a mapping? -=========================================================================== +Can I put arrays inside of a ``mapping``? How do I make a ``mapping`` of a ``mapping``? +======================================================================================= -Mappings are already syntactically similar to arrays as they are, therefore it doesn't make much sense to store an array in them. Rather what you should do is create a mapping of a mapping. +Mappings are already syntactically similar to arrays as they are, therefore it doesn't make much sense to store an array in them. Rather what you should do is create a mapping of a mapping. An example of this would be:: - contract c { + contract C { struct myStruct { uint someNumber; string someString; @@ -192,24 +192,24 @@ An example of this would be:: } } -Can you return an array or a string from a solidity function call? -================================================================== +Can you return an array or a ``string`` from a solidity function call? +====================================================================== Yes. See `array_receiver_and_returner.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/60_array_receiver_and_returner.sol>`_. What is problematic, though, is returning any variably-sized data (e.g. a -variably-sized array like `uint[]`) from a fuction **called from within Solidity**. +variably-sized array like ``uint[]``) from a fuction **called from within Solidity**. This is a limitation of the EVM and will be solved with the next protocol update. Returning variably-sized data as part of an external transaction or call is fine. -How do you represent double/float in Solidity? -============================================== +How do you represent ``double``/``float`` in Solidity? +====================================================== This is not yet possible. -Is it possible to in-line initialize an array like so: string[] myarray = ["a", "b"]; -======================================================================================= +Is it possible to in-line initialize an array like so: ``string[] myarray = ["a", "b"];`` +========================================================================================= Yes. However it should be noted that this currently only works with statically sized memory arrays. You can even create an inline memory array in the return statement. Pretty cool, huh? @@ -218,13 +218,13 @@ Example:: contract C { function f() returns (uint8[5]) { - string[4] memory AdaArr = ["This", "is", "an", "array"]; + string[4] memory adaArr = ["This", "is", "an", "array"]; return ([1, 2, 3, 4, 5]); } } -What are events and why do we need them? -======================================== +What are ``event``'s and why do we need them? +============================================= Let us suppose that you need a contract to alert the outside world when something happens. The contract can fire an event, which can be listened to @@ -239,20 +239,20 @@ The visibility specifiers do not only change the visibility but also the way functions can be called. In general, functions in the same contract can also be called internally (which is cheaper and allows for memory types to be passed by reference). This -is done if you just use `f(1,2)`. If you use `this.f(1,2)` -or `otherContract.f(1,2)`, the function is called externally. +is done if you just use ``f(1,2)``. If you use ``this.f(1,2)`` +or ``otherContract.f(1,2)``, the function is called externally. Internal function calls have the advantage that you can use all Solidity types as parameters, but you have to stick to the simpler ABI types for external calls. -* external: all, only externally +* ``external``: all, only externally -* public: all (this is the default), externally and internally +* ``public``: all (this is the default), externally and internally -* internal: only this contract and contracts deriving from it, only internally +* ``internal``: only this contract and contracts deriving from it, only internally -* private: only this contract, only internally +* ``private``: only this contract, only internally Do contract constructors have to be publicly visible? @@ -278,8 +278,8 @@ Is a constructor required? No. If there is no constructor, a generic one without arguments and no actions will be used. -Are timestamps (now, block.timestamp) reliable? -=============================================== +Are timestamps (``now,`` ``block.timestamp``) reliable? +======================================================= This depends on what you mean by "reliable". In general, they are supplied by miners and are therefore vulnerable. @@ -288,28 +288,28 @@ Unless someone really messes up the blockchain or the clock on your computer, you can make the following assumptions: You publish a transaction at a time X, this transaction contains same -code that calls `now` and is included in a block whose timestamp is Y +code that calls ``now`` and is included in a block whose timestamp is Y and this block is included into the canonical chain (published) at a time Z. -The value of `now` will be identical to Y and X <= Y <= Z. +The value of ``now`` will be identical to Y and X <= Y <= Z. -Never use `now` or `block.hash` as a source of randomness, unless you know +Never use ``now`` or ``block.hash`` as a source of randomness, unless you know what you are doing! -Can a contract function return a struct? -======================================== +Can a contract function return a ``struct``? +============================================ -Yes, but only in "internal" function calls. +Yes, but only in ``internal`` function calls. -If I return an enum, I only get integer values in web3.js. How to get the named values? -======================================================================================= +If I return an ``enum``, I only get integer values in web3.js. How to get the named values? +=========================================================================================== Enums are not supported by the ABI, they are just supported by Solidity. You have to do the mapping yourself for now, we might provide some help later. -What is the deal with "function () { ... }" inside Solidity contracts? How can a function not have a name? -========================================================================================================== +What is the deal with ``function () { ... }`` inside Solidity contracts? How can a function not have a name? +============================================================================================================ This function is called "fallback function" and it is called when someone just sent Ether to the contract without @@ -324,16 +324,16 @@ a way to pull out Ether from a contract. If the contract is not meant to receive Ether with simple transfers, you should implement the fallback function as -`function() { throw; }` +``function() { throw; }`` this will cause all transactions to this contract that do not call an -existing function to be reverted, so that all Ether is sent back. +existing function to be reverted, so that all Ether is sent back. Another use of the fallback function is to e.g. register that your contract received ether by using an event. *Attention*: If you implement the fallback function take care that it uses as -little gas as possible, because `send()` will only supply a limited amount. +little gas as possible, because ``send()`` will only supply a limited amount. Is it possible to pass arguments to the fallback function? ========================================================== @@ -342,12 +342,12 @@ The fallback function cannot take parameters. Under special circumstances, you can send data. If you take care that none of the other functions is invoked, you can access the data -by `msg.data`. +by ``msg.data``. Can state variables be initialized in-line? =========================================== -Yes, this is possible for all types (even for structs). However, for arrays it +Yes, this is possible for all types (even for structs). However, for arrays it should be noted that you must declare them as static memory arrays. Examples:: @@ -360,7 +360,7 @@ Examples:: S public x = S(1, 2); string name = "Ada"; - string[4] memory AdaArr = ["This", "is", "an", "array"]; + string[4] memory adaArr = ["This", "is", "an", "array"]; } @@ -368,8 +368,8 @@ Examples:: C c = new C(); } -What is the "modifier" keyword? -=============================== +What is the ``modifier`` keyword? +================================= Modifiers are a way to prepend or append code to a function in order to add guards, initialisation or cleanup functionality in a concise way. @@ -386,12 +386,12 @@ How do for loops work? Very similar to JavaScript. There is one point to watch out for, though: -If you use `for (var i = 0; i < a.length; i ++) { a[i] = i; }`, then -the type of `i` will be inferred only from `0`, whose type is `uint8`. -This means that if `a` has more than `255` elements, your loop will -not terminate because `i` can only hold values up to `255`. +If you use ``for (var i = 0; i < a.length; i ++) { a[i] = i; }``, then +the type of ``i`` will be inferred only from ``0``, whose type is ``uint8``. +This means that if ``a`` has more than ``255`` elements, your loop will +not terminate because ``i`` can only hold values up to ``255``. -Better use `for (uint i = 0; i < a.length...` +Better use ``for (uint i = 0; i < a.length...`` See `struct_and_for_loop_tester.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/65_struct_and_for_loop_tester.sol>`_. @@ -402,14 +402,14 @@ Solidity is character set agnostic concerning strings in the source code, althou utf-8 is recommended. Identifiers (variables, functions, ...) can only use ASCII. -What are some examples of basic string manipulation (substring, indexOf, charAt, etc)? -====================================================================================== +What are some examples of basic string manipulation (``substring``, ``indexOf``, ``charAt``, etc)? +================================================================================================== There are some string utility functions at `stringUtils.sol <https://github.com/ethereum/dapp-bin/blob/master/library/stringUtils.sol>`_ -which will be extended in the future. +which will be extended in the future. In addition, Arachnid has written `solidity-stringutils <https://github.com/Arachnid/solidity-stringutils>`_. For now, if you want to modify a string (even when you only want to know its length), -you should always convert it to a `bytes` first:: +you should always convert it to a ``bytes`` first:: contract C { string s; @@ -429,8 +429,8 @@ Can I concatenate two strings? You have to do it manually for now. -Why is the low-level function .call() less favorable than instantiating a contract with a variable (ContractB b;) and executing its functions (b.doSomething();)? -================================================================================================================================================================= +Why is the low-level function ``.call()`` less favorable than instantiating a contract with a variable (``ContractB b;``) and executing its functions (``b.doSomething();``)? +============================================================================================================================================================================= If you use actual functions, the compiler will tell you if the types or your arguments do not match, if the function does not exist @@ -445,12 +445,12 @@ Is unused gas automatically refunded? Yes and it is immediate, i.e. done as part of the transaction. -When returning a value of say "uint" type, is it possible to return an "undefined" or "null"-like value? -======================================================================================================== +When returning a value of say ``uint`` type, is it possible to return an ``undefined`` or "null"-like value? +============================================================================================================ This is not possible, because all types use up the full value range. -You have the option to `throw` on error, which will also revert the whole +You have the option to ``throw`` on error, which will also revert the whole transaction, which might be a good idea if you ran into an unexpected situation. @@ -495,8 +495,8 @@ No, a function call from one contract to another does not create its own transac you have to look in the overall transaction. This is also the reason why several block explorer do not show Ether sent between contracts correctly. -What is the memory keyword? What does it do? -============================================ +What is the ``memory`` keyword? What does it do? +================================================ The Ethereum Virtual Machine has three areas where it can store items. @@ -545,14 +545,14 @@ Example:: } } -The function `append` can work both on `data1` and `data2` and its modifications will be -stored permanently. If you remove the `storage` keyword, the default -is to use `memory` for function arguments. This has the effect that -at the point where `append(data1)` or `append(data2)` is called, an +The function ``append`` can work both on ``data1`` and ``data2`` and its modifications will be +stored permanently. If you remove the ``storage`` keyword, the default +is to use ``memory`` for function arguments. This has the effect that +at the point where ``append(data1)`` or ``append(data2)`` is called, an independent copy of the state variable is created in memory and -`append` operates on this copy (which does not support `.push` - but that +``append`` operates on this copy (which does not support ``.push`` - but that is another issue). The modifications to this independent copy do not -carry back to `data1` or `data2`. +carry back to ``data1`` or ``data2``. A common mistake is to declare a local variable and assume that it will be created in memory, although it will be created in storage:: @@ -569,16 +569,16 @@ be created in memory, although it will be created in storage:: } } -The type of the local variable `x` is `uint[] storage`, but since +The type of the local variable ``x`` is ``uint[] storage``, but since storage is not dynamically allocated, it has to be assigned from a state variable before it can be used. So no space in storage will be -allocated for `x`, but instead it functions only as an alias for +allocated for ``x``, but instead it functions only as an alias for a pre-existing variable in storage. -What will happen is that the compiler interprets `x` as a storage -pointer and will make it point to the storage slot `0` by default. -This has the effect that `someVariable` (which resides at storage -slot `0`) is modified by `x.push(2)`. +What will happen is that the compiler interprets ``x`` as a storage +pointer and will make it point to the storage slot ``0`` by default. +This has the effect that ``someVariable`` (which resides at storage +slot ``0``) is modified by ``x.push(2)``. The correct way to do this is the following:: @@ -598,11 +598,11 @@ Can a regular (i.e. non-contract) ethereum account be closed permanently like a No. Non-contract accounts "exist" as long as the private key is known by someone or can be generated in some way. -What is the difference between `bytes` and `byte[]`? -==================================================== +What is the difference between ``bytes`` and ``byte[]``? +======================================================== -`bytes` is usually more efficient: When used as arguments to functions (i.e. in -CALLDATA) or in memory, every single element of a `byte[]` is padded to 32 +``bytes`` is usually more efficient: When used as arguments to functions (i.e. in +CALLDATA) or in memory, every single element of a ``byte[]`` is padded to 32 bytes which wastes 31 bytes per element. Is it possible to send a value while calling an overloaded function? @@ -658,20 +658,20 @@ How do you create 2-dimensional arrays? See `2D_array.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/55_2D_array.sol>`_. -Note that filling a 10x10 square of `uint8` + contract creation took more than `800,000` -gas at the time of this writing. 17x17 took `2,000,000` gas. With the limit at +Note that filling a 10x10 square of ``uint8`` + contract creation took more than ``800,000`` +gas at the time of this writing. 17x17 took ``2,000,000`` gas. With the limit at 3.14 million... well, there’s a pretty low ceiling for what you can create right now. Note that merely "creating" the array is free, the costs are in filling it. Note2: Optimizing storage access can pull the gas costs down considerably, because -32 `uint8` values can be stored in a single slot. The problem is that these optimizations +32 ``uint8`` values can be stored in a single slot. The problem is that these optimizations currently do not work across loops and also have a problem with bounds checking. You might get much better results in the future, though. -What does p.recipient.call.value(p.amount)(p.data) do? -====================================================== +What does ``p.recipient.call.value(p.amount)(p.data)`` do? +========================================================== Every external function call in Solidity can be modified in two ways: @@ -680,23 +680,23 @@ Every external function call in Solidity can be modified in two ways: This is done by "calling a function on the function": -`f.gas(2).value(20)()` calls the modified function `f` and thereby sending 20 +``f.gas(2).value(20)()`` calls the modified function ``f`` and thereby sending 20 Wei and limiting the gas to 2 (so this function call will most likely go out of gas and return your 20 Wei). -In the above example, the low-level function `call` is used to invoke another -contract with `p.data` as payload and `p.amount` Wei is sent with that call. +In the above example, the low-level function ``call`` is used to invoke another +contract with ``p.data`` as payload and ``p.amount`` Wei is sent with that call. -What happens to a struct's mapping when copying over a struct? -============================================================== +What happens to a ``struct``'s mapping when copying over a ``struct``? +====================================================================== This is a very interesting question. Suppose that we have a contract field set up like such:: - struct user{ + struct user { mapping(string => address) usedContracts; } - function somefunction{ + function somefunction { user user1; user1.usedContracts["Hello"] = "World"; user user2 = user1; @@ -709,11 +709,14 @@ How do I initialize a contract with only a specific amount of wei? ================================================================== Currently the approach is a little ugly, but there is little that can be done to improve it. -In the case of a `contract A` calling a new instance of `contract B`, parentheses have to be used around -`new B` because `B.value` would refer to a member of `B` called `value`. +In the case of a ``contract A`` calling a new instance of ``contract B``, parentheses have to be used around +``new B`` because ``B.value`` would refer to a member of ``B`` called ``value``. You will need to make sure that you have both contracts aware of each other's presence. In this example:: + contract B {} + + contract A { address child; @@ -725,65 +728,65 @@ In this example:: Can a contract function accept a two-dimensional array? ======================================================= -This is not yet implemented for external calls and dynamic arrays - +This is not yet implemented for external calls and dynamic arrays - you can only use one level of dynamic arrays. -What is the relationship between bytes32 and string? Why is it that ‘bytes32 somevar = "stringliteral";’ works and what does the saved 32-byte hex value mean? -============================================================================================================================================================== +What is the relationship between ``bytes32`` and ``string``? Why is it that ``bytes32 somevar = "stringliteral";`` works and what does the saved 32-byte hex value mean? +======================================================================================================================================================================== -The type `bytes32` can hold 32 (raw) bytes. In the assignment `bytes32 samevar = "stringliteral";`, -the string literal is interpreted in its raw byte form and if you inspect `somevar` and -see a 32-byte hex value, this is just `"stringliteral"` in hex. +The type ``bytes32`` can hold 32 (raw) bytes. In the assignment ``bytes32 samevar = "stringliteral";``, +the string literal is interpreted in its raw byte form and if you inspect ``somevar`` and +see a 32-byte hex value, this is just ``"stringliteral"`` in hex. -The type `bytes` is similar, only that it can change its length. +The type ``bytes`` is similar, only that it can change its length. -Finally, `string` is basically identical to `bytes` only that it is assumed -to hold the utf-8 encoding of a real string. Since `string` stores the +Finally, ``string`` is basically identical to ``bytes`` only that it is assumed +to hold the utf-8 encoding of a real string. Since ``string`` stores the data in utf-8 encoding it is quite expensive to compute the number of characters in the string (the encoding of some characters takes more -than a single byte). Because of that, `string s; s.length` is not yet -supported and not even index access `s[2]`. But if you want to access +than a single byte). Because of that, ``string s; s.length`` is not yet +supported and not even index access ``s[2]``. But if you want to access the low-level byte encoding of the string, you can use -`bytes(s).length` and `bytes(s)[2]` which will result in the number +``bytes(s).length`` and ``bytes(s)[2]`` which will result in the number of bytes in the utf-8 encoding of the string (not the number of characters) and the second byte (not character) of the utf-8 encoded string, respectively. -Can a contract pass an array (static size) or string or bytes (dynamic size) to another contract? -================================================================================================= +Can a contract pass an array (static size) or string or ``bytes`` (dynamic size) to another contract? +===================================================================================================== Sure. Take care that if you cross the memory / storage boundary, independent copies will be created:: contract C { - uint[20] x; + uint[20] x; - function f() { - g(x); - h(x); - } + function f() { + g(x); + h(x); + } - function g(uint[20] y) { - y[2] = 3; - } + function g(uint[20] y) { + y[2] = 3; + } - function h(uint[20] storage y) { - y[3] = 4; - } - } + function h(uint[20] storage y) { + y[3] = 4; + } + } -The call to `g(x)` will not have an effect on `x` because it needs +The call to ``g(x)`` will not have an effect on ``x`` because it needs to create an independent copy of the storage value in memory (the default storage location is memory). On the other hand, -`h(x)` successfully modifies `x` because only a reference +``h(x)`` successfully modifies ``x`` because only a reference and not a copy is passed. -Sometimes, when I try to change the length of an array with ex: "arrayname.length = 7;" I get a compiler error "Value must be an lvalue". Why? -============================================================================================================================================== +Sometimes, when I try to change the length of an array with ex: ``arrayname.length = 7;`` I get a compiler error ``Value must be an lvalue``. Why? +================================================================================================================================================== You can resize a dynamic array in storage (i.e. an array declared at the -contract level) with `arrayname.length = <some new length>;`. If you get the +contract level) with ``arrayname.length = <some new length>;``. If you get the "lvalue" error, you are probably doing one of two things wrong. 1. You might be trying to resize an array in "memory", or @@ -803,16 +806,16 @@ contract level) with `arrayname.length = <some new length>;`. If you get the might be used to declaring them in C or Java, but they are access as in C or Java. -For example, `int8[][5] somearray;` are 5 dynamic `int8` arrays. +For example, ``int8[][5] somearray;`` are 5 dynamic ``int8`` arrays. -The reason for this is that `T[5]` is always an array of 5 `T`s, -no matter whether `T` itself is an array or not (this is not the +The reason for this is that ``T[5]`` is always an array of 5 ``T``'s, +no matter whether ``T`` itself is an array or not (this is not the case in C or Java). -Is it possible to return an array of strings ( string[] ) from a Solidity function? -=================================================================================== +Is it possible to return an array of strings (``string[]``) from a Solidity function? +===================================================================================== -Not yet, as this requires two levels of dynamic arrays (`string` is a dynamic array itself). +Not yet, as this requires two levels of dynamic arrays (``string`` is a dynamic array itself). If you issue a call for an array, it is possible to retrieve the whole array? Or must you write a helper function for that? =========================================================================================================================== @@ -822,8 +825,8 @@ individual elements. If you want to return the complete array, you have to manually write a function to do that. -What could have happened if an account has storage value/s but no code? Example: http://test.ether.camp/account/5f740b3a43fbb99724ce93a879805f4dc89178b5 -========================================================================================================================================================= +What could have happened if an account has storage value(s) but no code? Example: http://test.ether.camp/account/5f740b3a43fbb99724ce93a879805f4dc89178b5 +========================================================================================================================================================== The last thing a constructor does is returning the code of the contract. The gas costs for this depend on the length of the code and it might be @@ -836,12 +839,15 @@ https://github.com/ethereum/wiki/wiki/Subtleties After a successful CREATE operation's sub-execution, if the operation returns x, 5 * len(x) gas is subtracted from the remaining gas before the contract is created. If the remaining gas is less than 5 * len(x), then no gas is subtracted, the code of the created contract becomes the empty string, but this is not treated as an exceptional condition - no reverts happen. -How do I use .send()? -===================== +How do I use ``.send()``? +========================= + +If you want to send 20 Ether from a contract to the address ``x``, you use ``x.send(20 ether);``. +Here, ``x`` can be a plain address or a contract. If the contract already explicitly defines +a function ``send`` (and thus overwrites the special function), you can use ``address(x).send(20 ether);``. -If you want to send 20 Ether from a contract to the address `x`, you use `x.send(20 ether);`. -Here, `x` can be a plain address or a contract. If the contract already explicitly defines -a function `send` (and thus overwrites the special function), you can use `address(x).send(20 ether);`. +Note that the call to ``send`` may fail in certain conditions, such as if you have insufficient funds, so you should always check the return value. +``send`` returns ``true`` if the send was successful and ``false`` otherwise. What does the following strange check do in the Custom Token contract? ====================================================================== @@ -852,7 +858,7 @@ What does the following strange check do in the Custom Token contract? throw; Integers in Solidity (and most other machine-related programming languages) are restricted to a certain range. -For `uint256`, this is `0` up to `2**256 - 1`. If the result of some operation on those numbers +For ``uint256``, this is ``0`` up to ``2**256 - 1``. If the result of some operation on those numbers does not fit inside this range, it is truncated. These truncations can have `serious consequences <https://en.bitcoin.it/wiki/Value_overflow_incident>`_, so code like the one above is necessary to avoid certain attacks. diff --git a/docs/index.rst b/docs/index.rst index fab886c7..61081e1c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -8,7 +8,7 @@ crowdfunding, blind auctions, multi-signature wallets and more. .. note:: The best way to try out Solidity right now is using the - `Browser-Based Compiler <https://chriseth.github.io/browser-solidity/>`_ + `Browser-Based Compiler <https://ethereum.github.io/browser-solidity/>`_ (it can take a while to load, please be patient). Useful links @@ -16,8 +16,6 @@ Useful links * `Ethereum <https://ethereum.org>`_ -* `Browser-Based Compiler <https://chriseth.github.io/browser-solidity/>`_ - * `Changelog <https://github.com/ethereum/wiki/wiki/Solidity-Changelog>`_ * `Story Backlog <https://www.pivotaltracker.com/n/projects/1189488>`_ @@ -28,6 +26,39 @@ Useful links * `Gitter Chat <https://gitter.im/ethereum/solidity/>`_ +Available Solidity Integrations +------------------------------- + +* `Browser-Based Compiler <https://ethereum.github.io/browser-solidity/>`_ + Browser-based IDE with integrated compiler and Solidity runtime environment without server-side components. + +* `Ethereum Studio <https://live.ether.camp/>`_ + Specialized web IDE that also provides shell access to a complete Ethereum environment. + +* `Visual Studio Extension <https://visualstudiogallery.msdn.microsoft.com/96221853-33c4-4531-bdd5-d2ea5acc4799/>`_ + Solidity plugin for Microsoft Visual Studio that includes the Solidity compiler. + +* `Mix IDE <https://github.com/ethereum/mix/>`_ + Qt based IDE for designing, debugging and testing solidity smart contracts. + +* `Package for SublimeText — Solidity language syntax <https://packagecontrol.io/packages/Ethereum/>`_ + Solidity syntax highlighting for SublimeText editor. + +* `Atom Solidity package <https://github.com/gmtcreators/atom-solidity/>`_ + Plugin for the Atom editor that features syntax highlighting, compilation and a runtime environment (requires backend node). + +* `Atom Solidity Linter <https://atom.io/packages/linter-solidity>`_ + Plugin for the Atom editor that provides Solidity linting. + +* `Visual Studio Code extension <http://juan.blanco.ws/solidity-contracts-in-visual-studio-code/>`_ + Solidity plugin for Microsoft Visual Studio Code that includes syntax highlighting and the Solidity compiler. + +* `Emacs Solidity <https://github.com/ethereum/emacs-solidity/>`_ + Plugin for the Emacs editor providing syntax highlighting and compilation error reporting. + +* `Vim Solidity <https://github.com/tomlion/vim-solidity/>`_ + Plugin for the Vim editor providing syntax highlighting. + Language Documentation ---------------------- @@ -38,7 +69,7 @@ and the :ref:`Ethereum Virtual Machine <the-ethereum-virtual-machine>`. The next section will explain several *features* of Solidity by giving useful :ref:`example contracts <voting>` Remember that you can always try out the contracts -`in your browser <https://chriseth.github.io/browser-solidity>`_! +`in your browser <https://ethereum.github.io/browser-solidity>`_! The last and most extensive section will cover all aspects of Solidity in depth. diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index b4448c95..a5f9b0f2 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -6,18 +6,18 @@ Browser-Solidity ================ If you just want to try Solidity for small contracts, you -can try `browser-solidity <https://chriseth.github.io/browser-solidity>`_ +can try `browser-solidity <https://ethereum.github.io/browser-solidity>`_ which does not need any installation. If you want to use it without connection to the Internet, you can also just save the page -locally or clone http://github.com/chriseth/browser-solidity. +locally or clone http://github.com/ethereum/browser-solidity. -NPM / node.js +npm / Node.js ============= This is probably the most portable and most convenient way to install Solidity locally. A platform-independent JavaScript library is provided by compiling the C++ source -into JavaScript using Emscripten for browser-solidity and there is also an NPM +into JavaScript using Emscripten for browser-solidity and there is also an npm package available. To install it, simply use @@ -26,8 +26,8 @@ To install it, simply use npm install solc -Details about the usage of the nodejs package can be found in the -`repository <https://github.com/chriseth/browser-solidity#nodejs-usage>`_. +Details about the usage of the Node.js package can be found in the +`solc-js repository <https://github.com/ethereum/solc-js>`_. Binary Packages =============== @@ -59,76 +59,84 @@ Set up Homebrew: brew update brew upgrade - + brew install boost --c++11 # this takes a while - brew install cmake cryptopp miniupnpc leveldb gmp libmicrohttpd libjson-rpc-cpp + brew install cmake cryptopp miniupnpc leveldb gmp libmicrohttpd libjson-rpc-cpp # For Mix IDE and Alethzero only brew install xz d-bus brew install homebrew/versions/v8-315 - brew install llvm --HEAD --with-clang + brew install llvm --HEAD --with-clang brew install qt5 --with-d-bus # add --verbose if long waits with a stale screen drive you crazy as well -Ubuntu ------- - -Below are the build instructions for the latest versions of Ubuntu. The best -supported platform as of December 2014 is Ubuntu 14.04, 64 bit, with at least 2 -GB RAM. All our tests are done with this version. Community contributions for -other versions are welcome! - -Install dependencies: - -Before you can build the source, you need several tools and dependencies for the application to get started. +Ubuntu Trusty (14.04) +--------------------- -First, update your repositories. Not all packages are provided in the main -Ubuntu repository, those you'll get from the Ethereum PPA and the LLVM archive. +Below are the instructions to install the minimal dependencies required +to compile Solidity on Ubuntu 14.04 (Trusty Tahr). .. note:: - Ubuntu 14.04 users, you'll need the latest version of cmake. For this, use: - `sudo apt-add-repository ppa:george-edison55/cmake-3.x` - -Now add all the rest: + These dependencies are not enough to compile the GUIs (Alethzero and Mix). .. code-block:: bash - sudo apt-get -y update - sudo apt-get -y install language-pack-en-base - sudo dpkg-reconfigure locales - sudo apt-get -y install software-properties-common + sudo apt-get -y install build-essential git cmake libgmp-dev libboost-all-dev \ + libjsoncpp-dev libleveldb-dev libcurl4-openssl-dev libminiupnpc-dev \ + libmicrohttpd-dev + 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 upgrade # this will update cmake to version 3.x + sudo apt-get -y install libcryptopp-dev libjson-rpc-cpp-dev -For Ubuntu 15.04 (Vivid Vervet) or older, use the following command to add the develop packages: +Ubuntu Xenial (16.04) +--------------------- -.. code-block:: bash +Below are the instructions to install the minimal dependencies required +to compile Solidity on Ubuntu 16.04 (Xenial Xerus). + +One of the dependencies (Crypto++ Library, with version >= 5.6.2) can be +installed either by adding the Ethereum PPA (Option 1) or by backporting +``libcrypto++`` from Ubuntu Development to Ubuntu Xenial (Option 2). - sudo apt-get -y install build-essential git cmake libboost-all-dev libgmp-dev libleveldb-dev libminiupnpc-dev libreadline-dev libncurses5-dev libcurl4-openssl-dev libcryptopp-dev libjson-rpc-cpp-dev libmicrohttpd-dev libjsoncpp-dev libedit-dev libz-dev +.. note:: -For Ubuntu 15.10 (Wily Werewolf) or newer, use the following command instead: + These dependencies are not enough to compile the GUIs (Alethzero and Mix). .. code-block:: bash - sudo apt-get -y install build-essential git cmake libboost-all-dev libgmp-dev libleveldb-dev libminiupnpc-dev libreadline-dev libncurses5-dev libcurl4-openssl-dev libcryptopp-dev libjsonrpccpp-dev libmicrohttpd-dev libjsoncpp-dev libedit-dev libz-dev + sudo apt-get -y install build-essential git cmake libgmp-dev libboost-all-dev \ + libjsoncpp-dev libleveldb-dev libcurl4-openssl-dev libminiupnpc-dev \ + libjsonrpccpp-dev libmicrohttpd-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 -The reason for the change is that `libjsonrpccpp-dev` is available in the universe repository for newer versions of Ubuntu. + ## (Option 2) For those willing to backport libcrypto++: + #sudo apt-get -y install ubuntu-dev-tools + #sudo pbuilder create + #mkdir ubuntu + #cd ubuntu + #backportpackage --workdir=. --build --dont-sign libcrypto++ + #sudo dpkg -i buildresult/libcrypto++6_*.deb buildresult/libcrypto++-dev_*.deb + #cd .. Building -------- -Run this if you plan on installing Solidity only, ignore errors at the end as -they relate only to Alethzero and Mix +Run this if you plan on installing Solidity only: .. code-block:: bash git clone --recursive https://github.com/ethereum/webthree-umbrella.git cd webthree-umbrella ./webthree-helpers/scripts/ethupdate.sh --no-push --simple-pull --project solidity # update Solidity repo - ./webthree-helpers/scripts/ethbuild.sh --no-git --project solidity --all --cores 4 -DEVMJIT=0 # build Solidity and others - #enabling DEVMJIT on OS X will not build - #feel free to enable it on Linux + ./webthree-helpers/scripts/ethbuild.sh --no-git --project solidity --cores 4 -DEVMJIT=0 -DETHASHCL=0 # build Solidity only If you opted to install Alethzero and Mix: @@ -147,7 +155,7 @@ you should fork Solidity and add your personal fork as a second remote: git remote add personal git@github.com:username/solidity.git Note that webthree-umbrella uses submodules, so solidity is its own git -repository, but its settings are not stored in `.git/config`, but in -`webthree-umbrella/.git/modules/solidity/config`. +repository, but its settings are not stored in ``.git/config``, but in +``webthree-umbrella/.git/modules/solidity/config``. diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index cecd6b91..0cb2b0d0 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -14,15 +14,15 @@ right now, we will go into more detail later. Storage ======= -.. Gist: a4532ce30246847b371b - :: contract SimpleStorage { uint storedData; + function set(uint x) { storedData = x; } + function get() constant returns (uint retVal) { return storedData; } @@ -30,20 +30,20 @@ Storage 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 +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 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 +contract. And in this case, the functions ``set`` and ``get`` can be used to modify or retrieve the value of the variable. -To access a state variable, you do not need the prefix `this.` as is common in +To access a state variable, you do not need the prefix ``this.`` as is common in other languages. This contract does not yet do much 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 (feasible) a way to prevent you from publishing -this number. Of course, anyone could just call `set` again with a different value +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 of the blockchain. Later, we will see how you can impose access restrictions so that only you can alter the number. @@ -61,16 +61,6 @@ Furthermore, anyone can send coins to each other without any need for registering with username and password - all you need is an Ethereum keypair. -.. note:: - This is not a nice example for browser-solidity. - If you use `browser-solidity <https://chriseth.github.io/browser-solidity>`_ - to try this example, you cannot change the address where you call - functions from. So you will always be the "minter", you can mint coins and send - them somewhere, but you cannot impersonate someone else. This might change in - the future. - -.. Gist: ad490694f3e5b3de47ab - :: contract Coin { @@ -88,10 +78,12 @@ registering with username and password - all you need is an Ethereum keypair. function Coin() { minter = msg.sender; } + function mint(address receiver, uint amount) { if (msg.sender != minter) return; balances[receiver] += amount; } + function send(address receiver, uint amount) { if (balances[msg.sender] < amount) return; balances[msg.sender] -= amount; @@ -102,11 +94,11 @@ 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 +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 -persons. The keyword `public` automatically generates a function that +persons. The keyword ``public`` automatically generates a function that allows you to access the current value of the state variable. Without this keyword, other contracts have no way to access the variable and only the code of this contract can write to it. @@ -121,7 +113,7 @@ get the idea - the compiler figures that out for you. .. index:: mapping -The next line, `mapping (address => uint) public balances;` also +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 hashtables which are @@ -131,7 +123,7 @@ 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 accessor function created by the `public` keyword +like this one. The accessor function created by the ``public`` keyword is a bit more complex in this case. It roughly looks like the following:: @@ -144,12 +136,12 @@ single account. .. index:: event -The line `event Sent(address from, address to, uint value);` declares +The line ``event Sent(address from, address to, uint value);`` declares a so-called "event" which is fired in the last line of the function -`send`. User interfaces (as well as server appliances of course) can +``send``. User interfaces (as well as server appliances of course) can listen for those events being fired on the blockchain without much cost. As soon as it is fired, the listener will also receive the -arguments `from`, `to` and `value`, which makes it easy to track +arguments ``from``, ``to`` and ``value``, which makes it easy to track transactions. In order to listen for this event, you would use :: Coin.Sent().watch({}, '', function(error, result) { @@ -163,22 +155,22 @@ transactions. In order to listen for this event, you would use :: } } -Note how the automatically generated function `balances` is called from +Note how the automatically generated function ``balances`` is called from the user interface. .. index:: coin -The special function `Coin` is the +The special function ``Coin`` is the constructor 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 -contains some properties which allow access to the blockchain. `msg.sender` is +contract: ``msg`` (together with ``tx`` and ``block``) is a magic 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 +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 @@ -305,7 +297,7 @@ If the target account contains code, that code is executed and the payload is provided as input data. If the target account is the zero-account (the account with the -address `0`), the transaction creates a **new contract**. +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 @@ -327,7 +319,7 @@ the transaction and to pay for this execution. While the EVM executes the transaction, the gas is gradually depleted according to specific rules. The **gas price** is a value set by the creator of the transaction, who -has to pay `gas_price * gas` up front from the sending account. +has to pay ``gas_price * gas`` up front from the sending account. If some gas is left after the execution, it is refunded in the same way. If the gas is used up at any point (i.e. it is negative), @@ -414,7 +406,7 @@ Delegatecall / Callcode and Libraries There exists a special variant of a message call, named **delegatecall** which is identical to a message call apart from the fact that the code at the target address is executed in the context of the calling -contract and `msg.sender` and `msg.value` do not change their values. +contract and ``msg.sender`` and ``msg.value`` do not change their values. This means that a contract can dynamically load code from a different address at runtime. Storage, current address and balance still @@ -456,9 +448,9 @@ Selfdestruct ============ The only possibility that code is removed from the blockchain is -when a contract at that address performs the `SELFDESTRUCT` operation. +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. -Note that even if a contract's code does not contain the `SELFDESTRUCT` +Note that even if a contract's code does not contain the ``SELFDESTRUCT`` opcode, it can still perform that operation using delegatecall or callcode. diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst index 48ecfb09..a0170c5a 100644 --- a/docs/layout-of-source-files.rst +++ b/docs/layout-of-source-files.rst @@ -6,6 +6,8 @@ Source files can contain an arbitrary number of contract definitions and include .. index:: source file, ! import +.. _import: + Importing other Source Files ============================ @@ -21,20 +23,20 @@ At a global level, you can use import statements of the following form: import "filename"; -...will import all global symbols from "filename" (and symbols imported there) into the +...will import all global symbols from "filename" (and symbols imported there) into the current global scope (different than in ES6 but backwards-compatible for Solidity). :: import * as symbolName from "filename"; -...creates a new global symbol `symbolName` whose members are all the global symbols from `"filename"`. +...creates a new global symbol ``symbolName`` whose members are all the global symbols from ``"filename"``. :: import {symbol1 as alias, symbol2} from "filename"; -...creates new global symbols `alias` and `symbol2` which reference `symbol1` and `symbol2` from `"filename"`, respectively. +...creates new global symbols ``alias`` and ``symbol2`` which reference ``symbol1`` and ``symbol2`` from ``"filename"``, respectively. Another syntax is not part of ES6, but probably convenient: @@ -42,17 +44,17 @@ Another syntax is not part of ES6, but probably convenient: import "filename" as symbolName; -...is equivalent to `import * as symbolName from "filename";`. +...is equivalent to ``import * as symbolName from "filename";``. Paths ----- -In the above, `filename` is always treated as a path with `/` as directory separator, -`.` as the current and `..` as the parent directory. Path names that do not start -with `.` are treated as absolute paths. +In the above, ``filename`` is always treated as a path with ``/`` as directory separator, +``.`` as the current and ``..`` as the parent directory. Path names that do not start +with ``.`` are treated as absolute paths. -To import a file `x` from the same directory as the current file, use `import "./x" as x;`. -If you use `import "x" as x;` instead, a different file could be referenced +To import a file ``x`` from the same directory as the current file, use ``import "./x" as x;``. +If you use ``import "x" as x;`` instead, a different file could be referenced (in a global "include directory"). It depends on the compiler (see below) how to actually resolve the paths. @@ -64,22 +66,27 @@ Use in actual Compilers When the compiler is invoked, it is not only possible to specify how to discover the first element of a path, but it is possible to specify path prefix -remappings so that e.g. `github.com/ethereum/dapp-bin/library` is remapped to -`/usr/local/dapp-bin/library` and the compiler will read the files from there. If +remappings so that e.g. ``github.com/ethereum/dapp-bin/library`` is remapped to +``/usr/local/dapp-bin/library`` and the compiler will read the files from there. If remapping keys are prefixes of each other, the longest is tried first. This -allows for a "fallback-remapping" with e.g. `""` maps to -`"/usr/local/include/solidity"`. +allows for a "fallback-remapping" with e.g. ``""`` maps to +``"/usr/local/include/solidity"``. Furthermore, these remappings can +depend on the context, which allows you to configure packages to +import e.g. different versions of a library of the same name. **solc**: -For solc (the commandline compiler), these remappings are provided as `key=value` -arguments, where the `=value` part is optional (and defaults to key in that +For solc (the commandline compiler), these remappings are provided as +``context:prefix=target`` arguments, where both the ``context:`` and the +``=target`` parts are optional (where target defaults to prefix in that case). All remapping values that are regular files are compiled (including their dependencies). This mechanism is completely backwards-compatible (as long -as no filename contains a =) and thus not a breaking change. +as no filename contains = or :) and thus not a breaking change. All imports +in files in or below the directory ``context`` that import a file that +starts with ``prefix`` are redirected by replacing ``prefix`` by ``target``. So as an example, if you clone -`github.com/ethereum/dapp-bin/` locally to `/usr/local/dapp-bin`, you can use +``github.com/ethereum/dapp-bin/`` locally to ``/usr/local/dapp-bin``, you can use the following in your source file: :: @@ -88,26 +95,39 @@ the following in your source file: and then run the compiler as -.. code-block:: shell +.. code-block:: bash solc github.com/ethereum/dapp-bin/=/usr/local/dapp-bin/ source.sol +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 + +.. code-block:: bash + + solc module1:github.com/ethereum/dapp-bin/=/usr/local/dapp-bin/ \ + module2:github.com/ethereum/dapp-bin/=/usr/local/dapp-bin_old/ \ + source.sol + +so that all imports in ``module2`` point to the old version but imports +in ``module1`` get the new version. + Note that solc only allows you to include files from certain directories: They have to be in the directory (or subdirectory) of one of the explicitly specified source files or in the directory (or subdirectory) of a remapping target. If you want to allow direct absolute includes, just add the -remapping `=/`. +remapping ``=/``. If there are multiple remappings that lead to a valid file, the remapping with the longest common prefix is chosen. **browser-solidity**: -The `browser-based compiler <https://chriseth.github.io/browser-solidity>`_ +The `browser-based compiler <https://ethereum.github.io/browser-solidity>`_ provides an automatic remapping for github and will also automatically retrieve the file over the network: You can import the iterable mapping by e.g. -`import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping;`. +``import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping;``. Other source code providers may be added in the future. @@ -117,22 +137,23 @@ Other source code providers may be added in the future. Comments ======== -Single-line comments (`//`) and multi-line comments (`/*...*/`) are possible. +Single-line comments (``//``) and multi-line comments (``/*...*/``) are possible. :: // This is a single-line comment. - + /* - This is a + This is a multi-line comment. */ - - -There are special types of comments called natspec comments -(documentation yet to be written). These are introduced by -triple-slash comments (`///`) or using double asterisks (`/** ... */`). -Right in front of function declarations or statements, -you can use doxygen-style tags inside them to document functions, annotate conditions for formal -verification and provide a **confirmation text** that is shown to users if they want to -invoke a function. + + +Additionally, there is another type of comment called a natspec comment, +for which the documentation is not yet written. They are written with a +triple slash (``///``) or a double asterisk block(``/** ... */``) and +they should be used directly above function declarations or statements. +You can use Doxygen-style tags inside these comments to document +functions, annotate conditions for formal verification, and provide a +**confirmation text** which is shown to users when they attempt to invoke a +function. diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index f2ad0f88..c883815c 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -8,7 +8,7 @@ Miscellaneous Layout of State Variables in Storage ************************************ -Statically-sized variables (everything except mapping and dynamically-sized array types) are laid out contiguously in storage starting from position `0`. Multiple 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 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 that many bytes that are necessary to store them. @@ -17,33 +17,33 @@ Statically-sized variables (everything except mapping and dynamically-sized arra The elements of structs and arrays are stored after each other, just as if they were given explicitly. -Due to their unpredictable size, mapping and dynamically-sized array types use a `sha3` +Due to their unpredictable size, mapping and dynamically-sized array types use a ``sha3`` 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 an (unfilled) slot in storage at some position `p` according to the above rule (or by +occupies an (unfilled) slot in storage at some position ``p`` according to the above rule (or by recursively applying this rule for mappings to mappings or arrays of arrays). For a dynamic array, this slot stores the number of elements in the array (byte arrays and strings are an exception here, see below). For a mapping, 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 `sha3(p)` and the value corresponding to a mapping key -`k` is located at `sha3(k . p)` where `.` is concatenation. If the value is again a -non-elementary type, the positions are found by adding an offset of `sha3(k . p)`. +Array data is located at ``sha3(p)`` and the value corresponding to a mapping key +``k`` is located at ``sha3(k . p)`` where ``.`` is concatenation. If the value is again a +non-elementary type, the positions are found by adding an offset of ``sha3(k . p)``. -`bytes` and `string` store their data in the same slot where also the length is stored if they are short. In particular: If the data is at most `31` bytes long, it is stored in the higher-order bytes (left aligned) and the lowest-order byte stores `length * 2`. If it is longer, the main slot stores `length * 2 + 1` and the data is stored as usual in `sha3(slot)`. +``bytes`` and ``string`` store their data in the same slot where also the length is stored if they are short. In particular: If the data is at most ``31`` bytes long, it is stored in the higher-order bytes (left aligned) and the lowest-order byte stores ``length * 2``. If it is longer, the main slot stores ``length * 2 + 1`` and the data is stored as usual in ``sha3(slot)``. So for the following contract snippet:: - contract c { - struct S { uint a; uint b; } + contract C { + struct s { uint a; uint b; } uint x; - mapping(uint => mapping(uint => S)) data; + mapping(uint => mapping(uint => s)) data; } -The position of `data[4][9].b` is at `sha3(uint256(9) . sha3(uint256(4) . uint256(1))) + 1`. +The position of ``data[4][9].b`` is at ``sha3(uint256(9) . sha3(uint256(4) . uint256(1))) + 1``. ***************** Esoteric Features ***************** -There are some types in Solidity's type system that have no counterpart in the syntax. One of these types are the types of functions. But still, using `var` it is possible to have local variables of these types:: +There are some types in Solidity's type system that have no counterpart in the syntax. One of these types are the types of functions. But still, using ``var`` it is possible to have local variables of these types:: contract FunctionSelector { function select(bool useB, uint x) returns (uint z) { @@ -51,15 +51,17 @@ There are some types in Solidity's type system that have no counterpart in the s if (useB) f = b; return f(x); } + function a(uint x) returns (uint z) { return x * x; } + function b(uint x) returns (uint z) { return 2 * x; } } -Calling `select(false, x)` will compute `x * x` and `select(true, x)` will compute `2 * x`. +Calling ``select(false, x)`` will compute ``x * x`` and ``select(true, x)`` will compute ``2 * x``. .. index:: optimizer, common subexpression elimination, constant propagation @@ -67,7 +69,7 @@ Calling `select(false, x)` will compute `x * x` and `select(true, x)` will compu 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. +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. At the end of this process, we know which expressions have to be on the stack in 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 a JUMPI is found whose condition evaluates to a constant, it is transformed to an unconditional jump. @@ -95,45 +97,53 @@ even though the instructions contained a jump in the beginning. .. index:: ! commandline compiler, compiler;commandline, ! solc, ! linker +.. _commandline-compiler: + ****************************** Using the Commandline Compiler ****************************** -One of the build targets of the Solidity repository is `solc`, the solidity commandline compiler. -Using `solc --help` provides you with an explanation of all options. The compiler can produce various outputs, ranging from simple binaries and assembly over an abstract syntax tree (parse tree) to estimations of gas usage. -If you only want to compile a single file, you run it as `solc --bin sourceFile.sol` and it will print the binary. Before you deploy your contract, activate the optimizer while compiling using `solc --optimize --bin sourceFile.sol`. If you want to get some of the more advanced output variants of `solc`, it is probably better to tell it to output everything to separate files using `solc -o outputDirectory --bin --ast --asm sourceFile.sol`. +One of the build targets of the Solidity repository is ``solc``, the solidity commandline compiler. +Using ``solc --help`` provides you with an explanation of all options. The compiler can produce various outputs, ranging from simple binaries and assembly over an abstract syntax tree (parse tree) to estimations of gas usage. +If you only want to compile a single file, you run it as ``solc --bin sourceFile.sol`` and it will print the binary. Before you deploy your contract, activate the optimizer while compiling using ``solc --optimize --bin sourceFile.sol``. If you want to get some of the more advanced output variants of ``solc``, it is probably better to tell it to output everything to separate files using ``solc -o outputDirectory --bin --ast --asm sourceFile.sol``. The commandline compiler will automatically read imported files from the filesystem, but -it is also possible to provide path redirects using `prefix=path` in the following way: +it is also possible to provide path redirects using ``context:prefix=path`` in the following way: + +:: solc github.com/ethereum/dapp-bin/=/usr/local/lib/dapp-bin/ =/usr/local/lib/fallback file.sol This essentially instructs the compiler to search for anything starting with -`github.com/ethereum/dapp-bin/` under `/usr/local/lib/dapp-bin` and if it does not -find the file there, it will look at `/usr/local/lib/fallback` (the empty prefix -always matches). `solc` will not read files from the filesystem that lie outside of +``github.com/ethereum/dapp-bin/`` under ``/usr/local/lib/dapp-bin`` and if it does not +find the file there, it will look at ``/usr/local/lib/fallback`` (the empty prefix +always matches). ``solc`` will not read files from the filesystem that lie outside of the remapping targets and outside of the directories where explicitly specified source -files reside, so things like `import "/etc/passwd";` only work if you add `=/` as a remapping. +files reside, so things like ``import "/etc/passwd";`` only work if you add ``=/`` as a remapping. + +You can restrict remappings to only certain source files by prefixing a context. + +The section on :ref:`import` provides more details on remappings. If there are multiple matches due to remappings, the one with the longest common prefix is selected. -If your contracts use [libraries](#libraries), you will notice that the bytecode contains substrings of the form `__LibraryName______`. You can use `solc` as a linker meaning that it will insert the library addresses for you at those points: +If your contracts use :ref:`libraries <libraries>`, you will notice that the bytecode contains substrings of the form ``__LibraryName______``. You can use ``solc`` as a linker meaning that it will insert the library addresses for you at those points: -Either add `--libraries "Math:0x12345678901234567890 Heap:0xabcdef0123456"` to your command to provide an address for each library or store the string in a file (one library per line) and run `solc` using `--libraries fileName`. +Either add ``--libraries "Math:0x12345678901234567890 Heap:0xabcdef0123456"`` to your command to provide an address for each library or store the string in a file (one library per line) and run ``solc`` using ``--libraries fileName``. -If `solc` is called with the option `--link`, all input files are interpreted to be unlinked binaries (hex-encoded) in the `__LibraryName____`-format given above and are linked in-place (if the input is read from stdin, it is written to stdout). All options except `--libraries` are ignored (including `-o`) in this case. +If ``solc`` is called with the option ``--link``, all input files are interpreted to be unlinked binaries (hex-encoded) in the ``__LibraryName____``-format given above and are linked in-place (if the input is read from stdin, it is written to stdout). All options except ``--libraries`` are ignored (including ``-o``) in this case. *************** Tips and Tricks *************** -* Use `delete` on arrays to delete all its elements. +* 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! * Make your state variables public - the compiler will create :ref:`getters <visibility-and-accessors>` for you for free. * If you end up checking conditions on input or state a lot at the beginning of your functions, try using :ref:`modifiers`. -* If your contract has a function called `send` but you want to use the built-in send-function, use `address(contractVariable).send(amount)`. -* If you do **not** want your contracts to receive ether when called via `send`, you can add a throwing fallback function `function() { throw; }`. -* Initialise storage structs with a single assignment: `x = MyStruct({a: 1, b: 2});` +* If your contract has a function called ``send`` but you want to use the built-in send-function, use ``address(contractVariable).send(amount)``. +* If you do **not** want your contracts to receive ether when called via ``send``, you can add a throwing fallback function ``function() { throw; }``. +* Initialise storage structs with a single assignment: ``x = MyStruct({a: 1, b: 2});`` ******** Pitfalls @@ -141,7 +151,27 @@ Pitfalls Unfortunately, there are some subtleties the compiler does not yet warn you about. -- In `for (var i = 0; i < arrayName.length; i++) { ... }`, the type of `i` will be `uint8`, because this is the smallest type that is required to hold the value `0`. If the array has more than 255 elements, the loop will not terminate. +- In ``for (var i = 0; i < arrayName.length; i++) { ... }``, the type of ``i`` will be ``uint8``, because this is the smallest type that is required to hold the value ``0``. If the array has more than 255 elements, the loop will not terminate. +- If a contract receives Ether (without a function being called), the fallback function is executed. The contract can only rely + on the "gas stipend" (2300 gas) being available to it at that time. This stipend is not enough to access storage in any way. + To be sure that your contract can receive Ether in that way, check the gas requirements of the fallback function. +- If you want to send ether using ``address.send``, there are certain details to be aware of: + + 1. If the recipient is a contract, it causes its fallback function to be executed which can in turn call back into the sending contract + 2. Sending Ether can fail due to the call depth going above 1024. Since the caller is in total control of the call + depth, they can force the transfer to fail, so make sure to always check the return value of ``send``. Better yet, + write your contract using a pattern where the recipient can withdraw Ether instead. + 3. Sending Ether can also fail because the recipient runs out of gas (either explicitly by using ``throw`` or + because the operation is just too expensive). 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 practise here is to use + a "withdraw" pattern instead of a "send" pattern. + +- Loops that do not have a fixed number of iterations, e.g. loops that depends on storage values, have to be used carefully: + Due to the block gas limit, transactions can only consume a certain amount of gas. Either explicitly or just due to + normal operation, the number of iterations in a loop can grow beyond the block gas limit, which can cause the complete + contract to be stalled at a certain point. This does not apply at full extent to ``constant`` functions that are only executed + to read data from the blockchain. Still, such functions may be called by other contracts as part of on-chain operations + and stall those. Please be explicit about such cases in the documentation of your contracts. ********** Cheatsheet @@ -152,30 +182,30 @@ Cheatsheet Global Variables ================ -- `block.coinbase` (`address`): current block miner's address -- `block.difficulty` (`uint`): current block difficulty -- `block.gaslimit` (`uint`): current block gaslimit -- `block.number` (`uint`): current block number -- `block.blockhash` (`function(uint) returns (bytes32)`): hash of the given block - only works for 256 most recent blocks -- `block.timestamp` (`uint`): current block timestamp -- `msg.data` (`bytes`): complete calldata -- `msg.gas` (`uint`): remaining gas -- `msg.sender` (`address`): sender of the message (current call) -- `msg.value` (`uint`): number of wei sent with the message -- `now` (`uint`): current block timestamp (alias for `block.timestamp`) -- `tx.gasprice` (`uint`): gas price of the transaction -- `tx.origin` (`address`): sender of the transaction (full call chain) -- `sha3(...) returns (bytes32)`: compute the Ethereum-SHA3 hash of the (tightly packed) arguments -- `sha256(...) returns (bytes32)`: compute the SHA256 hash of the (tightly packed) arguments -- `ripemd160(...) returns (bytes20)`: compute RIPEMD of 256 the (tightly packed) arguments -- `ecrecover(bytes32, uint8, bytes32, bytes32) returns (address)`: recover public key from elliptic curve signature -- `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`. -- `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`. -- `this` (current contract's type): the current contract, explicitly convertible to `address` -- `super`: the contract one level higher in the inheritance hierarchy -- `selfdestruct(address)`: destroy the current contract, sending its funds to the given address -- `<address>.balance`: balance of the address in Wei -- `<address>.send(uint256) returns (bool)`: send given amount of Wei to address, returns `false` on failure. +- ``block.blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent blocks +- ``block.coinbase`` (``address``): current block miner's address +- ``block.difficulty`` (``uint``): current block difficulty +- ``block.gaslimit`` (``uint``): current block gaslimit +- ``block.number`` (``uint``): current block number +- ``block.timestamp`` (``uint``): current block timestamp +- ``msg.data`` (``bytes``): complete calldata +- ``msg.gas`` (``uint``): remaining gas +- ``msg.sender`` (``address``): sender of the message (current call) +- ``msg.value`` (``uint``): number of wei sent with the message +- ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``) +- ``tx.gasprice`` (``uint``): gas price of the transaction +- ``tx.origin`` (``address``): sender of the transaction (full call chain) +- ``sha3(...) returns (bytes32)``: compute the Ethereum-SHA-3 (KECCAK-256) hash of the (tightly packed) arguments +- ``sha256(...) returns (bytes32)``: compute the SHA-256 hash of the (tightly packed) arguments +- ``ripemd160(...) returns (bytes20)``: compute the RIPEMD-160 hash of the (tightly packed) arguments +- ``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``: recover address associated with the public key from elliptic curve signature +- ``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`` +- ``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`` +- ``this`` (current contract's type): the current contract, explicitly convertible to ``address`` +- ``super``: the contract one level higher in the inheritance hierarchy +- ``selfdestruct(address recipient)``: destroy the current contract, sending its funds to the given address +- ``<address>.balance`` (``uint256``): balance of the address in Wei +- ``<address>.send(uint256 amount) returns (bool)``: send given amount of Wei to address, returns ``false`` on failure .. index:: visibility, public, private, external, internal @@ -188,10 +218,10 @@ Function Visibility Specifiers return true; } -- `public`: visible externally and internally (creates accessor function for storage/state variables) -- `private`: only visible in the current contract -- `external`: only visible externally (only for functions) - i.e. can only be message-called (via `this.fun`) -- `internal`: only visible internally +- ``public``: visible externally and internally (creates accessor function for storage/state variables) +- ``private``: only visible in the current contract +- ``external``: only visible externally (only for functions) - i.e. can only be message-called (via ``this.fun``) +- ``internal``: only visible internally .. index:: modifiers, constant, anonymous, indexed @@ -199,8 +229,8 @@ Function Visibility Specifiers Modifiers ========= -- `constant` for state variables: Disallows assignment (except initialisation), does not occupy storage slot. -- `constant` for functions: Disallows modification of state - this is not enforced yet. -- `anonymous` for events: Does not store event signature as topic. -- `indexed` for event parameters: Stores the parameter as topic. +- ``constant`` for state variables: Disallows assignment (except initialisation), does not occupy storage slot. +- ``constant`` for functions: Disallows modification of state - this is not enforced yet. +- ``anonymous`` for events: Does not store event signature as topic. +- ``indexed`` for event parameters: Stores the parameter as topic. diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index 171e9273..6fa70be4 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -15,7 +15,7 @@ a lot of Solidity's features. It implements a voting contract. Of course, the main problems of electronic voting is how to assign voting rights to the correct persons and how to prevent manipulation. We will not -solve all problems here, but at least we will show +solve all problems here, but at least we will show how delegated voting can be done so that vote counting is **automatic and completely transparent** at the same time. @@ -30,12 +30,10 @@ The persons behind the addresses can then choose to either vote themselves or to delegate their vote to a person they trust. -At the end of the voting time, `winningProposal()` +At the end of the voting time, ``winningProposal()`` will return the proposal with the largest number of votes. -.. Gist: 618560d3f740204d46a5 - :: /// @title Voting with delegation. @@ -76,7 +74,7 @@ of votes. // to the end of the array. for (uint i = 0; i < proposalNames.length; i++) { // `Proposal({...})` creates a temporary - // Proposal object and `proposal.push(...)` + // Proposal object and `proposals.push(...)` // appends it to the end of `proposals`. proposals.push(Proposal({ name: proposalNames[i], @@ -108,9 +106,17 @@ of votes. // Forward the delegation as long as // `to` also delegated. - while (voters[to].delegate != address(0) && - voters[to].delegate != msg.sender) { - to = voters[to].delegate; + // In general, such loops are very dangerous, + // because if they run too long, they might + // need more gas than is available in a block. + // In this case, the delegation will not be executed, + // but in other situations, such loops might + // cause a contract to get "stuck" completely. + while ( + voters[to].delegate != address(0) && + voters[to].delegate != msg.sender + ) { + to = voters[to].delegate; } // We found a loop in the delegation, not allowed. @@ -125,7 +131,7 @@ of votes. Voter delegate = voters[to]; if (delegate.voted) { // If the delegate already voted, - // directly add to the number of votes + // directly add to the number of votes proposals[delegate.vote].voteCount += sender.weight; } else { @@ -199,8 +205,6 @@ contract has to be called manually for the beneficiary to receive his money - contracts cannot activate themselves. -.. {% include open_link gist="48cd2b65ff83bd04f7af" %} - :: contract SimpleAuction { @@ -215,6 +219,9 @@ activate themselves. address public highestBidder; uint public highestBid; + // Allowed withdrawals of previous bids + mapping(address => uint) pendingReturns; + // Set to true at the end, disallows any change bool ended; @@ -258,13 +265,29 @@ activate themselves. throw; } if (highestBidder != 0) { - highestBidder.send(highestBid); + // Sending back the money by simply using + // 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. + pendingReturns[highestBidder] += highestBid; } highestBidder = msg.sender; highestBid = msg.value; HighestBidIncreased(msg.sender, msg.value); } + /// Withdraw a bid that was overbid. + function withdraw() { + var amount = pendingReturns[msg.sender]; + // It is important to set this to zero because the recipient + // can call this function again as part of the receiving call + // before `send` returns. + pendingReturns[msg.sender] = 0; + if (!msg.sender.send(amount)) + throw; // If anything fails, this will revert the changes above + } + /// End the auction and send the highest bid /// to the beneficiary. function auctionEnd() { @@ -274,9 +297,8 @@ activate themselves. throw; // this function has already been called AuctionEnded(highestBidder, highestBid); - // We send all the money we have, because some - // of the refunds might have failed. - beneficiary.send(this.balance); + if (!beneficiary.send(highestBid)) + throw; ended = true; } @@ -292,7 +314,7 @@ activate themselves. } Blind Auction -================ +============= The previous open auction is extended to a blind auction in the following. The advantage of a blind auction is @@ -328,8 +350,6 @@ Bidders can confuse competition by placing several high or low invalid bids. -.. {% include open_link gist="70528429c2cd867dd1d6" %} - :: contract BlindAuction { @@ -349,6 +369,9 @@ high or low invalid bids. address public highestBidder; uint public highestBid; + // Allowed withdrawals of previous bids + mapping(address => uint) pendingReturns; + event AuctionEnded(address winner, uint highestBid); /// Modifiers are a convenient way to validate inputs to @@ -426,7 +449,8 @@ high or low invalid bids. // the same deposit. bid.blindedBid = 0; } - msg.sender.send(refund); + if (!msg.sender.send(refund)) + throw; } // This is an "internal" function which means that it @@ -440,13 +464,24 @@ high or low invalid bids. } if (highestBidder != 0) { // Refund the previously highest bidder. - highestBidder.send(highestBid); + pendingReturns[highestBidder] += highestBid; } highestBid = value; highestBidder = bidder; return true; } + /// Withdraw a bid that was overbid. + function withdraw() { + var amount = pendingReturns[msg.sender]; + // It is important to set this to zero because the recipient + // can call this function again as part of the receiving call + // before `send` returns. + pendingReturns[msg.sender] = 0; + if (!msg.sender.send(amount)) + throw; // If anything fails, this will revert the changes above + } + /// End the auction and send the highest bid /// to the beneficiary. function auctionEnd() @@ -457,7 +492,8 @@ high or low invalid bids. AuctionEnded(highestBidder, highestBid); // We send all the money we have, because some // of the refunds might have failed. - beneficiary.send(this.balance); + if (!beneficiary.send(this.balance)) + throw; ended = true; } @@ -472,8 +508,6 @@ high or low invalid bids. Safe Remote Purchase ******************** -.. {% include open_link gist="b16e8e76a423b7671e99" %} - :: contract Purchase { @@ -521,8 +555,9 @@ Safe Remote Purchase inState(State.Created) { aborted(); - seller.send(this.balance); state = State.Inactive; + if (!seller.send(this.balance)) + throw; } /// Confirm the purchase as buyer. @@ -545,9 +580,14 @@ Safe Remote Purchase inState(State.Locked) { itemReceived(); - buyer.send(value); // We ignore the return value on purpose - seller.send(this.balance); + // It is important to change the state first because + // otherwise, the contracts called using `send` below + // can call in again here. state = State.Inactive; + // This actually allows both the buyer and the seller to + // block the refund. + if (!buyer.send(value) || !seller.send(this.balance)) + throw; } function() { diff --git a/docs/structure-of-a-contract.rst b/docs/structure-of-a-contract.rst index 0968ffc8..79f78422 100644 --- a/docs/structure-of-a-contract.rst +++ b/docs/structure-of-a-contract.rst @@ -21,12 +21,12 @@ State variables are values which are permanently stored in contract storage. :: contract SimpleStorage { - uint storedData; // State variable - // ... + uint storedData; // State variable + // ... } See the :ref:`types` section for valid state variable types and -:ref:`visibility-and-accessors` for possible choices for +:ref:`visibility-and-accessors` for possible choices for visibility. .. _structure-functions: @@ -39,14 +39,14 @@ Functions are the executable units of code within a contract. :: contract SimpleAuction { - function bid() { // Function - // ... - } + function bid() { // Function + // ... + } } :ref:`function-calls` can happen internally or externally and have different levels of visibility (:ref:`visibility-and-accessors`) -towards other contracts. +towards other contracts. .. _structure-function-modifiers: @@ -57,18 +57,18 @@ Function modifiers can be used to amend the semantics of functions in a declarat (see :ref:`modifiers` in contracts section). :: - + contract Purchase { - address public seller; - - modifier onlySeller() { // Modifier - if (msg.sender != seller) throw; - _ - } - - function abort() onlySeller { // Modifier usage - // ... - } + address public seller; + + modifier onlySeller() { // Modifier + if (msg.sender != seller) throw; + _ + } + + function abort() onlySeller { // Modifier usage + // ... + } } .. _structure-events: @@ -81,15 +81,15 @@ Events are convenience interfaces with the EVM logging facilities. :: contract SimpleAuction { - event HighestBidIncreased(address bidder, uint amount); // Event - - function bid() { - // ... - HighestBidIncreased(msg.sender, msg.value); // Triggering event - } + event HighestBidIncreased(address bidder, uint amount); // Event + + function bid() { + // ... + HighestBidIncreased(msg.sender, msg.value); // Triggering event + } } -See :ref:`events` in contracts section for information on how events are declared +See :ref:`events` in contracts section for information on how events are declared and can be used from within a dapp. .. _structure-structs-types: @@ -97,18 +97,18 @@ and can be used from within a dapp. Structs Types ============= -Structs are custom defined types that can group several variables (see +Structs are custom defined types that can group several variables (see :ref:`structs` in types section). :: contract Ballot { - struct Voter { // Struct - uint weight; - bool voted; - address delegate; - uint vote; - } + struct Voter { // Struct + uint weight; + bool voted; + address delegate; + uint vote; + } } .. _structure-enum-types: @@ -116,11 +116,11 @@ Structs are custom defined types that can group several variables (see Enum Types ========== -Enums can be used to create custom types with a finite set of values (see +Enums can be used to create custom types with a finite set of values (see :ref:`enums` in types section). :: - + contract Purchase { - enum State { Created, Locked, Inactive } // Enum + enum State { Created, Locked, Inactive } // Enum } diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 9f1bcad5..99db8147 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -163,7 +163,7 @@ Yes:: No:: - spam( ham[ 1 ], Coin( { name: "ham" } ) );` + spam( ham[ 1 ], Coin( { name: "ham" } ) ); Exception:: @@ -225,11 +225,11 @@ No:: } } -The same recommendations apply to the control structures `if`, `else`, `while`, -and `for`. +The same recommendations apply to the control structures ``if``, ``else``, ``while``, +and ``for``. Additionally there should be a single space between the control structures -`if`, `while`, and `for` and the parenthetic block representing the +``if``, ``while``, and ``for`` and the parenthetic block representing the conditional, as well as a single space between the conditional parenthetic block and the opening brace. @@ -256,7 +256,7 @@ No:: for (...) { ...;} -For control structures who's body contains a single statement, omitting the +For control structures whose body contains a single statement, omitting the braces is ok *if* the statement is contained on a single line. Yes:: @@ -272,7 +272,7 @@ No:: value: 42 })); -For `if` blocks which have an `else` or `else if` clause, the `else` should be +For ``if`` blocks which have an ``else`` or ``else if`` clause, the ``else`` should be placed on it's own line following the previous closing parenthesis. The parenthesis for the else block should follow the same rules as the other conditional control structures. @@ -367,7 +367,7 @@ Yes:: address c, address d, address e, - address f, + address f ) { doSomething(); } @@ -449,7 +449,7 @@ No:: doSomething(); } -For constructor functions on inherited contracts who's bases require arguments, +For constructor functions on inherited contracts whose bases require arguments, it is recommended to drop the base constructors onto new lines in the same manner as modifiers if the function declaration is long or hard to read. diff --git a/docs/types.rst b/docs/types.rst index 0ec57afc..dbbd84d3 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -10,7 +10,7 @@ Solidity is a statically typed language, which means that the type of each variable (state and local) needs to be specified (or at least known - see :ref:`type-deduction` below) at compile-time. Solidity provides several elementary types which can be combined -to complex types. +to form complex types. .. index:: ! value type, ! type;value @@ -26,52 +26,56 @@ are used as function arguments or in assignments. Booleans -------- -`bool`: The possible values are constants `true` and `false`. +``bool``: The possible values are constants ``true`` and ``false``. -Operators: +Operators: -* `!` (logical negation) -* `&&` (logical conjunction, "and") -* `||` (logical disjunction, "or") -* `==` (equality) -* `!=` (inequality) +* ``!`` (logical negation) +* ``&&`` (logical conjunction, "and") +* ``||`` (logical disjunction, "or") +* ``==`` (equality) +* ``!=`` (inequality) -The operators `||` and `&&` apply the common short-circuiting rules. This means that in the expression `f(x) || g(y)`, if `f(x)` evaluates to `true`, `g(y)` will not be evaluated even if it may have side-effects. +The operators ``||`` and ``&&`` apply the common short-circuiting rules. This means that in the expression ``f(x) || g(y)``, if ``f(x)`` evaluates to ``true``, ``g(y)`` will not be evaluated even if it may have side-effects. .. index:: ! uint, ! int, ! integer Integers -------- -`int` / `uint`: Signed and unsigned integers of various sizes. Keywords `uint8` to `uint256` in steps of `8` (unsigned of 8 up to 256 bits) and `int8` to `int256`. `uint` and `int` are aliases for `uint256` and `int256`, respectively. +``int`` / ``uint``: Signed and unsigned integers of various sizes. Keywords ``uint8`` to ``uint256`` in steps of ``8`` (unsigned of 8 up to 256 bits) and ``int8`` to ``int256``. ``uint`` and ``int`` are aliases for ``uint256`` and ``int256``, respectively. -Operators: +Operators: -* Comparisons: `<=`, `<`, `==`, `!=`, `>=`, `>` (evaluate to `bool`) -* Bit operators: `&`, `|`, `^` (bitwise exclusive or), `~` (bitwise negation) -* Arithmetic operators: `+`, `-`, unary `-`, unary `+`, `*`, `/`, `%` (remainder), `**` (exponentiation) +* Comparisons: ``<=``, ``<``, ``==``, ``!=``, ``>=``, ``>`` (evaluate to ``bool``) +* Bit operators: ``&``, ``|``, ``^`` (bitwise exclusive or), ``~`` (bitwise negation) +* Arithmetic operators: ``+``, ``-``, unary ``-``, unary ``+``, ``*``, ``/``, ``%`` (remainder), ``**`` (exponentiation) Division always truncates (it just maps to the DIV opcode of the EVM), but it does not truncate if both -operators are :ref:`literals<integer_literals>` (or literal expressions). +operators are :ref:`literals<rational_literals>` (or literal expressions). .. index:: address, balance, send, call, callcode, delegatecall +.. _address: + Address ------- -`address`: Holds a 20 byte value (size of an Ethereum address). Address types also have members and serve as base for all contracts. +``address``: Holds a 20 byte value (size of an Ethereum address). Address types also have members and serve as base for all contracts. -Operators: +Operators: -* `<=`, `<`, `==`, `!=`, `>=` and `>` +* ``<=``, ``<``, ``==``, ``!=``, ``>=`` and ``>`` Members of Addresses ^^^^^^^^^^^^^^^^^^^^ -* `balance` and `send` +* ``balance`` and ``send`` + +For a quick reference, see :ref:`address_related`. -It is possible to query the balance of an address using the property `balance` -and to send Ether (in units of wei) to an address using the `send` function: +It is possible to query the balance of an address using the property ``balance`` +and to send Ether (in units of wei) to an address using the ``send`` function: :: @@ -80,12 +84,18 @@ and to send Ether (in units of wei) to an address using the `send` function: if (x.balance < 10 && myAddress.balance >= 10) x.send(10); .. note:: - If `x` is a contract address, its code (more specifically: its fallback function, if present) will be executed together with the `send` call (this is a limitation of the EVM and cannot be prevented). If that execution runs out of gas or fails in any way, the Ether transfer will be reverted. In this case, `send` returns `false`. + If ``x`` is a contract address, its code (more specifically: its fallback function, if present) will be executed together with the ``send`` call (this is a limitation of the EVM and cannot be prevented). If that execution runs out of gas or fails in any way, the Ether transfer will be reverted. In this case, ``send`` returns ``false``. + +.. warning:: + There are some dangers in using ``send``: The transfer fails if the call stack depth is at 1024 + (this can always be forced by the caller) and it also fails if the recipient runs out of gas. So in order + to make safe Ether transfers, always check the return value of ``send`` or even better: + Use a pattern where the recipient withdraws the money. -* `call`, `callcode` and `delegatecall` +* ``call``, ``callcode`` and ``delegatecall`` Furthermore, to interface with contracts that do not adhere to the ABI, -the function `call` is provided which takes an arbitrary number of arguments of any type. These arguments are padded to 32 bytes and concatenated. One exception is the case where the first argument is encoded to exactly four bytes. In this case, it is not padded to allow the use of function signatures here. +the function ``call`` is provided which takes an arbitrary number of arguments of any type. These arguments are padded to 32 bytes and concatenated. One exception is the case where the first argument is encoded to exactly four bytes. In this case, it is not padded to allow the use of function signatures here. :: @@ -93,15 +103,15 @@ the function `call` is provided which takes an arbitrary number of arguments of nameReg.call("register", "MyName"); nameReg.call(bytes4(sha3("fun(uint256)")), a); -`call` returns a boolean indicating whether the invoked function terminated (`true`) or caused an EVM exception (`false`). It is not possible to access the actual data returned (for this we would need to know the encoding and size in advance). +``call`` returns a boolean indicating whether the invoked function terminated (``true``) or caused an EVM exception (``false``). It is not possible to access the actual data returned (for this we would need to know the encoding and size in advance). -In a similar way, the function `delegatecall` can be used: The difference is that only the code of the given address is used, all other aspects (storage, balance, ...) are taken from the current contract. The purpose of `delegatecall` is to use library code which is stored in another contract. The user has to ensure that the layout of storage in both contracts is suitable for delegatecall to be used. Prior to homestead, only a limited variant called `callcode` was available that did not provide access to the original `msg.sender` and `msg.value` values. +In a similar way, the function ``delegatecall`` can be used: The difference is that only the code of the given address is used, all other aspects (storage, balance, ...) are taken from the current contract. The purpose of ``delegatecall`` is to use library code which is stored in another contract. The user has to ensure that the layout of storage in both contracts is suitable for delegatecall to be used. Prior to homestead, only a limited variant called ``callcode`` was available that did not provide access to the original ``msg.sender`` and ``msg.value`` values. -All three functions `call`, `delegatecall` and `callcode` are very low-level functions and should only be used as a *last resort* as they break the type-safety of Solidity. +All three functions ``call``, ``delegatecall`` and ``callcode`` are very low-level functions and should only be used as a *last resort* as they break the type-safety of Solidity. .. note:: All contracts inherit the members of address, so it is possible to query the balance of the - current contract using `this.balance`. + current contract using ``this.balance``. .. index:: byte array, bytes32 @@ -109,51 +119,92 @@ All three functions `call`, `delegatecall` and `callcode` are very low-level fun Fixed-size byte arrays ---------------------- -`bytes1`, `bytes2`, `bytes3`, ..., `bytes32`. `byte` is an alias for `bytes1`. +``bytes1``, ``bytes2``, ``bytes3``, ..., ``bytes32``. ``byte`` is an alias for ``bytes1``. -Operators: +Operators: -* Comparisons: `<=`, `<`, `==`, `!=`, `>=`, `>` (evaluate to `bool`) -* Bit operators: `&`, `|`, `^` (bitwise exclusive or), `~` (bitwise negation) -* Index access: If `x` is of type `bytesI`, then `x[k]` for `0 <= k < I` returns the `k` th byte (read-only). +* Comparisons: ``<=``, ``<``, ``==``, ``!=``, ``>=``, ``>`` (evaluate to ``bool``) +* Bit operators: ``&``, ``|``, ``^`` (bitwise exclusive or), ``~`` (bitwise negation) +* Index access: If ``x`` is of type ``bytesI``, then ``x[k]`` for ``0 <= k < I`` returns the ``k`` th byte (read-only). Members: -* `.length` yields the fixed length of the byte array (read-only). +* ``.length`` yields the fixed length of the byte array (read-only). Dynamically-sized byte array ---------------------------- -`bytes`: - Dynamically-sized byte array, see :ref:`arrays`. Not a value-type! -`string`: +``bytes``: + Dynamically-sized byte array, see :ref:`arrays`. Not a value-type! +``string``: Dynamically-sized UTF8-encoded string, see :ref:`arrays`. Not a value-type! -As a rule of thumb, use `bytes` for arbitrary-length raw byte data and `string` +As a rule of thumb, use ``bytes`` for arbitrary-length raw byte data and ``string`` for arbitrary-length string (utf-8) data. If you can limit the length to a certain -number of bytes, always use one of `bytes1` to `bytes32` because they are much cheaper. +number of bytes, always use one of ``bytes1`` to ``bytes32`` because they are much cheaper. + +.. index:: ! ufixed, ! fixed, ! fixed point number + +Fixed Point Numbers +------------------- + +**COMING SOON...** + +.. index:: literal, literal;rational -.. index:: literal, literal;integer +.. _rational_literals: -.. _integer_literals: +Rational and Integer Literals +----------------------------- -Integer Literals ------------------ +All number literals retain arbitrary precision until they are converted to a non-literal type (i.e. by +using them together with a non-literal type). This means that computations do not overflow but also +divisions do not truncate. -Integer Literals are arbitrary precision integers until they are used together with a non-literal. In `var x = 1 - 2;`, for example, the value of `1 - 2` is `-1`, which is assigned to `x` and thus `x` receives the type `int8` -- the smallest type that contains `-1`, although the natural types of `1` and `2` are actually `uint8`. +For example, ``(2**800 + 1) - 2**800`` results in the constant ``1`` (of type ``uint8``) +although intermediate results would not even fit the machine word size. Furthermore, ``.5 * 8`` results +in the integer ``4`` (although non-integers were used in between). -It is even possible to temporarily exceed the maximum of 256 bits as long as only integer literals are used for the computation: `var x = (0xffffffffffffffffffff * 0xffffffffffffffffffff) * 0;` Here, `x` will have the value `0` and thus the type `uint8`. +If the result is not an integer, +an appropriate ``ufixed`` or ``fixed`` type is used whose number of fractional bits is as large as +required (approximating the rational number in the worst case). + +In ``var x = 1/4;``, ``x`` will receive the type ``ufixed0x8`` while in ``var x = 1/3`` it will receive +the type ``ufixed0x256`` because ``1/3`` is not finitely representable in binary and will thus be +approximated. + +Any operator that can be applied to integers can also be applied to literal expressions as +long as the operators are integers. If any of the two is fractional, bit operations are disallowed +and exponentiation is disallowed if the exponent is fractional (because that might result in +a non-rational number). + +.. note:: + Most finite decimal fractions like ``5.3743`` are not finitely representable in binary. The correct type + for ``5.3743`` is ``ufixed8x248`` because that allows to best approximate the number. If you want to + use the number together with types like ``ufixed`` (i.e. ``ufixed128x128``), you have to explicitly + specify the desired precision: ``x + ufixed(5.3743)``. .. warning:: - Divison on integer literals used to truncate in earlier versions, but it will actually convert into a rational number in the future, i.e. `1/2` is not equal to `0`, but to `0.5`. + Division on integer literals used to truncate in earlier versions, but it will now convert into a rational number, i.e. ``5 / 2`` is not equal to ``2``, but to ``2.5``. + +.. note:: + Literal expressions are converted to a permanent type as soon as they are used with other + expressions. Even though we know that the value of the + expression assigned to ``b`` in the following example evaluates to an integer, it still + uses fixed point types (and not rational number literals) in between and so the code + does not compile + +:: + uint128 a = 1; + uint128 b = 2.5 + a + 0.5; .. index:: literal, literal;string, string String Literals --------------- -String Literals are written with double quotes (`"abc"`). As with integer literals, their type can vary, but they are implicitly convertible to `bytes` if they fit, to `bytes` and to `string`. +String Literals are written with double quotes (``"abc"``). As with integer literals, their type can vary, but they are implicitly convertible to ``bytes`` if they fit, to ``bytes`` and to ``string``. .. index:: enum @@ -171,21 +222,21 @@ to and from all integer types but implicit conversion is not allowed. enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill } ActionChoices choice; ActionChoices constant defaultChoice = ActionChoices.GoStraight; - function setGoStraight() - { + + function setGoStraight() { choice = ActionChoices.GoStraight; } + // Since enum types are not part of the ABI, the signature of "getChoice" // will automatically be changed to "getChoice() returns (uint8)" // for all matters external to Solidity. The integer type used is just // large enough to hold all enum values, i.e. if you have more values, // `uint16` will be used and so on. - function getChoice() returns (ActionChoices) - { + function getChoice() returns (ActionChoices) { return choice; } - function getDefaultChoice() returns (uint) - { + + function getDefaultChoice() returns (uint) { return uint(defaultChoice); } } @@ -207,8 +258,8 @@ Data location Every complex type, i.e. *arrays* and *structs*, has an additional annotation, the "data location", about whether it is stored in memory or in storage. Depending on the context, there is always a default, but it can be overridden by appending -either `storage` or `memory` to the type. The default for function parameters (including return parameters) is `memory`, the default for local variables is `storage` and the location is forced -to `storage` for state variables (obviously). +either ``storage`` or ``memory`` to the type. The default for function parameters (including return parameters) is ``memory``, the default for local variables is ``storage`` and the location is forced +to ``storage`` for state variables (obviously). There is also a third data location, "calldata", which is a non-modifyable non-persistent area where function arguments are stored. Function parameters @@ -226,26 +277,28 @@ memory-stored reference type does not create a copy. :: - contract c { - uint[] x; // the data location of x is storage - // the data location of memoryArray is memory - function f(uint[] memoryArray) { - x = memoryArray; // works, copies the whole array to storage - var y = x; // works, assigns a pointer, data location of y is storage - y[7]; // fine, returns the 8th element - y.length = 2; // fine, modifies x through y - delete x; // fine, clears the array, also modifies y - // The following does not work; it would need to create a new temporary / - // unnamed array in storage, but storage is "statically" allocated: - // y = memoryArray; - // This does not work either, since it would "reset" the pointer, but there - // is no sensible location it could point to. - // delete y; - g(x); // calls g, handing over a reference to x - h(x); // calls h and creates an independent, temporary copy in memory - } - function g(uint[] storage storageArray) internal {} - function h(uint[] memoryArray) {} + contract C { + uint[] x; // the data location of x is storage + + // the data location of memoryArray is memory + function f(uint[] memoryArray) { + x = memoryArray; // works, copies the whole array to storage + var y = x; // works, assigns a pointer, data location of y is storage + y[7]; // fine, returns the 8th element + y.length = 2; // fine, modifies x through y + delete x; // fine, clears the array, also modifies y + // The following does not work; it would need to create a new temporary / + // unnamed array in storage, but storage is "statically" allocated: + // y = memoryArray; + // This does not work either, since it would "reset" the pointer, but there + // is no sensible location it could point to. + // delete y; + g(x); // calls g, handing over a reference to x + h(x); // calls h and creates an independent, temporary copy in memory + } + + function g(uint[] storage storageArray) internal {} + function h(uint[] memoryArray) {} } Summary @@ -271,23 +324,23 @@ For storage arrays, the element type can be arbitrary (i.e. also other arrays, mappings or structs). For memory arrays, it cannot be a mapping and has to be an ABI type if it is an argument of a publicly-visible function. -An array of fixed size `k` and element type `T` is written as `T[k]`, -an array of dynamic size as `T[]`. As an example, an array of 5 dynamic -arrays of `uint` is `uint[][5]` (note that the notation is reversed when +An array of fixed size ``k`` and element type ``T`` is written as ``T[k]``, +an array of dynamic size as ``T[]``. As an example, an array of 5 dynamic +arrays of ``uint`` is ``uint[][5]`` (note that the notation is reversed when compared to some other languages). To access the second uint in the -third dynamic array, you use `x[2][1]` (indices are zero-based and -access works in the opposite way of the declaration, i.e. `x[2]` +third dynamic array, you use ``x[2][1]`` (indices are zero-based and +access works in the opposite way of the declaration, i.e. ``x[2]`` shaves off one level in the type from the right). -Variables of type `bytes` and `string` are special arrays. A `bytes` is similar to `byte[]`, -but it is packed tightly in calldata. `string` is equal to `bytes` but does not allow +Variables of type ``bytes`` and ``string`` are special arrays. A ``bytes`` is similar to ``byte[]``, +but it is packed tightly in calldata. ``string`` is equal to ``bytes`` but does not allow length or index access (for now). -So `bytes` should always be preferred over `byte[]` because it is cheaper. +So ``bytes`` should always be preferred over ``byte[]`` because it is cheaper. .. note:: - If you want to access the byte-representation of a string `s`, use - `bytes(s).length` / `bytes(s)[7] = 'x';`. Keep in mind + If you want to access the byte-representation of a string ``s``, use + ``bytes(s).length`` / ``bytes(s)[7] = 'x';``. Keep in mind that you are accessing the low-level bytes of the utf-8 representation, and not the individual characters! @@ -296,21 +349,59 @@ So `bytes` should always be preferred over `byte[]` because it is cheaper. Allocating Memory Arrays ^^^^^^^^^^^^^^^^^^^^^^^^ -Creating arrays with variable length in memory can be done using the `new` keyword. +Creating arrays with variable length in memory can be done using the ``new`` keyword. As opposed to storage arrays, it is **not** possible to resize memory arrays by assigning to -the `.length` member. +the ``.length`` member. + +:: + + contract C { + function f(uint len) { + uint[] memory a = new uint[](7); + bytes memory b = new bytes(len); + // Here we have a.length == 7 and b.length == len + a[6] = 8; + } + } + +.. index:: ! array;literals, !inline;arrays + +Array Literals / Inline Arrays +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Array literals are arrays that are written as an expression and are not +assigned to a variable right away. + +:: + + contract C { + function f() { + g([uint(1), 2, 3]); + } + function g(uint[3] _data) { + // ... + } + } + +The type of an array literal is a memory array of fixed size whose base +type is the common type of the given elements. The type of ``[1, 2, 3]`` is +``uint8[3] memory``, because the type of each of these constants is ``uint8``. +Because of that, it was necessary to convert the first element in the example +above to ``uint``. Note that currently, fixed size memory arrays cannot +be assigned to dynamically-sized memory arrays, i.e. the following is not +possible: :: contract C { - function f(uint len) { - uint[] memory a = new uint[](7); - bytes memory b = new bytes(len); - // Here we have a.length == 7 and b.length == len - a[6] = 8; - } + function f() { + // The next line creates a type error because uint[3] memory + // cannot be converted to uint[] memory. + uint[] x = [uint(1), 3, 4]; } +It is planned to remove this restriction in the future but currently creates +some complications because of how arrays are passed in the ABI. .. index:: ! array;length, length, push, !array;push @@ -318,19 +409,19 @@ Members ^^^^^^^ **length**: - Arrays have a `length` member to hold their number of elements. + Arrays have a ``length`` member to hold their number of elements. Dynamic arrays can be resized in storage (not in memory) by changing the - `.length` member. This does not happen automatically when attempting to access elements outside the current length. The size of memory arrays is fixed (but dynamic, i.e. it can depend on runtime parameters) once they are created. + ``.length`` member. This does not happen automatically when attempting to access elements outside the current length. The size of memory arrays is fixed (but dynamic, i.e. it can depend on runtime parameters) once they are created. **push**: - Dynamic storage arrays and `bytes` (not `string`) have a member function called `push` that can be used to append an element at the end of the array. The function returns the new length. + Dynamic storage arrays and ``bytes`` (not ``string``) have a member function called ``push`` that can be used to append an element at the end of the array. The function returns the new length. .. warning:: It is not yet possible to use arrays of arrays in external functions. .. warning:: Due to limitations of the EVM, it is not possible to return - dynamic content from external function calls. The function `f` in - `contract C { function f() returns (uint[]) { ... } }` will return + dynamic content from external function calls. The function ``f`` in + ``contract C { function f() returns (uint[]) { ... } }`` will return something if called from web3.js, but not if called from Solidity. The only workaround for now is to use large statically-sized arrays. @@ -339,51 +430,59 @@ Members :: contract ArrayContract { - uint[2**20] m_aLotOfIntegers; - // Note that the following is not a pair of arrays but an array of pairs. - bool[2][] m_pairsOfFlags; - // newPairs is stored in memory - the default for function arguments - function setAllFlagPairs(bool[2][] newPairs) { - // assignment to a storage array replaces the complete array - m_pairsOfFlags = newPairs; - } - function setFlagPair(uint index, bool flagA, bool flagB) { - // access to a non-existing index will throw an exception - m_pairsOfFlags[index][0] = flagA; - m_pairsOfFlags[index][1] = flagB; - } - function changeFlagArraySize(uint newSize) { - // if the new size is smaller, removed array elements will be cleared - m_pairsOfFlags.length = newSize; - } - function clear() { - // these clear the arrays completely - delete m_pairsOfFlags; - delete m_aLotOfIntegers; - // identical effect here - m_pairsOfFlags.length = 0; - } - bytes m_byteData; - function byteArrays(bytes data) { - // byte arrays ("bytes") are different as they are stored without padding, - // but can be treated identical to "uint8[]" - m_byteData = data; - m_byteData.length += 7; - m_byteData[3] = 8; - delete m_byteData[2]; - } - function addFlag(bool[2] flag) returns (uint) { - return m_pairsOfFlags.push(flag); - } - function createMemoryArray(uint size) returns (bytes) { - // Dynamic memory arrays are created using `new`: - uint[2][] memory arrayOfPairs = new uint[2][](size); - // Create a dynamic byte array: - bytes memory b = new bytes(200); - for (uint i = 0; i < b.length; i++) - b[i] = byte(i); - return b; - } + uint[2**20] m_aLotOfIntegers; + // Note that the following is not a pair of arrays but an array of pairs. + bool[2][] m_pairsOfFlags; + // newPairs is stored in memory - the default for function arguments + + function setAllFlagPairs(bool[2][] newPairs) { + // assignment to a storage array replaces the complete array + m_pairsOfFlags = newPairs; + } + + function setFlagPair(uint index, bool flagA, bool flagB) { + // access to a non-existing index will throw an exception + m_pairsOfFlags[index][0] = flagA; + m_pairsOfFlags[index][1] = flagB; + } + + function changeFlagArraySize(uint newSize) { + // if the new size is smaller, removed array elements will be cleared + m_pairsOfFlags.length = newSize; + } + + function clear() { + // these clear the arrays completely + delete m_pairsOfFlags; + delete m_aLotOfIntegers; + // identical effect here + m_pairsOfFlags.length = 0; + } + + bytes m_byteData; + + function byteArrays(bytes data) { + // byte arrays ("bytes") are different as they are stored without padding, + // but can be treated identical to "uint8[]" + m_byteData = data; + m_byteData.length += 7; + m_byteData[3] = 8; + delete m_byteData[2]; + } + + function addFlag(bool[2] flag) returns (uint) { + return m_pairsOfFlags.push(flag); + } + + function createMemoryArray(uint size) returns (bytes) { + // Dynamic memory arrays are created using `new`: + uint[2][] memory arrayOfPairs = new uint[2][](size); + // Create a dynamic byte array: + bytes memory b = new bytes(200); + for (uint i = 0; i < b.length; i++) + b[i] = byte(i); + return b; + } } @@ -400,41 +499,46 @@ shown in the following example: :: contract CrowdFunding { - // Defines a new type with two fields. - struct Funder { - address addr; - uint amount; - } - struct Campaign { - address beneficiary; - uint fundingGoal; - uint numFunders; - uint amount; - mapping (uint => Funder) funders; - } - uint numCampaigns; - mapping (uint => Campaign) campaigns; - function newCampaign(address beneficiary, uint goal) returns (uint campaignID) { - campaignID = numCampaigns++; // campaignID is return variable - // Creates new struct and saves in storage. We leave out the mapping type. - campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0); - } - function contribute(uint campaignID) { - Campaign c = campaigns[campaignID]; + // Defines a new type with two fields. + struct Funder { + address addr; + uint amount; + } + + struct Campaign { + address beneficiary; + uint fundingGoal; + uint numFunders; + uint amount; + mapping (uint => Funder) funders; + } + + uint numCampaigns; + mapping (uint => Campaign) campaigns; + + function newCampaign(address beneficiary, uint goal) returns (uint campaignID) { + campaignID = numCampaigns++; // campaignID is return variable + // Creates new struct and saves in storage. We leave out the mapping type. + campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0); + } + + function contribute(uint campaignID) { + Campaign c = campaigns[campaignID]; // Creates a new temporary memory struct, initialised with the given values // and copies it over to storage. // Note that you can also use Funder(msg.sender, msg.value) to initialise. - c.funders[c.numFunders++] = Funder({addr: msg.sender, amount: msg.value}); - c.amount += msg.value; - } - function checkGoalReached(uint campaignID) returns (bool reached) { - Campaign c = campaigns[campaignID]; - if (c.amount < c.fundingGoal) - return false; - c.beneficiary.send(c.amount); - c.amount = 0; - return true; - } + c.funders[c.numFunders++] = Funder({addr: msg.sender, amount: msg.value}); + c.amount += msg.value; + } + + function checkGoalReached(uint campaignID) returns (bool reached) { + Campaign c = campaigns[campaignID]; + if (c.amount < c.fundingGoal) + return false; + c.beneficiary.send(c.amount); + c.amount = 0; + return true; + } } The contract does not provide the full functionality of a crowdfunding @@ -453,21 +557,21 @@ members of the local variable actually write to the state. Of course, you can also directly access the members of the struct without assigning it to a local variable, as in -`campaigns[campaignID].amount = 0`. +``campaigns[campaignID].amount = 0``. .. index:: !mapping Mappings ======== -Mapping types are declared as `mapping _KeyType => _ValueType`, where -`_KeyType` can be almost any type except for a mapping and `_ValueType` +Mapping types are declared as ``mapping _KeyType => _ValueType``, where +``_KeyType`` can be almost any type except for a mapping and ``_ValueType`` can actually be any type, including mappings. Mappings can be seen as hashtables which are virtually initialized such that every possible key exists and is mapped to a value whose byte-representation is all zeros. The similarity ends here, though: The key data is not actually stored -in a mapping, only its `sha3` hash used to look up the value. +in a mapping, only its ``sha3`` hash 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". @@ -479,34 +583,35 @@ in internal functions). Operators Involving LValues =========================== -If `a` is an LValue (i.e. a variable or something that can be assigned to), the following operators are available as shorthands: +If ``a`` is an LValue (i.e. a variable or something that can be assigned to), the following operators are available as shorthands: -`a += e` is equivalent to `a = a + e`. The operators `-=`, `*=`, `/=`, `%=`, `a |=`, `&=` and `^=` are defined accordingly. `a++` and `a--` are equivalent to `a += 1` / `a -= 1` but the expression itself still has the previous value of `a`. In contrast, `--a` and `++a` have the same effect on `a` but return the value after the change. +``a += e`` is equivalent to ``a = a + e``. The operators ``-=``, ``*=``, ``/=``, ``%=``, ``a |=``, ``&=`` and ``^=`` are defined accordingly. ``a++`` and ``a--`` are equivalent to ``a += 1`` / ``a -= 1`` but the expression itself still has the previous value of ``a``. In contrast, ``--a`` and ``++a`` have the same effect on ``a`` but return the value after the change. delete ------ -`delete a` assigns the initial value for the type to `a`. I.e. for integers it is equivalent to `a = 0`, but it can also be used on arrays, where it assigns a dynamic array of length zero or a static array of the same length with all elements reset. For structs, it assigns a struct with all members reset. +``delete a`` assigns the initial value for the type to ``a``. I.e. for integers it is equivalent to ``a = 0``, but it can also be used on arrays, where it assigns a dynamic array of length zero or a static array of the same length with all elements reset. For structs, it assigns a struct with all members reset. -`delete` has no effect on whole mappings (as the keys of mappings may be arbitrary and are generally unknown). So if you delete a struct, it will reset all members that are not mappings and also recurse into the members unless they are mappings. However, individual keys and what they map to can be deleted. +``delete`` has no effect on whole mappings (as the keys of mappings may be arbitrary and are generally unknown). So if you delete a struct, it will reset all members that are not mappings and also recurse into the members unless they are mappings. However, individual keys and what they map to can be deleted. -It is important to note that `delete a` really behaves like an assignment to `a`, i.e. it stores a new object in `a`. +It is important to note that ``delete a`` really behaves like an assignment to ``a``, i.e. it stores a new object in ``a``. :: contract DeleteExample { - uint data; - uint[] dataArray; - function f() { - uint x = data; - delete x; // sets x to 0, does not affect data - delete data; // sets data to 0, does not affect x which still holds a copy - uint[] y = dataArray; - delete dataArray; // this sets dataArray.length to zero, but as uint[] is a complex object, also - // y is affected which is an alias to the storage object - // On the other hand: "delete y" is not valid, as assignments to local variables - // referencing storage objects can only be made from existing storage objects. - } + uint data; + uint[] dataArray; + + function f() { + uint x = data; + delete x; // sets x to 0, does not affect data + delete data; // sets data to 0, does not affect x which still holds a copy + uint[] y = dataArray; + delete dataArray; // this sets dataArray.length to zero, but as uint[] is a complex object, also + // y is affected which is an alias to the storage object + // On the other hand: "delete y" is not valid, as assignments to local variables + // referencing storage objects can only be made from existing storage objects. + } } .. index:: ! type;conversion, ! cast @@ -521,12 +626,12 @@ If an operator is applied to different types, the compiler tries to implicitly convert one of the operands to the type of the other (the same is true for assignments). In general, an implicit conversion between value-types is possible if it -makes sense semantically and no information is lost: `uint8` is convertible to -`uint16` and `int128` to `int256`, but `int8` is not convertible to `uint256` -(because `uint256` cannot hold e.g. `-1`). +makes sense semantically and no information is lost: ``uint8`` is convertible to +``uint16`` and ``int128`` to ``int256``, but ``int8`` is not convertible to ``uint256`` +(because ``uint256`` cannot hold e.g. ``-1``). Furthermore, unsigned integers can be converted to bytes of the same or larger -size, but not vice-versa. Any type that can be converted to `uint160` can also -be converted to `address`. +size, but not vice-versa. Any type that can be converted to ``uint160`` can also +be converted to ``address``. Explicit Conversions -------------------- @@ -537,7 +642,7 @@ doing, an explicit type conversion is sometimes possible:: int8 y = -3; uint x = uint(y); -At the end of this code snippet, `x` will have the value `0xfffff..fd` (64 hex +At the end of this code snippet, ``x`` will have the value ``0xfffff..fd`` (64 hex characters), which is -3 in two's complement representation of 256 bits. If a type is explicitly converted to a smaller type, higher-order bits are @@ -557,15 +662,15 @@ For convenience, it is not always necessary to explicitly specify the type of a variable, the compiler automatically infers it from the type of the first expression that is assigned to the variable:: - uint20 x = 0x123; + uint24 x = 0x123; var y = x; -Here, the type of `y` will be `uint20`. Using `var` is not possible for function +Here, the type of ``y`` will be ``uint24``. Using ``var`` is not possible for function parameters or return parameters. .. warning:: The type is only deduced from the first assignment, so - the loop in the following snippet is infinite, as `i` will have the type - `uint8` and any value of this type is smaller than `2000`. - `for (var i = 0; i < 2000; i++) { ... }` + the loop in the following snippet is infinite, as ``i`` will have the type + ``uint8`` and any value of this type is smaller than ``2000``. + ``for (var i = 0; i < 2000; i++) { ... }`` diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index a4239a29..8b8f4daa 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -7,23 +7,23 @@ Units and Globally Available Variables Ether Units =========== -A literal number can take a suffix of `wei`, `finney`, `szabo` or `ether` to convert between the subdenominations of Ether, where Ether currency numbers without a postfix are assumed to be "wei", e.g. `2 ether == 2000 finney` evaluates to `true`. +A literal number can take a suffix of ``wei``, ``finney``, ``szabo`` or ``ether`` to convert between the subdenominations of Ether, where Ether currency numbers without a postfix are assumed to be "wei", e.g. ``2 ether == 2000 finney`` evaluates to ``true``. .. index:: time, seconds, minutes, hours, days, weeks, years Time Units ========== -Suffixes of `seconds`, `minutes`, `hours`, `days`, `weeks` and -`years` after literal numbers can be used to convert between units of time where seconds are the base +Suffixes of ``seconds``, ``minutes``, ``hours``, ``days``, ``weeks`` and +``years`` after literal numbers can be used to convert between units of time where seconds are the base unit and units are considered naively in the following way: - * `1 == 1 second` - * `1 minutes == 60 seconds` - * `1 hours == 60 minutes` - * `1 days == 24 hours` - * `1 weeks = 7 days` - * `1 years = 365 days` + * ``1 == 1 second`` + * ``1 minutes == 60 seconds`` + * ``1 hours == 60 minutes`` + * ``1 days == 24 hours`` + * ``1 weeks = 7 days`` + * ``1 years = 365 days`` Take care if you perform calendar calculations using these units, because not every year equals 365 days and not even every day has 24 hours @@ -35,7 +35,7 @@ These suffixes cannot be applied to variables. If you want to interpret some input variable in e.g. days, you can do it in the following way:: function f(uint start, uint daysAfter) { - if (now >= start + daysAfter * 1 days) { ... } + if (now >= start + daysAfter * 1 days) { ... } } Special Variables and Functions @@ -48,31 +48,31 @@ namespace and are mainly used to provide information about the blockchain. Block and Transaction Properties ------------------------------------- - - - `block.coinbase` (`address`): current block miner's address - - `block.difficulty` (`uint`): current block difficulty - - `block.gaslimit` (`uint`): current block gaslimit - - `block.number` (`uint`): current block number - - `block.blockhash` (`function(uint) returns (bytes32)`): hash of the given block - only for 256 most recent blocks - - `block.timestamp` (`uint`): current block timestamp - - `msg.data` (`bytes`): complete calldata - - `msg.gas` (`uint`): remaining gas - - `msg.sender` (`address`): sender of the message (current call) - - `msg.sig` (`bytes4`): first four bytes of the calldata (i.e. function identifier) - - `msg.value` (`uint`): number of wei sent with the message - - `now` (`uint`): current block timestamp (alias for `block.timestamp`) - - `tx.gasprice` (`uint`): gas price of the transaction - - `tx.origin` (`address`): sender of the transaction (full call chain) +-------------------------------- + +- ``block.blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent blocks +- ``block.coinbase`` (``address``): current block miner's address +- ``block.difficulty`` (``uint``): current block difficulty +- ``block.gaslimit`` (``uint``): current block gaslimit +- ``block.number`` (``uint``): current block number +- ``block.timestamp`` (``uint``): current block timestamp +- ``msg.data`` (``bytes``): complete calldata +- ``msg.gas`` (``uint``): remaining gas +- ``msg.sender`` (``address``): sender of the message (current call) +- ``msg.sig`` (``bytes4``): first four bytes of the calldata (i.e. function identifier) +- ``msg.value`` (``uint``): number of wei sent with the message +- ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``) +- ``tx.gasprice`` (``uint``): gas price of the transaction +- ``tx.origin`` (``address``): sender of the transaction (full call chain) .. note:: - The values of all members of `msg`, including `msg.sender` and - `msg.value` can change for every **external** function call. + The values of all members of ``msg``, including ``msg.sender`` and + ``msg.value`` can change for every **external** function call. This includes calls to library functions. If you want to implement access restrictions in library functions using - `msg.sender`, you have to manually supply the value of - `msg.sender` as an argument. + ``msg.sender``, you have to manually supply the value of + ``msg.sender`` as an argument. .. note:: The block hashes are not available for all blocks for scalability reasons. @@ -84,18 +84,18 @@ Block and Transaction Properties Mathematical and Cryptographic Functions ---------------------------------------- -`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`. -`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`. -`sha3(...) returns (bytes32)`: - compute the Ethereum-SHA-3 hash of the (tightly packed) arguments -`sha256(...) returns (bytes32)`: +``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``. +``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``. +``sha3(...) returns (bytes32)``: + compute the Ethereum-SHA-3 (KECCAK-256) hash of the (tightly packed) arguments +``sha256(...) returns (bytes32)``: compute the SHA-256 hash of the (tightly packed) arguments -`ripemd160(...) returns (bytes20)`: +``ripemd160(...) returns (bytes20)``: compute RIPEMD-160 hash of the (tightly packed) arguments -`ecrecover(bytes32, uint8, bytes32, bytes32) returns (address)`: - recover public key from elliptic curve signature - arguments are (data, v, r, s) +``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``: + recover the address associated with the public key from elliptic curve signature In the above, "tightly packed" means that the arguments are concatenated without padding. This means that the following are all identical:: @@ -106,20 +106,39 @@ This means that the following are all identical:: sha3(6382179) sha3(97, 98, 99) -If padding is needed, explicit type conversions can be used: `sha3("\x00\x12")` is the -same as `sha3(uint16(0x12))`. +If padding is needed, explicit type conversions can be used: ``sha3("\x00\x12")`` is the +same as ``sha3(uint16(0x12))``. -It might be that you run into Out-of-Gas for `sha256`, `ripemd160` or `ecrecover` on a *private blockchain*. The reason for this is that those are implemented as so-called precompiled contracts and these contracts only really exist after they received the first message (although their contract code is hardcoded). Messages to non-existing contracts are more expensive and thus the execution runs into an Out-of-Gas error. A workaround for this problem is to first send e.g. 1 Wei to each of the contracts before you use them in your actual contracts. This is not an issue on the official or test net. +It might be that you run into Out-of-Gas for ``sha256``, ``ripemd160`` or ``ecrecover`` on a *private blockchain*. The reason for this is that those are implemented as so-called precompiled contracts and these contracts only really exist after they received the first message (although their contract code is hardcoded). Messages to non-existing contracts are more expensive and thus the execution runs into an Out-of-Gas error. A workaround for this problem is to first send e.g. 1 Wei to each of the contracts before you use them in your actual contracts. This is not an issue on the official or test net. + +.. _address_related: + +Address Related +--------------- + +``<address>.balance`` (``uint256``): + balance of the :ref:`address` in Wei +``<address>.send(uint256 amount) returns (bool)``: + send given amount of Wei to :ref:`address`, returns ``false`` on failure + +For more information, see the section on :ref:`address`. + +.. warning:: + There are some dangers in using ``send``: The transfer fails if the call stack depth is at 1024 + (this can always be forced by the caller) and it also fails if the recipient runs out of gas. So in order + to make safe Ether transfers, always check the return value of ``send`` or even better: + Use a pattern where the recipient withdraws the money. .. index:: this, selfdestruct Contract Related ---------------- -`this` (current contract's type): - the current contract, explicitly convertible to `address` -`selfdestruct(address)`: - destroy the current contract, sending its funds to the given address +``this`` (current contract's type): + the current contract, explicitly convertible to :ref:`address` + +``selfdestruct(address recipient)``: + destroy the current contract, sending its funds to the given :ref:`address` Furthermore, all functions of the current contract are callable directly including the current function. diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index d854dfec..2aaa6f1d 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -39,13 +39,13 @@ const std::map<std::string, Instruction> dev::solidity::c_instructions = { "MOD", Instruction::MOD }, { "SMOD", Instruction::SMOD }, { "EXP", Instruction::EXP }, - { "BNOT", Instruction::NOT }, + { "NOT", Instruction::NOT }, { "LT", Instruction::LT }, { "GT", Instruction::GT }, { "SLT", Instruction::SLT }, { "SGT", Instruction::SGT }, { "EQ", Instruction::EQ }, - { "NOT", Instruction::ISZERO }, + { "ISZERO", Instruction::ISZERO }, { "AND", Instruction::AND }, { "OR", Instruction::OR }, { "XOR", Instruction::XOR }, diff --git a/libevmasm/SourceLocation.h b/libevmasm/SourceLocation.h index 8e22a826..05304d14 100644 --- a/libevmasm/SourceLocation.h +++ b/libevmasm/SourceLocation.h @@ -45,7 +45,7 @@ struct SourceLocation end(_other.end), sourceName(std::move(_other.sourceName)) {} - SourceLocation(SourceLocation const& _other) = default; + SourceLocation(SourceLocation const&) = default; SourceLocation& operator=(SourceLocation const&) = default; SourceLocation& operator=(SourceLocation&& _other) noexcept { diff --git a/liblll/Parser.cpp b/liblll/Parser.cpp index df30f389..aa4a4de2 100644 --- a/liblll/Parser.cpp +++ b/liblll/Parser.cpp @@ -21,6 +21,10 @@ #include "Parser.h" +#if _MSC_VER +#pragma warning(disable:4348) +#endif + #define BOOST_RESULT_OF_USE_DECLTYPE #define BOOST_SPIRIT_USE_PHOENIX_V3 #include <boost/spirit/include/qi.hpp> diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp index 6beb655e..bdd8f61e 100644 --- a/libsolidity/analysis/ConstantEvaluator.cpp +++ b/libsolidity/analysis/ConstantEvaluator.cpp @@ -31,7 +31,7 @@ using namespace dev::solidity; void ConstantEvaluator::endVisit(UnaryOperation const& _operation) { TypePointer const& subType = _operation.subExpression().annotation().type; - if (!dynamic_cast<IntegerConstantType const*>(subType.get())) + if (!dynamic_cast<RationalNumberType const*>(subType.get())) BOOST_THROW_EXCEPTION(_operation.subExpression().createTypeError("Invalid constant expression.")); TypePointer t = subType->unaryOperatorResult(_operation.getOperator()); _operation.annotation().type = t; @@ -41,9 +41,9 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation) { TypePointer const& leftType = _operation.leftExpression().annotation().type; TypePointer const& rightType = _operation.rightExpression().annotation().type; - if (!dynamic_cast<IntegerConstantType const*>(leftType.get())) + if (!dynamic_cast<RationalNumberType const*>(leftType.get())) BOOST_THROW_EXCEPTION(_operation.leftExpression().createTypeError("Invalid constant expression.")); - if (!dynamic_cast<IntegerConstantType const*>(rightType.get())) + if (!dynamic_cast<RationalNumberType const*>(rightType.get())) BOOST_THROW_EXCEPTION(_operation.rightExpression().createTypeError("Invalid constant expression.")); TypePointer commonType = leftType->binaryOperatorResult(_operation.getOperator(), rightType); if (Token::isCompareOp(_operation.getOperator())) diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 5e407383..26d38cfe 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -296,7 +296,7 @@ void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract) { // order in the lists is from derived to base // list of lists to linearize, the last element is the list of direct bases - list<list<ContractDefinition const*>> input(1, {}); + list<list<ContractDefinition const*>> input(1, list<ContractDefinition const*>{}); for (ASTPointer<InheritanceSpecifier> const& baseSpecifier: _contract.baseContracts()) { UserDefinedTypeName const& baseName = baseSpecifier->name(); diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index d7542bf3..a7b9e8b8 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -103,10 +103,9 @@ void ReferencesResolver::endVisit(ArrayTypeName const& _typeName) { if (!length->annotation().type) ConstantEvaluator e(*length); - - auto const* lengthType = dynamic_cast<IntegerConstantType const*>(length->annotation().type.get()); - if (!lengthType) - fatalTypeError(length->location(), "Invalid array length."); + auto const* lengthType = dynamic_cast<RationalNumberType const*>(length->annotation().type.get()); + if (!lengthType || lengthType->isFractional()) + fatalTypeError(length->location(), "Invalid array length, expected integer literal."); else _typeName.annotation().type = make_shared<ArrayType>(DataLocation::Storage, baseType, lengthType->literalValue(nullptr)); } diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index bc342b58..ce55de00 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -32,7 +32,7 @@ using namespace dev; using namespace dev::solidity; -bool TypeChecker::checkTypeRequirements(const ContractDefinition& _contract) +bool TypeChecker::checkTypeRequirements(ContractDefinition const& _contract) { try { @@ -174,6 +174,9 @@ void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _cont for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.annotation().linearizedBaseContracts)) for (FunctionDefinition const* function: contract->definedFunctions()) { + // Take constructors out of overload hierarchy + if (function->isConstructor()) + continue; auto& overloads = functions[function->name()]; FunctionTypePointer funType = make_shared<FunctionType>(*function); auto it = find_if(overloads.begin(), overloads.end(), [&](FunTypeAndFlag const& _funAndFlag) @@ -772,26 +775,51 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) { // Infer type from value. solAssert(!var.typeName(), ""); - if ( - valueComponentType->category() == Type::Category::IntegerConstant && - !dynamic_pointer_cast<IntegerConstantType const>(valueComponentType)->integerType() - ) - fatalTypeError(_statement.initialValue()->location(), "Invalid integer constant " + valueComponentType->toString() + "."); var.annotation().type = valueComponentType->mobileType(); + if (!var.annotation().type) + { + if (valueComponentType->category() == Type::Category::RationalNumber) + fatalTypeError( + _statement.initialValue()->location(), + "Invalid rational " + + valueComponentType->toString() + + " (absolute value too large or divison by zero)." + ); + else + solAssert(false, ""); + } var.accept(*this); } else { var.accept(*this); if (!valueComponentType->isImplicitlyConvertibleTo(*var.annotation().type)) - typeError( - _statement.location(), - "Type " + - valueComponentType->toString() + - " is not implicitly convertible to expected type " + - var.annotation().type->toString() + - "." - ); + { + if ( + valueComponentType->category() == Type::Category::RationalNumber && + dynamic_cast<RationalNumberType const&>(*valueComponentType).isFractional() && + valueComponentType->mobileType() + ) + typeError( + _statement.location(), + "Type " + + valueComponentType->toString() + + " is not implicitly convertible to expected type " + + var.annotation().type->toString() + + ". Try converting to type " + + valueComponentType->mobileType()->toString() + + " or use an explicit conversion." + ); + else + typeError( + _statement.location(), + "Type " + + valueComponentType->toString() + + " is not implicitly convertible to expected type " + + var.annotation().type->toString() + + "." + ); + } } } return false; @@ -799,9 +827,9 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) void TypeChecker::endVisit(ExpressionStatement const& _statement) { - if (type(_statement.expression())->category() == Type::Category::IntegerConstant) - if (!dynamic_pointer_cast<IntegerConstantType const>(type(_statement.expression()))->integerType()) - typeError(_statement.expression().location(), "Invalid integer constant."); + if (type(_statement.expression())->category() == Type::Category::RationalNumber) + if (!dynamic_cast<RationalNumberType const&>(*type(_statement.expression())).mobileType()) + typeError(_statement.expression().location(), "Invalid rational number."); } bool TypeChecker::visit(Conditional const& _conditional) @@ -1106,9 +1134,9 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) auto const& argType = type(*arguments[i]); if (functionType->takesArbitraryParameters()) { - if (auto t = dynamic_cast<IntegerConstantType const*>(argType.get())) - if (!t->integerType()) - typeError(arguments[i]->location(), "Integer constant too large."); + if (auto t = dynamic_cast<RationalNumberType const*>(argType.get())) + if (!t->mobileType()) + typeError(arguments[i]->location(), "Invalid rational number (too large or division by zero)."); } else if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i])) typeError( @@ -1341,9 +1369,12 @@ bool TypeChecker::visit(IndexAccess const& _access) else { expectType(*index, IntegerType(256)); - if (auto integerType = dynamic_cast<IntegerConstantType const*>(type(*index).get())) - if (!actualType.isDynamicallySized() && actualType.length() <= integerType->literalValue(nullptr)) - typeError(_access.location(), "Out of bounds array access."); + if (auto numberType = dynamic_cast<RationalNumberType const*>(type(*index).get())) + { + if (!numberType->isFractional()) // error is reported above + if (!actualType.isDynamicallySized() && actualType.length() <= numberType->literalValue(nullptr)) + typeError(_access.location(), "Out of bounds array access."); + } } resultType = actualType.baseType(); isLValue = actualType.location() != DataLocation::CallData; @@ -1367,8 +1398,8 @@ bool TypeChecker::visit(IndexAccess const& _access) resultType = make_shared<TypeType>(make_shared<ArrayType>(DataLocation::Memory, typeType.actualType())); else { - index->accept(*this); - if (auto length = dynamic_cast<IntegerConstantType const*>(type(*index).get())) + expectType(*index, IntegerType(256)); + if (auto length = dynamic_cast<RationalNumberType const*>(type(*index).get())) resultType = make_shared<TypeType>(make_shared<ArrayType>( DataLocation::Memory, typeType.actualType(), @@ -1387,7 +1418,7 @@ bool TypeChecker::visit(IndexAccess const& _access) else { expectType(*index, IntegerType(256)); - if (auto integerType = dynamic_cast<IntegerConstantType const*>(type(*index).get())) + if (auto integerType = dynamic_cast<RationalNumberType const*>(type(*index).get())) if (bytesType.numBytes() <= integerType->literalValue(nullptr)) typeError(_access.location(), "Out of bounds array access."); } @@ -1492,16 +1523,33 @@ Declaration const& TypeChecker::dereference(UserDefinedTypeName const& _typeName void TypeChecker::expectType(Expression const& _expression, Type const& _expectedType) { _expression.accept(*this); - if (!type(_expression)->isImplicitlyConvertibleTo(_expectedType)) - typeError( - _expression.location(), - "Type " + - type(_expression)->toString() + - " is not implicitly convertible to expected type " + - _expectedType.toString() + - "." - ); + { + if ( + type(_expression)->category() == Type::Category::RationalNumber && + dynamic_pointer_cast<RationalNumberType const>(type(_expression))->isFractional() && + type(_expression)->mobileType() + ) + typeError( + _expression.location(), + "Type " + + type(_expression)->toString() + + " is not implicitly convertible to expected type " + + _expectedType.toString() + + ". Try converting to type " + + type(_expression)->mobileType()->toString() + + " or use an explicit conversion." + ); + else + typeError( + _expression.location(), + "Type " + + type(_expression)->toString() + + " is not implicitly convertible to expected type " + + _expectedType.toString() + + "." + ); + } } void TypeChecker::requireLValue(Expression const& _expression) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index d541de23..5630743b 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -122,7 +122,8 @@ TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type) ); Token::Value token = _type.token(); - unsigned int m = _type.firstNumber(); + unsigned m = _type.firstNumber(); + unsigned n = _type.secondNumber(); switch (token) { @@ -132,10 +133,18 @@ TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type) return make_shared<IntegerType>(m, IntegerType::Modifier::Unsigned); case Token::BytesM: return make_shared<FixedBytesType>(m); + case Token::FixedMxN: + return make_shared<FixedPointType>(m, n, FixedPointType::Modifier::Signed); + case Token::UFixedMxN: + return make_shared<FixedPointType>(m, n, FixedPointType::Modifier::Unsigned); case Token::Int: return make_shared<IntegerType>(256, IntegerType::Modifier::Signed); case Token::UInt: return make_shared<IntegerType>(256, IntegerType::Modifier::Unsigned); + case Token::Fixed: + return make_shared<FixedPointType>(128, 128, FixedPointType::Modifier::Signed); + case Token::UFixed: + return make_shared<FixedPointType>(128, 128, FixedPointType::Modifier::Unsigned); case Token::Byte: return make_shared<FixedBytesType>(1); case Token::Address: @@ -171,13 +180,17 @@ TypePointer Type::forLiteral(Literal const& _literal) case Token::FalseLiteral: return make_shared<BoolType>(); case Token::Number: - if (!IntegerConstantType::isValidLiteral(_literal)) + { + tuple<bool, rational> validLiteral = RationalNumberType::isValidLiteral(_literal); + if (get<0>(validLiteral) == true) + return make_shared<RationalNumberType>(get<1>(validLiteral)); + else return TypePointer(); - return make_shared<IntegerConstantType>(_literal); + } case Token::StringLiteral: return make_shared<StringLiteralType>(_literal); default: - return shared_ptr<Type>(); + return TypePointer(); } } @@ -246,17 +259,30 @@ IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier): bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const { - if (_convertTo.category() != category()) - return false; - IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo); - if (convertTo.m_bits < m_bits) - return false; - if (isAddress()) - return convertTo.isAddress(); - else if (isSigned()) - return convertTo.isSigned(); + if (_convertTo.category() == category()) + { + IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo); + if (convertTo.m_bits < m_bits) + return false; + if (isAddress()) + return convertTo.isAddress(); + else if (isSigned()) + return convertTo.isSigned(); + else + return !convertTo.isSigned() || convertTo.m_bits > m_bits; + } + else if (_convertTo.category() == Category::FixedPoint) + { + FixedPointType const& convertTo = dynamic_cast<FixedPointType const&>(_convertTo); + if (convertTo.integerBits() < m_bits || isAddress()) + return false; + else if (isSigned()) + return convertTo.isSigned(); + else + return !convertTo.isSigned() || convertTo.integerBits() > m_bits; + } else - return !convertTo.isSigned() || convertTo.m_bits > m_bits; + return false; } bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const @@ -264,7 +290,8 @@ bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const return _convertTo.category() == category() || _convertTo.category() == Category::Contract || _convertTo.category() == Category::Enum || - _convertTo.category() == Category::FixedBytes; + _convertTo.category() == Category::FixedBytes || + _convertTo.category() == Category::FixedPoint; } TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const @@ -302,10 +329,13 @@ string IntegerType::toString(bool) const TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const { - if (_other->category() != Category::IntegerConstant && _other->category() != category()) + if ( + _other->category() != Category::RationalNumber && + _other->category() != Category::FixedPoint && + _other->category() != category() + ) return TypePointer(); - auto commonType = dynamic_pointer_cast<IntegerType const>(Type::commonType(shared_from_this(), _other)); - + auto commonType = Type::commonType(shared_from_this(), _other); //might be a integer or fixed point if (!commonType) return TypePointer(); @@ -315,9 +345,14 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe if (Token::isBooleanOp(_operator)) return TypePointer(); // Nothing else can be done with addresses - if (commonType->isAddress()) - return TypePointer(); - + if (auto intType = dynamic_pointer_cast<IntegerType const>(commonType)) + { + if (intType->isAddress()) + return TypePointer(); + } + else if (auto fixType = dynamic_pointer_cast<FixedPointType const>(commonType)) + if (Token::Exp == _operator) + return TypePointer(); return commonType; } @@ -335,143 +370,292 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons return MemberList::MemberMap(); } -bool IntegerConstantType::isValidLiteral(const Literal& _literal) +FixedPointType::FixedPointType(int _integerBits, int _fractionalBits, FixedPointType::Modifier _modifier): + m_integerBits(_integerBits), m_fractionalBits(_fractionalBits), m_modifier(_modifier) { - try + solAssert( + m_integerBits + m_fractionalBits > 0 && + m_integerBits + m_fractionalBits <= 256 && + m_integerBits % 8 == 0 && + m_fractionalBits % 8 == 0, + "Invalid bit number(s) for fixed type: " + + dev::toString(_integerBits) + "x" + dev::toString(_fractionalBits) + ); +} + +bool FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const +{ + if (_convertTo.category() == category()) { - bigint x(_literal.value()); + FixedPointType const& convertTo = dynamic_cast<FixedPointType const&>(_convertTo); + if (convertTo.m_integerBits < m_integerBits || convertTo.m_fractionalBits < m_fractionalBits) + return false; + else if (isSigned()) + return convertTo.isSigned(); + else + return !convertTo.isSigned() || (convertTo.m_integerBits > m_integerBits); } - catch (...) - { + return false; +} + +bool FixedPointType::isExplicitlyConvertibleTo(Type const& _convertTo) const +{ + return _convertTo.category() == category() || + _convertTo.category() == Category::Integer || + _convertTo.category() == Category::FixedBytes; +} + +TypePointer FixedPointType::unaryOperatorResult(Token::Value _operator) const +{ + // "delete" is ok for all fixed types + if (_operator == Token::Delete) + return make_shared<TupleType>(); + // for fixed, we allow +, -, ++ and -- + else if ( + _operator == Token::Add || + _operator == Token::Sub || + _operator == Token::Inc || + _operator == Token::Dec || + _operator == Token::After + ) + return shared_from_this(); + else + return TypePointer(); +} + +bool FixedPointType::operator==(Type const& _other) const +{ + if (_other.category() != category()) return false; + FixedPointType const& other = dynamic_cast<FixedPointType const&>(_other); + return other.m_integerBits == m_integerBits && other.m_fractionalBits == m_fractionalBits && other.m_modifier == m_modifier; +} + +string FixedPointType::toString(bool) const +{ + string prefix = isSigned() ? "fixed" : "ufixed"; + return prefix + dev::toString(m_integerBits) + "x" + dev::toString(m_fractionalBits); +} + +TypePointer FixedPointType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const +{ + if ( + _other->category() != Category::RationalNumber && + _other->category() != category() && + _other->category() != Category::Integer + ) + return TypePointer(); + auto commonType = Type::commonType(shared_from_this(), _other); //might be fixed point or integer + + if (!commonType) + return TypePointer(); + + // All fixed types can be compared + if (Token::isCompareOp(_operator)) + return commonType; + if (Token::isBitOp(_operator) || Token::isBooleanOp(_operator)) + return TypePointer(); + if (auto fixType = dynamic_pointer_cast<FixedPointType const>(commonType)) + { + if (Token::Exp == _operator) + return TypePointer(); } - return true; + else if (auto intType = dynamic_pointer_cast<IntegerType const>(commonType)) + if (intType->isAddress()) + return TypePointer(); + return commonType; } -IntegerConstantType::IntegerConstantType(Literal const& _literal) +tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal) { - m_value = bigint(_literal.value()); + rational x; + try + { + rational numerator; + rational denominator(1); + + auto radixPoint = find(_literal.value().begin(), _literal.value().end(), '.'); + if (radixPoint != _literal.value().end()) + { + if ( + !all_of(radixPoint + 1, _literal.value().end(), ::isdigit) || + !all_of(_literal.value().begin(), radixPoint, ::isdigit) + ) + throw; + //Only decimal notation allowed here, leading zeros would switch to octal. + auto fractionalBegin = find_if_not( + radixPoint + 1, + _literal.value().end(), + [](char const& a) { return a == '0'; } + ); + denominator = bigint(string(fractionalBegin, _literal.value().end())); + denominator /= boost::multiprecision::pow( + bigint(10), + distance(radixPoint + 1, _literal.value().end()) + ); + numerator = bigint(string(_literal.value().begin(), radixPoint)); + x = numerator + denominator; + } + else + x = bigint(_literal.value()); + } + catch (...) + { + return make_tuple(false, rational(0)); + } switch (_literal.subDenomination()) { - case Literal::SubDenomination::Wei: - case Literal::SubDenomination::Second: - case Literal::SubDenomination::None: - break; - case Literal::SubDenomination::Szabo: - m_value *= bigint("1000000000000"); - break; - case Literal::SubDenomination::Finney: - m_value *= bigint("1000000000000000"); - break; - case Literal::SubDenomination::Ether: - m_value *= bigint("1000000000000000000"); - break; - case Literal::SubDenomination::Minute: - m_value *= bigint("60"); - break; - case Literal::SubDenomination::Hour: - m_value *= bigint("3600"); - break; - case Literal::SubDenomination::Day: - m_value *= bigint("86400"); - break; - case Literal::SubDenomination::Week: - m_value *= bigint("604800"); - break; - case Literal::SubDenomination::Year: - m_value *= bigint("31536000"); - break; + case Literal::SubDenomination::None: + case Literal::SubDenomination::Wei: + case Literal::SubDenomination::Second: + break; + case Literal::SubDenomination::Szabo: + x *= bigint("1000000000000"); + break; + case Literal::SubDenomination::Finney: + x *= bigint("1000000000000000"); + break; + case Literal::SubDenomination::Ether: + x *= bigint("1000000000000000000"); + break; + case Literal::SubDenomination::Minute: + x *= bigint("60"); + break; + case Literal::SubDenomination::Hour: + x *= bigint("3600"); + break; + case Literal::SubDenomination::Day: + x *= bigint("86400"); + break; + case Literal::SubDenomination::Week: + x *= bigint("604800"); + break; + case Literal::SubDenomination::Year: + x *= bigint("31536000"); + break; } + + + return make_tuple(true, x); } -bool IntegerConstantType::isImplicitlyConvertibleTo(Type const& _convertTo) const +bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const { - if (auto targetType = dynamic_cast<IntegerType const*>(&_convertTo)) + if (_convertTo.category() == Category::Integer) { + auto targetType = dynamic_cast<IntegerType const*>(&_convertTo); if (m_value == 0) return true; + if (isFractional()) + return false; int forSignBit = (targetType->isSigned() ? 1 : 0); if (m_value > 0) { - if (m_value <= (u256(-1) >> (256 - targetType->numBits() + forSignBit))) + if (m_value.numerator() <= (u256(-1) >> (256 - targetType->numBits() + forSignBit))) return true; } - else if (targetType->isSigned() && -m_value <= (u256(1) << (targetType->numBits() - forSignBit))) + else if (targetType->isSigned() && -m_value.numerator() <= (u256(1) << (targetType->numBits() - forSignBit))) return true; return false; } + else if (_convertTo.category() == Category::FixedPoint) + { + if (auto fixed = fixedPointType()) + { + // We disallow implicit conversion if we would have to truncate (fixedPointType() + // can return a type that requires truncation). + rational value = m_value * (bigint(1) << fixed->fractionalBits()); + return value.denominator() == 1 && fixed->isImplicitlyConvertibleTo(_convertTo); + } + return false; + } else if (_convertTo.category() == Category::FixedBytes) { FixedBytesType const& fixedBytes = dynamic_cast<FixedBytesType const&>(_convertTo); - return fixedBytes.numBytes() * 8 >= integerType()->numBits(); + if (!isFractional()) + return fixedBytes.numBytes() * 8 >= integerType()->numBits(); + else + return false; } - else - return false; + return false; } -bool IntegerConstantType::isExplicitlyConvertibleTo(Type const& _convertTo) const +bool RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - TypePointer intType = integerType(); - return intType && intType->isExplicitlyConvertibleTo(_convertTo); + TypePointer mobType = mobileType(); + return mobType && mobType->isExplicitlyConvertibleTo(_convertTo); } -TypePointer IntegerConstantType::unaryOperatorResult(Token::Value _operator) const +TypePointer RationalNumberType::unaryOperatorResult(Token::Value _operator) const { - bigint value; + rational value; switch (_operator) { case Token::BitNot: - value = ~m_value; + if (isFractional()) + return TypePointer(); + value = ~m_value.numerator(); break; case Token::Add: - value = m_value; + value = +(m_value); break; case Token::Sub: - value = -m_value; + value = -(m_value); break; case Token::After: return shared_from_this(); default: return TypePointer(); } - return make_shared<IntegerConstantType>(value); + return make_shared<RationalNumberType>(value); } -TypePointer IntegerConstantType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const +TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const { - if (_other->category() == Category::Integer) + if (_other->category() == Category::Integer || _other->category() == Category::FixedPoint) { - shared_ptr<IntegerType const> intType = integerType(); - if (!intType) + auto mobile = mobileType(); + if (!mobile) return TypePointer(); - return intType->binaryOperatorResult(_operator, _other); + return mobile->binaryOperatorResult(_operator, _other); } else if (_other->category() != category()) return TypePointer(); - IntegerConstantType const& other = dynamic_cast<IntegerConstantType const&>(*_other); + RationalNumberType const& other = dynamic_cast<RationalNumberType const&>(*_other); if (Token::isCompareOp(_operator)) { - shared_ptr<IntegerType const> thisIntegerType = integerType(); - shared_ptr<IntegerType const> otherIntegerType = other.integerType(); - if (!thisIntegerType || !otherIntegerType) + // Since we do not have a "BoolConstantType", we have to do the acutal comparison + // at runtime and convert to mobile typse first. Such a comparison is not a very common + // use-case and will be optimized away. + TypePointer thisMobile = mobileType(); + TypePointer otherMobile = other.mobileType(); + if (!thisMobile || !otherMobile) return TypePointer(); - return thisIntegerType->binaryOperatorResult(_operator, otherIntegerType); + return thisMobile->binaryOperatorResult(_operator, otherMobile); } else { - bigint value; + rational value; + bool fractional = isFractional() || other.isFractional(); switch (_operator) { + //bit operations will only be enabled for integers and fixed types that resemble integers case Token::BitOr: - value = m_value | other.m_value; + if (fractional) + return TypePointer(); + value = m_value.numerator() | other.m_value.numerator(); break; case Token::BitXor: - value = m_value ^ other.m_value; + if (fractional) + return TypePointer(); + value = m_value.numerator() ^ other.m_value.numerator(); break; case Token::BitAnd: - value = m_value & other.m_value; + if (fractional) + return TypePointer(); + value = m_value.numerator() & other.m_value.numerator(); break; case Token::Add: value = m_value + other.m_value; @@ -485,68 +669,104 @@ TypePointer IntegerConstantType::binaryOperatorResult(Token::Value _operator, Ty case Token::Div: if (other.m_value == 0) return TypePointer(); - value = m_value / other.m_value; + else + value = m_value / other.m_value; break; case Token::Mod: if (other.m_value == 0) return TypePointer(); - value = m_value % other.m_value; - break; + else if (fractional) + { + rational tempValue = m_value / other.m_value; + value = m_value - (tempValue.numerator() / tempValue.denominator()) * other.m_value; + } + else + value = m_value.numerator() % other.m_value.numerator(); + break; case Token::Exp: - if (other.m_value < 0) - return TypePointer(); - else if (other.m_value > numeric_limits<unsigned int>::max()) + { + using boost::multiprecision::pow; + if (other.isFractional()) return TypePointer(); + else if (abs(other.m_value) > numeric_limits<uint32_t>::max()) + return TypePointer(); // This will need too much memory to represent. + uint32_t exponent = abs(other.m_value).numerator().convert_to<uint32_t>(); + bigint numerator = pow(m_value.numerator(), exponent); + bigint denominator = pow(m_value.denominator(), exponent); + if (other.m_value >= 0) + value = rational(numerator, denominator); else - value = boost::multiprecision::pow(m_value, other.m_value.convert_to<unsigned int>()); + // invert + value = rational(denominator, numerator); break; + } default: return TypePointer(); } - return make_shared<IntegerConstantType>(value); + return make_shared<RationalNumberType>(value); } } -bool IntegerConstantType::operator==(Type const& _other) const +bool RationalNumberType::operator==(Type const& _other) const { if (_other.category() != category()) return false; - return m_value == dynamic_cast<IntegerConstantType const&>(_other).m_value; + RationalNumberType const& other = dynamic_cast<RationalNumberType const&>(_other); + return m_value == other.m_value; } -string IntegerConstantType::toString(bool) const +string RationalNumberType::toString(bool) const { - return "int_const " + m_value.str(); + if (!isFractional()) + return "int_const " + m_value.numerator().str(); + return "rational_const " + m_value.numerator().str() + '/' + m_value.denominator().str(); } -u256 IntegerConstantType::literalValue(Literal const*) const +u256 RationalNumberType::literalValue(Literal const*) const { + // We ignore the literal and hope that the type was correctly determined to represent + // its value. + u256 value; + bigint shiftedValue; + + if (!isFractional()) + shiftedValue = m_value.numerator(); + else + { + auto fixed = fixedPointType(); + solAssert(!!fixed, ""); + rational shifted = m_value * (bigint(1) << fixed->fractionalBits()); + // truncate + shiftedValue = shifted.numerator() / shifted.denominator(); + } + // we ignore the literal and hope that the type was correctly determined - solAssert(m_value <= u256(-1), "Integer constant too large."); - solAssert(m_value >= -(bigint(1) << 255), "Integer constant too small."); + solAssert(shiftedValue <= u256(-1), "Integer constant too large."); + solAssert(shiftedValue >= -(bigint(1) << 255), "Number constant too small."); if (m_value >= 0) - value = u256(m_value); + value = u256(shiftedValue); else - value = s2u(s256(m_value)); - + value = s2u(s256(shiftedValue)); return value; } -TypePointer IntegerConstantType::mobileType() const +TypePointer RationalNumberType::mobileType() const { - auto intType = integerType(); - solAssert(!!intType, "mobileType called with invalid integer constant " + toString(false)); - return intType; + if (!isFractional()) + return integerType(); + else + return fixedPointType(); } -shared_ptr<IntegerType const> IntegerConstantType::integerType() const +shared_ptr<IntegerType const> RationalNumberType::integerType() const { - bigint value = m_value; + solAssert(!isFractional(), "integerType() called for fractional number."); + bigint value = m_value.numerator(); bool negative = (value < 0); if (negative) // convert to positive number of same bit requirements - value = ((-value) - 1) << 1; + value = ((0 - value) - 1) << 1; if (value > u256(-1)) return shared_ptr<IntegerType const>(); else @@ -556,6 +776,58 @@ shared_ptr<IntegerType const> IntegerConstantType::integerType() const ); } +shared_ptr<FixedPointType const> RationalNumberType::fixedPointType() const +{ + bool negative = (m_value < 0); + unsigned fractionalBits = 0; + rational value = abs(m_value); // We care about the sign later. + rational maxValue = negative ? + rational(bigint(1) << 255, 1): + rational((bigint(1) << 256) - 1, 1); + + while (value * 0x100 <= maxValue && value.denominator() != 1 && fractionalBits < 256) + { + value *= 0x100; + fractionalBits += 8; + } + + if (value > maxValue) + return shared_ptr<FixedPointType const>(); + // u256(v) is the actual value that will be put on the stack + // From here on, very similar to integerType() + bigint v = value.numerator() / value.denominator(); + if (negative) + // modify value to satisfy bit requirements for negative numbers: + // add one bit for sign and decrement because negative numbers can be larger + v = (v - 1) << 1; + + if (v > u256(-1)) + return shared_ptr<FixedPointType const>(); + + unsigned totalBits = bytesRequired(v) * 8; + solAssert(totalBits <= 256, ""); + unsigned integerBits = totalBits >= fractionalBits ? totalBits - fractionalBits : 0; + // Special case: Numbers between -1 and 0 have their sign bit in the fractional part. + if (negative && abs(m_value) < 1 && totalBits > fractionalBits) + { + fractionalBits += 8; + integerBits = 0; + } + + if (integerBits > 256 || fractionalBits > 256 || fractionalBits + integerBits > 256) + return shared_ptr<FixedPointType const>(); + if (integerBits == 0 && fractionalBits == 0) + { + integerBits = 0; + fractionalBits = 8; + } + + return make_shared<FixedPointType>( + integerBits, fractionalBits, + negative ? FixedPointType::Modifier::Signed : FixedPointType::Modifier::Unsigned + ); +} + StringLiteralType::StringLiteralType(Literal const& _literal): m_value(_literal.value()) { @@ -609,6 +881,7 @@ bool FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const { return _convertTo.category() == Category::Integer || + _convertTo.category() == Category::FixedPoint || _convertTo.category() == Category::Contract || _convertTo.category() == category(); } @@ -650,19 +923,6 @@ bool FixedBytesType::operator==(Type const& _other) const return other.m_bytes == m_bytes; } -bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const -{ - // conversion to integer is fine, but not to address - // this is an example of explicit conversions being not transitive (though implicit should be) - if (_convertTo.category() == category()) - { - IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo); - if (!convertTo.isAddress()) - return true; - } - return isImplicitlyConvertibleTo(_convertTo); -} - u256 BoolType::literalValue(Literal const* _literal) const { solAssert(_literal, ""); @@ -1277,9 +1537,9 @@ bool EnumType::isExplicitlyConvertibleTo(Type const& _convertTo) const return _convertTo.category() == category() || _convertTo.category() == Category::Integer; } -unsigned int EnumType::memberValue(ASTString const& _member) const +unsigned EnumType::memberValue(ASTString const& _member) const { - unsigned int index = 0; + unsigned index = 0; for (ASTPointer<EnumValue> const& decl: m_enum.members()) { if (decl->name() == _member) @@ -1777,29 +2037,20 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound) location = Location::DelegateCall; } - TypePointers returnParameterTypes; - vector<string> returnParameterNames; - if (location == Location::Internal) - { - returnParameterNames = m_returnParameterNames; - returnParameterTypes = m_returnParameterTypes; - } - else + TypePointers returnParameterTypes = m_returnParameterTypes; + if (location != Location::Internal) { - // Removes dynamic types. - for (size_t i = 0; i < m_returnParameterTypes.size(); ++i) - if (!m_returnParameterTypes[i]->isDynamicallySized()) - { - returnParameterTypes.push_back(m_returnParameterTypes[i]); - returnParameterNames.push_back(m_returnParameterNames[i]); - } + // Alter dynamic types to be non-accessible. + for (auto& param: returnParameterTypes) + if (param->isDynamicallySized()) + param = make_shared<InaccessibleDynamicType>(); } return make_shared<FunctionType>( parameterTypes, returnParameterTypes, m_parameterNames, - returnParameterNames, + m_returnParameterNames, location, m_arbitraryParameters, m_declaration, diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index d42bb5dd..1ee762e5 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -26,6 +26,7 @@ #include <string> #include <map> #include <boost/noncopyable.hpp> +#include <boost/rational.hpp> #include <libdevcore/Common.h> #include <libdevcore/CommonIO.h> #include <libsolidity/interface/Exceptions.h> @@ -43,6 +44,7 @@ class FunctionType; // forward using TypePointer = std::shared_ptr<Type const>; using FunctionTypePointer = std::shared_ptr<FunctionType const>; using TypePointers = std::vector<TypePointer>; +using rational = boost::rational<dev::bigint>; enum class DataLocation { Storage, CallData, Memory }; @@ -133,9 +135,10 @@ class Type: private boost::noncopyable, public std::enable_shared_from_this<Type public: enum class Category { - Integer, IntegerConstant, StringLiteral, Bool, Real, Array, + Integer, RationalNumber, StringLiteral, Bool, FixedPoint, Array, FixedBytes, Contract, Struct, Function, Enum, Tuple, - Mapping, TypeType, Modifier, Magic, Module + Mapping, TypeType, Modifier, Magic, Module, + InaccessibleDynamic }; /// @{ @@ -202,8 +205,9 @@ public: virtual bool isValueType() const { return false; } virtual unsigned sizeOnStack() const { return 1; } /// @returns the mobile (in contrast to static) type corresponding to the given type. - /// This returns the corresponding integer type for IntegerConstantTypes and the pointer type - /// for storage reference types. + /// This returns the corresponding IntegerType or FixedPointType for RationalNumberType + /// and the pointer type for storage reference types. + /// Might return a null pointer if there is no fitting type. virtual TypePointer mobileType() const { return shared_from_this(); } /// @returns true if this is a non-value type and the data of this type is stored at the /// given location. @@ -309,20 +313,63 @@ private: }; /** - * Integer constants either literals or computed. Example expressions: 2, 2+10, ~10. - * There is one distinct type per value. + * A fixed point type number (signed, unsigned). */ -class IntegerConstantType: public Type +class FixedPointType: public Type { public: - virtual Category category() const override { return Category::IntegerConstant; } + enum class Modifier + { + Unsigned, Signed + }; + virtual Category category() const override { return Category::FixedPoint; } - /// @returns true if the literal is a valid integer. - static bool isValidLiteral(Literal const& _literal); + explicit FixedPointType(int _integerBits, int _fractionalBits, Modifier _modifier = Modifier::Unsigned); + + virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; + virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; - explicit IntegerConstantType(Literal const& _literal); - explicit IntegerConstantType(bigint _value): m_value(_value) {} + virtual bool operator==(Type const& _other) const override; + virtual unsigned calldataEncodedSize(bool _padded = true) const override { return _padded ? 32 : (m_integerBits + m_fractionalBits) / 8; } + virtual unsigned storageBytes() const override { return (m_integerBits + m_fractionalBits) / 8; } + virtual bool isValueType() const override { return true; } + + virtual std::string toString(bool _short) const override; + + virtual TypePointer encodingType() const override { return shared_from_this(); } + virtual TypePointer interfaceType(bool) const override { return shared_from_this(); } + + int numBits() const { return m_integerBits + m_fractionalBits; } + int integerBits() const { return m_integerBits; } + int fractionalBits() const { return m_fractionalBits; } + bool isSigned() const { return m_modifier == Modifier::Signed; } + +private: + int m_integerBits; + int m_fractionalBits; + Modifier m_modifier; +}; + +/** + * Integer and fixed point constants either literals or computed. + * Example expressions: 2, 3.14, 2+10.2, ~10. + * There is one distinct type per value. + */ +class RationalNumberType: public Type +{ +public: + + virtual Category category() const override { return Category::RationalNumber; } + + /// @returns true if the literal is a valid integer. + static std::tuple<bool, rational> isValidLiteral(Literal const& _literal); + + explicit RationalNumberType(rational const& _value): + m_value(_value) + {} virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; @@ -339,9 +386,15 @@ public: /// @returns the smallest integer type that can hold the value or an empty pointer if not possible. std::shared_ptr<IntegerType const> integerType() const; + /// @returns the smallest fixed type that can hold the value or incurs the least precision loss. + /// If the integer part does not fit, returns an empty pointer. + std::shared_ptr<FixedPointType const> fixedPointType() const; + + /// @returns true if the value is not an integer. + bool isFractional() const { return m_value.denominator() != 1; } private: - bigint m_value; + rational m_value; }; /** @@ -418,7 +471,6 @@ class BoolType: public Type public: BoolType() {} virtual Category category() const override { return Category::Bool; } - virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; @@ -1030,5 +1082,25 @@ private: Kind m_kind; }; +/** + * Special type that is used for dynamic types in returns from external function calls + * (The EVM currently cannot access dynamically-sized return values). + */ +class InaccessibleDynamicType: public Type +{ +public: + virtual Category category() const override { return Category::InaccessibleDynamic; } + + virtual bool isImplicitlyConvertibleTo(Type const&) const override { return false; } + virtual bool isExplicitlyConvertibleTo(Type const&) const override { return false; } + virtual unsigned calldataEncodedSize(bool _padded) const override { (void)_padded; return 32; } + virtual bool canBeStored() const override { return false; } + virtual bool canLiveOutsideStorage() const override { return false; } + virtual bool isValueType() const override { return true; } + virtual unsigned sizeOnStack() const override { return 1; } + virtual std::string toString(bool) const override { return "inaccessible dynamic type"; } + virtual TypePointer decodingType() const override { return std::make_shared<IntegerType>(256); } +}; + } } diff --git a/libsolidity/codegen/Compiler.cpp b/libsolidity/codegen/Compiler.cpp index 1675f659..1ae5dda9 100644 --- a/libsolidity/codegen/Compiler.cpp +++ b/libsolidity/codegen/Compiler.cpp @@ -21,51 +21,24 @@ */ #include <libsolidity/codegen/Compiler.h> -#include <algorithm> -#include <boost/range/adaptor/reversed.hpp> -#include <libevmasm/Instruction.h> #include <libevmasm/Assembly.h> -#include <libevmasm/GasMeter.h> -#include <libsolidity/inlineasm/AsmCodeGen.h> -#include <libsolidity/ast/AST.h> -#include <libsolidity/codegen/ExpressionCompiler.h> -#include <libsolidity/codegen/CompilerUtils.h> +#include <libsolidity/codegen/ContractCompiler.h> + using namespace std; using namespace dev; using namespace dev::solidity; -/** - * Simple helper class to ensure that the stack height is the same at certain places in the code. - */ -class StackHeightChecker -{ -public: - StackHeightChecker(CompilerContext const& _context): - m_context(_context), stackHeight(m_context.stackHeight()) {} - void check() { solAssert(m_context.stackHeight() == stackHeight, "I sense a disturbance in the stack."); } -private: - CompilerContext const& m_context; - unsigned stackHeight; -}; - void Compiler::compileContract( ContractDefinition const& _contract, std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts ) { - m_context = CompilerContext(); - { - CompilerContext::LocationSetter locationSetterRunTime(m_context, _contract); - initializeContext(_contract, _contracts); - appendFunctionSelector(_contract); - appendMissingFunctions(); - } + ContractCompiler runtimeCompiler(m_runtimeContext, m_optimize); + runtimeCompiler.compileContract(_contract, _contracts); + + ContractCompiler creationCompiler(m_context, m_optimize); + m_runtimeSub = creationCompiler.compileConstructor(m_runtimeContext, _contract, _contracts); - // Swap the runtime context with the creation-time context - swap(m_context, m_runtimeContext); - CompilerContext::LocationSetter locationSetterCreationTime(m_context, _contract); - initializeContext(_contract, _contracts); - packIntoContractCreator(_contract, m_runtimeContext); if (m_optimize) m_context.optimise(m_optimizeRuns); @@ -81,21 +54,8 @@ void Compiler::compileClone( map<ContractDefinition const*, eth::Assembly const*> const& _contracts ) { - m_context = CompilerContext(); // clear it just in case - initializeContext(_contract, _contracts); - - appendInitAndConstructorCode(_contract); - - //@todo determine largest return size of all runtime functions - eth::AssemblyItem runtimeSub = m_context.addSubroutine(cloneRuntime()); - solAssert(runtimeSub.data() < numeric_limits<size_t>::max(), ""); - m_runtimeSub = size_t(runtimeSub.data()); - - // stack contains sub size - m_context << Instruction::DUP1 << runtimeSub << u256(0) << Instruction::CODECOPY; - m_context << u256(0) << Instruction::RETURN; - - appendMissingFunctions(); + ContractCompiler cloneCompiler(m_context, m_optimize); + m_runtimeSub = cloneCompiler.compileClone(_contract, _contracts); if (m_optimize) m_context.optimise(m_optimizeRuns); @@ -105,765 +65,3 @@ eth::AssemblyItem Compiler::functionEntryLabel(FunctionDefinition const& _functi { return m_runtimeContext.functionEntryLabelIfExists(_function); } - -void Compiler::initializeContext( - ContractDefinition const& _contract, - map<ContractDefinition const*, eth::Assembly const*> const& _compiledContracts -) -{ - m_context.setCompiledContracts(_compiledContracts); - m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts); - CompilerUtils(m_context).initialiseFreeMemoryPointer(); - registerStateVariables(_contract); - m_context.resetVisitedNodes(&_contract); -} - -void Compiler::appendInitAndConstructorCode(ContractDefinition const& _contract) -{ - // Determine the arguments that are used for the base constructors. - std::vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts; - for (ContractDefinition const* contract: bases) - { - if (FunctionDefinition const* constructor = contract->constructor()) - for (auto const& modifier: constructor->modifiers()) - { - auto baseContract = dynamic_cast<ContractDefinition const*>( - modifier->name()->annotation().referencedDeclaration); - if (baseContract) - if (m_baseArguments.count(baseContract->constructor()) == 0) - m_baseArguments[baseContract->constructor()] = &modifier->arguments(); - } - - for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts()) - { - ContractDefinition const* baseContract = dynamic_cast<ContractDefinition const*>( - base->name().annotation().referencedDeclaration - ); - solAssert(baseContract, ""); - - if (m_baseArguments.count(baseContract->constructor()) == 0) - m_baseArguments[baseContract->constructor()] = &base->arguments(); - } - } - // Initialization of state variables in base-to-derived order. - for (ContractDefinition const* contract: boost::adaptors::reverse(bases)) - initializeStateVariables(*contract); - - if (FunctionDefinition const* constructor = _contract.constructor()) - appendConstructor(*constructor); - else if (auto c = m_context.nextConstructor(_contract)) - appendBaseConstructor(*c); -} - -void Compiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext) -{ - appendInitAndConstructorCode(_contract); - - eth::AssemblyItem runtimeSub = m_context.addSubroutine(_runtimeContext.assembly()); - solAssert(runtimeSub.data() < numeric_limits<size_t>::max(), ""); - m_runtimeSub = size_t(runtimeSub.data()); - - // stack contains sub size - m_context << Instruction::DUP1 << runtimeSub << u256(0) << Instruction::CODECOPY; - m_context << u256(0) << Instruction::RETURN; - - // note that we have to include the functions again because of absolute jump labels - appendMissingFunctions(); -} - -void Compiler::appendBaseConstructor(FunctionDefinition const& _constructor) -{ - CompilerContext::LocationSetter locationSetter(m_context, _constructor); - FunctionType constructorType(_constructor); - if (!constructorType.parameterTypes().empty()) - { - solAssert(m_baseArguments.count(&_constructor), ""); - std::vector<ASTPointer<Expression>> const* arguments = m_baseArguments[&_constructor]; - solAssert(arguments, ""); - for (unsigned i = 0; i < arguments->size(); ++i) - compileExpression(*(arguments->at(i)), constructorType.parameterTypes()[i]); - } - _constructor.accept(*this); -} - -void Compiler::appendConstructor(FunctionDefinition const& _constructor) -{ - CompilerContext::LocationSetter locationSetter(m_context, _constructor); - // copy constructor arguments from code to memory and then to stack, they are supplied after the actual program - if (!_constructor.parameters().empty()) - { - unsigned argumentSize = 0; - for (ASTPointer<VariableDeclaration> const& var: _constructor.parameters()) - if (var->annotation().type->isDynamicallySized()) - { - argumentSize = 0; - break; - } - else - argumentSize += var->annotation().type->calldataEncodedSize(); - - CompilerUtils(m_context).fetchFreeMemoryPointer(); - if (argumentSize == 0) - { - // argument size is dynamic, use CODESIZE to determine it - m_context.appendProgramSize(); // program itself - // CODESIZE is program plus manually added arguments - m_context << Instruction::CODESIZE << Instruction::SUB; - } - else - m_context << u256(argumentSize); - // stack: <memptr> <argument size> - m_context << Instruction::DUP1; - m_context.appendProgramSize(); - m_context << Instruction::DUP4 << Instruction::CODECOPY; - m_context << Instruction::DUP2 << Instruction::ADD; - CompilerUtils(m_context).storeFreeMemoryPointer(); - // stack: <memptr> - appendCalldataUnpacker(FunctionType(_constructor).parameterTypes(), true); - } - _constructor.accept(*this); -} - -void Compiler::appendFunctionSelector(ContractDefinition const& _contract) -{ - map<FixedHash<4>, FunctionTypePointer> interfaceFunctions = _contract.interfaceFunctions(); - map<FixedHash<4>, const eth::AssemblyItem> callDataUnpackerEntryPoints; - - FunctionDefinition const* fallback = _contract.fallbackFunction(); - eth::AssemblyItem notFound = m_context.newTag(); - // shortcut messages without data if we have many functions in order to be able to receive - // ether with constant gas - if (interfaceFunctions.size() > 5 || fallback) - { - m_context << Instruction::CALLDATASIZE << Instruction::ISZERO; - m_context.appendConditionalJumpTo(notFound); - } - - // retrieve the function signature hash from the calldata - if (!interfaceFunctions.empty()) - CompilerUtils(m_context).loadFromMemory(0, IntegerType(CompilerUtils::dataStartOffset * 8), true); - - // stack now is: 1 0 <funhash> - for (auto const& it: interfaceFunctions) - { - callDataUnpackerEntryPoints.insert(std::make_pair(it.first, m_context.newTag())); - m_context << dupInstruction(1) << u256(FixedHash<4>::Arith(it.first)) << Instruction::EQ; - m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.at(it.first)); - } - m_context.appendJumpTo(notFound); - - m_context << notFound; - if (fallback) - { - eth::AssemblyItem returnTag = m_context.pushNewTag(); - fallback->accept(*this); - m_context << returnTag; - appendReturnValuePacker(FunctionType(*fallback).returnParameterTypes(), _contract.isLibrary()); - } - else if (_contract.isLibrary()) - // Reject invalid library calls and ether sent to a library. - m_context.appendJumpTo(m_context.errorTag()); - else - m_context << Instruction::STOP; // function not found - - for (auto const& it: interfaceFunctions) - { - FunctionTypePointer const& functionType = it.second; - solAssert(functionType->hasDeclaration(), ""); - CompilerContext::LocationSetter locationSetter(m_context, functionType->declaration()); - m_context << callDataUnpackerEntryPoints.at(it.first); - eth::AssemblyItem returnTag = m_context.pushNewTag(); - m_context << CompilerUtils::dataStartOffset; - appendCalldataUnpacker(functionType->parameterTypes()); - m_context.appendJumpTo(m_context.functionEntryLabel(functionType->declaration())); - m_context << returnTag; - appendReturnValuePacker(functionType->returnParameterTypes(), _contract.isLibrary()); - } -} - -void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory) -{ - // We do not check the calldata size, everything is zero-padded - - //@todo this does not yet support nested dynamic arrays - - // Retain the offset pointer as base_offset, the point from which the data offsets are computed. - m_context << Instruction::DUP1; - for (TypePointer const& parameterType: _typeParameters) - { - // stack: v1 v2 ... v(k-1) base_offset current_offset - TypePointer type = parameterType->decodingType(); - if (type->category() == Type::Category::Array) - { - auto const& arrayType = dynamic_cast<ArrayType const&>(*type); - solAssert(!arrayType.baseType()->isDynamicallySized(), "Nested arrays not yet implemented."); - if (_fromMemory) - { - solAssert( - arrayType.baseType()->isValueType(), - "Nested memory arrays not yet implemented here." - ); - // @todo If base type is an array or struct, it is still calldata-style encoded, so - // we would have to convert it like below. - solAssert(arrayType.location() == DataLocation::Memory, ""); - if (arrayType.isDynamicallySized()) - { - // compute data pointer - m_context << Instruction::DUP1 << Instruction::MLOAD; - m_context << Instruction::DUP3 << Instruction::ADD; - m_context << Instruction::SWAP2 << Instruction::SWAP1; - m_context << u256(0x20) << Instruction::ADD; - } - else - { - m_context << Instruction::SWAP1 << Instruction::DUP2; - m_context << u256(arrayType.calldataEncodedSize(true)) << Instruction::ADD; - } - } - else - { - // first load from calldata and potentially convert to memory if arrayType is memory - TypePointer calldataType = arrayType.copyForLocation(DataLocation::CallData, false); - if (calldataType->isDynamicallySized()) - { - // put on stack: data_pointer length - CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory); - // stack: base_offset data_offset next_pointer - m_context << Instruction::SWAP1 << Instruction::DUP3 << Instruction::ADD; - // stack: base_offset next_pointer data_pointer - // retrieve length - CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory, true); - // stack: base_offset next_pointer length data_pointer - m_context << Instruction::SWAP2; - // stack: base_offset data_pointer length next_pointer - } - else - { - // leave the pointer on the stack - m_context << Instruction::DUP1; - m_context << u256(calldataType->calldataEncodedSize()) << Instruction::ADD; - } - if (arrayType.location() == DataLocation::Memory) - { - // stack: base_offset calldata_ref [length] next_calldata - // copy to memory - // move calldata type up again - CompilerUtils(m_context).moveIntoStack(calldataType->sizeOnStack()); - CompilerUtils(m_context).convertType(*calldataType, arrayType); - // fetch next pointer again - CompilerUtils(m_context).moveToStackTop(arrayType.sizeOnStack()); - } - // move base_offset up - CompilerUtils(m_context).moveToStackTop(1 + arrayType.sizeOnStack()); - m_context << Instruction::SWAP1; - } - } - else - { - solAssert(!type->isDynamicallySized(), "Unknown dynamically sized type: " + type->toString()); - CompilerUtils(m_context).loadFromMemoryDynamic(*type, !_fromMemory, true); - CompilerUtils(m_context).moveToStackTop(1 + type->sizeOnStack()); - m_context << Instruction::SWAP1; - } - // stack: v1 v2 ... v(k-1) v(k) base_offset mem_offset - } - m_context << Instruction::POP << Instruction::POP; -} - -void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters, bool _isLibrary) -{ - CompilerUtils utils(m_context); - if (_typeParameters.empty()) - m_context << Instruction::STOP; - else - { - utils.fetchFreeMemoryPointer(); - //@todo optimization: if we return a single memory array, there should be enough space before - // its data to add the needed parts and we avoid a memory copy. - utils.encodeToMemory(_typeParameters, _typeParameters, true, false, _isLibrary); - utils.toSizeAfterFreeMemoryPointer(); - m_context << Instruction::RETURN; - } -} - -void Compiler::registerStateVariables(ContractDefinition const& _contract) -{ - for (auto const& var: ContractType(_contract).stateVariables()) - m_context.addStateVariable(*get<0>(var), get<1>(var), get<2>(var)); -} - -void Compiler::initializeStateVariables(ContractDefinition const& _contract) -{ - for (VariableDeclaration const* variable: _contract.stateVariables()) - if (variable->value() && !variable->isConstant()) - ExpressionCompiler(m_context, m_optimize).appendStateVariableInitialization(*variable); -} - -bool Compiler::visit(VariableDeclaration const& _variableDeclaration) -{ - solAssert(_variableDeclaration.isStateVariable(), "Compiler visit to non-state variable declaration."); - CompilerContext::LocationSetter locationSetter(m_context, _variableDeclaration); - - m_context.startFunction(_variableDeclaration); - m_breakTags.clear(); - m_continueTags.clear(); - - if (_variableDeclaration.isConstant()) - ExpressionCompiler(m_context, m_optimize).appendConstStateVariableAccessor(_variableDeclaration); - else - ExpressionCompiler(m_context, m_optimize).appendStateVariableAccessor(_variableDeclaration); - - return false; -} - -bool Compiler::visit(FunctionDefinition const& _function) -{ - CompilerContext::LocationSetter locationSetter(m_context, _function); - - m_context.startFunction(_function); - - // stack upon entry: [return address] [arg0] [arg1] ... [argn] - // reserve additional slots: [retarg0] ... [retargm] [localvar0] ... [localvarp] - - unsigned parametersSize = CompilerUtils::sizeOnStack(_function.parameters()); - if (!_function.isConstructor()) - // adding 1 for return address. - m_context.adjustStackOffset(parametersSize + 1); - for (ASTPointer<VariableDeclaration const> const& variable: _function.parameters()) - { - m_context.addVariable(*variable, parametersSize); - parametersSize -= variable->annotation().type->sizeOnStack(); - } - - for (ASTPointer<VariableDeclaration const> const& variable: _function.returnParameters()) - appendStackVariableInitialisation(*variable); - for (VariableDeclaration const* localVariable: _function.localVariables()) - appendStackVariableInitialisation(*localVariable); - - if (_function.isConstructor()) - if (auto c = m_context.nextConstructor(dynamic_cast<ContractDefinition const&>(*_function.scope()))) - appendBaseConstructor(*c); - - m_returnTag = m_context.newTag(); - m_breakTags.clear(); - m_continueTags.clear(); - m_stackCleanupForReturn = 0; - m_currentFunction = &_function; - m_modifierDepth = 0; - - appendModifierOrFunctionCode(); - - m_context << m_returnTag; - - // Now we need to re-shuffle the stack. For this we keep a record of the stack layout - // that shows the target positions of the elements, where "-1" denotes that this element needs - // to be removed from the stack. - // Note that the fact that the return arguments are of increasing index is vital for this - // algorithm to work. - - unsigned const c_argumentsSize = CompilerUtils::sizeOnStack(_function.parameters()); - unsigned const c_returnValuesSize = CompilerUtils::sizeOnStack(_function.returnParameters()); - unsigned const c_localVariablesSize = CompilerUtils::sizeOnStack(_function.localVariables()); - - vector<int> stackLayout; - stackLayout.push_back(c_returnValuesSize); // target of return address - stackLayout += vector<int>(c_argumentsSize, -1); // discard all arguments - for (unsigned i = 0; i < c_returnValuesSize; ++i) - stackLayout.push_back(i); - stackLayout += vector<int>(c_localVariablesSize, -1); - - solAssert(stackLayout.size() <= 17, "Stack too deep, try removing local variables."); - while (stackLayout.back() != int(stackLayout.size() - 1)) - if (stackLayout.back() < 0) - { - m_context << Instruction::POP; - stackLayout.pop_back(); - } - else - { - m_context << swapInstruction(stackLayout.size() - stackLayout.back() - 1); - swap(stackLayout[stackLayout.back()], stackLayout.back()); - } - //@todo assert that everything is in place now - - for (ASTPointer<VariableDeclaration const> const& variable: _function.parameters() + _function.returnParameters()) - m_context.removeVariable(*variable); - for (VariableDeclaration const* localVariable: _function.localVariables()) - m_context.removeVariable(*localVariable); - - m_context.adjustStackOffset(-(int)c_returnValuesSize); - - if (!_function.isConstructor()) - m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction); - return false; -} - -bool Compiler::visit(InlineAssembly const& _inlineAssembly) -{ - ErrorList errors; - assembly::CodeGenerator codeGen(_inlineAssembly.operations(), errors); - int startStackHeight = m_context.stackHeight(); - m_context.appendInlineAssembly(codeGen.assemble( - [&](assembly::Identifier const& _identifier, eth::Assembly& _assembly, assembly::CodeGenerator::IdentifierContext _context) { - auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier); - if (ref == _inlineAssembly.annotation().externalReferences.end()) - return false; - Declaration const* decl = ref->second; - solAssert(!!decl, ""); - if (_context == assembly::CodeGenerator::IdentifierContext::RValue) - { - solAssert(!!decl->type(), "Type of declaration required but not yet determined."); - if (/*FunctionDefinition const* functionDef = */dynamic_cast<FunctionDefinition const*>(decl)) - { - solAssert(false, "Referencing local functions in inline assembly not yet implemented."); - // This does not work directly, because the label does not exist in _assembly - // (it is a fresh assembly object). - // _assembly.append(m_context.virtualFunctionEntryLabel(*functionDef).pushTag()); - } - else if (auto variable = dynamic_cast<VariableDeclaration const*>(decl)) - { - solAssert(!variable->isConstant(), ""); - if (m_context.isLocalVariable(variable)) - { - int stackDiff = _assembly.deposit() + startStackHeight - m_context.baseStackOffsetOfVariable(*variable); - if (stackDiff < 1 || stackDiff > 16) - BOOST_THROW_EXCEPTION( - CompilerError() << - errinfo_comment("Stack too deep, try removing local variables.") - ); - for (unsigned i = 0; i < variable->type()->sizeOnStack(); ++i) - _assembly.append(dupInstruction(stackDiff)); - } - else - { - solAssert(m_context.isStateVariable(variable), "Invalid variable type."); - auto const& location = m_context.storageLocationOfVariable(*variable); - if (!variable->type()->isValueType()) - { - solAssert(location.second == 0, "Intra-slot offest assumed to be zero."); - _assembly.append(location.first); - } - else - { - _assembly.append(location.first); - _assembly.append(u256(location.second)); - } - } - } - else if (auto contract = dynamic_cast<ContractDefinition const*>(decl)) - { - solAssert(contract->isLibrary(), ""); - _assembly.appendLibraryAddress(contract->name()); - } - else - solAssert(false, "Invalid declaration type."); - } else { - // lvalue context - auto variable = dynamic_cast<VariableDeclaration const*>(decl); - solAssert( - !!variable || !m_context.isLocalVariable(variable), - "Can only assign to stack variables in inline assembly." - ); - unsigned size = variable->type()->sizeOnStack(); - int stackDiff = _assembly.deposit() + startStackHeight - m_context.baseStackOffsetOfVariable(*variable) - size; - if (stackDiff > 16 || stackDiff < 1) - BOOST_THROW_EXCEPTION( - CompilerError() << - errinfo_comment("Stack too deep, try removing local variables.") - ); - for (unsigned i = 0; i < size; ++i) { - _assembly.append(swapInstruction(stackDiff)); - _assembly.append(Instruction::POP); - } - } - return true; - } - )); - solAssert(errors.empty(), "Code generation for inline assembly with errors requested."); - return false; -} - -bool Compiler::visit(IfStatement const& _ifStatement) -{ - StackHeightChecker checker(m_context); - CompilerContext::LocationSetter locationSetter(m_context, _ifStatement); - compileExpression(_ifStatement.condition()); - m_context << Instruction::ISZERO; - eth::AssemblyItem falseTag = m_context.appendConditionalJump(); - eth::AssemblyItem endTag = falseTag; - _ifStatement.trueStatement().accept(*this); - if (_ifStatement.falseStatement()) - { - endTag = m_context.appendJumpToNew(); - m_context << falseTag; - _ifStatement.falseStatement()->accept(*this); - } - m_context << endTag; - - checker.check(); - return false; -} - -bool Compiler::visit(WhileStatement const& _whileStatement) -{ - StackHeightChecker checker(m_context); - CompilerContext::LocationSetter locationSetter(m_context, _whileStatement); - eth::AssemblyItem loopStart = m_context.newTag(); - eth::AssemblyItem loopEnd = m_context.newTag(); - m_continueTags.push_back(loopStart); - m_breakTags.push_back(loopEnd); - - m_context << loopStart; - compileExpression(_whileStatement.condition()); - m_context << Instruction::ISZERO; - m_context.appendConditionalJumpTo(loopEnd); - - _whileStatement.body().accept(*this); - - m_context.appendJumpTo(loopStart); - m_context << loopEnd; - - m_continueTags.pop_back(); - m_breakTags.pop_back(); - - checker.check(); - return false; -} - -bool Compiler::visit(ForStatement const& _forStatement) -{ - StackHeightChecker checker(m_context); - CompilerContext::LocationSetter locationSetter(m_context, _forStatement); - eth::AssemblyItem loopStart = m_context.newTag(); - eth::AssemblyItem loopEnd = m_context.newTag(); - eth::AssemblyItem loopNext = m_context.newTag(); - m_continueTags.push_back(loopNext); - m_breakTags.push_back(loopEnd); - - if (_forStatement.initializationExpression()) - _forStatement.initializationExpression()->accept(*this); - - m_context << loopStart; - - // if there is no terminating condition in for, default is to always be true - if (_forStatement.condition()) - { - compileExpression(*_forStatement.condition()); - m_context << Instruction::ISZERO; - m_context.appendConditionalJumpTo(loopEnd); - } - - _forStatement.body().accept(*this); - - m_context << loopNext; - - // for's loop expression if existing - if (_forStatement.loopExpression()) - _forStatement.loopExpression()->accept(*this); - - m_context.appendJumpTo(loopStart); - m_context << loopEnd; - - m_continueTags.pop_back(); - m_breakTags.pop_back(); - - checker.check(); - return false; -} - -bool Compiler::visit(Continue const& _continueStatement) -{ - CompilerContext::LocationSetter locationSetter(m_context, _continueStatement); - if (!m_continueTags.empty()) - m_context.appendJumpTo(m_continueTags.back()); - return false; -} - -bool Compiler::visit(Break const& _breakStatement) -{ - CompilerContext::LocationSetter locationSetter(m_context, _breakStatement); - if (!m_breakTags.empty()) - m_context.appendJumpTo(m_breakTags.back()); - return false; -} - -bool Compiler::visit(Return const& _return) -{ - CompilerContext::LocationSetter locationSetter(m_context, _return); - if (Expression const* expression = _return.expression()) - { - solAssert(_return.annotation().functionReturnParameters, "Invalid return parameters pointer."); - vector<ASTPointer<VariableDeclaration>> const& returnParameters = - _return.annotation().functionReturnParameters->parameters(); - TypePointers types; - for (auto const& retVariable: returnParameters) - types.push_back(retVariable->annotation().type); - - TypePointer expectedType; - if (expression->annotation().type->category() == Type::Category::Tuple || types.size() != 1) - expectedType = make_shared<TupleType>(types); - else - expectedType = types.front(); - compileExpression(*expression, expectedType); - - for (auto const& retVariable: boost::adaptors::reverse(returnParameters)) - CompilerUtils(m_context).moveToStackVariable(*retVariable); - } - for (unsigned i = 0; i < m_stackCleanupForReturn; ++i) - m_context << Instruction::POP; - m_context.appendJumpTo(m_returnTag); - m_context.adjustStackOffset(m_stackCleanupForReturn); - return false; -} - -bool Compiler::visit(Throw const& _throw) -{ - CompilerContext::LocationSetter locationSetter(m_context, _throw); - m_context.appendJumpTo(m_context.errorTag()); - return false; -} - -bool Compiler::visit(VariableDeclarationStatement const& _variableDeclarationStatement) -{ - StackHeightChecker checker(m_context); - CompilerContext::LocationSetter locationSetter(m_context, _variableDeclarationStatement); - if (Expression const* expression = _variableDeclarationStatement.initialValue()) - { - CompilerUtils utils(m_context); - compileExpression(*expression); - TypePointers valueTypes; - if (auto tupleType = dynamic_cast<TupleType const*>(expression->annotation().type.get())) - valueTypes = tupleType->components(); - else - valueTypes = TypePointers{expression->annotation().type}; - auto const& assignments = _variableDeclarationStatement.annotation().assignments; - solAssert(assignments.size() == valueTypes.size(), ""); - for (size_t i = 0; i < assignments.size(); ++i) - { - size_t j = assignments.size() - i - 1; - solAssert(!!valueTypes[j], ""); - VariableDeclaration const* varDecl = assignments[j]; - if (!varDecl) - utils.popStackElement(*valueTypes[j]); - else - { - utils.convertType(*valueTypes[j], *varDecl->annotation().type); - utils.moveToStackVariable(*varDecl); - } - } - } - checker.check(); - return false; -} - -bool Compiler::visit(ExpressionStatement const& _expressionStatement) -{ - StackHeightChecker checker(m_context); - CompilerContext::LocationSetter locationSetter(m_context, _expressionStatement); - Expression const& expression = _expressionStatement.expression(); - compileExpression(expression); - CompilerUtils(m_context).popStackElement(*expression.annotation().type); - checker.check(); - return false; -} - -bool Compiler::visit(PlaceholderStatement const& _placeholderStatement) -{ - StackHeightChecker checker(m_context); - CompilerContext::LocationSetter locationSetter(m_context, _placeholderStatement); - ++m_modifierDepth; - appendModifierOrFunctionCode(); - --m_modifierDepth; - checker.check(); - return true; -} - -void Compiler::appendMissingFunctions() -{ - while (Declaration const* function = m_context.nextFunctionToCompile()) - { - m_context.setStackOffset(0); - function->accept(*this); - solAssert(m_context.nextFunctionToCompile() != function, "Compiled the wrong function?"); - } -} - -void Compiler::appendModifierOrFunctionCode() -{ - solAssert(m_currentFunction, ""); - if (m_modifierDepth >= m_currentFunction->modifiers().size()) - m_currentFunction->body().accept(*this); - else - { - ASTPointer<ModifierInvocation> const& modifierInvocation = m_currentFunction->modifiers()[m_modifierDepth]; - - // constructor call should be excluded - if (dynamic_cast<ContractDefinition const*>(modifierInvocation->name()->annotation().referencedDeclaration)) - { - ++m_modifierDepth; - appendModifierOrFunctionCode(); - --m_modifierDepth; - return; - } - - ModifierDefinition const& modifier = m_context.functionModifier(modifierInvocation->name()->name()); - CompilerContext::LocationSetter locationSetter(m_context, modifier); - solAssert(modifier.parameters().size() == modifierInvocation->arguments().size(), ""); - for (unsigned i = 0; i < modifier.parameters().size(); ++i) - { - m_context.addVariable(*modifier.parameters()[i]); - compileExpression( - *modifierInvocation->arguments()[i], - modifier.parameters()[i]->annotation().type - ); - } - for (VariableDeclaration const* localVariable: modifier.localVariables()) - appendStackVariableInitialisation(*localVariable); - - unsigned const c_stackSurplus = CompilerUtils::sizeOnStack(modifier.parameters()) + - CompilerUtils::sizeOnStack(modifier.localVariables()); - m_stackCleanupForReturn += c_stackSurplus; - - modifier.body().accept(*this); - - for (unsigned i = 0; i < c_stackSurplus; ++i) - m_context << Instruction::POP; - m_stackCleanupForReturn -= c_stackSurplus; - } -} - -void Compiler::appendStackVariableInitialisation(VariableDeclaration const& _variable) -{ - CompilerContext::LocationSetter location(m_context, _variable); - m_context.addVariable(_variable); - CompilerUtils(m_context).pushZeroValue(*_variable.annotation().type); -} - -void Compiler::compileExpression(Expression const& _expression, TypePointer const& _targetType) -{ - ExpressionCompiler expressionCompiler(m_context, m_optimize); - expressionCompiler.compile(_expression); - if (_targetType) - CompilerUtils(m_context).convertType(*_expression.annotation().type, *_targetType); -} - -eth::Assembly Compiler::cloneRuntime() -{ - eth::Assembly a; - a << Instruction::CALLDATASIZE; - a << u256(0) << Instruction::DUP1 << Instruction::CALLDATACOPY; - //@todo adjust for larger return values, make this dynamic. - a << u256(0x20) << u256(0) << Instruction::CALLDATASIZE; - a << u256(0); - // this is the address which has to be substituted by the linker. - //@todo implement as special "marker" AssemblyItem. - a << u256("0xcafecafecafecafecafecafecafecafecafecafe"); - a << u256(eth::GasCosts::callGas + 10) << Instruction::GAS << Instruction::SUB; - a << Instruction::DELEGATECALL; - //Propagate error condition (if DELEGATECALL pushes 0 on stack). - a << Instruction::ISZERO; - a.appendJumpI(a.errorTag()); - //@todo adjust for larger return values, make this dynamic. - a << u256(0x20) << u256(0) << Instruction::RETURN; - return a; -} diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h index 69040253..fccb68a9 100644 --- a/libsolidity/codegen/Compiler.h +++ b/libsolidity/codegen/Compiler.h @@ -24,20 +24,18 @@ #include <ostream> #include <functional> -#include <libsolidity/ast/ASTVisitor.h> #include <libsolidity/codegen/CompilerContext.h> #include <libevmasm/Assembly.h> namespace dev { namespace solidity { -class Compiler: private ASTConstVisitor +class Compiler { public: explicit Compiler(bool _optimize = false, unsigned _runs = 200): m_optimize(_optimize), - m_optimizeRuns(_runs), - m_returnTag(m_context.newTag()) + m_optimizeRuns(_runs) { } void compileContract( @@ -69,69 +67,11 @@ public: eth::AssemblyItem functionEntryLabel(FunctionDefinition const& _function) const; private: - /// Registers the non-function objects inside the contract with the context and stores the basic - /// information about the contract like the AST annotations. - void initializeContext( - ContractDefinition const& _contract, - std::map<ContractDefinition const*, eth::Assembly const*> const& _compiledContracts - ); - /// Adds the code that is run at creation time. Should be run after exchanging the run-time context - /// with a new and initialized context. Adds the constructor code. - void packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext); - /// Appends state variable initialisation and constructor code. - void appendInitAndConstructorCode(ContractDefinition const& _contract); - void appendBaseConstructor(FunctionDefinition const& _constructor); - void appendConstructor(FunctionDefinition const& _constructor); - void appendFunctionSelector(ContractDefinition const& _contract); - /// Creates code that unpacks the arguments for the given function represented by a vector of TypePointers. - /// From memory if @a _fromMemory is true, otherwise from call data. - /// Expects source offset on the stack, which is removed. - void appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory = false); - void appendReturnValuePacker(TypePointers const& _typeParameters, bool _isLibrary); - - void registerStateVariables(ContractDefinition const& _contract); - void initializeStateVariables(ContractDefinition const& _contract); - - virtual bool visit(VariableDeclaration const& _variableDeclaration) override; - virtual bool visit(FunctionDefinition const& _function) override; - virtual bool visit(InlineAssembly const& _inlineAssembly) override; - virtual bool visit(IfStatement const& _ifStatement) override; - virtual bool visit(WhileStatement const& _whileStatement) override; - virtual bool visit(ForStatement const& _forStatement) override; - virtual bool visit(Continue const& _continue) override; - virtual bool visit(Break const& _break) override; - virtual bool visit(Return const& _return) override; - virtual bool visit(Throw const& _throw) override; - virtual bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override; - virtual bool visit(ExpressionStatement const& _expressionStatement) override; - virtual bool visit(PlaceholderStatement const&) override; - - /// Repeatedly visits all function which are referenced but which are not compiled yet. - void appendMissingFunctions(); - - /// Appends one layer of function modifier code of the current function, or the function - /// body itself if the last modifier was reached. - void appendModifierOrFunctionCode(); - - void appendStackVariableInitialisation(VariableDeclaration const& _variable); - void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer()); - - /// @returns the runtime assembly for clone contracts. - static eth::Assembly cloneRuntime(); - bool const m_optimize; unsigned const m_optimizeRuns; CompilerContext m_context; - size_t m_runtimeSub = size_t(-1); ///< Identifier of the runtime sub-assembly + size_t m_runtimeSub = size_t(-1); ///< Identifier of the runtime sub-assembly, if present. CompilerContext m_runtimeContext; - std::vector<eth::AssemblyItem> m_breakTags; ///< tag to jump to for a "break" statement - std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" statement - eth::AssemblyItem m_returnTag; ///< tag to jump to for a "return" statement - unsigned m_modifierDepth = 0; - FunctionDefinition const* m_currentFunction = nullptr; - unsigned m_stackCleanupForReturn = 0; ///< this number of stack elements need to be removed before jump to m_returnTag - // arguments for base constructors, filled in derived-to-base order - std::map<FunctionDefinition const*, std::vector<ASTPointer<Expression>> const*> m_baseArguments; }; } diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 5abe59fe..a56335ce 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -113,8 +113,6 @@ public: /// Adds a subroutine to the code (in the data section) and pushes its size (via a tag) /// on the stack. @returns the assembly item corresponding to the pushed subroutine, i.e. its offset. eth::AssemblyItem addSubroutine(eth::Assembly const& _assembly) { return m_asm.appendSubSize(_assembly); } - /// Appends the given code (used by inline assembly) ignoring any stack height changes. - void appendInlineAssembly(eth::Assembly const& _assembly) { int deposit = m_asm.deposit(); m_asm.append(_assembly); m_asm.setDeposit(deposit); } /// Pushes the size of the final program void appendProgramSize() { return m_asm.appendProgramSize(); } /// Adds data to the data section, pushes a reference to the stack @@ -140,6 +138,10 @@ public: void optimise(unsigned _runs = 200) { m_asm.optimise(true, true, _runs); } eth::Assembly const& assembly() const { return m_asm; } + /// @returns non-const reference to the underlying assembly. Should be avoided in favour of + /// wrappers in this class. + eth::Assembly& nonConstAssembly() { return m_asm; } + /// @arg _sourceCodes is the map of input files to source code strings /// @arg _inJsonFormat shows whether the out should be in Json format Json::Value streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 36ed480e..efb9b10a 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -323,18 +323,17 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp } else { - // clear lower-order bytes for conversion to shorter bytes - we always clean + // clear for conversion to longer bytes solAssert(targetTypeCategory == Type::Category::FixedBytes, "Invalid type conversion requested."); FixedBytesType const& targetType = dynamic_cast<FixedBytesType const&>(_targetType); - if (targetType.numBytes() < typeOnStack.numBytes()) + if (targetType.numBytes() > typeOnStack.numBytes() || _cleanupNeeded) { - if (targetType.numBytes() == 0) - m_context << Instruction::DUP1 << Instruction::XOR; + if (typeOnStack.numBytes() == 0) + m_context << Instruction::POP << u256(0); else { - m_context << (u256(1) << (256 - targetType.numBytes() * 8)); - m_context << Instruction::DUP1 << Instruction::SWAP2; - m_context << Instruction::DIV << Instruction::MUL; + m_context << ((u256(1) << (256 - typeOnStack.numBytes() * 8)) - 1); + m_context << Instruction::NOT << Instruction::AND; } } } @@ -343,12 +342,14 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp case Type::Category::Enum: solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Enum, ""); break; + case Type::Category::FixedPoint: + solAssert(false, "Not yet implemented - FixedPointType."); case Type::Category::Integer: case Type::Category::Contract: - case Type::Category::IntegerConstant: + case Type::Category::RationalNumber: if (targetTypeCategory == Type::Category::FixedBytes) { - solAssert(stackTypeCategory == Type::Category::Integer || stackTypeCategory == Type::Category::IntegerConstant, + solAssert(stackTypeCategory == Type::Category::Integer || stackTypeCategory == Type::Category::RationalNumber, "Invalid conversion to FixedBytesType requested."); // conversion from bytes to string. no need to clean the high bit // only to shift left because of opposite alignment @@ -361,17 +362,33 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp else if (targetTypeCategory == Type::Category::Enum) // just clean convertType(_typeOnStack, *_typeOnStack.mobileType(), true); + else if (targetTypeCategory == Type::Category::FixedPoint) + { + solAssert( + stackTypeCategory == Type::Category::Integer || + stackTypeCategory == Type::Category::RationalNumber || + stackTypeCategory == Type::Category::FixedPoint, + "Invalid conversion to FixedMxNType requested." + ); + //shift all integer bits onto the left side of the fixed type + FixedPointType const& targetFixedPointType = dynamic_cast<FixedPointType const&>(_targetType); + if (auto typeOnStack = dynamic_cast<IntegerType const*>(&_typeOnStack)) + if (targetFixedPointType.integerBits() > typeOnStack->numBits()) + cleanHigherOrderBits(*typeOnStack); + solAssert(false, "Not yet implemented - FixedPointType."); + } else { solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Contract, ""); IntegerType addressType(0, IntegerType::Modifier::Address); IntegerType const& targetType = targetTypeCategory == Type::Category::Integer ? dynamic_cast<IntegerType const&>(_targetType) : addressType; - if (stackTypeCategory == Type::Category::IntegerConstant) + if (stackTypeCategory == Type::Category::RationalNumber) { - IntegerConstantType const& constType = dynamic_cast<IntegerConstantType const&>(_typeOnStack); + RationalNumberType const& constType = dynamic_cast<RationalNumberType const&>(_typeOnStack); // We know that the stack is clean, we only have to clean for a narrowing conversion // where cleanup is forced. + solAssert(!constType.isFractional(), "Not yet implemented - FixedPointType."); if (targetType.numBits() < constType.integerType()->numBits() && _cleanupNeeded) cleanHigherOrderBits(targetType); } @@ -619,6 +636,10 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp } break; } + case Type::Category::Bool: + solAssert(_targetType == _typeOnStack, "Invalid conversion for bool."); + if (_cleanupNeeded) + m_context << Instruction::ISZERO << Instruction::ISZERO; default: // All other types should not be convertible to non-equal types. solAssert(_typeOnStack == _targetType, "Invalid type conversion requested."); diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp new file mode 100644 index 00000000..bcfd33f2 --- /dev/null +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -0,0 +1,856 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2014 + * Solidity compiler. + */ + +#include <libsolidity/codegen/ContractCompiler.h> +#include <algorithm> +#include <boost/range/adaptor/reversed.hpp> +#include <libevmasm/Instruction.h> +#include <libevmasm/Assembly.h> +#include <libevmasm/GasMeter.h> +#include <libsolidity/inlineasm/AsmCodeGen.h> +#include <libsolidity/ast/AST.h> +#include <libsolidity/codegen/ExpressionCompiler.h> +#include <libsolidity/codegen/CompilerUtils.h> +using namespace std; +using namespace dev; +using namespace dev::solidity; + +/** + * Simple helper class to ensure that the stack height is the same at certain places in the code. + */ +class StackHeightChecker +{ +public: + StackHeightChecker(CompilerContext const& _context): + m_context(_context), stackHeight(m_context.stackHeight()) {} + void check() { solAssert(m_context.stackHeight() == stackHeight, "I sense a disturbance in the stack."); } +private: + CompilerContext const& m_context; + unsigned stackHeight; +}; + +void ContractCompiler::compileContract( + ContractDefinition const& _contract, + std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts +) +{ + CompilerContext::LocationSetter locationSetter(m_context, _contract); + initializeContext(_contract, _contracts); + appendFunctionSelector(_contract); + appendMissingFunctions(); +} + +size_t ContractCompiler::compileConstructor( + CompilerContext const& _runtimeContext, + ContractDefinition const& _contract, + std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts +) +{ + CompilerContext::LocationSetter locationSetter(m_context, _contract); + initializeContext(_contract, _contracts); + return packIntoContractCreator(_contract, _runtimeContext); +} + +size_t ContractCompiler::compileClone( + ContractDefinition const& _contract, + map<ContractDefinition const*, eth::Assembly const*> const& _contracts +) +{ + initializeContext(_contract, _contracts); + + appendInitAndConstructorCode(_contract); + + //@todo determine largest return size of all runtime functions + eth::AssemblyItem runtimeSub = m_context.addSubroutine(cloneRuntime()); + + // stack contains sub size + m_context << Instruction::DUP1 << runtimeSub << u256(0) << Instruction::CODECOPY; + m_context << u256(0) << Instruction::RETURN; + + appendMissingFunctions(); + + solAssert(runtimeSub.data() < numeric_limits<size_t>::max(), ""); + return size_t(runtimeSub.data()); +} + +void ContractCompiler::initializeContext( + ContractDefinition const& _contract, + map<ContractDefinition const*, eth::Assembly const*> const& _compiledContracts +) +{ + m_context.setCompiledContracts(_compiledContracts); + m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts); + CompilerUtils(m_context).initialiseFreeMemoryPointer(); + registerStateVariables(_contract); + m_context.resetVisitedNodes(&_contract); +} + +void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _contract) +{ + // Determine the arguments that are used for the base constructors. + std::vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts; + for (ContractDefinition const* contract: bases) + { + if (FunctionDefinition const* constructor = contract->constructor()) + for (auto const& modifier: constructor->modifiers()) + { + auto baseContract = dynamic_cast<ContractDefinition const*>( + modifier->name()->annotation().referencedDeclaration); + if (baseContract) + if (m_baseArguments.count(baseContract->constructor()) == 0) + m_baseArguments[baseContract->constructor()] = &modifier->arguments(); + } + + for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts()) + { + ContractDefinition const* baseContract = dynamic_cast<ContractDefinition const*>( + base->name().annotation().referencedDeclaration + ); + solAssert(baseContract, ""); + + if (m_baseArguments.count(baseContract->constructor()) == 0) + m_baseArguments[baseContract->constructor()] = &base->arguments(); + } + } + // Initialization of state variables in base-to-derived order. + for (ContractDefinition const* contract: boost::adaptors::reverse(bases)) + initializeStateVariables(*contract); + + if (FunctionDefinition const* constructor = _contract.constructor()) + appendConstructor(*constructor); + else if (auto c = m_context.nextConstructor(_contract)) + appendBaseConstructor(*c); +} + +size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext) +{ + appendInitAndConstructorCode(_contract); + + eth::AssemblyItem runtimeSub = m_context.addSubroutine(_runtimeContext.assembly()); + + // stack contains sub size + m_context << Instruction::DUP1 << runtimeSub << u256(0) << Instruction::CODECOPY; + m_context << u256(0) << Instruction::RETURN; + + // note that we have to include the functions again because of absolute jump labels + appendMissingFunctions(); + + solAssert(runtimeSub.data() < numeric_limits<size_t>::max(), ""); + return size_t(runtimeSub.data()); +} + +void ContractCompiler::appendBaseConstructor(FunctionDefinition const& _constructor) +{ + CompilerContext::LocationSetter locationSetter(m_context, _constructor); + FunctionType constructorType(_constructor); + if (!constructorType.parameterTypes().empty()) + { + solAssert(m_baseArguments.count(&_constructor), ""); + std::vector<ASTPointer<Expression>> const* arguments = m_baseArguments[&_constructor]; + solAssert(arguments, ""); + for (unsigned i = 0; i < arguments->size(); ++i) + compileExpression(*(arguments->at(i)), constructorType.parameterTypes()[i]); + } + _constructor.accept(*this); +} + +void ContractCompiler::appendConstructor(FunctionDefinition const& _constructor) +{ + CompilerContext::LocationSetter locationSetter(m_context, _constructor); + // copy constructor arguments from code to memory and then to stack, they are supplied after the actual program + if (!_constructor.parameters().empty()) + { + unsigned argumentSize = 0; + for (ASTPointer<VariableDeclaration> const& var: _constructor.parameters()) + if (var->annotation().type->isDynamicallySized()) + { + argumentSize = 0; + break; + } + else + argumentSize += var->annotation().type->calldataEncodedSize(); + + CompilerUtils(m_context).fetchFreeMemoryPointer(); + if (argumentSize == 0) + { + // argument size is dynamic, use CODESIZE to determine it + m_context.appendProgramSize(); // program itself + // CODESIZE is program plus manually added arguments + m_context << Instruction::CODESIZE << Instruction::SUB; + } + else + m_context << u256(argumentSize); + // stack: <memptr> <argument size> + m_context << Instruction::DUP1; + m_context.appendProgramSize(); + m_context << Instruction::DUP4 << Instruction::CODECOPY; + m_context << Instruction::DUP2 << Instruction::ADD; + CompilerUtils(m_context).storeFreeMemoryPointer(); + // stack: <memptr> + appendCalldataUnpacker(FunctionType(_constructor).parameterTypes(), true); + } + _constructor.accept(*this); +} + +void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contract) +{ + map<FixedHash<4>, FunctionTypePointer> interfaceFunctions = _contract.interfaceFunctions(); + map<FixedHash<4>, const eth::AssemblyItem> callDataUnpackerEntryPoints; + + FunctionDefinition const* fallback = _contract.fallbackFunction(); + eth::AssemblyItem notFound = m_context.newTag(); + // shortcut messages without data if we have many functions in order to be able to receive + // ether with constant gas + if (interfaceFunctions.size() > 5 || fallback) + { + m_context << Instruction::CALLDATASIZE << Instruction::ISZERO; + m_context.appendConditionalJumpTo(notFound); + } + + // retrieve the function signature hash from the calldata + if (!interfaceFunctions.empty()) + CompilerUtils(m_context).loadFromMemory(0, IntegerType(CompilerUtils::dataStartOffset * 8), true); + + // stack now is: 1 0 <funhash> + for (auto const& it: interfaceFunctions) + { + callDataUnpackerEntryPoints.insert(std::make_pair(it.first, m_context.newTag())); + m_context << dupInstruction(1) << u256(FixedHash<4>::Arith(it.first)) << Instruction::EQ; + m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.at(it.first)); + } + m_context.appendJumpTo(notFound); + + m_context << notFound; + if (fallback) + { + eth::AssemblyItem returnTag = m_context.pushNewTag(); + fallback->accept(*this); + m_context << returnTag; + appendReturnValuePacker(FunctionType(*fallback).returnParameterTypes(), _contract.isLibrary()); + } + else if (_contract.isLibrary()) + // Reject invalid library calls and ether sent to a library. + m_context.appendJumpTo(m_context.errorTag()); + else + m_context << Instruction::STOP; // function not found + + for (auto const& it: interfaceFunctions) + { + FunctionTypePointer const& functionType = it.second; + solAssert(functionType->hasDeclaration(), ""); + CompilerContext::LocationSetter locationSetter(m_context, functionType->declaration()); + m_context << callDataUnpackerEntryPoints.at(it.first); + eth::AssemblyItem returnTag = m_context.pushNewTag(); + m_context << CompilerUtils::dataStartOffset; + appendCalldataUnpacker(functionType->parameterTypes()); + m_context.appendJumpTo(m_context.functionEntryLabel(functionType->declaration())); + m_context << returnTag; + appendReturnValuePacker(functionType->returnParameterTypes(), _contract.isLibrary()); + } +} + +void ContractCompiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory) +{ + // We do not check the calldata size, everything is zero-padded + + //@todo this does not yet support nested dynamic arrays + + // Retain the offset pointer as base_offset, the point from which the data offsets are computed. + m_context << Instruction::DUP1; + for (TypePointer const& parameterType: _typeParameters) + { + // stack: v1 v2 ... v(k-1) base_offset current_offset + TypePointer type = parameterType->decodingType(); + if (type->category() == Type::Category::Array) + { + auto const& arrayType = dynamic_cast<ArrayType const&>(*type); + solAssert(!arrayType.baseType()->isDynamicallySized(), "Nested arrays not yet implemented."); + if (_fromMemory) + { + solAssert( + arrayType.baseType()->isValueType(), + "Nested memory arrays not yet implemented here." + ); + // @todo If base type is an array or struct, it is still calldata-style encoded, so + // we would have to convert it like below. + solAssert(arrayType.location() == DataLocation::Memory, ""); + if (arrayType.isDynamicallySized()) + { + // compute data pointer + m_context << Instruction::DUP1 << Instruction::MLOAD; + m_context << Instruction::DUP3 << Instruction::ADD; + m_context << Instruction::SWAP2 << Instruction::SWAP1; + m_context << u256(0x20) << Instruction::ADD; + } + else + { + m_context << Instruction::SWAP1 << Instruction::DUP2; + m_context << u256(arrayType.calldataEncodedSize(true)) << Instruction::ADD; + } + } + else + { + // first load from calldata and potentially convert to memory if arrayType is memory + TypePointer calldataType = arrayType.copyForLocation(DataLocation::CallData, false); + if (calldataType->isDynamicallySized()) + { + // put on stack: data_pointer length + CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory); + // stack: base_offset data_offset next_pointer + m_context << Instruction::SWAP1 << Instruction::DUP3 << Instruction::ADD; + // stack: base_offset next_pointer data_pointer + // retrieve length + CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory, true); + // stack: base_offset next_pointer length data_pointer + m_context << Instruction::SWAP2; + // stack: base_offset data_pointer length next_pointer + } + else + { + // leave the pointer on the stack + m_context << Instruction::DUP1; + m_context << u256(calldataType->calldataEncodedSize()) << Instruction::ADD; + } + if (arrayType.location() == DataLocation::Memory) + { + // stack: base_offset calldata_ref [length] next_calldata + // copy to memory + // move calldata type up again + CompilerUtils(m_context).moveIntoStack(calldataType->sizeOnStack()); + CompilerUtils(m_context).convertType(*calldataType, arrayType); + // fetch next pointer again + CompilerUtils(m_context).moveToStackTop(arrayType.sizeOnStack()); + } + // move base_offset up + CompilerUtils(m_context).moveToStackTop(1 + arrayType.sizeOnStack()); + m_context << Instruction::SWAP1; + } + } + else + { + solAssert(!type->isDynamicallySized(), "Unknown dynamically sized type: " + type->toString()); + CompilerUtils(m_context).loadFromMemoryDynamic(*type, !_fromMemory, true); + CompilerUtils(m_context).moveToStackTop(1 + type->sizeOnStack()); + m_context << Instruction::SWAP1; + } + // stack: v1 v2 ... v(k-1) v(k) base_offset mem_offset + } + m_context << Instruction::POP << Instruction::POP; +} + +void ContractCompiler::appendReturnValuePacker(TypePointers const& _typeParameters, bool _isLibrary) +{ + CompilerUtils utils(m_context); + if (_typeParameters.empty()) + m_context << Instruction::STOP; + else + { + utils.fetchFreeMemoryPointer(); + //@todo optimization: if we return a single memory array, there should be enough space before + // its data to add the needed parts and we avoid a memory copy. + utils.encodeToMemory(_typeParameters, _typeParameters, true, false, _isLibrary); + utils.toSizeAfterFreeMemoryPointer(); + m_context << Instruction::RETURN; + } +} + +void ContractCompiler::registerStateVariables(ContractDefinition const& _contract) +{ + for (auto const& var: ContractType(_contract).stateVariables()) + m_context.addStateVariable(*get<0>(var), get<1>(var), get<2>(var)); +} + +void ContractCompiler::initializeStateVariables(ContractDefinition const& _contract) +{ + for (VariableDeclaration const* variable: _contract.stateVariables()) + if (variable->value() && !variable->isConstant()) + ExpressionCompiler(m_context, m_optimise).appendStateVariableInitialization(*variable); +} + +bool ContractCompiler::visit(VariableDeclaration const& _variableDeclaration) +{ + solAssert(_variableDeclaration.isStateVariable(), "Compiler visit to non-state variable declaration."); + CompilerContext::LocationSetter locationSetter(m_context, _variableDeclaration); + + m_context.startFunction(_variableDeclaration); + m_breakTags.clear(); + m_continueTags.clear(); + + if (_variableDeclaration.isConstant()) + ExpressionCompiler(m_context, m_optimise).appendConstStateVariableAccessor(_variableDeclaration); + else + ExpressionCompiler(m_context, m_optimise).appendStateVariableAccessor(_variableDeclaration); + + return false; +} + +bool ContractCompiler::visit(FunctionDefinition const& _function) +{ + CompilerContext::LocationSetter locationSetter(m_context, _function); + + m_context.startFunction(_function); + + // stack upon entry: [return address] [arg0] [arg1] ... [argn] + // reserve additional slots: [retarg0] ... [retargm] [localvar0] ... [localvarp] + + unsigned parametersSize = CompilerUtils::sizeOnStack(_function.parameters()); + if (!_function.isConstructor()) + // adding 1 for return address. + m_context.adjustStackOffset(parametersSize + 1); + for (ASTPointer<VariableDeclaration const> const& variable: _function.parameters()) + { + m_context.addVariable(*variable, parametersSize); + parametersSize -= variable->annotation().type->sizeOnStack(); + } + + for (ASTPointer<VariableDeclaration const> const& variable: _function.returnParameters()) + appendStackVariableInitialisation(*variable); + for (VariableDeclaration const* localVariable: _function.localVariables()) + appendStackVariableInitialisation(*localVariable); + + if (_function.isConstructor()) + if (auto c = m_context.nextConstructor(dynamic_cast<ContractDefinition const&>(*_function.scope()))) + appendBaseConstructor(*c); + + m_returnTag = m_context.newTag(); + m_breakTags.clear(); + m_continueTags.clear(); + m_stackCleanupForReturn = 0; + m_currentFunction = &_function; + m_modifierDepth = 0; + + appendModifierOrFunctionCode(); + + m_context << m_returnTag; + + // Now we need to re-shuffle the stack. For this we keep a record of the stack layout + // that shows the target positions of the elements, where "-1" denotes that this element needs + // to be removed from the stack. + // Note that the fact that the return arguments are of increasing index is vital for this + // algorithm to work. + + unsigned const c_argumentsSize = CompilerUtils::sizeOnStack(_function.parameters()); + unsigned const c_returnValuesSize = CompilerUtils::sizeOnStack(_function.returnParameters()); + unsigned const c_localVariablesSize = CompilerUtils::sizeOnStack(_function.localVariables()); + + vector<int> stackLayout; + stackLayout.push_back(c_returnValuesSize); // target of return address + stackLayout += vector<int>(c_argumentsSize, -1); // discard all arguments + for (unsigned i = 0; i < c_returnValuesSize; ++i) + stackLayout.push_back(i); + stackLayout += vector<int>(c_localVariablesSize, -1); + + solAssert(stackLayout.size() <= 17, "Stack too deep, try removing local variables."); + while (stackLayout.back() != int(stackLayout.size() - 1)) + if (stackLayout.back() < 0) + { + m_context << Instruction::POP; + stackLayout.pop_back(); + } + else + { + m_context << swapInstruction(stackLayout.size() - stackLayout.back() - 1); + swap(stackLayout[stackLayout.back()], stackLayout.back()); + } + //@todo assert that everything is in place now + + for (ASTPointer<VariableDeclaration const> const& variable: _function.parameters() + _function.returnParameters()) + m_context.removeVariable(*variable); + for (VariableDeclaration const* localVariable: _function.localVariables()) + m_context.removeVariable(*localVariable); + + m_context.adjustStackOffset(-(int)c_returnValuesSize); + + if (!_function.isConstructor()) + m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction); + return false; +} + +bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) +{ + ErrorList errors; + assembly::CodeGenerator codeGen(_inlineAssembly.operations(), errors); + unsigned startStackHeight = m_context.stackHeight(); + codeGen.assemble( + m_context.nonConstAssembly(), + [&](assembly::Identifier const& _identifier, eth::Assembly& _assembly, assembly::CodeGenerator::IdentifierContext _context) { + auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier); + if (ref == _inlineAssembly.annotation().externalReferences.end()) + return false; + Declaration const* decl = ref->second; + solAssert(!!decl, ""); + if (_context == assembly::CodeGenerator::IdentifierContext::RValue) + { + solAssert(!!decl->type(), "Type of declaration required but not yet determined."); + if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(decl)) + _assembly.append(m_context.virtualFunctionEntryLabel(*functionDef).pushTag()); + else if (auto variable = dynamic_cast<VariableDeclaration const*>(decl)) + { + solAssert(!variable->isConstant(), ""); + if (m_context.isLocalVariable(variable)) + { + int stackDiff = _assembly.deposit() - m_context.baseStackOffsetOfVariable(*variable); + if (stackDiff < 1 || stackDiff > 16) + BOOST_THROW_EXCEPTION( + CompilerError() << + errinfo_comment("Stack too deep, try removing local variables.") + ); + for (unsigned i = 0; i < variable->type()->sizeOnStack(); ++i) + _assembly.append(dupInstruction(stackDiff)); + } + else + { + solAssert(m_context.isStateVariable(variable), "Invalid variable type."); + auto const& location = m_context.storageLocationOfVariable(*variable); + if (!variable->type()->isValueType()) + { + solAssert(location.second == 0, "Intra-slot offest assumed to be zero."); + _assembly.append(location.first); + } + else + { + _assembly.append(location.first); + _assembly.append(u256(location.second)); + } + } + } + else if (auto contract = dynamic_cast<ContractDefinition const*>(decl)) + { + solAssert(contract->isLibrary(), ""); + _assembly.appendLibraryAddress(contract->name()); + } + else + solAssert(false, "Invalid declaration type."); + } else { + // lvalue context + auto variable = dynamic_cast<VariableDeclaration const*>(decl); + solAssert( + !!variable || !m_context.isLocalVariable(variable), + "Can only assign to stack variables in inline assembly." + ); + unsigned size = variable->type()->sizeOnStack(); + int stackDiff = _assembly.deposit() - m_context.baseStackOffsetOfVariable(*variable) - size; + if (stackDiff > 16 || stackDiff < 1) + BOOST_THROW_EXCEPTION( + CompilerError() << + errinfo_comment("Stack too deep, try removing local variables.") + ); + for (unsigned i = 0; i < size; ++i) { + _assembly.append(swapInstruction(stackDiff)); + _assembly.append(Instruction::POP); + } + } + return true; + } + ); + solAssert(errors.empty(), "Code generation for inline assembly with errors requested."); + m_context.setStackOffset(startStackHeight); + return false; +} + +bool ContractCompiler::visit(IfStatement const& _ifStatement) +{ + StackHeightChecker checker(m_context); + CompilerContext::LocationSetter locationSetter(m_context, _ifStatement); + compileExpression(_ifStatement.condition()); + m_context << Instruction::ISZERO; + eth::AssemblyItem falseTag = m_context.appendConditionalJump(); + eth::AssemblyItem endTag = falseTag; + _ifStatement.trueStatement().accept(*this); + if (_ifStatement.falseStatement()) + { + endTag = m_context.appendJumpToNew(); + m_context << falseTag; + _ifStatement.falseStatement()->accept(*this); + } + m_context << endTag; + + checker.check(); + return false; +} + +bool ContractCompiler::visit(WhileStatement const& _whileStatement) +{ + StackHeightChecker checker(m_context); + CompilerContext::LocationSetter locationSetter(m_context, _whileStatement); + eth::AssemblyItem loopStart = m_context.newTag(); + eth::AssemblyItem loopEnd = m_context.newTag(); + m_continueTags.push_back(loopStart); + m_breakTags.push_back(loopEnd); + + m_context << loopStart; + compileExpression(_whileStatement.condition()); + m_context << Instruction::ISZERO; + m_context.appendConditionalJumpTo(loopEnd); + + _whileStatement.body().accept(*this); + + m_context.appendJumpTo(loopStart); + m_context << loopEnd; + + m_continueTags.pop_back(); + m_breakTags.pop_back(); + + checker.check(); + return false; +} + +bool ContractCompiler::visit(ForStatement const& _forStatement) +{ + StackHeightChecker checker(m_context); + CompilerContext::LocationSetter locationSetter(m_context, _forStatement); + eth::AssemblyItem loopStart = m_context.newTag(); + eth::AssemblyItem loopEnd = m_context.newTag(); + eth::AssemblyItem loopNext = m_context.newTag(); + m_continueTags.push_back(loopNext); + m_breakTags.push_back(loopEnd); + + if (_forStatement.initializationExpression()) + _forStatement.initializationExpression()->accept(*this); + + m_context << loopStart; + + // if there is no terminating condition in for, default is to always be true + if (_forStatement.condition()) + { + compileExpression(*_forStatement.condition()); + m_context << Instruction::ISZERO; + m_context.appendConditionalJumpTo(loopEnd); + } + + _forStatement.body().accept(*this); + + m_context << loopNext; + + // for's loop expression if existing + if (_forStatement.loopExpression()) + _forStatement.loopExpression()->accept(*this); + + m_context.appendJumpTo(loopStart); + m_context << loopEnd; + + m_continueTags.pop_back(); + m_breakTags.pop_back(); + + checker.check(); + return false; +} + +bool ContractCompiler::visit(Continue const& _continueStatement) +{ + CompilerContext::LocationSetter locationSetter(m_context, _continueStatement); + if (!m_continueTags.empty()) + m_context.appendJumpTo(m_continueTags.back()); + return false; +} + +bool ContractCompiler::visit(Break const& _breakStatement) +{ + CompilerContext::LocationSetter locationSetter(m_context, _breakStatement); + if (!m_breakTags.empty()) + m_context.appendJumpTo(m_breakTags.back()); + return false; +} + +bool ContractCompiler::visit(Return const& _return) +{ + CompilerContext::LocationSetter locationSetter(m_context, _return); + if (Expression const* expression = _return.expression()) + { + solAssert(_return.annotation().functionReturnParameters, "Invalid return parameters pointer."); + vector<ASTPointer<VariableDeclaration>> const& returnParameters = + _return.annotation().functionReturnParameters->parameters(); + TypePointers types; + for (auto const& retVariable: returnParameters) + types.push_back(retVariable->annotation().type); + + TypePointer expectedType; + if (expression->annotation().type->category() == Type::Category::Tuple || types.size() != 1) + expectedType = make_shared<TupleType>(types); + else + expectedType = types.front(); + compileExpression(*expression, expectedType); + + for (auto const& retVariable: boost::adaptors::reverse(returnParameters)) + CompilerUtils(m_context).moveToStackVariable(*retVariable); + } + for (unsigned i = 0; i < m_stackCleanupForReturn; ++i) + m_context << Instruction::POP; + m_context.appendJumpTo(m_returnTag); + m_context.adjustStackOffset(m_stackCleanupForReturn); + return false; +} + +bool ContractCompiler::visit(Throw const& _throw) +{ + CompilerContext::LocationSetter locationSetter(m_context, _throw); + m_context.appendJumpTo(m_context.errorTag()); + return false; +} + +bool ContractCompiler::visit(VariableDeclarationStatement const& _variableDeclarationStatement) +{ + StackHeightChecker checker(m_context); + CompilerContext::LocationSetter locationSetter(m_context, _variableDeclarationStatement); + if (Expression const* expression = _variableDeclarationStatement.initialValue()) + { + CompilerUtils utils(m_context); + compileExpression(*expression); + TypePointers valueTypes; + if (auto tupleType = dynamic_cast<TupleType const*>(expression->annotation().type.get())) + valueTypes = tupleType->components(); + else + valueTypes = TypePointers{expression->annotation().type}; + auto const& assignments = _variableDeclarationStatement.annotation().assignments; + solAssert(assignments.size() == valueTypes.size(), ""); + for (size_t i = 0; i < assignments.size(); ++i) + { + size_t j = assignments.size() - i - 1; + solAssert(!!valueTypes[j], ""); + VariableDeclaration const* varDecl = assignments[j]; + if (!varDecl) + utils.popStackElement(*valueTypes[j]); + else + { + utils.convertType(*valueTypes[j], *varDecl->annotation().type); + utils.moveToStackVariable(*varDecl); + } + } + } + checker.check(); + return false; +} + +bool ContractCompiler::visit(ExpressionStatement const& _expressionStatement) +{ + StackHeightChecker checker(m_context); + CompilerContext::LocationSetter locationSetter(m_context, _expressionStatement); + Expression const& expression = _expressionStatement.expression(); + compileExpression(expression); + CompilerUtils(m_context).popStackElement(*expression.annotation().type); + checker.check(); + return false; +} + +bool ContractCompiler::visit(PlaceholderStatement const& _placeholderStatement) +{ + StackHeightChecker checker(m_context); + CompilerContext::LocationSetter locationSetter(m_context, _placeholderStatement); + ++m_modifierDepth; + appendModifierOrFunctionCode(); + --m_modifierDepth; + checker.check(); + return true; +} + +void ContractCompiler::appendMissingFunctions() +{ + while (Declaration const* function = m_context.nextFunctionToCompile()) + { + m_context.setStackOffset(0); + function->accept(*this); + solAssert(m_context.nextFunctionToCompile() != function, "Compiled the wrong function?"); + } +} + +void ContractCompiler::appendModifierOrFunctionCode() +{ + solAssert(m_currentFunction, ""); + if (m_modifierDepth >= m_currentFunction->modifiers().size()) + { + solAssert(m_currentFunction->isImplemented(), ""); + m_currentFunction->body().accept(*this); + } + else + { + ASTPointer<ModifierInvocation> const& modifierInvocation = m_currentFunction->modifiers()[m_modifierDepth]; + + // constructor call should be excluded + if (dynamic_cast<ContractDefinition const*>(modifierInvocation->name()->annotation().referencedDeclaration)) + { + ++m_modifierDepth; + appendModifierOrFunctionCode(); + --m_modifierDepth; + return; + } + + ModifierDefinition const& modifier = m_context.functionModifier(modifierInvocation->name()->name()); + CompilerContext::LocationSetter locationSetter(m_context, modifier); + solAssert(modifier.parameters().size() == modifierInvocation->arguments().size(), ""); + for (unsigned i = 0; i < modifier.parameters().size(); ++i) + { + m_context.addVariable(*modifier.parameters()[i]); + compileExpression( + *modifierInvocation->arguments()[i], + modifier.parameters()[i]->annotation().type + ); + } + for (VariableDeclaration const* localVariable: modifier.localVariables()) + appendStackVariableInitialisation(*localVariable); + + unsigned const c_stackSurplus = CompilerUtils::sizeOnStack(modifier.parameters()) + + CompilerUtils::sizeOnStack(modifier.localVariables()); + m_stackCleanupForReturn += c_stackSurplus; + + modifier.body().accept(*this); + + for (unsigned i = 0; i < c_stackSurplus; ++i) + m_context << Instruction::POP; + m_stackCleanupForReturn -= c_stackSurplus; + } +} + +void ContractCompiler::appendStackVariableInitialisation(VariableDeclaration const& _variable) +{ + CompilerContext::LocationSetter location(m_context, _variable); + m_context.addVariable(_variable); + CompilerUtils(m_context).pushZeroValue(*_variable.annotation().type); +} + +void ContractCompiler::compileExpression(Expression const& _expression, TypePointer const& _targetType) +{ + ExpressionCompiler expressionCompiler(m_context, m_optimise); + expressionCompiler.compile(_expression); + if (_targetType) + CompilerUtils(m_context).convertType(*_expression.annotation().type, *_targetType); +} + +eth::Assembly ContractCompiler::cloneRuntime() +{ + eth::Assembly a; + a << Instruction::CALLDATASIZE; + a << u256(0) << Instruction::DUP1 << Instruction::CALLDATACOPY; + //@todo adjust for larger return values, make this dynamic. + a << u256(0x20) << u256(0) << Instruction::CALLDATASIZE; + a << u256(0); + // this is the address which has to be substituted by the linker. + //@todo implement as special "marker" AssemblyItem. + a << u256("0xcafecafecafecafecafecafecafecafecafecafe"); + a << u256(eth::GasCosts::callGas + 10) << Instruction::GAS << Instruction::SUB; + a << Instruction::DELEGATECALL; + //Propagate error condition (if DELEGATECALL pushes 0 on stack). + a << Instruction::ISZERO; + a.appendJumpI(a.errorTag()); + //@todo adjust for larger return values, make this dynamic. + a << u256(0x20) << u256(0) << Instruction::RETURN; + return a; +} diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h new file mode 100644 index 00000000..d1517e88 --- /dev/null +++ b/libsolidity/codegen/ContractCompiler.h @@ -0,0 +1,134 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2014 + * Code generator for contracts. + */ + +#pragma once + +#include <ostream> +#include <functional> +#include <libsolidity/ast/ASTVisitor.h> +#include <libsolidity/codegen/CompilerContext.h> +#include <libevmasm/Assembly.h> + +namespace dev { +namespace solidity { + +/** + * Code generator at the contract level. Can be used to generate code for exactly one contract + * either either in "runtime mode" or "creation mode". + */ +class ContractCompiler: private ASTConstVisitor +{ +public: + explicit ContractCompiler(CompilerContext& _context, bool _optimise): + m_optimise(_optimise), + m_context(_context), + m_returnTag(eth::Tag, u256(-1)) + { + m_context = CompilerContext(); + m_returnTag = m_context.newTag(); + } + + void compileContract( + ContractDefinition const& _contract, + std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts + ); + /// Compiles the constructor part of the contract. + /// @returns the identifier of the runtime sub-assembly. + size_t compileConstructor( + CompilerContext const& _runtimeContext, + ContractDefinition const& _contract, + std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts + ); + /// Compiles a contract that uses DELEGATECALL to call into a pre-deployed version of the given + /// contract at runtime, but contains the full creation-time code. + /// @returns the identifier of the runtime sub-assembly. + size_t compileClone( + ContractDefinition const& _contract, + std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts + ); + +private: + /// Registers the non-function objects inside the contract with the context and stores the basic + /// information about the contract like the AST annotations. + void initializeContext( + ContractDefinition const& _contract, + std::map<ContractDefinition const*, eth::Assembly const*> const& _compiledContracts + ); + /// Adds the code that is run at creation time. Should be run after exchanging the run-time context + /// with a new and initialized context. Adds the constructor code. + /// @returns the identifier of the runtime sub assembly + size_t packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext); + /// Appends state variable initialisation and constructor code. + void appendInitAndConstructorCode(ContractDefinition const& _contract); + void appendBaseConstructor(FunctionDefinition const& _constructor); + void appendConstructor(FunctionDefinition const& _constructor); + void appendFunctionSelector(ContractDefinition const& _contract); + /// Creates code that unpacks the arguments for the given function represented by a vector of TypePointers. + /// From memory if @a _fromMemory is true, otherwise from call data. + /// Expects source offset on the stack, which is removed. + void appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory = false); + void appendReturnValuePacker(TypePointers const& _typeParameters, bool _isLibrary); + + void registerStateVariables(ContractDefinition const& _contract); + void initializeStateVariables(ContractDefinition const& _contract); + + virtual bool visit(VariableDeclaration const& _variableDeclaration) override; + virtual bool visit(FunctionDefinition const& _function) override; + virtual bool visit(InlineAssembly const& _inlineAssembly) override; + virtual bool visit(IfStatement const& _ifStatement) override; + virtual bool visit(WhileStatement const& _whileStatement) override; + virtual bool visit(ForStatement const& _forStatement) override; + virtual bool visit(Continue const& _continue) override; + virtual bool visit(Break const& _break) override; + virtual bool visit(Return const& _return) override; + virtual bool visit(Throw const& _throw) override; + virtual bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override; + virtual bool visit(ExpressionStatement const& _expressionStatement) override; + virtual bool visit(PlaceholderStatement const&) override; + + /// Repeatedly visits all function which are referenced but which are not compiled yet. + void appendMissingFunctions(); + + /// Appends one layer of function modifier code of the current function, or the function + /// body itself if the last modifier was reached. + void appendModifierOrFunctionCode(); + + void appendStackVariableInitialisation(VariableDeclaration const& _variable); + void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer()); + + /// @returns the runtime assembly for clone contracts. + static eth::Assembly cloneRuntime(); + + bool const m_optimise; + CompilerContext& m_context; + std::vector<eth::AssemblyItem> m_breakTags; ///< tag to jump to for a "break" statement + std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" statement + eth::AssemblyItem m_returnTag; ///< tag to jump to for a "return" statement + unsigned m_modifierDepth = 0; + FunctionDefinition const* m_currentFunction = nullptr; + unsigned m_stackCleanupForReturn = 0; ///< this number of stack elements need to be removed before jump to m_returnTag + // arguments for base constructors, filled in derived-to-base order + std::map<FunctionDefinition const*, std::vector<ASTPointer<Expression>> const*> m_baseArguments; +}; + +} +} diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index ab02e0b5..b973a117 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -281,11 +281,7 @@ bool ExpressionCompiler::visit(TupleExpression const& _tuple) bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) { CompilerContext::LocationSetter locationSetter(m_context, _unaryOperation); - //@todo type checking and creating code for an operator should be in the same place: - // the operator should know how to convert itself and to which types it applies, so - // put this code together with "Type::acceptsBinary/UnaryOperator" into a class that - // represents the operator - if (_unaryOperation.annotation().type->category() == Type::Category::IntegerConstant) + if (_unaryOperation.annotation().type->category() == Type::Category::RationalNumber) { m_context << _unaryOperation.annotation().type->literalValue(nullptr); return false; @@ -360,17 +356,20 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) if (c_op == Token::And || c_op == Token::Or) // special case: short-circuiting appendAndOrOperatorCode(_binaryOperation); - else if (commonType.category() == Type::Category::IntegerConstant) + else if (commonType.category() == Type::Category::RationalNumber) m_context << commonType.literalValue(nullptr); else { - bool cleanupNeeded = commonType.category() == Type::Category::Integer && - (Token::isCompareOp(c_op) || c_op == Token::Div || c_op == Token::Mod); + bool cleanupNeeded = false; + if (Token::isCompareOp(c_op)) + cleanupNeeded = true; + if (commonType.category() == Type::Category::Integer && (c_op == Token::Div || c_op == Token::Mod)) + cleanupNeeded = true; // for commutative operators, push the literal as late as possible to allow improved optimization auto isLiteral = [](Expression const& _e) { - return dynamic_cast<Literal const*>(&_e) || _e.annotation().type->category() == Type::Category::IntegerConstant; + return dynamic_cast<Literal const*>(&_e) || _e.annotation().type->category() == Type::Category::RationalNumber; }; bool swap = m_optimize && Token::isCommutativeOp(c_op) && isLiteral(rightExpression) && !isLiteral(leftExpression); if (swap) @@ -1225,7 +1224,7 @@ void ExpressionCompiler::endVisit(Literal const& _literal) switch (type->category()) { - case Type::Category::IntegerConstant: + case Type::Category::RationalNumber: case Type::Category::Bool: m_context << type->literalValue(&_literal); break; @@ -1306,6 +1305,9 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Ty IntegerType const& type = dynamic_cast<IntegerType const&>(_type); bool const c_isSigned = type.isSigned(); + if (_type.category() == Type::Category::FixedPoint) + solAssert(false, "Not yet implemented - FixedPointType."); + switch (_operator) { case Token::Add: @@ -1358,6 +1360,8 @@ void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator) break; case Token::SAR: break; + case Token::SHR: + break; default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown shift operator.")); } diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp index fcadd2ff..ea8bc1ba 100644 --- a/libsolidity/codegen/LValue.cpp +++ b/libsolidity/codegen/LValue.cpp @@ -179,6 +179,9 @@ void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const m_context << Instruction::SWAP1 << Instruction::SLOAD << Instruction::SWAP1 << u256(0x100) << Instruction::EXP << Instruction::SWAP1 << Instruction::DIV; + if (m_dataType->category() == Type::Category::FixedPoint) + // implementation should be very similar to the integer case. + solAssert(false, "Not yet implemented - FixedPointType."); if (m_dataType->category() == Type::Category::FixedBytes) m_context << (u256(0x1) << (256 - 8 * m_dataType->storageBytes())) << Instruction::MUL; else if ( @@ -239,6 +242,9 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc << Instruction::DUP2 << Instruction::MUL << Instruction::DIV; + else if (m_dataType->category() == Type::Category::FixedPoint) + // implementation should be very similar to the integer case. + solAssert(false, "Not yet implemented - FixedPointType."); m_context << Instruction::MUL << Instruction::OR; // stack: value storage_ref updated_value m_context << Instruction::SWAP1 << Instruction::SSTORE; diff --git a/libsolidity/formal/Why3Translator.cpp b/libsolidity/formal/Why3Translator.cpp index 24fbab13..c794cb24 100644 --- a/libsolidity/formal/Why3Translator.cpp +++ b/libsolidity/formal/Why3Translator.cpp @@ -428,8 +428,11 @@ bool Why3Translator::visit(BinaryOperation const& _binaryOperation) Type const& commonType = *_binaryOperation.annotation().commonType; Token::Value const c_op = _binaryOperation.getOperator(); - if (commonType.category() == Type::Category::IntegerConstant) + if (commonType.category() == Type::Category::RationalNumber) { + auto const& constantNumber = dynamic_cast<RationalNumberType const&>(commonType); + if (constantNumber.isFractional()) + error(_binaryOperation, "Fractional numbers not supported."); add("(of_int " + toString(commonType.literalValue(nullptr)) + ")"); return false; } @@ -589,9 +592,14 @@ bool Why3Translator::visit(Literal const& _literal) else add("true"); break; - case Type::Category::IntegerConstant: + case Type::Category::RationalNumber: + { + auto const& constantNumber = dynamic_cast<RationalNumberType const&>(*type); + if (constantNumber.isFractional()) + error(_literal, "Fractional numbers not supported."); add("(of_int " + toString(type->literalValue(&_literal)) + ")"); break; + } default: error(_literal, "Not supported."); } diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libsolidity/inlineasm/AsmCodeGen.cpp index d571ce0d..53d19b0a 100644 --- a/libsolidity/inlineasm/AsmCodeGen.cpp +++ b/libsolidity/inlineasm/AsmCodeGen.cpp @@ -36,7 +36,8 @@ using namespace dev::solidity::assembly; struct GeneratorState { - explicit GeneratorState(ErrorList& _errors): errors(_errors) {} + GeneratorState(ErrorList& _errors, eth::Assembly& _assembly): + errors(_errors), assembly(_assembly) {} void addError(Error::Type _type, std::string const& _description, SourceLocation const& _location = SourceLocation()) { @@ -66,10 +67,10 @@ struct GeneratorState return label != labels.end() ? &label->second : nullptr; } - eth::Assembly assembly; map<string, eth::AssemblyItem> labels; vector<pair<string, int>> variables; ///< name plus stack height ErrorList& errors; + eth::Assembly& assembly; }; /** @@ -267,7 +268,8 @@ private: bool assembly::CodeGenerator::typeCheck(assembly::CodeGenerator::IdentifierAccess const& _identifierAccess) { size_t initialErrorLen = m_errors.size(); - GeneratorState state(m_errors); + eth::Assembly assembly; + GeneratorState state(m_errors, assembly); (LabelOrganizer(state))(m_parsedData); (CodeTransform(state, _identifierAccess))(m_parsedData); return m_errors.size() == initialErrorLen; @@ -275,9 +277,17 @@ bool assembly::CodeGenerator::typeCheck(assembly::CodeGenerator::IdentifierAcces eth::Assembly assembly::CodeGenerator::assemble(assembly::CodeGenerator::IdentifierAccess const& _identifierAccess) { - GeneratorState state(m_errors); + eth::Assembly assembly; + GeneratorState state(m_errors, assembly); + (LabelOrganizer(state))(m_parsedData); + (CodeTransform(state, _identifierAccess))(m_parsedData); + return assembly; +} + +void assembly::CodeGenerator::assemble(eth::Assembly& _assembly, assembly::CodeGenerator::IdentifierAccess const& _identifierAccess) +{ + GeneratorState state(m_errors, _assembly); (LabelOrganizer(state))(m_parsedData); (CodeTransform(state, _identifierAccess))(m_parsedData); - return state.assembly; } diff --git a/libsolidity/inlineasm/AsmCodeGen.h b/libsolidity/inlineasm/AsmCodeGen.h index aaabda45..b1fafe15 100644 --- a/libsolidity/inlineasm/AsmCodeGen.h +++ b/libsolidity/inlineasm/AsmCodeGen.h @@ -55,6 +55,8 @@ public: bool typeCheck(IdentifierAccess const& _identifierAccess = IdentifierAccess()); /// Performs code generation and @returns the result. eth::Assembly assemble(IdentifierAccess const& _identifierAccess = IdentifierAccess()); + /// Performs code generation and appends generated to to _assembly. + void assemble(eth::Assembly& _assembly, IdentifierAccess const& _identifierAccess = IdentifierAccess()); private: Block const& m_parsedData; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 83459183..c28e926b 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -63,6 +63,24 @@ CompilerStack::CompilerStack(bool _addStandardSources, ReadFileCallback const& _ addSources(StandardSources, true); // add them as libraries } +void CompilerStack::setRemappings(vector<string> const& _remappings) +{ + vector<Remapping> remappings; + for (auto const& remapping: _remappings) + { + auto eq = find(remapping.begin(), remapping.end(), '='); + if (eq == remapping.end()) + continue; // ignore + auto colon = find(remapping.begin(), eq, ':'); + Remapping r; + r.context = colon == eq ? string() : string(remapping.begin(), colon); + r.prefix = colon == eq ? string(remapping.begin(), eq) : string(colon + 1, eq); + r.target = string(eq + 1, remapping.end()); + remappings.push_back(r); + } + swap(m_remappings, remappings); +} + void CompilerStack::reset(bool _keepSources, bool _addStandardSources) { m_parseSuccessful = false; @@ -384,37 +402,72 @@ tuple<int, int, int, int> CompilerStack::positionFromSourceLocation(SourceLocati return make_tuple(++startLine, ++startColumn, ++endLine, ++endColumn); } -StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string const& _path) +StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string const& _sourcePath) { StringMap newSources; for (auto const& node: _ast.nodes()) if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get())) { - string path = absolutePath(import->path(), _path); - import->annotation().absolutePath = path; - if (m_sources.count(path) || newSources.count(path)) + string importPath = absolutePath(import->path(), _sourcePath); + // The current value of `path` is the absolute path as seen from this source file. + // We first have to apply remappings before we can store the actual absolute path + // as seen globally. + importPath = applyRemapping(importPath, _sourcePath); + import->annotation().absolutePath = importPath; + if (m_sources.count(importPath) || newSources.count(importPath)) continue; - string contents; - string errorMessage; - if (!m_readFile) - errorMessage = "File not supplied initially."; + + ReadFileResult result{false, string("File not supplied initially.")}; + if (m_readFile) + result = m_readFile(importPath); + + if (result.success) + newSources[importPath] = result.contentsOrErrorMesage; else - tie(contents, errorMessage) = m_readFile(path); - if (!errorMessage.empty()) { auto err = make_shared<Error>(Error::Type::ParserError); *err << errinfo_sourceLocation(import->location()) << - errinfo_comment("Source not found: " + errorMessage); + errinfo_comment("Source \"" + importPath + "\" not found: " + result.contentsOrErrorMesage); m_errors.push_back(std::move(err)); continue; } - else - newSources[path] = contents; } return newSources; } +string CompilerStack::applyRemapping(string const& _path, string const& _context) +{ + // Try to find the longest prefix match in all remappings that are active in the current context. + auto isPrefixOf = [](string const& _a, string const& _b) + { + if (_a.length() > _b.length()) + return false; + return std::equal(_a.begin(), _a.end(), _b.begin()); + }; + + size_t longestPrefix = 0; + string longestPrefixTarget; + for (auto const& redir: m_remappings) + { + // Skip if we already have a closer match. + if (longestPrefix > 0 && redir.prefix.length() <= longestPrefix) + continue; + // Skip if redir.context is not a prefix of _context + if (!isPrefixOf(redir.context, _context)) + continue; + // Skip if the prefix does not match. + if (!isPrefixOf(redir.prefix, _path)) + continue; + + longestPrefix = redir.prefix.length(); + longestPrefixTarget = redir.target; + } + string path = longestPrefixTarget; + path.append(_path.begin() + longestPrefix, _path.end()); + return path; +} + void CompilerStack::resolveImports() { // topological sorting (depth first search) of the import graph, cutting potential cycles diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index c7f98184..e111c982 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -75,15 +75,23 @@ enum class DocumentationType: uint8_t class CompilerStack: boost::noncopyable { public: - /// File reading callback, should return a pair of content and error message (exactly one nonempty) - /// for a given path. - using ReadFileCallback = std::function<std::pair<std::string, std::string>(std::string const&)>; + struct ReadFileResult + { + bool success; + std::string contentsOrErrorMesage; + }; + + /// File reading callback. + using ReadFileCallback = std::function<ReadFileResult(std::string const&)>; /// Creates a new compiler stack. /// @param _readFile callback to used to read files for import statements. Should return /// @param _addStandardSources Adds standard sources if @a _addStandardSources. explicit CompilerStack(bool _addStandardSources = true, ReadFileCallback const& _readFile = ReadFileCallback()); + /// Sets path remappings in the format "context:prefix=target" + void setRemappings(std::vector<std::string> const& _remappings); + /// Resets the compiler to a state where the sources are not parsed or even removed. void reset(bool _keepSources = false, bool _addStandardSources = true); @@ -209,6 +217,7 @@ private: /// @a m_readFile and stores the absolute paths of all imports in the AST annotations. /// @returns the newly loaded sources. StringMap loadMissingSources(SourceUnit const& _ast, std::string const& _path); + std::string applyRemapping(std::string const& _path, std::string const& _context); void resolveImports(); /// Checks whether there are libraries with the same name, reports that as an error and /// @returns false in this case. @@ -226,7 +235,17 @@ private: Contract const& contract(std::string const& _contractName = "") const; Source const& source(std::string const& _sourceName = "") const; + struct Remapping + { + std::string context; + std::string prefix; + std::string target; + }; + ReadFileCallback m_readFile; + /// list of path prefix remappings, e.g. mylibrary: github.com/ethereum = /usr/local/ethereum + /// "context:prefix=target" + std::vector<Remapping> m_remappings; bool m_parseSuccessful; std::map<std::string const, Source> m_sources; std::shared_ptr<GlobalContext> m_globalContext; diff --git a/libsolidity/parsing/Token.cpp b/libsolidity/parsing/Token.cpp index c73368e5..0ab97988 100644 --- a/libsolidity/parsing/Token.cpp +++ b/libsolidity/parsing/Token.cpp @@ -109,6 +109,7 @@ char const Token::m_tokenType[] = { TOKEN_LIST(KT, KK) }; + int Token::parseSize(string::const_iterator _begin, string::const_iterator _end) { try @@ -121,6 +122,7 @@ int Token::parseSize(string::const_iterator _begin, string::const_iterator _end) return -1; } } + tuple<Token::Value, unsigned int, unsigned int> Token::fromIdentifierOrKeyword(string const& _literal) { auto positionM = find_if(_literal.begin(), _literal.end(), ::isdigit); @@ -156,7 +158,7 @@ tuple<Token::Value, unsigned int, unsigned int> Token::fromIdentifierOrKeyword(s int n = parseSize(positionX + 1, _literal.end()); if ( 0 <= m && m <= 256 && - 0 <= n && n <= 256 && + 8 <= n && n <= 256 && m + n > 0 && m + n <= 256 && m % 8 == 0 && @@ -171,6 +173,7 @@ tuple<Token::Value, unsigned int, unsigned int> Token::fromIdentifierOrKeyword(s } return make_tuple(Token::Identifier, 0, 0); } + return make_tuple(keywordByName(_literal), 0, 0); } Token::Value Token::keywordByName(string const& _name) diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index f28df8bf..703e88f6 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -275,7 +275,7 @@ public: return Value(op + (BitOr - AssignBitOr)); } - static bool isBitOp(Value op) { return (BitOr <= op && op <= SHR) || op == BitNot; } + static bool isBitOp(Value op) { return (BitOr <= op && op <= BitAnd) || op == BitNot; } static bool isBooleanOp(Value op) { return (Or <= op && op <= And) || op == Not; } static bool isUnaryOp(Value op) { return (Not <= op && op <= Delete) || op == Add || op == Sub || op == After; } static bool isCountOp(Value op) { return op == Inc || op == Dec; } diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 003ad030..17ab5ba4 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -22,6 +22,13 @@ */ #include "CommandLineInterface.h" +#ifdef _WIN32 // windows + #include <io.h> + #define isatty _isatty + #define fileno _fileno +#else // unix + #include <unistd.h> +#endif #include <string> #include <iostream> #include <fstream> @@ -311,36 +318,31 @@ void CommandLineInterface::readInputFilesAndConfigureRemappings() } } else - for (string const& infile: m_args["input-file"].as<vector<string>>()) + for (string path: m_args["input-file"].as<vector<string>>()) { - auto eq = find(infile.begin(), infile.end(), '='); - if (eq != infile.end()) - { - string target(eq + 1, infile.end()); - m_remappings.push_back(make_pair(string(infile.begin(), eq), target)); - m_allowedDirectories.push_back(boost::filesystem::path(target).remove_filename()); - } + auto eq = find(path.begin(), path.end(), '='); + if (eq != path.end()) + path = string(eq + 1, path.end()); else { - auto path = boost::filesystem::path(infile); - if (!boost::filesystem::exists(path)) + auto infile = boost::filesystem::path(path); + if (!boost::filesystem::exists(infile)) { cerr << "Skipping non existant input file \"" << infile << "\"" << endl; continue; } - if (!boost::filesystem::is_regular_file(path)) + if (!boost::filesystem::is_regular_file(infile)) { cerr << "\"" << infile << "\" is not a valid file. Skipping" << endl; continue; } - m_sourceCodes[path.string()] = dev::contentsString(path.string()); - m_allowedDirectories.push_back(boost::filesystem::canonical(path).remove_filename()); + m_sourceCodes[infile.string()] = dev::contentsString(infile.string()); + path = boost::filesystem::canonical(infile).string(); } + m_allowedDirectories.push_back(boost::filesystem::path(path).remove_filename()); } - // Add empty remapping to try the path itself. - m_remappings.push_back(make_pair(string(), string())); } bool CommandLineInterface::parseLibraryOption(string const& _input) @@ -478,7 +480,7 @@ Allowed options)", return false; } - if (m_args.count("help")) + if (m_args.count("help") || (isatty(fileno(stdin)) && _argc == 1)) { cout << desc; return false; @@ -527,67 +529,43 @@ bool CommandLineInterface::processInput() return link(); } - function<pair<string,string>(string const&)> fileReader = [this](string const& _path) + CompilerStack::ReadFileCallback fileReader = [this](string const& _path) { - // Try to find the longest prefix match in all remappings. At the end, there will bean - // empty remapping so that we also try the path itself, but any file should be either - // in (a subdirectory of) the directory of an explicit source or a remapping target. - int errorLevel = 0; - size_t longestPrefix = 0; - string bestMatchPath; - for (auto const& redir: m_remappings) + auto boostPath = boost::filesystem::path(_path); + if (!boost::filesystem::exists(boostPath)) + return CompilerStack::ReadFileResult{false, "File not found."}; + boostPath = boost::filesystem::canonical(boostPath); + bool isAllowed = false; + for (auto const& allowedDir: m_allowedDirectories) { - auto const& virt = redir.first; - if (longestPrefix > 0 && virt.length() <= longestPrefix) - continue; - if (virt.length() > _path.length() || !std::equal(virt.begin(), virt.end(), _path.begin())) - continue; - string path = redir.second; - path.append(_path.begin() + virt.length(), _path.end()); - auto boostPath = boost::filesystem::path(path); - if (!boost::filesystem::exists(boostPath)) + // If dir is a prefix of boostPath, we are fine. + if ( + std::distance(allowedDir.begin(), allowedDir.end()) <= std::distance(boostPath.begin(), boostPath.end()) && + std::equal(allowedDir.begin(), allowedDir.end(), boostPath.begin()) + ) { - errorLevel = max(errorLevel, 0); - continue; - } - boostPath = boost::filesystem::canonical(boostPath); - bool isAllowed = false; - for (auto const& dir: m_allowedDirectories) - { - // If dir is a prefix of boostPath, we are fine. - if ( - std::distance(dir.begin(), dir.end()) <= std::distance(boostPath.begin(), boostPath.end()) && - std::equal(dir.begin(), dir.end(), boostPath.begin()) - ) - { - isAllowed = true; - break; - } - } - if (!isAllowed) - errorLevel = max(errorLevel, 2); - else if (!boost::filesystem::is_regular_file(boostPath)) - errorLevel = max(errorLevel, 1); - else - { - longestPrefix = virt.length(); - bestMatchPath = path; + isAllowed = true; + break; } } - if (!bestMatchPath.empty()) - return make_pair(m_sourceCodes[bestMatchPath] = dev::contentsString(bestMatchPath), string()); - if (errorLevel == 0) - return make_pair(string(), string("File not found.")); - else if (errorLevel == 1) - return make_pair(string(), string("Not a valid file.")); + if (!isAllowed) + return CompilerStack::ReadFileResult{false, "File outside of allowed directories."}; + else if (!boost::filesystem::is_regular_file(boostPath)) + return CompilerStack::ReadFileResult{false, "Not a valid file."}; else - return make_pair(string(), string("File outside of allowed directories.")); + { + auto contents = dev::contentsString(boostPath.string()); + m_sourceCodes[boostPath.string()] = contents; + return CompilerStack::ReadFileResult{true, contents}; + } }; m_compiler.reset(new CompilerStack(m_args.count(g_argAddStandard) > 0, fileReader)); auto scannerFromSourceName = [&](string const& _sourceName) -> solidity::Scanner const& { return m_compiler->scanner(_sourceName); }; try { + if (m_args.count("input-file")) + m_compiler->setRemappings(m_args["input-file"].as<vector<string>>()); for (auto const& sourceCode: m_sourceCodes) m_compiler->addSource(sourceCode.first, sourceCode.second); // TODO: Perhaps we should not compile unless requested diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index 52854bac..e240fe96 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -85,8 +85,6 @@ private: boost::program_options::variables_map m_args; /// map of input files to source code strings std::map<std::string, std::string> m_sourceCodes; - /// list of path prefix remappings, e.g. github.com/ethereum -> /usr/local/ethereum - std::vector<std::pair<std::string, std::string>> m_remappings; /// list of allowed directories to read files from std::vector<boost::filesystem::path> m_allowedDirectories; /// map of library names to addresses diff --git a/solc/jsonCompiler.cpp b/solc/jsonCompiler.cpp index eaf83705..e8f674a0 100644 --- a/solc/jsonCompiler.cpp +++ b/solc/jsonCompiler.cpp @@ -132,26 +132,31 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback CompilerStack::ReadFileCallback readCallback; if (_readCallback) { - readCallback = [=](string const& _path) -> pair<string, string> + readCallback = [=](string const& _path) { char* contents_c = nullptr; char* error_c = nullptr; _readCallback(_path.c_str(), &contents_c, &error_c); - string contents; - string error; + CompilerStack::ReadFileResult result; + result.success = true; if (!contents_c && !error_c) - error = "File not found."; + { + result.success = false; + result.contentsOrErrorMesage = "File not found."; + } if (contents_c) { - contents = string(contents_c); + result.success = true; + result.contentsOrErrorMesage = string(contents_c); free(contents_c); } if (error_c) { - error = string(error_c); + result.success = false; + result.contentsOrErrorMesage = string(error_c); free(error_c); } - return make_pair(move(contents), move(error)); + return result; }; } CompilerStack compiler(true, readCallback); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9d958b16..ed53ce59 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -40,7 +40,7 @@ foreach(file ${SRC_LIST}) endforeach(test_raw) endforeach(file) -file(GLOB HEADERS "*.h") +file(GLOB HEADERS "*.h" "*/*.h") set(EXECUTABLE soltest) eth_simple_add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) diff --git a/test/libsolidity/Imports.cpp b/test/libsolidity/Imports.cpp index 94d3e423..0736a853 100644 --- a/test/libsolidity/Imports.cpp +++ b/test/libsolidity/Imports.cpp @@ -142,6 +142,28 @@ BOOST_AUTO_TEST_CASE(name_clash_in_import) BOOST_CHECK(c.compile()); } +BOOST_AUTO_TEST_CASE(remappings) +{ + CompilerStack c; + c.setRemappings(vector<string>{"s=s_1.4.6", "t=Tee"}); + c.addSource("a", "import \"s/s.sol\"; contract A is S {}"); + c.addSource("b", "import \"t/tee.sol\"; contract A is Tee {} "); + c.addSource("s_1.4.6/s.sol", "contract S {}"); + c.addSource("Tee/tee.sol", "contract Tee {}"); + BOOST_CHECK(c.compile()); +} + +BOOST_AUTO_TEST_CASE(context_dependent_remappings) +{ + CompilerStack c; + c.setRemappings(vector<string>{"a:s=s_1.4.6", "b:s=s_1.4.7"}); + c.addSource("a/a.sol", "import \"s/s.sol\"; contract A is SSix {}"); + c.addSource("b/b.sol", "import \"s/s.sol\"; contract B is SSeven {}"); + c.addSource("s_1.4.6/s.sol", "contract SSix {} "); + c.addSource("s_1.4.7/s.sol", "contract SSeven {} "); + BOOST_CHECK(c.compile()); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 9df64cdc..07bf6759 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -6595,6 +6595,25 @@ BOOST_AUTO_TEST_CASE(inline_assembly_jumps) BOOST_CHECK(callContractFunction("f()", u256(7)) == encodeArgs(u256(34))); } +BOOST_AUTO_TEST_CASE(inline_assembly_function_access) +{ + char const* sourceCode = R"( + contract C { + uint public x; + function g(uint y) { x = 2 * y; assembly { stop } } + function f(uint _x) { + assembly { + _x + jump(g) + } + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("f(uint256)", u256(5)) == encodeArgs()); + BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(10))); +} + BOOST_AUTO_TEST_CASE(index_access_with_type_conversion) { // Test for a bug where higher order bits cleanup was not done for array index access. @@ -6631,6 +6650,7 @@ BOOST_AUTO_TEST_CASE(delete_on_array_of_structs) // This code interprets x as an array length and thus will go out of gas. // neither of the two should throw due to out-of-bounds access BOOST_CHECK(callContractFunction("f()") == encodeArgs(true)); + } BOOST_AUTO_TEST_CASE(internal_library_function) @@ -6735,6 +6755,89 @@ BOOST_AUTO_TEST_CASE(internal_library_function_return_var_size) BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(2))); } +BOOST_AUTO_TEST_CASE(iszero_bnot_correct) +{ + // A long time ago, some opcodes were renamed, which involved the opcodes + // "iszero" and "not". + char const* sourceCode = R"( + contract C { + function f() returns (bool) { + bytes32 x = 1; + assembly { x := not(x) } + if (x != ~bytes32(1)) return false; + assembly { x := iszero(x) } + if (x != bytes32(0)) return false; + return true; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("f()") == encodeArgs(true)); +} + +BOOST_AUTO_TEST_CASE(cleanup_bytes_types) +{ + // Checks that bytesXX types are properly cleaned before they are compared. + char const* sourceCode = R"( + contract C { + function f(bytes2 a, uint16 x) returns (uint) { + if (a != "ab") return 1; + if (x != 0x0102) return 2; + if (bytes3(x) != 0x0102) return 3; + return 0; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + // We input longer data on purpose. + BOOST_CHECK(callContractFunction("f(bytes2,uint16)", string("abc"), u256(0x040102)) == encodeArgs(0)); +} + +BOOST_AUTO_TEST_CASE(skip_dynamic_types) +{ + // The EVM cannot provide access to dynamically-sized return values, so we have to skip them. + char const* sourceCode = R"( + contract C { + function f() returns (uint, uint[], uint) { + return (7, new uint[](2), 8); + } + function g() returns (uint, uint) { + // Previous implementation "moved" b to the second place and did not skip. + var (a, _, b) = this.f(); + return (a, b); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("g()") == encodeArgs(u256(7), u256(8))); +} + +BOOST_AUTO_TEST_CASE(skip_dynamic_types_for_structs) +{ + // For accessors, the dynamic types are already removed in the external signature itself. + char const* sourceCode = R"( + contract C { + struct S { + uint x; + string a; // this is present in the accessor + uint[] b; // this is not present + uint y; + } + S public s; + function g() returns (uint, uint) { + s.x = 2; + s.a = "abc"; + s.b = [7, 8, 9]; + s.y = 6; + var (x, a, y) = this.s(); + return (x, y); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("g()") == encodeArgs(u256(2), u256(6))); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 398ac24f..967b2907 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -267,10 +267,10 @@ BOOST_AUTO_TEST_CASE(comparison) "}\n"; bytes code = compileFirstExpression(sourceCode); - bytes expectation({byte(Instruction::PUSH1), 0x1, + bytes expectation({byte(Instruction::PUSH1), 0x1, byte(Instruction::ISZERO), byte(Instruction::ISZERO), byte(Instruction::PUSH2), 0x11, 0xaa, byte(Instruction::PUSH2), 0x10, 0xaa, - byte(Instruction::LT), + byte(Instruction::LT), byte(Instruction::ISZERO), byte(Instruction::ISZERO), byte(Instruction::EQ), byte(Instruction::ISZERO)}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); @@ -296,7 +296,8 @@ BOOST_AUTO_TEST_CASE(short_circuiting) byte(Instruction::EQ), byte(Instruction::ISZERO), // after this we have 9 != 2 byte(Instruction::JUMPDEST), - byte(Instruction::PUSH1), 0x1, + byte(Instruction::ISZERO), byte(Instruction::ISZERO), + byte(Instruction::PUSH1), 0x1, byte(Instruction::ISZERO), byte(Instruction::ISZERO), byte(Instruction::EQ), byte(Instruction::ISZERO)}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 9c411781..697b3fa9 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -97,13 +97,20 @@ parseAnalyseAndReturnError(string const& _source, bool _reportWarnings = false) return make_pair(sourceUnit, std::make_shared<Error::Type const>(currentError->type())); } } + catch (InternalCompilerError const& _e) + { + string message("Internal compiler error"); + if (string const* description = boost::get_error_info<errinfo_comment>(_e)) + message += ": " + *description; + BOOST_FAIL(message); + } catch (Error const& _e) { return make_pair(sourceUnit, std::make_shared<Error::Type const>(_e.type())); } - catch (Exception const& /*_exception*/) + catch (...) { - return make_pair(sourceUnit, nullptr); + BOOST_FAIL("Unexpected exception."); } return make_pair(sourceUnit, nullptr); } @@ -537,6 +544,21 @@ BOOST_AUTO_TEST_CASE(redeclare_implemented_abstract_function_as_abstract) BOOST_CHECK(expectError(text) == Error::Type::TypeError); } +BOOST_AUTO_TEST_CASE(implement_abstract_via_constructor) +{ + ASTPointer<SourceUnit> sourceUnit; + char const* text = R"( + contract base { function foo(); } + contract foo is base { function foo() {} } + )"; + ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name resolving failed"); + std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes(); + BOOST_CHECK_EQUAL(nodes.size(), 2); + ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[1].get()); + BOOST_CHECK(derived); + BOOST_CHECK(!derived->annotation().isFullyImplemented); +} + BOOST_AUTO_TEST_CASE(function_canonical_signature) { ASTPointer<SourceUnit> sourceUnit; @@ -1330,15 +1352,6 @@ BOOST_AUTO_TEST_CASE(overflow_caused_by_ether_units) BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError); } -BOOST_AUTO_TEST_CASE(exp_operator_negative_exponent) -{ - char const* sourceCode = R"( - contract test { - function f() returns(uint d) { return 2 ** -3; } - })"; - BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError); -} - BOOST_AUTO_TEST_CASE(exp_operator_exponent_too_big) { char const* sourceCode = R"( @@ -2156,6 +2169,8 @@ BOOST_AUTO_TEST_CASE(dynamic_return_types_not_possible) function f(uint) returns (string); function g() { var (x,) = this.f(2); + // we can assign to x but it is not usable. + bytes(x).length; } } )"; @@ -2229,18 +2244,6 @@ BOOST_AUTO_TEST_CASE(literal_strings) BOOST_CHECK(success(text)); } -BOOST_AUTO_TEST_CASE(invalid_integer_literal_fraction) -{ - char const* text = R"( - contract Foo { - function f() { - var x = 1.20; - } - } - )"; - BOOST_CHECK(expectError(text) == Error::Type::TypeError); -} - BOOST_AUTO_TEST_CASE(invalid_integer_literal_exp) { char const* text = R"( @@ -2792,8 +2795,8 @@ BOOST_AUTO_TEST_CASE(inline_array_declaration_and_passing_implicit_conversion) uint8 x = 7; uint16 y = 8; uint32 z = 9; - uint32[3] memory ending = [x, y, z]; - return (ending[1]); + uint32[3] memory ending = [x, y, z]; + return (ending[1]); } } )"; @@ -3230,27 +3233,44 @@ BOOST_AUTO_TEST_CASE(int10abc_is_identifier) BOOST_CHECK(success(text)); } -BOOST_AUTO_TEST_CASE(invalid_fixed_types) +BOOST_AUTO_TEST_CASE(library_functions_do_not_have_value) { char const* text = R"( + library L { function l() {} } contract test { function f() { - fixed0x7 a = .3; - fixed99999999999999999999999999999999999999x7 b = 9.5; + L.l.value; } } )"; BOOST_CHECK(!success(text)); } -BOOST_AUTO_TEST_CASE(library_functions_do_not_have_value) +BOOST_AUTO_TEST_CASE(invalid_fixed_types_0x7_mxn) { char const* text = R"( - library L { function l() {} } contract test { - function f() { - L.l.value; - } + fixed0x7 a = .3; + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(invalid_fixed_types_long_invalid_identifier) +{ + char const* text = R"( + contract test { + fixed99999999999999999999999999999999999999x7 b = 9.5; + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(invalid_fixed_types_7x8_mxn) +{ + char const* text = R"( + contract test { + fixed7x8 c = 3.12345678; } )"; BOOST_CHECK(!success(text)); @@ -3282,6 +3302,454 @@ BOOST_AUTO_TEST_CASE(invalid_fixed_type_long) BOOST_CHECK(!success(text)); } +BOOST_AUTO_TEST_CASE(fixed_type_int_conversion) +{ + char const* text = R"( + contract test { + function f() { + uint128 a = 3; + int128 b = 4; + fixed c = b; + ufixed d = a; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(fixed_type_rational_int_conversion) +{ + char const* text = R"( + contract test { + function f() { + fixed c = 3; + ufixed d = 4; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(fixed_type_rational_fraction_conversion) +{ + char const* text = R"( + contract test { + function f() { + fixed a = 4.5; + ufixed d = 2.5; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(invalid_int_implicit_conversion_from_fixed) +{ + char const* text = R"( + contract test { + function f() { + fixed a = 4.5; + int b = a; + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(rational_unary_operation) +{ + char const* text = R"( + contract test { + function f() { + ufixed8x16 a = +3.25; + fixed8x16 b = -3.25; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(leading_zero_rationals_convert) +{ + char const* text = R"( + contract A { + function f() { + ufixed0x8 a = 0.5; + ufixed0x56 b = 0.0000000000000006661338147750939242541790008544921875; + fixed0x8 c = -0.5; + fixed0x56 d = -0.0000000000000006661338147750939242541790008544921875; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(size_capabilities_of_fixed_point_types) +{ + char const* text = R"( + contract test { + function f() { + ufixed248x8 a = 123456781234567979695948382928485849359686494864095409282048094275023098123.5; + ufixed0x256 b = 0.920890746623327805482905058466021565416131529487595827354393978494366605267637829135688384325135165352082715782143655824815685807141335814463015972119819459298455224338812271036061391763384038070334798471324635050876128428143374549108557403087615966796875; + ufixed0x256 c = 0.0000000000015198847363997979984922685411315294875958273543939784943666052676464653042434787697605517039455161817147718251801220885263595179331845639229818863564267318422845592626219390573301877339317935702714669975697814319204326238832436501979827880859375; + fixed248x8 d = -123456781234567979695948382928485849359686494864095409282048094275023098123.5; + fixed0x256 e = -0.93322335481643744342575580035176794825198893968114429702091846411734101080123092162893656820177312738451291806995868682861328125; + fixed0x256 g = -0.00011788606643744342575580035176794825198893968114429702091846411734101080123092162893656820177312738451291806995868682861328125; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(fixed_type_invalid_implicit_conversion_size) +{ + char const* text = R"( + contract test { + function f() { + ufixed a = 11/4; + ufixed248x8 b = a; + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(fixed_type_invalid_implicit_conversion_lost_data) +{ + char const* text = R"( + contract test { + function f() { + ufixed0x256 a = 1/3; + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(fixed_type_valid_explicit_conversions) +{ + char const* text = R"( + contract test { + function f() { + ufixed0x256 a = ufixed0x256(1/3); + ufixed0x248 b = ufixed0x248(1/3); + ufixed0x8 c = ufixed0x8(1/3); + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_rational) +{ + char const* text = R"( + contract test { + function f() { + uint[3.5] a; + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_fixed_type) +{ + char const* text = R"( + contract test { + function f() { + uint[fixed(3.5)] a; + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(rational_to_bytes_implicit_conversion) +{ + char const* text = R"( + contract test { + function f() { + bytes32 c = 3.2; + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(fixed_to_bytes_implicit_conversion) +{ + char const* text = R"( + contract test { + function f() { + fixed a = 3.2; + bytes32 c = a; + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(mapping_with_fixed_literal) +{ + char const* text = R"( + contract test { + mapping(ufixed8x248 => string) fixedString; + function f() { + fixedString[0.5] = "Half"; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(fixed_points_inside_structs) +{ + char const* text = R"( + contract test { + struct myStruct { + ufixed a; + int b; + } + myStruct a = myStruct(3.125, 3); + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(inline_array_fixed_types) +{ + char const* text = R"( + contract test { + function f() { + fixed[3] memory a = [fixed(3.5), fixed(-4.25), fixed(967.125)]; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(inline_array_rationals) +{ + char const* text = R"( + contract test { + function f() { + ufixed8x8[4] memory a = [3.5, 4.125, 2.5, 4.0]; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(rational_index_access) +{ + char const* text = R"( + contract test { + function f() { + uint[] memory a; + a[.5]; + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(rational_to_fixed_literal_expression) +{ + char const* text = R"( + contract test { + function f() { + ufixed8x8 a = 3.5 * 3; + ufixed8x8 b = 4 - 2.5; + ufixed8x8 c = 11 / 4; + ufixed16x240 d = 599 + 0.21875; + ufixed8x248 e = ufixed8x248(35.245 % 12.9); + ufixed8x248 f = ufixed8x248(1.2 % 2); + fixed g = 2 ** -2; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(rational_as_exponent_value) +{ + char const* text = R"( + contract test { + function f() { + fixed g = 2 ** -2.2; + ufixed b = 3 ** 2.5; + ufixed24x24 b = 2 ** (1/2); + fixed40x40 c = 42 ** (-1/4); + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(fixed_point_casting_exponents) +{ + char const* text = R"( + contract test { + function f() { + ufixed a = 3 ** ufixed(1.5); + ufixed b = 2 ** ufixed(1/2); + fixed c = 42 ** fixed(-1/4); + fixed d = 16 ** fixed(-0.33); + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(var_capable_of_holding_constant_rationals) +{ + char const* text = R"( + contract test { + function f() { + var a = 0.12345678; + var b = 12345678.352; + var c = 0.00000009; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(var_and_rational_with_tuple) +{ + char const* text = R"( + contract test { + function f() { + var (a, b) = (.5, 1/3); + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(var_handle_divided_integers) +{ + char const* text = R"( + contract test { + function f() { + var x = 1/3; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(rational_bitnot_unary_operation) +{ + char const* text = R"( + contract test { + function f() { + fixed a = ~3.56; + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(rational_bitor_binary_operation) +{ + char const* text = R"( + contract test { + function f() { + fixed a = 1.56 | 3; + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(rational_bitxor_binary_operation) +{ + char const* text = R"( + contract test { + function f() { + fixed a = 1.56 ^ 3; + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(rational_bitand_binary_operation) +{ + char const* text = R"( + contract test { + function f() { + fixed a = 1.56 & 3; + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(zero_handling) +{ + char const* text = R"( + contract test { + function f() { + fixed8x8 a = 0; + ufixed8x8 b = 0; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(missing_bool_conversion) +{ + char const* text = R"( + contract test { + function b(uint a) { + bool(a == 1); + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(integer_and_fixed_interaction) +{ + char const* text = R"( + contract test { + function f() { + ufixed a = uint128(1) + ufixed(2); + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(signed_rational_modulus) +{ + char const* text = R"( + contract test { + function f() { + fixed a = 0.42578125 % -0.4271087646484375; + fixed b = .5 % a; + fixed c = a % b; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(one_divided_by_three_integer_conversion) +{ + char const* text = R"( + contract test { + function f() { + uint a = 1/3; + } + } + )"; + BOOST_CHECK(!success(text)); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index e43b026c..909d18c9 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -1177,6 +1177,52 @@ BOOST_AUTO_TEST_CASE(conditional_with_assignment) BOOST_CHECK(successParse(text)); } +BOOST_AUTO_TEST_CASE(declaring_fixed_and_ufixed_variables) +{ + char const* text = R"( + contract A { + fixed40x40 storeMe; + function f(ufixed x, fixed32x32 y) { + ufixed8x8 a; + fixed b; + } + } + )"; + BOOST_CHECK(successParse(text)); +} + +BOOST_AUTO_TEST_CASE(declaring_fixed_literal_variables) +{ + char const* text = R"( + contract A { + fixed40x40 pi = 3.14; + } + )"; + BOOST_CHECK(successParse(text)); +} + +BOOST_AUTO_TEST_CASE(no_double_radix_in_fixed_literal) +{ + char const* text = R"( + contract A { + fixed40x40 pi = 3.14.15; + } + )"; + BOOST_CHECK(!successParse(text)); +} + +BOOST_AUTO_TEST_CASE(invalid_fixed_conversion_leading_zeroes_check) +{ + char const* text = R"( + contract test { + function f() { + fixed a = 1.0x2; + } + } + )"; + BOOST_CHECK(!successParse(text)); +} + BOOST_AUTO_TEST_SUITE_END() } |