diff --git a/CMakeLists.txt b/CMakeLists.txt index 9325fcb01..e623c7319 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -190,6 +190,7 @@ else() endif () find_path( PYTHON_ID pyconfig.h + ${PYTHON_INCLUDE_DIR} /usr/include/python2.7 /usr/local/include/python2.7 ) @@ -289,7 +290,7 @@ else() if (LANGUAGES) find_package(Boost 1.53 REQUIRED COMPONENTS thread date_time) else() - find_package(Boost 1.53 REQUIRED COMPONENTS thread date_time system) + find_package(Boost 1.53 REQUIRED COMPONENTS thread date_time system regex) endif() set(QTQML 1) @@ -323,7 +324,7 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") include_directories(/usr/local/include) endif() -add_subdirectory(libethential) +add_subdirectory(libdevcore) add_subdirectory(libevmface) add_subdirectory(liblll) add_subdirectory(libserpent) @@ -336,9 +337,16 @@ add_subdirectory(lllc) add_subdirectory(sc) if (NOT LANGUAGES) add_subdirectory(secp256k1) + add_subdirectory(libp2p) + add_subdirectory(libdevcrypto) + add_subdirectory(libwhisper) + add_subdirectory(libethcore) add_subdirectory(libevm) add_subdirectory(libethereum) +# add_subdirectory(libethereumx) # TODO remove + + add_subdirectory(libwebthree) add_subdirectory(test) add_subdirectory(eth) if("x${CMAKE_BUILD_TYPE}" STREQUAL "xDebug") @@ -358,8 +366,10 @@ if (NOT LANGUAGES) add_subdirectory(libqethereum) add_subdirectory(alethzero) + add_subdirectory(third) if(QTQML) - add_subdirectory(walleth) + #add_subdirectory(iethxi) + #add_subdirectory(walleth) // resurect once we want to submit ourselves to QML. endif() endif() endif() diff --git a/LICENSE b/LICENSE index 72dc60d84..5055c1434 100644 --- a/LICENSE +++ b/LICENSE @@ -1,19 +1,673 @@ -The MIT License (MIT) - -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. + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. + + + Copyright (C) + + 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 . + +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: + + Copyright (C) + 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 +. + + 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 +. diff --git a/alethzero/CMakeLists.txt b/alethzero/CMakeLists.txt index 11ff83b15..05e4535fc 100644 --- a/alethzero/CMakeLists.txt +++ b/alethzero/CMakeLists.txt @@ -52,7 +52,7 @@ else () endif () qt5_use_modules(${EXECUTEABLE} Core)# Gui Widgets Network WebKit WebKitWidgets) -target_link_libraries(${EXECUTEABLE} qethereum ethereum evm ethcore secp256k1 gmp ${CRYPTOPP_LS} serpent lll evmface ethential) +target_link_libraries(${EXECUTEABLE} webthree qethereum ethereum evm ethcore devcrypto secp256k1 gmp ${CRYPTOPP_LS} serpent lll evmface devcore) if (APPLE) # First have qt5 install plugins and frameworks diff --git a/alethzero/DownloadView.cpp b/alethzero/DownloadView.cpp new file mode 100644 index 000000000..657727a6a --- /dev/null +++ b/alethzero/DownloadView.cpp @@ -0,0 +1,83 @@ +/* + 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 . +*/ +/** @file DownloadView.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "DownloadView.h" + +#include +#include +#include +#include "Grapher.h" + +using namespace std; +using namespace dev; +using namespace dev::eth; + +DownloadView::DownloadView(QWidget* _p): QWidget(_p) +{ +} + +void DownloadView::paintEvent(QPaintEvent*) +{ + QPainter p(this); + + p.fillRect(rect(), Qt::white); + if (!m_man || m_man->chain().empty() || !m_man->subCount()) + return; + + double ratio = (double)rect().width() / rect().height(); + if (ratio < 1) + ratio = 1 / ratio; + double n = min(rect().width(), rect().height()) / ceil(sqrt(m_man->chain().size() / ratio)); + +// QSizeF area(rect().width() / floor(rect().width() / n), rect().height() / floor(rect().height() / n)); + QSizeF area(n, n); + QPointF pos(0, 0); + + auto const& bg = m_man->blocksGot(); + + for (unsigned i = bg.all().first, ei = bg.all().second; i < ei; ++i) + { + int s = -2; + if (bg.contains(i)) + s = -1; + else + { + unsigned h = 0; + m_man->foreachSub([&](DownloadSub const& sub) + { + if (sub.asked().contains(i)) + s = h; + h++; + }); + } + unsigned dh = 360 / m_man->subCount(); + if (s == -2) + p.fillRect(QRectF(QPointF(pos) + QPointF(3 * area.width() / 8, 3 * area.height() / 8), area / 4), Qt::black); + else if (s == -1) + p.fillRect(QRectF(QPointF(pos) + QPointF(1 * area.width() / 8, 1 * area.height() / 8), area * 3 / 4), Qt::black); + else + p.fillRect(QRectF(QPointF(pos) + QPointF(1 * area.width() / 8, 1 * area.height() / 8), area * 3 / 4), QColor::fromHsv(s * dh, 64, 128)); + + pos.setX(pos.x() + n); + if (pos.x() >= rect().width() - n) + pos = QPoint(0, pos.y() + n); + } +} diff --git a/alethzero/DownloadView.h b/alethzero/DownloadView.h new file mode 100644 index 000000000..22a11651c --- /dev/null +++ b/alethzero/DownloadView.h @@ -0,0 +1,53 @@ +/* + 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 . +*/ +/** @file DownloadView.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#ifdef Q_MOC_RUN +#define BOOST_MPL_IF_HPP_INCLUDED +#endif + +#include +#include +#ifndef Q_MOC_RUN +#include +#endif + +namespace dev { namespace eth { +struct MineInfo; +class DownloadMan; +}} + +class DownloadView: public QWidget +{ + Q_OBJECT + +public: + DownloadView(QWidget* _p = nullptr); + + void setDownloadMan(dev::eth::DownloadMan const* _man) { m_man = _man; } + +protected: + virtual void paintEvent(QPaintEvent*); + +private: + dev::eth::DownloadMan const* m_man = nullptr; +}; diff --git a/alethzero/Main.ui b/alethzero/Main.ui index 3f78ce6f1..dbc4bb144 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -132,6 +132,7 @@ + @@ -184,7 +185,11 @@ - + + + + + @@ -377,19 +382,27 @@ 0 - + - Monospace - 12 + Ubuntu Mono - - Qt::NoFocus - QFrame::NoFrame + + QFrame::Plain + + + 0 + + + QPlainTextEdit::NoWrap + + + true + @@ -498,6 +511,7 @@ + @@ -557,7 +571,7 @@ - Qt::NoFocus + Qt::ClickFocus QFrame::NoFrame @@ -565,6 +579,9 @@ 0 + + true + @@ -1416,6 +1433,39 @@ font-size: 14pt + + + QDockWidget::DockWidgetFeatureMask + + + Blockchain Download + + + 2 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + &Quit @@ -1553,9 +1603,12 @@ font-size: 14pt Ctrl+F10 - + + + true + - &Clear Pending + &Force Mining @@ -1650,6 +1703,43 @@ font-size: 14pt &Pretty... + + + &Refresh + + + + + true + + + &Use Private Chain... + + + + + true + + + &Enable LLL &Optimizer + + + + + true + + + Reserved Debug 1 + + + + + true + + + Enable Local Addresses + + @@ -1664,6 +1754,12 @@ font-size: 14pt
MiningView.h
1 + + DownloadView + QWidget +
DownloadView.h
+ 1 +
destination diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index dec7f252a..b4bbb8ca7 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -37,59 +38,49 @@ #include #include #include -#include +#include +#include +#include "DownloadView.h" #include "MiningView.h" #include "BuildInfo.h" #include "MainWin.h" #include "ui_Main.h" using namespace std; - -// types -using eth::bytes; -using eth::bytesConstRef; -using eth::h160; -using eth::h256; -using eth::u160; -using eth::u256; -using eth::Address; -using eth::BlockInfo; -using eth::Client; -using eth::Instruction; -using eth::KeyPair; -using eth::NodeMode; -using eth::BlockChain; -using eth::PeerInfo; -using eth::RLP; -using eth::Secret; -using eth::Transaction; -using eth::Executive; - -// functions -using eth::toHex; -using eth::compileLLL; -using eth::disassemble; -using eth::formatBalance; -using eth::fromHex; -using eth::sha3; -using eth::left160; -using eth::right160; -using eth::simpleDebugOut; -using eth::toLog2; -using eth::toString; -using eth::units; -using eth::operator<<; - -// vars -using eth::g_logPost; -using eth::g_logVerbosity; -using eth::c_instructionInfo; +using namespace dev; +using namespace dev::p2p; +using namespace dev::eth; static void initUnits(QComboBox* _b) { - for (auto n = (::uint)units().size(); n-- != 0; ) + for (auto n = (unsigned)units().size(); n-- != 0; ) _b->addItem(QString::fromStdString(units()[n].second), n); } +static QString fromRaw(dev::h256 _n, unsigned* _inc = nullptr) +{ + if (_n) + { + std::string s((char const*)_n.data(), 32); + auto l = s.find_first_of('\0'); + if (!l) + return QString(); + if (l != string::npos) + { + auto p = s.find_first_not_of('\0', l); + if (!(p == string::npos || (_inc && p == 31))) + return QString(); + if (_inc) + *_inc = (byte)s[31]; + s.resize(l); + } + for (auto i: s) + if (i < 32) + return QString(); + return QString::fromStdString(s); + } + return QString(); +} + Address c_config = Address("661005d2720d855f1d9976f88bb10c1a3398c77f"); Main::Main(QWidget *parent) : @@ -98,35 +89,27 @@ Main::Main(QWidget *parent) : { setWindowFlags(Qt::Window); ui->setupUi(this); - g_logPost = [=](std::string const& s, char const* c) { simpleDebugOut(s, c); ui->log->addItem(QString::fromStdString(s)); }; - -#if 0&Ð_DEBUG - m_servers.append("192.168.0.10:30301"); -#else - int pocnumber = QString(eth::EthVersion).section('.', 1, 1).toInt(); - if (pocnumber == 5) - m_servers.push_back("54.72.69.180:30303"); - else if (pocnumber == 6) - m_servers.push_back("54.72.69.180:30303"/*"54.72.31.55:30303"*/); - else + g_logPost = [=](std::string const& s, char const* c) { - connect(&m_webCtrl, &QNetworkAccessManager::finished, [&](QNetworkReply* _r) - { - m_servers = QString::fromUtf8(_r->readAll()).split("\n", QString::SkipEmptyParts); - }); - QNetworkRequest r(QUrl("http://www.ethereum.org/servers.poc" + QString::number(pocnumber) + ".txt")); - r.setHeader(QNetworkRequest::UserAgentHeader, "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1712.0 Safari/537.36"); - m_webCtrl.get(r); - srand(time(0)); - } + simpleDebugOut(s, c); + m_logLock.lock(); + m_logHistory.append(QString::fromStdString(s) + "\n"); + m_logChanged = true; + m_logLock.unlock(); +// ui->log->addItem(QString::fromStdString(s)); + }; + +#if ETH_DEBUG + m_servers.append("localhost:30300"); #endif + m_servers.append(QString::fromStdString(Host::pocHost() + ":30303")); cerr << "State root: " << BlockChain::genesis().stateRoot << endl; cerr << "Block Hash: " << sha3(BlockChain::createGenesisBlock()) << endl; cerr << "Block RLP: " << RLP(BlockChain::createGenesisBlock()) << endl; cerr << "Block Hex: " << toHex(BlockChain::createGenesisBlock()) << endl; - cerr << "Network protocol version: " << eth::c_protocolVersion << endl; - cerr << "Client database version: " << eth::c_databaseVersion << endl; + cerr << "Network protocol version: " << dev::eth::c_protocolVersion << endl; + cerr << "Client database version: " << dev::eth::c_databaseVersion << endl; ui->configDock->close(); on_verbosity_valueChanged(); @@ -144,25 +127,22 @@ Main::Main(QWidget *parent) : connect(ui->ourAccounts->model(), SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)), SLOT(ourAccountsRowsMoved())); - m_client.reset(new Client("AlethZero")); - m_client->start(); + m_webThree.reset(new WebThreeDirect(string("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir() + "/AlethZero", false, {"eth", "shh"})); connect(ui->webView, &QWebView::loadStarted, [this]() { - QEthereum *eth = new QEthereum(this, this->m_client.get(), this->owned()); - this->m_ethereum = eth; - connect(this, SIGNAL(changed()), this->m_ethereum, SIGNAL(changed())); + // NOTE: no need to delete as QETH_INSTALL_JS_NAMESPACE adopts it. + m_ethereum = new QEthereum(this, ethereum(), owned()); - QWebFrame* f = this->ui->webView->page()->mainFrame(); + QWebFrame* f = ui->webView->page()->mainFrame(); f->disconnect(SIGNAL(javaScriptWindowObjectCleared())); - eth->setup(f); - f->addToJavaScriptWindowObject("env", this, QWebFrame::QtOwnership); - connect(f, &QWebFrame::javaScriptWindowObjectCleared, QETH_INSTALL_JS_NAMESPACE); + auto qeth = m_ethereum; + connect(f, &QWebFrame::javaScriptWindowObjectCleared, QETH_INSTALL_JS_NAMESPACE(f, qeth, this)); }); connect(ui->webView, &QWebView::loadFinished, [=]() { - this->changed(); + m_ethereum->poll(); }); connect(ui->webView, &QWebView::titleChanged, [=]() @@ -171,11 +151,10 @@ Main::Main(QWidget *parent) : }); readSettings(); - refresh(); - m_refresh = new QTimer(this); - connect(m_refresh, SIGNAL(timeout()), SLOT(refresh())); - m_refresh->start(100); + installWatches(); + + startTimer(100); { QSettings s("ethereum", "alethzero"); @@ -185,18 +164,132 @@ Main::Main(QWidget *parent) : s.setValue("splashMessage", false); } } - m_pcWarp.clear(); } Main::~Main() { + // Must do this here since otherwise m_ethereum'll be deleted (and therefore clearWatches() called by the destructor) + // *after* the client is dead. + m_ethereum->clientDieing(); + g_logPost = simpleDebugOut; writeSettings(); } -void Main::on_clearPending_triggered() +dev::p2p::NetworkPreferences Main::netPrefs() const +{ + return NetworkPreferences(ui->port->value(), ui->forceAddress->text().toStdString(), ui->upnp->isChecked(), ui->localNetworking->isChecked()); +} + +void Main::onKeysChanged() +{ + installBalancesWatch(); +} + +unsigned Main::installWatch(dev::eth::MessageFilter const& _tf, std::function const& _f) +{ + auto ret = ethereum()->installWatch(_tf); + m_handlers[ret] = _f; + return ret; +} + +unsigned Main::installWatch(dev::h256 _tf, std::function const& _f) +{ + auto ret = ethereum()->installWatch(_tf); + m_handlers[ret] = _f; + return ret; +} + +void Main::installWatches() +{ + installWatch(dev::eth::MessageFilter().altered(c_config, 0), [=](){ installNameRegWatch(); }); + installWatch(dev::eth::MessageFilter().altered(c_config, 1), [=](){ installCurrenciesWatch(); }); + installWatch(dev::eth::PendingChangedFilter, [=](){ onNewPending(); }); + installWatch(dev::eth::ChainChangedFilter, [=](){ onNewBlock(); }); +} + +void Main::installNameRegWatch() +{ + ethereum()->uninstallWatch(m_nameRegFilter); + m_nameRegFilter = installWatch(dev::eth::MessageFilter().altered((u160)ethereum()->stateAt(c_config, 0)), [=](){ onNameRegChange(); }); +} + +void Main::installCurrenciesWatch() +{ + ethereum()->uninstallWatch(m_currenciesFilter); + m_currenciesFilter = installWatch(dev::eth::MessageFilter().altered((u160)ethereum()->stateAt(c_config, 1)), [=](){ onCurrenciesChange(); }); +} + +void Main::installBalancesWatch() +{ + dev::eth::MessageFilter tf; + + vector
altCoins; + Address coinsAddr = right160(ethereum()->stateAt(c_config, 1)); + for (unsigned i = 0; i < ethereum()->stateAt(coinsAddr, 0); ++i) + altCoins.push_back(right160(ethereum()->stateAt(coinsAddr, i + 1))); + for (auto i: m_myKeys) + { + tf.altered(i.address()); + for (auto c: altCoins) + tf.altered(c, (u160)i.address()); + } + + ethereum()->uninstallWatch(m_balancesFilter); + m_balancesFilter = installWatch(tf, [=](){ onBalancesChange(); }); +} + +void Main::onNameRegChange() +{ + cwatch << "NameReg changed!"; + + // update any namereg-dependent stuff - for now force a full update. + refreshAll(); +} + +void Main::onCurrenciesChange() +{ + cwatch << "Currencies changed!"; + installBalancesWatch(); + + // TODO: update any currency-dependent stuff? +} + +void Main::onBalancesChange() +{ + cwatch << "Our balances changed!"; + + refreshBalances(); +} + +void Main::onNewBlock() +{ + cwatch << "Blockchain changed!"; + + // update blockchain dependent views. + refreshBlockCount(); + refreshBlockChain(); + refreshAccounts(); +} + +void Main::onNewPending() { - m_client->clearPending(); + cwatch << "Pending transactions changed!"; + + // update any pending-transaction dependent views. + refreshPending(); + refreshAccounts(); +} + +void Main::on_forceMining_triggered() +{ + ethereum()->setForceMining(ui->forceMining->isChecked()); +} + +void Main::on_enableOptimizer_triggered() +{ + m_enableOptimizer = ui->enableOptimizer->isChecked(); + on_data_textChanged(); } void Main::load(QString _s) @@ -223,6 +316,7 @@ void Main::load(QString _s) } } } + // env.load("/home/gav/eth/init.eth") void Main::on_loadJS_triggered() @@ -257,7 +351,8 @@ void Main::eval(QString const& _js) { if (_js.trimmed().isEmpty()) return; - QVariant ev = ui->webView->page()->currentFrame()->evaluateJavaScript(_js); + QVariant ev = ui->webView->page()->currentFrame()->evaluateJavaScript((_js.startsWith("{") || _js.startsWith("if ") || _js.startsWith("if(")) ? _js : ("___RET=(" + _js + ")")); + QVariant jsonEv = ui->webView->page()->currentFrame()->evaluateJavaScript("JSON.stringify(___RET)"); QString s; if (ev.isNull()) s = "null"; @@ -265,6 +360,8 @@ void Main::eval(QString const& _js) s = "\"" + ev.toString().toHtmlEscaped() + "\""; else if (ev.type() == QVariant::Int || ev.type() == QVariant::Double) s = "" + ev.toString().toHtmlEscaped() + ""; + else if (jsonEv.type() == QVariant::String) + s = "" + jsonEv.toString().toHtmlEscaped() + ""; else s = "unknown type"; m_consoleHistory.push_back(qMakePair(_js, s)); @@ -276,45 +373,20 @@ void Main::eval(QString const& _js) ui->jsConsole->setHtml(s); } -QString fromRaw(eth::h256 _n, unsigned* _inc = nullptr) -{ - if (_n) - { - std::string s((char const*)_n.data(), 32); - auto l = s.find_first_of('\0'); - if (!l) - return QString(); - if (l != string::npos) - { - auto p = s.find_first_not_of('\0', l); - if (!(p == string::npos || (_inc && p == 31))) - return QString(); - if (_inc) - *_inc = (byte)s[31]; - s.resize(l); - } - for (auto i: s) - if (i < 32) - return QString(); - return QString::fromStdString(s); - } - return QString(); -} - -QString Main::pretty(eth::Address _a) const +QString Main::pretty(dev::Address _a) const { h256 n; - if (h160 nameReg = (u160)state().storage(c_config, 0)) - n = state().storage(nameReg, (u160)(_a)); + if (h160 nameReg = (u160)ethereum()->stateAt(c_config, 0)) + n = ethereum()->stateAt(nameReg, (u160)(_a)); if (!n) - n = state().storage(m_nameReg, (u160)(_a)); + n = ethereum()->stateAt(m_nameReg, (u160)(_a)); return fromRaw(n); } -QString Main::render(eth::Address _a) const +QString Main::render(dev::Address _a) const { QString p = pretty(_a); if (!p.isNull()) @@ -335,11 +407,11 @@ Address Main::fromString(QString const& _a) const memset(n.data() + sn.size(), 0, 32 - sn.size()); if (_a.size()) { - if (h160 nameReg = (u160)state().storage(c_config, 0)) - if (h256 a = state().storage(nameReg, n)) + if (h160 nameReg = (u160)ethereum()->stateAt(c_config, 0)) + if (h256 a = ethereum()->stateAt(nameReg, n)) return right160(a); - if (h256 a = state().storage(m_nameReg, n)) + if (h256 a = ethereum()->stateAt(m_nameReg, n)) return right160(a); } if (_a.size() == 40) @@ -348,14 +420,48 @@ Address Main::fromString(QString const& _a) const return Address(); } +QString Main::lookup(QString const& _a) const +{ + if (!_a.endsWith(".eth")) + return _a; + + string sn = _a.mid(0, _a.size() - 4).toStdString(); + if (sn.size() > 32) + sn = sha3(sn, false); + h256 n; + memcpy(n.data(), sn.data(), sn.size()); + +/* string sn2 = _a.toStdString(); + if (sn2.size() > 32) + sn2 = sha3(sn2, false); + h256 n2; + memcpy(n2.data(), sn2.data(), sn2.size()); +*/ + + h256 ret; + if (h160 dnsReg = (u160)ethereum()->stateAt(c_config, 4, 0)) + ret = ethereum()->stateAt(dnsReg, n); +/* if (!ret) + if (h160 nameReg = (u160)ethereum()->stateAt(c_config, 0, 0)) + ret = ethereum()->stateAt(nameReg, n2); +*/ + if (ret && !((u256)ret >> 32)) + return QString("%1.%2.%3.%4").arg((int)ret[28]).arg((int)ret[29]).arg((int)ret[30]).arg((int)ret[31]); + // TODO: support IPv6. + else if (ret) + return fromRaw(ret); + else + return _a; +} + void Main::on_about_triggered() { - QMessageBox::about(this, "About AlethZero PoC-" + QString(eth::EthVersion).section('.', 1, 1), QString("AlethZero/v") + eth::EthVersion + "/" ETH_QUOTED(ETH_BUILD_TYPE) "/" ETH_QUOTED(ETH_BUILD_PLATFORM) "\n" ETH_QUOTED(ETH_COMMIT_HASH) + (ETH_CLEAN_REPO ? "\nCLEAN" : "\n+ LOCAL CHANGES") + "\n\nBy Gav Wood, 2014.\nBased on a design by Vitalik Buterin.\n\nTeam Ethereum++ includes: Eric Lombrozo, Marko Simovic, Alex Leverington, Tim Hughes and several others."); + QMessageBox::about(this, "About AlethZero PoC-" + QString(dev::Version).section('.', 1, 1), QString("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) "\n" DEV_QUOTED(ETH_COMMIT_HASH) + (ETH_CLEAN_REPO ? "\nCLEAN" : "\n+ LOCAL CHANGES") + "\n\nBy Gav Wood, 2014.\nBased on a design by Vitalik Buterin.\n\nThanks to the various contributors including: Alex Leverington, Tim Hughes, caktux, Eric Lombrozo, Marko Simovic."); } void Main::on_paranoia_triggered() { - m_client->setParanoia(ui->paranoia->isChecked()); + ethereum()->setParanoia(ui->paranoia->isChecked()); } void Main::writeSettings() @@ -374,20 +480,22 @@ void Main::writeSettings() s.setValue("upnp", ui->upnp->isChecked()); s.setValue("forceAddress", ui->forceAddress->text()); s.setValue("usePast", ui->usePast->isChecked()); + s.setValue("localNetworking", ui->localNetworking->isChecked()); + s.setValue("forceMining", ui->forceMining->isChecked()); s.setValue("paranoia", ui->paranoia->isChecked()); s.setValue("showAll", ui->showAll->isChecked()); s.setValue("showAllAccounts", ui->showAllAccounts->isChecked()); + s.setValue("enableOptimizer", m_enableOptimizer); s.setValue("clientName", ui->clientName->text()); s.setValue("idealPeers", ui->idealPeers->value()); s.setValue("port", ui->port->value()); s.setValue("url", ui->urlEdit->text()); + s.setValue("privateChain", m_privateChain); + s.setValue("verbosity", ui->verbosity->value()); - if (m_client->peerServer()) - { - bytes d = m_client->peerServer()->savePeers(); + bytes d = m_webThree->savePeers(); + if (d.size()) m_peers = QByteArray((char*)d.data(), (int)d.size()); - - } s.setValue("peers", m_peers); s.setValue("nameReg", ui->nameReg->text()); @@ -395,11 +503,12 @@ void Main::writeSettings() s.setValue("windowState", saveState()); } -void Main::readSettings() +void Main::readSettings(bool _skipGeometry) { QSettings s("ethereum", "alethzero"); - restoreGeometry(s.value("geometry").toByteArray()); + if (!_skipGeometry) + restoreGeometry(s.value("geometry").toByteArray()); restoreState(s.value("windowState").toByteArray()); m_myKeys.clear(); @@ -416,18 +525,27 @@ void Main::readSettings() m_myKeys.append(KeyPair(k)); } } - m_client->setAddress(m_myKeys.back().address()); + ethereum()->setAddress(m_myKeys.back().address()); m_peers = s.value("peers").toByteArray(); ui->upnp->setChecked(s.value("upnp", true).toBool()); ui->forceAddress->setText(s.value("forceAddress", "").toString()); ui->usePast->setChecked(s.value("usePast", true).toBool()); + ui->localNetworking->setChecked(s.value("localNetworking", true).toBool()); + ui->forceMining->setChecked(s.value("forceMining", false).toBool()); + on_forceMining_triggered(); ui->paranoia->setChecked(s.value("paranoia", false).toBool()); ui->showAll->setChecked(s.value("showAll", false).toBool()); ui->showAllAccounts->setChecked(s.value("showAllAccounts", false).toBool()); + m_enableOptimizer = s.value("enableOptimizer", true).toBool(); + ui->enableOptimizer->setChecked(m_enableOptimizer); ui->clientName->setText(s.value("clientName", "").toString()); ui->idealPeers->setValue(s.value("idealPeers", ui->idealPeers->value()).toInt()); ui->port->setValue(s.value("port", ui->port->value()).toInt()); - ui->nameReg->setText(s.value("NameReg", "").toString()); + ui->nameReg->setText(s.value("nameReg", "").toString()); + m_privateChain = s.value("privateChain", "").toString(); + ui->usePrivate->setChecked(m_privateChain.size()); + ui->verbosity->setValue(s.value("verbosity", 1).toInt()); + ui->urlEdit->setText(s.value("url", "about:blank").toString()); //http://gavwood.com/gavcoin.html on_urlEdit_returnPressed(); } @@ -461,9 +579,33 @@ void Main::on_exportKey_triggered() } } +void Main::on_usePrivate_triggered() +{ + if (ui->usePrivate->isChecked()) + { + m_privateChain = QInputDialog::getText(this, "Enter Name", "Enter the name of your private chain", QLineEdit::Normal, QString("NewChain-%1").arg(time(0))); + if (m_privateChain.isEmpty()) + ui->usePrivate->setChecked(false); + } + else + { + m_privateChain.clear(); + } + on_killBlockchain_triggered(); +} + void Main::on_urlEdit_returnPressed() { - ui->webView->setUrl(ui->urlEdit->text()); + QString s = ui->urlEdit->text(); + QRegExp r("([a-z]+://)?([^/]*)(.*)"); + if (r.exactMatch(s)) + if (r.cap(2).isEmpty()) + s = (r.cap(1).isEmpty() ? "file://" : r.cap(1)) + r.cap(3); + else + s = (r.cap(1).isEmpty() ? "http://" : r.cap(1)) + lookup(r.cap(2)) + r.cap(3); + else{} + qDebug() << s; + ui->webView->setUrl(s); } void Main::on_nameReg_textChanged() @@ -472,67 +614,162 @@ void Main::on_nameReg_textChanged() if (s.size() == 40) { m_nameReg = Address(fromHex(s)); - refresh(true); + refreshAll(); } else m_nameReg = Address(); } +void Main::on_preview_triggered() +{ + ethereum()->setDefault(ui->preview->isChecked() ? 0 : -1); + refreshAll(); +} + void Main::refreshMining() { - eth::ClientGuard g(m_client.get()); - eth::MineProgress p = m_client->miningProgress(); - ui->mineStatus->setText(m_client->isMining() ? QString("%1s @ %2kH/s").arg(p.ms / 1000).arg(p.ms ? p.hashes / p.ms : 0) : "Not mining"); + dev::eth::MineProgress p = ethereum()->miningProgress(); + ui->mineStatus->setText(ethereum()->isMining() ? QString("%1s @ %2kH/s").arg(p.ms / 1000).arg(p.ms ? p.hashes / p.ms : 0) : "Not mining"); if (!ui->miningView->isVisible()) return; - list l = m_client->miningHistory(); - static uint lh = 0; + list l = ethereum()->miningHistory(); + static unsigned lh = 0; if (p.hashes < lh) ui->miningView->resetStats(); lh = p.hashes; ui->miningView->appendStats(l, p); /* if (p.ms) - for (eth::MineInfo const& i: l) + for (dev::eth::MineInfo const& i: l) cnote << i.hashes * 10 << "h/sec, need:" << i.requirement << " best:" << i.best << " best-so-far:" << p.best << " avg-speed:" << (p.hashes * 1000 / p.ms) << "h/sec"; */ } +void Main::refreshBalances() +{ + cwatch << "refreshBalances()"; + // update all the balance-dependent stuff. + ui->ourAccounts->clear(); + u256 totalBalance = 0; + map> altCoins; + Address coinsAddr = right160(ethereum()->stateAt(c_config, 1)); + for (unsigned i = 0; i < ethereum()->stateAt(coinsAddr, 0); ++i) + { + auto n = ethereum()->stateAt(coinsAddr, i + 1); + auto addr = right160(ethereum()->stateAt(coinsAddr, n)); + auto denom = ethereum()->stateAt(coinsAddr, sha3(h256(n).asBytes())); + if (denom == 0) + denom = 1; +// cdebug << n << addr << denom << sha3(h256(n).asBytes()); + altCoins[addr] = make_tuple(fromRaw(n), 0, denom); + } + for (auto i: m_myKeys) + { + u256 b = ethereum()->balanceAt(i.address()); + (new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(b).c_str()).arg(render(i.address())).arg((unsigned)ethereum()->countAt(i.address())), ui->ourAccounts)) + ->setData(Qt::UserRole, QByteArray((char const*)i.address().data(), Address::size)); + totalBalance += b; + + for (auto& c: altCoins) + get<1>(c.second) += (u256)ethereum()->stateAt(c.first, (u160)i.address()); + } + + QString b; + for (auto const& c: altCoins) + if (get<1>(c.second)) + { + stringstream s; + s << setw(toString(get<2>(c.second) - 1).size()) << setfill('0') << (get<1>(c.second) % get<2>(c.second)); + b += QString::fromStdString(toString(get<1>(c.second) / get<2>(c.second)) + "." + s.str() + " ") + get<0>(c.second).toUpper() + " | "; + } + ui->balance->setText(b + QString::fromStdString(formatBalance(totalBalance))); +} + void Main::refreshNetwork() { - auto ps = m_client->peers(); + auto ps = web3()->peers(); ui->peerCount->setText(QString::fromStdString(toString(ps.size())) + " peer(s)"); ui->peers->clear(); for (PeerInfo const& i: ps) - ui->peers->addItem(QString("%3 ms - %1:%2 - %4").arg(i.host.c_str()).arg(i.port).arg(chrono::duration_cast(i.lastPing).count()).arg(i.clientVersion.c_str())); + ui->peers->addItem(QString("%3 ms - %1:%2 - %4 %5").arg(i.host.c_str()).arg(i.port).arg(chrono::duration_cast(i.lastPing).count()).arg(i.clientVersion.c_str()).arg(QString::fromStdString(toString(i.caps)))); } -eth::State const& Main::state() const +void Main::refreshAll() { - return ui->preview->isChecked() ? m_client->postState() : m_client->state(); + refreshDestination(); + refreshBlockChain(); + refreshBlockCount(); + refreshPending(); + refreshAccounts(); + refreshBalances(); } -void Main::updateBlockCount() +void Main::refreshPending() { - auto d = m_client->blockChain().details(); - auto diff = BlockInfo(m_client->blockChain().block()).difficulty; - ui->blockCount->setText(QString("#%1 @%3 T%2 N%4 D%5").arg(d.number).arg(toLog2(d.totalDifficulty)).arg(toLog2(diff)).arg(eth::c_protocolVersion).arg(eth::c_databaseVersion)); + cwatch << "refreshPending()"; + ui->transactionQueue->clear(); + for (Transaction const& t: ethereum()->pending()) + { + QString s = t.receiveAddress ? + QString("%2 %5> %3: %1 [%4]") + .arg(formatBalance(t.value).c_str()) + .arg(render(t.safeSender())) + .arg(render(t.receiveAddress)) + .arg((unsigned)t.nonce) + .arg(ethereum()->codeAt(t.receiveAddress).size() ? '*' : '-') : + QString("%2 +> %3: %1 [%4]") + .arg(formatBalance(t.value).c_str()) + .arg(render(t.safeSender())) + .arg(render(right160(sha3(rlpList(t.safeSender(), t.nonce))))) + .arg((unsigned)t.nonce); + ui->transactionQueue->addItem(s); + } } -void Main::on_blockChainFilter_textChanged() +void Main::refreshAccounts() { - static QTimer* s_delayed = nullptr; - if (!s_delayed) - { - s_delayed = new QTimer(this); - s_delayed->setSingleShot(true); - connect(s_delayed, SIGNAL(timeout()), SLOT(refreshBlockChain())); - } - s_delayed->stop(); - s_delayed->start(200); + cwatch << "refreshAccounts()"; + ui->accounts->clear(); + ui->contracts->clear(); + for (auto n = 0; n < 2; ++n) + for (auto i: ethereum()->addresses()) + { + auto r = render(i); + if (r.contains('(') == !n) + { + if (n == 0 || ui->showAllAccounts->isChecked()) + (new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(ethereum()->balanceAt(i)).c_str()).arg(r).arg((unsigned)ethereum()->countAt(i)), ui->accounts)) + ->setData(Qt::UserRole, QByteArray((char const*)i.data(), Address::size)); + if (ethereum()->codeAt(i).size()) + (new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(ethereum()->balanceAt(i)).c_str()).arg(r).arg((unsigned)ethereum()->countAt(i)), ui->contracts)) + ->setData(Qt::UserRole, QByteArray((char const*)i.data(), Address::size)); + } + } +} + +void Main::refreshDestination() +{ + cwatch << "refreshDestination()"; + QString s; + for (auto i: ethereum()->addresses()) + if ((s = pretty(i)).size()) + // A namereg address + if (ui->destination->findText(s, Qt::MatchExactly | Qt::MatchCaseSensitive) == -1) + ui->destination->addItem(s); + for (int i = 0; i < ui->destination->count(); ++i) + if (ui->destination->itemText(i) != "(Create Contract)" && !fromString(ui->destination->itemText(i))) + ui->destination->removeItem(i--); +} + +void Main::refreshBlockCount() +{ + cwatch << "refreshBlockCount()"; + auto d = ethereum()->blockChain().details(); + auto diff = BlockInfo(ethereum()->blockChain().block()).difficulty; + ui->blockCount->setText(QString("%6 #%1 @%3 T%2 N%4 D%5").arg(d.number).arg(toLog2(d.totalDifficulty)).arg(toLog2(diff)).arg(dev::eth::c_protocolVersion).arg(dev::eth::c_databaseVersion).arg(m_privateChain.size() ? "[" + m_privateChain + "] " : "testnet")); } -static bool blockMatch(string const& _f, eth::BlockDetails const& _b, h256 _h, BlockChain const& _bc) +static bool blockMatch(string const& _f, dev::eth::BlockDetails const& _b, h256 _h, BlockChain const& _bc) { try { @@ -557,18 +794,22 @@ static bool transactionMatch(string const& _f, Transaction const& _t) return false; } +void Main::on_turboMining_triggered() +{ + ethereum()->setTurboMining(ui->turboMining->isChecked()); +} + void Main::refreshBlockChain() { - eth::ClientGuard g(m_client.get()); - auto const& st = state(); + cwatch << "refreshBlockChain()"; QByteArray oldSelected = ui->blocks->count() ? ui->blocks->currentItem()->data(Qt::UserRole).toByteArray() : QByteArray(); ui->blocks->clear(); string filter = ui->blockChainFilter->text().toLower().toStdString(); - auto const& bc = m_client->blockChain(); + auto const& bc = ethereum()->blockChain(); unsigned i = (ui->showAll->isChecked() || !filter.empty()) ? (unsigned)-1 : 10; - for (auto h = bc.currentHash(); h != bc.genesisHash() && i; h = bc.details(h).parent, --i) + for (auto h = bc.currentHash(); h != bc.genesisHash() && bc.details(h) && i; h = bc.details(h).parent, --i) { auto d = bc.details(h); auto bm = blockMatch(filter, d, h, bc); @@ -593,7 +834,7 @@ void Main::refreshBlockChain() .arg(render(t.safeSender())) .arg(render(t.receiveAddress)) .arg((unsigned)t.nonce) - .arg(st.addressHasCode(t.receiveAddress) ? '*' : '-') : + .arg(ethereum()->codeAt(t.receiveAddress).size() ? '*' : '-') : QString(" %2 +> %3: %1 [%4]") .arg(formatBalance(t.value).c_str()) .arg(render(t.safeSender())) @@ -614,116 +855,64 @@ void Main::refreshBlockChain() ui->blocks->setCurrentRow(0); } -void Main::refresh(bool _override) +void Main::on_blockChainFilter_textChanged() +{ + static QTimer* s_delayed = nullptr; + if (!s_delayed) + { + s_delayed = new QTimer(this); + s_delayed->setSingleShot(true); + connect(s_delayed, SIGNAL(timeout()), SLOT(refreshBlockChain())); + } + s_delayed->stop(); + s_delayed->start(200); +} + +void Main::on_refresh_triggered() +{ + refreshAll(); +} + +void Main::timerEvent(QTimerEvent*) { // 7/18, Alex: aggregating timers, prelude to better threading? // Runs much faster on slower dual-core processors static int interval = 100; // refresh mining every 200ms - if(interval / 100 % 2 == 0) + if (interval / 100 % 2 == 0) refreshMining(); + if (interval / 100 % 2 == 0 && m_webThree->ethereum()->isSyncing()) + ui->downloadView->update(); + + if (m_logChanged) + { + m_logLock.lock(); + m_logChanged = false; + ui->log->appendPlainText(m_logHistory.mid(0, m_logHistory.length() - 1)); + m_logHistory.clear(); + m_logLock.unlock(); + } + // refresh peer list every 1000ms, reset counter - if(interval == 1000) + if (interval == 1000) { interval = 0; refreshNetwork(); - } else + } + else interval += 100; - - eth::ClientGuard g(m_client.get()); - auto const& st = state(); - - bool c = m_client->changed(); - if (c || _override) - { - changed(); - - updateBlockCount(); - - auto acs = st.addresses(); - ui->accounts->clear(); - ui->contracts->clear(); - for (auto n = 0; n < 2; ++n) - for (auto i: acs) - { - auto r = render(i.first); - if (r.contains('(') == !n) - { - if (n == 0 || ui->showAllAccounts->isChecked()) - (new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(i.second).c_str()).arg(r).arg((unsigned)state().transactionsFrom(i.first)), ui->accounts)) - ->setData(Qt::UserRole, QByteArray((char const*)i.first.data(), Address::size)); - if (st.addressHasCode(i.first)) - (new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(i.second).c_str()).arg(r).arg((unsigned)st.transactionsFrom(i.first)), ui->contracts)) - ->setData(Qt::UserRole, QByteArray((char const*)i.first.data(), Address::size)); - - if (r.contains('(')) - { - // A namereg address - QString s = pretty(i.first); - if (ui->destination->findText(s, Qt::MatchExactly | Qt::MatchCaseSensitive) == -1) - ui->destination->addItem(s); - } - } - } - - for (int i = 0; i < ui->destination->count(); ++i) - if (ui->destination->itemText(i) != "(Create Contract)" && !fromString(ui->destination->itemText(i))) - ui->destination->removeItem(i--); - - ui->transactionQueue->clear(); - for (Transaction const& t: m_client->pending()) - { - QString s = t.receiveAddress ? - QString("%2 %5> %3: %1 [%4]") - .arg(formatBalance(t.value).c_str()) - .arg(render(t.safeSender())) - .arg(render(t.receiveAddress)) - .arg((unsigned)t.nonce) - .arg(st.addressHasCode(t.receiveAddress) ? '*' : '-') : - QString("%2 +> %3: %1 [%4]") - .arg(formatBalance(t.value).c_str()) - .arg(render(t.safeSender())) - .arg(render(right160(sha3(rlpList(t.safeSender(), t.nonce))))) - .arg((unsigned)t.nonce); - ui->transactionQueue->addItem(s); - } - - refreshBlockChain(); - } - - if (c || m_keysChanged || _override) - { - m_keysChanged = false; - ui->ourAccounts->clear(); - u256 totalBalance = 0; - map> altCoins; - Address coinsAddr = right160(st.storage(c_config, 1)); - for (unsigned i = 0; i < st.storage(coinsAddr, 0); ++i) - altCoins[right160(st.storage(coinsAddr, st.storage(coinsAddr, i + 1)))] = make_pair(fromRaw(st.storage(coinsAddr, i + 1)), 0); -// u256 totalGavCoinBalance = 0; - for (auto i: m_myKeys) - { - u256 b = st.balance(i.address()); - (new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(b).c_str()).arg(render(i.address())).arg((unsigned)st.transactionsFrom(i.address())), ui->ourAccounts)) - ->setData(Qt::UserRole, QByteArray((char const*)i.address().data(), Address::size)); - totalBalance += b; - - for (auto& c: altCoins) - c.second.second += (u256)st.storage(c.first, (u160)i.address()); - } + if (m_ethereum) + m_ethereum->poll(); - QString b; - for (auto const& c: altCoins) - if (c.second.second) - b += QString::fromStdString(toString(c.second.second)) + " " + c.second.first.toUpper() + " | "; - ui->balance->setText(b + QString::fromStdString(formatBalance(totalBalance))); - } + for (auto const& i: m_handlers) + if (ethereum()->checkWatch(i.first)) + i.second(); } -string Main::renderDiff(eth::StateDiff const& _d) const +string Main::renderDiff(dev::eth::StateDiff const& _d) const { stringstream s; @@ -732,7 +921,7 @@ string Main::renderDiff(eth::StateDiff const& _d) const { s << "
"; - eth::AccountDiff const& ad = i.second; + dev::eth::AccountDiff const& ad = i.second; s << "" << ad.lead() << " " << " " << render(i.first).toStdString() << ""; if (!ad.exist.to()) continue; @@ -740,12 +929,12 @@ string Main::renderDiff(eth::StateDiff const& _d) const if (ad.balance) { s << "
" << indent << "Balance " << std::dec << formatBalance(ad.balance.to()); - s << " " << std::showpos << (((eth::bigint)ad.balance.to()) - ((eth::bigint)ad.balance.from())) << std::noshowpos << ""; + s << " " << std::showpos << (((dev::bigint)ad.balance.to()) - ((dev::bigint)ad.balance.from())) << std::noshowpos << ""; } if (ad.nonce) { s << "
" << indent << "Count #" << std::dec << ad.nonce.to(); - s << " " << std::showpos << (((eth::bigint)ad.nonce.to()) - ((eth::bigint)ad.nonce.from())) << std::noshowpos << ""; + s << " " << std::showpos << (((dev::bigint)ad.nonce.to()) - ((dev::bigint)ad.nonce.from())) << std::noshowpos << ""; } if (ad.code) { @@ -754,7 +943,7 @@ string Main::renderDiff(eth::StateDiff const& _d) const s << " (" << ad.code.from().size() << " bytes)"; } - for (pair> const& i: ad.storage) + for (pair> const& i: ad.storage) { s << "
"; if (!i.second.from()) @@ -787,13 +976,12 @@ string Main::renderDiff(eth::StateDiff const& _d) const void Main::on_transactionQueue_currentItemChanged() { ui->pendingInfo->clear(); - eth::ClientGuard g(m_client.get()); stringstream s; int i = ui->transactionQueue->currentRow(); - if (i >= 0) + if (i >= 0 && i < (int)ethereum()->pending().size()) { - Transaction tx(m_client->postState().pending()[i]); + Transaction tx(ethereum()->pending()[i]); auto ss = tx.safeSender(); h256 th = sha3(rlpList(ss, tx.nonce)); s << "

" << th << "

"; @@ -814,17 +1002,18 @@ void Main::on_transactionQueue_currentItemChanged() else { if (tx.data.size()) - s << eth::memDump(tx.data, 16, true); + s << dev::memDump(tx.data, 16, true); } s << "
"; // s << "Pre: " << fs.rootHash() << "
"; // s << "Post: " << ts.rootHash() << ""; - s << renderDiff(m_client->postState().pendingDiff(i)); + s << renderDiff(ethereum()->diff(i, 0)); } ui->pendingInfo->setHtml(QString::fromStdString(s.str())); + ui->pendingInfo->moveCursor(QTextCursor::Start); } void Main::ourAccountsRowsMoved() @@ -847,8 +1036,7 @@ void Main::on_inject_triggered() { QString s = QInputDialog::getText(this, "Inject Transaction", "Enter transaction dump in hex"); bytes b = fromHex(s.toStdString()); - m_client->inject(&b); - refresh(); + ethereum()->inject(&b); } void Main::on_blocks_currentItemChanged() @@ -857,14 +1045,13 @@ void Main::on_blocks_currentItemChanged() ui->debugCurrent->setEnabled(false); ui->debugDumpState->setEnabled(false); ui->debugDumpStatePre->setEnabled(false); - eth::ClientGuard g(m_client.get()); if (auto item = ui->blocks->currentItem()) { auto hba = item->data(Qt::UserRole).toByteArray(); assert(hba.size() == 32); auto h = h256((byte const*)hba.data(), h256::ConstructFromPointer); - auto details = m_client->blockChain().details(h); - auto blockData = m_client->blockChain().block(h); + auto details = ethereum()->blockChain().details(h); + auto blockData = ethereum()->blockChain().block(h); auto block = RLP(blockData); BlockInfo info(blockData); @@ -888,7 +1075,7 @@ void Main::on_blocks_currentItemChanged() s << "
Bloom: " << details.bloom << ""; s << "
Transactions: " << block[1].itemCount() << " @" << info.transactionsRoot << ""; s << "
Uncles: " << block[2].itemCount() << " @" << info.sha3Uncles << ""; - s << "
Pre: " << BlockInfo(m_client->blockChain().block(info.parentHash)).stateRoot << ""; + s << "
Pre: " << BlockInfo(ethereum()->blockChain().block(info.parentHash)).stateRoot << ""; for (auto const& i: block[1]) s << "
" << sha3(i[0].data()).abridged() << ": " << i[1].toHash() << " [" << i[2].toInt() << " used]"; s << "
Post: " << info.stateRoot << ""; @@ -922,24 +1109,21 @@ void Main::on_blocks_currentItemChanged() else { if (tx.data.size()) - s << eth::memDump(tx.data, 16, true); + s << dev::memDump(tx.data, 16, true); } - auto st = eth::State(m_client->state().db(), m_client->blockChain(), h); - eth::State before = st.fromPending(txi); - eth::State after = st.fromPending(txi + 1); - s << renderDiff(before.diff(after)); + s << renderDiff(ethereum()->diff(txi, h)); ui->debugCurrent->setEnabled(true); ui->debugDumpState->setEnabled(true); ui->debugDumpStatePre->setEnabled(true); } ui->info->appendHtml(QString::fromStdString(s.str())); + ui->info->moveCursor(QTextCursor::Start); } } void Main::on_debugCurrent_triggered() { - eth::ClientGuard g(m_client.get()); if (auto item = ui->blocks->currentItem()) { auto hba = item->data(Qt::UserRole).toByteArray(); @@ -948,11 +1132,11 @@ void Main::on_debugCurrent_triggered() if (!item->data(Qt::UserRole + 1).isNull()) { - eth::State st(m_client->state().db(), m_client->blockChain(), h); unsigned txi = item->data(Qt::UserRole + 1).toInt(); - m_executiveState = st.fromPending(txi); + m_executiveState = ethereum()->state(txi + 1, h); m_currentExecution = unique_ptr(new Executive(m_executiveState)); - Transaction t = st.pending()[txi]; + Transaction t = m_executiveState.pending()[txi]; + m_executiveState = m_executiveState.fromPending(txi); auto r = t.rlp(); populateDebugger(&r); m_currentExecution.reset(); @@ -962,7 +1146,6 @@ void Main::on_debugCurrent_triggered() void Main::on_debugDumpState_triggered(int _add) { - eth::ClientGuard g(m_client.get()); if (auto item = ui->blocks->currentItem()) { auto hba = item->data(Qt::UserRole).toByteArray(); @@ -975,9 +1158,8 @@ void Main::on_debugDumpState_triggered(int _add) ofstream f(fn.toStdString()); if (f.is_open()) { - eth::State st(m_client->state().db(), m_client->blockChain(), h); unsigned txi = item->data(Qt::UserRole + 1).toInt(); - f << st.fromPending(txi + _add) << endl; + f << ethereum()->state(txi + _add, h) << endl; } } } @@ -988,7 +1170,7 @@ void Main::on_debugDumpStatePre_triggered() on_debugDumpState_triggered(0); } -void Main::populateDebugger(eth::bytesConstRef _r) +void Main::populateDebugger(dev::bytesConstRef _r) { bool done = m_currentExecution->setup(_r); if (!done) @@ -1000,10 +1182,10 @@ void Main::populateDebugger(eth::bytesConstRef _r) bytesConstRef lastData; h256 lastHash; h256 lastDataHash; - auto onOp = [&](uint64_t steps, Instruction inst, unsigned newMemSize, eth::bigint gasCost, void* voidVM, void const* voidExt) + auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, void* voidVM, void const* voidExt) { - eth::VM& vm = *(eth::VM*)voidVM; - eth::ExtVM const& ext = *(eth::ExtVM const*)voidExt; + dev::eth::VM& vm = *(dev::eth::VM*)voidVM; + dev::eth::ExtVM const& ext = *(dev::eth::ExtVM const*)voidExt; if (ext.code != lastExtCode) { lastExtCode = ext.code; @@ -1025,6 +1207,7 @@ void Main::populateDebugger(eth::bytesConstRef _r) m_history.append(WorldState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, vm.gas(), lastHash, lastDataHash, vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels})); }; m_currentExecution->go(onOp); + m_currentExecution->finalize(onOp); initDebugger(); updateDebugger(); } @@ -1033,33 +1216,32 @@ void Main::populateDebugger(eth::bytesConstRef _r) void Main::on_contracts_currentItemChanged() { ui->contractInfo->clear(); - eth::ClientGuard l(&*m_client); if (auto item = ui->contracts->currentItem()) { auto hba = item->data(Qt::UserRole).toByteArray(); assert(hba.size() == 20); - auto h = h160((byte const*)hba.data(), h160::ConstructFromPointer); + auto address = h160((byte const*)hba.data(), h160::ConstructFromPointer); stringstream s; try { - auto storage = state().storage(h); + auto storage = ethereum()->storageAt(address); for (auto const& i: storage) s << "@" << showbase << hex << prettyU256(i.first).toStdString() << "    " << showbase << hex << prettyU256(i.second).toStdString() << "
"; - s << "

Body Code

" << disassemble(state().code(h)); + s << "

Body Code

" << disassemble(ethereum()->codeAt(address)); ui->contractInfo->appendHtml(QString::fromStdString(s.str())); } - catch (eth::InvalidTrie) + catch (dev::eth::InvalidTrie) { ui->contractInfo->appendHtml("Corrupted trie."); } + ui->contractInfo->moveCursor(QTextCursor::Start); } } void Main::on_idealPeers_valueChanged() { - if (m_client->peerServer()) - m_client->peerServer()->setIdealPeerCount(ui->idealPeers->value()); + m_webThree->setIdealPeerCount(ui->idealPeers->value()); } void Main::on_ourAccounts_doubleClicked() @@ -1071,7 +1253,8 @@ void Main::on_ourAccounts_doubleClicked() void Main::on_log_doubleClicked() { - qApp->clipboard()->setText(ui->log->currentItem()->text()); + ui->log->setPlainText(""); + m_logHistory.clear(); } void Main::on_accounts_doubleClicked() @@ -1115,14 +1298,12 @@ void Main::on_data_textChanged() } else { - auto asmcode = eth::compileLLLToAsm(src, false); - auto asmcodeopt = eth::compileLLLToAsm(ui->data->toPlainText().toStdString(), true); - m_data = eth::compileLLL(ui->data->toPlainText().toStdString(), true, &errors); + m_data = dev::eth::compileLLL(src, m_enableOptimizer, &errors); if (errors.size()) { try { - m_data = eth::asBytes(::compile(src)); + m_data = dev::asBytes(::compile(src)); for (auto& i: errors) i = "(LLL " + i + ")"; } @@ -1132,7 +1313,15 @@ void Main::on_data_textChanged() } } else - lll = "

Opt

" + QString::fromStdString(asmcodeopt).toHtmlEscaped() + "

Pre

" + QString::fromStdString(asmcode).toHtmlEscaped() + "
"; + { + auto asmcode = dev::eth::compileLLLToAsm(src, false); + lll = "

Pre

" + QString::fromStdString(asmcode).toHtmlEscaped() + "
"; + if (m_enableOptimizer) + { + asmcode = dev::eth::compileLLLToAsm(src, true); + lll = "

Opt

" + QString::fromStdString(asmcode).toHtmlEscaped() + "
" + lll; + } + } } QString errs; if (errors.size()) @@ -1142,7 +1331,7 @@ void Main::on_data_textChanged() errs.append("
" + QString::fromStdString(i).toHtmlEscaped() + "
"); } ui->code->setHtml(errs + lll + "

Code

" + QString::fromStdString(disassemble(m_data)).toHtmlEscaped()); - ui->gas->setMinimum((qint64)state().createGas(m_data.size(), 0)); + ui->gas->setMinimum((qint64)Client::txGas(m_data.size(), 0)); if (!ui->gas->isEnabled()) ui->gas->setValue(m_backupGas); ui->gas->setEnabled(true); @@ -1153,8 +1342,9 @@ void Main::on_data_textChanged() QString s = ui->data->toPlainText(); while (s.size()) { - QRegExp r("(@|\\$)?\"([^\"]*)\"(.*)"); - QRegExp h("(@|\\$)?(0x)?(([a-fA-F0-9])+)(.*)"); + QRegExp r("(@|\\$)?\"([^\"]*)\"(\\s.*)?"); + QRegExp d("(@|\\$)?([0-9]+)(\\s*(ether)|(finney)|(szabo))?(\\s.*)?"); + QRegExp h("(@|\\$)?(0x)?(([a-fA-F0-9])+)(\\s.*)?"); if (r.exactMatch(s)) { for (auto i: r.cap(2)) @@ -1166,6 +1356,23 @@ void Main::on_data_textChanged() m_data.push_back(0); s = r.cap(3); } + else if (d.exactMatch(s)) + { + u256 v(d.cap(2).toStdString()); + if (d.cap(6) == "szabo") + v *= dev::eth::szabo; + else if (d.cap(5) == "finney") + v *= dev::eth::finney; + else if (d.cap(4) == "ether") + v *= dev::eth::ether; + bytes bs = dev::toCompactBigEndian(v); + if (d.cap(1) != "$") + for (auto i = bs.size(); i < 32; ++i) + m_data.push_back(0); + for (auto b: bs) + m_data.push_back(b); + s = d.cap(7); + } else if (h.exactMatch(s)) { bytes bs = fromHex((((h.cap(3).size() & 1) ? "0" : "") + h.cap(3)).toStdString()); @@ -1179,10 +1386,10 @@ void Main::on_data_textChanged() else s = s.mid(1); } - ui->code->setHtml(QString::fromStdString(eth::memDump(m_data, 8, true))); - if (m_client->postState().addressHasCode(fromString(ui->destination->currentText()))) + ui->code->setHtml(QString::fromStdString(dev::memDump(m_data, 8, true))); + if (ethereum()->codeAt(fromString(ui->destination->currentText()), 0).size()) { - ui->gas->setMinimum((qint64)state().callGas(m_data.size(), 1)); + ui->gas->setMinimum((qint64)Client::txGas(m_data.size(), 1)); if (!ui->gas->isEnabled()) ui->gas->setValue(m_backupGas); ui->gas->setEnabled(true); @@ -1191,7 +1398,7 @@ void Main::on_data_textChanged() { if (ui->gas->isEnabled()) m_backupGas = ui->gas->value(); - ui->gas->setValue((qint64)state().callGas(m_data.size())); + ui->gas->setValue((qint64)Client::txGas(m_data.size())); ui->gas->setEnabled(false); } } @@ -1203,10 +1410,12 @@ void Main::on_killBlockchain_triggered() writeSettings(); ui->mine->setChecked(false); ui->net->setChecked(false); - m_client.reset(); - m_client.reset(new Client("AlethZero", Address(), string(), true)); - m_client->start(); - readSettings(); + web3()->stopNetwork(); + ethereum()->killChain(); + m_ethereum->setClient(ethereum()); + readSettings(true); + installWatches(); + refreshAll(); } bool Main::isCreation() const @@ -1246,7 +1455,7 @@ void Main::updateFee() bool ok = false; for (auto i: m_myKeys) - if (state().balance(i.address()) >= totalReq) + if (ethereum()->balanceAt(i.address()) >= totalReq) { ok = true; break; @@ -1261,19 +1470,28 @@ void Main::on_net_triggered() { ui->port->setEnabled(!ui->net->isChecked()); ui->clientName->setEnabled(!ui->net->isChecked()); - string n = string("AlethZero/v") + eth::EthVersion; + string n = string("AlethZero/v") + dev::Version; if (ui->clientName->text().size()) n += "/" + ui->clientName->text().toStdString(); - n += "/" ETH_QUOTED(ETH_BUILD_TYPE) "/" ETH_QUOTED(ETH_BUILD_PLATFORM); - m_client->setClientVersion(n); + n += "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM); + web3()->setClientVersion(n); if (ui->net->isChecked()) { - m_client->startNetwork(ui->port->value(), string(), 0, NodeMode::Full, ui->idealPeers->value(), ui->forceAddress->text().toStdString(), ui->upnp->isChecked()); + // TODO: alter network stuff? + //ui->port->value(), string(), 0, NodeMode::Full, ui->idealPeers->value(), ui->forceAddress->text().toStdString(), ui->upnp->isChecked(), m_privateChain.size() ? sha3(m_privateChain.toStdString()) : 0 + web3()->setIdealPeerCount(ui->idealPeers->value()); + web3()->setNetworkPreferences(netPrefs()); + ethereum()->setNetworkId(m_privateChain.size() ? sha3(m_privateChain.toStdString()) : 0); + web3()->startNetwork(); + ui->downloadView->setDownloadMan(ethereum()->downloadMan()); if (m_peers.size() && ui->usePast->isChecked()) - m_client->peerServer()->restorePeers(bytesConstRef((byte*)m_peers.data(), m_peers.size())); + web3()->restorePeers(bytesConstRef((byte*)m_peers.data(), m_peers.size())); } else - m_client->stopNetwork(); + { + ui->downloadView->setDownloadMan(nullptr); + web3()->stopNetwork(); + } } void Main::on_connect_triggered() @@ -1289,7 +1507,7 @@ void Main::on_connect_triggered() { string host = s.section(":", 0, 0).toStdString(); unsigned short port = s.section(":", 1).toInt(); - m_client->connect(host, port); + web3()->connect(host, port); } } @@ -1303,27 +1521,25 @@ void Main::on_mine_triggered() { if (ui->mine->isChecked()) { - m_client->setAddress(m_myKeys.last().address()); - m_client->startMining(); + ethereum()->setAddress(m_myKeys.last().address()); + ethereum()->startMining(); } else - m_client->stopMining(); + ethereum()->stopMining(); } void Main::on_send_clicked() { u256 totalReq = value() + fee(); - eth::ClientGuard l(&*m_client); for (auto i: m_myKeys) - if (m_client->postState().balance(i.address()) >= totalReq) + if (ethereum()->balanceAt(i.address(), 0) >= totalReq) { debugFinished(); Secret s = i.secret(); if (isCreation()) - m_client->transact(s, value(), m_data, ui->gas->value(), gasPrice()); + ethereum()->transact(s, value(), m_data, ui->gas->value(), gasPrice()); else - m_client->transact(s, value(), fromString(ui->destination->currentText()), m_data, ui->gas->value(), gasPrice()); - refresh(); + ethereum()->transact(s, value(), fromString(ui->destination->currentText()), m_data, ui->gas->value(), gasPrice()); return; } statusBar()->showMessage("Couldn't make transaction: no single account contains at least the required amount."); @@ -1335,15 +1551,14 @@ void Main::on_debug_clicked() try { u256 totalReq = value() + fee(); - eth::ClientGuard l(&*m_client); for (auto i: m_myKeys) - if (m_client->state().balance(i.address()) >= totalReq) + if (ethereum()->balanceAt(i.address()) >= totalReq) { Secret s = i.secret(); - m_executiveState = state(); + m_executiveState = ethereum()->postState(); m_currentExecution = unique_ptr(new Executive(m_executiveState)); Transaction t; - t.nonce = m_executiveState.transactionsFrom(toAddress(s)); + t.nonce = m_executiveState.transactionsFrom(dev::toAddress(s)); t.value = value(); t.gasPrice = gasPrice(); t.gas = ui->gas->value(); @@ -1357,7 +1572,7 @@ void Main::on_debug_clicked() } statusBar()->showMessage("Couldn't make transaction: no single account contains at least the required amount."); } - catch (eth::Exception const& _e) + catch (dev::Exception const& _e) { statusBar()->showMessage("Error running transaction: " + QString::fromStdString(_e.description())); } @@ -1371,15 +1586,17 @@ void Main::on_create_triggered() void Main::on_debugStep_triggered() { - auto l = m_history[ui->debugTimeline->value()].levels.size(); - if (ui->debugTimeline->value() < m_history.size() && m_history[ui->debugTimeline->value() + 1].levels.size() > l) - { - on_debugStepInto_triggered(); - if (m_history[ui->debugTimeline->value()].levels.size() > l) - on_debugStepOut_triggered(); + if (ui->debugTimeline->value() < m_history.size()) { + auto l = m_history[ui->debugTimeline->value()].levels.size(); + if ((ui->debugTimeline->value() + 1) < m_history.size() && m_history[ui->debugTimeline->value() + 1].levels.size() > l) + { + on_debugStepInto_triggered(); + if (m_history[ui->debugTimeline->value()].levels.size() > l) + on_debugStepOut_triggered(); + } + else + on_debugStepInto_triggered(); } - else - on_debugStepInto_triggered(); } void Main::on_debugStepInto_triggered() @@ -1437,7 +1654,7 @@ void Main::on_dumpTrace_triggered() ofstream f(fn.toStdString()); if (f.is_open()) for (WorldState const& ws: m_history) - f << ws.cur << " " << hex << toHex(eth::toCompactBigEndian(ws.curPC, 1)) << " " << hex << toHex(eth::toCompactBigEndian((int)(byte)ws.inst, 1)) << " " << hex << toHex(eth::toCompactBigEndian((uint64_t)ws.gas, 1)) << endl; + f << ws.cur << " " << hex << toHex(dev::toCompactBigEndian(ws.curPC, 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)ws.inst, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)ws.gas, 1)) << endl; } void Main::on_dumpTracePretty_triggered() @@ -1450,11 +1667,11 @@ void Main::on_dumpTracePretty_triggered() f << endl << " STACK" << endl; for (auto i: ws.stack) f << (h256)i << endl; - f << " MEMORY" << endl << eth::memDump(ws.memory); + f << " MEMORY" << endl << dev::memDump(ws.memory); f << " STORAGE" << endl; for (auto const& i: ws.storage) f << showbase << hex << i.first << ": " << i.second << endl; - f << dec << ws.levels.size() << " | " << ws.cur << " | #" << ws.steps << " | " << hex << setw(4) << setfill('0') << ws.curPC << " : " << c_instructionInfo.at(ws.inst).name << " | " << dec << ws.gas << " | -" << dec << ws.gasCost << " | " << ws.newMemSize << "x32"; + f << dec << ws.levels.size() << " | " << ws.cur << " | #" << ws.steps << " | " << hex << setw(4) << setfill('0') << ws.curPC << " : " << dev::eth::instructionInfo(ws.inst).name << " | " << dec << ws.gas << " | -" << dec << ws.gasCost << " | " << ws.newMemSize << "x32"; } } @@ -1467,8 +1684,8 @@ void Main::on_dumpTraceStorage_triggered() { if (ws.inst == Instruction::STOP || ws.inst == Instruction::RETURN || ws.inst == Instruction::SUICIDE) for (auto i: ws.storage) - f << toHex(eth::toCompactBigEndian(i.first, 1)) << " " << toHex(eth::toCompactBigEndian(i.second, 1)) << endl; - f << ws.cur << " " << hex << toHex(eth::toCompactBigEndian(ws.curPC, 1)) << " " << hex << toHex(eth::toCompactBigEndian((int)(byte)ws.inst, 1)) << " " << hex << toHex(eth::toCompactBigEndian((uint64_t)ws.gas, 1)) << endl; + f << toHex(dev::toCompactBigEndian(i.first, 1)) << " " << toHex(dev::toCompactBigEndian(i.second, 1)) << endl; + f << ws.cur << " " << hex << toHex(dev::toCompactBigEndian(ws.curPC, 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)ws.inst, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)ws.gas, 1)) << endl; } } @@ -1526,16 +1743,16 @@ void Main::on_debugTimeline_valueChanged() updateDebugger(); } -QString Main::prettyU256(eth::u256 _n) const +QString Main::prettyU256(dev::u256 _n) const { unsigned inc = 0; QString raw; ostringstream s; if (!(_n >> 64)) - s << "0x" << (uint64_t)_n << ""; + s << "" << (uint64_t)_n << " (0x" << hex << (uint64_t)_n << ")"; else if (!~(_n >> 64)) - s << "0x" << (int64_t)_n << ""; - else if (_n >> 200 == 0) + s << "" << (int64_t)_n << " (0x" << hex << (int64_t)_n << ")"; + else if ((_n >> 200) == 0) { Address a = right160(_n); QString n = pretty(a); @@ -1570,7 +1787,7 @@ void Main::updateDebugger() bytes out(size, 0); for (; o < size && from + o < ws.memory.size(); ++o) out[o] = ws.memory[from + o]; - ui->debugMemory->setHtml("

RETURN

" + QString::fromStdString(eth::memDump(out, 16, true))); + ui->debugMemory->setHtml("

RETURN

" + QString::fromStdString(dev::memDump(out, 16, true))); } else if (ws.inst == Instruction::STOP) ui->debugMemory->setHtml("

STOP

"); @@ -1580,7 +1797,7 @@ void Main::updateDebugger() ui->debugMemory->setHtml("

EXCEPTION

"); ostringstream ss; - ss << dec << "EXIT | GAS: " << dec << max(0, (eth::bigint)ws.gas - ws.gasCost); + ss << dec << "EXIT | GAS: " << dec << max(0, (dev::bigint)ws.gas - ws.gasCost); ui->debugStateInfo->setText(QString::fromStdString(ss.str())); ui->debugStorage->setHtml(""); ui->debugCallData->setHtml(""); @@ -1603,7 +1820,7 @@ void Main::updateDebugger() ostringstream out; out << s.cur.abridged(); if (i) - out << " " << c_instructionInfo.at(s.inst).name << " @0x" << hex << s.curPC; + out << " " << instructionInfo(s.inst).name << " @0x" << hex << s.curPC; ui->callStack->addItem(QString::fromStdString(out.str())); } } @@ -1619,7 +1836,7 @@ void Main::updateDebugger() byte b = i < code.size() ? code[i] : 0; try { - QString s = c_instructionInfo.at((Instruction)b).name; + QString s = QString::fromStdString(instructionInfo((Instruction)b).name); ostringstream out; out << hex << setw(4) << setfill('0') << i; m_pcWarp[i] = dc->count(); @@ -1645,7 +1862,7 @@ void Main::updateDebugger() if (ws.callData) { assert(m_codes.count(ws.callData)); - ui->debugCallData->setHtml(QString::fromStdString(eth::memDump(m_codes[ws.callData], 16, true))); + ui->debugCallData->setHtml(QString::fromStdString(dev::memDump(m_codes[ws.callData], 16, true))); } else ui->debugCallData->setHtml(""); @@ -1655,7 +1872,7 @@ void Main::updateDebugger() for (auto i: ws.stack) stack.prepend("
" + prettyU256(i) + "
"); ui->debugStack->setHtml(stack); - ui->debugMemory->setHtml(QString::fromStdString(eth::memDump(ws.memory, 16, true))); + ui->debugMemory->setHtml(QString::fromStdString(dev::memDump(ws.memory, 16, true))); assert(m_codes.count(ws.code)); if (m_codes[ws.code].size() >= (unsigned)ws.curPC) @@ -1669,7 +1886,7 @@ void Main::updateDebugger() cwarn << "PC (" << (unsigned)ws.curPC << ") is after code range (" << m_codes[ws.code].size() << ")"; ostringstream ss; - ss << dec << "STEP: " << ws.steps << " | PC: 0x" << hex << ws.curPC << " : " << c_instructionInfo.at(ws.inst).name << " | ADDMEM: " << dec << ws.newMemSize << " words | COST: " << dec << ws.gasCost << " | GAS: " << dec << ws.gas; + ss << dec << "STEP: " << ws.steps << " | PC: 0x" << hex << ws.curPC << " : " << dev::eth::instructionInfo(ws.inst).name << " | ADDMEM: " << dec << ws.newMemSize << " words | COST: " << dec << ws.gasCost << " | GAS: " << dec << ws.gas; ui->debugStateInfo->setText(QString::fromStdString(ss.str())); stringstream s; for (auto const& i: ws.storage) @@ -1689,4 +1906,7 @@ void Main::updateDebugger() #include\ "moc_MiningView.cpp" +#include\ +"moc_DownloadView.cpp" + #endif diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index 99b337a20..ce1a9b670 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -27,36 +27,38 @@ #include #include #include -#include +#include #include #include #include +#include namespace Ui { class Main; } -namespace eth { +namespace dev { namespace eth { class Client; class State; -} +class MessageFilter; +}} class QQuickView; struct WorldState { uint64_t steps; - eth::Address cur; - eth::u256 curPC; - eth::Instruction inst; - unsigned newMemSize; - eth::u256 gas; - eth::h256 code; - eth::h256 callData; - eth::u256s stack; - eth::bytes memory; - eth::bigint gasCost; - std::map storage; + dev::Address cur; + dev::u256 curPC; + dev::eth::Instruction inst; + dev::bigint newMemSize; + dev::u256 gas; + dev::h256 code; + dev::h256 callData; + dev::u256s stack; + dev::bytes memory; + dev::bigint gasCost; + std::map storage; std::vector levels; }; @@ -68,9 +70,11 @@ public: explicit Main(QWidget *parent = 0); ~Main(); - eth::Client* client() { return m_client.get(); } + dev::WebThreeDirect* web3() const { return m_webThree.get(); } + dev::eth::Client* ethereum() const { return m_webThree->ethereum(); } + dev::shh::WhisperHost* whisper() const { return m_webThree->whisper(); } - QList const& owned() const { return m_myKeys; } + QList const& owned() const { return m_myKeys; } public slots: void load(QString _file); @@ -78,6 +82,8 @@ public slots: void debug(QString _entry); void warn(QString _entry); + void onKeysChanged(); + private slots: void eval(QString const& _js); @@ -106,7 +112,7 @@ private slots: void on_about_triggered(); void on_paranoia_triggered(); void on_nameReg_textChanged(); - void on_preview_triggered() { refresh(true); } + void on_preview_triggered(); void on_quit_triggered() { close(); } void on_urlEdit_returnPressed(); void on_debugStep_triggered(); @@ -118,11 +124,11 @@ private slots: void on_importKey_triggered(); void on_exportKey_triggered(); void on_inject_triggered(); - void on_showAll_triggered() { refresh(true); } - void on_showAllAccounts_triggered() { refresh(true); } + void on_showAll_triggered() { refreshBlockChain(); } + void on_showAllAccounts_triggered() { refreshAccounts(); } void on_loadJS_triggered(); void on_blockChainFilter_textChanged(); - void on_clearPending_triggered(); + void on_forceMining_triggered(); void on_dumpTrace_triggered(); void on_dumpTraceStorage_triggered(); void on_dumpTracePretty_triggered(); @@ -134,73 +140,105 @@ private slots: void on_debugCurrent_triggered(); void on_debugDumpState_triggered(int _add = 1); void on_debugDumpStatePre_triggered(); - - void refresh(bool _override = false); - void refreshNetwork(); - void refreshMining(); - void refreshBlockChain(); + void on_refresh_triggered(); + void on_usePrivate_triggered(); + void on_enableOptimizer_triggered(); + void on_turboMining_triggered(); signals: - void changed(); // TODO: manifest + void poll(); private: - void updateBlockCount(); + dev::p2p::NetworkPreferences netPrefs() const; + + QString pretty(dev::Address _a) const; + QString prettyU256(dev::u256 _n) const; - QString pretty(eth::Address _a) const; - QString prettyU256(eth::u256 _n) const; + QString lookup(QString const& _n) const; - void populateDebugger(eth::bytesConstRef r); + void populateDebugger(dev::bytesConstRef r); void initDebugger(); void updateDebugger(); void debugFinished(); - QString render(eth::Address _a) const; - eth::Address fromString(QString const& _a) const; - std::string renderDiff(eth::StateDiff const& _d) const; + QString render(dev::Address _a) const; + dev::Address fromString(QString const& _a) const; + std::string renderDiff(dev::eth::StateDiff const& _d) const; void alterDebugStateGroup(bool _enable) const; - eth::State const& state() const; - void updateFee(); - void readSettings(); + void readSettings(bool _skipGeometry = false); void writeSettings(); bool isCreation() const; - eth::u256 fee() const; - eth::u256 total() const; - eth::u256 value() const; - eth::u256 gasPrice() const; + dev::u256 fee() const; + dev::u256 total() const; + dev::u256 value() const; + dev::u256 gasPrice() const; + + unsigned installWatch(dev::eth::MessageFilter const& _tf, std::function const& _f); + unsigned installWatch(dev::h256 _tf, std::function const& _f); + + void onNewPending(); + void onNewBlock(); + void onNameRegChange(); + void onCurrenciesChange(); + void onBalancesChange(); + + void installWatches(); + void installCurrenciesWatch(); + void installNameRegWatch(); + void installBalancesWatch(); + + virtual void timerEvent(QTimerEvent*); + + void refreshNetwork(); + void refreshMining(); + + void refreshAll(); + void refreshPending(); + void refreshAccounts(); + void refreshDestination(); + void refreshBlockChain(); + void refreshBlockCount(); + void refreshBalances(); std::unique_ptr ui; - std::unique_ptr m_client; + std::unique_ptr m_webThree; + + std::map> m_handlers; + unsigned m_nameRegFilter = (unsigned)-1; + unsigned m_currenciesFilter = (unsigned)-1; + unsigned m_balancesFilter = (unsigned)-1; QByteArray m_peers; - QMutex m_guiLock; - QTimer* m_refresh; - QTimer* m_refreshNetwork; - QTimer* m_refreshMining; QStringList m_servers; - QList m_myKeys; + QList m_myKeys; + QString m_privateChain; bool m_keysChanged = false; - eth::bytes m_data; - eth::Address m_nameReg; + dev::bytes m_data; + dev::Address m_nameReg; unsigned m_backupGas; - eth::State m_executiveState; - std::unique_ptr m_currentExecution; - eth::h256 m_lastCode; - eth::h256 m_lastData; + dev::eth::State m_executiveState; + std::unique_ptr m_currentExecution; + dev::h256 m_lastCode; + dev::h256 m_lastData; std::vector m_lastLevels; QMap m_pcWarp; QList m_history; - std::map m_codes; // and pcWarps + std::map m_codes; // and pcWarps + bool m_enableOptimizer = true; QNetworkAccessManager m_webCtrl; QList> m_consoleHistory; + QMutex m_logLock; + QString m_logHistory; + bool m_logChanged = true; - QEthereum* m_ethereum; + QEthereum* m_ethereum = nullptr; }; diff --git a/alethzero/MiningView.cpp b/alethzero/MiningView.cpp index f09fe425f..fa6da737b 100644 --- a/alethzero/MiningView.cpp +++ b/alethzero/MiningView.cpp @@ -29,17 +29,18 @@ using namespace std; using namespace lb; -// do *not* use eth since eth::uint conflicts with Qt's global unit definition -// using namespace eth; +// do *not* use eth since unsigned conflicts with Qt's global unit definition +// using namespace dev; +using namespace dev::eth; // types -using eth::MineInfo; -using eth::MineProgress; +using dev::eth::MineInfo; +using dev::eth::MineProgress; // functions -using eth::toString; -using eth::trimFront; +using dev::toString; +using dev::trimFront; string id(float _y) { return toString(_y); } string s(float _x){ return toString(round(_x * 1000) / 1000) + (!_x ? "s" : ""); } diff --git a/alethzero/MiningView.h b/alethzero/MiningView.h index 1b4a91b9a..8f3135f75 100644 --- a/alethzero/MiningView.h +++ b/alethzero/MiningView.h @@ -27,12 +27,13 @@ #include #include +#ifndef Q_MOC_RUN #include +#endif -namespace eth -{ +namespace dev { namespace eth { struct MineInfo; -} +}} class MiningView: public QWidget { @@ -41,14 +42,14 @@ class MiningView: public QWidget public: MiningView(QWidget* _p = nullptr); - void appendStats(std::list const& _l, eth::MineProgress const& _p); + void appendStats(std::list const& _l, dev::eth::MineProgress const& _p); void resetStats(); protected: virtual void paintEvent(QPaintEvent*); private: - eth::MineProgress m_progress; + dev::eth::MineProgress m_progress; unsigned m_duration = 300; std::vector m_values; std::vector m_bests; diff --git a/eth/CMakeLists.txt b/eth/CMakeLists.txt index 93946aa8f..eee1d258b 100644 --- a/eth/CMakeLists.txt +++ b/eth/CMakeLists.txt @@ -4,13 +4,13 @@ aux_source_directory(. SRC_LIST) include_directories(..) link_directories(../libethcore) -link_directories(../libethereum) +link_directories(../libwebthree) set(EXECUTABLE eth) add_executable(${EXECUTABLE} ${SRC_LIST}) -target_link_libraries(${EXECUTABLE} ethereum) +target_link_libraries(${EXECUTABLE} webthree) target_link_libraries(${EXECUTABLE} secp256k1) target_link_libraries(${EXECUTABLE} gmp) if(MINIUPNPC_LS) diff --git a/eth/CommonJS.cpp b/eth/CommonJS.cpp index e4c60a120..57958a117 100644 --- a/eth/CommonJS.cpp +++ b/eth/CommonJS.cpp @@ -22,9 +22,10 @@ #include "CommonJS.h" using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; -bytes eth::jsToBytes(string const& _s) +bytes dev::eth::jsToBytes(string const& _s) { if (_s.substr(0, 2) == "0x") // Hex @@ -37,7 +38,7 @@ bytes eth::jsToBytes(string const& _s) return asBytes(_s); } -string eth::jsPadded(string const& _s, unsigned _l, unsigned _r) +string dev::eth::jsPadded(string const& _s, unsigned _l, unsigned _r) { bytes b = jsToBytes(_s); while (b.size() < _l) @@ -47,7 +48,7 @@ string eth::jsPadded(string const& _s, unsigned _l, unsigned _r) return asString(b).substr(b.size() - max(_l, _r)); } -string eth::jsPadded(string const& _s, unsigned _l) +string dev::eth::jsPadded(string const& _s, unsigned _l) { if (_s.substr(0, 2) == "0x" || _s.find_first_not_of("0123456789") == string::npos) // Numeric: pad to right @@ -57,7 +58,7 @@ string eth::jsPadded(string const& _s, unsigned _l) return jsPadded(_s, 0, _l); } -string eth::jsUnpadded(string _s) +string dev::eth::jsUnpadded(string _s) { auto p = _s.find_last_not_of((char)0); _s.resize(p == string::npos ? 0 : (p + 1)); diff --git a/eth/CommonJS.h b/eth/CommonJS.h index 57f2db39f..caa7e6651 100644 --- a/eth/CommonJS.h +++ b/eth/CommonJS.h @@ -23,66 +23,69 @@ #pragma once #include -#include -#include -#include -#include +#include +#include +#include +#include #include +namespace dev +{ namespace eth { -eth::bytes jsToBytes(std::string const& _s); +bytes jsToBytes(std::string const& _s); std::string jsPadded(std::string const& _s, unsigned _l, unsigned _r); std::string jsPadded(std::string const& _s, unsigned _l); std::string jsUnpadded(std::string _s); -template eth::FixedHash jsToFixed(std::string const& _s) +template FixedHash jsToFixed(std::string const& _s) { if (_s.substr(0, 2) == "0x") // Hex - return eth::FixedHash(_s.substr(2)); + return FixedHash(_s.substr(2)); else if (_s.find_first_not_of("0123456789") == std::string::npos) // Decimal - return (typename eth::FixedHash::Arith)(_s); + return (typename FixedHash::Arith)(_s); else // Binary - return eth::FixedHash(asBytes(jsPadded(_s, N))); + return FixedHash(asBytes(jsPadded(_s, N))); } template boost::multiprecision::number> jsToInt(std::string const& _s) { if (_s.substr(0, 2) == "0x") // Hex - return eth::fromBigEndian>>(eth::fromHex(_s.substr(2))); + return fromBigEndian>>(fromHex(_s.substr(2))); else if (_s.find_first_not_of("0123456789") == std::string::npos) // Decimal return boost::multiprecision::number>(_s); else // Binary - return eth::fromBigEndian>>(asBytes(jsPadded(_s, N))); + return fromBigEndian>>(asBytes(jsPadded(_s, N))); } -inline eth::Address jsToAddress(std::string const& _s) { return jsToFixed<20>(_s); } -inline eth::Secret jsToSecret(std::string const& _s) { return jsToFixed<32>(_s); } -inline eth::u256 jsToU256(std::string const& _s) { return jsToInt<32>(_s); } +inline Address jsToAddress(std::string const& _s) { return jsToFixed<20>(_s); } +inline Secret jsToSecret(std::string const& _s) { return jsToFixed<32>(_s); } +inline u256 jsToU256(std::string const& _s) { return jsToInt<32>(_s); } -template std::string toJS(eth::FixedHash const& _h) { return "0x" + toHex(_h.ref()); } -template std::string toJS(boost::multiprecision::number> const& _n) { return "0x" + eth::toHex(eth::toCompactBigEndian(_n)); } +template std::string toJS(FixedHash const& _h) { return "0x" + toHex(_h.ref()); } +template std::string toJS(boost::multiprecision::number> const& _n) { return "0x" + toHex(toCompactBigEndian(_n)); } inline std::string jsToBinary(std::string const& _s) { - return eth::asString(jsToBytes(_s)); + return asString(jsToBytes(_s)); } inline std::string jsToDecimal(std::string const& _s) { - return eth::toString(jsToU256(_s)); + return toString(jsToU256(_s)); } inline std::string jsToHex(std::string const& _s) { - return "0x" + eth::toHex(asBytes(_s)); + return "0x" + toHex(asBytes(_s)); } } +} diff --git a/eth/EthStubServer.cpp b/eth/EthStubServer.cpp index 0d5182eb8..ad6a87781 100644 --- a/eth/EthStubServer.cpp +++ b/eth/EthStubServer.cpp @@ -25,13 +25,15 @@ #include #include #include +#include #include "CommonJS.h" using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; -EthStubServer::EthStubServer(jsonrpc::AbstractServerConnector* _conn, Client& _client): +EthStubServer::EthStubServer(jsonrpc::AbstractServerConnector* _conn, WebThreeDirect& _web3): AbstractEthStubServer(_conn), - m_client(_client) + m_web3(_web3) { } @@ -58,40 +60,43 @@ Json::Value EthStubServer::procedures() return ret; } +dev::eth::Client& EthStubServer::ethereum() const +{ + return *m_web3.ethereum(); +} + std::string EthStubServer::coinbase() { - ClientGuard g(&m_client); - return toJS(m_client.address()); + return toJS(ethereum().address()); } std::string EthStubServer::balanceAt(std::string const& _a) { - ClientGuard g(&m_client); - return toJS(m_client.postState().balance(jsToAddress(_a))); + return toJS(ethereum().balanceAt(jsToAddress(_a), 0)); } Json::Value EthStubServer::check(Json::Value const& _as) { - if (m_client.changed()) + // TODO +// if (ethereum().changed()) return _as; - else +/* else { Json::Value ret; ret.resize(0); return ret; - } + }*/ } std::string EthStubServer::create(const std::string& _bCode, const std::string& _sec, const std::string& _xEndowment, const std::string& _xGas, const std::string& _xGasPrice) { - ClientGuard g(&m_client); - Address ret = m_client.transact(jsToSecret(_sec), jsToU256(_xEndowment), jsToBytes(_bCode), jsToU256(_xGas), jsToU256(_xGasPrice)); + Address ret = ethereum().transact(jsToSecret(_sec), jsToU256(_xEndowment), jsToBytes(_bCode), jsToU256(_xGas), jsToU256(_xGasPrice)); return toJS(ret); } std::string EthStubServer::lll(const std::string& _s) { - return "0x" + toHex(eth::compileLLL(_s)); + return "0x" + toHex(dev::eth::compileLLL(_s)); } std::string EthStubServer::gasPrice() @@ -101,18 +106,17 @@ std::string EthStubServer::gasPrice() bool EthStubServer::isContractAt(const std::string& _a) { - ClientGuard g(&m_client); - return m_client.postState().addressHasCode(jsToAddress(_a)); + return ethereum().codeAt(jsToAddress(_a), 0).size(); } bool EthStubServer::isListening() { - return m_client.haveNetwork(); + return m_web3.haveNetwork(); } bool EthStubServer::isMining() { - return m_client.isMining(); + return ethereum().isMining(); } std::string EthStubServer::key() @@ -132,27 +136,28 @@ Json::Value EthStubServer::keys() int EthStubServer::peerCount() { - ClientGuard g(&m_client); - return m_client.peerCount(); + return m_web3.peerCount(); } std::string EthStubServer::storageAt(const std::string& _a, const std::string& x) { - ClientGuard g(&m_client); - return toJS(m_client.postState().storage(jsToAddress(_a), jsToU256(x))); + return toJS(ethereum().stateAt(jsToAddress(_a), jsToU256(x), 0)); +} + +std::string EthStubServer::stateAt(const std::string& _a, const std::string& x, const std::string& s) +{ + return toJS(ethereum().stateAt(jsToAddress(_a), jsToU256(x), std::atol(s.c_str()))); } Json::Value EthStubServer::transact(const std::string& _aDest, const std::string& _bData, const std::string& _sec, const std::string& _xGas, const std::string& _xGasPrice, const std::string& _xValue) { - ClientGuard g(&m_client); - m_client.transact(jsToSecret(_sec), jsToU256(_xValue), jsToAddress(_aDest), jsToBytes(_bData), jsToU256(_xGas), jsToU256(_xGasPrice)); + ethereum().transact(jsToSecret(_sec), jsToU256(_xValue), jsToAddress(_aDest), jsToBytes(_bData), jsToU256(_xGas), jsToU256(_xGasPrice)); return Json::Value(); } std::string EthStubServer::txCountAt(const std::string& _a) { - ClientGuard g(&m_client); - return toJS(m_client.postState().transactionsFrom(jsToAddress(_a))); + return toJS(ethereum().countAt(jsToAddress(_a), 0)); } std::string EthStubServer::secretToAddress(const std::string& _a) @@ -173,7 +178,7 @@ Json::Value EthStubServer::block(const std::string& _hash) Json::Value EthStubServer::blockJson(const std::string& _hash) { Json::Value res; - auto const& bc = m_client.blockChain(); + auto const& bc = ethereum().blockChain(); auto b = _hash.length() ? bc.block(h256(_hash)) : bc.block(); diff --git a/eth/EthStubServer.h b/eth/EthStubServer.h index bd7041d3d..469abed07 100644 --- a/eth/EthStubServer.h +++ b/eth/EthStubServer.h @@ -23,18 +23,18 @@ #include #include +#include #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #include "abstractethstubserver.h" #pragma GCC diagnostic pop -namespace eth { class Client; } -namespace eth { class KeyPair; } +namespace dev { class WebThreeDirect; namespace eth { class Client; } class KeyPair; } class EthStubServer: public AbstractEthStubServer { public: - EthStubServer(jsonrpc::AbstractServerConnector* _conn, eth::Client& _client); + EthStubServer(jsonrpc::AbstractServerConnector* _conn, dev::WebThreeDirect& _web3); virtual Json::Value procedures(); virtual std::string balanceAt(std::string const& _a); @@ -49,16 +49,18 @@ public: virtual Json::Value keys(); virtual int peerCount(); virtual std::string storageAt(const std::string& a, const std::string& x); + virtual std::string stateAt(const std::string& a, const std::string& x, const std::string& s); virtual Json::Value transact(const std::string& aDest, const std::string& bData, const std::string& sec, const std::string& xGas, const std::string& xGasPrice, const std::string& xValue); virtual std::string txCountAt(const std::string& a); virtual std::string secretToAddress(const std::string& a); virtual Json::Value lastBlock(); virtual std::string lll(const std::string& s); virtual Json::Value block(const std::string&); - void setKeys(std::vector _keys) { m_keys = _keys; } + void setKeys(std::vector _keys) { m_keys = _keys; } private: - eth::Client& m_client; - std::vector m_keys; + dev::eth::Client& ethereum() const; + dev::WebThreeDirect& m_web3; + std::vector m_keys; Json::Value jsontypeToValue(int); Json::Value blockJson(const std::string&); }; diff --git a/eth/abstractethstubserver.h b/eth/abstractethstubserver.h index 940e4c809..7f110513d 100644 --- a/eth/abstractethstubserver.h +++ b/eth/abstractethstubserver.h @@ -30,6 +30,7 @@ class AbstractEthStubServer : public jsonrpc::AbstractServerbindAndAddMethod(new jsonrpc::Procedure("procedures", jsonrpc::PARAMS_BY_NAME, jsonrpc::JSON_ARRAY, NULL), &AbstractEthStubServer::proceduresI); this->bindAndAddMethod(new jsonrpc::Procedure("secretToAddress", jsonrpc::PARAMS_BY_NAME, jsonrpc::JSON_STRING, "a",jsonrpc::JSON_STRING, NULL), &AbstractEthStubServer::secretToAddressI); this->bindAndAddMethod(new jsonrpc::Procedure("storageAt", jsonrpc::PARAMS_BY_NAME, jsonrpc::JSON_STRING, "a",jsonrpc::JSON_STRING,"x",jsonrpc::JSON_STRING, NULL), &AbstractEthStubServer::storageAtI); + this->bindAndAddMethod(new jsonrpc::Procedure("stateAt", jsonrpc::PARAMS_BY_NAME, jsonrpc::JSON_STRING, "a",jsonrpc::JSON_STRING,"x",jsonrpc::JSON_STRING,"s",jsonrpc::JSON_STRING, NULL), &AbstractEthStubServer::stateAtI); this->bindAndAddMethod(new jsonrpc::Procedure("transact", jsonrpc::PARAMS_BY_NAME, jsonrpc::JSON_OBJECT, "aDest",jsonrpc::JSON_STRING,"bData",jsonrpc::JSON_STRING,"sec",jsonrpc::JSON_STRING,"xGas",jsonrpc::JSON_STRING,"xGasPrice",jsonrpc::JSON_STRING,"xValue",jsonrpc::JSON_STRING, NULL), &AbstractEthStubServer::transactI); this->bindAndAddMethod(new jsonrpc::Procedure("txCountAt", jsonrpc::PARAMS_BY_NAME, jsonrpc::JSON_STRING, "a",jsonrpc::JSON_STRING, NULL), &AbstractEthStubServer::txCountAtI); @@ -120,6 +121,11 @@ class AbstractEthStubServer : public jsonrpc::AbstractServerstorageAt(request["a"].asString(), request["x"].asString()); } + inline virtual void stateAtI(const Json::Value& request, Json::Value& response) + { + response = this->stateAt(request["a"].asString(), request["x"].asString(), request["s"].asString()); + } + inline virtual void transactI(const Json::Value& request, Json::Value& response) { response = this->transact(request["aDest"].asString(), request["bData"].asString(), request["sec"].asString(), request["xGas"].asString(), request["xGasPrice"].asString(), request["xValue"].asString()); @@ -148,6 +154,7 @@ class AbstractEthStubServer : public jsonrpc::AbstractServer #endif -#include +#include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include #if ETH_READLINE #include #include @@ -46,10 +43,13 @@ #endif #include "BuildInfo.h" using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::p2p; +using namespace dev::eth; using namespace boost::algorithm; -using eth::Instruction; -using eth::c_instructionInfo; +using dev::eth::Instruction; + +#undef RETURN bool isTrue(std::string const& _m) { @@ -73,6 +73,7 @@ void interactiveHelp() << " verbosity () Gets or sets verbosity level." << endl << " minestart Starts mining." << endl << " minestop Stops mining." << endl + << " mineforce Forces mining, even when there are no transactions." << endl << " address Gives the current address." << endl << " secret Gives the current secret" << endl << " block Gives the current block height." << endl @@ -81,13 +82,14 @@ void interactiveHelp() << " send Execute a given transaction with current secret." << endl << " contract Create a new contract with current secret." << endl << " peers List the peers that are connected" << endl - << " listAccounts List the accounts on the network." << endl - << " listContracts List the contracts on the network." << endl - << " setSecret Set the secret to the hex secret key." < Set the coinbase (mining payout) address." < Export the config (.RLP) to the path provided." < Import the config (.RLP) from the path provided." < Dumps a contract to /.evm." << endl + << " listAccounts List the accounts on the network." << endl + << " listContracts List the contracts on the network." << endl + << " setSecret Set the secret to the hex secret key." < Set the coinbase (mining payout) address." < Export the config (.RLP) to the path provided." < Import the config (.RLP) from the path provided." < Dumps a contract to /.evm." << endl + << " dumptrace Dumps a transaction trace" << endl << "to . should be one of pretty, standard, standard+." << endl << " exit Exits the application." << endl; } @@ -97,10 +99,12 @@ void help() << "Usage eth [OPTIONS] " << endl << "Options:" << endl << " -a,--address Set the coinbase (mining payout) address to addr (default: auto)." << endl + << " -b,--bootstrap Connect to the default Ethereum peerserver." << endl << " -c,--client-name Add a name to your client's version string (default: blank)." << endl << " -d,--db-path Load database from path (default: ~/.ethereum " << endl << " /Etherum or Library/Application Support/Ethereum)." << endl - << " -h,--help Show this help message and exit." << endl + << " -f,--force-mining Mine even when there are no transaction to mine (Default: off)" << endl + << " -h,--help Show this help message and exit." << endl << " -i,--interactive Enter interactive mode (default: non-interactive)." << endl #if ETH_JSONRPC << " -j,--json-rpc Enable JSON-RPC server (default: off)." << endl @@ -108,8 +112,9 @@ void help() #endif << " -l,--listen Listen on the given port for incoming connected (default: 30303)." << endl << " -m,--mining Enable mining, optionally for a specified number of blocks (Default: off)" << endl - << " -n,--upnp Use upnp for NAT (default: on)." << endl - << " -o,--mode Start a full node or a peer node (Default: full)." << endl + << " -n,--upnp Use upnp for NAT (default: on)." << endl + << " -L,--local-networking Use peers whose addresses are local." << endl + << " -o,--mode Start a full node or a peer node (Default: full)." << endl << " -p,--port Connect to remote port (default: 30303)." << endl << " -r,--remote Connect to remote host (default: none)." << endl << " -s,--secret Set the secret key for use with send command (default: auto)." << endl @@ -124,23 +129,14 @@ string credits(bool _interactive = false) { std::ostringstream cout; cout - << "Ethereum (++) " << eth::EthVersion << endl + << "Ethereum (++) " << dev::Version << endl << " Code by Gav Wood, (c) 2013, 2014." << endl << " Based on a design by Vitalik Buterin." << endl << endl; if (_interactive) { - string vs = toString(eth::EthVersion); - vs = vs.substr(vs.find_first_of('.') + 1)[0]; - int pocnumber = stoi(vs); - string m_servers; - if (pocnumber == 4) - m_servers = "54.72.31.55"; - else - m_servers = "54.72.69.180"; - cout << "Type 'netstart 30303' to start networking" << endl; - cout << "Type 'connect " << m_servers << " 30303' to connect" << endl; + cout << "Type 'connect " << Host::pocHost() << " 30303' to connect" << endl; cout << "Type 'exit' to quit" << endl << endl; } return cout.str(); @@ -148,13 +144,13 @@ string credits(bool _interactive = false) void version() { - cout << "eth version " << eth::EthVersion << endl; - cout << "Build: " << ETH_QUOTED(ETH_BUILD_PLATFORM) << "/" << ETH_QUOTED(ETH_BUILD_TYPE) << endl; + cout << "eth version " << dev::Version << endl; + cout << "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl; exit(0); } Address c_config = Address("ccdeac59d35627b7de09332e819d5159e7bb7250"); -string pretty(h160 _a, eth::State _st) +string pretty(h160 _a, dev::eth::State _st) { string ns; h256 n; @@ -169,22 +165,25 @@ string pretty(h160 _a, eth::State _st) } return ns; } -bytes parse_data(string _args); + int main(int argc, char** argv) { unsigned short listenPort = 30303; string remoteHost; unsigned short remotePort = 30303; string dbPath; - eth::uint mining = ~(eth::uint)0; + unsigned mining = ~(unsigned)0; NodeMode mode = NodeMode::Full; unsigned peers = 5; bool interactive = false; #if ETH_JSONRPC - int jsonrpc = 8080; + int jsonrpc = -1; #endif string publicIP; + bool bootstrap = false; bool upnp = true; + bool useLocal = false; + bool forceMining = false; string clientName; // Init defaults @@ -229,10 +228,12 @@ int main(int argc, char** argv) upnp = false; else { - cerr << "Invalid UPnP option: " << m << endl; + cerr << "Invalid -n/--upnp option: " << m << endl; return -1; } } + else if (arg == "-L" || arg == "--local-networking") + useLocal = true; else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc) clientName = argv[++i]; else if ((arg == "-a" || arg == "--address" || arg == "--coinbase-address") && i + 1 < argc) @@ -245,22 +246,26 @@ int main(int argc, char** argv) { string m = argv[++i]; if (isTrue(m)) - mining = ~(eth::uint)0; + mining = ~(unsigned)0; else if (isFalse(m)) mining = 0; else if (int i = stoi(m)) mining = i; else { - cerr << "Unknown mining option: " << m << endl; + cerr << "Unknown -m/--mining option: " << m << endl; return -1; } } + else if (arg == "-b" || arg == "--bootstrap") + bootstrap = true; + else if (arg == "-f" || arg == "--force-mining") + forceMining = true; else if (arg == "-i" || arg == "--interactive") interactive = true; #if ETH_JSONRPC else if ((arg == "-j" || arg == "--json-rpc")) - jsonrpc = jsonrpc ? jsonrpc : 8080; + jsonrpc = jsonrpc == -1 ? 8080 : jsonrpc; else if (arg == "--json-rpc-port" && i + 1 < argc) jsonrpc = atoi(argv[++i]); #endif @@ -291,18 +296,33 @@ int main(int argc, char** argv) if (!clientName.empty()) clientName += "/"; - Client c("Ethereum(++)/" + clientName + "v" + eth::EthVersion + "/" ETH_QUOTED(ETH_BUILD_TYPE) "/" ETH_QUOTED(ETH_BUILD_PLATFORM), coinbase, dbPath); - c.start(); + cout << credits(); + NetworkPreferences netPrefs(listenPort, publicIP, upnp, useLocal); + dev::WebThreeDirect web3("Ethereum(++)/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), dbPath, false, mode == NodeMode::Full ? set{"eth", "shh"} : set{}, netPrefs); + web3.setIdealPeerCount(peers); + eth::Client* c = mode == NodeMode::Full ? web3.ethereum() : nullptr; + + if (c) + { + c->setForceMining(forceMining); + c->setAddress(coinbase); + } + cout << "Address: " << endl << toHex(us.address().asArray()) << endl; - c.startNetwork(listenPort, remoteHost, remotePort, mode, peers, publicIP, upnp); + web3.startNetwork(); + + if (bootstrap) + web3.connect(Host::pocHost()); + if (remoteHost.size()) + web3.connect(remoteHost, remotePort); #if ETH_JSONRPC auto_ptr jsonrpcServer; if (jsonrpc > -1) { - jsonrpcServer = auto_ptr(new EthStubServer(new jsonrpc::HttpServer(jsonrpc), c)); + jsonrpcServer = auto_ptr(new EthStubServer(new jsonrpc::HttpServer(jsonrpc), web3)); jsonrpcServer->setKeys({us}); jsonrpcServer->StartListening(); } @@ -310,9 +330,16 @@ int main(int argc, char** argv) if (interactive) { + string logbuf; string l; while (true) { + g_logPost = [](std::string const& a, char const*) { cout << "\r \r" << a << endl << "Press Enter" << flush; }; + cout << logbuf << "Press Enter" << flush; + std::getline(cin, l); + logbuf.clear(); + g_logPost = [&](std::string const& a, char const*) { logbuf += a + "\n"; }; + #if ETH_READLINE if (l.size()) add_history(l.c_str()); @@ -333,31 +360,34 @@ int main(int argc, char** argv) iss >> cmd; if (cmd == "netstart") { - eth::uint port; - iss >> port; - ClientGuard g(&c); - c.startNetwork((short)port); + iss >> netPrefs.listenPort; + web3.setNetworkPreferences(netPrefs); + web3.startNetwork(); } else if (cmd == "connect") { string addr; - eth::uint port; + unsigned port; iss >> addr >> port; - ClientGuard g(&c); - c.connect(addr, (short)port); + web3.connect(addr, (short)port); } else if (cmd == "netstop") { - ClientGuard g(&c); - c.stopNetwork(); + web3.stopNetwork(); + } + else if (c && cmd == "minestart") + { + c->startMining(); } - else if (cmd == "minestart") + else if (c && cmd == "minestop") { - c.startMining(); + c->stopMining(); } - else if (cmd == "minestop") + else if (c && cmd == "mineforce") { - c.stopMining(); + string enable; + iss >> enable; + c->setForceMining(isTrue(enable)); } else if (cmd == "verbosity") { @@ -376,7 +406,7 @@ int main(int argc, char** argv) { if (jsonrpc < 0) jsonrpc = 8080; - jsonrpcServer = auto_ptr(new EthStubServer(new jsonrpc::HttpServer(jsonrpc), c)); + jsonrpcServer = auto_ptr(new EthStubServer(new jsonrpc::HttpServer(jsonrpc), web3)); jsonrpcServer->setKeys({us}); jsonrpcServer->StartListening(); } @@ -397,28 +427,24 @@ int main(int argc, char** argv) { cout << "Secret Key: " << toHex(us.secret().asArray()) << endl; } - else if (cmd == "block") + else if (c && cmd == "block") { - ClientGuard g(&c); - cout << "Current block: " << c.blockChain().details().number; + cout << "Current block: " <blockChain().details().number << endl; } else if (cmd == "peers") { - ClientGuard g(&c); - for (auto it: c.peers()) + for (auto it: web3.peers()) cout << it.host << ":" << it.port << ", " << it.clientVersion << ", " << std::chrono::duration_cast(it.lastPing).count() << "ms" << endl; } - else if (cmd == "balance") + else if (c && cmd == "balance") { - ClientGuard g(&c); - cout << "Current balance: " << formatBalance(c.postState().balance(us.address())) << " = " << c.postState().balance(us.address()) << " wei" << endl; + cout << "Current balance: " << formatBalance( c->balanceAt(us.address())) << " = " <balanceAt(us.address()) << " wei" << endl; } - else if (cmd == "transact") + else if (c && cmd == "transact") { - ClientGuard g(&c); - auto const& bc = c.blockChain(); + auto const& bc =c->blockChain(); auto h = bc.currentHash(); auto blockData = bc.block(h); BlockInfo info(blockData); @@ -435,7 +461,7 @@ int main(int argc, char** argv) cnote << "Data:"; cnote << sdata; - bytes data = parse_data(sdata); + bytes data = dev::eth::parseData(sdata); cnote << "Bytes:"; string sbd = asString(data); bytes bbd = asBytes(sbd); @@ -444,16 +470,16 @@ int main(int argc, char** argv) cnote << ssbd.str(); int ssize = sechex.length(); int size = hexAddr.length(); - u256 minGas = (u256)c.state().callGas(data.size(), 0); + u256 minGas = (u256)Client::txGas(data.size(), 0); if (size < 40) { if (size > 0) - cwarn << "Invalid address length: " << size; + cwarn << "Invalid address length:" << size; } else if (gasPrice < info.minGasPrice) - cwarn << "Minimum gas price is " << info.minGasPrice; + cwarn << "Minimum gas price is" << info.minGasPrice; else if (gas < minGas) - cwarn << "Minimum gas amount is " << minGas; + cwarn << "Minimum gas amount is" << minGas; else if (ssize < 40) { if (ssize > 0) @@ -463,48 +489,36 @@ int main(int argc, char** argv) { Secret secret = h256(fromHex(sechex)); Address dest = h160(fromHex(hexAddr)); - c.transact(secret, amount, dest, data, gas, gasPrice); + c->transact(secret, amount, dest, data, gas, gasPrice); } } else cwarn << "Require parameters: transact ADDRESS AMOUNT GASPRICE GAS SECRET DATA"; } - else if (cmd == "listContracts") + else if (c && cmd == "listContracts") { - ClientGuard g(&c); - auto const& st = c.state(); - auto acs = st.addresses(); + auto acs =c->addresses(); string ss; for (auto const& i: acs) - { - auto r = i.first; - if (st.addressHasCode(r)) + if ( c->codeAt(i, 0).size()) { - ss = toString(r) + " : " + toString(formatBalance(i.second)) + " [" + toString((unsigned)st.transactionsFrom(i.first)) + "]"; + ss = toString(i) + " : " + toString( c->balanceAt(i)) + " [" + toString((unsigned) c->countAt(i)) + "]"; cout << ss << endl; } - } } - else if (cmd == "listAccounts") + else if (c && cmd == "listAccounts") { - ClientGuard g(&c); - auto const& st = c.state(); - auto acs = st.addresses(); + auto acs =c->addresses(); string ss; for (auto const& i: acs) - { - auto r = i.first; - if (!st.addressHasCode(r)) + if ( c->codeAt(i, 0).empty()) { - ss = toString(r) + pretty(r, st) + " : " + toString(formatBalance(i.second)) + " [" + toString((unsigned)st.transactionsFrom(i.first)) + "]"; + ss = toString(i) + " : " + toString( c->balanceAt(i)) + " [" + toString((unsigned) c->countAt(i)) + "]"; cout << ss << endl; } - - } } - else if (cmd == "send") + else if (c && cmd == "send") { - ClientGuard g(&c); if (iss.peek() != -1) { string hexAddr; @@ -515,31 +529,30 @@ int main(int argc, char** argv) if (size < 40) { if (size > 0) - cwarn << "Invalid address length: " << size; + cwarn << "Invalid address length:" << size; } else { - auto const& bc = c.blockChain(); + auto const& bc =c->blockChain(); auto h = bc.currentHash(); auto blockData = bc.block(h); BlockInfo info(blockData); - u256 minGas = (u256)c.state().callGas(0, 0); + u256 minGas = (u256)Client::txGas(0, 0); Address dest = h160(fromHex(hexAddr)); - c.transact(us.secret(), amount, dest, bytes(), minGas, info.minGasPrice); + c->transact(us.secret(), amount, dest, bytes(), minGas, info.minGasPrice); } - } else cwarn << "Require parameters: send ADDRESS AMOUNT"; } - else if (cmd == "contract") + else if (c && cmd == "contract") { - ClientGuard g(&c); - auto const& bc = c.blockChain(); + auto const& bc =c->blockChain(); auto h = bc.currentHash(); auto blockData = bc.block(h); BlockInfo info(blockData); - if(iss.peek() != -1) { + if (iss.peek() != -1) + { u256 endowment; u256 gas; u256 gasPrice; @@ -550,7 +563,7 @@ int main(int argc, char** argv) bytes init; cnote << "Init:"; cnote << sinit; - cnote << "Code size: " << size; + cnote << "Code size:" << size; if (size < 1) cwarn << "No code submitted"; else @@ -563,20 +576,75 @@ int main(int argc, char** argv) cnote << "Init:"; cnote << ssc.str(); } - u256 minGas = (u256)c.state().createGas(init.size(), 0); + u256 minGas = (u256)Client::txGas(init.size(), 0); if (endowment < 0) cwarn << "Invalid endowment"; else if (gasPrice < info.minGasPrice) - cwarn << "Minimum gas price is " << info.minGasPrice; + cwarn << "Minimum gas price is" << info.minGasPrice; else if (gas < minGas) - cwarn << "Minimum gas amount is " << minGas; + cwarn << "Minimum gas amount is" << minGas; else - c.transact(us.secret(), endowment, init, gas, gasPrice); + c->transact(us.secret(), endowment, init, gas, gasPrice); } else cwarn << "Require parameters: contract ENDOWMENT GASPRICE GAS CODEHEX"; } - else if (cmd == "inspect") + else if (c && cmd == "dumptrace") + { + unsigned block; + unsigned index; + string filename; + string format; + iss >> block >> index >> filename >> format; + ofstream f; + f.open(filename); + + dev::eth::State state =c->state(index + 1,c->blockChain().numberHash(block)); + if (index < state.pending().size()) + { + Executive e(state); + Transaction t = state.pending()[index]; + state = state.fromPending(index); + bytes r = t.rlp(); + e.setup(&r); + + OnOpFunc oof; + if (format == "pretty") + oof = [&](uint64_t steps, Instruction instr, bigint newMemSize, bigint gasCost, void* vvm, void const* vextVM) + { + dev::eth::VM* vm = (VM*)vvm; + dev::eth::ExtVM const* ext = (ExtVM const*)vextVM; + f << endl << " STACK" << endl; + for (auto i: vm->stack()) + f << (h256)i << endl; + f << " MEMORY" << endl << dev::memDump(vm->memory()); + f << " STORAGE" << endl; + for (auto const& i: ext->state().storage(ext->myAddress)) + f << showbase << hex << i.first << ": " << i.second << endl; + f << dec << ext->level << " | " << ext->myAddress << " | #" << steps << " | " << hex << setw(4) << setfill('0') << vm->curPC() << " : " << dev::eth::instructionInfo(instr).name << " | " << dec << vm->gas() << " | -" << dec << gasCost << " | " << newMemSize << "x32"; + }; + else if (format == "standard") + oof = [&](uint64_t, Instruction instr, bigint, bigint, void* vvm, void const* vextVM) + { + dev::eth::VM* vm = (VM*)vvm; + dev::eth::ExtVM const* ext = (ExtVM const*)vextVM; + f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)vm->gas(), 1)) << endl; + }; + else if (format == "standard+") + oof = [&](uint64_t, Instruction instr, bigint, bigint, void* vvm, void const* vextVM) + { + dev::eth::VM* vm = (VM*)vvm; + dev::eth::ExtVM const* ext = (ExtVM const*)vextVM; + if (instr == Instruction::STOP || instr == Instruction::RETURN || instr == Instruction::SUICIDE) + for (auto const& i: ext->state().storage(ext->myAddress)) + f << toHex(dev::toCompactBigEndian(i.first, 1)) << " " << toHex(dev::toCompactBigEndian(i.second, 1)) << endl; + f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)vm->gas(), 1)) << endl; + }; + e.go(oof); + e.finalize(oof); + } + } + else if (c && cmd == "inspect") { string rechex; iss >> rechex; @@ -585,20 +653,28 @@ int main(int argc, char** argv) cwarn << "Invalid address length"; else { - ClientGuard g(&c); auto h = h160(fromHex(rechex)); stringstream s; - auto mem = c.state().storage(h); - for (auto const& i: mem) - s << "@" << showbase << hex << i.first << " " << showbase << hex << i.second << endl; - s << endl << disassemble(c.state().code(h)); + try + { + auto storage =c->storageAt(h, 0); + for (auto const& i: storage) + s << "@" << showbase << hex << i.first << " " << showbase << hex << i.second << endl; + s << endl << disassemble( c->codeAt(h, 0)) << endl; + + string outFile = getDataDir() + "/" + rechex + ".evm"; + ofstream ofs; + ofs.open(outFile, ofstream::binary); + ofs.write(s.str().c_str(), s.str().length()); + ofs.close(); - string outFile = getDataDir() + "/" + rechex + ".evm"; - ofstream ofs; - ofs.open(outFile, ofstream::binary); - ofs.write(s.str().c_str(), s.str().length()); - ofs.close(); + cnote << "Saved" << rechex << "to" << outFile; + } + catch (dev::eth::InvalidTrie) + { + cwarn << "Corrupted trie."; + } } } else if (cmd == "setSecret") @@ -670,75 +746,22 @@ int main(int argc, char** argv) jsonrpcServer->StopListening(); #endif } - else + else if (c) { - eth::uint n = c.blockChain().details().number; + unsigned n =c->blockChain().details().number; if (mining) - c.startMining(); + c->startMining(); while (true) { - if (c.blockChain().details().number - n == mining) - c.stopMining(); + if ( c->isMining() &&c->blockChain().details().number - n == mining) + c->stopMining(); this_thread::sleep_for(chrono::milliseconds(100)); } } - + else + while (true) + this_thread::sleep_for(chrono::milliseconds(1000)); return 0; } -bytes parse_data(string _args) -{ - bytes m_data; - stringstream args(_args); - string arg; - int cc = 0; - while (args >> arg) - { - int al = arg.length(); - if (boost::starts_with(arg, "0x")) - { - bytes bs = fromHex(arg); - m_data += bs; - } - else if (arg[0] == '@') - { - arg = arg.substr(1, arg.length()); - if (boost::starts_with(arg, "0x")) - { - cnote << "hex: " << arg; - bytes bs = fromHex(arg); - int size = bs.size(); - if (size < 32) - for (auto i = 0; i < 32 - size; ++i) - m_data.push_back(0); - m_data += bs; - } - else if (boost::starts_with(arg, "\"") && boost::ends_with(arg, "\"")) - { - arg = arg.substr(1, arg.length() - 2); - cnote << "string: " << arg; - if (al < 32) - for (int i = 0; i < 32 - al; ++i) - m_data.push_back(0); - for (int i = 0; i < al; ++i) - m_data.push_back(arg[i]); - } - else - { - cnote << "value: " << arg; - bytes bs = toBigEndian(u256(arg)); - int size = bs.size(); - if (size < 32) - for (auto i = 0; i < 32 - size; ++i) - m_data.push_back(0); - m_data += bs; - } - } - else - for (int i = 0; i < al; ++i) - m_data.push_back(arg[i]); - cc++; - } - return m_data; -} diff --git a/exp/CMakeLists.txt b/exp/CMakeLists.txt index 5b76b4c7a..da6775798 100644 --- a/exp/CMakeLists.txt +++ b/exp/CMakeLists.txt @@ -9,6 +9,7 @@ set(EXECUTABLE exp) add_executable(${EXECUTABLE} ${SRC_LIST}) target_link_libraries(${EXECUTABLE} ethereum) +target_link_libraries(${EXECUTABLE} p2p) target_link_libraries(${EXECUTABLE} gmp) target_link_libraries(${EXECUTABLE} ${CRYPTOPP_LS}) if(MINIUPNPC_LS) diff --git a/exp/main.cpp b/exp/main.cpp index 6a5e5557f..1f29ab207 100644 --- a/exp/main.cpp +++ b/exp/main.cpp @@ -19,26 +19,97 @@ * @date 2014 * Ethereum client. */ - -#include -#include -#include -#include "BuildInfo.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; +using namespace dev::p2p; +using namespace dev::shh; + +int main() +{ + DownloadMan man; + DownloadSub s0(man); + DownloadSub s1(man); + DownloadSub s2(man); + man.resetToChain(h256s({u256(0), u256(1), u256(2), u256(3), u256(4), u256(5), u256(6), u256(7), u256(8)})); + cnote << s0.nextFetch(2); + cnote << s1.nextFetch(2); + cnote << s2.nextFetch(2); + s0.noteBlock(u256(0)); + s0.doneFetch(); + cnote << s0.nextFetch(2); + s1.noteBlock(u256(2)); + s1.noteBlock(u256(3)); + s1.doneFetch(); + cnote << s1.nextFetch(2); + s0.doneFetch(); + cnote << s0.nextFetch(2); + +/* RangeMask m(0, 100); + cnote << m; + m += UnsignedRange(3, 10); + cnote << m; + m += UnsignedRange(11, 16); + cnote << m; + m += UnsignedRange(10, 11); + cnote << m; + cnote << ~m; + cnote << (~m).lowest(10); + for (auto i: (~m).lowest(10)) + cnote << i;*/ + return 0; +} -int main(int, char**) +/* +int main(int argc, char** argv) { - u256 z = 0; - u256 s = 7; - u256 ms = z - s; - s256 ams = -7; - s256 sms = u2s(ms); - cnote << sms; - cnote << ams; - cnote << ms; - u256 t = 3; - s256 st = u2s(t); - cnote << ms << t << (sms % t) << sms << st << (s2u(sms % st) + 70); + g_logVerbosity = 20; + + short listenPort = 30303; + string remoteHost; + short remotePort = 30303; + + for (int i = 1; i < argc; ++i) + { + string arg = argv[i]; + if (arg == "-l" && i + 1 < argc) + listenPort = (short)atoi(argv[++i]); + else if (arg == "-r" && i + 1 < argc) + remoteHost = argv[++i]; + else if (arg == "-p" && i + 1 < argc) + remotePort = (short)atoi(argv[++i]); + else + remoteHost = argv[i]; + } + + Host ph("Test", NetworkPreferences(listenPort, "", false, true)); + ph.registerCapability(new WhisperHost()); + auto wh = ph.cap(); + + ph.start(); + + if (!remoteHost.empty()) + ph.connect(remoteHost, remotePort); + + /// Only interested in the packet if the lowest bit is 1 + auto w = wh->installWatch(MessageFilter(std::vector >({{fromHex("0000000000000000000000000000000000000000000000000000000000000001"), fromHex("0000000000000000000000000000000000000000000000000000000000000001")}}))); + + + for (int i = 0; ; ++i) + { + wh->sendRaw(h256(u256(i * i)).asBytes(), h256(u256(i)).asBytes(), 1000); + for (auto i: wh->checkWatch(w)) + cnote << "New message:" << (u256)h256(wh->message(i).payload); + } return 0; } +*/ diff --git a/iethxi/CMakeLists.txt b/iethxi/CMakeLists.txt new file mode 100644 index 000000000..fc8edf1dc --- /dev/null +++ b/iethxi/CMakeLists.txt @@ -0,0 +1,117 @@ +cmake_policy(SET CMP0015 NEW) + +if ("${TARGET_PLATFORM}" STREQUAL "w64") + cmake_policy(SET CMP0020 NEW) +endif () + + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +aux_source_directory(. SRC_LIST) + +include_directories(..) +link_directories(../libethcore) +link_directories(../libethereum) +link_directories(../libqethereum) + +# Find Qt5 for Apple and update src_list for windows +if (APPLE) + # homebrew defaults to qt4 and installs qt5 as 'keg-only' + # which places it into /usr/local/opt insteadof /usr/local. + + set(CMAKE_PREFIX_PATH /usr/local/opt/qt5) + include_directories(/usr/local/opt/qt5/include /usr/local/include) +elseif ("${TARGET_PLATFORM}" STREQUAL "w64") + set(SRC_LIST ${SRC_LIST} ../windows/qt_plugin_import.cpp) +elseif (UNIX) + set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ";$ENV{QTDIR}/lib/cmake") +endif () + + +find_package(Qt5Widgets REQUIRED) +find_package(Qt5Gui REQUIRED) +find_package(Qt5Quick REQUIRED) +find_package(Qt5Qml REQUIRED) +find_package(Qt5Network REQUIRED) +qt5_wrap_ui(ui_Main.h Main.ui) +qt5_add_resources(RESOURCE_ADDED Resources.qrc) + +# Set name of binary and add_executable() +if (APPLE) + set(EXECUTEABLE IEthXi) + set(CMAKE_INSTALL_PREFIX ./) + set(BIN_INSTALL_DIR ".") + set(DOC_INSTALL_DIR ".") + + set(PROJECT_VERSION "${ETH_VERSION}") + set(MACOSX_BUNDLE_INFO_STRING "${PROJECT_NAME} ${PROJECT_VERSION}") + set(MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_NAME} ${PROJECT_VERSION}") + set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_NAME} ${PROJECT_VERSION}") + set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION}") + set(MACOSX_BUNDLE_COPYRIGHT "${PROJECT_COPYRIGHT_YEAR} ${PROJECT_VENDOR}") + set(MACOSX_BUNDLE_GUI_IDENTIFIER "${PROJECT_DOMAIN_SECOND}.${PROJECT_DOMAIN_FIRST}") + set(MACOSX_BUNDLE_BUNDLE_NAME ${EXECUTEABLE}) + include(BundleUtilities) + + add_executable(${EXECUTEABLE} MACOSX_BUNDLE Main.ui ${RESOURCE_ADDED} ${SRC_LIST}) +else () + set(EXECUTEABLE iethxi) + add_executable(${EXECUTEABLE} Main.ui ${RESOURCE_ADDED} ${SRC_LIST}) +endif () + +qt5_use_modules(${EXECUTEABLE} Core Gui Widgets Network Quick Qml) +target_link_libraries(${EXECUTEABLE} qethereum ethereum secp256k1 ${CRYPTOPP_LS}) + +if (APPLE) + if (${ADDFRAMEWORKS}) + set_target_properties(${EXECUTEABLE} PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/EthereumMacOSXBundleInfo.plist.in") + endif () + + SET_SOURCE_FILES_PROPERTIES(${EXECUTEABLE} PROPERTIES MACOSX_PACKAGE_LOCATION MacOS) + + # This is a workaround for when the build-type defaults to Debug, and when a multi-config generator like xcode is used, where the type + # will not be set but defaults to release. + set(generator_lowercase "${CMAKE_GENERATOR}") + string(TOLOWER "${CMAKE_GENERATOR}" generator_lowercase) + if ("${generator_lowercase}" STREQUAL "xcode") + # TODO: Not sure how to resolve this. Possibly \${TARGET_BUILD_DIR} + set(binary_build_dir "${CMAKE_CURRENT_BINARY_DIR}/Debug") + else () + set(binary_build_dir "${CMAKE_CURRENT_BINARY_DIR}") + endif () + + set(APPS ${binary_build_dir}/${EXECUTEABLE}.app) + + # This tool and the next will automatically looked at the linked libraries in order to determine what dependencies are required. Thus, target_link_libaries only needs to add ethereum and secp256k1 (above) + install(CODE " + include(BundleUtilities) + set(BU_CHMOD_BUNDLE_ITEMS 1) + fixup_bundle(\"${APPS}\" \"${BUNDLELIBS}\" \"../libqethereum ../libethereum ../secp256k1\") + " COMPONENT RUNTIME ) + + if (${ADDFRAMEWORKS}) + add_custom_target(addframeworks ALL + COMMAND /usr/local/opt/qt5/bin/macdeployqt ${binary_build_dir}/${EXECUTEABLE}.app + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + DEPENDS ${PROJECT_NAME} + ) + endif () + +elseif ("${TARGET_PLATFORM}" STREQUAL "w64") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-keep-inline-dllexport -static-libgcc -static-libstdc++ -static") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-s -Wl,-subsystem,windows -mthreads -L/usr/x86_64-w64-mingw32/plugins/platforms") + target_link_libraries(${EXECUTEABLE} gcc) + target_link_libraries(${EXECUTEABLE} mingw32 qtmain mswsock iphlpapi qwindows shlwapi Qt5PlatformSupport gdi32 comdlg32 oleaut32 imm32 winmm ole32 uuid ws2_32) + target_link_libraries(${EXECUTEABLE} boost_system-mt-s) + target_link_libraries(${EXECUTEABLE} boost_filesystem-mt-s) + target_link_libraries(${EXECUTEABLE} boost_thread_win32-mt-s) + target_link_libraries(${EXECUTEABLE} Qt5PlatformSupport) + set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS) +elseif (UNIX) +else () + target_link_libraries(${EXECUTEABLE} boost_system) + target_link_libraries(${EXECUTEABLE} boost_filesystem) + find_package(Threads REQUIRED) + target_link_libraries(${EXECUTEABLE} ${CMAKE_THREAD_LIBS_INIT}) + install( TARGETS ${EXECUTEABLE} RUNTIME DESTINATION bin ) +endif () + diff --git a/iethxi/EthereumMacOSXBundleInfo.plist.in b/iethxi/EthereumMacOSXBundleInfo.plist.in new file mode 100644 index 000000000..684ad7908 --- /dev/null +++ b/iethxi/EthereumMacOSXBundleInfo.plist.in @@ -0,0 +1,38 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleGetInfoString + ${MACOSX_BUNDLE_INFO_STRING} + CFBundleIconFile + ${MACOSX_BUNDLE_ICON_FILE} + CFBundleIdentifier + ${MACOSX_BUNDLE_GUI_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLongVersionString + ${MACOSX_BUNDLE_LONG_VERSION_STRING} + CFBundleName + ${MACOSX_BUNDLE_BUNDLE_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} + CFBundleSignature + ???? + CFBundleVersion + ${MACOSX_BUNDLE_BUNDLE_VERSION} + CSResourcesFileMapped + + LSRequiresCarbon + + NSHumanReadableCopyright + ${MACOSX_BUNDLE_COPYRIGHT} + NSHighResolutionCapable + + + diff --git a/iethxi/Main.ui b/iethxi/Main.ui new file mode 100644 index 000000000..fe289ba9f --- /dev/null +++ b/iethxi/Main.ui @@ -0,0 +1,168 @@ + + + Main + + + + 0 + 0 + 562 + 488 + + + + Walleth + + + true + + + QMainWindow::AllowNestedDocks|QMainWindow::AllowTabbedDocks|QMainWindow::VerticalTabs + + + true + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + 0 wei + + + + + + + 0 peers + + + + + + + 1 block + + + + + + + + + + + 0 + 0 + 562 + 25 + + + + + &File + + + + + + &Network + + + + + + + + T&ools + + + + + + + + &Help + + + + + + + + + + + + &Quit + + + + + true + + + true + + + Use &UPnP + + + + + &Connect to Peer... + + + + + true + + + Enable &Network + + + + + true + + + &Mine + + + + + &New Address + + + + + &About... + + + + + true + + + &Preview + + + + + + + diff --git a/iethxi/MainWin.cpp b/iethxi/MainWin.cpp new file mode 100644 index 000000000..276ff7630 --- /dev/null +++ b/iethxi/MainWin.cpp @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "BuildInfo.h" +#include "MainWin.h" +#include "ui_Main.h" +using namespace std; +using namespace eth; + +Main::Main(QWidget *parent) : + QObject(parent) +{ +/* qRegisterMetaType("eth::u256"); + qRegisterMetaType("eth::KeyPair"); + qRegisterMetaType("eth::Secret"); + qRegisterMetaType("eth::Address"); + qRegisterMetaType("QmlAccount*"); + qRegisterMetaType("QmlEthereum*"); + + qmlRegisterType("org.ethereum", 1, 0, "Ethereum"); + qmlRegisterType("org.ethereum", 1, 0, "Account"); + qmlRegisterSingletonType("org.ethereum", 1, 0, "Balance", QmlEthereum::constructU256Helper); + qmlRegisterSingletonType("org.ethereum", 1, 0, "Key", QmlEthereum::constructKeyHelper); +*/ + /* + ui->librariesView->setModel(m_libraryMan); + ui->graphsView->setModel(m_graphMan); + */ + + + + +// QQmlContext* context = m_view->rootContext(); +// context->setContextProperty("u256", new U256Helper(this)); +} + +Main::~Main() +{ +} + +// extra bits needed to link on VS +#ifdef _MSC_VER + +// include moc file, ofuscated to hide from automoc +#include\ +"moc_MainWin.cpp" + +#endif diff --git a/iethxi/MainWin.h b/iethxi/MainWin.h new file mode 100644 index 000000000..acbea8ca8 --- /dev/null +++ b/iethxi/MainWin.h @@ -0,0 +1,18 @@ +#ifndef MAIN_H +#define MAIN_H + +#include + +class Main: public QObject +{ + Q_OBJECT + +public: + explicit Main(QWidget *parent = 0); + ~Main(); + +private: + QQmlApplicationEngine* m_view; +}; + +#endif // MAIN_H diff --git a/iethxi/Resources.qrc b/iethxi/Resources.qrc new file mode 100644 index 000000000..1789216ed --- /dev/null +++ b/iethxi/Resources.qrc @@ -0,0 +1,5 @@ + + + Simple.qml + + diff --git a/iethxi/Simple.qml b/iethxi/Simple.qml new file mode 100644 index 000000000..ac9dc5e37 --- /dev/null +++ b/iethxi/Simple.qml @@ -0,0 +1,9 @@ +import QtQuick.Controls 1.1 + +ApplicationWindow { + title: "My App" + Button { + text: "Push Me" + anchors.centerIn: parent + } +} diff --git a/iethxi/main.cpp b/iethxi/main.cpp new file mode 100644 index 000000000..569b6d17c --- /dev/null +++ b/iethxi/main.cpp @@ -0,0 +1,9 @@ +#include +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + QQmlApplicationEngine app(QUrl("qrc:/Simple.qml")); + return a.exec(); +} diff --git a/libethential/All.h b/libdevcore/All.h similarity index 100% rename from libethential/All.h rename to libdevcore/All.h diff --git a/libethential/CMakeLists.txt b/libdevcore/CMakeLists.txt similarity index 95% rename from libethential/CMakeLists.txt rename to libdevcore/CMakeLists.txt index 139e80ce4..3f3ecb619 100644 --- a/libethential/CMakeLists.txt +++ b/libdevcore/CMakeLists.txt @@ -8,7 +8,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB") aux_source_directory(. SRC_LIST) -set(EXECUTABLE ethential) +set(EXECUTABLE devcore) # set(CMAKE_INSTALL_PREFIX ../lib) if(ETH_STATIC) @@ -20,7 +20,7 @@ file(GLOB HEADERS "*.h") include_directories(..) -target_link_libraries(${EXECUTABLE} ethential) +target_link_libraries(${EXECUTABLE} devcore) if("${TARGET_PLATFORM}" STREQUAL "w64") include_directories(/usr/x86_64-w64-mingw32/include/cryptopp) diff --git a/libethential/Common.cpp b/libdevcore/Common.cpp similarity index 91% rename from libethential/Common.cpp rename to libdevcore/Common.cpp index 7c3d39f93..8312960c1 100644 --- a/libethential/Common.cpp +++ b/libdevcore/Common.cpp @@ -22,11 +22,12 @@ #include "Common.h" using namespace std; -using namespace eth; +using namespace dev; -namespace eth +namespace dev { -char const* EthVersion = "0.5.18"; +char const* Version = "0.6.9"; } + diff --git a/libethential/Common.h b/libdevcore/Common.h similarity index 91% rename from libethential/Common.h rename to libdevcore/Common.h index c06ede0c0..132429010 100644 --- a/libethential/Common.h +++ b/libdevcore/Common.h @@ -23,7 +23,7 @@ #pragma once -// way to many uint to size_t warnings in 32 bit build +// way to many unsigned to size_t warnings in 32 bit build #ifdef _M_IX86 #pragma warning(disable:4244) #endif @@ -43,13 +43,13 @@ using byte = uint8_t; // Quote a given token stream to turn it into a string. -#define ETH_QUOTED_HELPER(s) #s -#define ETH_QUOTED(s) ETH_QUOTED_HELPER(s) +#define DEV_QUOTED_HELPER(s) #s +#define DEV_QUOTED(s) DEV_QUOTED_HELPER(s) -namespace eth +namespace dev { -extern char const* EthVersion; +extern char const* Version; // Binary data types. using bytes = std::vector; @@ -62,8 +62,6 @@ using u256 = boost::multiprecision::number>; using u160 = boost::multiprecision::number>; using s160 = boost::multiprecision::number>; -using uint = uint64_t; -using sint = int64_t; using u256s = std::vector; using u160s = std::vector; using u256Set = std::set; @@ -98,4 +96,11 @@ inline u256 s2u(s256 _u) return (u256)(c_end + _u); } +inline unsigned int toLog2(u256 _x) +{ + unsigned ret; + for (ret = 0; _x >>= 1; ++ret) {} + return ret; +} + } diff --git a/libdevcore/CommonData.cpp b/libdevcore/CommonData.cpp new file mode 100644 index 000000000..6e889a6b0 --- /dev/null +++ b/libdevcore/CommonData.cpp @@ -0,0 +1,282 @@ +/* + 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 . +*/ +/** @file Common.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "CommonData.h" + +#include +#include "Exceptions.h" +using namespace std; +using namespace dev; + +std::string dev::escaped(std::string const& _s, bool _all) +{ + std::string ret; + ret.reserve(_s.size()); + ret.push_back('"'); + for (auto i: _s) + if (i == '"' && !_all) + ret += "\\\""; + else if (i == '\\' && !_all) + ret += "\\\\"; + else if (i < ' ' || _all) + { + ret += "\\x"; + ret.push_back("0123456789abcdef"[(uint8_t)i / 16]); + ret.push_back("0123456789abcdef"[(uint8_t)i % 16]); + } + else + ret.push_back(i); + ret.push_back('"'); + return ret; +} + +std::string dev::randomWord() +{ + static std::mt19937_64 s_eng(0); + std::string ret(std::uniform_int_distribution(1, 5)(s_eng), ' '); + char const n[] = "qwertyuiop";//asdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890"; + std::uniform_int_distribution d(0, sizeof(n) - 2); + for (char& c: ret) + c = n[d(s_eng)]; + return ret; +} + +int dev::fromHex(char _i) +{ + if (_i >= '0' && _i <= '9') + return _i - '0'; + if (_i >= 'a' && _i <= 'f') + return _i - 'a' + 10; + if (_i >= 'A' && _i <= 'F') + return _i - 'A' + 10; + throw BadHexCharacter(); +} + +bytes dev::fromHex(std::string const& _s) +{ + unsigned s = (_s[0] == '0' && _s[1] == 'x') ? 2 : 0; + std::vector ret; + ret.reserve((_s.size() - s + 1) / 2); + + if (_s.size() % 2) + try + { + ret.push_back(fromHex(_s[s++])); + } + catch (...){ ret.push_back(0); } + for (unsigned i = s; i < _s.size(); i += 2) + try + { + ret.push_back((byte)(fromHex(_s[i]) * 16 + fromHex(_s[i + 1]))); + } + catch (...){ ret.push_back(0); } + return ret; +} + +bytes dev::asNibbles(std::string const& _s) +{ + std::vector ret; + ret.reserve(_s.size() * 2); + for (auto i: _s) + { + ret.push_back(i / 16); + ret.push_back(i % 16); + } + return ret; +} + +#if 0 + +/* Following code is copyright 2012-2014 Luke Dashjr + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the standard MIT license. See COPYING for more details. + */ + +#include +#include +#include +#include + +static const int8_t b58digits_map[] = { + -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8,-1,-1,-1,-1,-1,-1, + -1, 9,10,11,12,13,14,15, 16,-1,17,18,19,20,21,-1, + 22,23,24,25,26,27,28,29, 30,31,32,-1,-1,-1,-1,-1, + -1,33,34,35,36,37,38,39, 40,41,42,43,-1,44,45,46, + 47,48,49,50,51,52,53,54, 55,56,57,-1,-1,-1,-1,-1, +}; + +bool b58tobin(void *bin, size_t *binszp, const char *b58, size_t b58sz) +{ + size_t binsz = *binszp; + const unsigned char *b58u = (void*)b58; + unsigned char *binu = bin; + size_t outisz = (binsz + 3) / 4; + uint32_t outi[outisz]; + uint64_t t; + uint32_t c; + size_t i, j; + uint8_t bytesleft = binsz % 4; + uint32_t zeromask = bytesleft ? (0xffffffff << (bytesleft * 8)) : 0; + unsigned zerocount = 0; + + if (!b58sz) + b58sz = strlen(b58); + + memset(outi, 0, outisz * sizeof(*outi)); + + // Leading zeros, just count + for (i = 0; i < b58sz && !b58digits_map[b58u[i]]; ++i) + ++zerocount; + + for ( ; i < b58sz; ++i) + { + if (b58u[i] & 0x80) + // High-bit set on invalid digit + return false; + if (b58digits_map[b58u[i]] == -1) + // Invalid base58 digit + return false; + c = (unsigned)b58digits_map[b58u[i]]; + for (j = outisz; j--; ) + { + t = ((uint64_t)outi[j]) * 58 + c; + c = (t & 0x3f00000000) >> 32; + outi[j] = t & 0xffffffff; + } + if (c) + // Output number too big (carry to the next int32) + return false; + if (outi[0] & zeromask) + // Output number too big (last int32 filled too far) + return false; + } + + j = 0; + switch (bytesleft) { + case 3: + *(binu++) = (outi[0] & 0xff0000) >> 16; + case 2: + *(binu++) = (outi[0] & 0xff00) >> 8; + case 1: + *(binu++) = (outi[0] & 0xff); + ++j; + default: + break; + } + + for (; j < outisz; ++j) + { + *(binu++) = (outi[j] >> 0x18) & 0xff; + *(binu++) = (outi[j] >> 0x10) & 0xff; + *(binu++) = (outi[j] >> 8) & 0xff; + *(binu++) = (outi[j] >> 0) & 0xff; + } + + // Count canonical base58 byte count + binu = bin; + for (i = 0; i < binsz; ++i) + { + if (binu[i]) + break; + --*binszp; + } + *binszp += zerocount; + + return true; +} + +static +bool my_dblsha256(void *hash, const void *data, size_t datasz) +{ + uint8_t buf[0x20]; + return b58_sha256_impl(buf, data, datasz) && b58_sha256_impl(hash, buf, sizeof(buf)); +} + +int b58check(const void *bin, size_t binsz, const char *base58str, size_t b58sz) +{ + unsigned char buf[32]; + const uint8_t *binc = bin; + unsigned i; + if (binsz < 4) + return -4; + if (!my_dblsha256(buf, bin, binsz - 4)) + return -2; + if (memcmp(&binc[binsz - 4], buf, 4)) + return -1; + + // Check number of zeros is correct AFTER verifying checksum (to avoid possibility of accessing base58str beyond the end) + for (i = 0; binc[i] == '\0' && base58str[i] == '1'; ++i) + {} // Just finding the end of zeros, nothing to do in loop + if (binc[i] == '\0' || base58str[i] == '1') + return -3; + + return binc[0]; +} + +static const char b58digits_ordered[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + +bool b58enc(char *b58, size_t *b58sz, const void *data, size_t binsz) +{ + const uint8_t *bin = data; + int carry; + size_t i, j, high, zcount = 0; + size_t size; + + while (zcount < binsz && !bin[zcount]) + ++zcount; + + size = (binsz - zcount) * 138 / 100 + 1; + uint8_t buf[size]; + memset(buf, 0, size); + + for (i = zcount, high = size - 1; i < binsz; ++i, high = j) + { + for (carry = bin[i], j = size - 1; (j > high) || carry; --j) + { + carry += 256 * buf[j]; + buf[j] = carry % 58; + carry /= 58; + } + } + + for (j = 0; j < size && !buf[j]; ++j); + + if (*b58sz <= zcount + size - j) + { + *b58sz = zcount + size - j + 1; + return false; + } + + if (zcount) + memset(b58, '1', zcount); + for (i = zcount; j < size; ++i, ++j) + b58[i] = b58digits_ordered[buf[j]]; + b58[i] = '\0'; + *b58sz = i + 1; + + return true; +} + +#endif diff --git a/libethential/CommonData.h b/libdevcore/CommonData.h similarity index 93% rename from libethential/CommonData.h rename to libdevcore/CommonData.h index 6840194fc..11850fa69 100644 --- a/libethential/CommonData.h +++ b/libdevcore/CommonData.h @@ -30,7 +30,7 @@ #include #include "Common.h" -namespace eth +namespace dev { // String conversion functions, mainly to/from hex/nibble/byte representations. @@ -55,6 +55,11 @@ int fromHex(char _i); /// @example fromHex("41626261") == asBytes("Abba") bytes fromHex(std::string const& _s); +#if 0 +std::string toBase58(bytesConstRef _data); +bytes fromBase58(std::string const& _s); +#endif + /// Converts byte array to a string containing the same (binary) data. Unless /// the byte array happens to contain ASCII data, this won't be printable. inline std::string asString(bytes const& _b) @@ -79,7 +84,7 @@ bytes asNibbles(std::string const& _s); /// The size of the collection object will be unchanged. If it is too small, it will not represent the /// value properly, if too big then the additional elements will be zeroed out. /// @a _Out will typically be either std::string or bytes. -/// @a _T will typically by uint, u160, u256 or bigint. +/// @a _T will typically by unsigned, u160, u256 or bigint. template inline void toBigEndian(_T _val, _Out& o_out) { @@ -89,7 +94,7 @@ inline void toBigEndian(_T _val, _Out& o_out) /// Converts a big-endian byte-stream represented on a templated collection to a templated integer value. /// @a _In will typically be either std::string or bytes. -/// @a _T will typically by uint, u160, u256 or bigint. +/// @a _T will typically by unsigned, u160, u256 or bigint. template inline _T fromBigEndian(_In const& _bytes) { @@ -140,10 +145,10 @@ std::string escaped(std::string const& _s, bool _all = true); /// @returns the number of elements both @a _t and @a _u share, in order, at the beginning. /// @example commonPrefix("Hello world!", "Hello, world!") == 5 template -uint commonPrefix(_T const& _t, _U const& _u) +unsigned commonPrefix(_T const& _t, _U const& _u) { - uint s = std::min(_t.size(), _u.size()); - for (uint i = 0;; ++i) + unsigned s = std::min(_t.size(), _u.size()); + for (unsigned i = 0;; ++i) if (i == s || _t[i] != _u[i]) return i; return s; @@ -157,9 +162,9 @@ std::string randomWord(); /// Determine bytes required to encode the given integer value. @returns 0 if @a _i is zero. template -inline uint bytesRequired(_T _i) +inline unsigned bytesRequired(_T _i) { - uint i = 0; + unsigned i = 0; for (; _i != 0; ++i, _i >>= 8) {} return i; } @@ -167,7 +172,7 @@ inline uint bytesRequired(_T _i) /// Trims a given number of elements from the front of a collection. /// Only works for POD element types. template -void trimFront(_T& _t, uint _elements) +void trimFront(_T& _t, unsigned _elements) { static_assert(std::is_pod::value, ""); memmove(_t.data(), _t.data() + _elements, (_t.size() - _elements) * sizeof(_t[0])); diff --git a/libethential/CommonIO.cpp b/libdevcore/CommonIO.cpp similarity index 89% rename from libethential/CommonIO.cpp rename to libdevcore/CommonIO.cpp index 82d79ccd7..5797e44dc 100644 --- a/libethential/CommonIO.cpp +++ b/libdevcore/CommonIO.cpp @@ -24,9 +24,9 @@ #include #include "Exceptions.h" using namespace std; -using namespace eth; +using namespace dev; -string eth::memDump(bytes const& _b, unsigned _w, bool _html) +string dev::memDump(bytes const& _b, unsigned _w, bool _html) { stringstream ret; if (_html) @@ -36,7 +36,7 @@ string eth::memDump(bytes const& _b, unsigned _w, bool _html) ret << hex << setw(4) << setfill('0') << i << " "; for (unsigned j = i; j < i + _w; ++j) if (j < _b.size()) - if (_b[j] >= 32 && _b[j] < 128) + if (_b[j] >= 32 && _b[j] < 127) if ((char)_b[j] == '<' && _html) ret << "<"; else if ((char)_b[j] == '&' && _html) @@ -57,7 +57,7 @@ string eth::memDump(bytes const& _b, unsigned _w, bool _html) return ret.str(); } -bytes eth::contents(std::string const& _file) +bytes dev::contents(std::string const& _file) { std::ifstream is(_file, std::ifstream::binary); if (!is) @@ -72,7 +72,7 @@ bytes eth::contents(std::string const& _file) return ret; } -void eth::writeFile(std::string const& _file, bytes const& _data) +void dev::writeFile(std::string const& _file, bytes const& _data) { ofstream(_file, ios::trunc).write((char const*)_data.data(), _data.size()); } diff --git a/libethential/CommonIO.h b/libdevcore/CommonIO.h similarity index 99% rename from libethential/CommonIO.h rename to libdevcore/CommonIO.h index 824f2c270..98c5a96c7 100644 --- a/libethential/CommonIO.h +++ b/libdevcore/CommonIO.h @@ -37,7 +37,7 @@ #include #include "Common.h" -namespace eth +namespace dev { /// Retrieve and returns the contents of the given file. If the file doesn't exist or isn't readable, returns an empty bytes. @@ -49,15 +49,6 @@ void writeFile(std::string const& _file, bytes const& _data); /// Nicely renders the given bytes to a string, optionally as HTML. std::string memDump(bytes const& _b, unsigned _w = 8, bool _html = false); -/// Converts arbitrary value to string representation using std::stringstream. -template -std::string toString(_T const& _t) -{ - std::ostringstream o; - o << _t; - return o.str(); -} - // Stream I/O functions. // Provides templated stream I/O for all STL collections so they can be shifted on to any iostream-like interface. @@ -223,4 +214,15 @@ template inline std::ostream& operator<<(std::ostream& _out, template _S& operator<<(_S& _out, std::shared_ptr<_T> const& _p) { if (_p) _out << "@" << (*_p); else _out << "nullptr"; return _out; } +// Functions that use streaming stuff. + +/// Converts arbitrary value to string representation using std::stringstream. +template +std::string toString(_T const& _t) +{ + std::ostringstream o; + o << _t; + return o.str(); +} + } diff --git a/libethential/Exceptions.h b/libdevcore/Exceptions.h similarity index 98% rename from libethential/Exceptions.h rename to libdevcore/Exceptions.h index 18ddfe5e8..15ef2019f 100644 --- a/libethential/Exceptions.h +++ b/libdevcore/Exceptions.h @@ -26,7 +26,7 @@ #include "CommonData.h" #include "FixedHash.h" -namespace eth +namespace dev { class Exception: public std::exception diff --git a/libdevcore/FixedHash.cpp b/libdevcore/FixedHash.cpp new file mode 100644 index 000000000..0369ea854 --- /dev/null +++ b/libdevcore/FixedHash.cpp @@ -0,0 +1,28 @@ +/* + 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 . +*/ +/** @file FixedHash.cpp + * @author Gav Wood + * @date 2014 + */ + +#include +#include "FixedHash.h" + +using namespace std; +using namespace dev; + +std::mt19937_64 dev::s_fixedHashEngine(time(0)); diff --git a/libethential/FixedHash.h b/libdevcore/FixedHash.h similarity index 86% rename from libethential/FixedHash.h rename to libdevcore/FixedHash.h index f792e378d..36b10d0bf 100644 --- a/libethential/FixedHash.h +++ b/libdevcore/FixedHash.h @@ -28,9 +28,11 @@ #include #include "CommonData.h" -namespace eth +namespace dev { +extern std::mt19937_64 s_fixedHashEngine; + /// Fixed-size raw-byte array container type, with an API optimised for storing hashes. /// Transparently converts to/from the corresponding arithmetic type; this will /// assume the data contained in the hash is big-endian. @@ -63,13 +65,13 @@ public: FixedHash(Arith const& _arith) { toBigEndian(_arith, m_data); } /// Explicitly construct, copying from a byte array. - explicit FixedHash(bytes const& _b) { if (_b.size() == N) memcpy(m_data.data(), _b.data(), std::min(_b.size(), N)); } + explicit FixedHash(bytes const& _b) { if (_b.size() == N) memcpy(m_data.data(), _b.data(), std::min(_b.size(), N)); } /// Explicitly construct, copying from a bytes in memory with given pointer. explicit FixedHash(byte const* _bs, ConstructFromPointerType) { memcpy(m_data.data(), _bs, N); } /// Explicitly construct, copying from a string. - explicit FixedHash(std::string const& _s, ConstructFromStringType _t = FromHex): FixedHash(_t == FromHex ? fromHex(_s) : eth::asBytes(_s)) {} + explicit FixedHash(std::string const& _s, ConstructFromStringType _t = FromHex): FixedHash(_t == FromHex ? fromHex(_s) : dev::asBytes(_s)) {} /// Convert to arithmetic type. operator Arith() const { return fromBigEndian(m_data); } @@ -83,13 +85,13 @@ public: bool operator<(FixedHash const& _c) const { return m_data < _c.m_data; } // The obvious binary operators. - FixedHash& operator^=(FixedHash const& _c) { for (auto i = 0; i < N; ++i) m_data[i] ^= _c.m_data[i]; return *this; } + FixedHash& operator^=(FixedHash const& _c) { for (unsigned i = 0; i < N; ++i) m_data[i] ^= _c.m_data[i]; return *this; } FixedHash operator^(FixedHash const& _c) const { return FixedHash(*this) ^= _c; } - FixedHash& operator|=(FixedHash const& _c) { for (auto i = 0; i < N; ++i) m_data[i] |= _c.m_data[i]; return *this; } + FixedHash& operator|=(FixedHash const& _c) { for (unsigned i = 0; i < N; ++i) m_data[i] |= _c.m_data[i]; return *this; } FixedHash operator|(FixedHash const& _c) const { return FixedHash(*this) |= _c; } - FixedHash& operator&=(FixedHash const& _c) { for (auto i = 0; i < N; ++i) m_data[i] &= _c.m_data[i]; return *this; } + FixedHash& operator&=(FixedHash const& _c) { for (unsigned i = 0; i < N; ++i) m_data[i] &= _c.m_data[i]; return *this; } FixedHash operator&(FixedHash const& _c) const { return FixedHash(*this) &= _c; } - FixedHash& operator~() { for (auto i = 0; i < N; ++i) m_data[i] = ~m_data[i]; return *this; } + FixedHash& operator~() { for (unsigned i = 0; i < N; ++i) m_data[i] = ~m_data[i]; return *this; } /// @returns true if all bytes in @a _c are set in this object. bool contains(FixedHash const& _c) const { return (*this & _c) == _c; } @@ -100,7 +102,7 @@ public: byte operator[](unsigned _i) const { return m_data[_i]; } /// @returns an abridged version of the hash as a user-readable hex string. - std::string abridged() const { return toHex(ref().cropped(0, 4)) + ".."; } + std::string abridged() const { return toHex(ref().cropped(0, 4)) + "\342\200\246"; } /// @returns a mutable byte vector_ref to the object's data. bytesRef ref() { return bytesRef(m_data.data(), N); } @@ -133,6 +135,8 @@ public: return ret; } + static FixedHash random() { return random(s_fixedHashEngine); } + /// A generic std::hash compatible function object. struct hash { @@ -154,16 +158,14 @@ public: return ret; } - private: std::array m_data; ///< The binary data. }; - /// Fast equality operator for h256. template<> inline bool FixedHash<32>::operator==(FixedHash<32> const& _other) const { - const uint64_t* hash1 = (const uint64_t*)this->data(); + const uint64_t* hash1 = (const uint64_t*)data(); const uint64_t* hash2 = (const uint64_t*)_other.data(); return (hash1[0] == hash2[0]) && (hash1[1] == hash2[1]) && (hash1[2] == hash2[2]) && (hash1[3] == hash2[3]); } @@ -215,10 +217,20 @@ inline h160 left160(h256 const& _t) return ret; } +inline std::string toString(h256s const& _bs) +{ + std::ostringstream out; + out << "[ "; + for (auto i: _bs) + out << i.abridged() << ", "; + out << "]"; + return out.str(); +} + } namespace std { - /// Forward std::hash to eth::h256::hash. - template<> struct hash: eth::h256::hash {}; + /// Forward std::hash to dev::h256::hash. + template<> struct hash: dev::h256::hash {}; } diff --git a/libethential/FixedHash.cpp b/libdevcore/Guards.cpp similarity index 90% rename from libethential/FixedHash.cpp rename to libdevcore/Guards.cpp index 9d16cacb2..69ae6c03a 100644 --- a/libethential/FixedHash.cpp +++ b/libdevcore/Guards.cpp @@ -14,12 +14,16 @@ You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ -/** @file FixedHash.cpp +/** @file Guards.cpp * @author Gav Wood * @date 2014 */ -#include "FixedHash.h" - +#include "Guards.h" using namespace std; -using namespace eth; +using namespace dev; + +namespace dev +{ + +} diff --git a/libdevcore/Guards.h b/libdevcore/Guards.h new file mode 100644 index 000000000..f5c64b041 --- /dev/null +++ b/libdevcore/Guards.h @@ -0,0 +1,41 @@ +/* + 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 . +*/ +/** @file Guards.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include + +namespace dev +{ + +using Mutex = std::mutex; +using RecursiveMutex = std::recursive_mutex; +using SharedMutex = boost::shared_mutex; + +using Guard = std::lock_guard; +using RecursiveGuard = std::lock_guard; +using ReadGuard = boost::shared_lock; +using UpgradableGuard = boost::upgrade_lock; +using UpgradeGuard = boost::upgrade_to_unique_lock; +using WriteGuard = boost::unique_lock; + +} diff --git a/libethential/Log.cpp b/libdevcore/Log.cpp similarity index 78% rename from libethential/Log.cpp rename to libdevcore/Log.cpp index 952069830..1c7a34932 100644 --- a/libethential/Log.cpp +++ b/libdevcore/Log.cpp @@ -23,22 +23,26 @@ #include #include +#include "Guards.h" using namespace std; -using namespace eth; +using namespace dev; // Logging -int eth::g_logVerbosity = 5; -map eth::g_logOverride; +int dev::g_logVerbosity = 5; +map dev::g_logOverride; -ThreadLocalLogName eth::t_logThreadName("main"); +ThreadLocalLogName dev::t_logThreadName("main"); // foward declare without all of Windows.h #ifdef _WIN32 extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char* lpOutputString); #endif -void eth::simpleDebugOut(std::string const& _s, char const*) +void dev::simpleDebugOut(std::string const& _s, char const*) { + static Mutex s_lock; + Guard l(s_lock); + cout << _s << endl << flush; // helpful to use OutputDebugString on windows @@ -50,5 +54,5 @@ void eth::simpleDebugOut(std::string const& _s, char const*) #endif } -std::function eth::g_logPost = simpleDebugOut; +std::function dev::g_logPost = simpleDebugOut; diff --git a/libethential/Log.h b/libdevcore/Log.h similarity index 90% rename from libethential/Log.h rename to libdevcore/Log.h index ea7a06233..704660276 100644 --- a/libethential/Log.h +++ b/libdevcore/Log.h @@ -28,7 +28,7 @@ #include #include "vector_ref.h" -namespace eth +namespace dev { /// The null output stream. Used when logging is disabled. @@ -39,7 +39,7 @@ public: }; /// A simple log-output function that prints log messages to stdout. -void simpleDebugOut(std::string const&, char const* ); +void simpleDebugOut(std::string const&, char const*); /// The logging system's current verbosity. extern int g_logVerbosity; @@ -107,19 +107,19 @@ private: // Simple cout-like stream objects for accessing common log channels. // Dirties the global namespace, but oh so convenient... -#define cnote eth::LogOutputStream() -#define cwarn eth::LogOutputStream() +#define cnote dev::LogOutputStream() +#define cwarn dev::LogOutputStream() // Null stream-like objects. -#define ndebug if (true) {} else eth::NullOutputStream() -#define nlog(X) if (true) {} else eth::NullOutputStream() -#define nslog(X) if (true) {} else eth::NullOutputStream() +#define ndebug if (true) {} else dev::NullOutputStream() +#define nlog(X) if (true) {} else dev::NullOutputStream() +#define nslog(X) if (true) {} else dev::NullOutputStream() // Kill debugging log channel when we're in release mode. #if NDEBUG #define cdebug ndebug #else -#define cdebug eth::LogOutputStream() +#define cdebug dev::LogOutputStream() #endif // Kill all logs when when NLOG is defined. @@ -127,8 +127,8 @@ private: #define clog(X) nlog(X) #define cslog(X) nslog(X) #else -#define clog(X) eth::LogOutputStream() -#define cslog(X) eth::LogOutputStream() +#define clog(X) dev::LogOutputStream() +#define cslog(X) dev::LogOutputStream() #endif } diff --git a/libethential/RLP.cpp b/libdevcore/RLP.cpp similarity index 87% rename from libethential/RLP.cpp rename to libdevcore/RLP.cpp index e8647e8de..68ae02065 100644 --- a/libethential/RLP.cpp +++ b/libdevcore/RLP.cpp @@ -21,10 +21,10 @@ #include "RLP.h" using namespace std; -using namespace eth; +using namespace dev; -bytes eth::RLPNull = rlp(""); -bytes eth::RLPEmptyList = rlpList(); +bytes dev::RLPNull = rlp(""); +bytes dev::RLPEmptyList = rlpList(); RLP::iterator& RLP::iterator::operator++() { @@ -32,7 +32,7 @@ RLP::iterator& RLP::iterator::operator++() { m_lastItem.retarget(m_lastItem.next().data(), m_remaining); m_lastItem = m_lastItem.cropped(0, RLP(m_lastItem).actualSize()); - m_remaining -= std::min(m_remaining, m_lastItem.size()); + m_remaining -= std::min(m_remaining, m_lastItem.size()); } else m_lastItem.retarget(m_lastItem.next().data(), 0); @@ -54,7 +54,7 @@ RLP::iterator::iterator(RLP const& _parent, bool _begin) } } -RLP RLP::operator[](uint _i) const +RLP RLP::operator[](unsigned _i) const { if (_i < m_lastIndex) { @@ -81,7 +81,7 @@ RLPs RLP::toList() const return ret; } -eth::uint RLP::actualSize() const +unsigned RLP::actualSize() const { if (isNull()) return 0; @@ -118,11 +118,11 @@ bool RLP::isInt() const return false; } -eth::uint RLP::length() const +unsigned RLP::length() const { if (isNull()) return 0; - uint ret = 0; + unsigned ret = 0; byte n = m_data[0]; if (n < c_rlpDataImmLenStart) return 1; @@ -147,12 +147,12 @@ eth::uint RLP::length() const return ret; } -eth::uint RLP::items() const +unsigned RLP::items() const { if (isList()) { bytesConstRef d = payload().cropped(0, length()); - eth::uint i = 0; + unsigned i = 0; for (; d.size(); ++i) d = d.cropped(RLP(d).actualSize()); return i; @@ -160,16 +160,16 @@ eth::uint RLP::items() const return 0; } -RLPStream& RLPStream::appendRaw(bytesConstRef _s, uint _itemCount) +RLPStream& RLPStream::appendRaw(bytesConstRef _s, unsigned _itemCount) { - uint os = m_out.size(); + unsigned os = m_out.size(); m_out.resize(os + _s.size()); memcpy(m_out.data() + os, _s.data(), _s.size()); noteAppended(_itemCount); return *this; } -void RLPStream::noteAppended(uint _itemCount) +void RLPStream::noteAppended(unsigned _itemCount) { if (!_itemCount) return; @@ -184,9 +184,9 @@ void RLPStream::noteAppended(uint _itemCount) { auto p = m_listStack.back().second; m_listStack.pop_back(); - uint s = m_out.size() - p; // list size + unsigned s = m_out.size() - p; // list size auto brs = bytesRequired(s); - uint encodeSize = s < c_rlpListImmLenCount ? 1 : (1 + brs); + unsigned encodeSize = s < c_rlpListImmLenCount ? 1 : (1 + brs); // cdebug << "s: " << s << ", p: " << p << ", m_out.size(): " << m_out.size() << ", encodeSize: " << encodeSize << " (br: " << brs << ")"; auto os = m_out.size(); m_out.resize(os + encodeSize); @@ -205,7 +205,7 @@ void RLPStream::noteAppended(uint _itemCount) } } -RLPStream& RLPStream::appendList(uint _items) +RLPStream& RLPStream::appendList(unsigned _items) { // cdebug << "appendList(" << _items << ")"; if (_items) @@ -227,7 +227,7 @@ RLPStream& RLPStream::appendList(bytesConstRef _rlp) RLPStream& RLPStream::append(bytesConstRef _s, bool _compact) { - uint s = _s.size(); + unsigned s = _s.size(); byte const* d = _s.data(); if (_compact) for (unsigned i = 0; i < _s.size() && !*d; ++i, --s, ++d) {} @@ -254,7 +254,7 @@ RLPStream& RLPStream::append(bigint _i) m_out.push_back((byte)_i); else { - uint br = bytesRequired(_i); + unsigned br = bytesRequired(_i); if (br < c_rlpDataImmLenCount) m_out.push_back((byte)(br + c_rlpDataImmLenStart)); else @@ -269,21 +269,21 @@ RLPStream& RLPStream::append(bigint _i) return *this; } -void RLPStream::pushCount(uint _count, byte _base) +void RLPStream::pushCount(unsigned _count, byte _base) { auto br = bytesRequired(_count); m_out.push_back((byte)(br + _base)); // max 8 bytes. pushInt(_count, br); } -std::ostream& eth::operator<<(std::ostream& _out, eth::RLP const& _d) +std::ostream& dev::operator<<(std::ostream& _out, RLP const& _d) { if (_d.isNull()) _out << "null"; else if (_d.isInt()) _out << std::showbase << std::hex << std::nouppercase << _d.toInt(RLP::LaisezFaire) << dec; else if (_d.isData()) - _out << eth::escaped(_d.toString(), false); + _out << escaped(_d.toString(), false); else if (_d.isList()) { _out << "["; diff --git a/libethential/RLP.h b/libdevcore/RLP.h similarity index 75% rename from libethential/RLP.h rename to libdevcore/RLP.h index a3f42a783..35e1d1dcf 100644 --- a/libethential/RLP.h +++ b/libdevcore/RLP.h @@ -28,21 +28,21 @@ #include #include #include -#include -#include -#include -#include +#include +#include +#include +#include -namespace eth +namespace dev { class RLP; typedef std::vector RLPs; -template struct intTraits { static const uint maxSize = sizeof(_T); }; -template <> struct intTraits { static const uint maxSize = 20; }; -template <> struct intTraits { static const uint maxSize = 32; }; -template <> struct intTraits { static const uint maxSize = ~(uint)0; }; +template struct intTraits { static const unsigned maxSize = sizeof(_T); }; +template <> struct intTraits { static const unsigned maxSize = 20; }; +template <> struct intTraits { static const unsigned maxSize = 32; }; +template <> struct intTraits { static const unsigned maxSize = ~(unsigned)0; }; static const byte c_rlpMaxLengthBytes = 8; static const byte c_rlpDataImmLenStart = 0x80; @@ -72,7 +72,7 @@ public: explicit RLP(bytes const& _d): m_data(&_d) {} /// Construct a node to read RLP data in the bytes given. - RLP(byte const* _b, uint _s): m_data(bytesConstRef(_b, _s)) {} + RLP(byte const* _b, unsigned _s): m_data(bytesConstRef(_b, _s)) {} /// Construct a node to read RLP data in the string. explicit RLP(std::string const& _s): m_data(bytesConstRef((byte const*)_s.data(), _s.size())) {} @@ -99,12 +99,12 @@ public: bool isInt() const; /// @returns the number of items in the list, or zero if it isn't a list. - uint itemCount() const { return isList() ? items() : 0; } - uint itemCountStrict() const { if (!isList()) throw BadCast(); return items(); } + unsigned itemCount() const { return isList() ? items() : 0; } + unsigned itemCountStrict() const { if (!isList()) throw BadCast(); return items(); } /// @returns the number of bytes in the data, or zero if it isn't data. - uint size() const { return isData() ? length() : 0; } - uint sizeStrict() const { if (!isData()) throw BadCast(); return length(); } + unsigned size() const { return isData() ? length() : 0; } + unsigned sizeStrict() const { if (!isData()) throw BadCast(); return length(); } /// Equality operators; does best-effort conversion and checks for equality. bool operator==(char const* _s) const { return isData() && toString() == _s; } @@ -113,8 +113,8 @@ public: bool operator!=(std::string const& _s) const { return isData() && toString() != _s; } template bool operator==(FixedHash<_N> const& _h) const { return isData() && toHash<_N>() == _h; } template bool operator!=(FixedHash<_N> const& _s) const { return isData() && toHash<_N>() != _s; } - bool operator==(uint const& _i) const { return isInt() && toInt() == _i; } - bool operator!=(uint const& _i) const { return isInt() && toInt() != _i; } + bool operator==(unsigned const& _i) const { return isInt() && toInt() == _i; } + bool operator!=(unsigned const& _i) const { return isInt() && toInt() != _i; } bool operator==(u256 const& _i) const { return isInt() && toInt() == _i; } bool operator!=(u256 const& _i) const { return isInt() && toInt() != _i; } bool operator==(bigint const& _i) const { return isInt() && toInt() == _i; } @@ -123,7 +123,7 @@ public: /// Subscript operator. /// @returns the list item @a _i if isList() and @a _i < listItems(), or RLP() otherwise. /// @note if used to access items in ascending order, this is efficient. - RLP operator[](uint _i) const; + RLP operator[](unsigned _i) const; typedef RLP element_type; @@ -146,7 +146,7 @@ public: iterator() {} iterator(RLP const& _parent, bool _begin); - uint m_remaining = 0; + unsigned m_remaining = 0; bytesConstRef m_lastItem; }; @@ -160,10 +160,13 @@ public: explicit operator std::string() const { return toString(); } explicit operator RLPs() const { return toList(); } explicit operator byte() const { return toInt(); } - explicit operator uint() const { return toInt(); } explicit operator u256() const { return toInt(); } explicit operator bigint() const { return toInt(); } template explicit operator FixedHash<_N>() const { return toHash>(); } + template explicit operator std::pair() const { return toPair(); } + template explicit operator std::vector() const { return toVector(); } + template explicit operator std::set() const { return toSet(); } + template explicit operator std::array() const { return toArray(); } /// Converts to bytearray. @returns the empty byte array if not a string. bytes toBytes() const { if (!isData()) return bytes(); return bytes(payload().data(), payload().data() + length()); } @@ -174,8 +177,59 @@ public: /// Converts to string. @throws BadCast if not a string. std::string toStringStrict() const { if (!isData()) throw BadCast(); return payload().cropped(0, length()).toString(); } - template std::vector toVector() const { std::vector ret; if (isList()) { ret.reserve(itemCount()); for (auto const& i: *this) ret.push_back((T)i); } return ret; } - template std::array toArray() const { if (itemCount() != N || !isList()) throw BadCast(); std::array ret; for (uint i = 0; i < N; ++i) ret[i] = (T)operator[](i); return ret; } + template + std::vector toVector() const + { + std::vector ret; + if (isList()) + { + ret.reserve(itemCount()); + for (auto const& i: *this) + { + ret.push_back((T)i); + } + } + return ret; + } + + template + std::set toSet() const + { + std::set ret; + if (isList()) + { + for (auto const& i: *this) + { + ret.insert((T)i); + } + } + return ret; + } + + template + std::pair toPair() const + { + std::pair ret; + if (isList()) + { + ret.first = (T)(*this)[0]; + ret.second = (U)(*this)[1]; + } + return ret; + } + + template + std::array toArray() const + { + if (itemCount() != N || !isList()) + throw BadCast(); + std::array ret; + for (unsigned i = 0; i < N; ++i) + { + ret[i] = (T)operator[](i); + } + return ret; + } /// Int conversion flags enum @@ -188,7 +242,7 @@ public: }; /// Converts to int of type given; if isString(), decodes as big-endian bytestream. @returns 0 if not an int or string. - template _T toInt(int _flags = Strict) const + template _T toInt(int _flags = Strict) const { if ((!isInt() && !(_flags & AllowNonCanon)) || isList() || isNull()) if (_flags & ThrowOnFail) @@ -231,27 +285,27 @@ public: /// @returns the theoretical size of this item. /// @note Under normal circumstances, is equivalent to m_data.size() - use that unless you know it won't work. - uint actualSize() const; + unsigned actualSize() const; private: /// Single-byte data payload. bool isSingleByte() const { return !isNull() && m_data[0] < c_rlpDataImmLenStart; } /// @returns the bytes used to encode the length of the data. Valid for all types. - uint lengthSize() const { if (isData() && m_data[0] > c_rlpDataIndLenZero) return m_data[0] - c_rlpDataIndLenZero; if (isList() && m_data[0] > c_rlpListIndLenZero) return m_data[0] - c_rlpListIndLenZero; return 0; } + unsigned lengthSize() const { if (isData() && m_data[0] > c_rlpDataIndLenZero) return m_data[0] - c_rlpDataIndLenZero; if (isList() && m_data[0] > c_rlpListIndLenZero) return m_data[0] - c_rlpListIndLenZero; return 0; } /// @returns the size in bytes of the payload, as given by the RLP as opposed to as inferred from m_data. - uint length() const; + unsigned length() const; /// @returns the number of data items. - uint items() const; + unsigned items() const; /// Our byte data. bytesConstRef m_data; /// The list-indexing cache. - mutable uint m_lastIndex = (uint)-1; - mutable uint m_lastEnd = 0; + mutable unsigned m_lastIndex = (unsigned)-1; + mutable unsigned m_lastEnd = 0; mutable bytesConstRef m_lastItem; }; @@ -265,12 +319,12 @@ public: RLPStream() {} /// Initializes the RLPStream as a list of @a _listItems items. - explicit RLPStream(uint _listItems) { appendList(_listItems); } + explicit RLPStream(unsigned _listItems) { appendList(_listItems); } ~RLPStream() {} /// Append given datum to the byte stream. - RLPStream& append(uint _s) { return append(bigint(_s)); } + RLPStream& append(unsigned _s) { return append(bigint(_s)); } RLPStream& append(u160 _s) { return append(bigint(_s)); } RLPStream& append(u256 _s) { return append(bigint(_s)); } RLPStream& append(bigint _s); @@ -281,22 +335,24 @@ public: template RLPStream& append(FixedHash _s, bool _compact = false, bool _allOrNothing = false) { return _allOrNothing && !_s ? append(bytesConstRef()) : append(_s.ref(), _compact); } /// Appends an arbitrary RLP fragment - this *must* be a single item. - RLPStream& append(RLP const& _rlp, uint _itemCount = 1) { return appendRaw(_rlp.data(), _itemCount); } + RLPStream& append(RLP const& _rlp, unsigned _itemCount = 1) { return appendRaw(_rlp.data(), _itemCount); } /// Appends a sequence of data to the stream as a list. template RLPStream& append(std::vector<_T> const& _s) { return appendVector(_s); } - template RLPStream& append(std::array<_T, S> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; } template RLPStream& appendVector(std::vector<_T> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; } + template RLPStream& append(std::array<_T, S> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; } + template RLPStream& append(std::set<_T> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; } + template RLPStream& append(std::pair const& _s) { appendList(2); append(_s.first); append(_s.second); return *this; } /// Appends a list. - RLPStream& appendList(uint _items); + RLPStream& appendList(unsigned _items); RLPStream& appendList(bytesConstRef _rlp); RLPStream& appendList(bytes const& _rlp) { return appendList(&_rlp); } RLPStream& appendList(RLPStream const& _s) { return appendList(&_s.out()); } /// Appends raw (pre-serialised) RLP data. Use with caution. - RLPStream& appendRaw(bytesConstRef _rlp, uint _itemCount = 1); - RLPStream& appendRaw(bytes const& _rlp, uint _itemCount = 1) { return appendRaw(&_rlp, _itemCount); } + RLPStream& appendRaw(bytesConstRef _rlp, unsigned _itemCount = 1); + RLPStream& appendRaw(bytes const& _rlp, unsigned _itemCount = 1) { return appendRaw(&_rlp, _itemCount); } /// Shift operators for appending data items. template RLPStream& operator<<(T _data) { return append(_data); } @@ -311,14 +367,14 @@ public: void swapOut(bytes& _dest) { assert(m_listStack.empty()); swap(m_out, _dest); } private: - void noteAppended(uint _itemCount = 1); + void noteAppended(unsigned _itemCount = 1); /// Push the node-type byte (using @a _base) along with the item count @a _count. /// @arg _count is number of characters for strings, data-bytes for ints, or items for lists. - void pushCount(uint _count, byte _offset); + void pushCount(unsigned _count, byte _offset); /// Push an integer as a raw big-endian byte-stream. - template void pushInt(_T _i, uint _br) + template void pushInt(_T _i, unsigned _br) { m_out.resize(m_out.size() + _br); byte* b = &m_out.back(); @@ -329,7 +385,7 @@ private: /// Our output byte stream. bytes m_out; - std::vector> m_listStack; + std::vector> m_listStack; }; template void rlpListAux(RLPStream& _out, _T _t) { _out << _t; } @@ -354,6 +410,6 @@ extern bytes RLPNull; extern bytes RLPEmptyList; /// Human readable version of RLP. -std::ostream& operator<<(std::ostream& _out, eth::RLP const& _d); +std::ostream& operator<<(std::ostream& _out, dev::RLP const& _d); } diff --git a/libdevcore/RangeMask.cpp b/libdevcore/RangeMask.cpp new file mode 100644 index 000000000..5317e00e8 --- /dev/null +++ b/libdevcore/RangeMask.cpp @@ -0,0 +1,22 @@ +/* + 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 . +*/ +/** @file RangeMask.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "RangeMask.h" diff --git a/libdevcore/RangeMask.h b/libdevcore/RangeMask.h new file mode 100644 index 000000000..f1b0043ff --- /dev/null +++ b/libdevcore/RangeMask.h @@ -0,0 +1,212 @@ +/* + 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 . +*/ +/** @file EthereumHost.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include +#include + +namespace dev +{ + +class RLPStream; + +using UnsignedRange = std::pair; +using UnsignedRanges = std::vector; + +template +class RangeMask +{ + template friend std::ostream& operator<<(std::ostream& _out, RangeMask const& _r); + +public: + using Range = std::pair; + using Ranges = std::vector; + + RangeMask() {} + RangeMask(T _begin, T _end): m_all(_begin, _end) {} + RangeMask(Range const& _c): m_all(_c) {} + + RangeMask operator+(RangeMask const& _m) const { return RangeMask(*this) += _m; } + + RangeMask lowest(T _items) const + { + RangeMask ret(m_all); + for (auto i = m_ranges.begin(); i != m_ranges.end() && _items; ++i) + _items -= (ret.m_ranges[i->first] = std::min(i->first + _items, i->second)) - i->first; + return ret; + } + + RangeMask operator~() const + { + RangeMask ret(m_all); + T last = m_all.first; + for (auto i: m_ranges) + { + if (i.first != last) + ret.m_ranges[last] = i.first; + last = i.second; + } + if (last != m_all.second) + ret.m_ranges[last] = m_all.second; + return ret; + } + + RangeMask& operator+=(RangeMask const& _m) + { + for (auto const& i: _m.m_ranges) + operator+=(i); + return *this; + } + RangeMask& operator+=(UnsignedRange const& _m) + { + for (auto i = _m.first; i < _m.second;) + { + // for each number, we find the element equal or next lower. this, if any, must contain the value. + auto uit = m_ranges.upper_bound(i); + auto it = uit == m_ranges.begin() ? m_ranges.end() : std::prev(uit); + if (it == m_ranges.end() || it->second < i) + // lower range is too low to merge. + // if the next higher range is too high. + if (uit == m_ranges.end() || uit->first > _m.second) + { + // just create a new range + m_ranges[i] = _m.second; + break; + } + else + { + if (uit->first == i) + // move i to end of range + i = uit->second; + else + { + // merge with the next higher range + // move i to end of range + i = m_ranges[i] = uit->second; + i = uit->second; + m_ranges.erase(uit); + } + } + else if (it->second == i) + { + // if the next higher range is too high. + if (uit == m_ranges.end() || uit->first > _m.second) + { + // merge with the next lower range + m_ranges[it->first] = _m.second; + break; + } + else + { + // merge with both next lower & next higher. + i = m_ranges[it->first] = uit->second; + m_ranges.erase(uit); + } + } + else + i = it->second; + } + return *this; + } + + RangeMask& operator+=(T _i) + { + return operator+=(Range(_i, _i + 1)); + } + + bool contains(T _i) const + { + auto it = m_ranges.upper_bound(_i); + if (it == m_ranges.begin()) + return false; + return (--it)->second > _i; + } + + bool empty() const + { + return m_ranges.empty(); + } + + bool full() const + { + return m_ranges.size() == 1 && m_ranges.begin()->first == m_all.first && m_ranges.begin()->second == m_all.second; + } + + void clear() + { + m_ranges.clear(); + } + + std::pair const& all() const { return m_all; } + + class const_iterator + { + friend class RangeMask; + + public: + const_iterator() {} + + T operator*() const { return m_value; } + const_iterator& operator++() { if (m_owner) m_value = m_owner->next(m_value); return *this; } + const_iterator operator++(int) { auto ret = *this; if (m_owner) m_value = m_owner->next(m_value); return ret; } + + bool operator==(const_iterator const& _i) const { return m_owner == _i.m_owner && m_value == _i.m_value; } + bool operator!=(const_iterator const& _i) const { return !operator==(_i); } + bool operator<(const_iterator const& _i) const { return m_value < _i.m_value; } + + private: + const_iterator(RangeMask const& _m, bool _end): m_owner(&_m), m_value(_m.m_ranges.empty() || _end ? _m.m_all.second : _m.m_ranges.begin()->first) {} + + RangeMask const* m_owner = nullptr; + T m_value = 0; + }; + + const_iterator begin() const { return const_iterator(*this, false); } + const_iterator end() const { return const_iterator(*this, true); } + T next(T _t) const + { + _t++; + // find next item in range at least _t + auto uit = m_ranges.upper_bound(_t); // > _t + auto it = uit == m_ranges.begin() ? m_ranges.end() : std::prev(uit); + if (it != m_ranges.end() && it->first <= _t && it->second > _t) + return _t; + return uit == m_ranges.end() ? m_all.second : uit->first; + } + +private: + UnsignedRange m_all; + std::map m_ranges; +}; + +template inline std::ostream& operator<<(std::ostream& _out, RangeMask const& _r) +{ + _out << _r.m_all.first << "{ "; + for (auto const& i: _r.m_ranges) + _out << "[" << i.first << ", " << i.second << ") "; + _out << "}" << _r.m_all.second; + return _out; +} + +} diff --git a/libdevcore/Worker.cpp b/libdevcore/Worker.cpp new file mode 100644 index 000000000..29ff766d7 --- /dev/null +++ b/libdevcore/Worker.cpp @@ -0,0 +1,63 @@ +/* + 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 . +*/ +/** @file Worker.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "Worker.h" + +#include +#include +#include "Log.h" +using namespace std; +using namespace dev; + +void Worker::startWorking() +{ + cdebug << "startWorking for thread" << m_name; + Guard l(x_work); + if (m_work) + return; + cdebug << "Spawning" << m_name; + m_stop = false; + m_work.reset(new thread([&]() + { + setThreadName(m_name.c_str()); + while (!m_stop) + { + this_thread::sleep_for(chrono::milliseconds(30)); + doWork(); + } + cdebug << "Finishing up worker thread"; + doneWorking(); + })); +} + +void Worker::stopWorking() +{ + cdebug << "stopWorking for thread" << m_name; + Guard l(x_work); + if (!m_work) + return; + cdebug << "Stopping" << m_name; + m_stop = true; + m_work->join(); + m_work.reset(); + cdebug << "Stopped" << m_name; +} + diff --git a/libdevcore/Worker.h b/libdevcore/Worker.h new file mode 100644 index 000000000..8f0baaf60 --- /dev/null +++ b/libdevcore/Worker.h @@ -0,0 +1,59 @@ +/* + 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 . +*/ +/** @file Worker.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include "Guards.h" + +namespace dev +{ + +class Worker +{ +protected: + Worker(std::string const& _name = "anon"): m_name(_name) {} + + /// Move-constructor. + Worker(Worker&& _m) { std::swap(m_name, _m.m_name); } + + /// Move-assignment. + Worker& operator=(Worker&& _m) { std::swap(m_name, _m.m_name); return *this; } + + virtual ~Worker() { stopWorking(); } + + void setName(std::string _n) { if (!isWorking()) m_name = _n; } + + void startWorking(); + void stopWorking(); + bool isWorking() const { Guard l(x_work); return !!m_work; } + virtual void doWork() = 0; + virtual void doneWorking() {} + +private: + mutable Mutex x_work; ///< Lock for the network existance. + std::unique_ptr m_work; ///< The network thread. + bool m_stop = false; + std::string m_name; +}; + +} diff --git a/libdevcore/_libdevcore.cpp b/libdevcore/_libdevcore.cpp new file mode 100644 index 000000000..4160a7602 --- /dev/null +++ b/libdevcore/_libdevcore.cpp @@ -0,0 +1,10 @@ +#ifdef _MSC_VER +#include "All.h" +#include "Common.cpp" +#include "CommonData.cpp" +#include "CommonIO.cpp" +#include "FixedHash.cpp" +#include "Guards.cpp" +#include "Log.cpp" +#include "RLP.cpp" +#endif diff --git a/libethential/vector_ref.h b/libdevcore/vector_ref.h similarity index 96% rename from libethential/vector_ref.h rename to libdevcore/vector_ref.h index 4bde3f582..be2fea13c 100644 --- a/libethential/vector_ref.h +++ b/libdevcore/vector_ref.h @@ -5,12 +5,7 @@ #include #include -#pragma warning(push) -#pragma warning(disable: 4100 4267) -#include -#pragma warning(pop) - -namespace eth +namespace dev { template @@ -26,8 +21,9 @@ public: vector_ref(std::string* _data): m_data((_T*)_data->data()), m_count(_data->size() / sizeof(_T)) {} vector_ref(typename std::conditional::value, std::vector::type> const*, std::vector<_T>*>::type _data): m_data(_data->data()), m_count(_data->size()) {} vector_ref(typename std::conditional::value, std::string const&, std::string&>::type _data): m_data((_T*)_data.data()), m_count(_data.size() / sizeof(_T)) {} +#ifdef STORAGE_LEVELDB_INCLUDE_DB_H_ vector_ref(leveldb::Slice const& _s): m_data(_s.data()), m_count(_s.size() / sizeof(_T)) {} - +#endif explicit operator bool() const { return m_data && m_count; } bool contentsEqual(std::vector const& _c) const { return _c.size() == m_count && !memcmp(_c.data(), m_data, m_count); } @@ -56,7 +52,9 @@ public: bool operator==(vector_ref<_T> const& _cmp) const { return m_data == _cmp.m_data && m_count == _cmp.m_count; } bool operator!=(vector_ref<_T> const& _cmp) const { return !operator==(_cmp); } +#ifdef STORAGE_LEVELDB_INCLUDE_DB_H_ operator leveldb::Slice() const { return leveldb::Slice((char const*)m_data, m_count * sizeof(_T)); } +#endif void reset() { m_data = nullptr; m_count = 0; } diff --git a/libdevcrypto/All.h b/libdevcrypto/All.h new file mode 100644 index 000000000..6d1844857 --- /dev/null +++ b/libdevcrypto/All.h @@ -0,0 +1,9 @@ +#pragma once + +#include "Common.h" +#include "FileSystem.h" +#include "MemoryDB.h" +#include "OverlayDB.h" +#include "SHA3.h" +#include "TrieCommon.h" +#include "TrieDB.h" diff --git a/libdevcrypto/CMakeLists.txt b/libdevcrypto/CMakeLists.txt new file mode 100644 index 000000000..e7f112f95 --- /dev/null +++ b/libdevcrypto/CMakeLists.txt @@ -0,0 +1,56 @@ +cmake_policy(SET CMP0015 NEW) + +aux_source_directory(. SRC_LIST) + +set(EXECUTABLE devcrypto) + +# set(CMAKE_INSTALL_PREFIX ../lib) +if(ETH_STATIC) + add_library(${EXECUTABLE} STATIC ${SRC_LIST}) +else() + add_library(${EXECUTABLE} SHARED ${SRC_LIST}) +endif() + +file(GLOB HEADERS "*.h") + +include_directories(..) + +target_link_libraries(${EXECUTABLE} devcore) +target_link_libraries(${EXECUTABLE} secp256k1) +target_link_libraries(${EXECUTABLE} gmp) +target_link_libraries(${EXECUTABLE} ${LEVELDB_LS}) +target_link_libraries(${EXECUTABLE} ${CRYPTOPP_LS}) + +if("${TARGET_PLATFORM}" STREQUAL "w64") + target_link_libraries(${EXECUTABLE} boost_system-mt-s) + target_link_libraries(${EXECUTABLE} boost_filesystem-mt-s) + target_link_libraries(${EXECUTABLE} boost_thread_win32-mt-s) + target_link_libraries(${EXECUTABLE} iphlpapi) + target_link_libraries(${EXECUTABLE} ws2_32) + target_link_libraries(${EXECUTABLE} mswsock) + target_link_libraries(${EXECUTABLE} shlwapi) +elseif (APPLE) + # Latest mavericks boost libraries only come with -mt + target_link_libraries(${EXECUTABLE} boost_system-mt) + target_link_libraries(${EXECUTABLE} boost_filesystem-mt) + target_link_libraries(${EXECUTABLE} boost_thread-mt) + find_package(Threads REQUIRED) + target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) +elseif (UNIX) + find_package(Boost 1.53 REQUIRED COMPONENTS filesystem) + target_link_libraries(${EXECUTABLE} ${Boost_SYSTEM_LIBRARY}) + target_link_libraries(${EXECUTABLE} ${Boost_FILESYSTEM_LIBRARY}) + target_link_libraries(${EXECUTABLE} ${Boost_THREAD_LIBRARY}) + target_link_libraries(${EXECUTABLE} ${Boost_DATE_TIME_LIBRARY}) + target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) +else () + target_link_libraries(${EXECUTABLE} boost_system) + target_link_libraries(${EXECUTABLE} boost_filesystem) + target_link_libraries(${EXECUTABLE} boost_thread) + find_package(Threads REQUIRED) + target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) +endif () + +install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) +install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) + diff --git a/libdevcrypto/Common.cpp b/libdevcrypto/Common.cpp new file mode 100644 index 000000000..ddc7b0b4b --- /dev/null +++ b/libdevcrypto/Common.cpp @@ -0,0 +1,104 @@ +/* + 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 . +*/ +/** @file CommonEth.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "Common.h" +#include +#include +#include "SHA3.h" +using namespace std; +using namespace dev; +using namespace dev::eth; + +//#define ETH_ADDRESS_DEBUG 1 + +Address dev::toAddress(Secret _private) +{ + secp256k1_start(); + + byte pubkey[65]; + int pubkeylen = 65; + int ok = secp256k1_ecdsa_seckey_verify(_private.data()); + if (!ok) + return Address(); + ok = secp256k1_ecdsa_pubkey_create(pubkey, &pubkeylen, _private.data(), 0); + assert(pubkeylen == 65); + if (!ok) + return Address(); + ok = secp256k1_ecdsa_pubkey_verify(pubkey, 65); + if (!ok) + return Address(); + auto ret = right160(dev::eth::sha3(bytesConstRef(&(pubkey[1]), 64))); +#if ETH_ADDRESS_DEBUG + cout << "---- ADDRESS -------------------------------" << endl; + cout << "SEC: " << _private << endl; + cout << "PUB: " << toHex(bytesConstRef(&(pubkey[1]), 64)) << endl; + cout << "ADR: " << ret << endl; +#endif + return ret; +} + +KeyPair KeyPair::create() +{ + secp256k1_start(); + static std::mt19937_64 s_eng(time(0)); + std::uniform_int_distribution d(0, 255); + + for (int i = 0; i < 100; ++i) + { + h256 sec; + for (unsigned i = 0; i < 32; ++i) + sec[i] = (byte)d(s_eng); + + KeyPair ret(sec); + if (ret.address()) + return ret; + } + return KeyPair(); +} + +KeyPair::KeyPair(h256 _sec): + m_secret(_sec) +{ + int ok = secp256k1_ecdsa_seckey_verify(m_secret.data()); + if (!ok) + return; + + byte pubkey[65]; + int pubkeylen = 65; + ok = secp256k1_ecdsa_pubkey_create(pubkey, &pubkeylen, m_secret.data(), 0); + if (!ok || pubkeylen != 65) + return; + + ok = secp256k1_ecdsa_pubkey_verify(pubkey, 65); + if (!ok) + return; + + m_secret = m_secret; + memcpy(m_public.data(), &(pubkey[1]), 64); + m_address = right160(dev::eth::sha3(bytesConstRef(&(pubkey[1]), 64))); + +#if ETH_ADDRESS_DEBUG + cout << "---- ADDRESS -------------------------------" << endl; + cout << "SEC: " << m_secret << endl; + cout << "PUB: " << m_public << endl; + cout << "ADR: " << m_address << endl; +#endif +} diff --git a/libdevcrypto/Common.h b/libdevcrypto/Common.h new file mode 100644 index 000000000..2694d7f8d --- /dev/null +++ b/libdevcrypto/Common.h @@ -0,0 +1,86 @@ +/* + 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 . +*/ +/** @file CommonEth.h + * @author Gav Wood + * @date 2014 + * + * Ethereum-specific data structures & algorithms. + */ + +#pragma once + +#include +#include + +namespace dev +{ + +/// A secret key: 32 bytes. +/// @NOTE This is not endian-specific; it's just a bunch of bytes. +using Secret = h256; + +/// A public key: 64 bytes. +/// @NOTE This is not endian-specific; it's just a bunch of bytes. +using Public = h512; + +/// An Ethereum address: 20 bytes. +/// @NOTE This is not endian-specific; it's just a bunch of bytes. +using Address = h160; + +/// A vector of Ethereum addresses. +using Addresses = h160s; + +/// Convert a private key into the public key equivalent. +/// @returns 0 if it's not a valid private key. +Address toAddress(h256 _private); + +/// Simple class that represents a "key pair". +/// All of the data of the class can be regenerated from the secret key (m_secret) alone. +/// Actually stores a tuplet of secret, public and address (the right 160-bits of the public). +class KeyPair +{ +public: + /// Null constructor. + KeyPair() {} + + /// Normal constructor - populates object from the given secret key. + KeyPair(Secret _k); + + /// Create a new, randomly generated object. + static KeyPair create(); + + /// Retrieve the secret key. + Secret const& secret() const { return m_secret; } + /// Retrieve the secret key. + Secret const& sec() const { return m_secret; } + + /// Retrieve the public key. + Public const& pub() const { return m_public; } + + /// Retrieve the associated address of the public key. + Address const& address() const { return m_address; } + + bool operator==(KeyPair const& _c) const { return m_secret == _c.m_secret; } + bool operator!=(KeyPair const& _c) const { return m_secret != _c.m_secret; } + +private: + Secret m_secret; + Public m_public; + Address m_address; +}; + +} diff --git a/libdevcrypto/CryptoHeaders.h b/libdevcrypto/CryptoHeaders.h new file mode 100644 index 000000000..4ff63f1d7 --- /dev/null +++ b/libdevcrypto/CryptoHeaders.h @@ -0,0 +1,36 @@ +/* + 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 . +*/ +/** @file CryptoHeaders.h + * @author Tim Hughes + * @date 2014 + */ +#pragma once + +// need to leave this one disabled +#pragma GCC diagnostic ignored "-Wunused-function" + +#pragma warning(push) +#pragma warning(disable:4100 4244) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wconversion" +#pragma GCC diagnostic ignored "-Wunused-parameter" +#include +#include +#include +#include +#pragma warning(pop) +#pragma GCC diagnostic pop diff --git a/libethcore/FileSystem.cpp b/libdevcrypto/FileSystem.cpp similarity index 94% rename from libethcore/FileSystem.cpp rename to libdevcrypto/FileSystem.cpp index 6d7044753..e161c3234 100644 --- a/libethcore/FileSystem.cpp +++ b/libdevcrypto/FileSystem.cpp @@ -22,17 +22,17 @@ */ #include "FileSystem.h" -#include -#include +#include +#include #ifdef _WIN32 #include #endif #include using namespace std; -using namespace eth; +using namespace dev; -std::string eth::getDataDir() +std::string dev::getDataDir() { #ifdef _WIN32 char path[1024] = ""; diff --git a/libethcore/FileSystem.h b/libdevcrypto/FileSystem.h similarity index 98% rename from libethcore/FileSystem.h rename to libdevcrypto/FileSystem.h index a6ce93fb0..605545b0d 100644 --- a/libethcore/FileSystem.h +++ b/libdevcrypto/FileSystem.h @@ -25,7 +25,7 @@ #include -namespace eth +namespace dev { /// @returns the path for user data. diff --git a/libethcore/MemoryDB.cpp b/libdevcrypto/MemoryDB.cpp similarity index 96% rename from libethcore/MemoryDB.cpp rename to libdevcrypto/MemoryDB.cpp index 8755266c3..4480417fc 100644 --- a/libethcore/MemoryDB.cpp +++ b/libdevcrypto/MemoryDB.cpp @@ -19,11 +19,14 @@ * @date 2014 */ -#include +#include #include "MemoryDB.h" using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; +namespace dev +{ namespace eth { @@ -113,3 +116,4 @@ set MemoryDB::keys() const } } +} diff --git a/libethcore/MemoryDB.h b/libdevcrypto/MemoryDB.h similarity index 91% rename from libethcore/MemoryDB.h rename to libdevcrypto/MemoryDB.h index 33cd71fcd..59435d3bf 100644 --- a/libethcore/MemoryDB.h +++ b/libdevcrypto/MemoryDB.h @@ -22,11 +22,13 @@ #pragma once #include -#include -#include -#include -#include +#include +#include +#include +#include +namespace dev +{ namespace eth { @@ -54,7 +56,7 @@ public: protected: std::map m_over; - std::map m_refCount; + std::map m_refCount; mutable bool m_enforceRefs = false; }; @@ -83,3 +85,4 @@ inline std::ostream& operator<<(std::ostream& _out, MemoryDB const& _m) } } +} diff --git a/libethcore/OverlayDB.cpp b/libdevcrypto/OverlayDB.cpp similarity index 95% rename from libethcore/OverlayDB.cpp rename to libdevcrypto/OverlayDB.cpp index a8941e9ef..460609fb3 100644 --- a/libethcore/OverlayDB.cpp +++ b/libdevcrypto/OverlayDB.cpp @@ -19,11 +19,14 @@ * @date 2014 */ -#include +#include #include "OverlayDB.h" using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; +namespace dev +{ namespace eth { @@ -97,3 +100,4 @@ void OverlayDB::kill(h256 _h) } } +} diff --git a/libethcore/OverlayDB.h b/libdevcrypto/OverlayDB.h similarity index 87% rename from libethcore/OverlayDB.h rename to libdevcrypto/OverlayDB.h index e2a5aba1a..9db4eaed6 100644 --- a/libethcore/OverlayDB.h +++ b/libdevcrypto/OverlayDB.h @@ -21,12 +21,19 @@ #pragma once +#pragma warning(push) +#pragma warning(disable: 4100 4267) +#include +#pragma warning(pop) + #include -#include -#include +#include +#include #include "MemoryDB.h" namespace ldb = leveldb; +namespace dev +{ namespace eth { @@ -56,3 +63,4 @@ private: }; } +} diff --git a/libethcore/SHA3.cpp b/libdevcrypto/SHA3.cpp similarity index 83% rename from libethcore/SHA3.cpp rename to libdevcrypto/SHA3.cpp index 90e85f4ef..cdd1002e6 100644 --- a/libethcore/SHA3.cpp +++ b/libdevcrypto/SHA3.cpp @@ -23,11 +23,17 @@ #include "CryptoHeaders.h" using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; -h256 eth::EmptySHA3 = sha3(bytesConstRef()); +namespace dev +{ +namespace eth +{ + +h256 EmptySHA3 = sha3(bytesConstRef()); -std::string eth::sha3(std::string const& _input, bool _hex) +std::string sha3(std::string const& _input, bool _hex) { if (!_hex) { @@ -44,7 +50,7 @@ std::string eth::sha3(std::string const& _input, bool _hex) return ret; } -void eth::sha3(bytesConstRef _input, bytesRef _output) +void sha3(bytesConstRef _input, bytesRef _output) { CryptoPP::SHA3_256 ctx; ctx.Update((byte*)_input.data(), _input.size()); @@ -52,17 +58,19 @@ void eth::sha3(bytesConstRef _input, bytesRef _output) ctx.Final(_output.data()); } -bytes eth::sha3Bytes(bytesConstRef _input) +bytes sha3Bytes(bytesConstRef _input) { bytes ret(32); sha3(_input, &ret); return ret; } -h256 eth::sha3(bytesConstRef _input) +h256 sha3(bytesConstRef _input) { h256 ret; sha3(_input, bytesRef(&ret[0], 32)); return ret; } +} +} diff --git a/libethcore/SHA3.h b/libdevcrypto/SHA3.h similarity index 96% rename from libethcore/SHA3.h rename to libdevcrypto/SHA3.h index 5972c0e47..1b37846b3 100644 --- a/libethcore/SHA3.h +++ b/libdevcrypto/SHA3.h @@ -24,9 +24,11 @@ #pragma once #include -#include -#include +#include +#include +namespace dev +{ namespace eth { @@ -59,3 +61,4 @@ inline h256 sha3(std::string const& _input) { return sha3(bytesConstRef(_input)) extern h256 EmptySHA3; } +} diff --git a/libethcore/TrieCommon.cpp b/libdevcrypto/TrieCommon.cpp similarity index 82% rename from libethcore/TrieCommon.cpp rename to libdevcrypto/TrieCommon.cpp index fedf3b095..bad29059e 100644 --- a/libethcore/TrieCommon.cpp +++ b/libdevcrypto/TrieCommon.cpp @@ -21,6 +21,8 @@ #include "TrieCommon.h" +namespace dev +{ namespace eth { @@ -42,8 +44,8 @@ namespace eth std::string hexPrefixEncode(bytes const& _hexVector, bool _leaf, int _begin, int _end) { - uint begin = _begin; - uint end = _end < 0 ? _hexVector.size() + 1 + _end : _end; + unsigned begin = _begin; + unsigned end = _end < 0 ? _hexVector.size() + 1 + _end : _end; bool odd = ((end - begin) % 2) != 0; std::string ret(1, ((_leaf ? 2 : 0) | (odd ? 1 : 0)) * 16); @@ -52,21 +54,21 @@ std::string hexPrefixEncode(bytes const& _hexVector, bool _leaf, int _begin, int ret[0] |= _hexVector[begin]; ++begin; } - for (uint i = begin; i < end; i += 2) + for (unsigned i = begin; i < end; i += 2) ret += _hexVector[i] * 16 + _hexVector[i + 1]; return ret; } -std::string hexPrefixEncode(bytesConstRef _data, bool _leaf, int _beginNibble, int _endNibble, uint _offset) +std::string hexPrefixEncode(bytesConstRef _data, bool _leaf, int _beginNibble, int _endNibble, unsigned _offset) { - uint begin = _beginNibble + _offset; - uint end = (_endNibble < 0 ? (_data.size() * 2 - _offset) + 1 + _endNibble : _endNibble) + _offset; + unsigned begin = _beginNibble + _offset; + unsigned end = (_endNibble < 0 ? (_data.size() * 2 - _offset) + 1 + _endNibble : _endNibble) + _offset; bool odd = (end - begin) & 1; std::string ret(1, ((_leaf ? 2 : 0) | (odd ? 1 : 0)) * 16); ret.reserve((end - begin) / 2 + 1); - uint d = odd ? 1 : 2; + unsigned d = odd ? 1 : 2; for (auto i = begin; i < end; ++i, ++d) { byte n = nibble(_data, i); @@ -78,19 +80,19 @@ std::string hexPrefixEncode(bytesConstRef _data, bool _leaf, int _beginNibble, i return ret; } -std::string hexPrefixEncode(bytesConstRef _d1, uint _o1, bytesConstRef _d2, uint _o2, bool _leaf) +std::string hexPrefixEncode(bytesConstRef _d1, unsigned _o1, bytesConstRef _d2, unsigned _o2, bool _leaf) { - uint begin1 = _o1; - uint end1 = _d1.size() * 2; - uint begin2 = _o2; - uint end2 = _d2.size() * 2; + unsigned begin1 = _o1; + unsigned end1 = _d1.size() * 2; + unsigned begin2 = _o2; + unsigned end2 = _d2.size() * 2; bool odd = (end1 - begin1 + end2 - begin2) & 1; std::string ret(1, ((_leaf ? 2 : 0) | (odd ? 1 : 0)) * 16); ret.reserve((end1 - begin1 + end2 - begin2) / 2 + 1); - uint d = odd ? 1 : 2; + unsigned d = odd ? 1 : 2; for (auto i = begin1; i < end1; ++i, ++d) { byte n = nibble(_d1, i); @@ -125,3 +127,4 @@ byte uniqueInUse(RLP const& _orig, byte except) } } +} diff --git a/libethcore/TrieCommon.h b/libdevcrypto/TrieCommon.h similarity index 68% rename from libethcore/TrieCommon.h rename to libdevcrypto/TrieCommon.h index c748fd8d0..ecfb4de67 100644 --- a/libethcore/TrieCommon.h +++ b/libdevcrypto/TrieCommon.h @@ -21,43 +21,45 @@ #pragma once -#include -#include +#include +#include +namespace dev +{ namespace eth { -inline byte nibble(bytesConstRef _data, uint _i) +inline byte nibble(bytesConstRef _data, unsigned _i) { return (_i & 1) ? (_data[_i / 2] & 15) : (_data[_i / 2] >> 4); } -inline uint sharedNibbles(bytesConstRef _a, uint _ab, uint _ae, bytesConstRef _b, uint _bb, uint _be) +inline unsigned sharedNibbles(bytesConstRef _a, unsigned _ab, unsigned _ae, bytesConstRef _b, unsigned _bb, unsigned _be) { - uint ret = 0; - for (uint ai = _ab, bi = _bb; ai < _ae && bi < _be && nibble(_a, ai) == nibble(_b, bi); ++ai, ++bi, ++ret) {} + unsigned ret = 0; + for (unsigned ai = _ab, bi = _bb; ai < _ae && bi < _be && nibble(_a, ai) == nibble(_b, bi); ++ai, ++bi, ++ret) {} return ret; } struct NibbleSlice { bytesConstRef data; - uint offset; + unsigned offset; - NibbleSlice(bytesConstRef _d = bytesConstRef(), uint _o = 0): data(_d), offset(_o) {} - byte operator[](uint _index) const { return nibble(data, offset + _index); } - uint size() const { return data.size() * 2 - offset; } - NibbleSlice mid(uint _index) const { return NibbleSlice(data, offset + _index); } + NibbleSlice(bytesConstRef _d = bytesConstRef(), unsigned _o = 0): data(_d), offset(_o) {} + byte operator[](unsigned _index) const { return nibble(data, offset + _index); } + unsigned size() const { return data.size() * 2 - offset; } + NibbleSlice mid(unsigned _index) const { return NibbleSlice(data, offset + _index); } bool contains(NibbleSlice _k) const { return shared(_k) == _k.size(); } - uint shared(NibbleSlice _k) const { return sharedNibbles(data, offset, offset + size(), _k.data, _k.offset, _k.offset + _k.size()); } + unsigned shared(NibbleSlice _k) const { return sharedNibbles(data, offset, offset + size(), _k.data, _k.offset, _k.offset + _k.size()); } bool operator==(NibbleSlice _k) const { return _k.size() == size() && shared(_k) == _k.size(); } bool operator!=(NibbleSlice _s) const { return !operator==(_s); } }; inline std::ostream& operator<<(std::ostream& _out, NibbleSlice const& _m) { - for (uint i = 0; i < _m.size(); ++i) + for (unsigned i = 0; i < _m.size(); ++i) _out << std::hex << (int)_m[i] << std::dec; return _out; } @@ -86,8 +88,8 @@ inline NibbleSlice keyOf(RLP const& _twoItem) byte uniqueInUse(RLP const& _orig, byte except); std::string hexPrefixEncode(bytes const& _hexVector, bool _leaf = false, int _begin = 0, int _end = -1); -std::string hexPrefixEncode(bytesConstRef _data, bool _leaf, int _beginNibble, int _endNibble, uint _offset); -std::string hexPrefixEncode(bytesConstRef _d1, uint _o1, bytesConstRef _d2, uint _o2, bool _leaf); +std::string hexPrefixEncode(bytesConstRef _data, bool _leaf, int _beginNibble, int _endNibble, unsigned _offset); +std::string hexPrefixEncode(bytesConstRef _d1, unsigned _o1, bytesConstRef _d2, unsigned _o2, bool _leaf); inline std::string hexPrefixEncode(NibbleSlice _s, bool _leaf, int _begin = 0, int _end = -1) { @@ -100,3 +102,4 @@ inline std::string hexPrefixEncode(NibbleSlice _s1, NibbleSlice _s2, bool _leaf) } } +} diff --git a/libethcore/TrieDB.cpp b/libdevcrypto/TrieDB.cpp similarity index 86% rename from libethcore/TrieDB.cpp rename to libdevcrypto/TrieDB.cpp index c0562889f..3c551d2b4 100644 --- a/libethcore/TrieDB.cpp +++ b/libdevcrypto/TrieDB.cpp @@ -19,17 +19,14 @@ * @date 2014 */ -#include +#include #include "TrieDB.h" using namespace std; -using namespace eth; - -namespace eth -{ +using namespace dev; +using namespace dev::eth; #if !ETH_LANGUAGES -const h256 c_shaNull = sha3(rlp("")); +const h256 dev::eth::c_shaNull = sha3(rlp("")); #endif -} diff --git a/libethcore/TrieDB.h b/libdevcrypto/TrieDB.h similarity index 98% rename from libethcore/TrieDB.h rename to libdevcrypto/TrieDB.h index c6114b95d..8ee6f3bca 100644 --- a/libethcore/TrieDB.h +++ b/libdevcrypto/TrieDB.h @@ -21,16 +21,23 @@ #pragma once +#pragma warning(push) +#pragma warning(disable: 4100 4267) +#include +#pragma warning(pop) + #include #include -#include -#include -#include +#include +#include +#include #include "MemoryDB.h" #include "OverlayDB.h" #include "TrieCommon.h" namespace ldb = leveldb; +namespace dev +{ namespace eth { @@ -223,7 +230,7 @@ private: // in: [K1 & K2, V] (DEL) : nibbles(K1) == _s, 0 < _s <= nibbles(K1 & K2) // out: [K1, H] ; [K2, V] => H (INS) (being [K1, [K2, V]] if necessary) - bytes cleve(RLP const& _orig, uint _s); + bytes cleve(RLP const& _orig, unsigned _s); // in: [K1, H] (DEL) ; H <= [K2, V] (DEL) (being [K1, [K2, V]] (DEL) if necessary) // out: [K1 & K2, V] @@ -308,9 +315,12 @@ std::ostream& operator<<(std::ostream& _out, TrieDB const& _db) return _out; } +} } // Template implementations... +namespace dev +{ namespace eth { @@ -756,7 +766,7 @@ template bytes GenericTrieDB::place(RLP const& _orig, NibbleSlice return (RLPStream(2) << _orig[0] << _s).out(); auto s = RLPStream(17); - for (uint i = 0; i < 16; ++i) + for (unsigned i = 0; i < 16; ++i) s << _orig[i]; s << _s; return s.out(); @@ -778,7 +788,7 @@ template bytes GenericTrieDB::remove(RLP const& _orig) if (_orig.itemCount() == 2) return RLPNull; RLPStream r(17); - for (uint i = 0; i < 16; ++i) + for (unsigned i = 0; i < 16; ++i) r << _orig[i]; r << ""; return r.out(); @@ -793,7 +803,7 @@ template RLPStream& GenericTrieDB::streamNode(RLPStream& _s, byte return _s; } -template bytes GenericTrieDB::cleve(RLP const& _orig, uint _s) +template bytes GenericTrieDB::cleve(RLP const& _orig, unsigned _s) { #if ETH_PARANOIA tdebug << "cleve " << _orig << _s; @@ -874,14 +884,14 @@ template bytes GenericTrieDB::branch(RLP const& _orig) if (k.size() == 0) { assert(isLeaf(_orig)); - for (uint i = 0; i < 16; ++i) + for (unsigned i = 0; i < 16; ++i) r << ""; r << _orig[1]; } else { byte b = k[0]; - for (uint i = 0; i < 16; ++i) + for (unsigned i = 0; i < 16; ++i) if (i == b) if (isLeaf(_orig) || k.size() > 1) { @@ -899,3 +909,5 @@ template bytes GenericTrieDB::branch(RLP const& _orig) } } +} + diff --git a/libethcore/All.h b/libethcore/All.h index 9cd9b72ee..4a6747ff0 100644 --- a/libethcore/All.h +++ b/libethcore/All.h @@ -3,10 +3,6 @@ #include "BlockInfo.h" #include "CommonEth.h" #include "Dagger.h" -#include "FileSystem.h" -#include "MemoryDB.h" -#include "OverlayDB.h" -#include "SHA3.h" -#include "TrieCommon.h" -#include "TrieDB.h" -#include "UPnP.h" +#include "CryptoHeaders.h" +#include "Exceptions.h" + diff --git a/libethcore/BlockInfo.cpp b/libethcore/BlockInfo.cpp index 172583271..9ceb4c708 100644 --- a/libethcore/BlockInfo.cpp +++ b/libethcore/BlockInfo.cpp @@ -21,16 +21,17 @@ #if !ETH_LANGUAGES -#include -#include -#include +#include +#include +#include #include "Dagger.h" #include "Exceptions.h" #include "BlockInfo.h" using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; -u256 eth::c_genesisDifficulty = (u256)1 << 22; +u256 dev::eth::c_genesisDifficulty = (u256)1 << 17; BlockInfo::BlockInfo(): timestamp(Invalid256) { @@ -64,8 +65,15 @@ void BlockInfo::fillStream(RLPStream& _s, bool _nonce) const _s << nonce; } +h256 BlockInfo::headerHash(bytesConstRef _block) +{ + return sha3(RLP(_block)[0].data()); +} + void BlockInfo::populateFromHeader(RLP const& _header, bool _checkNonce) { + hash = dev::eth::sha3(_header.data()); + int field = 0; try { @@ -101,10 +109,9 @@ void BlockInfo::populateFromHeader(RLP const& _header, bool _checkNonce) void BlockInfo::populate(bytesConstRef _block, bool _checkNonce) { - hash = eth::sha3(_block); - RLP root(_block); RLP header = root[0]; + if (!header.isList()) throw InvalidBlockFormat(0, header.data()); populateFromHeader(header, _checkNonce); @@ -166,7 +173,7 @@ u256 BlockInfo::calculateDifficulty(BlockInfo const& _parent) const if (!parentHash) return c_genesisDifficulty; else - return timestamp >= _parent.timestamp + 42 ? _parent.difficulty - (_parent.difficulty >> 10) : (_parent.difficulty + (_parent.difficulty >> 10)); + return timestamp >= _parent.timestamp + 5 ? _parent.difficulty - (_parent.difficulty >> 10) : (_parent.difficulty + (_parent.difficulty >> 10)); } void BlockInfo::verifyParent(BlockInfo const& _parent) const @@ -176,7 +183,7 @@ void BlockInfo::verifyParent(BlockInfo const& _parent) const throw InvalidDifficulty(); if (gasLimit != calculateGasLimit(_parent)) - throw InvalidGasLimit(); + throw InvalidGasLimit(gasLimit, calculateGasLimit(_parent)); // Check timestamp is after previous timestamp. if (parentHash) diff --git a/libethcore/BlockInfo.h b/libethcore/BlockInfo.h index ecec391c1..283f11b88 100644 --- a/libethcore/BlockInfo.h +++ b/libethcore/BlockInfo.h @@ -21,10 +21,12 @@ #pragma once -#include -#include +#include +#include #include "CommonEth.h" +namespace dev +{ namespace eth { @@ -48,13 +50,13 @@ extern u256 c_genesisDifficulty; * and calculateGasLimit() and the object serialised to RLP with fillStream. To determine the * header hash without the nonce (for mining), the method headerHashWithoutNonce() is provided. * - * The defualt constructor creates an empty object, which can be tested against with the boolean + * The default constructor creates an empty object, which can be tested against with the boolean * conversion operator. */ struct BlockInfo { public: - h256 hash; ///< SHA3 hash of the entire block! Not serialised (the only member not contained in a block header). + h256 hash; ///< SHA3 hash of the block header! Not serialised (the only member not contained in a block header). h256 parentHash; h256 sha3Uncles; Address coinbaseAddress; @@ -73,6 +75,9 @@ public: explicit BlockInfo(bytes const& _block): BlockInfo(&_block) {} explicit BlockInfo(bytesConstRef _block); + static h256 headerHash(bytes const& _block) { return headerHash(&_block); } + static h256 headerHash(bytesConstRef _block); + static BlockInfo fromHeader(bytesConstRef _block); explicit operator bool() const { return timestamp != Invalid256; } @@ -118,5 +123,4 @@ inline std::ostream& operator<<(std::ostream& _out, BlockInfo const& _bi) } } - - +} diff --git a/libethcore/CMakeLists.txt b/libethcore/CMakeLists.txt index e10f6a481..6aba644f1 100644 --- a/libethcore/CMakeLists.txt +++ b/libethcore/CMakeLists.txt @@ -15,12 +15,10 @@ file(GLOB HEADERS "*.h") include_directories(..) -target_link_libraries(${EXECUTABLE} ethential) +target_link_libraries(${EXECUTABLE} devcrypto) +target_link_libraries(${EXECUTABLE} devcore) target_link_libraries(${EXECUTABLE} secp256k1) target_link_libraries(${EXECUTABLE} gmp) -if(MINIUPNPC_LS) -target_link_libraries(${EXECUTABLE} ${MINIUPNPC_LS}) -endif() target_link_libraries(${EXECUTABLE} ${LEVELDB_LS}) target_link_libraries(${EXECUTABLE} ${CRYPTOPP_LS}) diff --git a/libethcore/CommonEth.cpp b/libethcore/CommonEth.cpp index e88de28e1..eed8b4bb9 100644 --- a/libethcore/CommonEth.cpp +++ b/libethcore/CommonEth.cpp @@ -22,15 +22,20 @@ #include "CommonEth.h" #include #include -#include +#include #include "Exceptions.h" using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; //#define ETH_ADDRESS_DEBUG 1 +namespace dev +{ +namespace eth +{ -const unsigned eth::c_protocolVersion = 23; -const unsigned eth::c_databaseVersion = 1; +const unsigned c_protocolVersion = 33; +const unsigned c_databaseVersion = 1; static const vector> g_units = { @@ -55,12 +60,12 @@ static const vector> g_units = {u256(1), "wei"} }; -vector> const& eth::units() +vector> const& units() { return g_units; } -std::string eth::formatBalance(u256 _b) +std::string formatBalance(u256 _b) { ostringstream ret; if (_b > g_units[0].first * 10000) @@ -79,7 +84,7 @@ std::string eth::formatBalance(u256 _b) return ret.str(); } -Address eth::toAddress(Secret _private) +Address toAddress(Secret _private) { secp256k1_start(); @@ -95,7 +100,7 @@ Address eth::toAddress(Secret _private) ok = secp256k1_ecdsa_pubkey_verify(pubkey, 65); if (!ok) return Address(); - auto ret = right160(eth::sha3(bytesConstRef(&(pubkey[1]), 64))); + auto ret = right160(dev::eth::sha3(bytesConstRef(&(pubkey[1]), 64))); #if ETH_ADDRESS_DEBUG cout << "---- ADDRESS -------------------------------" << endl; cout << "SEC: " << _private << endl; @@ -105,50 +110,4 @@ Address eth::toAddress(Secret _private) return ret; } -KeyPair KeyPair::create() -{ - secp256k1_start(); - static std::mt19937_64 s_eng(time(0)); - std::uniform_int_distribution d(0, 255); - - for (int i = 0; i < 100; ++i) - { - h256 sec; - for (unsigned i = 0; i < 32; ++i) - sec[i] = (byte)d(s_eng); - - KeyPair ret(sec); - if (ret.address()) - return ret; - } - return KeyPair(); -} - -KeyPair::KeyPair(h256 _sec): - m_secret(_sec) -{ - int ok = secp256k1_ecdsa_seckey_verify(m_secret.data()); - if (!ok) - return; - - byte pubkey[65]; - int pubkeylen = 65; - ok = secp256k1_ecdsa_pubkey_create(pubkey, &pubkeylen, m_secret.data(), 0); - if (!ok || pubkeylen != 65) - return; - - ok = secp256k1_ecdsa_pubkey_verify(pubkey, 65); - if (!ok) - return; - - m_secret = m_secret; - memcpy(m_public.data(), &(pubkey[1]), 64); - m_address = right160(eth::sha3(bytesConstRef(&(pubkey[1]), 64))); - -#if ETH_ADDRESS_DEBUG - cout << "---- ADDRESS -------------------------------" << endl; - cout << "SEC: " << m_secret << endl; - cout << "PUB: " << m_public << endl; - cout << "ADR: " << m_address << endl; -#endif -} +}} diff --git a/libethcore/CommonEth.h b/libethcore/CommonEth.h index d336165cb..1965a881a 100644 --- a/libethcore/CommonEth.h +++ b/libethcore/CommonEth.h @@ -23,9 +23,12 @@ #pragma once -#include -#include +#include +#include +#include +namespace dev +{ namespace eth { @@ -35,21 +38,6 @@ extern const unsigned c_protocolVersion; /// Current database version. extern const unsigned c_databaseVersion; -/// A secret key: 32 bytes. -/// @NOTE This is not endian-specific; it's just a bunch of bytes. -using Secret = h256; - -/// A public key: 64 bytes. -/// @NOTE This is not endian-specific; it's just a bunch of bytes. -using Public = h512; - -/// An Ethereum address: 20 bytes. -/// @NOTE This is not endian-specific; it's just a bunch of bytes. -using Address = h160; - -/// A vector of Ethereum addresses. -using Addresses = h160s; - /// User-friendly string representation of the amount _b in wei. std::string formatBalance(u256 _b); @@ -77,43 +65,5 @@ static const u256 Mwei = u256(1000000); static const u256 Kwei = u256(1000); static const u256 wei = u256(1); -/// Convert a private key into the public key equivalent. -/// @returns 0 if it's not a valid private key. -Address toAddress(h256 _private); - -/// Simple class that represents a "key pair". -/// All of the data of the class can be regenerated from the secret key (m_secret) alone. -/// Actually stores a tuplet of secret, public and address (the right 160-bits of the public). -class KeyPair -{ -public: - /// Null constructor. - KeyPair() {} - - /// Normal constructor - populates object from the given secret key. - KeyPair(Secret _k); - - /// Create a new, randomly generated object. - static KeyPair create(); - - /// Retrieve the secret key. - Secret const& secret() const { return m_secret; } - /// Retrieve the secret key. - Secret const& sec() const { return m_secret; } - - /// Retrieve the public key. - Public const& pub() const { return m_public; } - - /// Retrieve the associated address of the public key. - Address const& address() const { return m_address; } - - bool operator==(KeyPair const& _c) const { return m_secret == _c.m_secret; } - bool operator!=(KeyPair const& _c) const { return m_secret != _c.m_secret; } - -private: - Secret m_secret; - Public m_public; - Address m_address; -}; - +} } diff --git a/libethcore/Dagger.cpp b/libethcore/Dagger.cpp index 1651a1941..956557b64 100644 --- a/libethcore/Dagger.cpp +++ b/libethcore/Dagger.cpp @@ -25,20 +25,23 @@ #include #include #include +#include #include -#include +#include #include "Dagger.h" using namespace std; using namespace std::chrono; +namespace dev +{ namespace eth { #if FAKE_DAGGER -MineInfo Dagger::mine(h256& o_solution, h256 const& _root, u256 const& _difficulty, uint _msTimeout, bool const& _continue) +MineInfo Dagger::mine(h256& o_solution, h256 const& _root, u256 const& _difficulty, unsigned _msTimeout, bool _continue, bool _turbo) { - MineInfo ret{0.f, 1e99, 0, false}; + MineInfo ret; static std::mt19937_64 s_eng((time(0) + (unsigned)m_last)); u256 s = (m_last = h256::random(s_eng)); @@ -49,7 +52,10 @@ MineInfo Dagger::mine(h256& o_solution, h256 const& _root, u256 const& _difficul // [--------*-------------------------] // // evaluate until we run out of time - for (auto startTime = steady_clock::now(); (steady_clock::now() - startTime) < milliseconds(_msTimeout) && _continue; s++, ret.hashes++) + auto startTime = steady_clock::now(); + if (!_turbo) + this_thread::sleep_for(chrono::milliseconds(_msTimeout * 90 / 100)); + for (; (steady_clock::now() - startTime) < milliseconds(_msTimeout) && _continue; s++, ret.hashes++) { o_solution = (h256)s; auto e = (bigint)(u256)eval(_root, o_solution); @@ -87,7 +93,7 @@ bool Dagger::verify(h256 const& _root, u256 const& _nonce, u256 const& _difficul return eval(_root, _nonce) < bound(_difficulty); } -bool Dagger::mine(u256& o_solution, h256 const& _root, u256 const& _difficulty, uint _msTimeout, bool const& _continue) +bool Dagger::mine(u256& o_solution, h256 const& _root, u256 const& _difficulty, unsigned _msTimeout, bool const& _continue) { // restart search if root has changed if (m_root != _root) @@ -181,5 +187,5 @@ h256 Dagger::eval(h256 const& _root, u256 const& _nonce) #endif } - +} #endif diff --git a/libethcore/Dagger.h b/libethcore/Dagger.h index e0c993ea7..c32ef38be 100644 --- a/libethcore/Dagger.h +++ b/libethcore/Dagger.h @@ -23,25 +23,23 @@ #pragma once -#include +#include #include "CommonEth.h" #define FAKE_DAGGER 1 -namespace eth +namespace dev { - -inline uint toLog2(u256 _d) +namespace eth { - return (uint)log2((double)_d); -} struct MineInfo { - double requirement; - double best; - uint hashes; - bool completed; + void combine(MineInfo const& _m) { requirement = std::max(requirement, _m.requirement); best = std::min(best, _m.best); hashes += _m.hashes; completed = completed || _m.completed; } + double requirement = 0; + double best = 1e99; + unsigned hashes = 0; + bool completed = false; }; #if FAKE_DAGGER @@ -52,7 +50,7 @@ public: static h256 eval(h256 const& _root, h256 const& _nonce) { h256 b[2] = { _root, _nonce }; return sha3(bytesConstRef((byte const*)&b[0], 64)); } static bool verify(h256 const& _root, h256 const& _nonce, u256 const& _difficulty) { return (bigint)(u256)eval(_root, _nonce) <= (bigint(1) << 256) / _difficulty; } - MineInfo mine(h256& o_solution, h256 const& _root, u256 const& _difficulty, uint _msTimeout = 100, bool const& _continue = bool(true)); + MineInfo mine(h256& o_solution, h256 const& _root, u256 const& _difficulty, unsigned _msTimeout = 100, bool _continue = true, bool _turbo = false); h256 m_last; }; @@ -70,7 +68,7 @@ public: static h256 eval(h256 const& _root, u256 const& _nonce); static bool verify(h256 const& _root, u256 const& _nonce, u256 const& _difficulty); - bool mine(u256& o_solution, h256 const& _root, u256 const& _difficulty, uint _msTimeout = 100, bool const& _continue = bool(true)); + bool mine(u256& o_solution, h256 const& _root, u256 const& _difficulty, unsigned _msTimeout = 100, bool const& _continue = bool(true)); private: @@ -83,5 +81,4 @@ private: #endif } - - +} diff --git a/libethcore/Exceptions.h b/libethcore/Exceptions.h index 127a84450..c936b1f1b 100644 --- a/libethcore/Exceptions.h +++ b/libethcore/Exceptions.h @@ -1,40 +1,46 @@ #pragma once -#include +#include +namespace dev +{ namespace eth { -class NotEnoughCash: public Exception {}; +class DatabaseAlreadyOpen: public dev::Exception {}; + +class NotEnoughCash: public dev::Exception {}; -class GasPriceTooLow: public Exception {}; -class BlockGasLimitReached: public Exception {}; -class NoSuchContract: public Exception {}; -class ContractAddressCollision: public Exception {}; -class FeeTooSmall: public Exception {}; -class TooMuchGasUsed: public Exception {}; -class ExtraDataTooBig: public Exception {}; -class InvalidSignature: public Exception {}; -class InvalidTransactionFormat: public Exception { public: InvalidTransactionFormat(int _f, bytesConstRef _d): m_f(_f), m_d(_d.toBytes()) {} int m_f; bytes m_d; virtual std::string description() const { return "Invalid transaction format: Bad field " + toString(m_f) + " (" + toHex(m_d) + ")"; } }; -class InvalidBlockFormat: public Exception { public: InvalidBlockFormat(int _f, bytesConstRef _d): m_f(_f), m_d(_d.toBytes()) {} int m_f; bytes m_d; virtual std::string description() const { return "Invalid block format: Bad field " + toString(m_f) + " (" + toHex(m_d) + ")"; } }; -class InvalidBlockHeaderFormat: public Exception { public: InvalidBlockHeaderFormat(int _f, bytesConstRef _d): m_f(_f), m_d(_d.toBytes()) {} int m_f; bytes m_d; virtual std::string description() const { return "Invalid block header format: Bad field " + toString(m_f) + " (" + toHex(m_d) + ")"; } }; -class InvalidUnclesHash: public Exception {}; -class InvalidUncle: public Exception {}; -class UncleNotAnUncle: public Exception {}; -class DuplicateUncleNonce: public Exception {}; -class InvalidStateRoot: public Exception {}; -class InvalidTransactionsHash: public Exception { public: InvalidTransactionsHash(h256 _head, h256 _real): m_head(_head), m_real(_real) {} h256 m_head; h256 m_real; virtual std::string description() const { return "Invalid transactions hash: header says: " + toHex(m_head.ref()) + " block is:" + toHex(m_real.ref()); } }; -class InvalidTransaction: public Exception {}; -class InvalidDifficulty: public Exception {}; -class InvalidGasLimit: public Exception {}; -class InvalidMinGasPrice: public Exception { public: InvalidMinGasPrice(u256 _provided = 0, u256 _limit = 0): provided(_provided), limit(_limit) {} u256 provided; u256 limit; virtual std::string description() const { return "Invalid minimum gas price (provided: " + toString(provided) + " limit:" + toString(limit) + ")"; } }; -class InvalidTransactionGasUsed: public Exception {}; -class InvalidTransactionStateRoot: public Exception {}; -class InvalidTimestamp: public Exception {}; -class InvalidNonce: public Exception { public: InvalidNonce(u256 _required = 0, u256 _candidate = 0): required(_required), candidate(_candidate) {} u256 required; u256 candidate; virtual std::string description() const { return "Invalid nonce (r: " + toString(required) + " c:" + toString(candidate) + ")"; } }; -class InvalidBlockNonce: public Exception { public: InvalidBlockNonce(h256 _h = h256(), h256 _n = h256(), u256 _d = 0): h(_h), n(_n), d(_d) {} h256 h; h256 n; u256 d; virtual std::string description() const { return "Invalid nonce (h: " + toString(h) + " n:" + toString(n) + " d:" + toString(d) + ")"; } }; -class InvalidParentHash: public Exception {}; -class InvalidNumber: public Exception {}; -class InvalidContractAddress: public Exception {}; +class GasPriceTooLow: public dev::Exception {}; +class BlockGasLimitReached: public dev::Exception {}; +class NoSuchContract: public dev::Exception {}; +class ContractAddressCollision: public dev::Exception {}; +class FeeTooSmall: public dev::Exception {}; +class TooMuchGasUsed: public dev::Exception {}; +class ExtraDataTooBig: public dev::Exception {}; +class InvalidSignature: public dev::Exception {}; +class InvalidTransactionFormat: public dev::Exception { public: InvalidTransactionFormat(int _f, bytesConstRef _d): m_f(_f), m_d(_d.toBytes()) {} int m_f; bytes m_d; virtual std::string description() const { return "Invalid transaction format: Bad field " + toString(m_f) + " (" + toHex(m_d) + ")"; } }; +class InvalidBlockFormat: public dev::Exception { public: InvalidBlockFormat(int _f, bytesConstRef _d): m_f(_f), m_d(_d.toBytes()) {} int m_f; bytes m_d; virtual std::string description() const { return "Invalid block format: Bad field " + toString(m_f) + " (" + toHex(m_d) + ")"; } }; +class InvalidBlockHeaderFormat: public dev::Exception { public: InvalidBlockHeaderFormat(int _f, bytesConstRef _d): m_f(_f), m_d(_d.toBytes()) {} int m_f; bytes m_d; virtual std::string description() const { return "Invalid block header format: Bad field " + toString(m_f) + " (" + toHex(m_d) + ")"; } }; +class InvalidUnclesHash: public dev::Exception {}; +class InvalidUncle: public dev::Exception {}; +class UncleTooOld: public dev::Exception {}; +class UncleInChain: public dev::Exception {}; +class DuplicateUncleNonce: public dev::Exception {}; +class InvalidStateRoot: public dev::Exception {}; +class InvalidTransactionsHash: public dev::Exception { public: InvalidTransactionsHash(h256 _head, h256 _real): m_head(_head), m_real(_real) {} h256 m_head; h256 m_real; virtual std::string description() const { return "Invalid transactions hash: header says: " + toHex(m_head.ref()) + " block is:" + toHex(m_real.ref()); } }; +class InvalidTransaction: public dev::Exception {}; +class InvalidDifficulty: public dev::Exception {}; +class InvalidGasLimit: public dev::Exception { public: InvalidGasLimit(u256 _provided = 0, u256 _valid = 0): provided(_provided), valid(_valid) {} u256 provided; u256 valid; virtual std::string description() const { return "Invalid gas limit (provided: " + toString(provided) + " valid:" + toString(valid) + ")"; } }; +class InvalidMinGasPrice: public dev::Exception { public: InvalidMinGasPrice(u256 _provided = 0, u256 _limit = 0): provided(_provided), limit(_limit) {} u256 provided; u256 limit; virtual std::string description() const { return "Invalid minimum gas price (provided: " + toString(provided) + " limit:" + toString(limit) + ")"; } }; +class InvalidTransactionGasUsed: public dev::Exception {}; +class InvalidTransactionStateRoot: public dev::Exception {}; +class InvalidTimestamp: public dev::Exception {}; +class InvalidNonce: public dev::Exception { public: InvalidNonce(u256 _required = 0, u256 _candidate = 0): required(_required), candidate(_candidate) {} u256 required; u256 candidate; virtual std::string description() const { return "Invalid nonce (r: " + toString(required) + " c:" + toString(candidate) + ")"; } }; +class InvalidBlockNonce: public dev::Exception { public: InvalidBlockNonce(h256 _h = h256(), h256 _n = h256(), u256 _d = 0): h(_h), n(_n), d(_d) {} h256 h; h256 n; u256 d; virtual std::string description() const { return "Invalid nonce (h: " + toString(h) + " n:" + toString(n) + " d:" + toString(d) + ")"; } }; +class InvalidParentHash: public dev::Exception {}; +class InvalidNumber: public dev::Exception {}; +class InvalidContractAddress: public dev::Exception {}; } +} diff --git a/libethcore/_libethcore.cpp b/libethcore/_libethcore.cpp new file mode 100644 index 000000000..8a3831584 --- /dev/null +++ b/libethcore/_libethcore.cpp @@ -0,0 +1,6 @@ +#ifdef _MSC_VER +#include "All.h" +#include "BlockInfo.cpp" +#include "CommonEth.cpp" +#include "Dagger.cpp" +#endif diff --git a/libethential/CommonData.cpp b/libethential/CommonData.cpp deleted file mode 100644 index 3d4d9bdbe..000000000 --- a/libethential/CommonData.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/* - 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 . -*/ -/** @file Common.cpp - * @author Gav Wood - * @date 2014 - */ - -#include "CommonData.h" - -#include -#include "Exceptions.h" -using namespace std; -using namespace eth; - -std::string eth::escaped(std::string const& _s, bool _all) -{ - std::string ret; - ret.reserve(_s.size()); - ret.push_back('"'); - for (auto i: _s) - if (i == '"' && !_all) - ret += "\\\""; - else if (i == '\\' && !_all) - ret += "\\\\"; - else if (i < ' ' || _all) - { - ret += "\\x"; - ret.push_back("0123456789abcdef"[(uint8_t)i / 16]); - ret.push_back("0123456789abcdef"[(uint8_t)i % 16]); - } - else - ret.push_back(i); - ret.push_back('"'); - return ret; -} - -std::string eth::randomWord() -{ - static std::mt19937_64 s_eng(0); - std::string ret(std::uniform_int_distribution(1, 5)(s_eng), ' '); - char const n[] = "qwertyuiop";//asdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890"; - std::uniform_int_distribution d(0, sizeof(n) - 2); - for (char& c: ret) - c = n[d(s_eng)]; - return ret; -} - -int eth::fromHex(char _i) -{ - if (_i >= '0' && _i <= '9') - return _i - '0'; - if (_i >= 'a' && _i <= 'f') - return _i - 'a' + 10; - if (_i >= 'A' && _i <= 'F') - return _i - 'A' + 10; - throw BadHexCharacter(); -} - -bytes eth::fromHex(std::string const& _s) -{ - uint s = (_s[0] == '0' && _s[1] == 'x') ? 2 : 0; - std::vector ret; - ret.reserve((_s.size() - s + 1) / 2); - - if (_s.size() % 2) - try - { - ret.push_back(fromHex(_s[s++])); - } - catch (...){ ret.push_back(0); } - for (uint i = s; i < _s.size(); i += 2) - try - { - ret.push_back((byte)(fromHex(_s[i]) * 16 + fromHex(_s[i + 1]))); - } - catch (...){ ret.push_back(0); } - return ret; -} - -bytes eth::asNibbles(std::string const& _s) -{ - std::vector ret; - ret.reserve(_s.size() * 2); - for (auto i: _s) - { - ret.push_back(i / 16); - ret.push_back(i % 16); - } - return ret; -} diff --git a/libethereum/AccountDiff.cpp b/libethereum/AccountDiff.cpp new file mode 100644 index 000000000..c434086fa --- /dev/null +++ b/libethereum/AccountDiff.cpp @@ -0,0 +1,84 @@ +/* + 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 . +*/ +/** @file AccountDiff.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "AccountDiff.h" + +#include +using namespace std; +using namespace dev; +using namespace dev::eth; + +AccountChange AccountDiff::changeType() const +{ + bool bn = (balance || nonce); + bool sc = (!storage.empty() || code); + return exist ? exist.from() ? AccountChange::Deletion : AccountChange::Creation : (bn && sc) ? AccountChange::All : bn ? AccountChange::Intrinsic: sc ? AccountChange::CodeStorage : AccountChange::None; +} + +char const* AccountDiff::lead() const +{ + bool bn = (balance || nonce); + bool sc = (!storage.empty() || code); + return exist ? exist.from() ? "XXX" : "+++" : (bn && sc) ? "***" : bn ? " * " : sc ? "* *" : " "; +} + +namespace dev { + +std::ostream& operator<<(std::ostream& _out, dev::eth::AccountDiff const& _s) +{ + if (!_s.exist.to()) + return _out; + + if (_s.nonce) + { + _out << std::dec << "#" << _s.nonce.to() << " "; + if (_s.nonce.from()) + _out << "(" << std::showpos << (((bigint)_s.nonce.to()) - ((bigint)_s.nonce.from())) << std::noshowpos << ") "; + } + if (_s.balance) + { + _out << std::dec << _s.balance.to() << " "; + if (_s.balance.from()) + _out << "(" << std::showpos << (((bigint)_s.balance.to()) - ((bigint)_s.balance.from())) << std::noshowpos << ") "; + } + if (_s.code) + _out << "$" << std::hex << nouppercase << _s.code.to() << " (" << _s.code.from() << ") "; + for (pair> const& i: _s.storage) + if (!i.second.from()) + _out << endl << " + " << (h256)i.first << ": " << std::hex << nouppercase << i.second.to(); + else if (!i.second.to()) + _out << endl << "XXX " << (h256)i.first << " (" << std::hex << nouppercase << i.second.from() << ")"; + else + _out << endl << " * " << (h256)i.first << ": " << std::hex << nouppercase << i.second.to() << " (" << i.second.from() << ")"; + return _out; +} + +std::ostream& operator<<(std::ostream& _out, dev::eth::StateDiff const& _s) +{ + _out << _s.accounts.size() << " accounts changed:" << endl; + dev::eth::AccountDiff d; + _out << d; + for (auto const& i: _s.accounts) + _out << i.second.lead() << " " << i.first << ": " << i.second << endl; + return _out; +} + +} diff --git a/libethereum/AccountDiff.h b/libethereum/AccountDiff.h new file mode 100644 index 000000000..d4c30ead5 --- /dev/null +++ b/libethereum/AccountDiff.h @@ -0,0 +1,76 @@ +/* + 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 . +*/ +/** @file AccountDiff.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include + +namespace dev +{ +namespace eth +{ + +enum class ExistDiff { Same, New, Dead }; +template +class Diff +{ +public: + Diff() {} + Diff(T _from, T _to): m_from(_from), m_to(_to) {} + + T const& from() const { return m_from; } + T const& to() const { return m_to; } + + explicit operator bool() const { return m_from != m_to; } + +private: + T m_from; + T m_to; +}; + +enum class AccountChange { None, Creation, Deletion, Intrinsic, CodeStorage, All }; + +struct AccountDiff +{ + inline bool changed() const { return storage.size() || code || nonce || balance || exist; } + char const* lead() const; + AccountChange changeType() const; + + Diff exist; + Diff balance; + Diff nonce; + std::map> storage; + Diff code; +}; + +struct StateDiff +{ + std::map accounts; +}; + +} + +std::ostream& operator<<(std::ostream& _out, dev::eth::StateDiff const& _s); +std::ostream& operator<<(std::ostream& _out, dev::eth::AccountDiff const& _s); + +} + diff --git a/libethereum/AddressState.cpp b/libethereum/AddressState.cpp index a3eb65acb..268440314 100644 --- a/libethereum/AddressState.cpp +++ b/libethereum/AddressState.cpp @@ -22,7 +22,8 @@ #include "AddressState.h" #include using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; #pragma GCC diagnostic ignored "-Wunused-variable" namespace { char dummy; }; diff --git a/libethereum/AddressState.h b/libethereum/AddressState.h index bc4aa6df0..668e9225f 100644 --- a/libethereum/AddressState.h +++ b/libethereum/AddressState.h @@ -21,10 +21,12 @@ #pragma once -#include -#include -#include +#include +#include +#include +namespace dev +{ namespace eth { @@ -61,7 +63,6 @@ public: private: bool m_isAlive; - bool m_gotCode; u256 m_nonce; u256 m_balance; @@ -79,5 +80,5 @@ private: }; } - +} diff --git a/libethereum/All.h b/libethereum/All.h index adc7fedb4..ba22640e7 100644 --- a/libethereum/All.h +++ b/libethereum/All.h @@ -6,9 +6,10 @@ #include "Defaults.h" #include "Executive.h" #include "ExtVM.h" -#include "PeerNetwork.h" -#include "PeerServer.h" -#include "PeerSession.h" +#include "CommonNet.h" +#include "EthereumHost.h" +#include "EthereumPeer.h" #include "State.h" #include "Transaction.h" #include "TransactionQueue.h" +#include "Utility.h" diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 660bf3498..4c7a7b48c 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -22,25 +22,23 @@ #include "BlockChain.h" #include -#include -#include -#include +#include +#include +#include #include #include #include #include "State.h" #include "Defaults.h" using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; #define ETH_CATCH 1 -namespace eth +std::ostream& dev::eth::operator<<(std::ostream& _out, BlockChain const& _bc) { - -std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc) -{ - string cmp = toBigEndianString(_bc.m_lastBlockHash); + string cmp = toBigEndianString(_bc.currentHash()); auto it = _bc.m_extrasDB->NewIterator(_bc.m_readOptions); for (it->SeekToFirst(); it->Valid(); it->Next()) if (it->key().ToString() != "best") @@ -51,23 +49,8 @@ std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc) delete it; return _out; } -} - -BlockDetails::BlockDetails(RLP const& _r) -{ - number = _r[0].toInt(); - totalDifficulty = _r[1].toInt(); - parent = _r[2].toHash(); - children = _r[3].toVector(); - bloom = _r[4].toHash(); -} -bytes BlockDetails::rlp() const -{ - return rlpList(number, totalDifficulty, parent, children, bloom); -} - -std::map const& eth::genesisState() +std::map const& dev::eth::genesisState() { static std::map s_ret; if (s_ret.empty()) @@ -87,6 +70,21 @@ std::map const& eth::genesisState() } BlockInfo* BlockChain::s_genesis = nullptr; +boost::shared_mutex BlockChain::x_genesis; + +ldb::Slice dev::eth::toSlice(h256 _h, unsigned _sub) +{ +#if ALL_COMPILERS_ARE_CPP11_COMPLIANT + static thread_local h256 h = _h ^ h256(u256(_sub)); + return ldb::Slice((char const*)&h, 32); +#else + static boost::thread_specific_ptr t_h; + if (!t_h.get()) + t_h.reset(new h256); + *t_h = _h ^ h256(u256(_sub)); + return ldb::Slice((char const*)t_h.get(), 32); +#endif +} bytes BlockChain::createGenesisBlock() { @@ -98,18 +96,32 @@ bytes BlockChain::createGenesisBlock() MemoryDB db; TrieDB state(&db); state.init(); - eth::commit(genesisState(), db, state); + dev::eth::commit(genesisState(), db, state); stateRoot = state.root(); } block.appendList(13) << h256() << sha3EmptyList << h160(); - block.append(stateRoot, false, true) << bytes() << c_genesisDifficulty << 0 << 0 << 1000000 << 0 << (uint)0 << string() << sha3(bytes(1, 42)); + block.append(stateRoot, false, true) << bytes() << c_genesisDifficulty << 0 << 0 << 1000000 << 0 << (unsigned)0 << string() << sha3(bytes(1, 42)); block.appendRaw(RLPEmptyList); block.appendRaw(RLPEmptyList); return block.out(); } BlockChain::BlockChain(std::string _path, bool _killExisting) +{ + // Initialise with the genesis as the last block on the longest chain. + m_genesisHash = BlockChain::genesis().hash; + m_genesisBlock = BlockChain::createGenesisBlock(); + + open(_path, _killExisting); +} + +BlockChain::~BlockChain() +{ + close(); +} + +void BlockChain::open(std::string _path, bool _killExisting) { if (_path.empty()) _path = Defaults::get()->m_dbPath; @@ -122,21 +134,19 @@ BlockChain::BlockChain(std::string _path, bool _killExisting) ldb::Options o; o.create_if_missing = true; - auto s = ldb::DB::Open(o, _path + "/blocks", &m_db); - assert(m_db); - s = ldb::DB::Open(o, _path + "/details", &m_extrasDB); - assert(m_extrasDB); - - // Initialise with the genesis as the last block on the longest chain. - m_genesisHash = BlockChain::genesis().hash; - m_genesisBlock = BlockChain::createGenesisBlock(); + ldb::DB::Open(o, _path + "/blocks", &m_db); + ldb::DB::Open(o, _path + "/details", &m_extrasDB); + if (!m_db) + throw DatabaseAlreadyOpen(); + if (!m_extrasDB) + throw DatabaseAlreadyOpen(); if (!details(m_genesisHash)) { // Insert details of genesis block. m_details[m_genesisHash] = BlockDetails(0, c_genesisDifficulty, h256(), {}, h256()); auto r = m_details[m_genesisHash].rlp(); - m_extrasDB->Put(m_writeOptions, ldb::Slice((char const*)&m_genesisHash, 32), (ldb::Slice)eth::ref(r)); + m_extrasDB->Put(m_writeOptions, ldb::Slice((char const*)&m_genesisHash, 32), (ldb::Slice)dev::ref(r)); } checkConsistency(); @@ -144,16 +154,22 @@ BlockChain::BlockChain(std::string _path, bool _killExisting) // TODO: Implement ability to rebuild details map from DB. std::string l; m_extrasDB->Get(m_readOptions, ldb::Slice("best"), &l); + m_lastBlockHash = l.empty() ? m_genesisHash : *(h256*)l.data(); - cnote << "Opened blockchain DB. Latest: " << m_lastBlockHash; + cnote << "Opened blockchain DB. Latest: " << currentHash(); } -BlockChain::~BlockChain() +void BlockChain::close() { cnote << "Closing blockchain DB"; delete m_extrasDB; delete m_db; + m_lastBlockHash = m_genesisHash; + m_details.clear(); + m_blooms.clear(); + m_traces.clear(); + m_cache.clear(); } template @@ -165,38 +181,64 @@ bool contains(T const& _t, V const& _v) return false; } -bool BlockChain::attemptImport(bytes const& _block, OverlayDB const& _stateDB) +inline string toString(h256s const& _bs) +{ + ostringstream out; + out << "[ "; + for (auto i: _bs) + out << i.abridged() << ", "; + out << "]"; + return out.str(); +} + +h256s BlockChain::sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max) +{ + _bq.tick(*this); + + vector blocks; + _bq.drain(blocks); + + h256s ret; + for (auto const& block: blocks) + { + try + { + for (auto h: import(block, _stateDB)) + if (!_max--) + break; + else + ret.push_back(h); + } + catch (UnknownParent) + { + cwarn << "Unknown parent of block!!!" << BlockInfo::headerHash(block).abridged(); + _bq.import(&block, *this); + } + catch (Exception const& _e) + { + cwarn << "Unexpected exception!" << _e.description(); + _bq.import(&block, *this); + } + catch (...) + {} + } + _bq.doneDrain(); + return ret; +} + +h256s BlockChain::attemptImport(bytes const& _block, OverlayDB const& _stateDB) noexcept { -#if ETH_CATCH try -#endif { - import(_block, _stateDB); - return true; + return import(_block, _stateDB); } -#if ETH_CATCH catch (...) { - return false; + return h256s(); } -#endif } -inline ldb::Slice toSlice(h256 _h, unsigned _sub = 0) -{ -#if ALL_COMPILERS_ARE_CPP11_COMPLIANT - static thread_local h256 h = _h ^ h256(u256(_sub)); - return ldb::Slice((char const*)&h, 32); -#else - static boost::thread_specific_ptr t_h; - if (!t_h.get()) - t_h.reset(new h256); - *t_h = _h ^ h256(u256(_sub)); - return ldb::Slice((char const*)t_h.get(), 32); -#endif -} - -void BlockChain::import(bytes const& _block, OverlayDB const& _db) +h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) { // VERIFY: populates from the block and checks the block is internally coherent. BlockInfo bi; @@ -215,33 +257,35 @@ void BlockChain::import(bytes const& _block, OverlayDB const& _db) throw; } #endif - auto newHash = eth::sha3(_block); + auto newHash = BlockInfo::headerHash(_block); // Check block doesn't already exist first! - if (details(newHash)) + if (isKnown(newHash)) { clog(BlockChainNote) << newHash << ": Not new."; throw AlreadyHaveBlock(); } // Work out its number as the parent's number + 1 - auto pd = details(bi.parentHash); - if (!pd) + if (!isKnown(bi.parentHash)) { clog(BlockChainNote) << newHash << ": Unknown parent " << bi.parentHash; // We don't know the parent (yet) - discard for now. It'll get resent to us if we find out about its ancestry later on. throw UnknownParent(); } + auto pd = details(bi.parentHash); + assert(pd); + // Check it's not crazy if (bi.timestamp > (u256)time(0)) { clog(BlockChainNote) << newHash << ": Future time " << bi.timestamp << " (now at " << time(0) << ")"; - // We don't know the parent (yet) - discard for now. It'll get resent to us if we find out about its ancestry later on. + // Block has a timestamp in the future. This is no good. throw FutureTime(); } - clog(BlockChainNote) << "Attempting import of " << newHash << "..."; + clog(BlockChainNote) << "Attempting import of " << newHash.abridged() << "..."; u256 td; #if ETH_CATCH @@ -268,17 +312,23 @@ void BlockChain::import(bytes const& _block, OverlayDB const& _db) #endif // All ok - insert into DB { - lock_guard l(m_lock); - m_details[newHash] = BlockDetails((uint)pd.number + 1, td, bi.parentHash, {}, b); + WriteGuard l(x_details); + m_details[newHash] = BlockDetails((unsigned)pd.number + 1, td, bi.parentHash, {}, b); m_details[bi.parentHash].children.push_back(newHash); + } + { + WriteGuard l(x_blooms); m_blooms[newHash] = bb; + } + { + WriteGuard l(x_traces); m_traces[newHash] = bt; } - m_extrasDB->Put(m_writeOptions, toSlice(newHash), (ldb::Slice)eth::ref(m_details[newHash].rlp())); - m_extrasDB->Put(m_writeOptions, toSlice(bi.parentHash), (ldb::Slice)eth::ref(m_details[bi.parentHash].rlp())); - m_extrasDB->Put(m_writeOptions, toSlice(newHash, 1), (ldb::Slice)eth::ref(m_blooms[newHash].rlp())); - m_extrasDB->Put(m_writeOptions, toSlice(newHash, 2), (ldb::Slice)eth::ref(m_traces[newHash].rlp())); + m_extrasDB->Put(m_writeOptions, toSlice(newHash), (ldb::Slice)dev::ref(m_details[newHash].rlp())); + m_extrasDB->Put(m_writeOptions, toSlice(bi.parentHash), (ldb::Slice)dev::ref(m_details[bi.parentHash].rlp())); + m_extrasDB->Put(m_writeOptions, toSlice(newHash, 1), (ldb::Slice)dev::ref(m_blooms[newHash].rlp())); + m_extrasDB->Put(m_writeOptions, toSlice(newHash, 2), (ldb::Slice)dev::ref(m_traces[newHash].rlp())); m_db->Put(m_writeOptions, toSlice(newHash), (ldb::Slice)ref(_block)); #if ETH_PARANOIA @@ -295,22 +345,78 @@ void BlockChain::import(bytes const& _block, OverlayDB const& _db) // cnote << "Parent " << bi.parentHash << " has " << details(bi.parentHash).children.size() << " children."; + h256s ret; // This might be the new best block... - if (td > details(m_lastBlockHash).totalDifficulty) + h256 last = currentHash(); + if (td > details(last).totalDifficulty) { - m_lastBlockHash = newHash; + ret = treeRoute(last, newHash); + { + WriteGuard l(x_lastBlockHash); + m_lastBlockHash = newHash; + } m_extrasDB->Put(m_writeOptions, ldb::Slice("best"), ldb::Slice((char const*)&newHash, 32)); - clog(BlockChainNote) << " Imported and best. Has" << (details(bi.parentHash).children.size() - 1) << "siblings."; + clog(BlockChainNote) << " Imported and best" << td << ". Has" << (details(bi.parentHash).children.size() - 1) << "siblings. Route:" << toString(ret); } else { - clog(BlockChainNote) << " Imported but not best (oTD:" << details(m_lastBlockHash).totalDifficulty << ", TD:" << td << ")"; + clog(BlockChainNote) << " Imported but not best (oTD:" << details(last).totalDifficulty << " > TD:" << td << ")"; } + return ret; +} + +h256s BlockChain::treeRoute(h256 _from, h256 _to, h256* o_common, bool _pre, bool _post) const +{ +// cdebug << "treeRoute" << _from.abridged() << "..." << _to.abridged(); + if (!_from || !_to) + { + return h256s(); + } + h256s ret; + h256s back; + unsigned fn = details(_from).number; + unsigned tn = details(_to).number; +// cdebug << "treeRoute" << fn << "..." << tn; + while (fn > tn) + { + if (_pre) + ret.push_back(_from); + _from = details(_from).parent; + fn--; +// cdebug << "from:" << fn << _from.abridged(); + } + while (fn < tn) + { + if (_post) + back.push_back(_to); + _to = details(_to).parent; + tn--; +// cdebug << "to:" << tn << _to.abridged(); + } + while (_from != _to) + { + assert(_from); + assert(_to); + _from = details(_from).parent; + _to = details(_to).parent; + if (_pre) + ret.push_back(_from); + if (_post) + back.push_back(_to); + fn--; + tn--; +// cdebug << "from:" << fn << _from.abridged() << "; to:" << tn << _to.abridged(); + } + if (o_common) + *o_common = _from; + ret.reserve(ret.size() + back.size()); + for (auto it = back.cbegin(); it != back.cend(); ++it) + ret.push_back(*it); + return ret; } void BlockChain::checkConsistency() { - lock_guard l(m_lock); m_details.clear(); ldb::Iterator* it = m_db->NewIterator(m_readOptions); for (it->SeekToFirst(); it->Valid(); it->Next()) @@ -329,34 +435,59 @@ void BlockChain::checkConsistency() delete it; } +h256Set BlockChain::allUnclesFrom(h256 _parent) const +{ + // Get all uncles cited given a parent (i.e. featured as uncles/main in parent, parent + 1, ... parent + 5). + h256Set ret; + h256 p = _parent; + for (unsigned i = 0; i < 6 && p != m_genesisHash; ++i, p = details(p).parent) + { + ret.insert(p); // TODO: check: should this be details(p).parent? + for (auto i: RLP(block(p))[2]) + ret.insert(sha3(i.data())); + } + return ret; +} + +bool BlockChain::isKnown(h256 _hash) const +{ + if (_hash == m_genesisHash) + return true; + { + ReadGuard l(x_cache); + if (m_cache.count(_hash)) + return true; + } + string d; + m_db->Get(m_readOptions, ldb::Slice((char const*)&_hash, 32), &d); + return !!d.size(); +} + bytes BlockChain::block(h256 _hash) const { if (_hash == m_genesisHash) return m_genesisBlock; - lock_guard l(m_lock); - - auto it = m_cache.find(_hash); - if (it != m_cache.end()) - return it->second; + { + ReadGuard l(x_cache); + auto it = m_cache.find(_hash); + if (it != m_cache.end()) + return it->second; + } string d; m_db->Get(m_readOptions, ldb::Slice((char const*)&_hash, 32), &d); + WriteGuard l(x_cache); m_cache[_hash].resize(d.size()); memcpy(m_cache[_hash].data(), d.data(), d.size()); if (!d.size()) - cwarn << "Couldn't find requested block:" << _hash; + cwarn << "Couldn't find requested block:" << _hash.abridged(); return m_cache[_hash]; } -eth::uint BlockChain::number(h256 _hash) const -{ - return details(_hash).number; -} - h256 BlockChain::numberHash(unsigned _n) const { if (!_n) @@ -365,69 +496,3 @@ h256 BlockChain::numberHash(unsigned _n) const for (; _n < details().number; ++_n, ret = details(ret).parent) {} return ret; } - -BlockDetails BlockChain::details(h256 _h) const -{ - lock_guard l(m_lock); - - BlockDetailsHash::const_iterator it = m_details.find(_h); - if (it != m_details.end()) - return it->second; - - std::string s; - m_extrasDB->Get(m_readOptions, toSlice(_h), &s); - if (s.empty()) - { -// cout << "Not found in DB: " << _h << endl; - return NullBlockDetails; - } - { - bool ok; - tie(it, ok) = m_details.insert(std::make_pair(_h, BlockDetails(RLP(s)))); - } - return it->second; -} - -BlockBlooms BlockChain::blooms(h256 _h) const -{ - lock_guard l(m_lock); - - BlockBloomsHash::const_iterator it = m_blooms.find(_h); - if (it != m_blooms.end()) - return it->second; - - std::string s; - m_extrasDB->Get(m_readOptions, toSlice(_h, 1), &s); - if (s.empty()) - { -// cout << "Not found in DB: " << _h << endl; - return NullBlockBlooms; - } - { - bool ok; - tie(it, ok) = m_blooms.insert(std::make_pair(_h, BlockBlooms(RLP(s)))); - } - return it->second; -} - -BlockTraces BlockChain::traces(h256 _h) const -{ - lock_guard l(m_lock); - - BlockTracesHash::const_iterator it = m_traces.find(_h); - if (it != m_traces.end()) - return it->second; - - std::string s; - m_extrasDB->Get(m_readOptions, toSlice(_h, 2), &s); - if (s.empty()) - { -// cout << "Not found in DB: " << _h << endl; - return NullBlockTraces; - } - { - bool ok; - tie(it, ok) = m_traces.insert(std::make_pair(_h, BlockTraces(RLP(s)))); - } - return it->second; -} diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 2eeda53c0..cf33a5104 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -21,63 +21,25 @@ #pragma once +#pragma warning(push) +#pragma warning(disable: 4100 4267) +#include +#pragma warning(pop) + #include -#include +#include #include #include -#include "Manifest.h" +#include +#include "BlockDetails.h" #include "AddressState.h" +#include "BlockQueue.h" namespace ldb = leveldb; -namespace eth -{ - -class RLP; -class RLPStream; - -struct BlockDetails -{ - BlockDetails(): number(0), totalDifficulty(0) {} - BlockDetails(uint _n, u256 _tD, h256 _p, h256s _c, h256 _bloom): number(_n), totalDifficulty(_tD), parent(_p), children(_c), bloom(_bloom) {} - BlockDetails(RLP const& _r); - bytes rlp() const; - - bool isNull() const { return !totalDifficulty; } - explicit operator bool() const { return !isNull(); } - - uint number; // TODO: remove? - u256 totalDifficulty; - h256 parent; - h256s children; - h256 bloom; -}; - -struct BlockBlooms +namespace dev { - BlockBlooms() {} - BlockBlooms(RLP const& _r) { blooms = _r.toVector(); } - bytes rlp() const { RLPStream s; s << blooms; return s.out(); } - - h256s blooms; -}; - -struct BlockTraces +namespace eth { - BlockTraces() {} - BlockTraces(RLP const& _r) { for (auto const& i: _r) traces.emplace_back(i.data()); } - bytes rlp() const { RLPStream s(traces.size()); for (auto const& i: traces) i.streamOut(s); return s.out(); } - - Manifests traces; -}; - - -typedef std::map BlockDetailsHash; -typedef std::map BlockBloomsHash; -typedef std::map BlockTracesHash; - -static const BlockDetails NullBlockDetails; -static const BlockBlooms NullBlockBlooms; -static const BlockTraces NullBlockTraces; static const h256s NullH256s; @@ -94,9 +56,11 @@ struct BlockChainNote: public LogChannel { static const char* name() { return "= // TODO: Move all this Genesis stuff into Genesis.h/.cpp std::map const& genesisState(); +ldb::Slice toSlice(h256 _h, unsigned _sub = 0); + /** * @brief Implements the blockchain database. All data this gives is disk-backed. - * @todo Make thread-safe. + * @threadsafe * @todo Make not memory hog (should actually act as a cache and deallocate old entries). */ class BlockChain @@ -106,71 +70,130 @@ public: BlockChain(std::string _path, bool _killExisting = false); ~BlockChain(); + void reopen(std::string _path, bool _killExisting = false) { close(); open(_path, _killExisting); } + /// (Potentially) renders invalid existing bytesConstRef returned by lastBlock. /// To be called from main loop every 100ms or so. void process(); - /// Attempt to import the given block. - bool attemptImport(bytes const& _block, OverlayDB const& _stateDB); + /// Sync the chain with any incoming blocks. All blocks should, if processed in order + h256s sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max); + + /// Attempt to import the given block directly into the BlockChain and sync with the state DB. + /// @returns the block hashes of any blocks that came into/went out of the canonical block chain. + h256s attemptImport(bytes const& _block, OverlayDB const& _stateDB) noexcept; /// Import block into disk-backed DB - void import(bytes const& _block, OverlayDB const& _stateDB); + /// @returns the block hashes of any blocks that came into/went out of the canonical block chain. + h256s import(bytes const& _block, OverlayDB const& _stateDB); + + /// Returns true if the given block is known (though not necessarily a part of the canon chain). + bool isKnown(h256 _hash) const; /// Get the familial details concerning a block (or the most recent mined if none given). Thread-safe. - BlockDetails details(h256 _hash) const; + BlockDetails details(h256 _hash) const { return queryExtras(_hash, m_details, x_details, NullBlockDetails); } BlockDetails details() const { return details(currentHash()); } /// Get the transactions' bloom filters of a block (or the most recent mined if none given). Thread-safe. - BlockBlooms blooms(h256 _hash) const; + BlockBlooms blooms(h256 _hash) const { return queryExtras(_hash, m_blooms, x_blooms, NullBlockBlooms); } BlockBlooms blooms() const { return blooms(currentHash()); } /// Get the transactions' trace manifests of a block (or the most recent mined if none given). Thread-safe. - BlockTraces traces(h256 _hash) const; + BlockTraces traces(h256 _hash) const { return queryExtras(_hash, m_traces, x_traces, NullBlockTraces); } BlockTraces traces() const { return traces(currentHash()); } - /// Get a given block (RLP format). Thread-safe. + /// Get a block (RLP format) for the given hash (or the most recent mined if none given). Thread-safe. bytes block(h256 _hash) const; bytes block() const { return block(currentHash()); } - uint number(h256 _hash) const; - uint number() const { return number(currentHash()); } + /// Get a number for the given hash (or the most recent mined if none given). Thread-safe. + unsigned number(h256 _hash) const { return details(_hash).number; } + unsigned number() const { return number(currentHash()); } /// Get a given block (RLP format). Thread-safe. - h256 currentHash() const { return m_lastBlockHash; } + h256 currentHash() const { ReadGuard l(x_lastBlockHash); return m_lastBlockHash; } - /// Get the hash of the genesis block. + /// Get the hash of the genesis block. Thread-safe. h256 genesisHash() const { return m_genesisHash; } - /// Get the hash of a block of a given number. + /// Get the hash of a block of a given number. Slow; try not to use it too much. h256 numberHash(unsigned _n) const; - std::vector> interestQueue() { std::vector> ret; swap(ret, m_interestQueue); return ret; } - void pushInterest(Address _a) { m_interest[_a]++; } - void popInterest(Address _a) { if (m_interest[_a] > 1) m_interest[_a]--; else if (m_interest[_a]) m_interest.erase(_a); } + /// Get all blocks not allowed as uncles given a parent (i.e. featured as uncles/main in parent, parent + 1, ... parent + 5). + /// @returns set including the header-hash of every parent (including @a _parent) up to and including generation +5 + /// togther with all their quoted uncles. + h256Set allUnclesFrom(h256 _parent) const; - static BlockInfo const& genesis() { if (!s_genesis) { auto gb = createGenesisBlock(); (s_genesis = new BlockInfo)->populate(&gb); } return *s_genesis; } + /// @returns the genesis block header. + static BlockInfo const& genesis() { UpgradableGuard l(x_genesis); if (!s_genesis) { auto gb = createGenesisBlock(); UpgradeGuard ul(l); (s_genesis = new BlockInfo)->populate(&gb); } return *s_genesis; } + + /// @returns the genesis block as its RLP-encoded byte array. + /// @note This is slow as it's constructed anew each call. Consider genesis() instead. static bytes createGenesisBlock(); + /** @returns the hash of all blocks between @a _from and @a _to, all blocks are ordered first by a number of + * blocks that are parent-to-child, then two sibling blocks, then a number of blocks that are child-to-parent. + * + * If non-null, the h256 at @a o_common is set to the latest common ancestor of both blocks. + * + * e.g. if the block tree is 3a -> 2a -> 1a -> g and 2b -> 1b -> g (g is genesis, *a, *b are competing chains), + * then: + * @code + * treeRoute(3a, 2b) == { 3a, 2a, 1a, 1b, 2b }; // *o_common == g + * treeRoute(2a, 1a) == { 2a, 1a }; // *o_common == 1a + * treeRoute(1a, 2a) == { 1a, 2a }; // *o_common == 1a + * treeRoute(1b, 2a) == { 1b, 1a, 2a }; // *o_common == g + * @endcode + */ + h256s treeRoute(h256 _from, h256 _to, h256* o_common = nullptr, bool _pre = true, bool _post = true) const; + private: + void open(std::string _path, bool _killExisting = false); + void close(); + + template T queryExtras(h256 _h, std::map& _m, boost::shared_mutex& _x, T const& _n) const + { + { + ReadGuard l(_x); + auto it = _m.find(_h); + if (it != _m.end()) + return it->second; + } + + std::string s; + m_extrasDB->Get(m_readOptions, toSlice(_h, N), &s); + if (s.empty()) + { + // cout << "Not found in DB: " << _h << endl; + return _n; + } + + WriteGuard l(_x); + auto ret = _m.insert(std::make_pair(_h, T(RLP(s)))); + return ret.first->second; + } + void checkConsistency(); - /// Get fully populated from disk DB. + /// The caches of the disk DB and their locks. + mutable boost::shared_mutex x_details; mutable BlockDetailsHash m_details; + mutable boost::shared_mutex x_blooms; mutable BlockBloomsHash m_blooms; + mutable boost::shared_mutex x_traces; mutable BlockTracesHash m_traces; - + mutable boost::shared_mutex x_cache; mutable std::map m_cache; - mutable std::recursive_mutex m_lock; - - /// The queue of transactions that have happened that we're interested in. - std::map m_interest; - std::vector> m_interestQueue; + /// The disk DBs. Thread-safe, so no need for locks. ldb::DB* m_db; ldb::DB* m_extrasDB; /// Hash of the last (valid) block on the longest chain. + mutable boost::shared_mutex x_lastBlockHash; h256 m_lastBlockHash; + + /// Genesis block info. h256 m_genesisHash; bytes m_genesisBlock; @@ -179,9 +202,12 @@ private: friend std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc); + /// Static genesis info and its lock. + static boost::shared_mutex x_genesis; static BlockInfo* s_genesis; }; std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc); } +} diff --git a/libethereum/BlockDetails.cpp b/libethereum/BlockDetails.cpp new file mode 100644 index 000000000..5a61eb037 --- /dev/null +++ b/libethereum/BlockDetails.cpp @@ -0,0 +1,41 @@ +/* + 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 . +*/ +/** @file BlockDetails.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "BlockDetails.h" + +#include +using namespace std; +using namespace dev; +using namespace dev::eth; + +BlockDetails::BlockDetails(RLP const& _r) +{ + number = _r[0].toInt(); + totalDifficulty = _r[1].toInt(); + parent = _r[2].toHash(); + children = _r[3].toVector(); + bloom = _r[4].toHash(); +} + +bytes BlockDetails::rlp() const +{ + return rlpList(number, totalDifficulty, parent, children, bloom); +} diff --git a/libethereum/BlockDetails.h b/libethereum/BlockDetails.h new file mode 100644 index 000000000..9cb49a21c --- /dev/null +++ b/libethereum/BlockDetails.h @@ -0,0 +1,84 @@ +/* + 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 . +*/ +/** @file BlockDetails.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#pragma warning(push) +#pragma warning(disable: 4100 4267) +#include +#pragma warning(pop) + +#include +#include +#include "Manifest.h" +namespace ldb = leveldb; + +namespace dev +{ +namespace eth +{ + +struct BlockDetails +{ + BlockDetails(): number(0), totalDifficulty(0) {} + BlockDetails(unsigned _n, u256 _tD, h256 _p, h256s _c, h256 _bloom): number(_n), totalDifficulty(_tD), parent(_p), children(_c), bloom(_bloom) {} + BlockDetails(RLP const& _r); + bytes rlp() const; + + bool isNull() const { return !totalDifficulty; } + explicit operator bool() const { return !isNull(); } + + unsigned number; // TODO: remove? + u256 totalDifficulty; + h256 parent; + h256s children; + h256 bloom; +}; + +struct BlockBlooms +{ + BlockBlooms() {} + BlockBlooms(RLP const& _r) { blooms = _r.toVector(); } + bytes rlp() const { RLPStream s; s << blooms; return s.out(); } + + h256s blooms; +}; + +struct BlockTraces +{ + BlockTraces() {} + BlockTraces(RLP const& _r) { for (auto const& i: _r) traces.emplace_back(i.data()); } + bytes rlp() const { RLPStream s(traces.size()); for (auto const& i: traces) i.streamOut(s); return s.out(); } + + Manifests traces; +}; + + +typedef std::map BlockDetailsHash; +typedef std::map BlockBloomsHash; +typedef std::map BlockTracesHash; + +static const BlockDetails NullBlockDetails; +static const BlockBlooms NullBlockBlooms; +static const BlockTraces NullBlockTraces; + +} +} diff --git a/libethereum/BlockQueue.cpp b/libethereum/BlockQueue.cpp new file mode 100644 index 000000000..a845965d5 --- /dev/null +++ b/libethereum/BlockQueue.cpp @@ -0,0 +1,142 @@ +/* + 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 . +*/ +/** @file BlockQueue.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "BlockQueue.h" + +#include +#include +#include +#include "BlockChain.h" +using namespace std; +using namespace dev; +using namespace dev::eth; + +bool BlockQueue::import(bytesConstRef _block, BlockChain const& _bc) +{ + // Check if we already know this block. + h256 h = BlockInfo::headerHash(_block); + + cblockq << "Queuing block" << h.abridged() << "for import..."; + + UpgradableGuard l(m_lock); + + if (m_readySet.count(h) || m_drainingSet.count(h) || m_unknownSet.count(h)) + { + // Already know about this one. + cblockq << "Already known."; + return false; + } + + // VERIFY: populates from the block and checks the block is internally coherent. + BlockInfo bi; + +#if ETH_CATCH + try +#endif + { + bi.populate(_block); + bi.verifyInternals(_block); + } +#if ETH_CATCH + catch (Exception const& _e) + { + cwarn << "Ignoring malformed block: " << _e.description(); + return false; + } +#endif + + // Check block doesn't already exist first! + if (_bc.details(h)) + { + cblockq << "Already known in chain."; + return false; + } + + UpgradeGuard ul(l); + + // Check it's not in the future + if (bi.timestamp > (u256)time(0)) + { + m_future.insert(make_pair((unsigned)bi.timestamp, _block.toBytes())); + cblockq << "OK - queued for future."; + } + else + { + // We now know it. + if (!m_readySet.count(bi.parentHash) && !m_drainingSet.count(bi.parentHash) && !_bc.isKnown(bi.parentHash)) + { + // We don't know the parent (yet) - queue it up for later. It'll get resent to us if we find out about its ancestry later on. + cblockq << "OK - queued as unknown parent:" << bi.parentHash.abridged(); + m_unknown.insert(make_pair(bi.parentHash, make_pair(h, _block.toBytes()))); + m_unknownSet.insert(h); + } + else + { + // If valid, append to blocks. + cblockq << "OK - ready for chain insertion."; + m_ready.push_back(_block.toBytes()); + m_readySet.insert(h); + + noteReadyWithoutWriteGuard(h); + } + } + + return true; +} + +void BlockQueue::tick(BlockChain const& _bc) +{ + unsigned t = time(0); + for (auto i = m_future.begin(); i != m_future.end() && i->first < time(0); ++i) + import(&(i->second), _bc); + + WriteGuard l(m_lock); + m_future.erase(m_future.begin(), m_future.upper_bound(t)); +} + +void BlockQueue::drain(std::vector& o_out) +{ + WriteGuard l(m_lock); + if (m_drainingSet.empty()) + { + swap(o_out, m_ready); + swap(m_drainingSet, m_readySet); + } +} + +void BlockQueue::noteReadyWithoutWriteGuard(h256 _good) +{ + list goodQueue(1, _good); + while (goodQueue.size()) + { + auto r = m_unknown.equal_range(goodQueue.front()); + goodQueue.pop_front(); + for (auto it = r.first; it != r.second; ++it) + { + m_ready.push_back(it->second.second); + auto newReady = it->second.first; + m_unknownSet.erase(newReady); + m_readySet.insert(newReady); + goodQueue.push_back(newReady); + } + m_unknown.erase(r.first, r.second); + } +} diff --git a/libethereum/BlockQueue.h b/libethereum/BlockQueue.h new file mode 100644 index 000000000..20bc4ce59 --- /dev/null +++ b/libethereum/BlockQueue.h @@ -0,0 +1,84 @@ +/* + 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 . +*/ +/** @file BlockQueue.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace dev +{ +namespace eth +{ + +class BlockChain; + +struct BlockQueueChannel: public LogChannel { static const char* name() { return "[]Q"; } static const int verbosity = 4; }; +#define cblockq dev::LogOutputStream() + +/** + * @brief A queue of blocks. Sits between network or other I/O and the BlockChain. + * Sorts them ready for blockchain insertion (with the BlockChain::sync() method). + * @threadsafe + */ +class BlockQueue +{ +public: + /// Import a block into the queue. + bool import(bytesConstRef _tx, BlockChain const& _bc); + + /// Notes that time has moved on and some blocks that used to be "in the future" may no be valid. + void tick(BlockChain const& _bc); + + /// Grabs the blocks that are ready, giving them in the correct order for insertion into the chain. + /// Don't forget to call doneDrain() once you're done importing. + void drain(std::vector& o_out); + + /// Must be called after a drain() call. Notes that the drained blocks have been imported into the blockchain, so we can forget about them. + void doneDrain() { WriteGuard l(m_lock); m_drainingSet.clear(); } + + /// Notify the queue that the chain has changed and a new block has attained 'ready' status (i.e. is in the chain). + void noteReady(h256 _b) { WriteGuard l(m_lock); noteReadyWithoutWriteGuard(_b); } + + /// Get information on the items queued. + std::pair items() const { ReadGuard l(m_lock); return std::make_pair(m_ready.size(), m_unknown.size()); } + + /// Clear everything. + void clear() { WriteGuard l(m_lock); m_readySet.clear(); m_drainingSet.clear(); m_ready.clear(); m_unknownSet.clear(); m_unknown.clear(); m_future.clear(); } + +private: + void noteReadyWithoutWriteGuard(h256 _b); + void notePresentWithoutWriteGuard(bytesConstRef _block); + + mutable boost::shared_mutex m_lock; ///< General lock. + std::set m_readySet; ///< All blocks ready for chain-import. + std::set m_drainingSet; ///< All blocks being imported. + std::vector m_ready; ///< List of blocks, in correct order, ready for chain-import. + std::set m_unknownSet; ///< Set of all blocks whose parents are not ready/in-chain. + std::multimap> m_unknown; ///< For transactions that have an unknown parent; we map their parent hash to the block stuff, and insert once the block appears. + std::multimap m_future; ///< Set of blocks that are not yet valid. +}; + +} +} diff --git a/libethereum/CMakeLists.txt b/libethereum/CMakeLists.txt index f50e709fb..128dd5999 100644 --- a/libethereum/CMakeLists.txt +++ b/libethereum/CMakeLists.txt @@ -19,6 +19,9 @@ include_directories(..) target_link_libraries(${EXECUTABLE} evm) target_link_libraries(${EXECUTABLE} lll) +target_link_libraries(${EXECUTABLE} whisper) +target_link_libraries(${EXECUTABLE} p2p) +target_link_libraries(${EXECUTABLE} devcrypto) target_link_libraries(${EXECUTABLE} ethcore) target_link_libraries(${EXECUTABLE} secp256k1) if(MINIUPNPC_LS) @@ -30,6 +33,7 @@ target_link_libraries(${EXECUTABLE} gmp) if("${TARGET_PLATFORM}" STREQUAL "w64") target_link_libraries(${EXECUTABLE} boost_system-mt-s) + target_link_libraries(${EXECUTABLE} boost_regex-mt-s) target_link_libraries(${EXECUTABLE} boost_filesystem-mt-s) target_link_libraries(${EXECUTABLE} boost_thread_win32-mt-s) target_link_libraries(${EXECUTABLE} iphlpapi) @@ -39,18 +43,21 @@ if("${TARGET_PLATFORM}" STREQUAL "w64") elseif (APPLE) # Latest mavericks boost libraries only come with -mt target_link_libraries(${EXECUTABLE} boost_system-mt) + target_link_libraries(${EXECUTABLE} boost_regex-mt) target_link_libraries(${EXECUTABLE} boost_filesystem-mt) target_link_libraries(${EXECUTABLE} boost_thread-mt) find_package(Threads REQUIRED) target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) elseif (UNIX) target_link_libraries(${EXECUTABLE} ${Boost_SYSTEM_LIBRARY}) + target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARY}) target_link_libraries(${EXECUTABLE} ${Boost_FILESYSTEM_LIBRARY}) target_link_libraries(${EXECUTABLE} ${Boost_THREAD_LIBRARY}) target_link_libraries(${EXECUTABLE} ${Boost_DATE_TIME_LIBRARY}) target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) else () target_link_libraries(${EXECUTABLE} boost_system) + target_link_libraries(${EXECUTABLE} boost_regex) target_link_libraries(${EXECUTABLE} boost_filesystem) target_link_libraries(${EXECUTABLE} boost_thread) find_package(Threads REQUIRED) diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 3fc03b459..de0077f89 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -24,11 +24,14 @@ #include #include #include -#include +#include +#include #include "Defaults.h" -#include "PeerServer.h" +#include "EthereumHost.h" using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; +using namespace p2p; VersionChecker::VersionChecker(string const& _dbPath): m_path(_dbPath.size() ? _dbPath : Defaults::dbPath()) @@ -50,110 +53,262 @@ void VersionChecker::setOk() } } -Client::Client(std::string const& _clientVersion, Address _us, std::string const& _dbPath, bool _forceClean): - m_clientVersion(_clientVersion), +Client::Client(p2p::Host* _extNet, std::string const& _dbPath, bool _forceClean, u256 _networkId): + Worker("eth"), m_vc(_dbPath), m_bc(_dbPath, !m_vc.ok() || _forceClean), m_stateDB(State::openDB(_dbPath, !m_vc.ok() || _forceClean)), - m_preMine(_us, m_stateDB), - m_postMine(_us, m_stateDB), - m_workState(Active) + m_preMine(Address(), m_stateDB), + m_postMine(Address(), m_stateDB) { + m_host = _extNet->registerCapability(new EthereumHost(m_bc, m_tq, m_bq, _networkId)); + + setMiningThreads(); if (_dbPath.size()) Defaults::setDBPath(_dbPath); m_vc.setOk(); - m_changed = true; + doWork(); + + startWorking(); +} + +Client::~Client() +{ + stopWorking(); } -void Client::start() { - static const char* c_threadName = "eth"; +void Client::setNetworkId(u256 _n) +{ + if (auto h = m_host.lock()) + h->setNetworkId(_n); +} - m_work.reset(new thread([&](){ - setThreadName(c_threadName); - while (m_workState.load(std::memory_order_acquire) != Deleting) - work(); - m_workState.store(Deleted, std::memory_order_release); +DownloadMan const* Client::downloadMan() const { if (auto h = m_host.lock()) return &(h->downloadMan()); return nullptr; } +bool Client::isSyncing() const { if (auto h = m_host.lock()) return h->isSyncing(); return false; } - // Synchronise the state according to the head of the block chain. - // TODO: currently it contains keys for *all* blocks. Make it remove old ones. - m_preMine.sync(m_bc); - m_postMine = m_preMine; - })); +void Client::doneWorking() +{ + // Synchronise the state according to the head of the block chain. + // TODO: currently it contains keys for *all* blocks. Make it remove old ones. + WriteGuard l(x_stateDB); + m_preMine.sync(m_bc); + m_postMine = m_preMine; } -Client::~Client() +void Client::flushTransactions() { - if (m_workState.load(std::memory_order_acquire) == Active) - m_workState.store(Deleting, std::memory_order_release); - while (m_workState.load(std::memory_order_acquire) != Deleted) - this_thread::sleep_for(chrono::milliseconds(10)); - m_work->join(); + doWork(); } -void Client::startNetwork(unsigned short _listenPort, std::string const& _seedHost, unsigned short _port, NodeMode _mode, unsigned _peers, string const& _publicIP, bool _upnp) +void Client::killChain() { - ClientGuard l(this); - if (m_net.get()) - return; - try + bool wasMining = isMining(); + if (wasMining) + stopMining(); + stopWorking(); + + m_tq.clear(); + m_bq.clear(); + m_miners.clear(); + m_preMine = State(); + m_postMine = State(); + + { + WriteGuard l(x_stateDB); + m_stateDB = OverlayDB(); + m_stateDB = State::openDB(Defaults::dbPath(), true); + } + m_bc.reopen(Defaults::dbPath(), true); + + m_preMine = State(Address(), m_stateDB); + m_postMine = State(Address(), m_stateDB); + + if (auto h = m_host.lock()) + h->reset(); + + doWork(); + + setMiningThreads(0); + + startWorking(); + if (wasMining) + startMining(); +} + +void Client::clearPending() +{ + h256Set changeds; { - m_net.reset(new PeerServer(m_clientVersion, m_bc, 0, _listenPort, _mode, _publicIP, _upnp)); + WriteGuard l(x_stateDB); + if (!m_postMine.pending().size()) + return; + for (unsigned i = 0; i < m_postMine.pending().size(); ++i) + appendFromNewPending(m_postMine.bloom(i), changeds); + changeds.insert(PendingChangedFilter); + m_postMine = m_preMine; } - catch (std::exception const&) + { - // Probably already have the port open. - cwarn << "Could not initialize with specified/default port. Trying system-assigned port"; - m_net.reset(new PeerServer(m_clientVersion, m_bc, 0, _mode, _publicIP, _upnp)); + ReadGuard l(x_miners); + for (auto& m: m_miners) + m.noteStateChange(); } - m_net->setIdealPeerCount(_peers); - if (_seedHost.size()) - connect(_seedHost, _port); + noteChanged(changeds); } -std::vector Client::peers() +unsigned Client::installWatch(h256 _h) { - ClientGuard l(this); - return m_net ? m_net->peers() : std::vector(); + auto ret = m_watches.size() ? m_watches.rbegin()->first + 1 : 0; + m_watches[ret] = ClientWatch(_h); + cwatch << "+++" << ret << _h; + return ret; } -size_t Client::peerCount() const +unsigned Client::installWatch(MessageFilter const& _f) { - ClientGuard l(this); - return m_net ? m_net->peerCount() : 0; + lock_guard l(m_filterLock); + + h256 h = _f.sha3(); + + if (!m_filters.count(h)) + m_filters.insert(make_pair(h, _f)); + + return installWatch(h); } -void Client::connect(std::string const& _seedHost, unsigned short _port) +void Client::uninstallWatch(unsigned _i) { - ClientGuard l(this); - if (!m_net.get()) + cwatch << "XXX" << _i; + + lock_guard l(m_filterLock); + + auto it = m_watches.find(_i); + if (it == m_watches.end()) return; - m_net->connect(_seedHost, _port); + auto id = it->second.id; + m_watches.erase(it); + + auto fit = m_filters.find(id); + if (fit != m_filters.end()) + if (!--fit->second.refCount) + m_filters.erase(fit); } -void Client::stopNetwork() +void Client::noteChanged(h256Set const& _filters) { - ClientGuard l(this); - m_net.reset(nullptr); + lock_guard l(m_filterLock); + for (auto& i: m_watches) + if (_filters.count(i.second.id)) + { + cwatch << "!!!" << i.first << i.second.id; + i.second.changes++; + } +} + +void Client::appendFromNewPending(h256 _bloom, h256Set& o_changed) const +{ + lock_guard l(m_filterLock); + for (pair const& i: m_filters) + if ((unsigned)i.second.filter.latest() > m_bc.number() && i.second.filter.matches(_bloom)) + o_changed.insert(i.first); } -void Client::startMining() +void Client::appendFromNewBlock(h256 _block, h256Set& o_changed) const { - m_doMine = true; - m_restartMining = true; + auto d = m_bc.details(_block); + + lock_guard l(m_filterLock); + for (pair const& i: m_filters) + if ((unsigned)i.second.filter.latest() >= d.number && (unsigned)i.second.filter.earliest() <= d.number && i.second.filter.matches(d.bloom)) + o_changed.insert(i.first); } -void Client::stopMining() +void Client::setForceMining(bool _enable) { - m_doMine = false; + m_forceMining = _enable; + if (!m_host.lock()) + { + ReadGuard l(x_miners); + for (auto& m: m_miners) + m.noteStateChange(); + } +} + +void Client::setMiningThreads(unsigned _threads) +{ + stopMining(); + + auto t = _threads ? _threads : thread::hardware_concurrency(); + WriteGuard l(x_miners); + m_miners.clear(); + m_miners.resize(t); + unsigned i = 0; + for (auto& m: m_miners) + m.setup(this, i++); +} + +MineProgress Client::miningProgress() const +{ + MineProgress ret; + ReadGuard l(x_miners); + for (auto& m: m_miners) + ret.combine(m.miningProgress()); + return ret; +} + +std::list Client::miningHistory() +{ + std::list ret; + + ReadGuard l(x_miners); + if (m_miners.empty()) + return ret; + ret = m_miners[0].miningHistory(); + for (unsigned i = 1; i < m_miners.size(); ++i) + { + auto l = m_miners[i].miningHistory(); + auto ri = ret.begin(); + auto li = l.begin(); + for (; ri != ret.end() && li != l.end(); ++ri, ++li) + ri->combine(*li); + } + return ret; +} + +void Client::setupState(State& _s) +{ + { + ReadGuard l(x_stateDB); + cwork << "SETUP MINE"; + _s = m_postMine; + } + if (m_paranoia) + { + if (_s.amIJustParanoid(m_bc)) + { + cnote << "I'm just paranoid. Block is fine."; + _s.commitToMine(m_bc); + } + else + { + cwarn << "I'm not just paranoid. Cannot mine. Please file a bug report."; + } + } + else + _s.commitToMine(m_bc); } void Client::transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) { - ClientGuard l(this); + startWorking(); + Transaction t; - cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret)); - t.nonce = m_postMine.transactionsFrom(toAddress(_secret)); +// cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret)); + { + ReadGuard l(x_stateDB); + t.nonce = m_postMine.transactionsFrom(toAddress(_secret)); + } t.value = _value; t.gasPrice = _gasPrice; t.gas = _gas; @@ -164,11 +319,37 @@ void Client::transact(Secret _secret, u256 _value, Address _dest, bytes const& _ m_tq.attemptImport(t.rlp()); } +bytes Client::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) +{ + State temp; + Transaction t; +// cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret)); + { + ReadGuard l(x_stateDB); + temp = m_postMine; + t.nonce = temp.transactionsFrom(toAddress(_secret)); + } + t.value = _value; + t.gasPrice = _gasPrice; + t.gas = _gas; + t.receiveAddress = _dest; + t.data = _data; + t.sign(_secret); + bytes out; + u256 gasUsed = temp.execute(t.data, &out, false); + (void)gasUsed; // TODO: do something with gasused which it returns. + return out; +} + Address Client::transact(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) { - ClientGuard l(this); + startWorking(); + Transaction t; - t.nonce = m_postMine.transactionsFrom(toAddress(_secret)); + { + ReadGuard l(x_stateDB); + t.nonce = m_postMine.transactionsFrom(toAddress(_secret)); + } t.value = _endowment; t.gasPrice = _gasPrice; t.gas = _gas; @@ -182,25 +363,39 @@ Address Client::transact(Secret _secret, u256 _endowment, bytes const& _init, u2 void Client::inject(bytesConstRef _rlp) { - ClientGuard l(this); + startWorking(); + m_tq.attemptImport(_rlp); - m_changed = true; } -void Client::work() +void Client::doWork() { - bool changed = false; + // TODO: Use condition variable rather than polling. - // Process network events. - // Synchronise block chain with network. - // Will broadcast any of our (new) transactions and blocks, and collect & add any of their (new) transactions and blocks. - if (m_net) - { - ClientGuard l(this); - m_net->process(); // must be in guard for now since it uses the blockchain. TODO: make BlockChain thread-safe. + cworkin << "WORK"; + h256Set changeds; - if (m_net->sync(m_bc, m_tq, m_stateDB)) - changed = true; + { + ReadGuard l(x_miners); + for (auto& m: m_miners) + if (m.isComplete()) + { + cwork << "CHAIN <== postSTATE"; + h256s hs; + { + WriteGuard l(x_stateDB); + hs = m_bc.attemptImport(m.blockData(), m_stateDB); + } + if (hs.size()) + { + for (auto h: hs) + appendFromNewBlock(h, changeds); + changeds.insert(ChainChangedFilter); + //changeds.insert(PendingChangedFilter); // if we mined the new block, then we've probably reset the pending transactions. + } + for (auto& m: m_miners) + m.noteStateChange(); + } } // Synchronise state to block chain. @@ -210,90 +405,60 @@ void Client::work() // if there are no checkpoints before our fork) reverting to the genesis block and replaying // all blocks. // Resynchronise state with block chain & trans + bool rsm = false; { - ClientGuard l(this); + WriteGuard l(x_stateDB); + cwork << "BQ ==> CHAIN ==> STATE"; + OverlayDB db = m_stateDB; + x_stateDB.unlock(); + h256s newBlocks = m_bc.sync(m_bq, db, 100); // TODO: remove transactions from m_tq nicely rather than relying on out of date nonce later on. + if (newBlocks.size()) + { + for (auto i: newBlocks) + appendFromNewBlock(i, changeds); + changeds.insert(ChainChangedFilter); + } + x_stateDB.lock(); + if (newBlocks.size()) + m_stateDB = db; + + cwork << "preSTATE <== CHAIN"; if (m_preMine.sync(m_bc) || m_postMine.address() != m_preMine.address()) { - if (m_doMine) + if (isMining()) cnote << "New block on chain: Restarting mining operation."; - changed = true; - m_restartMining = true; // need to re-commit to mine. m_postMine = m_preMine; + rsm = true; + changeds.insert(PendingChangedFilter); + // TODO: Move transactions pending from m_postMine back to transaction queue. } - if (m_postMine.sync(m_tq, &changed)) - { - if (m_doMine) - cnote << "Additional transaction ready: Restarting mining operation."; - m_restartMining = true; - } - } - if (m_doMine) - { - if (m_restartMining) + // returns h256s as blooms, once for each transaction. + cwork << "postSTATE <== TQ"; + h256s newPendingBlooms = m_postMine.sync(m_tq); + if (newPendingBlooms.size()) { - m_mineProgress.best = (double)-1; - m_mineProgress.hashes = 0; - m_mineProgress.ms = 0; - ClientGuard l(this); - if (m_paranoia) - { - if (m_postMine.amIJustParanoid(m_bc)) - { - cnote << "I'm just paranoid. Block is fine."; - m_postMine.commitToMine(m_bc); - } - else - { - cwarn << "I'm not just paranoid. Cannot mine. Please file a bug report."; - m_doMine = false; - } - } - else - m_postMine.commitToMine(m_bc); + for (auto i: newPendingBlooms) + appendFromNewPending(i, changeds); + changeds.insert(PendingChangedFilter); + + if (isMining()) + cnote << "Additional transaction ready: Restarting mining operation."; + rsm = true; } } - - if (m_doMine) + if (rsm) { - m_restartMining = false; - - // Mine for a while. - MineInfo mineInfo = m_postMine.mine(100); - - m_mineProgress.best = min(m_mineProgress.best, mineInfo.best); - m_mineProgress.current = mineInfo.best; - m_mineProgress.requirement = mineInfo.requirement; - m_mineProgress.ms += 100; - m_mineProgress.hashes += mineInfo.hashes; - { - ClientGuard l(this); - m_mineHistory.push_back(mineInfo); - } - - if (mineInfo.completed) - { - // Import block. - ClientGuard l(this); - m_postMine.completeMine(); - m_bc.attemptImport(m_postMine.blockData(), m_stateDB); - m_changed = true; - } + ReadGuard l(x_miners); + for (auto& m: m_miners) + m.noteStateChange(); } - else - this_thread::sleep_for(chrono::milliseconds(100)); - - m_changed = m_changed || changed; -} -void Client::lock() const -{ - m_lock.lock(); -} + cwork << "noteChanged" << changeds.size() << "items"; + noteChanged(changeds); + cworkout << "WORK"; -void Client::unlock() const -{ - m_lock.unlock(); + this_thread::sleep_for(chrono::milliseconds(100)); } unsigned Client::numberOf(int _n) const @@ -308,6 +473,7 @@ unsigned Client::numberOf(int _n) const State Client::asOf(int _h) const { + ReadGuard l(x_stateDB); if (_h == 0) return m_postMine; else if (_h == -1) @@ -316,154 +482,81 @@ State Client::asOf(int _h) const return State(m_stateDB, m_bc, m_bc.numberHash(numberOf(_h))); } -u256 Client::balanceAt(Address _a, int _block) const +State Client::state(unsigned _txi, h256 _block) const { - ClientGuard l(this); - return asOf(_block).balance(_a); + ReadGuard l(x_stateDB); + return State(m_stateDB, m_bc, _block).fromPending(_txi); } -u256 Client::countAt(Address _a, int _block) const +eth::State Client::state(h256 _block) const { - ClientGuard l(this); - return asOf(_block).transactionsFrom(_a); + ReadGuard l(x_stateDB); + return State(m_stateDB, m_bc, _block); } -u256 Client::stateAt(Address _a, u256 _l, int _block) const +eth::State Client::state(unsigned _txi) const { - ClientGuard l(this); - return asOf(_block).storage(_a, _l); + ReadGuard l(x_stateDB); + return m_postMine.fromPending(_txi); } -bytes Client::codeAt(Address _a, int _block) const +StateDiff Client::diff(unsigned _txi, int _block) const { - ClientGuard l(this); - return asOf(_block).code(_a); + State st = state(_block); + return st.fromPending(_txi).diff(st.fromPending(_txi + 1)); } -bool TransactionFilter::matches(h256 _bloom) const +StateDiff Client::diff(unsigned _txi, h256 _block) const { - auto have = [=](Address const& a) { return _bloom.contains(a.bloom()); }; - if (m_from.size()) - { - for (auto i: m_from) - if (have(i)) - goto OK1; - return false; - } - OK1: - if (m_to.size()) - { - for (auto i: m_to) - if (have(i)) - goto OK2; - return false; - } - OK2: - if (m_stateAltered.size() || m_altered.size()) - { - for (auto i: m_altered) - if (have(i)) - goto OK3; - for (auto i: m_stateAltered) - if (have(i.first) && _bloom.contains(h256(i.second).bloom())) - goto OK3; - return false; - } - OK3: - return true; + State st = state(_block); + return st.fromPending(_txi).diff(st.fromPending(_txi + 1)); } -bool TransactionFilter::matches(State const& _s, unsigned _i) const +std::vector
Client::addresses(int _block) const { - h256 b = _s.changesFromPending(_i).bloom(); - if (!matches(b)) - return false; - - Transaction t = _s.pending()[_i]; - if (!m_to.empty() && !m_to.count(t.receiveAddress)) - return false; - if (!m_from.empty() && !m_from.count(t.sender())) - return false; - if (m_stateAltered.empty() && m_altered.empty()) - return true; - StateDiff d = _s.pendingDiff(_i); - if (!m_altered.empty()) - { - for (auto const& s: m_altered) - if (d.accounts.count(s)) - return true; - return false; - } - if (!m_stateAltered.empty()) - { - for (auto const& s: m_stateAltered) - if (d.accounts.count(s.first) && d.accounts.at(s.first).storage.count(s.second)) - return true; - return false; - } - return true; + vector
ret; + for (auto const& i: asOf(_block).addresses()) + ret.push_back(i.first); + return ret; } -PastMessages TransactionFilter::matches(Manifest const& _m, unsigned _i) const +u256 Client::balanceAt(Address _a, int _block) const { - PastMessages ret; - matches(_m, vector(1, _i), _m.from, PastMessages(), ret); - return ret; + return asOf(_block).balance(_a); } -bool TransactionFilter::matches(Manifest const& _m, vector _p, Address _o, PastMessages _limbo, PastMessages& o_ret) const +std::map Client::storageAt(Address _a, int _block) const { - bool ret; - - if ((m_from.empty() || m_from.count(_m.from)) && (m_to.empty() || m_to.count(_m.to))) - _limbo.push_back(PastMessage(_m, _p, _o)); - - // Handle limbos, by checking against all addresses in alteration. - bool alters = m_altered.empty() && m_stateAltered.empty(); - alters = alters || m_altered.count(_m.from) || m_altered.count(_m.to); - - if (!alters) - for (auto const& i: _m.altered) - if (m_altered.count(_m.to) || m_stateAltered.count(make_pair(_m.to, i))) - { - alters = true; - break; - } - // If we do alter stuff, - if (alters) - { - o_ret += _limbo; - _limbo.clear(); - ret = true; - } + return asOf(_block).storage(_a); +} - _p.push_back(0); - for (auto const& m: _m.internal) - { - if (matches(m, _p, _o, _limbo, o_ret)) - { - _limbo.clear(); - ret = true; - } - _p.back()++; - } +u256 Client::countAt(Address _a, int _block) const +{ + return asOf(_block).transactionsFrom(_a); +} - return ret; +u256 Client::stateAt(Address _a, u256 _l, int _block) const +{ + return asOf(_block).storage(_a, _l); } -PastMessages Client::transactions(TransactionFilter const& _f) const +bytes Client::codeAt(Address _a, int _block) const { - ClientGuard l(this); + return asOf(_block).code(_a); +} +PastMessages Client::messages(MessageFilter const& _f) const +{ PastMessages ret; - unsigned begin = numberOf(_f.latest()); - unsigned end = min(begin, numberOf(_f.earliest())); + unsigned begin = min(m_bc.number(), (unsigned)_f.latest()); + unsigned end = min(begin, (unsigned)_f.earliest()); unsigned m = _f.max(); unsigned s = _f.skip(); // Handle pending transactions differently as they're not on the block chain. - if (_f.latest() == 0) + if (begin == m_bc.number()) { + ReadGuard l(x_stateDB); for (unsigned i = 0; i < m_postMine.pending().size(); ++i) { // Might have a transaction that contains a matching message. @@ -477,36 +570,29 @@ PastMessages Client::transactions(TransactionFilter const& _f) const s--; else // Have a transaction that contains a matching message. - ret.insert(ret.begin(), pm[j].polish(h256(), ts, 0)); + ret.insert(ret.begin(), pm[j].polish(h256(), ts, m_bc.number() + 1, m_postMine.address())); } } -/* for (unsigned i = m_postMine.pending().size(); i-- && ret.size() != m;) - if (_f.matches(m_postMine, i)) - { - if (s) - s--; - else - ret.insert(ret.begin(), PastMessage(m_postMine.pending()[i], h256(), i, time(0), 0)); - }*/ - // Early exit here since we can't rely on begin/end, being out of the blockchain as we are. - if (_f.earliest() == 0) - return ret; } +#if ETH_DEBUG unsigned skipped = 0; unsigned falsePos = 0; - auto cn = m_bc.number(); +#endif auto h = m_bc.numberHash(begin); unsigned n = begin; for (; ret.size() != m && n != end; n--, h = m_bc.details(h).parent) { auto d = m_bc.details(h); +#if ETH_DEBUG int total = 0; +#endif if (_f.matches(d.bloom)) { // Might have a block that contains a transaction that contains a matching message. auto bs = m_bc.blooms(h).blooms; Manifests ms; + BlockInfo bi; for (unsigned i = 0; i < bs.size(); ++i) if (_f.matches(bs[i])) { @@ -517,45 +603,35 @@ PastMessages Client::transactions(TransactionFilter const& _f) const PastMessages pm = _f.matches(changes, i); if (pm.size()) { +#if ETH_DEBUG total += pm.size(); - auto ts = BlockInfo(m_bc.block(h)).timestamp; +#endif + if (!bi) + bi.populate(m_bc.block(h)); + auto ts = bi.timestamp; + auto cb = bi.coinbaseAddress; for (unsigned j = 0; j < pm.size() && ret.size() != m; ++j) if (s) s--; else // Have a transaction that contains a matching message. - ret.insert(ret.begin(), pm[j].polish(h, ts, cn - n + 2)); + ret.push_back(pm[j].polish(h, ts, n, cb)); } } +#if ETH_DEBUG if (!total) falsePos++; -/* try - { - State st(m_stateDB, m_bc, h); - unsigned os = s; - for (unsigned i = st.pending().size(); i-- && ret.size() != m;) - if (_f.matches(st, i)) - { - if (s) - s--; - else - ret.insert(ret.begin(), PastMessage(st.pending()[i], h, i, BlockInfo(m_bc.block(h)).timestamp, cn - n + 2)); - } - if (os - s == st.pending().size()) - falsePos++; - } - catch (...) - { - // Gaa. bad state. not good at all. bury head in sand for now. - } -*/ } else skipped++; - +#else + } +#endif if (n == end) break; } +#if ETH_DEBUG // cdebug << (begin - n) << "searched; " << skipped << "skipped; " << falsePos << "false +ves"; +#endif return ret; } diff --git a/libethereum/Client.h b/libethereum/Client.h index 387282935..d00bf53ba 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -25,36 +25,30 @@ #include #include #include -#include +#include +#include +#include +#include +#include +#include #include +#include #include "BlockChain.h" #include "TransactionQueue.h" #include "State.h" -#include "PeerNetwork.h" +#include "CommonNet.h" +#include "PastMessage.h" +#include "MessageFilter.h" +#include "Miner.h" +#include "Interface.h" -namespace eth +namespace dev { - -struct MineProgress +namespace eth { - double requirement; - double best; - double current; - uint hashes; - uint ms; -}; class Client; - -class ClientGuard -{ -public: - inline ClientGuard(Client const* _c); - inline ~ClientGuard(); - -private: - Client const* m_client; -}; +class DownloadMan; enum ClientWorkState { @@ -63,6 +57,12 @@ enum ClientWorkState Deleted }; +enum class NodeMode +{ + PeerServer, + Full +}; + class VersionChecker { public: @@ -78,73 +78,45 @@ private: static const int GenesisBlock = INT_MIN; -struct PastMessage +struct InstalledFilter { - PastMessage(Manifest const& _m, std::vector _path, Address _o): to(_m.to), from(_m.from), value(_m.value), input(_m.input), output(_m.output), path(_path), origin(_o) {} - - PastMessage& polish(h256 _b, u256 _ts, int _a) { block = _b; timestamp = _ts; age = _a; return *this; } + InstalledFilter(MessageFilter const& _f): filter(_f) {} - Address to; ///< The receiving address of the transaction. Address() in the case of a creation. - Address from; ///< The receiving address of the transaction. Address() in the case of a creation. - u256 value; ///< The value associated with the call. - bytes input; ///< The data associated with the message, or the initialiser if it's a creation transaction. - bytes output; ///< The data returned by the message, or the body code if it's a creation transaction. - - std::vector path; ///< Call path into the block transaction. size() is always > 0. First item is the transaction index in the block. - Address origin; ///< Originating sender of the transaction. - h256 block; ///< Block hash. - u256 timestamp; ///< Block timestamp. - int age; ///< Transaction age. + MessageFilter filter; + unsigned refCount = 1; }; -typedef std::vector PastMessages; +static const h256 PendingChangedFilter = u256(0); +static const h256 ChainChangedFilter = u256(1); -class TransactionFilter +struct ClientWatch { -public: - TransactionFilter(int _earliest = GenesisBlock, int _latest = 0, unsigned _max = 10, unsigned _skip = 0): m_earliest(_earliest), m_latest(_latest), m_max(_max), m_skip(_skip) {} - - int earliest() const { return m_earliest; } - int latest() const { return m_latest; } - unsigned max() const { return m_max; } - unsigned skip() const { return m_skip; } - bool matches(h256 _bloom) const; - bool matches(State const& _s, unsigned _i) const; - PastMessages matches(Manifest const& _m, unsigned _i) const; - - TransactionFilter from(Address _a) { m_from.insert(_a); return *this; } - TransactionFilter to(Address _a) { m_to.insert(_a); return *this; } - TransactionFilter altered(Address _a, u256 _l) { m_stateAltered.insert(std::make_pair(_a, _l)); return *this; } - TransactionFilter altered(Address _a) { m_altered.insert(_a); return *this; } - TransactionFilter withMax(unsigned _m) { m_max = _m; return *this; } - TransactionFilter withSkip(unsigned _m) { m_skip = _m; return *this; } - TransactionFilter withEarliest(int _e) { m_earliest = _e; return *this; } - TransactionFilter withLatest(int _e) { m_latest = _e; return *this; } + ClientWatch() {} + explicit ClientWatch(h256 _id): id(_id) {} -private: - bool matches(Manifest const& _m, std::vector _p, Address _o, PastMessages _limbo, PastMessages& o_ret) const; - - std::set
m_from; - std::set
m_to; - std::set> m_stateAltered; - std::set
m_altered; - int m_earliest; - int m_latest; - unsigned m_max; - unsigned m_skip; + h256 id; + unsigned changes = 1; }; +struct WatchChannel: public LogChannel { static const char* name() { return "(o)"; } static const int verbosity = 7; }; +#define cwatch dev::LogOutputStream() +struct WorkInChannel: public LogChannel { static const char* name() { return ">W>"; } static const int verbosity = 16; }; +struct WorkOutChannel: public LogChannel { static const char* name() { return "() +#define cworkin dev::LogOutputStream() +#define cworkout dev::LogOutputStream() + /** * @brief Main API hub for interfacing with Ethereum. */ -class Client +class Client: public MinerHost, public Interface, Worker { -public: - /// Constructor. - explicit Client(std::string const& _clientVersion, Address _us = Address(), std::string const& _dbPath = std::string(), bool _forceClean = false); + friend class Miner; - // Start client. Boost require threads are started outside constructor. - void start(); +public: + /// New-style Constructor. + explicit Client(p2p::Host* _host, std::string const& _dbPath = std::string(), bool _forceClean = false, u256 _networkId = 0); /// Destructor. ~Client(); @@ -156,107 +128,141 @@ public: /// @returns the new contract's address (assuming it all goes through). Address transact(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas = 10000, u256 _gasPrice = 10 * szabo); + /// Injects the RLP-encoded transaction given by the _rlp into the transaction queue directly. void inject(bytesConstRef _rlp); - /// Makes the given call. Nothing is recorded into the state. TODO -// bytes call(Secret _secret, u256 _amount, u256 _gasPrice, Address _dest, u256 _gas, bytes _data = bytes()); - - /// Requires transactions involving this address be queued for inspection. - void setInterest(Address _dest); + /// Blocks until all pending transactions have been processed. + void flushTransactions(); - /// @returns incoming minable transactions that we wanted to be notified of. Clears the queue. - Transactions pendingQueue() { ClientGuard g(this); return m_tq.interestQueue(); } + /// Makes the given call. Nothing is recorded into the state. + bytes call(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo); - /// @returns alterations in state of a mined block that we wanted to be notified of. Clears the queue. - std::vector> minedQueue() { ClientGuard g(this); return m_bc.interestQueue(); } + // Informational stuff - // Not yet - probably best as using some sort of signals implementation. - /// Calls @a _f when a valid transaction is received that involves @a _dest and once per such transaction. -// void onPending(Address _dest, function const& _f); + // [NEW API] - /// Calls @a _f when a transaction is mined that involves @a _dest and once per change. -// void onConfirmed(Address _dest, function const& _f); + using Interface::balanceAt; + using Interface::countAt; + using Interface::stateAt; + using Interface::codeAt; + using Interface::storageAt; - // Informational stuff + u256 balanceAt(Address _a, int _block) const; + u256 countAt(Address _a, int _block) const; + u256 stateAt(Address _a, u256 _l, int _block) const; + bytes codeAt(Address _a, int _block) const; + std::map storageAt(Address _a, int _block) const; - /// Determines whether at least one of the state/blockChain/transactionQueue has changed since the last call to changed(). - bool changed() const { auto ret = m_changed; m_changed = false; return ret; } - bool peekChanged() const { return m_changed; } + unsigned installWatch(MessageFilter const& _filter); + unsigned installWatch(h256 _filterId); + void uninstallWatch(unsigned _watchId); + bool peekWatch(unsigned _watchId) const { std::lock_guard l(m_filterLock); try { return m_watches.at(_watchId).changes != 0; } catch (...) { return false; } } + bool checkWatch(unsigned _watchId) { std::lock_guard l(m_filterLock); bool ret = false; try { ret = m_watches.at(_watchId).changes != 0; m_watches.at(_watchId).changes = 0; } catch (...) {} return ret; } - /// Get a map containing each of the pending transactions. - Transactions pending() const { return m_postMine.pending(); } + PastMessages messages(unsigned _watchId) const { try { std::lock_guard l(m_filterLock); return messages(m_filters.at(m_watches.at(_watchId).id).filter); } catch (...) { return PastMessages(); } } + PastMessages messages(MessageFilter const& _filter) const; - // [OLD API]: + // [EXTRA API]: - /// Locks/unlocks the state/blockChain/transactionQueue for access. - void lock() const; - void unlock() const; + /// @returns the length of the chain. + virtual unsigned number() const { return m_bc.number(); } - /// Get the object representing the current state of Ethereum. - State const& state() const { return m_preMine; } - /// Get the object representing the current state of Ethereum. - State const& postState() const { return m_postMine; } - /// Get the object representing the current canonical blockchain. - BlockChain const& blockChain() const { return m_bc; } - - // [NEW API] + /// Get a map containing each of the pending transactions. + /// @TODO: Remove in favour of transactions(). + Transactions pending() const { return m_postMine.pending(); } - u256 balanceAt(Address _a, int _block = -1) const; - u256 countAt(Address _a, int _block = -1) const; - u256 stateAt(Address _a, u256 _l, int _block = -1) const; - bytes codeAt(Address _a, int _block = -1) const; - PastMessages transactions(TransactionFilter const& _f) const; + /// Differences between transactions. + using Interface::diff; + StateDiff diff(unsigned _txi, h256 _block) const; + StateDiff diff(unsigned _txi, int _block) const; - // Misc stuff: + /// Get a list of all active addresses. + using Interface::addresses; + std::vector
addresses(int _block) const; - void setClientVersion(std::string const& _name) { m_clientVersion = _name; } + /// Get the remaining gas limit in this block. + u256 gasLimitRemaining() const { return m_postMine.gasLimitRemaining(); } - // Network stuff: + // [PRIVATE API - only relevant for base clients, not available in general] - /// Get information on the current peer set. - std::vector peers(); - /// Same as peers().size(), but more efficient. - size_t peerCount() const; + dev::eth::State state(unsigned _txi, h256 _block) const; + dev::eth::State state(h256 _block) const; + dev::eth::State state(unsigned _txi) const; - /// Start the network subsystem. - void startNetwork(unsigned short _listenPort = 30303, std::string const& _remoteHost = std::string(), unsigned short _remotePort = 30303, NodeMode _mode = NodeMode::Full, unsigned _peers = 5, std::string const& _publicIP = std::string(), bool _upnp = true); - /// Connect to a particular peer. - void connect(std::string const& _seedHost, unsigned short _port = 30303); - /// Stop the network subsystem. - void stopNetwork(); - /// Is the network subsystem up? - bool haveNetwork() { return !!m_net; } - /// Get access to the peer server object. This will be null if the network isn't online. - PeerServer* peerServer() const { return m_net.get(); } + /// Get the object representing the current state of Ethereum. + dev::eth::State postState() const { ReadGuard l(x_stateDB); return m_postMine; } + /// Get the object representing the current canonical blockchain. + BlockChain const& blockChain() const { return m_bc; } // Mining stuff: /// Check block validity prior to mining. - bool paranoia() const { return m_paranoia; } + bool miningParanoia() const { return m_paranoia; } /// Change whether we check block validity prior to mining. void setParanoia(bool _p) { m_paranoia = _p; } + /// Should we force mining to happen, even without transactions? + bool forceMining() const { return m_forceMining; } + /// Enable/disable forcing of mining to happen, even without transactions. + void setForceMining(bool _enable); + /// Are we mining as fast as we can? + bool turboMining() const { return m_turboMining; } + /// Enable/disable fast mining. + void setTurboMining(bool _enable = true) { m_turboMining = _enable; } + /// Set the coinbase address. void setAddress(Address _us) { m_preMine.setAddress(_us); } /// Get the coinbase address. Address address() const { return m_preMine.address(); } + /// Stops mining and sets the number of mining threads (0 for automatic). + void setMiningThreads(unsigned _threads = 0); + /// Get the effective number of mining threads. + unsigned miningThreads() const { ReadGuard l(x_miners); return m_miners.size(); } /// Start mining. - void startMining(); + /// NOT thread-safe - call it & stopMining only from a single thread + void startMining() { startWorking(); ReadGuard l(x_miners); for (auto& m: m_miners) m.start(); } /// Stop mining. - void stopMining(); + /// NOT thread-safe + void stopMining() { ReadGuard l(x_miners); for (auto& m: m_miners) m.stop(); } /// Are we mining now? - bool isMining() { return m_doMine; } - /// Register a callback for information concerning mining. - /// This callback will be in an arbitrary thread, blocking progress. JUST COPY THE DATA AND GET OUT. + bool isMining() { ReadGuard l(x_miners); return m_miners.size() && m_miners[0].isRunning(); } /// Check the progress of the mining. - MineProgress miningProgress() const { return m_mineProgress; } + MineProgress miningProgress() const; /// Get and clear the mining history. - std::list miningHistory() { auto ret = m_mineHistory; m_mineHistory.clear(); return ret; } + std::list miningHistory(); + + // Debug stuff: + DownloadMan const* downloadMan() const; + bool isSyncing() const; + /// Sets the network id. + void setNetworkId(u256 _n); /// Clears pending transactions. Just for debug use. - void clearPending() { ClientGuard l(this); m_postMine = m_preMine; changed(); } + void clearPending(); + /// Kills the blockchain. Just for debug use. + void killChain(); private: - void work(); + /// Do some work. Handles blockchain maintenance and mining. + virtual void doWork(); + + virtual void doneWorking(); + + /// Overrides for being a mining host. + virtual void setupState(State& _s); + virtual bool turbo() const { return m_turboMining; } + virtual bool force() const { return m_forceMining; } + + /// Collate the changed filters for the bloom filter of the given pending transaction. + /// Insert any filters that are activated into @a o_changed. + void appendFromNewPending(h256 _pendingTransactionBloom, h256Set& o_changed) const; + + /// Collate the changed filters for the hash of the given block. + /// Insert any filters that are activated into @a o_changed. + void appendFromNewBlock(h256 _blockHash, h256Set& o_changed) const; + + /// Record that the set of filters @a _filters have changed. + /// This doesn't actually make any callbacks, but incrememnts some counters in m_watches. + void noteChanged(h256Set const& _filters); /// Return the actual block number of the block with the given int-number (positive is the same, INT_MIN is genesis block, < 0 is negative age, thus -1 is most recently mined, 0 is pending. unsigned numberOf(int _b) const; @@ -264,36 +270,28 @@ private: State asOf(int _h) const; State asOf(unsigned _h) const; - std::string m_clientVersion; ///< Our end-application client's name/version. - VersionChecker m_vc; ///< Dummy object to check & update the protocol version. - BlockChain m_bc; ///< Maintains block database. - TransactionQueue m_tq; ///< Maintains list of incoming transactions not yet on the block chain. + VersionChecker m_vc; ///< Dummy object to check & update the protocol version. + BlockChain m_bc; ///< Maintains block database. + TransactionQueue m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain. + BlockQueue m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported). + + mutable boost::shared_mutex x_stateDB; ///< Lock on the state DB, effectively a lock on m_postMine. OverlayDB m_stateDB; ///< Acts as the central point for the state database, so multiple States can share it. - State m_preMine; ///< The present state of the client. - State m_postMine; ///< The state of the client which we're mining (i.e. it'll have all the rewards added). - std::unique_ptr m_net; ///< Should run in background and send us events when blocks found and allow us to send blocks as required. - - std::unique_ptr m_work;///< The work thread. - - mutable std::recursive_mutex m_lock; - std::atomic m_workState; - bool m_paranoia = false; - bool m_doMine = false; ///< Are we supposed to be mining? - MineProgress m_mineProgress; - std::list m_mineHistory; - mutable bool m_restartMining = false; - - mutable bool m_changed; -}; + State m_preMine; ///< The present state of the client. + State m_postMine; ///< The state of the client which we're mining (i.e. it'll have all the rewards added). -inline ClientGuard::ClientGuard(Client const* _c): m_client(_c) -{ - m_client->lock(); -} + std::weak_ptr m_host; ///< Our Ethereum Host. Don't do anything if we can't lock. -inline ClientGuard::~ClientGuard() -{ - m_client->unlock(); -} + std::vector m_miners; + mutable boost::shared_mutex x_miners; + bool m_paranoia = false; ///< Should we be paranoid about our state? + bool m_turboMining = false; ///< Don't squander all of our time mining actually just sleeping. + bool m_forceMining = false; ///< Mine even when there are no transactions pending? + mutable std::mutex m_filterLock; + std::map m_filters; + std::map m_watches; +}; + +} } diff --git a/libethereum/CommonNet.cpp b/libethereum/CommonNet.cpp new file mode 100644 index 000000000..eb92f0b18 --- /dev/null +++ b/libethereum/CommonNet.cpp @@ -0,0 +1,28 @@ +/* + 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 . +*/ +/** @file PeerNetwork.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "CommonNet.h" +using namespace std; +using namespace dev; +using namespace dev::eth; + +#pragma GCC diagnostic ignored "-Wunused-variable" +namespace { char dummy; }; diff --git a/libethereum/CommonNet.h b/libethereum/CommonNet.h new file mode 100644 index 000000000..c53b644e4 --- /dev/null +++ b/libethereum/CommonNet.h @@ -0,0 +1,74 @@ +/* + 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 . +*/ +/** @file PeerNetwork.h + * @author Gav Wood + * @date 2014 + * + * Miscellanea required for the PeerServer/Session classes. + */ + +#pragma once + +#include +#include +#include +#include + +namespace dev +{ +namespace eth +{ + +#if ETH_DEBUG +static const unsigned c_maxHashes = 64; ///< Maximum number of hashes BlockHashes will ever send. +static const unsigned c_maxHashesAsk = 64; ///< Maximum number of hashes GetBlockHashes will ever ask for. +static const unsigned c_maxBlocks = 32; ///< Maximum number of blocks Blocks will ever send. +static const unsigned c_maxBlocksAsk = 32; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain). +#else +static const unsigned c_maxHashes = 256; ///< Maximum number of hashes BlockHashes will ever send. +static const unsigned c_maxHashesAsk = 256; ///< Maximum number of hashes GetBlockHashes will ever ask for. +static const unsigned c_maxBlocks = 128; ///< Maximum number of blocks Blocks will ever send. +static const unsigned c_maxBlocksAsk = 128; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain). +#endif +class OverlayDB; +class BlockChain; +class TransactionQueue; +class EthereumHost; +class EthereumPeer; + +enum EthereumPacket +{ + StatusPacket = 0x10, + GetTransactionsPacket, + TransactionsPacket, + GetBlockHashesPacket, + BlockHashesPacket, + GetBlocksPacket, + BlocksPacket, +}; + +enum class Grabbing +{ + State, + Hashes, + Chain, + ChainHelper, + Nothing +}; + +} +} diff --git a/libethereum/Defaults.cpp b/libethereum/Defaults.cpp index e78156094..febe53d84 100644 --- a/libethereum/Defaults.cpp +++ b/libethereum/Defaults.cpp @@ -21,9 +21,10 @@ #include "Defaults.h" -#include +#include using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; Defaults* Defaults::s_this = nullptr; diff --git a/libethereum/Defaults.h b/libethereum/Defaults.h index d4bd5322f..91d5872f7 100644 --- a/libethereum/Defaults.h +++ b/libethereum/Defaults.h @@ -21,8 +21,10 @@ #pragma once -#include +#include +namespace dev +{ namespace eth { @@ -45,3 +47,4 @@ private: }; } +} diff --git a/libethereum/DownloadMan.cpp b/libethereum/DownloadMan.cpp new file mode 100644 index 000000000..09a3bcc00 --- /dev/null +++ b/libethereum/DownloadMan.cpp @@ -0,0 +1,75 @@ +/* + 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 . +*/ +/** @file DownloadMan.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "DownloadMan.h" +using namespace std; +using namespace dev; +using namespace dev::eth; + +DownloadSub::DownloadSub(DownloadMan& _man): m_man(&_man) +{ + WriteGuard l(m_man->x_subs); + m_man->m_subs.insert(this); +} + +DownloadSub::~DownloadSub() +{ + if (m_man) + { + WriteGuard l(m_man->x_subs); + m_man->m_subs.erase(this); + } +} + +h256Set DownloadSub::nextFetch(unsigned _n) +{ + Guard l(m_fetch); + + if (m_remaining.size()) + return m_remaining; + + m_asked.clear(); + m_indices.clear(); + m_remaining.clear(); + + if (!m_man) + return h256Set(); + + m_asked = (~(m_man->taken() + m_attempted)).lowest(_n); + if (m_asked.empty()) + m_asked = (~(m_man->taken(true) + m_attempted)).lowest(_n); + m_attempted += m_asked; + for (auto i: m_asked) + { + auto x = m_man->m_chain[i]; + m_remaining.insert(x); + m_indices[x] = i; + } + return m_remaining; +} + +void DownloadSub::noteBlock(h256 _hash) +{ + Guard l(m_fetch); + if (m_man && m_indices.count(_hash)) + m_man->m_blocksGot += m_indices[_hash]; + m_remaining.erase(_hash); +} diff --git a/libethereum/DownloadMan.h b/libethereum/DownloadMan.h new file mode 100644 index 000000000..e67076d5a --- /dev/null +++ b/libethereum/DownloadMan.h @@ -0,0 +1,136 @@ +/* + 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 . +*/ +/** @file DownloadMan.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace dev +{ + +namespace eth +{ + +class DownloadMan; + +class DownloadSub +{ + friend class DownloadMan; + +public: + DownloadSub(DownloadMan& _man); + ~DownloadSub(); + + /// Finished last fetch - grab the next bunch of block hashes to download. + h256Set nextFetch(unsigned _n); + + /// Note that we've received a particular block. + void noteBlock(h256 _hash); + + /// Nothing doing here. + void doneFetch() { resetFetch(); } + + RangeMask const& asked() const { return m_asked; } + RangeMask const& attemped() const { return m_attempted; } + +private: + void resetFetch() // Called by DownloadMan when we need to reset the download. + { + Guard l(m_fetch); + m_remaining.clear(); + m_indices.clear(); + m_asked.clear(); + m_attempted.clear(); + } + + DownloadMan* m_man = nullptr; + + Mutex m_fetch; + h256Set m_remaining; + std::map m_indices; + RangeMask m_asked; + RangeMask m_attempted; +}; + +class DownloadMan +{ + friend class DownloadSub; + +public: + ~DownloadMan() + { + for (auto i: m_subs) + i->m_man = nullptr; + } + + void resetToChain(h256s const& _chain) + { + { + ReadGuard l(x_subs); + for (auto i: m_subs) + i->resetFetch(); + } + m_chain.clear(); + m_chain.reserve(_chain.size()); + for (auto i = _chain.rbegin(); i != _chain.rend(); ++i) + m_chain.push_back(*i); + m_blocksGot = RangeMask(0, m_chain.size()); + } + + RangeMask taken(bool _desperate = false) const + { + auto ret = m_blocksGot; + if (!_desperate) + { + ReadGuard l(x_subs); + for (auto i: m_subs) + ret += i->m_asked; + } + return ret; + } + + bool isComplete() const + { + return m_blocksGot.full(); + } + + h256s chain() const { return m_chain; } + void foreachSub(std::function const& _f) const { ReadGuard l(x_subs); for(auto i: m_subs) _f(*i); } + unsigned subCount() const { ReadGuard l(x_subs); return m_subs.size(); } + RangeMask blocksGot() const { return m_blocksGot; } + +private: + h256s m_chain; + RangeMask m_blocksGot; + + mutable SharedMutex x_subs; + std::set m_subs; +}; + +} + +} diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp new file mode 100644 index 000000000..1b3049fba --- /dev/null +++ b/libethereum/EthereumHost.cpp @@ -0,0 +1,281 @@ +/* + 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 . +*/ +/** @file EthereumHost.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "EthereumHost.h" + +#include +#include +#include +#include +#include +#include +#include +#include "BlockChain.h" +#include "TransactionQueue.h" +#include "BlockQueue.h" +#include "EthereumPeer.h" +#include "DownloadMan.h" +using namespace std; +using namespace dev; +using namespace dev::eth; +using namespace p2p; + +EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId): + HostCapability(), + Worker ("ethsync"), + m_chain (_ch), + m_tq (_tq), + m_bq (_bq), + m_networkId (_networkId) +{ + m_latestBlockSent = _ch.currentHash(); +} + +EthereumHost::~EthereumHost() +{ + for (auto const& i: peers()) + i->cap()->giveUpOnFetch(); +} + +bool EthereumHost::ensureInitialised(TransactionQueue& _tq) +{ + if (!m_latestBlockSent) + { + // First time - just initialise. + m_latestBlockSent = m_chain.currentHash(); + clog(NetNote) << "Initialising: latest=" << m_latestBlockSent.abridged(); + + for (auto const& i: _tq.transactions()) + m_transactionsSent.insert(i.first); + return true; + } + return false; +} + +void EthereumHost::noteHavePeerState(EthereumPeer* _who) +{ + clog(NetAllDetail) << "Have peer state."; + + // if already downloading hash-chain, ignore. + if (m_grabbing != Grabbing::Nothing) + { + clog(NetAllDetail) << "Already downloading chain. Just set to help out."; + _who->ensureGettingChain(); + return; + } + + // otherwise check to see if we should be downloading... + _who->tryGrabbingHashChain(); +} + +void EthereumHost::updateGrabbing(Grabbing _g) +{ + m_grabbing = _g; + if (_g == Grabbing::Nothing) + readyForSync(); + else if (_g == Grabbing::Chain) + for (auto j: peers()) + j->cap()->ensureGettingChain(); +} + +void EthereumHost::noteHaveChain(EthereumPeer* _from) +{ + auto td = _from->m_totalDifficulty; + + if (_from->m_neededBlocks.empty()) + { + _from->m_grabbing = Grabbing::Nothing; + updateGrabbing(Grabbing::Nothing); + return; + } + + clog(NetNote) << "Hash-chain COMPLETE:" << _from->m_totalDifficulty << "vs" << m_chain.details().totalDifficulty << ";" << _from->m_neededBlocks.size() << " blocks, ends" << _from->m_neededBlocks.back().abridged(); + + if (td < m_chain.details().totalDifficulty || (td == m_chain.details().totalDifficulty && m_chain.currentHash() == _from->m_latestHash)) + { + clog(NetNote) << "Difficulty of hashchain not HIGHER. Ignoring."; + _from->m_grabbing = Grabbing::Nothing; + updateGrabbing(Grabbing::Nothing); + return; + } + + clog(NetNote) << "Difficulty of hashchain HIGHER. Replacing fetch queue [latest now" << _from->m_latestHash.abridged() << ", was" << m_latestBlockSent.abridged() << "]"; + + // Looks like it's the best yet for total difficulty. Set to download. + m_man.resetToChain(_from->m_neededBlocks); + m_latestBlockSent = _from->m_latestHash; + + _from->m_grabbing = Grabbing::Chain; + updateGrabbing(Grabbing::Chain); +} + +void EthereumHost::readyForSync() +{ + // start grabbing next hash chain if there is one. + for (auto j: peers()) + { + j->cap()->tryGrabbingHashChain(); + if (j->cap()->m_grabbing == Grabbing::Hashes) + { + m_grabbing = Grabbing::Hashes; + return; + } + } + clog(NetNote) << "No more peers to sync with."; +} + +void EthereumHost::noteDoneBlocks(EthereumPeer* _who) +{ + if (m_man.isComplete()) + { + // Done our chain-get. + clog(NetNote) << "Chain download complete."; + updateGrabbing(Grabbing::Nothing); + } + if (_who->m_grabbing == Grabbing::Chain) + { + // Done our chain-get. + clog(NetNote) << "Chain download failed. Peer with blocks didn't have them all. This peer is bad and should be punished."; + // TODO: note that peer is BADBADBAD! + updateGrabbing(Grabbing::Nothing); + } +} + +bool EthereumHost::noteBlock(h256 _hash, bytesConstRef _data) +{ + if (!m_chain.details(_hash)) + { + lock_guard l(m_incomingLock); + m_incomingBlocks.push_back(_data.toBytes()); + return true; + } + return false; +} + +void EthereumHost::doWork() +{ + bool netChange = ensureInitialised(m_tq); + auto h = m_chain.currentHash(); + maintainTransactions(m_tq, h); + maintainBlocks(m_bq, h); +// return netChange; + // TODO: Figure out what to do with netChange. + (void)netChange; +} + +void EthereumHost::maintainTransactions(TransactionQueue& _tq, h256 _currentHash) +{ + bool resendAll = (_currentHash != m_latestBlockSent); + { + lock_guard l(m_incomingLock); + for (auto it = m_incomingTransactions.begin(); it != m_incomingTransactions.end(); ++it) + if (_tq.import(&*it)) + {}//ret = true; // just putting a transaction in the queue isn't enough to change the state - it might have an invalid nonce... + else + m_transactionsSent.insert(sha3(*it)); // if we already had the transaction, then don't bother sending it on. + m_incomingTransactions.clear(); + } + + // Send any new transactions. + for (auto const& p: peers()) + { + auto ep = p->cap(); + if (ep) + { + bytes b; + unsigned n = 0; + for (auto const& i: _tq.transactions()) + if ((!m_transactionsSent.count(i.first) && !ep->m_knownTransactions.count(i.first)) || ep->m_requireTransactions || resendAll) + { + b += i.second; + ++n; + m_transactionsSent.insert(i.first); + } + ep->clearKnownTransactions(); + + if (n) + { + RLPStream ts; + EthereumPeer::prep(ts); + ts.appendList(n + 1) << TransactionsPacket; + ts.appendRaw(b, n).swapOut(b); + seal(b); + ep->send(&b); + } + ep->m_requireTransactions = false; + } + } +} + +void EthereumHost::reset() +{ + m_grabbing = Grabbing::Nothing; + + m_man.resetToChain(h256s()); + + m_incomingTransactions.clear(); + m_incomingBlocks.clear(); + + m_latestBlockSent = h256(); + m_transactionsSent.clear(); +} + +void EthereumHost::maintainBlocks(BlockQueue& _bq, h256 _currentHash) +{ + // Import new blocks + { + lock_guard l(m_incomingLock); + for (auto it = m_incomingBlocks.rbegin(); it != m_incomingBlocks.rend(); ++it) + if (_bq.import(&*it, m_chain)) + {} + else{} // TODO: don't forward it. + m_incomingBlocks.clear(); + } + + // If we've finished our initial sync send any new blocks. + if (m_grabbing == Grabbing::Nothing && m_chain.details(m_latestBlockSent) && m_chain.details(m_latestBlockSent).totalDifficulty < m_chain.details(_currentHash).totalDifficulty) + { + RLPStream ts; + EthereumPeer::prep(ts); + bytes bs; + unsigned c = 0; + for (auto h: m_chain.treeRoute(m_latestBlockSent, _currentHash, nullptr, false, true)) + { + bs += m_chain.block(h); + ++c; + } + clog(NetMessageSummary) << "Sending" << c << "new blocks (current is" << _currentHash << ", was" << m_latestBlockSent << ")"; + ts.appendList(1 + c).append(BlocksPacket).appendRaw(bs, c); + bytes b; + ts.swapOut(b); + seal(b); + + for (auto j: peers()) + { + auto p = j->cap(); + Guard l(p->x_knownBlocks); + if (!p->m_knownBlocks.count(_currentHash)) + p->send(&b); + p->m_knownBlocks.clear(); + } + m_latestBlockSent = _currentHash; + } +} diff --git a/libethereum/EthereumHost.h b/libethereum/EthereumHost.h new file mode 100644 index 000000000..07ef92513 --- /dev/null +++ b/libethereum/EthereumHost.h @@ -0,0 +1,130 @@ +/* + 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 . +*/ +/** @file EthereumHost.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "CommonNet.h" +#include "EthereumPeer.h" +#include "DownloadMan.h" + +namespace dev +{ + +class RLPStream; + +namespace eth +{ + +class TransactionQueue; +class BlockQueue; + +/** + * @brief The EthereumHost class + * @warning None of this is thread-safe. You have been warned. + */ +class EthereumHost: public p2p::HostCapability, Worker +{ + friend class EthereumPeer; + +public: + /// Start server, but don't listen. + EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId); + + /// Will block on network process events. + virtual ~EthereumHost(); + + unsigned protocolVersion() const { return c_protocolVersion; } + u256 networkId() const { return m_networkId; } + void setNetworkId(u256 _n) { m_networkId = _n; } + + void reset(); + + DownloadMan const& downloadMan() const { return m_man; } + bool isSyncing() const { return m_grabbing == Grabbing::Chain; } + +private: + void noteHavePeerState(EthereumPeer* _who); + /// Session wants to pass us a block that we might not have. + /// @returns true if we didn't have it. + bool noteBlock(h256 _hash, bytesConstRef _data); + /// Session has finished getting the chain of hashes. + void noteHaveChain(EthereumPeer* _who); + /// Called when the peer can no longer provide us with any needed blocks. + void noteDoneBlocks(EthereumPeer* _who); + + /// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network. + void doWork(); + + /// Called by peer to add incoming transactions. + void addIncomingTransaction(bytes const& _bytes) { std::lock_guard l(m_incomingLock); m_incomingTransactions.push_back(_bytes); } + + void maintainTransactions(TransactionQueue& _tq, h256 _currentBlock); + void maintainBlocks(BlockQueue& _bq, h256 _currentBlock); + + /// Get a bunch of needed blocks. + /// Removes them from our list of needed blocks. + /// @returns empty if there's no more blocks left to fetch, otherwise the blocks to fetch. + h256Set neededBlocks(h256Set const& _exclude); + + /// Check to see if the network peer-state initialisation has happened. + bool isInitialised() const { return m_latestBlockSent; } + + /// Initialises the network peer-state, doing the stuff that needs to be once-only. @returns true if it really was first. + bool ensureInitialised(TransactionQueue& _tq); + + virtual void onStarting() { startWorking(); } + virtual void onStopping() { stopWorking(); } + + void readyForSync(); + void updateGrabbing(Grabbing _g); + + BlockChain const& m_chain; + TransactionQueue& m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain. + BlockQueue& m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported). + + u256 m_networkId; + + Grabbing m_grabbing = Grabbing::Nothing; // TODO: needs to be thread-safe & switch to just having a peer id. + + mutable std::recursive_mutex m_incomingLock; + std::vector m_incomingTransactions; + std::vector m_incomingBlocks; + + DownloadMan m_man; + + h256 m_latestBlockSent; + h256Set m_transactionsSent; +}; + +} +} diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp new file mode 100644 index 000000000..7608afc2a --- /dev/null +++ b/libethereum/EthereumPeer.cpp @@ -0,0 +1,319 @@ +/* + 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 . +*/ +/** @file EthereumPeer.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "EthereumPeer.h" + +#include +#include +#include +#include +#include "BlockChain.h" +#include "EthereumHost.h" +using namespace std; +using namespace dev; +using namespace dev::eth; +using namespace p2p; + +#define clogS(X) dev::LogOutputStream(false) << "| " << std::setw(2) << session()->socketId() << "] " + +EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h): + Capability(_s, _h), + m_sub(host()->m_man) +{ + sendStatus(); +} + +EthereumPeer::~EthereumPeer() +{ + giveUpOnFetch(); +} + +EthereumHost* EthereumPeer::host() const +{ + return static_cast(Capability::hostCapability()); +} + +void EthereumPeer::sendStatus() +{ + RLPStream s; + prep(s); + s.appendList(6) << StatusPacket + << host()->protocolVersion() + << host()->networkId() + << host()->m_chain.details().totalDifficulty + << host()->m_chain.currentHash() + << host()->m_chain.genesisHash(); + sealAndSend(s); +} + +void EthereumPeer::startInitialSync() +{ + // Grab transactions off them. + { + RLPStream s; + prep(s).appendList(1); + s << GetTransactionsPacket; + sealAndSend(s); + } + + host()->noteHavePeerState(this); +} + +void EthereumPeer::tryGrabbingHashChain() +{ + // if already done this, then ignore. + if (m_grabbing != Grabbing::State) + { + clogS(NetAllDetail) << "Already synced with this peer."; + return; + } + + h256 c = host()->m_chain.currentHash(); + unsigned n = host()->m_chain.number(); + u256 td = host()->m_chain.details().totalDifficulty; + + clogS(NetAllDetail) << "Attempt chain-grab? Latest:" << c.abridged() << ", number:" << n << ", TD:" << td << " versus " << m_totalDifficulty; + if (td >= m_totalDifficulty) + { + clogS(NetAllDetail) << "No. Our chain is better."; + m_grabbing = Grabbing::Nothing; + return; // All good - we have the better chain. + } + + // Our chain isn't better - grab theirs. + { + clogS(NetAllDetail) << "Yes. Their chain is better."; + + host()->updateGrabbing(Grabbing::Hashes); + m_grabbing = Grabbing::Hashes; + RLPStream s; + prep(s).appendList(3); + s << GetBlockHashesPacket << m_latestHash << c_maxHashesAsk; + m_neededBlocks = h256s(1, m_latestHash); + sealAndSend(s); + } +} + +void EthereumPeer::giveUpOnFetch() +{ + clogS(NetNote) << "GIVE UP FETCH"; + + // a bit overkill given that the other nodes may yet have the needed blocks, but better to be safe than sorry. + if (m_grabbing == Grabbing::Chain || m_grabbing == Grabbing::ChainHelper) + { + host()->noteDoneBlocks(this); + m_grabbing = Grabbing::Nothing; + } + + m_sub.doneFetch(); + // NOTE: need to notify of giving up on chain-hashes, too, altering state as necessary. +} + +bool EthereumPeer::interpret(RLP const& _r) +{ + switch (_r[0].toInt()) + { + case StatusPacket: + { + m_protocolVersion = _r[1].toInt(); + m_networkId = _r[2].toInt(); + m_totalDifficulty = _r[3].toInt(); + m_latestHash = _r[4].toHash(); + auto genesisHash = _r[5].toHash(); + + clogS(NetMessageSummary) << "Status:" << m_protocolVersion << "/" << m_networkId << "/" << genesisHash.abridged() << ", TD:" << m_totalDifficulty << "=" << m_latestHash.abridged(); + + if (genesisHash != host()->m_chain.genesisHash()) + disable("Invalid genesis hash"); + if (m_protocolVersion != host()->protocolVersion()) + disable("Invalid protocol version."); + if (m_networkId != host()->networkId()) + disable("Invalid network identifier."); + + startInitialSync(); + break; + } + case GetTransactionsPacket: + { + m_requireTransactions = true; + break; + } + case TransactionsPacket: + { + clogS(NetMessageSummary) << "Transactions (" << dec << (_r.itemCount() - 1) << "entries)"; + addRating(_r.itemCount() - 1); + lock_guard l(host()->m_incomingLock); + for (unsigned i = 1; i < _r.itemCount(); ++i) + { + host()->addIncomingTransaction(_r[i].data().toBytes()); + + lock_guard l(x_knownTransactions); + m_knownTransactions.insert(sha3(_r[i].data())); + } + break; + } + case GetBlockHashesPacket: + { + h256 later = _r[1].toHash(); + unsigned limit = _r[2].toInt(); + clogS(NetMessageSummary) << "GetBlockHashes (" << limit << "entries," << later.abridged() << ")"; + + unsigned c = min(max(1, host()->m_chain.number(later)) - 1, limit); + + RLPStream s; + prep(s).appendList(1 + c).append(BlockHashesPacket); + h256 p = host()->m_chain.details(later).parent; + for (unsigned i = 0; i < c; ++i, p = host()->m_chain.details(p).parent) + s << p; + sealAndSend(s); + break; + } + case BlockHashesPacket: + { + clogS(NetMessageSummary) << "BlockHashes (" << dec << (_r.itemCount() - 1) << "entries)" << (_r.itemCount() - 1 ? "" : ": NoMoreHashes"); + + if (m_grabbing != Grabbing::Hashes) + { + cwarn << "Peer giving us hashes when we didn't ask for them."; + break; + } + if (_r.itemCount() == 1) + { + host()->noteHaveChain(this); + return true; + } + for (unsigned i = 1; i < _r.itemCount(); ++i) + { + auto h = _r[i].toHash(); + if (host()->m_chain.details(h)) + { + host()->noteHaveChain(this); + return true; + } + else + m_neededBlocks.push_back(h); + } + // run through - ask for more. + RLPStream s; + prep(s).appendList(3); + s << GetBlockHashesPacket << m_neededBlocks.back() << c_maxHashesAsk; + sealAndSend(s); + break; + } + case GetBlocksPacket: + { + clogS(NetMessageSummary) << "GetBlocks (" << dec << (_r.itemCount() - 1) << "entries)"; + // return the requested blocks. + bytes rlp; + unsigned n = 0; + for (unsigned i = 1; i < _r.itemCount() && i <= c_maxBlocks; ++i) + { + auto b = host()->m_chain.block(_r[i].toHash()); + if (b.size()) + { + rlp += b; + ++n; + } + } + RLPStream s; + sealAndSend(prep(s).appendList(n + 1).append(BlocksPacket).appendRaw(rlp, n)); + break; + } + case BlocksPacket: + { + clogS(NetMessageSummary) << "Blocks (" << dec << (_r.itemCount() - 1) << "entries)" << (_r.itemCount() - 1 ? "" : ": NoMoreBlocks"); + + if (_r.itemCount() == 1) + { + // Couldn't get any from last batch - probably got to this peer's latest block - just give up. + m_sub.doneFetch(); + giveUpOnFetch(); + } + + unsigned used = 0; + for (unsigned i = 1; i < _r.itemCount(); ++i) + { + auto h = BlockInfo::headerHash(_r[i].data()); + m_sub.noteBlock(h); + if (host()->noteBlock(h, _r[i].data())) + used++; + Guard l(x_knownBlocks); + m_knownBlocks.insert(h); + } + addRating(used); + unsigned knownParents = 0; + unsigned unknownParents = 0; + if (g_logVerbosity >= NetMessageSummary::verbosity) + { + for (unsigned i = 1; i < _r.itemCount(); ++i) + { + auto h = BlockInfo::headerHash(_r[i].data()); + BlockInfo bi(_r[i].data()); + Guard l(x_knownBlocks); + if (!host()->m_chain.details(bi.parentHash) && !m_knownBlocks.count(bi.parentHash)) + { + unknownParents++; + clogS(NetAllDetail) << "Unknown parent" << bi.parentHash.abridged() << "of block" << h.abridged(); + } + else + { + knownParents++; + clogS(NetAllDetail) << "Known parent" << bi.parentHash.abridged() << "of block" << h.abridged(); + } + } + } + clogS(NetMessageSummary) << dec << knownParents << "known parents," << unknownParents << "unknown," << used << "used."; + continueGettingChain(); + break; + } + default: + return false; + } + return true; +} + +void EthereumPeer::ensureGettingChain() +{ + if (m_grabbing == Grabbing::ChainHelper) + return; // Already asked & waiting for some. + + continueGettingChain(); +} + +void EthereumPeer::continueGettingChain() +{ + if (m_grabbing != Grabbing::Chain) + m_grabbing = Grabbing::ChainHelper; + + auto blocks = m_sub.nextFetch(c_maxBlocksAsk); + + if (blocks.size()) + { + RLPStream s; + prep(s); + s.appendList(blocks.size() + 1) << GetBlocksPacket; + for (auto const& i: blocks) + s << i; + sealAndSend(s); + } + else + giveUpOnFetch(); +} diff --git a/libethereum/EthereumPeer.h b/libethereum/EthereumPeer.h new file mode 100644 index 000000000..f88a757d8 --- /dev/null +++ b/libethereum/EthereumPeer.h @@ -0,0 +1,95 @@ +/* + 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 . +*/ +/** @file EthereumPeer.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "CommonNet.h" +#include "DownloadMan.h" + +namespace dev +{ +namespace eth +{ + +/** + * @brief The EthereumPeer class + * @todo Document fully. + */ +class EthereumPeer: public p2p::Capability +{ + friend class EthereumHost; + +public: + EthereumPeer(p2p::Session* _s, p2p::HostCapabilityFace* _h); + virtual ~EthereumPeer(); + + static std::string name() { return "eth"; } + + EthereumHost* host() const; + +private: + virtual bool interpret(RLP const& _r); + + void sendStatus(); + void startInitialSync(); + + void tryGrabbingHashChain(); + + /// Ensure that we are waiting for a bunch of blocks from our peer. + void ensureGettingChain(); + /// Ensure that we are waiting for a bunch of blocks from our peer. + void continueGettingChain(); + + void giveUpOnFetch(); + + void clearKnownTransactions() { std::lock_guard l(x_knownTransactions); m_knownTransactions.clear(); } + + unsigned m_protocolVersion; + u256 m_networkId; + + Grabbing m_grabbing = Grabbing::State; + + h256 m_latestHash; ///< Peer's latest block's hash. + u256 m_totalDifficulty; ///< Peer's latest block's total difficulty. + h256s m_neededBlocks; ///< The blocks that we should download from this peer. + + bool m_requireTransactions; + + Mutex x_knownBlocks; + std::set m_knownBlocks; + std::set m_knownTransactions; + std::mutex x_knownTransactions; + + DownloadSub m_sub; +}; + +} +} diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index 1fff722eb..a6b57a05c 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -19,12 +19,14 @@ * @date 2014 */ +#include #include #include "Executive.h" #include "State.h" #include "ExtVM.h" using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; #define ETH_VMTRACE 1 @@ -143,7 +145,7 @@ bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _g OnOpFunc Executive::simpleTrace() { - return [](uint64_t steps, Instruction inst, unsigned newMemSize, bigint gasCost, void* voidVM, void const* voidExt) + return [](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, void* voidVM, void const* voidExt) { ExtVM const& ext = *(ExtVM const*)voidExt; VM& vm = *(VM*)voidVM; @@ -156,8 +158,8 @@ OnOpFunc Executive::simpleTrace() o << " STORAGE" << endl; for (auto const& i: ext.state().storage(ext.myAddress)) o << showbase << hex << i.first << ": " << i.second << endl; - eth::LogOutputStream(true) << o.str(); - eth::LogOutputStream(false) << " | " << dec << ext.level << " | " << ext.myAddress << " | #" << steps << " | " << hex << setw(4) << setfill('0') << vm.curPC() << " : " << c_instructionInfo.at(inst).name << " | " << dec << vm.gas() << " | -" << dec << gasCost << " | " << newMemSize << "x32" << " ]"; + dev::LogOutputStream(true) << o.str(); + dev::LogOutputStream(false) << " | " << dec << ext.level << " | " << ext.myAddress << " | #" << steps << " | " << hex << setw(4) << setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " | " << dec << vm.gas() << " | -" << dec << gasCost << " | " << newMemSize << "x32" << " ]"; }; } @@ -165,6 +167,8 @@ bool Executive::go(OnOpFunc const& _onOp) { if (m_vm) { + boost::timer t; + auto sgas = m_vm->gas(); bool revert = false; try { @@ -183,6 +187,7 @@ bool Executive::go(OnOpFunc const& _onOp) catch (VMException const& _e) { clog(StateChat) << "VM Exception: " << _e.description(); + m_endGas = m_vm->gas(); } catch (Exception const& _e) { @@ -192,6 +197,7 @@ bool Executive::go(OnOpFunc const& _onOp) { clog(StateChat) << "std::exception in VM: " << _e.what(); } + cnote << "VM took:" << t.elapsed() << "; gas used: " << (sgas - m_endGas); // Write state out only in the case of a non-excepted transaction. if (revert) @@ -213,21 +219,19 @@ u256 Executive::gas() const return m_vm ? m_vm->gas() : m_endGas; } -void Executive::finalize() +void Executive::finalize(OnOpFunc const& _onOp) { if (m_t.isCreation() && m_newAddress && m_out.size()) // non-reverted creation - put code in place. m_s.m_cache[m_newAddress].setCode(m_out); + if (m_ext) + m_endGas += m_ext->doPosts(_onOp); + // cnote << "Refunding" << formatBalance(m_endGas * m_ext->gasPrice) << "to origin (=" << m_endGas << "*" << formatBalance(m_ext->gasPrice) << ")"; m_s.addBalance(m_sender, m_endGas * m_t.gasPrice); - u256 gasSpentInEth = (m_t.gas - m_endGas) * m_t.gasPrice; -/* unsigned c_feesKept = 8; - u256 feesEarned = gasSpentInEth - (gasSpentInEth / c_feesKept); - cnote << "Transferring" << (100.0 - 100.0 / c_feesKept) << "% of" << formatBalance(gasSpent) << "=" << formatBalance(feesEarned) << "to miner (" << formatBalance(gasSpentInEth - feesEarned) << "is burnt)."; -*/ - u256 feesEarned = gasSpentInEth; + u256 feesEarned = (m_t.gas - m_endGas) * m_t.gasPrice; // cnote << "Transferring" << formatBalance(gasSpent) << "to miner."; m_s.addBalance(m_s.m_currentBlock.coinbaseAddress, feesEarned); diff --git a/libethereum/Executive.h b/libethereum/Executive.h index 0d3a5288f..82b7df7e9 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -22,13 +22,15 @@ #pragma once #include -#include +#include #include #include #include #include "Transaction.h" #include "Manifest.h" +namespace dev +{ namespace eth { @@ -48,7 +50,7 @@ public: bool create(Address _txSender, u256 _endowment, u256 _gasPrice, u256 _gas, bytesConstRef _code, Address _originAddress); bool call(Address _myAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256 _gas, Address _originAddress); bool go(OnOpFunc const& _onOp = OnOpFunc()); - void finalize(); + void finalize(OnOpFunc const& _onOp = OnOpFunc()); u256 gasUsed() const; static OnOpFunc simpleTrace(); @@ -78,3 +80,4 @@ private: }; } +} diff --git a/libethereum/ExtVM.h b/libethereum/ExtVM.h index b980d6f01..b4db1975e 100644 --- a/libethereum/ExtVM.h +++ b/libethereum/ExtVM.h @@ -27,6 +27,8 @@ #include #include "State.h" +namespace dev +{ namespace eth { @@ -49,6 +51,9 @@ public: /// Write a value in storage. void setStore(u256 _n, u256 _v) { m_s.setStorage(myAddress, _n, _v); if (m_ms) m_ms->altered.push_back(_n); } + /// Read address's code. + bytes const& codeAt(Address _a) { return m_s.code(_a); } + /// Create a new contract. h160 create(u256 _endowment, u256* _gas, bytesConstRef _code, OnOpFunc const& _onOp = OnOpFunc()) { @@ -56,18 +61,18 @@ public: m_s.noteSending(myAddress); if (m_ms) m_ms->internal.resize(m_ms->internal.size() + 1); - auto ret = m_s.create(myAddress, _endowment, gasPrice, _gas, _code, origin, &suicides, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, level + 1); + auto ret = m_s.create(myAddress, _endowment, gasPrice, _gas, _code, origin, &suicides, &posts, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, level + 1); if (m_ms && !m_ms->internal.back().from) m_ms->internal.pop_back(); return ret; } - /// Create a new message call. - bool call(Address _receiveAddress, u256 _txValue, bytesConstRef _txData, u256* _gas, bytesRef _out, OnOpFunc const& _onOp = OnOpFunc()) + /// Create a new message call. Leave _myAddressOverride as the default to use the present address as caller. + bool call(Address _receiveAddress, u256 _txValue, bytesConstRef _txData, u256* _gas, bytesRef _out, OnOpFunc const& _onOp = OnOpFunc(), Address _myAddressOverride = Address(), Address _codeAddressOverride = Address()) { if (m_ms) m_ms->internal.resize(m_ms->internal.size() + 1); - auto ret = m_s.call(_receiveAddress, myAddress, _txValue, gasPrice, _txData, _gas, _out, origin, &suicides, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, level + 1); + auto ret = m_s.call(_receiveAddress, _codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _txValue, gasPrice, _txData, _gas, _out, origin, &suicides, &posts, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, level + 1); if (m_ms && !m_ms->internal.back().from) m_ms->internal.pop_back(); return ret; @@ -93,6 +98,20 @@ public: /// @TODO check call site for the parent manifest being discarded. void revert() { if (m_ms) *m_ms = Manifest(); m_s.m_cache = m_origCache; } + /// Execute any posts we have left. + u256 doPosts(OnOpFunc const& _onOp = OnOpFunc()) + { + u256 ret; + while (posts.size()) + { + Post& p = posts.front(); + call(p.to, p.value, &p.data, &p.gas, bytesRef(), _onOp, p.from); + ret += p.gas; + posts.pop_front(); + } + return ret; + } + State& state() const { return m_s; } /// @note not a part of the main API; just for use by tracing/debug stuff. @@ -105,5 +124,5 @@ private: }; } - +} diff --git a/libethereum/Interface.cpp b/libethereum/Interface.cpp new file mode 100644 index 000000000..7d731bb1f --- /dev/null +++ b/libethereum/Interface.cpp @@ -0,0 +1,25 @@ +/* + 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 . +*/ +/** @file Interface.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "Interface.h" + +#pragma GCC diagnostic ignored "-Wunused-variable" +namespace { char dummy; }; diff --git a/libethereum/Interface.h b/libethereum/Interface.h new file mode 100644 index 000000000..a5df3e5b1 --- /dev/null +++ b/libethereum/Interface.h @@ -0,0 +1,195 @@ +/* + 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 . +*/ +/** @file Interface.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include "MessageFilter.h" +#include "Transaction.h" +#include "AccountDiff.h" +#include "Miner.h" + +namespace dev +{ +namespace eth +{ + +/** + * @brief Main API hub for interfacing with Ethereum. + */ +class Interface +{ +public: + /// Constructor. + Interface() {} + + /// Destructor. + virtual ~Interface() {} + + // [TRANSACTION API] + + /// Submits the given message-call transaction. + virtual void transact(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo) = 0; + + /// Submits a new contract-creation transaction. + /// @returns the new contract's address (assuming it all goes through). + virtual Address transact(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas = 10000, u256 _gasPrice = 10 * szabo) = 0; + + /// Injects the RLP-encoded transaction given by the _rlp into the transaction queue directly. + virtual void inject(bytesConstRef _rlp) = 0; + + /// Blocks until all pending transactions have been processed. + virtual void flushTransactions() = 0; + + /// Makes the given call. Nothing is recorded into the state. + virtual bytes call(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo) = 0; + + // [STATE-QUERY API] + + int getDefault() const { return m_default; } + void setDefault(int _block) { m_default = _block; } + + u256 balanceAt(Address _a) const { return balanceAt(_a, m_default); } + u256 countAt(Address _a) const { return countAt(_a, m_default); } + u256 stateAt(Address _a, u256 _l) const { return stateAt(_a, _l, m_default); } + bytes codeAt(Address _a) const { return codeAt(_a, m_default); } + std::map storageAt(Address _a) const { return storageAt(_a, m_default); } + + virtual u256 balanceAt(Address _a, int _block) const = 0; + virtual u256 countAt(Address _a, int _block) const = 0; + virtual u256 stateAt(Address _a, u256 _l, int _block) const = 0; + virtual bytes codeAt(Address _a, int _block) const = 0; + virtual std::map storageAt(Address _a, int _block) const = 0; + + // [MESSAGE API] + + virtual PastMessages messages(unsigned _watchId) const = 0; + virtual PastMessages messages(MessageFilter const& _filter) const = 0; + + /// Install, uninstall and query watches. + virtual unsigned installWatch(MessageFilter const& _filter) = 0; + virtual unsigned installWatch(h256 _filterId) = 0; + virtual void uninstallWatch(unsigned _watchId) = 0; + virtual bool peekWatch(unsigned _watchId) const = 0; + virtual bool checkWatch(unsigned _watchId) = 0; + + // TODO: Block query API. + + // [EXTRA API]: + + /// @returns The height of the chain. + virtual unsigned number() const = 0; + + /// Get a map containing each of the pending transactions. + /// @TODO: Remove in favour of transactions(). + virtual Transactions pending() const = 0; + + /// Differences between transactions. + StateDiff diff(unsigned _txi) const { return diff(_txi, m_default); } + virtual StateDiff diff(unsigned _txi, h256 _block) const = 0; + virtual StateDiff diff(unsigned _txi, int _block) const = 0; + + /// Get a list of all active addresses. + virtual Addresses addresses() const { return addresses(m_default); } + virtual Addresses addresses(int _block) const = 0; + + /// Get the fee associated for a transaction with the given data. + static u256 txGas(unsigned _dataCount, u256 _gas = 0) { return c_txDataGas * _dataCount + c_txGas + _gas; } + + /// Get the remaining gas limit in this block. + virtual u256 gasLimitRemaining() const = 0; + + // [MINING API]: + + /// Set the coinbase address. + virtual void setAddress(Address _us) = 0; + /// Get the coinbase address. + virtual Address address() const = 0; + + /// Stops mining and sets the number of mining threads (0 for automatic). + virtual void setMiningThreads(unsigned _threads = 0) = 0; + /// Get the effective number of mining threads. + virtual unsigned miningThreads() const = 0; + + /// Start mining. + /// NOT thread-safe - call it & stopMining only from a single thread + virtual void startMining() = 0; + /// Stop mining. + /// NOT thread-safe + virtual void stopMining() = 0; + /// Are we mining now? + virtual bool isMining() = 0; + + /// Check the progress of the mining. + virtual MineProgress miningProgress() const = 0; + +protected: + int m_default = -1; +}; + +class Watch; + +} +} + +namespace std { void swap(dev::eth::Watch& _a, dev::eth::Watch& _b); } + +namespace dev +{ +namespace eth +{ + +class Watch: public boost::noncopyable +{ + friend void std::swap(Watch& _a, Watch& _b); + +public: + Watch() {} + Watch(Interface& _c, h256 _f): m_c(&_c), m_id(_c.installWatch(_f)) {} + Watch(Interface& _c, MessageFilter const& _tf): m_c(&_c), m_id(_c.installWatch(_tf)) {} + ~Watch() { if (m_c) m_c->uninstallWatch(m_id); } + + bool check() { return m_c ? m_c->checkWatch(m_id) : false; } + bool peek() { return m_c ? m_c->peekWatch(m_id) : false; } + PastMessages messages() const { return m_c->messages(m_id); } + +private: + Interface* m_c = nullptr; + unsigned m_id = 0; +}; + +} +} + +namespace std +{ + +inline void swap(dev::eth::Watch& _a, dev::eth::Watch& _b) +{ + swap(_a.m_c, _b.m_c); + swap(_a.m_id, _b.m_id); +} + +} diff --git a/libethereum/Manifest.cpp b/libethereum/Manifest.cpp index d5d7e2df9..2d1e4aa6e 100644 --- a/libethereum/Manifest.cpp +++ b/libethereum/Manifest.cpp @@ -21,7 +21,8 @@ #include "Manifest.h" using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; Manifest::Manifest(bytesConstRef _r) { diff --git a/libethereum/Manifest.h b/libethereum/Manifest.h index 94ecd1496..12c40b3a9 100644 --- a/libethereum/Manifest.h +++ b/libethereum/Manifest.h @@ -21,9 +21,13 @@ #pragma once -#include +#include +#include +#include #include +namespace dev +{ namespace eth { @@ -41,6 +45,21 @@ struct Manifest h256 bloom() const { h256 ret = from.bloom() | to.bloom(); for (auto const& i: internal) ret |= i.bloom(); for (auto const& i: altered) ret |= h256(i).bloom(); return ret; } + std::string toString(unsigned _indent = 0) const + { + std::ostringstream oss; + oss << std::string(_indent * 3, ' ') << from << " -> " << to << " [" << value << "]: {"; + if (internal.size()) + { + oss << std::endl; + for (auto const& m: internal) + oss << m.toString(_indent + 1) << std::endl; + oss << std::string(_indent * 3, ' '); + } + oss << "} I:" << toHex(input) << "; O:" << toHex(output); + return oss.str(); + } + Address from; Address to; u256 value; @@ -51,3 +70,4 @@ struct Manifest }; } +} diff --git a/libethereum/MessageFilter.cpp b/libethereum/MessageFilter.cpp new file mode 100644 index 000000000..d88bc8a9b --- /dev/null +++ b/libethereum/MessageFilter.cpp @@ -0,0 +1,151 @@ +/* + 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 . +*/ +/** @file MessageFilter.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "MessageFilter.h" + +#include +#include "State.h" +using namespace std; +using namespace dev; +using namespace dev::eth; + +void MessageFilter::fillStream(RLPStream& _s) const +{ + _s.appendList(8) << m_from << m_to << m_stateAltered << m_altered << m_earliest << m_latest << m_max << m_skip; +} + +h256 MessageFilter::sha3() const +{ + RLPStream s; + fillStream(s); + return dev::eth::sha3(s.out()); +} + +bool MessageFilter::matches(h256 _bloom) const +{ + auto have = [=](Address const& a) { return _bloom.contains(a.bloom()); }; + if (m_from.size()) + { + for (auto i: m_from) + if (have(i)) + goto OK1; + return false; + } + OK1: + if (m_to.size()) + { + for (auto i: m_to) + if (have(i)) + goto OK2; + return false; + } + OK2: + if (m_stateAltered.size() || m_altered.size()) + { + for (auto i: m_altered) + if (have(i)) + goto OK3; + for (auto i: m_stateAltered) + if (have(i.first) && _bloom.contains(h256(i.second).bloom())) + goto OK3; + return false; + } + OK3: + return true; +} + +bool MessageFilter::matches(State const& _s, unsigned _i) const +{ + h256 b = _s.changesFromPending(_i).bloom(); + if (!matches(b)) + return false; + + Transaction t = _s.pending()[_i]; + if (!m_to.empty() && !m_to.count(t.receiveAddress)) + return false; + if (!m_from.empty() && !m_from.count(t.sender())) + return false; + if (m_stateAltered.empty() && m_altered.empty()) + return true; + StateDiff d = _s.pendingDiff(_i); + if (!m_altered.empty()) + { + for (auto const& s: m_altered) + if (d.accounts.count(s)) + return true; + return false; + } + if (!m_stateAltered.empty()) + { + for (auto const& s: m_stateAltered) + if (d.accounts.count(s.first) && d.accounts.at(s.first).storage.count(s.second)) + return true; + return false; + } + return true; +} + +PastMessages MessageFilter::matches(Manifest const& _m, unsigned _i) const +{ + PastMessages ret; + matches(_m, vector(1, _i), _m.from, PastMessages(), ret); + return ret; +} + +bool MessageFilter::matches(Manifest const& _m, vector _p, Address _o, PastMessages _limbo, PastMessages& o_ret) const +{ + bool ret; + + if ((m_from.empty() || m_from.count(_m.from)) && (m_to.empty() || m_to.count(_m.to))) + _limbo.push_back(PastMessage(_m, _p, _o)); + + // Handle limbos, by checking against all addresses in alteration. + bool alters = m_altered.empty() && m_stateAltered.empty(); + alters = alters || m_altered.count(_m.from) || m_altered.count(_m.to); + + if (!alters) + for (auto const& i: _m.altered) + if (m_altered.count(_m.to) || m_stateAltered.count(make_pair(_m.to, i))) + { + alters = true; + break; + } + // If we do alter stuff, + if (alters) + { + o_ret += _limbo; + _limbo.clear(); + ret = true; + } + + _p.push_back(0); + for (auto const& m: _m.internal) + { + if (matches(m, _p, _o, _limbo, o_ret)) + { + _limbo.clear(); + ret = true; + } + _p.back()++; + } + + return ret; +} diff --git a/libethereum/MessageFilter.h b/libethereum/MessageFilter.h new file mode 100644 index 000000000..c9fb4e51c --- /dev/null +++ b/libethereum/MessageFilter.h @@ -0,0 +1,76 @@ +/* + 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 . +*/ +/** @file MessageFilter.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include +#include "PastMessage.h" + +namespace dev +{ +namespace eth +{ + +struct Manifest; +class State; + +class MessageFilter +{ +public: + MessageFilter(int _earliest = 0, int _latest = -1, unsigned _max = 10, unsigned _skip = 0): m_earliest(_earliest), m_latest(_latest), m_max(_max), m_skip(_skip) {} + + void fillStream(RLPStream& _s) const; + h256 sha3() const; + + int earliest() const { return m_earliest; } + int latest() const { return m_latest; } + unsigned max() const { return m_max; } + unsigned skip() const { return m_skip; } + bool matches(h256 _bloom) const; + bool matches(State const& _s, unsigned _i) const; + PastMessages matches(Manifest const& _m, unsigned _i) const; + + MessageFilter from(Address _a) { m_from.insert(_a); return *this; } + MessageFilter to(Address _a) { m_to.insert(_a); return *this; } + MessageFilter altered(Address _a, u256 _l) { m_stateAltered.insert(std::make_pair(_a, _l)); return *this; } + MessageFilter altered(Address _a) { m_altered.insert(_a); return *this; } + MessageFilter withMax(unsigned _m) { m_max = _m; return *this; } + MessageFilter withSkip(unsigned _m) { m_skip = _m; return *this; } + MessageFilter withEarliest(int _e) { m_earliest = _e; return *this; } + MessageFilter withLatest(int _e) { m_latest = _e; return *this; } + +private: + bool matches(Manifest const& _m, std::vector _p, Address _o, PastMessages _limbo, PastMessages& o_ret) const; + + std::set
m_from; + std::set
m_to; + std::set> m_stateAltered; + std::set
m_altered; + int m_earliest = 0; + int m_latest = -1; + unsigned m_max; + unsigned m_skip; +}; + +} +} diff --git a/libethereum/Miner.cpp b/libethereum/Miner.cpp new file mode 100644 index 000000000..9b651e2c5 --- /dev/null +++ b/libethereum/Miner.cpp @@ -0,0 +1,84 @@ +/* + 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 . + */ +/** @file Miner.cpp + * @author Alex Leverington + * @author Gav Wood + * @date 2014 + */ + +#include "Miner.h" +#include "State.h" +using namespace std; +using namespace dev; +using namespace dev::eth; + +Miner::Miner(MinerHost* _host, unsigned _id): + Worker("miner-" + toString(_id)), + m_host(_host) +{ +} + +void Miner::doWork() +{ + // Do some mining. + if (m_miningStatus != Waiting && m_miningStatus != Mined) + { + if (m_miningStatus == Preparing) + { + m_host->setupState(m_mineState); + if (m_host->force() || m_mineState.pending().size()) + m_miningStatus = Mining; + else + m_miningStatus = Waiting; + + { + Guard l(x_mineInfo); + m_mineProgress.best = (double)-1; + m_mineProgress.hashes = 0; + m_mineProgress.ms = 0; + } + } + + if (m_miningStatus == Mining) + { + // Mine for a while. + MineInfo mineInfo = m_mineState.mine(100, m_host->turbo()); + + { + Guard l(x_mineInfo); + m_mineProgress.best = min(m_mineProgress.best, mineInfo.best); + m_mineProgress.current = mineInfo.best; + m_mineProgress.requirement = mineInfo.requirement; + m_mineProgress.ms += 100; + m_mineProgress.hashes += mineInfo.hashes; + m_mineHistory.push_back(mineInfo); + } + if (mineInfo.completed) + { + m_mineState.completeMine(); + m_host->onComplete(); + m_miningStatus = Mined; + } + else + m_host->onProgressed(); + } + } + else + { + this_thread::sleep_for(chrono::milliseconds(100)); + } +} diff --git a/libethereum/Miner.h b/libethereum/Miner.h new file mode 100644 index 000000000..763249b7d --- /dev/null +++ b/libethereum/Miner.h @@ -0,0 +1,139 @@ +/* + 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 . + */ +/** @file Miner.h + * @author Alex Leverington + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include "State.h" + +namespace dev +{ +namespace eth +{ + +/** + * @brief Describes the progress of a mining operation. + */ +struct MineProgress +{ + void combine(MineProgress const& _m) { requirement = std::max(requirement, _m.requirement); best = std::min(best, _m.best); current = std::max(current, _m.current); hashes += _m.hashes; ms = std::max(ms, _m.ms); } + double requirement = 0; ///< The PoW requirement - as the second logarithm of the minimum acceptable hash. + double best = 1e99; ///< The PoW achievement - as the second logarithm of the minimum found hash. + double current = 0; ///< The most recent PoW achievement - as the second logarithm of the presently found hash. + unsigned hashes = 0; ///< Total number of hashes computed. + unsigned ms = 0; ///< Total number of milliseconds of mining thus far. +}; + +/** + * @brief Class for hosting one or more Miners. + * @warning Must be implemented in a threadsafe manner since it will be called from multiple + * miner threads. + */ +class MinerHost +{ +public: + virtual void setupState(State& _s) = 0; ///< Reset the given State object to the one that should be being mined. + virtual void onProgressed() {} ///< Called once some progress has been made. + virtual void onComplete() {} ///< Called once a block is found. + virtual bool turbo() const = 0; ///< @returns true iff the Miner should mine as fast as possible. + virtual bool force() const = 0; ///< @returns true iff the Miner should mine regardless of the number of transactions. +}; + +/** + * @brief Implements Miner. + * To begin mining, use start() & stop(). noteStateChange() can be used to reset the mining and set up the + * State object according to the host. Use isRunning() to determine if the miner has been start()ed. + * Use isComplete() to determine if the miner has finished mining. + * + * blockData() can be used to retrieve the complete block, ready for insertion into the BlockChain. + * + * Information on the mining can be queried through miningProgress() and miningHistory(). + * @threadsafe + * @todo Signal Miner to restart once with condition variables. + */ +class Miner: Worker +{ +public: + /// Null constructor. + Miner(): m_host(nullptr) {} + + /// Constructor. + Miner(MinerHost* _host, unsigned _id = 0); + + /// Move-constructor. + Miner(Miner&& _m): Worker((Worker&&)_m) { std::swap(m_host, _m.m_host); } + + /// Move-assignment. + Miner& operator=(Miner&& _m) { Worker::operator=((Worker&&)_m); std::swap(m_host, _m.m_host); return *this; } + + /// Destructor. Stops miner. + ~Miner() { stop(); } + + /// Setup its basics. + void setup(MinerHost* _host, unsigned _id = 0) { m_host = _host; setName("miner-" + toString(_id)); } + + /// Start mining. + void start() { startWorking(); } + + /// Stop mining. + void stop() { stopWorking(); } + + /// Call to notify Miner of a state change. + void noteStateChange() { m_miningStatus = Preparing; } + + /// @returns true iff the mining has been start()ed. It may still not be actually mining, depending on the host's turbo() & force(). + bool isRunning() { return isWorking(); } + + /// @returns true if mining is complete. + bool isComplete() const { return m_miningStatus == Mined; } + + /// @returns the internal State object. + bytes const& blockData() { return m_mineState.blockData(); } + + /// Check the progress of the mining. + MineProgress miningProgress() const { Guard l(x_mineInfo); return m_mineProgress; } + + /// Get and clear the mining history. + std::list miningHistory() { Guard l(x_mineInfo); auto ret = m_mineHistory; m_mineHistory.clear(); return ret; } + +private: + /// Do some work on the mining. + virtual void doWork(); + + MinerHost* m_host = nullptr; ///< Our host. + + enum MiningStatus { Waiting, Preparing, Mining, Mined, Stopping, Stopped }; + MiningStatus m_miningStatus = Waiting; ///< TODO: consider mutex/atomic variable. + State m_mineState; ///< The state on which we are mining, generally equivalent to m_postMine. + + mutable std::mutex x_mineInfo; ///< Lock for the mining progress & history. + MineProgress m_mineProgress; ///< What's our progress? + std::list m_mineHistory; ///< What the history of our mining? +}; + +} +} diff --git a/libethereum/PastMessage.cpp b/libethereum/PastMessage.cpp new file mode 100644 index 000000000..d81ae672c --- /dev/null +++ b/libethereum/PastMessage.cpp @@ -0,0 +1,28 @@ +/* + 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 . +*/ +/** @file PastMessage.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "PastMessage.h" +using namespace std; +using namespace dev; +using namespace dev::eth; + +#pragma GCC diagnostic ignored "-Wunused-variable" +namespace { char dummy; }; diff --git a/libethereum/PastMessage.h b/libethereum/PastMessage.h new file mode 100644 index 000000000..6446eff5a --- /dev/null +++ b/libethereum/PastMessage.h @@ -0,0 +1,56 @@ +/* + 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 . +*/ +/** @file PastMessage.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include "Manifest.h" + +namespace dev +{ +namespace eth +{ + +struct PastMessage +{ + PastMessage(Manifest const& _m, std::vector _path, Address _o): to(_m.to), from(_m.from), value(_m.value), input(_m.input), output(_m.output), path(_path), origin(_o) {} + + PastMessage& polish(h256 _b, u256 _ts, unsigned _n, Address _coinbase) { block = _b; timestamp = _ts; number = _n; coinbase = _coinbase; return *this; } + + Address to; ///< The receiving address of the transaction. Address() in the case of a creation. + Address from; ///< The receiving address of the transaction. Address() in the case of a creation. + u256 value; ///< The value associated with the call. + bytes input; ///< The data associated with the message, or the initialiser if it's a creation transaction. + bytes output; ///< The data returned by the message, or the body code if it's a creation transaction. + + std::vector path; ///< Call path into the block transaction. size() is always > 0. First item is the transaction index in the block. + Address origin; ///< Originating sender of the transaction. + Address coinbase; ///< Block coinbase. + h256 block; ///< Block hash. + u256 timestamp; ///< Block timestamp. + unsigned number; ///< Block number. +}; + +typedef std::vector PastMessages; + +} +} diff --git a/libethereum/PeerServer.cpp b/libethereum/PeerServer.cpp deleted file mode 100644 index 99e4ad0b0..000000000 --- a/libethereum/PeerServer.cpp +++ /dev/null @@ -1,570 +0,0 @@ -/* - 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 . -*/ -/** @file PeerNetwork.cpp - * @authors: - * Gav Wood - * Eric Lombrozo - * @date 2014 - */ - -#include "PeerServer.h" - -#include -#ifdef _WIN32 -// winsock is already included -// #include -#else -#include -#endif - -#include -#include -#include -#include -#include -#include -#include "BlockChain.h" -#include "TransactionQueue.h" -#include "PeerSession.h" -using namespace std; -using namespace eth; - -// Addresses we will skip during network interface discovery -// Use a vector as the list is small -// Why this and not names? -// Under MacOSX loopback (127.0.0.1) can be named lo0 and br0 are bridges (0.0.0.0) -static const set c_rejectAddresses = { - {bi::address_v4::from_string("127.0.0.1")}, - {bi::address_v6::from_string("::1")}, - {bi::address_v4::from_string("0.0.0.0")}, - {bi::address_v6::from_string("::")} -}; - -PeerServer::PeerServer(std::string const& _clientVersion, BlockChain const& _ch, unsigned int _networkId, unsigned short _port, NodeMode _m, string const& _publicAddress, bool _upnp): - m_clientVersion(_clientVersion), - m_mode(_m), - m_listenPort(_port), - m_chain(&_ch), - m_acceptor(m_ioService, bi::tcp::endpoint(bi::tcp::v4(), _port)), - m_socket(m_ioService), - m_key(KeyPair::create()), - m_networkId(_networkId) -{ - populateAddresses(); - determinePublic(_publicAddress, _upnp); - ensureAccepting(); - clog(NetNote) << "Id:" << toHex(m_key.address().ref().cropped(0, 4)) << "Mode: " << (_m == NodeMode::PeerServer ? "PeerServer" : "Full"); -} - -PeerServer::PeerServer(std::string const& _clientVersion, BlockChain const& _ch, unsigned int _networkId, NodeMode _m, string const& _publicAddress, bool _upnp): - m_clientVersion(_clientVersion), - m_mode(_m), - m_listenPort(0), - m_chain(&_ch), - m_acceptor(m_ioService, bi::tcp::endpoint(bi::tcp::v4(), 0)), - m_socket(m_ioService), - m_key(KeyPair::create()), - m_networkId(_networkId) -{ - m_listenPort = m_acceptor.local_endpoint().port(); - - // populate addresses. - populateAddresses(); - determinePublic(_publicAddress, _upnp); - ensureAccepting(); - clog(NetNote) << "Id:" << toHex(m_key.address().ref().cropped(0, 4)) << "Mode: " << (m_mode == NodeMode::PeerServer ? "PeerServer" : "Full"); -} - -PeerServer::PeerServer(std::string const& _clientVersion, BlockChain const& _ch, unsigned int _networkId, NodeMode _m): - m_clientVersion(_clientVersion), - m_mode(_m), - m_listenPort(0), - m_chain(&_ch), - m_acceptor(m_ioService, bi::tcp::endpoint(bi::tcp::v4(), 0)), - m_socket(m_ioService), - m_key(KeyPair::create()), - m_networkId(_networkId) -{ - // populate addresses. - populateAddresses(); - clog(NetNote) << "Id:" << toHex(m_key.address().ref().cropped(0, 4)) << "Mode: " << (m_mode == NodeMode::PeerServer ? "PeerServer" : "Full"); -} - -PeerServer::~PeerServer() -{ - for (auto const& i: m_peers) - if (auto p = i.second.lock()) - p->disconnect(ClientQuit); - delete m_upnp; -} - -unsigned PeerServer::protocolVersion() -{ - return c_protocolVersion; -} - -void PeerServer::seal(bytes& _b) -{ - _b[0] = 0x22; - _b[1] = 0x40; - _b[2] = 0x08; - _b[3] = 0x91; - uint32_t len = (uint32_t)_b.size() - 8; - _b[4] = (len >> 24) & 0xff; - _b[5] = (len >> 16) & 0xff; - _b[6] = (len >> 8) & 0xff; - _b[7] = len & 0xff; -} - -void PeerServer::determinePublic(string const& _publicAddress, bool _upnp) -{ - if (_upnp) - try - { - m_upnp = new UPnP; - } - catch (NoUPnPDevice) {} // let m_upnp continue as null - we handle it properly. - - bi::tcp::resolver r(m_ioService); - if (m_upnp && m_upnp->isValid() && m_peerAddresses.size()) - { - clog(NetNote) << "External addr: " << m_upnp->externalIP(); - int p = m_upnp->addRedirect(m_peerAddresses[0].to_string().c_str(), m_listenPort); - if (p) - clog(NetNote) << "Punched through NAT and mapped local port" << m_listenPort << "onto external port" << p << "."; - else - { - // couldn't map - clog(NetWarn) << "Couldn't punch through NAT (or no NAT in place). Assuming " << m_listenPort << " is local & external port."; - p = m_listenPort; - } - - auto eip = m_upnp->externalIP(); - if (eip == string("0.0.0.0") && _publicAddress.empty()) - m_public = bi::tcp::endpoint(bi::address(), (unsigned short)p); - else - { - m_public = bi::tcp::endpoint(bi::address::from_string(_publicAddress.empty() ? eip : _publicAddress), (unsigned short)p); - m_addresses.push_back(m_public.address().to_v4()); - } - } - else - { - // No UPnP - fallback on given public address or, if empty, the assumed peer address. - m_public = bi::tcp::endpoint(_publicAddress.size() ? bi::address::from_string(_publicAddress) - : m_peerAddresses.size() ? m_peerAddresses[0] - : bi::address(), m_listenPort); - m_addresses.push_back(m_public.address().to_v4()); - } -} - -void PeerServer::populateAddresses() -{ -#ifdef _WIN32 - WSAData wsaData; - if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) - throw NoNetworking(); - - char ac[80]; - if (gethostname(ac, sizeof(ac)) == SOCKET_ERROR) - { - clog(NetWarn) << "Error " << WSAGetLastError() << " when getting local host name."; - WSACleanup(); - throw NoNetworking(); - } - - struct hostent* phe = gethostbyname(ac); - if (phe == 0) - { - clog(NetWarn) << "Bad host lookup."; - WSACleanup(); - throw NoNetworking(); - } - - for (int i = 0; phe->h_addr_list[i] != 0; ++i) - { - struct in_addr addr; - memcpy(&addr, phe->h_addr_list[i], sizeof(struct in_addr)); - char *addrStr = inet_ntoa(addr); - bi::address ad(bi::address::from_string(addrStr)); - m_addresses.push_back(ad.to_v4()); - bool isLocal = std::find(c_rejectAddresses.begin(), c_rejectAddresses.end(), ad) != c_rejectAddresses.end(); - if (!isLocal) - m_peerAddresses.push_back(ad.to_v4()); - clog(NetNote) << "Address: " << ac << " = " << m_addresses.back() << (isLocal ? " [LOCAL]" : " [PEER]"); - } - - WSACleanup(); -#else - ifaddrs* ifaddr; - if (getifaddrs(&ifaddr) == -1) - throw NoNetworking(); - - bi::tcp::resolver r(m_ioService); - - for (ifaddrs* ifa = ifaddr; ifa; ifa = ifa->ifa_next) - { - if (!ifa->ifa_addr) - continue; - if (ifa->ifa_addr->sa_family == AF_INET) - { - char host[NI_MAXHOST]; - if (getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST)) - continue; - try - { - auto it = r.resolve({host, "30303"}); - bi::tcp::endpoint ep = it->endpoint(); - bi::address ad = ep.address(); - m_addresses.push_back(ad.to_v4()); - bool isLocal = std::find(c_rejectAddresses.begin(), c_rejectAddresses.end(), ad) != c_rejectAddresses.end(); - if (!isLocal) - m_peerAddresses.push_back(ad.to_v4()); - clog(NetNote) << "Address: " << host << " = " << m_addresses.back() << (isLocal ? " [LOCAL]" : " [PEER]"); - } - catch (...) - { - clog(NetNote) << "Couldn't resolve: " << host; - } - } - } - - freeifaddrs(ifaddr); -#endif -} - -std::map PeerServer::potentialPeers() -{ - std::map ret; - if (!m_public.address().is_unspecified()) - ret.insert(make_pair(m_key.pub(), m_public)); - for (auto i: m_peers) - if (auto j = i.second.lock()) - { - auto ep = j->endpoint(); - // Skip peers with a listen port of zero or are on a private network - bool peerOnNet = (j->m_listenPort != 0 && !isPrivateAddress(ep.address())); - if (peerOnNet && ep.port() && j->m_id) - ret.insert(make_pair(i.first, ep)); - } - return ret; -} - -void PeerServer::ensureAccepting() -{ - if (m_accepting == false) - { - clog(NetNote) << "Listening on local port " << m_listenPort << " (public: " << m_public << ")"; - m_accepting = true; - m_acceptor.async_accept(m_socket, [=](boost::system::error_code ec) - { - if (!ec) - try - { - try { - clog(NetNote) << "Accepted connection from " << m_socket.remote_endpoint(); - } catch (...){} - bi::address remoteAddress = m_socket.remote_endpoint().address(); - // Port defaults to 0 - we let the hello tell us which port the peer listens to - auto p = std::make_shared(this, std::move(m_socket), m_networkId, remoteAddress); - p->start(); - } - catch (std::exception const& _e) - { - clog(NetWarn) << "ERROR: " << _e.what(); - } - m_accepting = false; - if (ec.value() != 1 && (m_mode == NodeMode::PeerServer || m_peers.size() < m_idealPeerCount * 2)) - ensureAccepting(); - }); - } -} - -void PeerServer::connect(std::string const& _addr, unsigned short _port) noexcept -{ - try - { - connect(bi::tcp::endpoint(bi::address::from_string(_addr), _port)); - } - catch (exception const& e) - { - // Couldn't connect - clog(NetNote) << "Bad host " << _addr << " (" << e.what() << ")"; - } -} - -void PeerServer::connect(bi::tcp::endpoint const& _ep) -{ - clog(NetNote) << "Attempting connection to " << _ep; - bi::tcp::socket* s = new bi::tcp::socket(m_ioService); - s->async_connect(_ep, [=](boost::system::error_code const& ec) - { - if (ec) - { - clog(NetNote) << "Connection refused to " << _ep << " (" << ec.message() << ")"; - for (auto i = m_incomingPeers.begin(); i != m_incomingPeers.end(); ++i) - if (i->second.first == _ep && i->second.second < 3) - { - m_freePeers.push_back(i->first); - goto OK; - } - // for-else - clog(NetNote) << "Giving up."; - OK:; - } - else - { - auto p = make_shared(this, std::move(*s), m_networkId, _ep.address(), _ep.port()); - clog(NetNote) << "Connected to " << _ep; - p->start(); - } - delete s; - }); -} - -bool PeerServer::ensureInitialised(BlockChain& _bc, TransactionQueue& _tq) -{ - if (m_latestBlockSent == h256()) - { - // First time - just initialise. - m_latestBlockSent = _bc.currentHash(); - clog(NetNote) << "Initialising: latest=" << m_latestBlockSent; - - for (auto const& i: _tq.transactions()) - m_transactionsSent.insert(i.first); - m_lastPeersRequest = chrono::steady_clock::time_point::min(); - return true; - } - return false; -} - -bool PeerServer::noteBlock(h256 _hash, bytesConstRef _data) -{ - if (!m_chain->details(_hash)) - { - lock_guard l(m_incomingLock); - m_incomingBlocks.push_back(_data.toBytes()); - return true; - } - return false; -} - -bool PeerServer::sync(BlockChain& _bc, TransactionQueue& _tq, OverlayDB& _o) -{ - bool ret = ensureInitialised(_bc, _tq); - - if (m_mode == NodeMode::Full) - { - for (auto it = m_incomingTransactions.begin(); it != m_incomingTransactions.end(); ++it) - if (_tq.import(&*it)) - {}//ret = true; // just putting a transaction in the queue isn't enough to change the state - it might have an invalid nonce... - else - m_transactionsSent.insert(sha3(*it)); // if we already had the transaction, then don't bother sending it on. - m_incomingTransactions.clear(); - - auto h = _bc.currentHash(); - bool resendAll = (h != m_latestBlockSent); - - // Send any new transactions. - for (auto j: m_peers) - if (auto p = j.second.lock()) - { - bytes b; - uint n = 0; - for (auto const& i: _tq.transactions()) - if ((!m_transactionsSent.count(i.first) && !p->m_knownTransactions.count(i.first)) || p->m_requireTransactions || resendAll) - { - b += i.second; - ++n; - m_transactionsSent.insert(i.first); - } - if (n) - { - RLPStream ts; - PeerSession::prep(ts); - ts.appendList(n + 1) << TransactionsPacket; - ts.appendRaw(b, n).swapOut(b); - seal(b); - p->send(&b); - } - p->m_knownTransactions.clear(); - p->m_requireTransactions = false; - } - - // Send any new blocks. - if (h != m_latestBlockSent) - { - // TODO: find where they diverge and send complete new branch. - RLPStream ts; - PeerSession::prep(ts); - ts.appendList(2) << BlocksPacket; - bytes b; - ts.appendRaw(_bc.block(_bc.currentHash())).swapOut(b); - seal(b); - for (auto j: m_peers) - if (auto p = j.second.lock()) - { - if (!p->m_knownBlocks.count(_bc.currentHash())) - p->send(&b); - p->m_knownBlocks.clear(); - } - } - m_latestBlockSent = h; - - for (int accepted = 1, n = 0; accepted; ++n) - { - accepted = 0; - lock_guard l(m_incomingLock); - if (m_incomingBlocks.size()) - for (auto it = prev(m_incomingBlocks.end());; --it) - { - try - { - _bc.import(*it, _o); - it = m_incomingBlocks.erase(it); - ++accepted; - ret = true; - } - catch (UnknownParent) - { - // Don't (yet) know its parent. Leave it for later. - m_unknownParentBlocks.push_back(*it); - it = m_incomingBlocks.erase(it); - } - catch (...) - { - // Some other error - erase it. - it = m_incomingBlocks.erase(it); - } - - if (it == m_incomingBlocks.begin()) - break; - } - if (!n && accepted) - { - for (auto i: m_unknownParentBlocks) - m_incomingBlocks.push_back(i); - m_unknownParentBlocks.clear(); - } - } - - // Connect to additional peers - while (m_peers.size() < m_idealPeerCount) - { - if (m_freePeers.empty()) - { - if (chrono::steady_clock::now() > m_lastPeersRequest + chrono::seconds(10)) - { - RLPStream s; - bytes b; - (PeerSession::prep(s).appendList(1) << GetPeersPacket).swapOut(b); - seal(b); - for (auto const& i: m_peers) - if (auto p = i.second.lock()) - if (p->isOpen()) - p->send(&b); - m_lastPeersRequest = chrono::steady_clock::now(); - } - - - if (!m_accepting) - ensureAccepting(); - - break; - } - - auto x = time(0) % m_freePeers.size(); - m_incomingPeers[m_freePeers[x]].second++; - connect(m_incomingPeers[m_freePeers[x]].first); - m_freePeers.erase(m_freePeers.begin() + x); - } - } - - // platform for consensus of social contract. - // restricts your freedom but does so fairly. and that's the value proposition. - // guarantees that everyone else respect the rules of the system. (i.e. obeys laws). - - // We'll keep at most twice as many as is ideal, halfing what counts as "too young to kill" until we get there. - for (uint old = 15000; m_peers.size() > m_idealPeerCount * 2 && old > 100; old /= 2) - while (m_peers.size() > m_idealPeerCount) - { - // look for worst peer to kick off - // first work out how many are old enough to kick off. - shared_ptr worst; - unsigned agedPeers = 0; - for (auto i: m_peers) - if (auto p = i.second.lock()) - if ((m_mode != NodeMode::PeerServer || p->m_caps != 0x01) && chrono::steady_clock::now() > p->m_connect + chrono::milliseconds(old)) // don't throw off new peers; peer-servers should never kick off other peer-servers. - { - ++agedPeers; - if ((!worst || p->m_rating < worst->m_rating || (p->m_rating == worst->m_rating && p->m_connect > worst->m_connect))) // kill older ones - worst = p; - } - if (!worst || agedPeers <= m_idealPeerCount) - break; - worst->disconnect(TooManyPeers); - } - - return ret; -} - -std::vector PeerServer::peers(bool _updatePing) const -{ - if (_updatePing) - const_cast(this)->pingAll(); - this_thread::sleep_for(chrono::milliseconds(200)); - std::vector ret; - for (auto& i: m_peers) - if (auto j = i.second.lock()) - if (j->m_socket.is_open()) - ret.push_back(j->m_info); - return ret; -} - -void PeerServer::pingAll() -{ - for (auto& i: m_peers) - if (auto j = i.second.lock()) - j->ping(); -} - -bytes PeerServer::savePeers() const -{ - RLPStream ret; - int n = 0; - for (auto& i: m_peers) - if (auto p = i.second.lock()) - if (p->m_socket.is_open() && p->endpoint().port()) - { - ret.appendList(3) << p->endpoint().address().to_v4().to_bytes() << p->endpoint().port() << p->m_id; - n++; - } - return RLPStream(n).appendRaw(ret.out(), n).out(); -} - -void PeerServer::restorePeers(bytesConstRef _b) -{ - for (auto i: RLP(_b)) - { - auto k = (Public)i[2]; - if (!m_incomingPeers.count(k)) - { - m_incomingPeers.insert(make_pair(k, make_pair(bi::tcp::endpoint(bi::address_v4(i[0].toArray()), i[1].toInt()), 0))); - m_freePeers.push_back(k); - } - } -} diff --git a/libethereum/PeerServer.h b/libethereum/PeerServer.h deleted file mode 100644 index 6e44d2ea4..000000000 --- a/libethereum/PeerServer.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - 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 . -*/ -/** @file PeerServer.h - * @author Gav Wood - * @date 2014 - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include "PeerNetwork.h" -namespace ba = boost::asio; -namespace bi = boost::asio::ip; - -namespace eth -{ - -class PeerServer -{ - friend class PeerSession; - -public: - /// Start server, listening for connections on the given port. - PeerServer(std::string const& _clientVersion, BlockChain const& _ch, unsigned int _networkId, unsigned short _port, NodeMode _m = NodeMode::Full, std::string const& _publicAddress = std::string(), bool _upnp = true); - /// Start server, listening for connections on a system-assigned port. - PeerServer(std::string const& _clientVersion, BlockChain const& _ch, unsigned int _networkId, NodeMode _m = NodeMode::Full, std::string const& _publicAddress = std::string(), bool _upnp = true); - /// Start server, but don't listen. - PeerServer(std::string const& _clientVersion, BlockChain const& _ch, unsigned int _networkId, NodeMode _m = NodeMode::Full); - - ~PeerServer(); - - static unsigned protocolVersion(); - unsigned networkId() { return m_networkId; } - - /// Connect to a peer explicitly. - void connect(std::string const& _addr, unsigned short _port = 30303) noexcept; - void connect(bi::tcp::endpoint const& _ep); - - /// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network. - bool sync(BlockChain& _bc, TransactionQueue&, OverlayDB& _o); - - /// Conduct I/O, polling, syncing, whatever. - /// Ideally all time-consuming I/O is done in a background thread or otherwise asynchronously, but you get this call every 100ms or so anyway. - /// This won't touch alter the blockchain. - void process() { if (isInitialised()) m_ioService.poll(); } - - /// Set ideal number of peers. - void setIdealPeerCount(unsigned _n) { m_idealPeerCount = _n; } - - void setMode(NodeMode _m) { m_mode = _m; } - - /// Get peer information. - std::vector peers(bool _updatePing = false) const; - - /// Get number of peers connected; equivalent to, but faster than, peers().size(). - size_t peerCount() const { return m_peers.size(); } - - /// Ping the peers, to update the latency information. - void pingAll(); - - /// Get the port we're listening on currently. - unsigned short listenPort() const { return m_public.port(); } - - bytes savePeers() const; - void restorePeers(bytesConstRef _b); - -private: - /// Session wants to pass us a block that we might not have. - /// @returns true if we didn't have it. - bool noteBlock(h256 _hash, bytesConstRef _data); - - void seal(bytes& _b); - void populateAddresses(); - void determinePublic(std::string const& _publicAddress, bool _upnp); - void ensureAccepting(); - - /// Check to see if the network peer-state initialisation has happened. - bool isInitialised() const { return m_latestBlockSent; } - /// Initialises the network peer-state, doing the stuff that needs to be once-only. @returns true if it really was first. - bool ensureInitialised(BlockChain& _bc, TransactionQueue& _tq); - - std::map potentialPeers(); - - std::string m_clientVersion; - NodeMode m_mode = NodeMode::Full; - - unsigned short m_listenPort; - - BlockChain const* m_chain = nullptr; - ba::io_service m_ioService; - bi::tcp::acceptor m_acceptor; - bi::tcp::socket m_socket; - - UPnP* m_upnp = nullptr; - bi::tcp::endpoint m_public; - KeyPair m_key; - - unsigned m_networkId; - std::map> m_peers; - - std::vector m_incomingTransactions; - std::vector m_incomingBlocks; - mutable std::recursive_mutex m_incomingLock; - std::vector m_unknownParentBlocks; - std::vector m_freePeers; - std::map> m_incomingPeers; - - h256 m_latestBlockSent; - std::set m_transactionsSent; - - std::chrono::steady_clock::time_point m_lastPeersRequest; - unsigned m_idealPeerCount = 5; - - std::vector m_addresses; - std::vector m_peerAddresses; - - bool m_accepting = false; -}; - -} diff --git a/libethereum/PeerSession.cpp b/libethereum/PeerSession.cpp deleted file mode 100644 index f06b2fcbf..000000000 --- a/libethereum/PeerSession.cpp +++ /dev/null @@ -1,606 +0,0 @@ -/* - 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 . -*/ -/** @file PeerSession.cpp - * @author Gav Wood - * @date 2014 - */ - -#include "PeerSession.h" - -#include -#include -#include -#include "BlockChain.h" -#include "PeerServer.h" -using namespace std; -using namespace eth; - -#define clogS(X) eth::LogOutputStream(false) << "| " << std::setw(2) << m_socket.native_handle() << "] " - -static const eth::uint c_maxHashes = 32; ///< Maximum number of hashes GetChain will ever send. -static const eth::uint c_maxBlocks = 64; ///< Maximum number of blocks Blocks will ever send. BUG: if this gets too big (e.g. 2048) stuff starts going wrong. -static const eth::uint c_maxBlocksAsk = 256; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain). - -PeerSession::PeerSession(PeerServer* _s, bi::tcp::socket _socket, uint _rNId, bi::address _peerAddress, unsigned short _peerPort): - m_server(_s), - m_socket(std::move(_socket)), - m_reqNetworkId(_rNId), - m_listenPort(_peerPort), - m_rating(0) -{ - m_disconnect = std::chrono::steady_clock::time_point::max(); - m_connect = std::chrono::steady_clock::now(); - m_info = PeerInfo({"?", _peerAddress.to_string(), m_listenPort, std::chrono::steady_clock::duration(0)}); -} - -PeerSession::~PeerSession() -{ - // Read-chain finished for one reason or another. - try - { - if (m_socket.is_open()) - m_socket.close(); - } - catch (...){} -} - -bi::tcp::endpoint PeerSession::endpoint() const -{ - if (m_socket.is_open()) - try { - return bi::tcp::endpoint(m_socket.remote_endpoint().address(), m_listenPort); - } catch (...){} - - return bi::tcp::endpoint(); -} - -bool PeerSession::interpret(RLP const& _r) -{ - clogS(NetRight) << _r; - switch (_r[0].toInt()) - { - case HelloPacket: - { - m_protocolVersion = _r[1].toInt(); - m_networkId = _r[2].toInt(); - auto clientVersion = _r[3].toString(); - m_caps = _r[4].toInt(); - m_listenPort = _r[5].toInt(); - m_id = _r[6].toHash(); - - clogS(NetMessageSummary) << "Hello: " << clientVersion << "V[" << m_protocolVersion << "/" << m_networkId << "]" << m_id.abridged() << showbase << hex << m_caps << dec << m_listenPort; - - if (m_server->m_peers.count(m_id)) - if (auto l = m_server->m_peers[m_id].lock()) - if (l.get() != this && l->isOpen()) - { - // Already connected. - cwarn << "Already have peer id" << m_id.abridged() << "at" << l->endpoint() << "rather than" << endpoint(); - disconnect(DuplicatePeer); - return false; - } - - if (m_protocolVersion != PeerServer::protocolVersion() || m_networkId != m_server->networkId() || !m_id) - { - disconnect(IncompatibleProtocol); - return false; - } - try - { m_info = PeerInfo({clientVersion, m_socket.remote_endpoint().address().to_string(), m_listenPort, std::chrono::steady_clock::duration()}); } - catch (...) - { - disconnect(BadProtocol); - return false; - } - - m_server->m_peers[m_id] = shared_from_this(); - - // Grab their block chain off them. - { - uint n = m_server->m_chain->number(m_server->m_latestBlockSent); - clogS(NetAllDetail) << "Want chain. Latest:" << m_server->m_latestBlockSent << ", number:" << n; - uint count = std::min(c_maxHashes, n + 1); - RLPStream s; - prep(s).appendList(2 + count); - s << GetChainPacket; - auto h = m_server->m_latestBlockSent; - for (uint i = 0; i < count; ++i, h = m_server->m_chain->details(h).parent) - { - clogS(NetAllDetail) << " " << i << ":" << h; - s << h; - } - - s << c_maxBlocksAsk; - sealAndSend(s); - s.clear(); - prep(s).appendList(1); - s << GetTransactionsPacket; - sealAndSend(s); - } - break; - } - case DisconnectPacket: - { - string reason = "Unspecified"; - if (_r[1].isInt()) - reason = reasonOf((DisconnectReason)_r[1].toInt()); - - clogS(NetMessageSummary) << "Disconnect (reason: " << reason << ")"; - if (m_socket.is_open()) - clogS(NetNote) << "Closing " << m_socket.remote_endpoint(); - else - clogS(NetNote) << "Remote closed."; - m_socket.close(); - return false; - } - case PingPacket: - { - clogS(NetTriviaSummary) << "Ping"; - RLPStream s; - sealAndSend(prep(s).appendList(1) << PongPacket); - break; - } - case PongPacket: - m_info.lastPing = std::chrono::steady_clock::now() - m_ping; - clogS(NetTriviaSummary) << "Latency: " << chrono::duration_cast(m_info.lastPing).count() << " ms"; - break; - case GetPeersPacket: - { - clogS(NetTriviaSummary) << "GetPeers"; - auto peers = m_server->potentialPeers(); - RLPStream s; - prep(s).appendList(peers.size() + 1); - s << PeersPacket; - for (auto i: peers) - { - clogS(NetTriviaDetail) << "Sending peer " << toHex(i.first.ref().cropped(0, 4)) << i.second; - s.appendList(3) << bytesConstRef(i.second.address().to_v4().to_bytes().data(), 4) << i.second.port() << i.first; - } - sealAndSend(s); - break; - } - case PeersPacket: - clogS(NetTriviaSummary) << "Peers (" << dec << (_r.itemCount() - 1) << " entries)"; - for (unsigned i = 1; i < _r.itemCount(); ++i) - { - bi::address_v4 peerAddress(_r[i][0].toHash>().asArray()); - auto ep = bi::tcp::endpoint(peerAddress, _r[i][1].toInt()); - Public id = _r[i][2].toHash(); - if (isPrivateAddress(peerAddress)) - goto CONTINUE; - - clogS(NetAllDetail) << "Checking: " << ep << "(" << toHex(id.ref().cropped(0, 4)) << ")"; - - // check that it's not us or one we already know: - if (id && (m_server->m_key.pub() == id || m_server->m_peers.count(id) || m_server->m_incomingPeers.count(id))) - goto CONTINUE; - - // check that we're not already connected to addr: - if (!ep.port()) - goto CONTINUE; - for (auto i: m_server->m_addresses) - if (ep.address() == i && ep.port() == m_server->listenPort()) - goto CONTINUE; - for (auto i: m_server->m_peers) - if (shared_ptr p = i.second.lock()) - { - clogS(NetAllDetail) << " ...against " << p->endpoint(); - if (p->m_socket.is_open() && p->endpoint() == ep) - goto CONTINUE; - } - for (auto i: m_server->m_incomingPeers) - if (i.second.first == ep) - goto CONTINUE; - m_server->m_incomingPeers[id] = make_pair(ep, 0); - m_server->m_freePeers.push_back(id); - clogS(NetTriviaDetail) << "New peer: " << ep << "(" << id << ")"; - CONTINUE:; - } - break; - case TransactionsPacket: - if (m_server->m_mode == NodeMode::PeerServer) - break; - clogS(NetMessageSummary) << "Transactions (" << dec << (_r.itemCount() - 1) << " entries)"; - m_rating += _r.itemCount() - 1; - for (unsigned i = 1; i < _r.itemCount(); ++i) - { - m_server->m_incomingTransactions.push_back(_r[i].data().toBytes()); - m_knownTransactions.insert(sha3(_r[i].data())); - } - break; - case BlocksPacket: - { - if (m_server->m_mode == NodeMode::PeerServer) - break; - clogS(NetMessageSummary) << "Blocks (" << dec << (_r.itemCount() - 1) << " entries)"; - unsigned used = 0; - for (unsigned i = 1; i < _r.itemCount(); ++i) - { - auto h = sha3(_r[i].data()); - if (m_server->noteBlock(h, _r[i].data())) - { - m_knownBlocks.insert(h); - used++; - } - } - m_rating += used; - if (g_logVerbosity >= 3) - for (unsigned i = 1; i < _r.itemCount(); ++i) - { - auto h = sha3(_r[i].data()); - BlockInfo bi(_r[i].data()); - if (!m_server->m_chain->details(bi.parentHash) && !m_knownBlocks.count(bi.parentHash)) - clogS(NetMessageDetail) << "Unknown parent " << bi.parentHash << " of block " << h; - else - clogS(NetMessageDetail) << "Known parent " << bi.parentHash << " of block " << h; - } - if (used) // we received some - check if there's any more - { - RLPStream s; - prep(s).appendList(3); - s << GetChainPacket; - s << sha3(_r[1].data()); - s << c_maxBlocksAsk; - sealAndSend(s); - } - break; - } - case GetChainPacket: - { - if (m_server->m_mode == NodeMode::PeerServer) - break; - clogS(NetMessageSummary) << "GetChain (" << (_r.itemCount() - 2) << " hashes, " << (_r[_r.itemCount() - 1].toInt()) << ")"; - // ******************************************************************** - // NEEDS FULL REWRITE! - h256s parents; - parents.reserve(_r.itemCount() - 2); - for (unsigned i = 1; i < _r.itemCount() - 1; ++i) - parents.push_back(_r[i].toHash()); - if (_r.itemCount() == 2) - break; - // return 2048 block max. - uint baseCount = (uint)min(_r[_r.itemCount() - 1].toInt(), c_maxBlocks); - clogS(NetMessageSummary) << "GetChain (" << baseCount << " max, from " << parents.front() << " to " << parents.back() << ")"; - for (auto parent: parents) - { - auto h = m_server->m_chain->currentHash(); - h256 latest = m_server->m_chain->currentHash(); - uint latestNumber = 0; - uint parentNumber = 0; - RLPStream s; - - // try to find parent in our blockchain - // todo: add some delta() fn to blockchain - BlockDetails fParent = m_server->m_chain->details(parent); - if (fParent) - { - latestNumber = m_server->m_chain->number(latest); - parentNumber = fParent.number; - uint count = min(latestNumber - parentNumber, baseCount); - clogS(NetAllDetail) << "Requires " << dec << (latestNumber - parentNumber) << " blocks from " << latestNumber << " to " << parentNumber; - clogS(NetAllDetail) << latest << " - " << parent; - - prep(s); - s.appendList(1 + count) << BlocksPacket; - uint endNumber = parentNumber; - uint startNumber = endNumber + count; - clogS(NetAllDetail) << "Sending " << dec << count << " blocks from " << startNumber << " to " << endNumber; - - // append blocks - uint n = latestNumber; - // seek back (occurs when count is limited by baseCount) - for (; n > startNumber; n--, h = m_server->m_chain->details(h).parent) {} - for (uint i = 0; i < count; ++i, --n, h = m_server->m_chain->details(h).parent) - { - if (h == parent || n == endNumber) - { - cwarn << "BUG! Couldn't create the reply for GetChain!"; - return true; - } - clogS(NetAllDetail) << " " << dec << i << " " << h; - s.appendRaw(m_server->m_chain->block(h)); - } - clogS(NetAllDetail) << "Parent: " << h; - } - else if (parent != parents.back()) - continue; - - if (h != parent) - { - // not in the blockchain; - if (parent == parents.back()) - { - // out of parents... - clogS(NetAllDetail) << "GetChain failed; not in chain"; - // No good - must have been on a different branch. - s.clear(); - prep(s).appendList(2) << NotInChainPacket << parents.back(); - } - else - // still some parents left - try them. - continue; - } - // send the packet (either Blocks or NotInChain) & exit. - sealAndSend(s); - break; - // ******************************************************************** - } - break; - } - case NotInChainPacket: - { - if (m_server->m_mode == NodeMode::PeerServer) - break; - h256 noGood = _r[1].toHash(); - clogS(NetMessageSummary) << "NotInChain (" << noGood << ")"; - if (noGood == m_server->m_chain->genesisHash()) - { - clogS(NetWarn) << "Discordance over genesis block! Disconnect."; - disconnect(WrongGenesis); - } - else - { - uint count = std::min(c_maxHashes, m_server->m_chain->number(noGood)); - RLPStream s; - prep(s).appendList(2 + count); - s << GetChainPacket; - auto h = m_server->m_chain->details(noGood).parent; - for (uint i = 0; i < count; ++i, h = m_server->m_chain->details(h).parent) - s << h; - s << c_maxBlocksAsk; - sealAndSend(s); - } - break; - } - case GetTransactionsPacket: - { - if (m_server->m_mode == NodeMode::PeerServer) - break; - m_requireTransactions = true; - break; - } - default: - break; - } - return true; -} - -void PeerSession::ping() -{ - RLPStream s; - sealAndSend(prep(s).appendList(1) << PingPacket); - m_ping = std::chrono::steady_clock::now(); -} - -RLPStream& PeerSession::prep(RLPStream& _s) -{ - return _s.appendRaw(bytes(8, 0)); -} - -void PeerSession::sealAndSend(RLPStream& _s) -{ - bytes b; - _s.swapOut(b); - m_server->seal(b); - sendDestroy(b); -} - -bool PeerSession::checkPacket(bytesConstRef _msg) -{ - if (_msg.size() < 8) - return false; - if (!(_msg[0] == 0x22 && _msg[1] == 0x40 && _msg[2] == 0x08 && _msg[3] == 0x91)) - return false; - uint32_t len = ((_msg[4] * 256 + _msg[5]) * 256 + _msg[6]) * 256 + _msg[7]; - if (_msg.size() != len + 8) - return false; - RLP r(_msg.cropped(8)); - if (r.actualSize() != len) - return false; - return true; -} - -void PeerSession::sendDestroy(bytes& _msg) -{ - clogS(NetLeft) << RLP(bytesConstRef(&_msg).cropped(8)); - - if (!checkPacket(bytesConstRef(&_msg))) - { - cwarn << "INVALID PACKET CONSTRUCTED!"; - } - - bytes buffer = bytes(std::move(_msg)); - writeImpl(buffer); -} - -void PeerSession::send(bytesConstRef _msg) -{ - clogS(NetLeft) << RLP(_msg.cropped(8)); - - if (!checkPacket(_msg)) - { - cwarn << "INVALID PACKET CONSTRUCTED!"; - } - - bytes buffer = bytes(_msg.toBytes()); - writeImpl(buffer); -} - -void PeerSession::writeImpl(bytes& _buffer) -{ -// cerr << (void*)this << " writeImpl" << endl; - if (!m_socket.is_open()) - return; - - lock_guard l(m_writeLock); - m_writeQueue.push_back(_buffer); - if (m_writeQueue.size() == 1) - write(); -} - -void PeerSession::write() -{ -// cerr << (void*)this << " write" << endl; - lock_guard l(m_writeLock); - if (m_writeQueue.empty()) - return; - - const bytes& bytes = m_writeQueue[0]; - auto self(shared_from_this()); - ba::async_write(m_socket, ba::buffer(bytes), [this, self](boost::system::error_code ec, std::size_t /*length*/) - { -// cerr << (void*)this << " write.callback" << endl; - - // must check queue, as write callback can occur following dropped() - if (ec) - { - cwarn << "Error sending: " << ec.message(); - dropped(); - } - else - { - m_writeQueue.pop_front(); - write(); - } - }); -} - -void PeerSession::dropped() -{ -// cerr << (void*)this << " dropped" << endl; - if (m_socket.is_open()) - try - { - clogS(NetNote) << "Closing " << m_socket.remote_endpoint(); - m_socket.close(); - } - catch (...) {} - - // Remove from peer server - for (auto i = m_server->m_peers.begin(); i != m_server->m_peers.end(); ++i) - if (i->second.lock().get() == this) - { - m_server->m_peers.erase(i); - break; - } -} - -void PeerSession::disconnect(int _reason) -{ - clogS(NetNote) << "Disconnecting (reason:" << reasonOf((DisconnectReason)_reason) << ")"; - if (m_socket.is_open()) - { - if (m_disconnect == chrono::steady_clock::time_point::max()) - { - RLPStream s; - prep(s); - s.appendList(2) << DisconnectPacket << _reason; - sealAndSend(s); - m_disconnect = chrono::steady_clock::now(); - } - else - dropped(); - } -} - -void PeerSession::start() -{ - RLPStream s; - prep(s); - s.appendList(7) << HelloPacket << (uint)PeerServer::protocolVersion() << m_server->networkId() << m_server->m_clientVersion << (m_server->m_mode == NodeMode::Full ? 0x07 : m_server->m_mode == NodeMode::PeerServer ? 0x01 : 0) << m_server->m_public.port() << m_server->m_key.pub(); - sealAndSend(s); - - ping(); - - doRead(); -} - -void PeerSession::doRead() -{ - // ignore packets received while waiting to disconnect - if (chrono::steady_clock::now() - m_disconnect > chrono::seconds(0)) - return; - - auto self(shared_from_this()); - m_socket.async_read_some(boost::asio::buffer(m_data), [this,self](boost::system::error_code ec, std::size_t length) - { - // If error is end of file, ignore - if (ec && ec.category() != boost::asio::error::get_misc_category() && ec.value() != boost::asio::error::eof) - { - // got here with length of 1241... - cwarn << "Error reading: " << ec.message(); - dropped(); - } - else if (ec && length == 0) - { - return; - } - else - { - try - { - m_incoming.resize(m_incoming.size() + length); - memcpy(m_incoming.data() + m_incoming.size() - length, m_data.data(), length); - while (m_incoming.size() > 8) - { - if (m_incoming[0] != 0x22 || m_incoming[1] != 0x40 || m_incoming[2] != 0x08 || m_incoming[3] != 0x91) - doRead(); - else - { - uint32_t len = fromBigEndian(bytesConstRef(m_incoming.data() + 4, 4)); - uint32_t tlen = len + 8; - if (m_incoming.size() < tlen) - break; - - // enough has come in. - auto data = bytesConstRef(m_incoming.data(), tlen); - if (!checkPacket(data)) - { - cerr << "Received " << len << ": " << toHex(bytesConstRef(m_incoming.data() + 8, len)) << endl; - cwarn << "INVALID MESSAGE RECEIVED"; - disconnect(BadProtocol); - return; - } - else - { - RLP r(data.cropped(8)); - if (!interpret(r)) - { - // error - dropped(); - return; - } - } - memmove(m_incoming.data(), m_incoming.data() + tlen, m_incoming.size() - tlen); - m_incoming.resize(m_incoming.size() - tlen); - } - } - doRead(); - } - catch (Exception const& _e) - { - clogS(NetWarn) << "ERROR: " << _e.description(); - dropped(); - } - catch (std::exception const& _e) - { - clogS(NetWarn) << "ERROR: " << _e.what(); - dropped(); - } - } - }); -} diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 66dec31e7..a74b6ba18 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -33,7 +33,8 @@ #include "Defaults.h" #include "ExtVM.h" using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; #define ctrace clog(StateTrace) @@ -52,6 +53,9 @@ OverlayDB State::openDB(std::string _path, bool _killExisting) o.create_if_missing = true; ldb::DB* db = nullptr; ldb::DB::Open(o, _path + "/state", &db); + if (!db) + throw DatabaseAlreadyOpen(); + cnote << "Opened state DB."; return OverlayDB(db); } @@ -69,7 +73,7 @@ State::State(Address _coinbaseAddress, OverlayDB const& _db): paranoia("beginning of normal construction.", true); - eth::commit(genesisState(), m_db, m_state); + dev::eth::commit(genesisState(), m_db, m_state); m_db.commit(); paranoia("after DB commit of normal construction.", true); @@ -148,10 +152,15 @@ State& State::operator=(State const& _s) m_currentBlock = _s.m_currentBlock; m_ourAddress = _s.m_ourAddress; m_blockReward = _s.m_blockReward; + m_lastTx = _s.m_lastTx; paranoia("after state cloning (assignment op)", true); return *this; } +State::~State() +{ +} + struct CachedAddressState { CachedAddressState(std::string const& _rlp, AddressState const* _s, OverlayDB const* _o): rS(_rlp), r(rS), s(_s), o(_o) {} @@ -287,7 +296,7 @@ void State::ensureCached(std::map& _cache, Address _a, bo if (state.isNull()) s = AddressState(0, 0, h256(), EmptySHA3); else - s = AddressState(state[0].toInt(), state[1].toInt(), state[2].toHash(), state[3].toHash()); + s = AddressState(state[0].toInt(), state[1].toInt(), state[2].toHash(), state[3].isEmpty() ? EmptySHA3 : state[3].toHash()); bool ok; tie(it, ok) = _cache.insert(make_pair(_a, s)); } @@ -297,7 +306,7 @@ void State::ensureCached(std::map& _cache, Address _a, bo void State::commit() { - eth::commit(m_cache, m_db, m_state); + dev::eth::commit(m_cache, m_db, m_state); m_cache.clear(); } @@ -311,22 +320,29 @@ bool State::sync(BlockChain const& _bc, h256 _block, BlockInfo const& _bi) bool ret = false; // BLOCK BlockInfo bi = _bi; - try - { - if (!bi) + if (!bi) + while (1) { - auto b = _bc.block(_block); - bi.populate(b); -// bi.verifyInternals(_bc.block(_block)); // Unneeded - we already verify on import into the blockchain. + try + { + auto b = _bc.block(_block); + bi.populate(b); + // bi.verifyInternals(_bc.block(_block)); // Unneeded - we already verify on import into the blockchain. + break; + } + catch (Exception const& _e) + { + // TODO: Slightly nicer handling? :-) + cerr << "ERROR: Corrupt block-chain! Delete your block-chain DB and restart." << endl; + cerr << _e.description() << endl; + } + catch (std::exception const& _e) + { + // TODO: Slightly nicer handling? :-) + cerr << "ERROR: Corrupt block-chain! Delete your block-chain DB and restart." << endl; + cerr << _e.what() << endl; + } } - } - catch (...) - { - // TODO: Slightly nicer handling? :-) - cerr << "ERROR: Corrupt block-chain! Delete your block-chain DB and restart." << endl; - exit(1); - } - if (bi == m_currentBlock) { // We mined the last block. @@ -390,7 +406,7 @@ u256 State::enactOn(bytesConstRef _block, BlockInfo const& _bi, BlockChain const sync(_bc, _bi.parentHash); resetCurrent(); m_previousBlock = biParent; - return enact(_block, biGrandParent); + return enact(_block, &_bc); } map State::addresses() const @@ -454,28 +470,24 @@ bool State::cull(TransactionQueue& _tq) const return ret; } -bool State::sync(TransactionQueue& _tq, bool* _changed) +h256s State::sync(TransactionQueue& _tq, bool* o_transactionQueueChanged) { // TRANSACTIONS - bool ret = false; + h256s ret; auto ts = _tq.transactions(); - vector> futures; for (int goodTxs = 1; goodTxs;) { goodTxs = 0; for (auto const& i: ts) - { if (!m_transactionSet.count(i.first)) { // don't have it yet! Execute it now. try { - ret = true; uncommitToMine(); execute(i.second); - if (_changed) - *_changed = true; + ret.push_back(m_transactions.back().changes.bloom()); _tq.noteGood(i); ++goodTxs; } @@ -485,8 +497,8 @@ bool State::sync(TransactionQueue& _tq, bool* _changed) { // too old _tq.drop(i.first); - if (_changed) - *_changed = true; + if (o_transactionQueueChanged) + *o_transactionQueueChanged = true; } else _tq.setFuture(i); @@ -495,16 +507,15 @@ bool State::sync(TransactionQueue& _tq, bool* _changed) { // Something else went wrong - drop it. _tq.drop(i.first); - if (_changed) - *_changed = true; + if (o_transactionQueueChanged) + *o_transactionQueueChanged = true; } } - } } return ret; } -u256 State::enact(bytesConstRef _block, BlockInfo const& _grandParent, bool _checkNonce) +u256 State::enact(bytesConstRef _block, BlockChain const* _bc, bool _checkNonce) { // m_currentBlock is assumed to be prepopulated and reset. @@ -563,16 +574,21 @@ u256 State::enact(bytesConstRef _block, BlockInfo const& _grandParent, bool _che // Check uncles & apply their rewards to state. set nonces = { m_currentBlock.nonce }; Addresses rewarded; + set knownUncles = _bc ? _bc->allUnclesFrom(m_currentBlock.parentHash) : set(); for (auto const& i: RLP(_block)[2]) { BlockInfo uncle = BlockInfo::fromHeader(i.data()); - - if (m_previousBlock.parentHash != uncle.parentHash) - throw UncleNotAnUncle(); if (nonces.count(uncle.nonce)) throw DuplicateUncleNonce(); - if (_grandParent) - uncle.verifyParent(_grandParent); + if (_bc) + { + BlockInfo uncleParent(_bc->block(uncle.parentHash)); + if ((bigint)uncleParent.number < (bigint)m_currentBlock.number - 6) + throw UncleTooOld(); + if (knownUncles.count(sha3(i.data()))) + throw UncleInChain(); + uncle.verifyParent(uncleParent); + } nonces.insert(uncle.nonce); tdIncrease += uncle.difficulty; @@ -656,7 +672,7 @@ bool State::amIJustParanoid(BlockChain const& _bc) cnote << "PARANOIA root:" << s.rootHash(); // s.m_currentBlock.populate(&block.out(), false); // s.m_currentBlock.verifyInternals(&block.out()); - s.enact(&block.out(), BlockInfo(), false); // don't check nonce for this since we haven't mined it yet. + s.enact(&block.out(), &_bc, false); // don't check nonce for this since we haven't mined it yet. s.cleanup(false); return true; } @@ -674,7 +690,7 @@ bool State::amIJustParanoid(BlockChain const& _bc) h256 State::bloom() const { - h256 ret; + h256 ret = m_currentBlock.coinbaseAddress.bloom(); for (auto const& i: m_transactions) ret |= i.changes.bloom(); return ret; @@ -686,7 +702,7 @@ void State::commitToMine(BlockChain const& _bc) { uncommitToMine(); - cnote << "Commiting to mine on block" << m_previousBlock.hash; + cnote << "Committing to mine on block" << m_previousBlock.hash.abridged(); #ifdef ETH_PARANOIA commit(); cnote << "Pre-reward stateRoot:" << m_state.root(); @@ -694,26 +710,30 @@ void State::commitToMine(BlockChain const& _bc) m_lastTx = m_db; - RLPStream uncles; Addresses uncleAddresses; + RLPStream unclesData; + unsigned unclesCount = 0; if (m_previousBlock != BlockChain::genesis()) { - // Find uncles if we're not a direct child of the genesis. + // Find great-uncles (or second-cousins or whatever they are) - children of great-grandparents, great-great-grandparents... that were not already uncles in previous generations. // cout << "Checking " << m_previousBlock.hash << ", parent=" << m_previousBlock.parentHash << endl; - auto us = _bc.details(m_previousBlock.parentHash).children; - assert(us.size() >= 1); // must be at least 1 child of our grandparent - it's our own parent! - uncles.appendList(us.size() - 1); // one fewer - uncles precludes our parent from the list of grandparent's children. - for (auto const& u: us) - if (u != m_previousBlock.hash) // ignore our own parent - it's not an uncle. - { - BlockInfo ubi(_bc.block(u)); - ubi.fillStream(uncles, true); - uncleAddresses.push_back(ubi.coinbaseAddress); - } + set knownUncles = _bc.allUnclesFrom(m_currentBlock.parentHash); + auto p = m_previousBlock.parentHash; + for (unsigned gen = 0; gen < 6 && p != _bc.genesisHash(); ++gen, p = _bc.details(p).parent) + { + auto us = _bc.details(p).children; + assert(us.size() >= 1); // must be at least 1 child of our grandparent - it's our own parent! + for (auto const& u: us) + if (!knownUncles.count(BlockInfo::headerHash(_bc.block(u)))) // ignore any uncles/mainline blocks that we know about. We use header-hash for this. + { + BlockInfo ubi(_bc.block(u)); + ubi.fillStream(unclesData, true); + ++unclesCount; + uncleAddresses.push_back(ubi.coinbaseAddress); + } + } } - else - uncles.appendList(0); MemoryDB tm; GenericTrieDB transactionReceipts(&tm); @@ -733,7 +753,8 @@ void State::commitToMine(BlockChain const& _bc) } txs.swapOut(m_currentTxs); - uncles.swapOut(m_currentUncles); + + RLPStream(unclesCount).appendRaw(unclesData.out(), unclesCount).swapOut(m_currentUncles); m_currentBlock.transactionsRoot = transactionReceipts.root(); m_currentBlock.sha3Uncles = sha3(m_currentUncles); @@ -744,7 +765,7 @@ void State::commitToMine(BlockChain const& _bc) // Commit any and all changes to the trie that are in the cache, then update the state root accordingly. commit(); - cnote << "Post-reward stateRoot:" << m_state.root(); + cnote << "Post-reward stateRoot:" << m_state.root().abridged(); // cnote << m_state; // cnote << *this; @@ -753,13 +774,13 @@ void State::commitToMine(BlockChain const& _bc) m_currentBlock.parentHash = m_previousBlock.hash; } -MineInfo State::mine(uint _msTimeout) +MineInfo State::mine(unsigned _msTimeout, bool _turbo) { // Update difficulty according to timestamp. m_currentBlock.difficulty = m_currentBlock.calculateDifficulty(m_previousBlock); // TODO: Miner class that keeps dagger between mine calls (or just non-polling mining). - auto ret = m_dagger.mine(/*out*/m_currentBlock.nonce, m_currentBlock.headerHashWithoutNonce(), m_currentBlock.difficulty, _msTimeout); + auto ret = m_dagger.mine(/*out*/m_currentBlock.nonce, m_currentBlock.headerHashWithoutNonce(), m_currentBlock.difficulty, _msTimeout, true, _turbo); if (!ret.completed) m_currentBytes.clear(); @@ -772,9 +793,6 @@ void State::completeMine() cdebug << "Completing mine!"; // Got it! - // Commit to disk. - m_db.commit(); - // Compile block: RLPStream ret; ret.appendList(3); @@ -783,7 +801,7 @@ void State::completeMine() ret.appendRaw(m_currentUncles); ret.swapOut(m_currentBytes); m_currentBlock.hash = sha3(m_currentBytes); - cnote << "Mined " << m_currentBlock.hash << "(parent: " << m_currentBlock.parentHash << ")"; + cnote << "Mined " << m_currentBlock.hash.abridged() << "(parent: " << m_currentBlock.parentHash.abridged() << ")"; // Quickly reset the transactions. // TODO: Leave this in a better state than this limbo, or at least record that it's in limbo. @@ -1032,7 +1050,7 @@ u256 State::execute(bytesConstRef _rlp, bytes* o_output, bool _commit) return e.gasUsed(); } -bool State::call(Address _receiveAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256* _gas, bytesRef _out, Address _originAddress, std::set
* o_suicides, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level) +bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256* _gas, bytesRef _out, Address _originAddress, std::set
* o_suicides, PostList* o_posts, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level) { if (!_originAddress) _originAddress = _senderAddress; @@ -1048,10 +1066,10 @@ bool State::call(Address _receiveAddress, Address _senderAddress, u256 _value, u o_ms->input = _data.toBytes(); } - if (addressHasCode(_receiveAddress)) + if (addressHasCode(_codeAddress)) { VM vm(*_gas); - ExtVM evm(*this, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &code(_receiveAddress), o_ms, _level); + ExtVM evm(*this, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &code(_codeAddress), o_ms, _level); bool revert = false; try @@ -1061,6 +1079,9 @@ bool State::call(Address _receiveAddress, Address _senderAddress, u256 _value, u if (o_suicides) for (auto i: evm.suicides) o_suicides->insert(i); + if (o_posts) + for (auto i: evm.posts) + o_posts->push_back(i); if (o_ms) o_ms->output = out.toBytes(); } @@ -1093,7 +1114,7 @@ bool State::call(Address _receiveAddress, Address _senderAddress, u256 _value, u return true; } -h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _origin, std::set
* o_suicides, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level) +h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _origin, std::set
* o_suicides, PostList* o_posts, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level) { if (!_origin) _origin = _sender; @@ -1111,7 +1132,7 @@ h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, newAddress = (u160)newAddress + 1; // Set up new account... - m_cache[newAddress] = AddressState(0, 0, h256(), h256()); + m_cache[newAddress] = AddressState(0, _endowment, h256(), h256()); // Execute init code. VM vm(*_gas); @@ -1127,6 +1148,9 @@ h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, if (o_suicides) for (auto i: evm.suicides) o_suicides->insert(i); + if (o_posts) + for (auto i: evm.posts) + o_posts->push_back(i); } catch (OutOfGas const& /*_e*/) { @@ -1183,13 +1207,13 @@ void State::applyRewards(Addresses const& _uncleAddresses) u256 r = m_blockReward; for (auto const& i: _uncleAddresses) { - addBalance(i, m_blockReward * 3 / 4); - r += m_blockReward / 8; + addBalance(i, m_blockReward * 15 / 16); + r += m_blockReward / 32; } addBalance(m_currentBlock.coinbaseAddress, r); } -std::ostream& eth::operator<<(std::ostream& _out, State const& _s) +std::ostream& dev::eth::operator<<(std::ostream& _out, State const& _s) { _out << "--- " << _s.rootHash() << std::endl; std::set
d; @@ -1232,10 +1256,12 @@ std::ostream& eth::operator<<(std::ostream& _out, State const& _s) } if (cache) for (auto const& j: cache->storage()) + { if ((!mem.count(j.first) && j.second) || (mem.count(j.first) && mem.at(j.first) != j.second)) mem[j.first] = j.second, delta.insert(j.first); else if (j.second) cached.insert(j.first); + } if (delta.size()) lead = (lead == " . ") ? "*.* " : "*** "; @@ -1262,54 +1288,3 @@ std::ostream& eth::operator<<(std::ostream& _out, State const& _s) } return _out; } - -AccountChange AccountDiff::changeType() const -{ - bool bn = (balance || nonce); - bool sc = (!storage.empty() || code); - return exist ? exist.from() ? AccountChange::Deletion : AccountChange::Creation : (bn && sc) ? AccountChange::All : bn ? AccountChange::Intrinsic: sc ? AccountChange::CodeStorage : AccountChange::None; -} - -char const* AccountDiff::lead() const -{ - bool bn = (balance || nonce); - bool sc = (!storage.empty() || code); - return exist ? exist.from() ? "XXX" : "+++" : (bn && sc) ? "***" : bn ? " * " : sc ? "* *" : " "; -} - -std::ostream& eth::operator<<(std::ostream& _out, AccountDiff const& _s) -{ - if (!_s.exist.to()) - return _out; - - if (_s.nonce) - { - _out << std::dec << "#" << _s.nonce.to() << " "; - if (_s.nonce.from()) - _out << "(" << std::showpos << (((bigint)_s.nonce.to()) - ((bigint)_s.nonce.from())) << std::noshowpos << ") "; - } - if (_s.balance) - { - _out << std::dec << _s.balance.to() << " "; - if (_s.balance.from()) - _out << "(" << std::showpos << (((bigint)_s.balance.to()) - ((bigint)_s.balance.from())) << std::noshowpos << ") "; - } - if (_s.code) - _out << "$" << std::hex << nouppercase << _s.code.to() << " (" << _s.code.from() << ") "; - for (pair> const& i: _s.storage) - if (!i.second.from()) - _out << endl << " + " << (h256)i.first << ": " << std::hex << nouppercase << i.second.to(); - else if (!i.second.to()) - _out << endl << "XXX " << (h256)i.first << " (" << std::hex << nouppercase << i.second.from() << ")"; - else - _out << endl << " * " << (h256)i.first << ": " << std::hex << nouppercase << i.second.to() << " (" << i.second.from() << ")"; - return _out; -} - -std::ostream& eth::operator<<(std::ostream& _out, StateDiff const& _s) -{ - _out << _s.accounts.size() << " accounts changed:" << endl; - for (auto const& i: _s.accounts) - _out << i.second.lead() << " " << i.first << ": " << i.second << endl; - return _out; -} diff --git a/libethereum/State.h b/libethereum/State.h index 3618b9a9a..fea8e06c7 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -24,9 +24,9 @@ #include #include #include -#include -#include -#include +#include +#include +#include #include #include #include @@ -36,7 +36,10 @@ #include "AddressState.h" #include "Transaction.h" #include "Executive.h" +#include "AccountDiff.h" +namespace dev +{ namespace eth { @@ -64,44 +67,6 @@ struct TransactionReceipt Manifest changes; }; -enum class ExistDiff { Same, New, Dead }; -template -class Diff -{ -public: - Diff() {} - Diff(T _from, T _to): m_from(_from), m_to(_to) {} - - T const& from() const { return m_from; } - T const& to() const { return m_to; } - - explicit operator bool() const { return m_from != m_to; } - -private: - T m_from; - T m_to; -}; - -enum class AccountChange { None, Creation, Deletion, Intrinsic, CodeStorage, All }; - -struct AccountDiff -{ - inline bool changed() const { return storage.size() || code || nonce || balance || exist; } - char const* lead() const; - AccountChange changeType() const; - - Diff exist; - Diff balance; - Diff nonce; - std::map> storage; - Diff code; -}; - -struct StateDiff -{ - std::map accounts; -}; - /** * @brief Model of the current state of the ledger. * Maintains current ledger (m_current) as a fast hash-map. This is hashed only when required (i.e. to create or verify a block). @@ -125,6 +90,8 @@ public: /// Copy state object. State& operator=(State const& _s); + ~State(); + /// Set the coinbase address for any transactions we do. /// This causes a complete reset of current block. void setAddress(Address _coinbaseAddress) { m_ourAddress = _coinbaseAddress; resetCurrent(); } @@ -138,6 +105,8 @@ public: /// @returns the set containing all addresses currently in use in Ethereum. std::map addresses() const; + BlockInfo const& info() const { return m_currentBlock; } + /// @brief Checks that mining the current object will result in a valid block. /// Effectively attempts to import the serialised block. /// @returns true if all is ok. If it's false, worry. @@ -155,7 +124,7 @@ public: /// This function is thread-safe. You can safely have other interactions with this object while it is happening. /// @param _msTimeout Timeout before return in milliseconds. /// @returns Information on the mining. - MineInfo mine(uint _msTimeout = 1000); + MineInfo mine(unsigned _msTimeout = 1000, bool _turbo = false); /** Commit to DB and build the final block if the previous call to mine()'s result is completion. * Typically looks like: @@ -178,10 +147,10 @@ public: // TODO: Cleaner interface. /// Sync our transactions, killing those from the queue that we have and assimilating those that we don't. - /// @returns true if we uncommitted from mining during the operation. - /// @a o_changed boolean pointer, the value of which will be set to true if the state changed and the pointer - /// is non-null - bool sync(TransactionQueue& _tq, bool* o_changed = nullptr); + /// @returns a list of bloom filters one for each transaction placed from the queue into the state. + /// @a o_transactionQueueChanged boolean pointer, the value of which will be set to true if the transaction queue + /// changed and the pointer is non-null + h256s sync(TransactionQueue& _tq, bool* o_transactionQueueChanged = nullptr); /// Like sync but only operate on _tq, killing the invalid/old ones. bool cull(TransactionQueue& _tq) const; @@ -190,6 +159,9 @@ public: u256 execute(bytes const& _rlp, bytes* o_output = nullptr, bool _commit = true) { return execute(&_rlp, o_output, _commit); } u256 execute(bytesConstRef _rlp, bytes* o_output = nullptr, bool _commit = true); + /// Get the remaining gas limit in this block. + u256 gasLimitRemaining() const { return m_currentBlock.gasLimit - gasUsed(); } + /// Check if the address is in use. bool addressInUse(Address _address) const; @@ -262,15 +234,6 @@ public: /// @return the difference between this state (origin) and @a _c (destination). StateDiff diff(State const& _c) const; - /// Get the fee associated for a transaction with the given data. - u256 txGas(uint _dataCount, u256 _gas = 0) const { return c_txDataGas * _dataCount + c_txGas + _gas; } - - /// Get the fee associated for a contract created with the given data. - u256 createGas(uint _dataCount, u256 _gas = 0) const { return txGas(_dataCount, _gas); } - - /// Get the fee associated for a normal transaction. - u256 callGas(uint _dataCount, u256 _gas = 0) const { return txGas(_dataCount, _gas); } - /// Sync our state with the block chain. /// This basically involves wiping ourselves if we've been superceded and rebuilding from the transaction queue. bool sync(BlockChain const& _bc); @@ -303,20 +266,20 @@ private: /// Commit all changes waiting in the address cache to the DB. void commit(); - /// Execute the given block, assuming it corresponds to m_currentBlock. If _grandParent is passed, it will be used to check the uncles. + /// Execute the given block, assuming it corresponds to m_currentBlock. If _bc is passed, it will be used to check the uncles. /// Throws on failure. - u256 enact(bytesConstRef _block, BlockInfo const& _grandParent = BlockInfo(), bool _checkNonce = true); + u256 enact(bytesConstRef _block, BlockChain const* _bc = nullptr, bool _checkNonce = true); // Two priviledged entry points for the VM (these don't get added to the Transaction lists): // We assume all instrinsic fees are paid up before this point. /// Execute a contract-creation transaction. - h160 create(Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _originAddress = Address(), std::set
* o_suicides = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0); + h160 create(Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _originAddress = Address(), std::set
* o_suicides = nullptr, PostList* o_posts = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0); /// Execute a call. /// @a _gas points to the amount of gas to use for the call, and will lower it accordingly. /// @returns false if the call ran out of gas before completion. true otherwise. - bool call(Address _myAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256* _gas, bytesRef _out, Address _originAddress = Address(), std::set
* o_suicides = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0); + bool call(Address _myAddress, Address _codeAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256* _gas, bytesRef _out, Address _originAddress = Address(), std::set
* o_suicides = nullptr, PostList* o_posts = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0); /// Sets m_currentBlock to a clean state, (i.e. no change from m_previousBlock). void resetCurrent(); @@ -336,7 +299,6 @@ private: TrieDB m_state; ///< Our state tree, as an OverlayDB DB. std::vector m_transactions; ///< The current list of transactions that we've included in the state. std::set m_transactionSet; ///< The set of transaction hashes that we've included in the state. -// GenericTrieDB m_transactionManifest; ///< The transactions trie; saved from the last commitToMine, or invalid/empty if commitToMine was never called. OverlayDB m_lastTx; mutable std::map m_cache; ///< Our address cache. This stores the states of each address that has (or at least might have) been changed. @@ -360,8 +322,6 @@ private: }; std::ostream& operator<<(std::ostream& _out, State const& _s); -std::ostream& operator<<(std::ostream& _out, StateDiff const& _s); -std::ostream& operator<<(std::ostream& _out, AccountDiff const& _s); template void commit(std::map const& _cache, DB& _db, TrieDB& _state) @@ -391,15 +351,21 @@ void commit(std::map const& _cache, DB& _db, TrieDB -#include -#include +#include +#include #include #include "Transaction.h" using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; #define ETH_ADDRESS_DEBUG 0 -Transaction::Transaction(bytesConstRef _rlpData) +Transaction::Transaction(bytesConstRef _rlpData, bool _checkSender) { int field = 0; RLP rlp(_rlpData); @@ -42,6 +43,8 @@ Transaction::Transaction(bytesConstRef _rlpData) value = rlp[field = 4].toInt(); data = rlp[field = 5].toBytes(); vrs = Signature{ rlp[field = 6].toInt(), rlp[field = 7].toInt(), rlp[field = 8].toInt() }; + if (_checkSender) + m_sender = sender(); } catch (RLPException const&) { @@ -63,27 +66,30 @@ Address Transaction::safeSender() const noexcept Address Transaction::sender() const { - secp256k1_start(); + if (!m_sender) + { + secp256k1_start(); - h256 sig[2] = { vrs.r, vrs.s }; - h256 msg = sha3(false); + h256 sig[2] = { vrs.r, vrs.s }; + h256 msg = sha3(false); - byte pubkey[65]; - int pubkeylen = 65; - if (!secp256k1_ecdsa_recover_compact(msg.data(), 32, sig[0].data(), pubkey, &pubkeylen, 0, (int)vrs.v - 27)) - throw InvalidSignature(); + byte pubkey[65]; + int pubkeylen = 65; + if (!secp256k1_ecdsa_recover_compact(msg.data(), 32, sig[0].data(), pubkey, &pubkeylen, 0, (int)vrs.v - 27)) + throw InvalidSignature(); - // TODO: check right160 is correct and shouldn't be left160. - auto ret = right160(eth::sha3(bytesConstRef(&(pubkey[1]), 64))); + // TODO: check right160 is correct and shouldn't be left160. + m_sender = right160(dev::eth::sha3(bytesConstRef(&(pubkey[1]), 64))); #if ETH_ADDRESS_DEBUG - cout << "---- RECOVER -------------------------------" << endl; - cout << "MSG: " << msg << endl; - cout << "R S V: " << sig[0] << " " << sig[1] << " " << (int)(vrs.v - 27) << "+27" << endl; - cout << "PUB: " << toHex(bytesConstRef(&(pubkey[1]), 64)) << endl; - cout << "ADR: " << ret << endl; + cout << "---- RECOVER -------------------------------" << endl; + cout << "MSG: " << msg << endl; + cout << "R S V: " << sig[0] << " " << sig[1] << " " << (int)(vrs.v - 27) << "+27" << endl; + cout << "PUB: " << toHex(bytesConstRef(&(pubkey[1]), 64)) << endl; + cout << "ADR: " << m_sender << endl; #endif - return ret; + } + return m_sender; } void Transaction::sign(Secret _priv) @@ -114,7 +120,12 @@ void Transaction::sign(Secret _priv) void Transaction::fillStream(RLPStream& _s, bool _sig) const { _s.appendList((_sig ? 3 : 0) + 6); - _s << nonce << gasPrice << gas << receiveAddress << value << data; + _s << nonce << gasPrice << gas; + if (receiveAddress) + _s << receiveAddress; + else + _s << ""; + _s << value << data; if (_sig) _s << vrs.v << vrs.r << vrs.s; } diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h index e3384c540..5b327d96b 100644 --- a/libethereum/Transaction.h +++ b/libethereum/Transaction.h @@ -21,10 +21,12 @@ #pragma once -#include -#include +#include +#include #include +namespace dev +{ namespace eth { @@ -38,8 +40,8 @@ struct Signature struct Transaction { Transaction() {} - Transaction(bytesConstRef _rlp); - Transaction(bytes const& _rlp): Transaction(&_rlp) {} + Transaction(bytesConstRef _rlp, bool _checkSender = false); + Transaction(bytes const& _rlp, bool _checkSender = false): Transaction(&_rlp, _checkSender) {} bool operator==(Transaction const& _c) const { return receiveAddress == _c.receiveAddress && value == _c.value && data == _c.data; } bool operator!=(Transaction const& _c) const { return !operator==(_c); } @@ -55,8 +57,8 @@ struct Transaction Signature vrs; ///< The signature of the transaction. Encodes the sender. Address safeSender() const noexcept; ///< Like sender() but will never throw. - Address sender() const; ///< Determine the sender of the transaction from the signature (and hash). - void sign(Secret _priv); ///< Sign the transaction. + Address sender() const; ///< Determine the sender of the transaction from the signature (and hash). + void sign(Secret _priv); ///< Sign the transaction. bool isCreation() const { return !receiveAddress; } @@ -65,8 +67,11 @@ struct Transaction void fillStream(RLPStream& _s, bool _sig = true) const; bytes rlp(bool _sig = true) const { RLPStream s; fillStream(s, _sig); return s.out(); } std::string rlpString(bool _sig = true) const { return asString(rlp(_sig)); } - h256 sha3(bool _sig = true) const { RLPStream s; fillStream(s, _sig); return eth::sha3(s.out()); } - bytes sha3Bytes(bool _sig = true) const { RLPStream s; fillStream(s, _sig); return eth::sha3Bytes(s.out()); } + h256 sha3(bool _sig = true) const { RLPStream s; fillStream(s, _sig); return dev::eth::sha3(s.out()); } + bytes sha3Bytes(bool _sig = true) const { RLPStream s; fillStream(s, _sig); return dev::eth::sha3Bytes(s.out()); } + +private: + mutable Address m_sender; }; using Transactions = std::vector; @@ -91,5 +96,4 @@ inline std::ostream& operator<<(std::ostream& _out, Transaction const& _t) } } - - +} diff --git a/libethereum/TransactionQueue.cpp b/libethereum/TransactionQueue.cpp index f63384926..f15cefc26 100644 --- a/libethereum/TransactionQueue.cpp +++ b/libethereum/TransactionQueue.cpp @@ -21,30 +21,33 @@ #include "TransactionQueue.h" -#include +#include #include #include "Transaction.h" using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; -bool TransactionQueue::import(bytesConstRef _block) +bool TransactionQueue::import(bytesConstRef _transactionRLP) { // Check if we already know this transaction. - h256 h = sha3(_block); - if (m_data.count(h)) + h256 h = sha3(_transactionRLP); + + UpgradableGuard l(m_lock); + if (m_known.count(h)) return false; try { - // Check validity of _block as a transaction. To do this we just deserialise and attempt to determine the sender. If it doesn't work, the signature is bad. + // Check validity of _transactionRLP as a transaction. To do this we just deserialise and attempt to determine the sender. + // If it doesn't work, the signature is bad. // The transaction's nonce may yet be invalid (or, it could be "valid" but we may be missing a marginally older transaction). - Transaction t(_block); - auto s = t.sender(); - if (m_interest.count(s)) - m_interestQueue.push_back(t); + Transaction t(_transactionRLP, true); + UpgradeGuard ul(l); // If valid, append to blocks. - m_data[h] = _block.toBytes(); + m_current[h] = _transactionRLP.toBytes(); + m_known.insert(h); } catch (InvalidTransactionFormat const& _e) { @@ -62,17 +65,42 @@ bool TransactionQueue::import(bytesConstRef _block) void TransactionQueue::setFuture(std::pair const& _t) { - if (m_data.count(_t.first)) + WriteGuard l(m_lock); + if (m_current.count(_t.first)) { - m_data.erase(_t.first); - m_future.insert(make_pair(Transaction(_t.second).sender(), _t)); + m_current.erase(_t.first); + m_unknown.insert(make_pair(Transaction(_t.second).sender(), _t)); } } void TransactionQueue::noteGood(std::pair const& _t) { - auto r = m_future.equal_range(Transaction(_t.second).sender()); + WriteGuard l(m_lock); + auto r = m_unknown.equal_range(Transaction(_t.second).sender()); for (auto it = r.first; it != r.second; ++it) - m_data.insert(_t); - m_future.erase(r.first, r.second); + m_current.insert(it->second); + m_unknown.erase(r.first, r.second); +} + +void TransactionQueue::drop(h256 _txHash) +{ + UpgradableGuard l(m_lock); + + if (!m_known.count(_txHash)) + return; + + UpgradeGuard ul(l); + m_known.erase(_txHash); + + if (m_current.count(_txHash)) + m_current.erase(_txHash); + else + { + for (auto i = m_unknown.begin(); i != m_unknown.end(); ++i) + if (i->second.first == _txHash) + { + m_unknown.erase(i); + break; + } + } } diff --git a/libethereum/TransactionQueue.h b/libethereum/TransactionQueue.h index 0c52370b3..22a420602 100644 --- a/libethereum/TransactionQueue.h +++ b/libethereum/TransactionQueue.h @@ -21,9 +21,13 @@ #pragma once -#include -#include "Transaction.h" +#include +#include +#include "libethcore/CommonEth.h" +#include +namespace dev +{ namespace eth { @@ -31,30 +35,32 @@ class BlockChain; /** * @brief A queue of Transactions, each stored as RLP. + * @threadsafe */ class TransactionQueue { public: - bool attemptImport(bytesConstRef _block) { try { import(_block); return true; } catch (...) { return false; } } - bool attemptImport(bytes const& _block) { try { import(&_block); return true; } catch (...) { return false; } } - bool import(bytesConstRef _block); - void drop(h256 _txHash) { m_data.erase(_txHash); } - std::map const& transactions() const { return m_data; } + bool attemptImport(bytesConstRef _tx) { try { import(_tx); return true; } catch (...) { return false; } } + bool attemptImport(bytes const& _tx) { return attemptImport(&_tx); } + bool import(bytesConstRef _tx); + + void drop(h256 _txHash); + + std::map transactions() const { ReadGuard l(m_lock); return m_current; } + std::pair items() const { ReadGuard l(m_lock); return std::make_pair(m_current.size(), m_unknown.size()); } void setFuture(std::pair const& _t); void noteGood(std::pair const& _t); - Transactions interestQueue() { Transactions ret; swap(ret, m_interestQueue); return ret; } - void pushInterest(Address _a) { m_interest[_a]++; } - void popInterest(Address _a) { if (m_interest[_a] > 1) m_interest[_a]--; else if (m_interest[_a]) m_interest.erase(_a); } + void clear() { WriteGuard l(m_lock); m_known.clear(); m_current.clear(); m_unknown.clear(); } private: - std::map m_data; ///< Map of SHA3(tx) to tx. - Transactions m_interestQueue; - std::map m_interest; - std::multimap> m_future; ///< For transactions that have a future nonce; we map their sender address to the tx stuff, and insert once the sender has a valid TX. + mutable boost::shared_mutex m_lock; ///< General lock. + std::set m_known; ///< Hashes of transactions in both sets. + std::map m_current; ///< Map of SHA3(tx) to tx. + std::multimap> m_unknown; ///< For transactions that have a future nonce; we map their sender address to the tx stuff, and insert once the sender has a valid TX. }; } - +} diff --git a/libethereum/Utility.cpp b/libethereum/Utility.cpp new file mode 100644 index 000000000..b6653fe39 --- /dev/null +++ b/libethereum/Utility.cpp @@ -0,0 +1,83 @@ +/* + 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 . +*/ +/** @file Utility.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "Utility.h" + +#include +#include +using namespace std; +using namespace dev; +using namespace dev::eth; + +bytes dev::eth::parseData(string const& _args) +{ + bytes m_data; + + boost::smatch what; + static const boost::regex r("(@|\\$)?\"([^\"]*)\"(\\s.*)?"); + static const boost::regex d("(@|\\$)?([0-9]+)(\\s*(ether)|(finney)|(szabo))?(\\s.*)?"); + static const boost::regex h("(@|\\$)?(0x)?(([a-fA-F0-9])+)(\\s.*)?"); + + string s = _args; + while (s.size()) + if (boost::regex_match(s, what, d)) + { + u256 v((string)what[2]); + if (what[6] == "szabo") + v *= dev::eth::szabo; + else if (what[5] == "finney") + v *= dev::eth::finney; + else if (what[4] == "ether") + v *= dev::eth::ether; + bytes bs = dev::toCompactBigEndian(v); + if (what[1] != "$") + for (auto i = bs.size(); i < 32; ++i) + m_data.push_back(0); + for (auto b: bs) + m_data.push_back(b); + s = what[7]; + } + else if (boost::regex_match(s, what, h)) + { + bytes bs = fromHex(((what[3].length() & 1) ? "0" : "") + what[3]); + if (what[1] != "$") + for (auto i = bs.size(); i < 32; ++i) + m_data.push_back(0); + for (auto b: bs) + m_data.push_back(b); + s = what[5]; + } + else if (boost::regex_match(s, what, r)) + { + for (auto i: (string)what[2]) + m_data.push_back((byte)i); + if (what[1] != "$") + for (int i = what[2].length(); i < 32; ++i) + m_data.push_back(0); + else + m_data.push_back(0); + s = what[3]; + } + else + s = s.substr(1); + + return m_data; +} diff --git a/libethereum/Utility.h b/libethereum/Utility.h new file mode 100644 index 000000000..93c3d54c8 --- /dev/null +++ b/libethereum/Utility.h @@ -0,0 +1,35 @@ +/* + 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 . +*/ +/** @file Utility.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include + +namespace dev +{ +namespace eth +{ + +bytes parseData(std::string const& _args); + +} +} diff --git a/libethereumx/CMakeLists.txt b/libethereumx/CMakeLists.txt new file mode 100644 index 000000000..1c74bf2c3 --- /dev/null +++ b/libethereumx/CMakeLists.txt @@ -0,0 +1,67 @@ +cmake_policy(SET CMP0015 NEW) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB") + +aux_source_directory(. SRC_LIST) + +set(EXECUTABLE ethereumx) + +# set(CMAKE_INSTALL_PREFIX ../lib) +if(ETH_STATIC) + add_library(${EXECUTABLE} STATIC ${SRC_LIST}) +else() + add_library(${EXECUTABLE} SHARED ${SRC_LIST}) +endif() + +file(GLOB HEADERS "*.h") + +include_directories(..) + +target_link_libraries(${EXECUTABLE} ethereum) +target_link_libraries(${EXECUTABLE} evm) +target_link_libraries(${EXECUTABLE} lll) +target_link_libraries(${EXECUTABLE} ethcore) +target_link_libraries(${EXECUTABLE} secp256k1) +if(MINIUPNPC_LS) +target_link_libraries(${EXECUTABLE} ${MINIUPNPC_LS}) +endif() +target_link_libraries(${EXECUTABLE} ${LEVELDB_LS}) +target_link_libraries(${EXECUTABLE} ${CRYPTOPP_LS}) +target_link_libraries(${EXECUTABLE} gmp) + +if("${TARGET_PLATFORM}" STREQUAL "w64") + target_link_libraries(${EXECUTABLE} boost_system-mt-s) + target_link_libraries(${EXECUTABLE} boost_regex-mt-s) + target_link_libraries(${EXECUTABLE} boost_filesystem-mt-s) + target_link_libraries(${EXECUTABLE} boost_thread_win32-mt-s) + target_link_libraries(${EXECUTABLE} iphlpapi) + target_link_libraries(${EXECUTABLE} ws2_32) + target_link_libraries(${EXECUTABLE} mswsock) + target_link_libraries(${EXECUTABLE} shlwapi) +elseif (APPLE) + # Latest mavericks boost libraries only come with -mt + target_link_libraries(${EXECUTABLE} boost_system-mt) + target_link_libraries(${EXECUTABLE} boost_regex-mt) + target_link_libraries(${EXECUTABLE} boost_filesystem-mt) + target_link_libraries(${EXECUTABLE} boost_thread-mt) + find_package(Threads REQUIRED) + target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) +elseif (UNIX) + target_link_libraries(${EXECUTABLE} ${Boost_SYSTEM_LIBRARY}) + target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARY}) + target_link_libraries(${EXECUTABLE} ${Boost_FILESYSTEM_LIBRARY}) + target_link_libraries(${EXECUTABLE} ${Boost_THREAD_LIBRARY}) + target_link_libraries(${EXECUTABLE} ${Boost_DATE_TIME_LIBRARY}) + target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) +else () + target_link_libraries(${EXECUTABLE} boost_system) + target_link_libraries(${EXECUTABLE} boost_regex) + target_link_libraries(${EXECUTABLE} boost_filesystem) + target_link_libraries(${EXECUTABLE} boost_thread) + find_package(Threads REQUIRED) + target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) +endif () + +install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) +install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) + diff --git a/libethereumx/Ethereum.cpp b/libethereumx/Ethereum.cpp new file mode 100644 index 000000000..d1968b7fa --- /dev/null +++ b/libethereumx/Ethereum.cpp @@ -0,0 +1,131 @@ +/* + 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 . +*/ +/** @file Ethereum.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "Ethereum.h" + +#include +#include +using namespace std; +using namespace dev; +using namespace dev::eth; +using namespace p2p; + +Ethereum::Ethereum() +{ + ensureReady(); +} + +void Ethereum::ensureReady() +{ + while (!m_client && connectionOpen()) + try + { + m_client = unique_ptr(new Client("+ethereum+")); + if (m_client) + startRPCServer(); + } + catch (DatabaseAlreadyOpen) + { + connectToRPCServer(); + } +} + +Ethereum::~Ethereum() +{ +} + +bool Ethereum::connectionOpen() const +{ + return false; +} + +void Ethereum::connectToRPCServer() +{ +} + +void Ethereum::startRPCServer() +{ +} + +void Ethereum::flushTransactions() +{ +} + +std::vector Ethereum::peers() +{ + return std::vector(); +} + +size_t Ethereum::peerCount() const +{ + return 0; +} + +void Ethereum::connect(std::string const& _seedHost, unsigned short _port) +{ +} + +void Ethereum::transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) +{ +} + +bytes Ethereum::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) +{ + return bytes(); +} + +Address Ethereum::transact(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) +{ + return Address(); +} + +void Ethereum::inject(bytesConstRef _rlp) +{ +} + +u256 Ethereum::balanceAt(Address _a, int _block) const +{ + return u256(); +} + +PastMessages Ethereum::messages(MessageFilter const& _filter) const +{ +} + +std::map Ethereum::storageAt(Address _a, int _block) const +{ + return std::map(); +} + +u256 Ethereum::countAt(Address _a, int _block) const +{ + return u256(); +} + +u256 Ethereum::stateAt(Address _a, u256 _l, int _block) const +{ + return u256(); +} + +bytes Ethereum::codeAt(Address _a, int _block) const +{ + return bytes(); +} diff --git a/libethereumx/Ethereum.h b/libethereumx/Ethereum.h new file mode 100644 index 000000000..517010b93 --- /dev/null +++ b/libethereumx/Ethereum.h @@ -0,0 +1,148 @@ +/* + 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 . +*/ +/** @file Ethereum.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace dev +{ +namespace eth +{ + +class Client; + +/** + * @brief Main API hub for interfacing with Ethereum. + * This class is automatically able to share a single machine-wide Client instance with other + * instances, cross-process. + * + * Other than that, it provides much the same subset of functionality as Client. + */ +class Ethereum +{ + friend class Miner; + +public: + /// Constructor. After this, everything should be set up to go. + Ethereum(); + + /// Destructor. + ~Ethereum(); + + /// Submits the given message-call transaction. + void transact(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo); + + /// Submits a new contract-creation transaction. + /// @returns the new contract's address (assuming it all goes through). + Address transact(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas = 10000, u256 _gasPrice = 10 * szabo); + + /// Injects the RLP-encoded transaction given by the _rlp into the transaction queue directly. + void inject(bytesConstRef _rlp); + + /// Blocks until all pending transactions have been processed. + void flushTransactions(); + + /// Makes the given call. Nothing is recorded into the state. + bytes call(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo); + + // Informational stuff + + // [NEW API] + + int getDefault() const { return m_default; } + void setDefault(int _block) { m_default = _block; } + + u256 balanceAt(Address _a) const { return balanceAt(_a, m_default); } + u256 countAt(Address _a) const { return countAt(_a, m_default); } + u256 stateAt(Address _a, u256 _l) const { return stateAt(_a, _l, m_default); } + bytes codeAt(Address _a) const { return codeAt(_a, m_default); } + std::map storageAt(Address _a) const { return storageAt(_a, m_default); } + + u256 balanceAt(Address _a, int _block) const; + u256 countAt(Address _a, int _block) const; + u256 stateAt(Address _a, u256 _l, int _block) const; + bytes codeAt(Address _a, int _block) const; + std::map storageAt(Address _a, int _block) const; + + PastMessages messages(MessageFilter const& _filter) const; + + // [EXTRA API]: +#if 0 + /// Get a map containing each of the pending transactions. + /// @TODO: Remove in favour of transactions(). + Transactions pending() const { return m_postMine.pending(); } + + /// Differences between transactions. + StateDiff diff(unsigned _txi) const { return diff(_txi, m_default); } + StateDiff diff(unsigned _txi, h256 _block) const; + StateDiff diff(unsigned _txi, int _block) const; + + /// Get a list of all active addresses. + std::vector
addresses() const { return addresses(m_default); } + std::vector
addresses(int _block) const; + + /// Get the fee associated for a transaction with the given data. + static u256 txGas(uint _dataCount, u256 _gas = 0) { return c_txDataGas * _dataCount + c_txGas + _gas; } + + /// Get the remaining gas limit in this block. + u256 gasLimitRemaining() const { return m_postMine.gasLimitRemaining(); } +#endif + // Network stuff: + + /// Get information on the current peer set. + std::vector peers(); + /// Same as peers().size(), but more efficient. + size_t peerCount() const; + + /// Connect to a particular peer. + void connect(std::string const& _seedHost, unsigned short _port = 30303); + +private: + /// Ensure that through either the client or the + void ensureReady(); + /// Check to see if the client/server connection is open. + bool connectionOpen() const; + /// Start the API client. + void connectToRPCServer(); + /// Start the API server. + void startRPCServer(); + + std::unique_ptr m_client; + + int m_default = -1; +}; + +} +} diff --git a/libevm/CMakeLists.txt b/libevm/CMakeLists.txt index 2365821b4..f19119f83 100644 --- a/libevm/CMakeLists.txt +++ b/libevm/CMakeLists.txt @@ -18,8 +18,9 @@ file(GLOB HEADERS "*.h") include_directories(..) target_link_libraries(${EXECUTABLE} ethcore) +target_link_libraries(${EXECUTABLE} devcrypto) target_link_libraries(${EXECUTABLE} evmface) -target_link_libraries(${EXECUTABLE} ethential) +target_link_libraries(${EXECUTABLE} devcore) target_link_libraries(${EXECUTABLE} secp256k1) target_link_libraries(${EXECUTABLE} gmp) if(MINIUPNPC_LS) diff --git a/libevm/ExtVMFace.cpp b/libevm/ExtVMFace.cpp index bdaa8f8a6..7c938417a 100644 --- a/libevm/ExtVMFace.cpp +++ b/libevm/ExtVMFace.cpp @@ -22,7 +22,8 @@ #include "ExtVMFace.h" using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; ExtVMFace::ExtVMFace(Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock): myAddress(_myAddress), diff --git a/libevm/ExtVMFace.h b/libevm/ExtVMFace.h index 4890a5979..19cc944a4 100644 --- a/libevm/ExtVMFace.h +++ b/libevm/ExtVMFace.h @@ -21,14 +21,29 @@ #pragma once -#include +#include #include #include #include +namespace dev +{ namespace eth { +struct Post +{ + Address from; + Address to; + u256 value; + bytes data; + u256 gas; +}; + +using PostList = std::list; + +using OnOpFunc = std::function; + /** * @brief A null implementation of the class for specifying VM externalities. */ @@ -53,6 +68,9 @@ public: /// Read address's balance. u256 balance(Address) { return 0; } + /// Read address's code. + bytes const& codeAt(Address) { return NullBytes; } + /// Subtract amount from account's balance. void subBalance(u256) {} @@ -66,11 +84,17 @@ public: h160 create(u256, u256*, bytesConstRef, bytesConstRef) { return h160(); } /// Make a new message call. - bool call(Address, u256, bytesConstRef, u256*, bytesRef) { return false; } + bool call(Address, u256, bytesConstRef, u256*, bytesRef, OnOpFunc const&, Address, Address) { return false; } + + /// Post a new message call. + void post(Address _to, u256 _value, bytesConstRef _data, u256 _gas) { posts.push_back(Post({myAddress, _to, _value, _data.toBytes(), _gas})); } /// Revert any changes made (by any of the other calls). void revert() {} + /// Execute any posts that may exist, including those that are incurred as a result of earlier posts. + void doPosts() {} + Address myAddress; ///< Address associated with executing code (a contract, or contract-to-be). Address caller; ///< Address which sent the message (either equal to origin or a contract). Address origin; ///< Original transactor. @@ -81,8 +105,8 @@ public: BlockInfo previousBlock; ///< The previous block's information. BlockInfo currentBlock; ///< The current block's information. std::set
suicides; ///< Any accounts that have suicided. + std::list posts; ///< Any posts that have been made. }; -typedef std::function OnOpFunc; - +} } diff --git a/libevm/FeeStructure.cpp b/libevm/FeeStructure.cpp index f7dbbfbf8..d29b9fef9 100644 --- a/libevm/FeeStructure.cpp +++ b/libevm/FeeStructure.cpp @@ -22,15 +22,16 @@ #include "FeeStructure.h" using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; -u256 const eth::c_stepGas = 1; -u256 const eth::c_balanceGas = 20; -u256 const eth::c_sha3Gas = 20; -u256 const eth::c_sloadGas = 20; -u256 const eth::c_sstoreGas = 100; -u256 const eth::c_createGas = 100; -u256 const eth::c_callGas = 20; -u256 const eth::c_memoryGas = 1; -u256 const eth::c_txDataGas = 5; -u256 const eth::c_txGas = 500; +u256 const dev::eth::c_stepGas = 1; +u256 const dev::eth::c_balanceGas = 20; +u256 const dev::eth::c_sha3Gas = 20; +u256 const dev::eth::c_sloadGas = 20; +u256 const dev::eth::c_sstoreGas = 100; +u256 const dev::eth::c_createGas = 100; +u256 const dev::eth::c_callGas = 20; +u256 const dev::eth::c_memoryGas = 1; +u256 const dev::eth::c_txDataGas = 5; +u256 const dev::eth::c_txGas = 500; diff --git a/libevm/FeeStructure.h b/libevm/FeeStructure.h index 341c81f8c..76be9a398 100644 --- a/libevm/FeeStructure.h +++ b/libevm/FeeStructure.h @@ -21,8 +21,10 @@ #pragma once -#include +#include +namespace dev +{ namespace eth { @@ -38,3 +40,4 @@ extern u256 const c_txDataGas; ///< Per byte of data attached to a transaction extern u256 const c_txGas; ///< Per transaction. NOTE: Not payable on data of calls between transactions. } +} diff --git a/libevm/VM.cpp b/libevm/VM.cpp index 5383f4906..e47237d5a 100644 --- a/libevm/VM.cpp +++ b/libevm/VM.cpp @@ -22,7 +22,8 @@ #include "VM.h" using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; void VM::reset(u256 _gas) { diff --git a/libevm/VM.h b/libevm/VM.h index 15f05fd26..cbecc3ee7 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -22,14 +22,16 @@ #pragma once #include -#include +#include #include #include -#include +#include #include #include "FeeStructure.h" #include "ExtVMFace.h" +namespace dev +{ namespace eth { @@ -39,7 +41,6 @@ class BreakPointHit: public VMException {}; class BadInstruction: public VMException {}; class OutOfGas: public VMException {}; class StackTooSmall: public VMException { public: StackTooSmall(u256 _req, u256 _got): req(_req), got(_got) {} u256 req; u256 got; }; -class OperandOutOfRange: public VMException { public: OperandOutOfRange(u256 _min, u256 _max, u256 _got): mn(_min), mx(_max), got(_got) {} u256 mn; u256 mx; u256 got; }; // Convert from a 256-bit integer stack/memory entry into a 160-bit Address hash. // Currently we just pull out the right (low-order in BE) 160-bits. @@ -87,8 +88,10 @@ private: } // INLINE: -template eth::bytesConstRef eth::VM::go(Ext& _ext, OnOpFunc const& _onOp, uint64_t _steps) +template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc const& _onOp, uint64_t _steps) { + auto memNeed = [](dev::u256 _offset, dev::u256 _size) { return _size ? _offset + _size : 0; }; + u256 nextPC = m_curPC + 1; auto osteps = _steps; for (bool stopped = false; !stopped && _steps--; m_curPC = nextPC, nextPC = m_curPC + 1) @@ -98,7 +101,7 @@ template eth::bytesConstRef eth::VM::go(Ext& _ext, OnOpFunc const& _ // FEES... bigint runGas = c_stepGas; - unsigned newTempSize = (unsigned)m_temp.size(); + bigint newTempSize = m_temp.size(); switch (inst) { case Instruction::STOP: @@ -126,49 +129,65 @@ template eth::bytesConstRef eth::VM::go(Ext& _ext, OnOpFunc const& _ // These all operate on memory and therefore potentially expand it: case Instruction::MSTORE: require(2); - newTempSize = (unsigned)m_stack.back() + 32; + newTempSize = m_stack.back() + 32; break; case Instruction::MSTORE8: require(2); - newTempSize = (unsigned)m_stack.back() + 1; + newTempSize = m_stack.back() + 1; break; case Instruction::MLOAD: require(1); - newTempSize = (unsigned)m_stack.back() + 32; + newTempSize = m_stack.back() + 32; break; case Instruction::RETURN: require(2); - newTempSize = (unsigned)m_stack.back() + (unsigned)m_stack[m_stack.size() - 2]; + newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 2]); break; case Instruction::SHA3: require(2); runGas = c_sha3Gas; - newTempSize = (unsigned)m_stack.back() + (unsigned)m_stack[m_stack.size() - 2]; + newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 2]); break; case Instruction::CALLDATACOPY: require(3); - newTempSize = (unsigned)m_stack.back() + (unsigned)m_stack[m_stack.size() - 3]; + newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 3]); break; case Instruction::CODECOPY: require(3); - newTempSize = (unsigned)m_stack.back() + (unsigned)m_stack[m_stack.size() - 3]; + newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 3]); break; - + case Instruction::EXTCODECOPY: + require(4); + newTempSize = memNeed(m_stack[m_stack.size() - 2], m_stack[m_stack.size() - 4]); + break; + case Instruction::BALANCE: runGas = c_balanceGas; break; case Instruction::CALL: require(7); - runGas = c_callGas + (unsigned)m_stack[m_stack.size() - 1]; - newTempSize = std::max((unsigned)m_stack[m_stack.size() - 6] + (unsigned)m_stack[m_stack.size() - 7], (unsigned)m_stack[m_stack.size() - 4] + (unsigned)m_stack[m_stack.size() - 5]); + runGas = c_callGas + m_stack[m_stack.size() - 1]; + newTempSize = std::max(memNeed(m_stack[m_stack.size() - 6], m_stack[m_stack.size() - 7]), memNeed(m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5])); + break; + + case Instruction::CALLSTATELESS: + require(7); + runGas = c_callGas + m_stack[m_stack.size() - 1]; + newTempSize = std::max(memNeed(m_stack[m_stack.size() - 6], m_stack[m_stack.size() - 7]), memNeed(m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5])); + break; + + case Instruction::POST: + require(5); + runGas = c_callGas + m_stack[m_stack.size() - 1]; + newTempSize = memNeed(m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5]); break; case Instruction::CREATE: { require(3); - unsigned inOff = (unsigned)m_stack[m_stack.size() - 2]; - unsigned inSize = (unsigned)m_stack[m_stack.size() - 3]; + auto inOff = m_stack[m_stack.size() - 2]; + auto inSize = m_stack[m_stack.size() - 3]; newTempSize = inOff + inSize; runGas = c_createGas; break; @@ -183,7 +202,7 @@ template eth::bytesConstRef eth::VM::go(Ext& _ext, OnOpFunc const& _ runGas += c_memoryGas * (newTempSize - m_temp.size()) / 32; if (_onOp) - _onOp(osteps - _steps - 1, inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : 0, runGas, this, &_ext); + _onOp(osteps - _steps - 1, inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : bigint(0), runGas, this, &_ext); if (m_gas < runGas) { @@ -195,7 +214,7 @@ template eth::bytesConstRef eth::VM::go(Ext& _ext, OnOpFunc const& _ m_gas = (u256)((bigint)m_gas - runGas); if (newTempSize > m_temp.size()) - m_temp.resize(newTempSize); + m_temp.resize((size_t)newTempSize); // EXECUTE... switch (inst) @@ -241,9 +260,9 @@ template eth::bytesConstRef eth::VM::go(Ext& _ext, OnOpFunc const& _ { require(2); auto base = m_stack.back(); - unsigned expon = (unsigned)m_stack[m_stack.size() - 2]; + auto expon = m_stack[m_stack.size() - 2]; m_stack.pop_back(); - m_stack.back() = boost::multiprecision::pow(base, expon); + m_stack.back() = (u256)boost::multiprecision::powm((bigint)base, (bigint)expon, bigint(2) << 256); break; } case Instruction::NEG: @@ -296,7 +315,19 @@ template eth::bytesConstRef eth::VM::go(Ext& _ext, OnOpFunc const& _ break; case Instruction::BYTE: require(2); - m_stack[m_stack.size() - 2] = m_stack.back() < 32 ? (m_stack[m_stack.size() - 2] >> (uint)(8 * (31 - m_stack.back()))) & 0xff : 0; + m_stack[m_stack.size() - 2] = m_stack.back() < 32 ? (m_stack[m_stack.size() - 2] >> (unsigned)(8 * (31 - m_stack.back()))) & 0xff : 0; + m_stack.pop_back(); + break; + case Instruction::ADDMOD: + require(3); + m_stack[m_stack.size() - 3] = u256((bigint(m_stack.back()) + bigint(m_stack[m_stack.size() - 2])) % m_stack[m_stack.size() - 3]); + m_stack.pop_back(); + m_stack.pop_back(); + break; + case Instruction::MULMOD: + require(3); + m_stack[m_stack.size() - 3] = u256((bigint(m_stack.back()) * bigint(m_stack[m_stack.size() - 2])) % m_stack[m_stack.size() - 3]); + m_stack.pop_back(); m_stack.pop_back(); break; case Instruction::SHA3: @@ -375,6 +406,26 @@ template eth::bytesConstRef eth::VM::go(Ext& _ext, OnOpFunc const& _ memset(m_temp.data() + mf + el, 0, l - el); break; } + case Instruction::EXTCODESIZE: + require(1); + m_stack.back() = _ext.codeAt(asAddress(m_stack.back())).size(); + break; + case Instruction::EXTCODECOPY: + { + require(4); + Address a = asAddress(m_stack.back()); + m_stack.pop_back(); + unsigned mf = (unsigned)m_stack.back(); + m_stack.pop_back(); + unsigned cf = (unsigned)m_stack.back(); + m_stack.pop_back(); + unsigned l = (unsigned)m_stack.back(); + m_stack.pop_back(); + unsigned el = cf + l > _ext.codeAt(a).size() ? _ext.codeAt(a).size() < cf ? 0 : _ext.codeAt(a).size() - cf : l; + memcpy(m_temp.data() + mf, _ext.codeAt(a).data() + cf, el); + memset(m_temp.data() + mf + el, 0, l - el); + break; + } case Instruction::GASPRICE: m_stack.push_back(_ext.gasPrice); break; @@ -440,39 +491,52 @@ template eth::bytesConstRef eth::VM::go(Ext& _ext, OnOpFunc const& _ require(1); m_stack.pop_back(); break; - case Instruction::DUP: - require(1); - m_stack.push_back(m_stack.back()); - break; - /*case Instruction::DUPN: + case Instruction::DUP1: + case Instruction::DUP2: + case Instruction::DUP3: + case Instruction::DUP4: + case Instruction::DUP5: + case Instruction::DUP6: + case Instruction::DUP7: + case Instruction::DUP8: + case Instruction::DUP9: + case Instruction::DUP10: + case Instruction::DUP11: + case Instruction::DUP12: + case Instruction::DUP13: + case Instruction::DUP14: + case Instruction::DUP15: + case Instruction::DUP16: { - auto s = store(curPC + 1); - if (s == 0 || s > stack.size()) - throw OperandOutOfRange(1, stack.size(), s); - stack.push_back(stack[stack.size() - (uint)s]); - nextPC = curPC + 2; - break; - }*/ - case Instruction::SWAP: + auto n = 1 + (int)inst - (int)Instruction::DUP1; + require(n); + m_stack.push_back(m_stack[m_stack.size() - n]); + break; + } + case Instruction::SWAP1: + case Instruction::SWAP2: + case Instruction::SWAP3: + case Instruction::SWAP4: + case Instruction::SWAP5: + case Instruction::SWAP6: + case Instruction::SWAP7: + case Instruction::SWAP8: + case Instruction::SWAP9: + case Instruction::SWAP10: + case Instruction::SWAP11: + case Instruction::SWAP12: + case Instruction::SWAP13: + case Instruction::SWAP14: + case Instruction::SWAP15: + case Instruction::SWAP16: { - require(2); + unsigned n = (int)inst - (int)Instruction::SWAP1 + 2; + require(n); auto d = m_stack.back(); - m_stack.back() = m_stack[m_stack.size() - 2]; - m_stack[m_stack.size() - 2] = d; + m_stack.back() = m_stack[m_stack.size() - n]; + m_stack[m_stack.size() - n] = d; break; } - /*case Instruction::SWAPN: - { - require(1); - auto d = stack.back(); - auto s = store(curPC + 1); - if (s == 0 || s > stack.size()) - throw OperandOutOfRange(1, stack.size(), s); - stack.back() = stack[stack.size() - (uint)s]; - stack[stack.size() - (uint)s] = d; - nextPC = curPC + 2; - break; - }*/ case Instruction::MLOAD: { require(1); @@ -547,12 +611,13 @@ template eth::bytesConstRef eth::VM::go(Ext& _ext, OnOpFunc const& _ break; } case Instruction::CALL: + case Instruction::CALLSTATELESS: { require(7); u256 gas = m_stack.back(); m_stack.pop_back(); - u160 receiveAddress = asAddress(m_stack.back()); + Address receiveAddress = asAddress(m_stack.back()); m_stack.pop_back(); u256 value = m_stack.back(); m_stack.pop_back(); @@ -569,7 +634,7 @@ template eth::bytesConstRef eth::VM::go(Ext& _ext, OnOpFunc const& _ if (_ext.balance(_ext.myAddress) >= value) { _ext.subBalance(value); - m_stack.push_back(_ext.call(receiveAddress, value, bytesConstRef(m_temp.data() + inOff, inSize), &gas, bytesRef(m_temp.data() + outOff, outSize), _onOp)); + m_stack.push_back(_ext.call(receiveAddress, value, bytesConstRef(m_temp.data() + inOff, inSize), &gas, bytesRef(m_temp.data() + outOff, outSize), _onOp, Address(), inst == Instruction::CALL ? receiveAddress : _ext.myAddress)); } else m_stack.push_back(0); @@ -597,6 +662,29 @@ template eth::bytesConstRef eth::VM::go(Ext& _ext, OnOpFunc const& _ } case Instruction::STOP: return bytesConstRef(); + case Instruction::POST: + { + require(5); + + u256 gas = m_stack.back(); + m_stack.pop_back(); + u160 receiveAddress = asAddress(m_stack.back()); + m_stack.pop_back(); + u256 value = m_stack.back(); + m_stack.pop_back(); + + unsigned inOff = (unsigned)m_stack.back(); + m_stack.pop_back(); + unsigned inSize = (unsigned)m_stack.back(); + m_stack.pop_back(); + + if (_ext.balance(_ext.myAddress) >= value) + { + _ext.subBalance(value); + _ext.post(receiveAddress, value, bytesConstRef(m_temp.data() + inOff, inSize), gas); + } + break; + } default: throw BadInstruction(); } @@ -605,4 +693,4 @@ template eth::bytesConstRef eth::VM::go(Ext& _ext, OnOpFunc const& _ throw StepsDone(); return bytesConstRef(); } - +} diff --git a/libevm/_libevm.cpp b/libevm/_libevm.cpp new file mode 100644 index 000000000..27186cbf2 --- /dev/null +++ b/libevm/_libevm.cpp @@ -0,0 +1,6 @@ +#ifdef _MSC_VER +#include "All.h" +#include "ExtVMFace.cpp" +#include "FeeStructure.cpp" +#include "VM.cpp" +#endif diff --git a/libevmface/CMakeLists.txt b/libevmface/CMakeLists.txt index 93b33b66b..212ce825d 100644 --- a/libevmface/CMakeLists.txt +++ b/libevmface/CMakeLists.txt @@ -17,7 +17,7 @@ file(GLOB HEADERS "*.h") include_directories(..) -target_link_libraries(${EXECUTABLE} ethential) +target_link_libraries(${EXECUTABLE} devcore) if("${TARGET_PLATFORM}" STREQUAL "w64") target_link_libraries(${EXECUTABLE} boost_thread_win32-mt-s) diff --git a/libevmface/Instruction.cpp b/libevmface/Instruction.cpp index 559ebfb5b..7577c2aab 100644 --- a/libevmface/Instruction.cpp +++ b/libevmface/Instruction.cpp @@ -21,11 +21,12 @@ #include "Instruction.h" -#include +#include using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; -const std::map eth::c_instructions = +const std::map dev::eth::c_instructions = { { "STOP", Instruction::STOP }, { "ADD", Instruction::ADD }, @@ -47,6 +48,8 @@ const std::map eth::c_instructions = { "OR", Instruction::OR }, { "XOR", Instruction::XOR }, { "BYTE", Instruction::BYTE }, + { "ADDMOD", Instruction::ADDMOD }, + { "MULMOD", Instruction::MULMOD }, { "SHA3", Instruction::SHA3 }, { "ADDRESS", Instruction::ADDRESS }, { "BALANCE", Instruction::BALANCE }, @@ -59,6 +62,8 @@ const std::map eth::c_instructions = { "CODESIZE", Instruction::CODESIZE }, { "CODECOPY", Instruction::CODECOPY }, { "GASPRICE", Instruction::GASPRICE }, + { "EXTCODESIZE", Instruction::EXTCODESIZE }, + { "EXTCODECOPY", Instruction::EXTCODECOPY }, { "PREVHASH", Instruction::PREVHASH }, { "COINBASE", Instruction::COINBASE }, { "TIMESTAMP", Instruction::TIMESTAMP }, @@ -66,8 +71,6 @@ const std::map eth::c_instructions = { "DIFFICULTY", Instruction::DIFFICULTY }, { "GASLIMIT", Instruction::GASLIMIT }, { "POP", Instruction::POP }, - { "DUP", Instruction::DUP }, - { "SWAP", Instruction::SWAP }, { "MLOAD", Instruction::MLOAD }, { "MSTORE", Instruction::MSTORE }, { "MSTORE8", Instruction::MSTORE8 }, @@ -110,107 +113,177 @@ const std::map eth::c_instructions = { "PUSH30", Instruction::PUSH30 }, { "PUSH31", Instruction::PUSH31 }, { "PUSH32", Instruction::PUSH32 }, + { "DUP1", Instruction::DUP1 }, + { "DUP2", Instruction::DUP2 }, + { "DUP3", Instruction::DUP3 }, + { "DUP4", Instruction::DUP4 }, + { "DUP5", Instruction::DUP5 }, + { "DUP6", Instruction::DUP6 }, + { "DUP7", Instruction::DUP7 }, + { "DUP8", Instruction::DUP8 }, + { "DUP9", Instruction::DUP9 }, + { "DUP10", Instruction::DUP10 }, + { "DUP11", Instruction::DUP11 }, + { "DUP12", Instruction::DUP12 }, + { "DUP13", Instruction::DUP13 }, + { "DUP14", Instruction::DUP14 }, + { "DUP15", Instruction::DUP15 }, + { "DUP16", Instruction::DUP16 }, + { "SWAP1", Instruction::SWAP1 }, + { "SWAP2", Instruction::SWAP2 }, + { "SWAP3", Instruction::SWAP3 }, + { "SWAP4", Instruction::SWAP4 }, + { "SWAP5", Instruction::SWAP5 }, + { "SWAP6", Instruction::SWAP6 }, + { "SWAP7", Instruction::SWAP7 }, + { "SWAP8", Instruction::SWAP8 }, + { "SWAP9", Instruction::SWAP9 }, + { "SWAP10", Instruction::SWAP10 }, + { "SWAP11", Instruction::SWAP11 }, + { "SWAP12", Instruction::SWAP12 }, + { "SWAP13", Instruction::SWAP13 }, + { "SWAP14", Instruction::SWAP14 }, + { "SWAP15", Instruction::SWAP15 }, + { "SWAP16", Instruction::SWAP16 }, { "CREATE", Instruction::CREATE }, { "CALL", Instruction::CALL }, + { "CALLSTATELESS", Instruction::CALLSTATELESS }, { "RETURN", Instruction::RETURN }, + { "POST", Instruction::POST }, { "SUICIDE", Instruction::SUICIDE } }; -const std::map eth::c_instructionInfo = -{ // Add, Args, Ret - { Instruction::STOP, { "STOP", 0, 0, 0 } }, - { Instruction::ADD, { "ADD", 0, 2, 1 } }, - { Instruction::SUB, { "SUB", 0, 2, 1 } }, - { Instruction::MUL, { "MUL", 0, 2, 1 } }, - { Instruction::DIV, { "DIV", 0, 2, 1 } }, - { Instruction::SDIV, { "SDIV", 0, 2, 1 } }, - { Instruction::MOD, { "MOD", 0, 2, 1 } }, - { Instruction::SMOD, { "SMOD", 0, 2, 1 } }, - { Instruction::EXP, { "EXP", 0, 2, 1 } }, - { Instruction::NEG, { "NEG", 0, 1, 1 } }, - { Instruction::LT, { "LT", 0, 2, 1 } }, - { Instruction::GT, { "GT", 0, 2, 1 } }, - { Instruction::SLT, { "SLT", 0, 2, 1 } }, - { Instruction::SGT, { "SGT", 0, 2, 1 } }, - { Instruction::EQ, { "EQ", 0, 2, 1 } }, - { Instruction::NOT, { "NOT", 0, 1, 1 } }, - { Instruction::AND, { "AND", 0, 2, 1 } }, - { Instruction::OR, { "OR", 0, 2, 1 } }, - { Instruction::XOR, { "XOR", 0, 2, 1 } }, - { Instruction::BYTE, { "BYTE", 0, 2, 1 } }, - { Instruction::SHA3, { "SHA3", 0, 2, 1 } }, - { Instruction::ADDRESS, { "ADDRESS", 0, 0, 1 } }, - { Instruction::BALANCE, { "BALANCE", 0, 1, 1 } }, - { Instruction::ORIGIN, { "ORIGIN", 0, 0, 1 } }, - { Instruction::CALLER, { "CALLER", 0, 0, 1 } }, - { Instruction::CALLVALUE, { "CALLVALUE", 0, 0, 1 } }, - { Instruction::CALLDATALOAD, { "CALLDATALOAD", 0, 1, 1 } }, - { Instruction::CALLDATASIZE, { "CALLDATASIZE", 0, 0, 1 } }, - { Instruction::CALLDATACOPY, { "CALLDATACOPY", 0, 3, 0 } }, - { Instruction::CODESIZE, { "CODESIZE", 0, 0, 1 } }, - { Instruction::CODECOPY, { "CODECOPY", 0, 3, 0 } }, - { Instruction::GASPRICE, { "GASPRICE", 0, 0, 1 } }, - { Instruction::PREVHASH, { "PREVHASH", 0, 0, 1 } }, - { Instruction::COINBASE, { "COINBASE", 0, 0, 1 } }, - { Instruction::TIMESTAMP, { "TIMESTAMP", 0, 0, 1 } }, - { Instruction::NUMBER, { "NUMBER", 0, 0, 1 } }, - { Instruction::DIFFICULTY, { "DIFFICULTY", 0, 0, 1 } }, - { Instruction::GASLIMIT, { "GASLIMIT", 0, 0, 1 } }, - { Instruction::POP, { "POP", 0, 1, 0 } }, - { Instruction::DUP, { "DUP", 0, 1, 2 } }, - { Instruction::SWAP, { "SWAP", 0, 2, 2 } }, - { Instruction::MLOAD, { "MLOAD", 0, 1, 1 } }, - { Instruction::MSTORE, { "MSTORE", 0, 2, 0 } }, - { Instruction::MSTORE8, { "MSTORE8", 0, 2, 0 } }, - { Instruction::SLOAD, { "SLOAD", 0, 1, 1 } }, - { Instruction::SSTORE, { "SSTORE", 0, 2, 0 } }, - { Instruction::JUMP, { "JUMP", 0, 1, 0 } }, - { Instruction::JUMPI, { "JUMPI", 0, 2, 0 } }, - { Instruction::PC, { "PC", 0, 0, 1 } }, - { Instruction::MSIZE, { "MSIZE", 0, 0, 1 } }, - { Instruction::GAS, { "GAS", 0, 0, 1 } }, - { Instruction::PUSH1, { "PUSH1", 1, 0, 1 } }, - { Instruction::PUSH2, { "PUSH2", 2, 0, 1 } }, - { Instruction::PUSH3, { "PUSH3", 3, 0, 1 } }, - { Instruction::PUSH4, { "PUSH4", 4, 0, 1 } }, - { Instruction::PUSH5, { "PUSH5", 5, 0, 1 } }, - { Instruction::PUSH6, { "PUSH6", 6, 0, 1 } }, - { Instruction::PUSH7, { "PUSH7", 7, 0, 1 } }, - { Instruction::PUSH8, { "PUSH8", 8, 0, 1 } }, - { Instruction::PUSH9, { "PUSH9", 9, 0, 1 } }, - { Instruction::PUSH10, { "PUSH10", 10, 0, 1 } }, - { Instruction::PUSH11, { "PUSH11", 11, 0, 1 } }, - { Instruction::PUSH12, { "PUSH12", 12, 0, 1 } }, - { Instruction::PUSH13, { "PUSH13", 13, 0, 1 } }, - { Instruction::PUSH14, { "PUSH14", 14, 0, 1 } }, - { Instruction::PUSH15, { "PUSH15", 15, 0, 1 } }, - { Instruction::PUSH16, { "PUSH16", 16, 0, 1 } }, - { Instruction::PUSH17, { "PUSH17", 17, 0, 1 } }, - { Instruction::PUSH18, { "PUSH18", 18, 0, 1 } }, - { Instruction::PUSH19, { "PUSH19", 19, 0, 1 } }, - { Instruction::PUSH20, { "PUSH20", 20, 0, 1 } }, - { Instruction::PUSH21, { "PUSH21", 21, 0, 1 } }, - { Instruction::PUSH22, { "PUSH22", 22, 0, 1 } }, - { Instruction::PUSH23, { "PUSH23", 23, 0, 1 } }, - { Instruction::PUSH24, { "PUSH24", 24, 0, 1 } }, - { Instruction::PUSH25, { "PUSH25", 25, 0, 1 } }, - { Instruction::PUSH26, { "PUSH26", 26, 0, 1 } }, - { Instruction::PUSH27, { "PUSH27", 27, 0, 1 } }, - { Instruction::PUSH28, { "PUSH28", 28, 0, 1 } }, - { Instruction::PUSH29, { "PUSH29", 29, 0, 1 } }, - { Instruction::PUSH30, { "PUSH30", 30, 0, 1 } }, - { Instruction::PUSH31, { "PUSH31", 31, 0, 1 } }, - { Instruction::PUSH32, { "PUSH32", 32, 0, 1 } }, - { Instruction::CREATE, { "CREATE", 0, 3, 1 } }, - { Instruction::CALL, { "CALL", 0, 7, 1 } }, - { Instruction::RETURN, { "RETURN", 0, 2, 0 } }, - { Instruction::SUICIDE, { "SUICIDE", 0, 1, 0} } +static const std::map c_instructionInfo = +{ // Add, Args, Ret + { Instruction::STOP, { "STOP", 0, 0, 0 } }, + { Instruction::ADD, { "ADD", 0, 2, 1 } }, + { Instruction::SUB, { "SUB", 0, 2, 1 } }, + { Instruction::MUL, { "MUL", 0, 2, 1 } }, + { Instruction::DIV, { "DIV", 0, 2, 1 } }, + { Instruction::SDIV, { "SDIV", 0, 2, 1 } }, + { Instruction::MOD, { "MOD", 0, 2, 1 } }, + { Instruction::SMOD, { "SMOD", 0, 2, 1 } }, + { Instruction::EXP, { "EXP", 0, 2, 1 } }, + { Instruction::NEG, { "NEG", 0, 1, 1 } }, + { Instruction::LT, { "LT", 0, 2, 1 } }, + { Instruction::GT, { "GT", 0, 2, 1 } }, + { Instruction::SLT, { "SLT", 0, 2, 1 } }, + { Instruction::SGT, { "SGT", 0, 2, 1 } }, + { Instruction::EQ, { "EQ", 0, 2, 1 } }, + { Instruction::NOT, { "NOT", 0, 1, 1 } }, + { Instruction::AND, { "AND", 0, 2, 1 } }, + { Instruction::OR, { "OR", 0, 2, 1 } }, + { Instruction::XOR, { "XOR", 0, 2, 1 } }, + { Instruction::BYTE, { "BYTE", 0, 2, 1 } }, + { Instruction::ADDMOD, { "ADDMOD", 0, 3, 1 } }, + { Instruction::MULMOD, { "MULMOD", 0, 3, 1 } }, + { Instruction::SHA3, { "SHA3", 0, 2, 1 } }, + { Instruction::ADDRESS, { "ADDRESS", 0, 0, 1 } }, + { Instruction::BALANCE, { "BALANCE", 0, 1, 1 } }, + { Instruction::ORIGIN, { "ORIGIN", 0, 0, 1 } }, + { Instruction::CALLER, { "CALLER", 0, 0, 1 } }, + { Instruction::CALLVALUE, { "CALLVALUE", 0, 0, 1 } }, + { Instruction::CALLDATALOAD,{ "CALLDATALOAD", 0, 1, 1 } }, + { Instruction::CALLDATASIZE,{ "CALLDATASIZE", 0, 0, 1 } }, + { Instruction::CALLDATACOPY,{ "CALLDATACOPY", 0, 3, 0 } }, + { Instruction::CODESIZE, { "CODESIZE", 0, 0, 1 } }, + { Instruction::CODECOPY, { "CODECOPY", 0, 3, 0 } }, + { Instruction::GASPRICE, { "GASPRICE", 0, 0, 1 } }, + { Instruction::EXTCODESIZE, { "EXTCODESIZE", 0, 1, 1 } }, + { Instruction::EXTCODECOPY, { "EXTCODECOPY", 0, 4, 0 } }, + { Instruction::PREVHASH, { "PREVHASH", 0, 0, 1 } }, + { Instruction::COINBASE, { "COINBASE", 0, 0, 1 } }, + { Instruction::TIMESTAMP, { "TIMESTAMP", 0, 0, 1 } }, + { Instruction::NUMBER, { "NUMBER", 0, 0, 1 } }, + { Instruction::DIFFICULTY, { "DIFFICULTY", 0, 0, 1 } }, + { Instruction::GASLIMIT, { "GASLIMIT", 0, 0, 1 } }, + { Instruction::POP, { "POP", 0, 1, 0 } }, + { Instruction::MLOAD, { "MLOAD", 0, 1, 1 } }, + { Instruction::MSTORE, { "MSTORE", 0, 2, 0 } }, + { Instruction::MSTORE8, { "MSTORE8", 0, 2, 0 } }, + { Instruction::SLOAD, { "SLOAD", 0, 1, 1 } }, + { Instruction::SSTORE, { "SSTORE", 0, 2, 0 } }, + { Instruction::JUMP, { "JUMP", 0, 1, 0 } }, + { Instruction::JUMPI, { "JUMPI", 0, 2, 0 } }, + { Instruction::PC, { "PC", 0, 0, 1 } }, + { Instruction::MSIZE, { "MSIZE", 0, 0, 1 } }, + { Instruction::GAS, { "GAS", 0, 0, 1 } }, + { Instruction::PUSH1, { "PUSH1", 1, 0, 1 } }, + { Instruction::PUSH2, { "PUSH2", 2, 0, 1 } }, + { Instruction::PUSH3, { "PUSH3", 3, 0, 1 } }, + { Instruction::PUSH4, { "PUSH4", 4, 0, 1 } }, + { Instruction::PUSH5, { "PUSH5", 5, 0, 1 } }, + { Instruction::PUSH6, { "PUSH6", 6, 0, 1 } }, + { Instruction::PUSH7, { "PUSH7", 7, 0, 1 } }, + { Instruction::PUSH8, { "PUSH8", 8, 0, 1 } }, + { Instruction::PUSH9, { "PUSH9", 9, 0, 1 } }, + { Instruction::PUSH10, { "PUSH10", 10, 0, 1 } }, + { Instruction::PUSH11, { "PUSH11", 11, 0, 1 } }, + { Instruction::PUSH12, { "PUSH12", 12, 0, 1 } }, + { Instruction::PUSH13, { "PUSH13", 13, 0, 1 } }, + { Instruction::PUSH14, { "PUSH14", 14, 0, 1 } }, + { Instruction::PUSH15, { "PUSH15", 15, 0, 1 } }, + { Instruction::PUSH16, { "PUSH16", 16, 0, 1 } }, + { Instruction::PUSH17, { "PUSH17", 17, 0, 1 } }, + { Instruction::PUSH18, { "PUSH18", 18, 0, 1 } }, + { Instruction::PUSH19, { "PUSH19", 19, 0, 1 } }, + { Instruction::PUSH20, { "PUSH20", 20, 0, 1 } }, + { Instruction::PUSH21, { "PUSH21", 21, 0, 1 } }, + { Instruction::PUSH22, { "PUSH22", 22, 0, 1 } }, + { Instruction::PUSH23, { "PUSH23", 23, 0, 1 } }, + { Instruction::PUSH24, { "PUSH24", 24, 0, 1 } }, + { Instruction::PUSH25, { "PUSH25", 25, 0, 1 } }, + { Instruction::PUSH26, { "PUSH26", 26, 0, 1 } }, + { Instruction::PUSH27, { "PUSH27", 27, 0, 1 } }, + { Instruction::PUSH28, { "PUSH28", 28, 0, 1 } }, + { Instruction::PUSH29, { "PUSH29", 29, 0, 1 } }, + { Instruction::PUSH30, { "PUSH30", 30, 0, 1 } }, + { Instruction::PUSH31, { "PUSH31", 31, 0, 1 } }, + { Instruction::PUSH32, { "PUSH32", 32, 0, 1 } }, + { Instruction::DUP1, { "DUP1", 0, 1, 2 } }, + { Instruction::DUP2, { "DUP2", 0, 2, 3 } }, + { Instruction::DUP3, { "DUP3", 0, 3, 4 } }, + { Instruction::DUP4, { "DUP4", 0, 4, 5 } }, + { Instruction::DUP5, { "DUP5", 0, 5, 6 } }, + { Instruction::DUP6, { "DUP6", 0, 6, 7 } }, + { Instruction::DUP7, { "DUP7", 0, 7, 8 } }, + { Instruction::DUP8, { "DUP8", 0, 8, 9 } }, + { Instruction::DUP9, { "DUP9", 0, 9, 10 } }, + { Instruction::DUP10, { "DUP10", 0, 10, 11 } }, + { Instruction::DUP11, { "DUP11", 0, 11, 12 } }, + { Instruction::DUP12, { "DUP12", 0, 12, 13 } }, + { Instruction::DUP13, { "DUP13", 0, 13, 14 } }, + { Instruction::DUP14, { "DUP14", 0, 14, 15 } }, + { Instruction::DUP15, { "DUP15", 0, 15, 16 } }, + { Instruction::DUP16, { "DUP16", 0, 16, 17 } }, + { Instruction::SWAP1, { "SWAP1", 0, 2, 2 } }, + { Instruction::SWAP2, { "SWAP2", 0, 3, 3 } }, + { Instruction::SWAP3, { "SWAP3", 0, 4, 4 } }, + { Instruction::SWAP4, { "SWAP4", 0, 5, 5 } }, + { Instruction::SWAP5, { "SWAP5", 0, 6, 6 } }, + { Instruction::SWAP6, { "SWAP6", 0, 7, 7 } }, + { Instruction::SWAP7, { "SWAP7", 0, 8, 8 } }, + { Instruction::SWAP8, { "SWAP8", 0, 9, 9 } }, + { Instruction::SWAP9, { "SWAP9", 0, 10, 10 } }, + { Instruction::SWAP10, { "SWAP10", 0, 11, 11 } }, + { Instruction::SWAP11, { "SWAP11", 0, 12, 12 } }, + { Instruction::SWAP12, { "SWAP12", 0, 13, 13 } }, + { Instruction::SWAP13, { "SWAP13", 0, 14, 14 } }, + { Instruction::SWAP14, { "SWAP14", 0, 15, 15 } }, + { Instruction::SWAP15, { "SWAP15", 0, 16, 16 } }, + { Instruction::SWAP16, { "SWAP16", 0, 17, 17 } }, + { Instruction::CREATE, { "CREATE", 0, 3, 1 } }, + { Instruction::CALL, { "CALL", 0, 7, 1 } }, + { Instruction::CALLSTATELESS, { "CALLSTATELESS",0, 7, 1 } }, + { Instruction::RETURN, { "RETURN", 0, 2, 0 } }, + { Instruction::POST, { "POST", 0, 5, 0 } }, + { Instruction::SUICIDE, { "SUICIDE", 0, 1, 0} } }; -string eth::disassemble(bytes const& _mem) +string dev::eth::disassemble(bytes const& _mem) { stringstream ret; - uint numerics = 0; + unsigned numerics = 0; for (auto it = _mem.begin(); it != _mem.end(); ++it) { byte n = *it; @@ -230,3 +303,15 @@ string eth::disassemble(bytes const& _mem) } return ret.str(); } + +InstructionInfo dev::eth::instructionInfo(Instruction _inst) +{ + try + { + return c_instructionInfo.at(_inst); + } + catch (...) + { + return InstructionInfo({"", 0, 0, 0}); + } +} diff --git a/libevmface/Instruction.h b/libevmface/Instruction.h index 4e0fe82b8..6c87b7a76 100644 --- a/libevmface/Instruction.h +++ b/libevmface/Instruction.h @@ -21,127 +21,164 @@ #pragma once -#include -#include +#include +#include namespace boost { namespace spirit { class utree; } } namespace sp = boost::spirit; +namespace dev +{ namespace eth { -// TODO: Update comments. - /// Virtual machine bytecode instruction. enum class Instruction: uint8_t { STOP = 0x00, ///< halts execution - ADD, - MUL, - SUB, - DIV, - SDIV, - MOD, - SMOD, - EXP, - NEG, - LT, - GT, - SLT, - SGT, - EQ, - NOT, - - AND = 0x10, - OR, - XOR, - BYTE, - - SHA3 = 0x20, - - ADDRESS = 0x30, - BALANCE, - ORIGIN, - CALLER, - CALLVALUE, - CALLDATALOAD, - CALLDATASIZE, - CALLDATACOPY, - CODESIZE, - CODECOPY, - GASPRICE, - - PREVHASH = 0x40, - COINBASE, - TIMESTAMP, - NUMBER, - DIFFICULTY, - GASLIMIT, - - POP = 0x50, - DUP, - SWAP, - MLOAD, - MSTORE, - MSTORE8, - SLOAD, - SSTORE, - JUMP, - JUMPI, - PC, - MSIZE, - GAS, - - PUSH1 = 0x60, - PUSH2, - PUSH3, - PUSH4, - PUSH5, - PUSH6, - PUSH7, - PUSH8, - PUSH9, - PUSH10, - PUSH11, - PUSH12, - PUSH13, - PUSH14, - PUSH15, - PUSH16, - PUSH17, - PUSH18, - PUSH19, - PUSH20, - PUSH21, - PUSH22, - PUSH23, - PUSH24, - PUSH25, - PUSH26, - PUSH27, - PUSH28, - PUSH29, - PUSH30, - PUSH31, - PUSH32, - - CREATE = 0xf0, - CALL, - RETURN, - SUICIDE = 0xff + ADD, ///< addition operation + MUL, ///< mulitplication operation + SUB, ///< subtraction operation + DIV, ///< integer division operation + SDIV, ///< signed integer division operation + MOD, ///< modulo remainder operation + SMOD, ///< signed modulo remainder operation + EXP, ///< exponential operation + NEG, ///< negation operation + LT, ///< less-than comparision + GT, ///< greater-than comparision + SLT, ///< signed less-than comparision + SGT, ///< signed greater-than comparision + EQ, ///< equality comparision + NOT, ///< simple not operator + + AND = 0x10, ///< bitwise AND operation + OR, ///< bitwise OR operation + XOR, ///< bitwise XOR operation + BYTE, ///< retrieve single byte from word + ADDMOD, ///< unsigned modular addition + MULMOD, ///< unsigned modular multiplication + SHA3 = 0x20, ///< compute SHA3-256 hash + + ADDRESS = 0x30, ///< get address of currently executing account + BALANCE, ///< get balance of the given account + ORIGIN, ///< get execution origination address + CALLER, ///< get caller address + CALLVALUE, ///< get deposited value by the instruction/transaction responsible for this execution + CALLDATALOAD, ///< get input data of current environment + CALLDATASIZE, ///< get size of input data in current environment + CALLDATACOPY, ///< copy input data in current environment to memory + CODESIZE, ///< get size of code running in current environment + CODECOPY, ///< copy code running in current environment to memory + GASPRICE, ///< get price of gas in current environment + EXTCODESIZE, ///< get external code size (from another contract) + EXTCODECOPY, ///< copy external code (from another contract) + + PREVHASH = 0x40, ///< get hash of most recent complete block + COINBASE, ///< get the block's coinbase address + TIMESTAMP, ///< get the block's timestamp + NUMBER, ///< get the block's number + DIFFICULTY, ///< get the block's difficulty + GASLIMIT, ///< get the block's gas limit + + POP = 0x50, ///< remove item from stack + MLOAD = 0x53, ///< load word from memory + MSTORE, ///< save word to memory + MSTORE8, ///< save byte to memory + SLOAD, ///< load word from storage + SSTORE, ///< save word to storage + JUMP, ///< alter the program counter + JUMPI, ///< conditionally alter the program counter + PC, ///< get the program counter + MSIZE, ///< get the size of active memory + GAS, ///< get the amount of available gas + + PUSH1 = 0x60, ///< place 1 byte item on stack + PUSH2, ///< place 2 byte item on stack + PUSH3, ///< place 3 byte item on stack + PUSH4, ///< place 4 byte item on stack + PUSH5, ///< place 5 byte item on stack + PUSH6, ///< place 6 byte item on stack + PUSH7, ///< place 7 byte item on stack + PUSH8, ///< place 8 byte item on stack + PUSH9, ///< place 9 byte item on stack + PUSH10, ///< place 10 byte item on stack + PUSH11, ///< place 11 byte item on stack + PUSH12, ///< place 12 byte item on stack + PUSH13, ///< place 13 byte item on stack + PUSH14, ///< place 14 byte item on stack + PUSH15, ///< place 15 byte item on stack + PUSH16, ///< place 16 byte item on stack + PUSH17, ///< place 17 byte item on stack + PUSH18, ///< place 18 byte item on stack + PUSH19, ///< place 19 byte item on stack + PUSH20, ///< place 20 byte item on stack + PUSH21, ///< place 21 byte item on stack + PUSH22, ///< place 22 byte item on stack + PUSH23, ///< place 23 byte item on stack + PUSH24, ///< place 24 byte item on stack + PUSH25, ///< place 25 byte item on stack + PUSH26, ///< place 26 byte item on stack + PUSH27, ///< place 27 byte item on stack + PUSH28, ///< place 28 byte item on stack + PUSH29, ///< place 29 byte item on stack + PUSH30, ///< place 30 byte item on stack + PUSH31, ///< place 31 byte item on stack + PUSH32, ///< place 32 byte item on stack + + DUP1 = 0x80, ///< copies the highest item in the stack to the top of the stack + DUP2, ///< copies the second highest item in the stack to the top of the stack + DUP3, ///< copies the third highest item in the stack to the top of the stack + DUP4, ///< copies the 4th highest item in the stack to the top of the stack + DUP5, ///< copies the 5th highest item in the stack to the top of the stack + DUP6, ///< copies the 6th highest item in the stack to the top of the stack + DUP7, ///< copies the 7th highest item in the stack to the top of the stack + DUP8, ///< copies the 8th highest item in the stack to the top of the stack + DUP9, ///< copies the 9th highest item in the stack to the top of the stack + DUP10, ///< copies the 10th highest item in the stack to the top of the stack + DUP11, ///< copies the 11th highest item in the stack to the top of the stack + DUP12, ///< copies the 12th highest item in the stack to the top of the stack + DUP13, ///< copies the 13th highest item in the stack to the top of the stack + DUP14, ///< copies the 14th highest item in the stack to the top of the stack + DUP15, ///< copies the 15th highest item in the stack to the top of the stack + DUP16, ///< copies the 16th highest item in the stack to the top of the stack + + SWAP1 = 0x90, ///< swaps the highest and second highest value on the stack + SWAP2, ///< swaps the highest and third highest value on the stack + SWAP3, ///< swaps the highest and 4th highest value on the stack + SWAP4, ///< swaps the highest and 5th highest value on the stack + SWAP5, ///< swaps the highest and 6th highest value on the stack + SWAP6, ///< swaps the highest and 7th highest value on the stack + SWAP7, ///< swaps the highest and 8th highest value on the stack + SWAP8, ///< swaps the highest and 9th highest value on the stack + SWAP9, ///< swaps the highest and 10th highest value on the stack + SWAP10, ///< swaps the highest and 11th highest value on the stack + SWAP11, ///< swaps the highest and 12th highest value on the stack + SWAP12, ///< swaps the highest and 13th highest value on the stack + SWAP13, ///< swaps the highest and 14th highest value on the stack + SWAP14, ///< swaps the highest and 15th highest value on the stack + SWAP15, ///< swaps the highest and 16th highest value on the stack + SWAP16, ///< swaps the highest and 17th highest value on the stack + + CREATE = 0xf0, ///< create a new account with associated code + CALL, ///< message-call into an account + RETURN, ///< halt execution returning output data + POST, ///< asynchronous call without output (adds a message to the post queue) + CALLSTATELESS, + SUICIDE = 0xff ///< halt execution and register account for later deletion }; /// Information structure for a particular instruction. struct InstructionInfo { - char const* name; ///< The name of the instruction. + std::string name; ///< The name of the instruction. int additional; ///< Additional items required in memory for this instructions (only for PUSH). int args; ///< Number of items required on the stack for this instruction (and, for the purposes of ret, the number taken from the stack). int ret; ///< Number of items placed (back) on the stack by this instruction, assuming args items were removed. }; /// Information on all the instructions. -extern const std::map c_instructionInfo; +InstructionInfo instructionInfo(Instruction _inst); /// Convert from string mnemonic to Instruction type. extern const std::map c_instructions; @@ -150,3 +187,4 @@ extern const std::map c_instructions; std::string disassemble(bytes const& _mem); } +} diff --git a/liblll/Assembly.cpp b/liblll/Assembly.cpp index 6f5678539..2250a71fe 100644 --- a/liblll/Assembly.cpp +++ b/liblll/Assembly.cpp @@ -21,18 +21,19 @@ #include "Assembly.h" -#include +#include using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; int AssemblyItem::deposit() const { switch (m_type) { case Operation: - return c_instructionInfo.at((Instruction)(byte)m_data).ret - c_instructionInfo.at((Instruction)(byte)m_data).args; - case Push: case PushString: case PushTag: case PushData: + return instructionInfo((Instruction)(byte)m_data).ret - instructionInfo((Instruction)(byte)m_data).args; + case Push: case PushString: case PushTag: case PushData: case PushSub: case PushSubSize: return 1; case Tag: return 0; @@ -59,15 +60,19 @@ unsigned Assembly::bytesRequired() const ret += 33; break; case Push: - ret += 1 + max(1, eth::bytesRequired(i.m_data)); + ret += 1 + max(1, dev::bytesRequired(i.m_data)); + break; + case PushSubSize: + ret += 4; // worst case: a 16MB program break; case PushTag: case PushData: + case PushSub: ret += 1 + br; case Tag:; default:; } - if (eth::bytesRequired(ret) <= br) + if (dev::bytesRequired(ret) <= br) return ret; } } @@ -87,6 +92,8 @@ void Assembly::append(Assembly const& _a) m_data.insert(i); for (auto const& i: _a.m_strings) m_strings.insert(i); + for (auto const& i: _a.m_subs) + m_subs.insert(i); assert(!_a.m_baseDeposit); assert(!_a.m_totalDeposit); @@ -104,19 +111,19 @@ void Assembly::append(Assembly const& _a, int _deposit) } } -ostream& eth::operator<<(ostream& _out, AssemblyItemsConstRef _i) +ostream& dev::eth::operator<<(ostream& _out, AssemblyItemsConstRef _i) { for (AssemblyItem const& i: _i) switch (i.type()) { case Operation: - _out << " " << c_instructionInfo.at((Instruction)(byte)i.data()).name; + _out << " " << instructionInfo((Instruction)(byte)i.data()).name; break; case Push: _out << " PUSH" << i.data(); break; case PushString: - _out << " PUSH'[" << h256(i.data()).abridged() << "]"; + _out << " PUSH'[" << hex << (unsigned)i.data() << "]"; break; case PushTag: _out << " PUSH[tag" << i.data() << "]"; @@ -125,7 +132,13 @@ ostream& eth::operator<<(ostream& _out, AssemblyItemsConstRef _i) _out << " tag" << i.data() << ":"; break; case PushData: - _out << " PUSH*[" << h256(i.data()).abridged() << "]"; + _out << " PUSH*[" << hex << (unsigned)i.data() << "]"; + break; + case PushSub: + _out << " PUSHs[" << hex << h256(i.data()).abridged() << "]"; + break; + case PushSubSize: + _out << " PUSHss[" << hex << h256(i.data()).abridged() << "]"; break; case UndefinedItem: _out << " ???"; @@ -134,38 +147,50 @@ ostream& eth::operator<<(ostream& _out, AssemblyItemsConstRef _i) return _out; } -ostream& Assembly::streamOut(ostream& _out) const +ostream& Assembly::streamOut(ostream& _out, string const& _prefix) const { - _out << ".code:" << endl; + _out << _prefix << ".code:" << endl; for (AssemblyItem const& i: m_items) switch (i.m_type) { case Operation: - _out << " " << c_instructionInfo.at((Instruction)(byte)i.m_data).name << endl; + _out << _prefix << " " << instructionInfo((Instruction)(byte)i.m_data).name << endl; break; case Push: - _out << " PUSH " << i.m_data << endl; + _out << _prefix << " PUSH " << i.m_data << endl; break; case PushString: - _out << " PUSH \"" << m_strings.at((h256)i.m_data) << "\"" << endl; + _out << _prefix << " PUSH \"" << m_strings.at((h256)i.m_data) << "\"" << endl; break; case PushTag: - _out << " PUSH [tag" << i.m_data << "]" << endl; + _out << _prefix << " PUSH [tag" << i.m_data << "]" << endl; + break; + case PushSub: + _out << _prefix << " PUSH [$" << h256(i.m_data).abridged() << "]" << endl; + break; + case PushSubSize: + _out << _prefix << " PUSH #[$" << h256(i.m_data).abridged() << "]" << endl; break; case Tag: - _out << "tag" << i.m_data << ": " << endl; + _out << _prefix << "tag" << i.m_data << ": " << endl; break; case PushData: - _out << " PUSH [" << h256(i.m_data).abridged() << "]" << endl; + _out << _prefix << " PUSH [" << hex << (unsigned)i.m_data << "]" << endl; break; default:; } - if (m_data.size()) + if (m_data.size() || m_subs.size()) { - _out << ".data:" << endl; + _out << _prefix << ".data:" << endl; for (auto const& i: m_data) - _out << " " << i.first.abridged() << ": " << toHex(i.second) << endl; + if (!m_subs.count(i.first)) + _out << _prefix << " " << hex << (unsigned)(u256)i.first << ": " << toHex(i.second) << endl; + for (auto const& i: m_subs) + { + _out << _prefix << " " << hex << (unsigned)(u256)i.first << ": " << endl; + i.second.streamOut(_out, _prefix + " "); + } } return _out; } @@ -193,22 +218,24 @@ inline bool matches(AssemblyItemsConstRef _a, AssemblyItemsConstRef _b) } struct OptimiserChannel: public LogChannel { static const char* name() { return "OPT"; } static const int verbosity = 12; }; -#define copt eth::LogOutputStream() +#define copt dev::LogOutputStream() -void Assembly::optimise() +Assembly& Assembly::optimise(bool _enable) { + if (!_enable) + return *this; map> c_simple = { { Instruction::SUB, [](u256 a, u256 b)->u256{return a - b;} }, { Instruction::DIV, [](u256 a, u256 b)->u256{return a / b;} }, - { Instruction::SDIV, [](u256 a, u256 b)->u256{return s2u(u2s(a) / u2s(b));} }, + { Instruction::SDIV, [](u256 a, u256 b)->u256{return s2u(u2s(a) / u2s(b));} }, { Instruction::MOD, [](u256 a, u256 b)->u256{return a % b;} }, - { Instruction::SMOD, [](u256 a, u256 b)->u256{return s2u(u2s(a) % u2s(b));} }, - { Instruction::EXP, [](u256 a, u256 b)->u256{return boost::multiprecision::pow(a, (unsigned)b);} }, + { Instruction::SMOD, [](u256 a, u256 b)->u256{return s2u(u2s(a) % u2s(b));} }, + { Instruction::EXP, [](u256 a, u256 b)->u256{return (u256)boost::multiprecision::powm((bigint)a, (bigint)b, bigint(2) << 256);} }, { Instruction::LT, [](u256 a, u256 b)->u256{return a < b ? 1 : 0;} }, { Instruction::GT, [](u256 a, u256 b)->u256{return a > b ? 1 : 0;} }, - { Instruction::SLT, [](u256 a, u256 b)->u256{return u2s(a) < u2s(b) ? 1 : 0;} }, - { Instruction::SGT, [](u256 a, u256 b)->u256{return u2s(a) > u2s(b) ? 1 : 0;} }, + { Instruction::SLT, [](u256 a, u256 b)->u256{return u2s(a) < u2s(b) ? 1 : 0;} }, + { Instruction::SGT, [](u256 a, u256 b)->u256{return u2s(a) > u2s(b) ? 1 : 0;} }, { Instruction::EQ, [](u256 a, u256 b)->u256{return a == b ? 1 : 0;} }, }; map> c_associative = @@ -221,6 +248,8 @@ void Assembly::optimise() { { Push, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, { { PushTag, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, { { PushString, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, + { { PushSub, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, + { { PushSubSize, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, { { Push, PushTag, Instruction::JUMPI }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].data()) return { m[1], Instruction::JUMP }; else return {}; } }, { { Instruction::NOT, Instruction::NOT }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, }; @@ -309,6 +338,11 @@ void Assembly::optimise() } copt << total << " optimisations done."; + + for (auto& i: m_subs) + i.second.optimise(true); + + return *this; } bytes Assembly::assemble() const @@ -320,9 +354,12 @@ bytes Assembly::assemble() const vector tagPos(m_usedTags); map tagRef; multimap dataRef; - unsigned bytesPerTag = eth::bytesRequired(totalBytes); + unsigned bytesPerTag = dev::bytesRequired(totalBytes); byte tagPush = (byte)Instruction::PUSH1 - 1 + bytesPerTag; + for (auto const& i: m_subs) + m_data[i.first] = i.second.assemble(); + for (AssemblyItem const& i: m_items) switch (i.m_type) { @@ -344,7 +381,7 @@ bytes Assembly::assemble() const } case Push: { - byte b = max(1, eth::bytesRequired(i.m_data)); + byte b = max(1, dev::bytesRequired(i.m_data)); ret.push_back((byte)Instruction::PUSH1 - 1 + b); ret.resize(ret.size() + b); bytesRef byr(&ret.back() + 1 - b, b); @@ -358,13 +395,23 @@ bytes Assembly::assemble() const ret.resize(ret.size() + bytesPerTag); break; } - case PushData: + case PushData: case PushSub: { ret.push_back(tagPush); dataRef.insert(make_pair((h256)i.m_data, ret.size())); ret.resize(ret.size() + bytesPerTag); break; } + case PushSubSize: + { + auto s = m_data[i.m_data].size(); + byte b = max(1, dev::bytesRequired(s)); + ret.push_back((byte)Instruction::PUSH1 - 1 + b); + ret.resize(ret.size() + b); + bytesRef byr(&ret.back() + 1 - b, b); + toBigEndian(s, byr); + break; + } case Tag: tagPos[(unsigned)i.m_data] = ret.size(); break; diff --git a/liblll/Assembly.h b/liblll/Assembly.h index 4a6d02ce0..b8152fe02 100644 --- a/liblll/Assembly.h +++ b/liblll/Assembly.h @@ -23,14 +23,16 @@ #include #include -#include +#include #include #include "Exceptions.h" +namespace dev +{ namespace eth { -enum AssemblyItemType { UndefinedItem, Operation, Push, PushString, PushTag, Tag, PushData }; +enum AssemblyItemType { UndefinedItem, Operation, Push, PushString, PushTag, PushSub, PushSubSize, Tag, PushData }; class Assembly; @@ -70,7 +72,9 @@ public: AssemblyItem newTag() { return AssemblyItem(Tag, m_usedTags++); } AssemblyItem newPushTag() { return AssemblyItem(PushTag, m_usedTags++); } AssemblyItem newData(bytes const& _data) { h256 h = (u256)std::hash()(asString(_data)); m_data[h] = _data; return AssemblyItem(PushData, h); } + AssemblyItem newSub(Assembly const& _sub) { h256 h = h256::random(s_fixedHashEngine); m_subs[h] = _sub; return AssemblyItem(PushSub, h); } AssemblyItem newPushString(std::string const& _data) { h256 h = (u256)std::hash()(_data); m_strings[h] = _data; return AssemblyItem(PushString, h); } + AssemblyItem newPushSubSize(h256 const& _subId) { return AssemblyItem(PushSubSize, _subId); } AssemblyItem append() { return append(newTag()); } void append(Assembly const& _a); @@ -78,6 +82,7 @@ public: AssemblyItem const& append(AssemblyItem const& _i); AssemblyItem const& append(std::string const& _data) { return append(newPushString(_data)); } AssemblyItem const& append(bytes const& _data) { return append(newData(_data)); } + AssemblyItem appendSubSize(Assembly const& _a) { auto ret = newSub(_a); append(newPushSubSize(ret.data())); return ret; } AssemblyItem appendJump() { auto ret = append(newPushTag()); append(Instruction::JUMP); return ret; } AssemblyItem appendJumpI() { auto ret = append(newPushTag()); append(Instruction::JUMPI); return ret; } @@ -102,8 +107,8 @@ public: std::string out() const { std::stringstream ret; streamOut(ret); return ret.str(); } int deposit() const { return m_deposit; } bytes assemble() const; - void optimise(); - std::ostream& streamOut(std::ostream& _out) const; + Assembly& optimise(bool _enable); + std::ostream& streamOut(std::ostream& _out, std::string const& _prefix = "") const; private: void donePath() { if (m_totalDeposit != INT_MAX && m_totalDeposit != m_deposit) throw InvalidDeposit(); } @@ -111,7 +116,8 @@ private: unsigned m_usedTags = 0; AssemblyItems m_items; - std::map m_data; + mutable std::map m_data; + std::map m_subs; std::map m_strings; int m_deposit = 0; @@ -126,3 +132,4 @@ inline std::ostream& operator<<(std::ostream& _out, Assembly const& _a) } } +} diff --git a/liblll/CMakeLists.txt b/liblll/CMakeLists.txt index 98bebdfed..7746613ec 100644 --- a/liblll/CMakeLists.txt +++ b/liblll/CMakeLists.txt @@ -18,7 +18,7 @@ file(GLOB HEADERS "*.h") include_directories(..) target_link_libraries(${EXECUTABLE} evmface) -target_link_libraries(${EXECUTABLE} ethential) +target_link_libraries(${EXECUTABLE} devcore) if("${TARGET_PLATFORM}" STREQUAL "w64") diff --git a/liblll/CodeFragment.cpp b/liblll/CodeFragment.cpp index d19d75090..533d00a96 100644 --- a/liblll/CodeFragment.cpp +++ b/liblll/CodeFragment.cpp @@ -23,12 +23,13 @@ #include #include -#include +#include #include #include "CompilerState.h" #include "Parser.h" using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; namespace qi = boost::spirit::qi; namespace px = boost::phoenix; namespace sp = boost::spirit; @@ -44,12 +45,6 @@ void CodeFragment::finalise(CompilerState const& _cs) } } -bytes CodeFragment::code(CompilerState const& _cs) -{ - finalise(_cs); - return m_asm.assemble(); -} - CodeFragment::CodeFragment(sp::utree const& _t, CompilerState& _s, bool _allowASM) { /* cdebug << "CodeFragment. Locals:"; @@ -83,7 +78,7 @@ CodeFragment::CodeFragment(sp::utree const& _t, CompilerState& _s, bool _allowAS string us = boost::algorithm::to_upper_copy(s); if (_allowASM && c_instructions.count(us)) m_asm.append(c_instructions.at(us)); - if (_s.defs.count(s)) + else if (_s.defs.count(s)) m_asm.append(_s.defs.at(s).m_asm); else if (_s.args.count(s)) m_asm.append(_s.args.at(s).m_asm); @@ -319,7 +314,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) ++ii; } m_asm.append((u256)data.size()); - m_asm.append(Instruction::DUP); + m_asm.append(Instruction::DUP1); m_asm.append(data); m_asm.append(pos.m_asm, 1); m_asm.append(Instruction::CODECOPY); @@ -375,7 +370,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) else if (c_instructions.count(us)) { auto it = c_instructions.find(us); - int ea = c_instructionInfo.at(it->second).args; + int ea = instructionInfo(it->second).args; if (ea >= 0) requireSize(ea); else @@ -499,11 +494,8 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) requireMaxSize(3); requireDeposit(1, 1); - code[0].optimise(); - bytes subcode = code[0].code(ns); - - m_asm.append((u256)subcode.size()); - m_asm.append(Instruction::DUP); + auto subPush = m_asm.appendSubSize(code[0].assembly(ns)); + m_asm.append(Instruction::DUP1); if (code.size() == 3) { requireDeposit(2, 1); @@ -511,9 +503,9 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) m_asm.append(Instruction::LT); m_asm.append(Instruction::NOT); m_asm.append(Instruction::MUL); - m_asm.append(Instruction::DUP); + m_asm.append(Instruction::DUP1); } - m_asm.append(subcode); + m_asm.append(subPush); m_asm.append(code[1].m_asm, 1); m_asm.append(Instruction::CODECOPY); } diff --git a/liblll/CodeFragment.h b/liblll/CodeFragment.h index 58e409125..60ab7c6de 100644 --- a/liblll/CodeFragment.h +++ b/liblll/CodeFragment.h @@ -21,7 +21,7 @@ #pragma once -#include +#include #include #include "Assembly.h" #include "Exceptions.h" @@ -29,6 +29,8 @@ namespace boost { namespace spirit { class utree; } } namespace sp = boost::spirit; +namespace dev +{ namespace eth { @@ -43,13 +45,7 @@ public: static CodeFragment compile(std::string const& _src, CompilerState& _s); /// Consolidates data and compiles code. - bytes code(CompilerState const& _cs); - - /// Consolidates data and compiles code. - std::string assembly(CompilerState const& _cs) { finalise(_cs); return m_asm.out(); } - - /// Optimise the code. Best do this just before calling code() or assembly(). - void optimise() { m_asm.optimise(); } + Assembly& assembly(CompilerState const& _cs) { finalise(_cs); return m_asm; } private: void finalise(CompilerState const& _cs); @@ -64,3 +60,4 @@ private: static const CodeFragment NullCodeFragment; } +} diff --git a/liblll/Compiler.cpp b/liblll/Compiler.cpp index 1621acf90..389c10be2 100644 --- a/liblll/Compiler.cpp +++ b/liblll/Compiler.cpp @@ -25,18 +25,17 @@ #include "CodeFragment.h" using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; -bytes eth::compileLLL(string const& _src, bool _opt, vector* _errors) +bytes dev::eth::compileLLL(string const& _src, bool _opt, vector* _errors) { try { CompilerState cs; cs.populateStandard(); auto f = CodeFragment::compile(_src, cs); - if (_opt) - f.optimise(); - bytes ret = f.code(cs); + bytes ret = f.assembly(cs).optimise(_opt).assemble(); for (auto i: cs.treesToKill) killBigints(i); return ret; @@ -54,16 +53,13 @@ bytes eth::compileLLL(string const& _src, bool _opt, vector* _errors) return bytes(); } -std::string eth::compileLLLToAsm(std::string const& _src, bool _opt, std::vector* _errors) +std::string dev::eth::compileLLLToAsm(std::string const& _src, bool _opt, std::vector* _errors) { try { CompilerState cs; cs.populateStandard(); - auto f = CodeFragment::compile(_src, cs); - if (_opt) - f.optimise(); - string ret = f.assembly(cs); + string ret = CodeFragment::compile(_src, cs).assembly(cs).optimise(_opt).out(); for (auto i: cs.treesToKill) killBigints(i); return ret; @@ -81,7 +77,7 @@ std::string eth::compileLLLToAsm(std::string const& _src, bool _opt, std::vector return string(); } -string eth::parseLLL(string const& _src) +string dev::eth::parseLLL(string const& _src) { sp::utree o; try diff --git a/liblll/Compiler.h b/liblll/Compiler.h index da84a2fea..0fadd2785 100644 --- a/liblll/Compiler.h +++ b/liblll/Compiler.h @@ -23,8 +23,10 @@ #include #include -#include +#include +namespace dev +{ namespace eth { @@ -33,4 +35,4 @@ std::string compileLLLToAsm(std::string const& _src, bool _opt = true, std::vect bytes compileLLL(std::string const& _src, bool _opt = true, std::vector* _errors = nullptr); } - +} diff --git a/liblll/CompilerState.cpp b/liblll/CompilerState.cpp index 380d5a66f..c3dc2dda3 100644 --- a/liblll/CompilerState.cpp +++ b/liblll/CompilerState.cpp @@ -23,7 +23,8 @@ #include "CodeFragment.h" using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; CompilerState::CompilerState() { @@ -46,12 +47,7 @@ void CompilerState::populateStandard() static const string s = "{" "(def 'gav 0x51ba59315b3a95761d0863b05ccc7a7f54703d99)" "(def 'config 0x661005d2720d855f1d9976f88bb10c1a3398c77f)" - "(def 'namereg 0x50441127ea5b9dfd835a9aba4e1dc9c1257b58ca)" - "(def 'gavcoin 0x5620133321fcac7f15a5c570016f6cb6dc263f9d)" "(def 'allgas (- (gas) 21))" - "(def 'sendgavcoin (to value) { [0]'send [32]:to [64]:value (call allgas gavcoin 0 0 96 0 0) })" - "(def 'regname (name) { [0]'register [32]name (call allgas namereg 0 0 64 0 0) })" - "(def 'regcoins (name) { [0]'register [32]name (call allgas namereg 0 0 64 0 0) })" "(def 'send (to value) (call allgas to value 0 0 0 0))" "(def 'send (gaslimit to value) (call gaslimit to value 0 0 0 0))" "(def 'msg (gaslimit to value data datasize outsize) { (set x outsize) (set y (alloc @32)) (call gaslimit to value data datasize @0 @32) @0 })" @@ -62,11 +58,20 @@ void CompilerState::populateStandard() "(def 'create (value code) { [0]:(msize) (create value @0 (lll code @0)) })" "(def 'create (code) { [0]:(msize) (create 0 @0 (lll code @0)) })" "(def 'sha3 (val) { [0]:val (sha3 0 32) })" + "(def 'sha3pair (a b) { [0]:a [32]:b (sha3 0 64) })" + "(def 'sha3trip (a b c) { [0]:a [32]:b [64]:c (sha3 0 96) })" "(def 'return (val) { [0]:val (return 0 32) })" "(def 'returnlll (code) (return 0 (lll code 0)) )" "(def 'makeperm (name pos) { (def name (sload pos)) (def name (v) (sstore pos v)) } )" "(def 'permcount 0)" "(def 'perm (name) { (makeperm name permcount) (def 'permcount (+ permcount 1)) } )" + "(def 'namereg (msg config 0))" + "(def 'coinreg (msg config 1))" + "(def 'gavcoin (msg config 2))" + "(def 'sendgavcoin (to value) { [32]'send [64]:to [96]:value (call allgas gavcoin 0 32 96 0 0) })" + "(def 'regname (name) { [32]'register [64]name (call allgas namereg 0 32 64 0 0) })" + "(def 'regcoin (name) { [32]name (call allgas coinreg 0 32 32 0 0) })" + "(def 'regcoin (name denom) { [32]name [64]denom (call allgas coinreg 0 32 64 0 0) })" "}"; CodeFragment::compile(s, *this); } diff --git a/liblll/CompilerState.h b/liblll/CompilerState.h index a0b3bf46e..bfe56f927 100644 --- a/liblll/CompilerState.h +++ b/liblll/CompilerState.h @@ -24,6 +24,8 @@ #include #include "CodeFragment.h" +namespace dev +{ namespace eth { @@ -52,3 +54,4 @@ struct CompilerState }; } +} diff --git a/liblll/Exceptions.h b/liblll/Exceptions.h index a1aee1738..c45215f1a 100644 --- a/liblll/Exceptions.h +++ b/liblll/Exceptions.h @@ -21,11 +21,15 @@ #pragma once +#include + +namespace dev +{ namespace eth { /// Compile a Low-level Lisp-like Language program into EVM-code. -class CompilerException: public Exception {}; +class CompilerException: public dev::Exception {}; class InvalidOperation: public CompilerException {}; class IntegerOutOfRange: public CompilerException {}; class StringTooLong: public CompilerException {}; @@ -40,3 +44,4 @@ class BareSymbol: public CompilerException {}; class ExpectedLiteral: public CompilerException {}; } +} diff --git a/liblll/Parser.cpp b/liblll/Parser.cpp index 1907fd17c..52a05174b 100644 --- a/liblll/Parser.cpp +++ b/liblll/Parser.cpp @@ -28,12 +28,13 @@ #include using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; namespace qi = boost::spirit::qi; namespace px = boost::phoenix; namespace sp = boost::spirit; -void eth::killBigints(sp::utree const& _this) +void dev::eth::killBigints(sp::utree const& _this) { switch (_this.which()) { @@ -43,7 +44,7 @@ void eth::killBigints(sp::utree const& _this) } } -void eth::debugOutAST(ostream& _out, sp::utree const& _this) +void dev::eth::debugOutAST(ostream& _out, sp::utree const& _this) { switch (_this.which()) { @@ -69,7 +70,7 @@ void eth::debugOutAST(ostream& _out, sp::utree const& _this) } } -namespace eth { +namespace dev { namespace eth { namespace parseTreeLLL_ { template @@ -81,12 +82,13 @@ struct tagNode } }; -}} +}}} -void eth::parseTreeLLL(string const& _s, sp::utree& o_out) +void dev::eth::parseTreeLLL(string const& _s, sp::utree& o_out) { - using qi::ascii::space; - using eth::parseTreeLLL_::tagNode; + using qi::standard::space; + using qi::standard::space_type; + using dev::eth::parseTreeLLL_::tagNode; typedef sp::basic_string symbol_type; typedef string::const_iterator it; @@ -94,24 +96,24 @@ void eth::parseTreeLLL(string const& _s, sp::utree& o_out) static const u256 finney = u256(1000000000) * 1000000; static const u256 szabo = u256(1000000000) * 1000; - qi::rule element; + qi::rule element; qi::rule str = '"' > qi::lexeme[+(~qi::char_(std::string("\"") + '\0'))] > '"'; qi::rule strsh = '\'' > qi::lexeme[+(~qi::char_(std::string(" ;$@()[]{}:\n\t") + '\0'))]; qi::rule symbol = qi::lexeme[+(~qi::char_(std::string(" $@[]{}:();\"\x01-\x1f\x7f") + '\0'))]; qi::rule intstr = qi::lexeme[ qi::no_case["0x"][qi::_val = "0x"] >> *qi::char_("0-9a-fA-F")[qi::_val += qi::_1]] | qi::lexeme[+qi::char_("0-9")[qi::_val += qi::_1]]; qi::rule integer = intstr; qi::rule multiplier = qi::lit("wei")[qi::_val = 1] | qi::lit("szabo")[qi::_val = szabo] | qi::lit("finney")[qi::_val = finney] | qi::lit("ether")[qi::_val = ether]; - qi::rule quantity = integer[qi::_val = qi::_1] >> -multiplier[qi::_val *= qi::_1]; - qi::rule atom = quantity[qi::_val = px::construct(px::new_(qi::_1))] | (str | strsh)[qi::_val = qi::_1] | symbol[qi::_val = qi::_1]; - qi::rule seq = '{' > *element > '}'; - qi::rule mload = '@' > element; - qi::rule sload = qi::lit("@@") > element; - qi::rule mstore = '[' > element > ']' > -qi::lit(":") > element; - qi::rule sstore = qi::lit("[[") > element > qi::lit("]]") > -qi::lit(":") > element; - qi::rule calldataload = qi::lit("$") > element; - qi::rule list = '(' > *element > ')'; - - qi::rule extra = sload[tagNode<2>()] | mload[tagNode<1>()] | sstore[tagNode<4>()] | mstore[tagNode<3>()] | seq[tagNode<5>()] | calldataload[tagNode<6>()]; + qi::rule quantity = integer[qi::_val = qi::_1] >> -multiplier[qi::_val *= qi::_1]; + qi::rule atom = quantity[qi::_val = px::construct(px::new_(qi::_1))] | (str | strsh)[qi::_val = qi::_1] | symbol[qi::_val = qi::_1]; + qi::rule seq = '{' > *element > '}'; + qi::rule mload = '@' > element; + qi::rule sload = qi::lit("@@") > element; + qi::rule mstore = '[' > element > ']' > -qi::lit(":") > element; + qi::rule sstore = qi::lit("[[") > element > qi::lit("]]") > -qi::lit(":") > element; + qi::rule calldataload = qi::lit("$") > element; + qi::rule list = '(' > *element > ')'; + + qi::rule extra = sload[tagNode<2>()] | mload[tagNode<1>()] | sstore[tagNode<4>()] | mstore[tagNode<3>()] | seq[tagNode<5>()] | calldataload[tagNode<6>()]; element = atom | list | extra; string s; diff --git a/liblll/Parser.h b/liblll/Parser.h index 5286c3e8a..b21989f06 100644 --- a/liblll/Parser.h +++ b/liblll/Parser.h @@ -23,11 +23,13 @@ #include #include -#include +#include namespace boost { namespace spirit { class utree; } } namespace sp = boost::spirit; +namespace dev +{ namespace eth { @@ -36,4 +38,4 @@ void parseTreeLLL(std::string const& _s, sp::utree& o_out); void debugOutAST(std::ostream& _out, sp::utree const& _this); } - +} diff --git a/libp2p/All.h b/libp2p/All.h new file mode 100644 index 000000000..b9dd26276 --- /dev/null +++ b/libp2p/All.h @@ -0,0 +1,8 @@ +#pragma once + +#include "Common.h" +#include "HostCapability.h" +#include "Capability.h" +#include "Host.h" +#include "Session.h" + diff --git a/libp2p/CMakeLists.txt b/libp2p/CMakeLists.txt new file mode 100644 index 000000000..ab852fc57 --- /dev/null +++ b/libp2p/CMakeLists.txt @@ -0,0 +1,64 @@ +cmake_policy(SET CMP0015 NEW) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB") + +aux_source_directory(. SRC_LIST) + +set(EXECUTABLE p2p) + +# set(CMAKE_INSTALL_PREFIX ../lib) +if(ETH_STATIC) + add_library(${EXECUTABLE} STATIC ${SRC_LIST}) +else() + add_library(${EXECUTABLE} SHARED ${SRC_LIST}) +endif() + +file(GLOB HEADERS "*.h") + +include_directories(..) + +target_link_libraries(${EXECUTABLE} devcore) +target_link_libraries(${EXECUTABLE} secp256k1) +if(MINIUPNPC_LS) +target_link_libraries(${EXECUTABLE} ${MINIUPNPC_LS}) +endif() +target_link_libraries(${EXECUTABLE} ${LEVELDB_LS}) +target_link_libraries(${EXECUTABLE} ${CRYPTOPP_LS}) +target_link_libraries(${EXECUTABLE} gmp) + +if("${TARGET_PLATFORM}" STREQUAL "w64") + target_link_libraries(${EXECUTABLE} boost_system-mt-s) + target_link_libraries(${EXECUTABLE} boost_regex-mt-s) + target_link_libraries(${EXECUTABLE} boost_filesystem-mt-s) + target_link_libraries(${EXECUTABLE} boost_thread_win32-mt-s) + target_link_libraries(${EXECUTABLE} iphlpapi) + target_link_libraries(${EXECUTABLE} ws2_32) + target_link_libraries(${EXECUTABLE} mswsock) + target_link_libraries(${EXECUTABLE} shlwapi) +elseif (APPLE) + # Latest mavericks boost libraries only come with -mt + target_link_libraries(${EXECUTABLE} boost_system-mt) + target_link_libraries(${EXECUTABLE} boost_regex-mt) + target_link_libraries(${EXECUTABLE} boost_filesystem-mt) + target_link_libraries(${EXECUTABLE} boost_thread-mt) + find_package(Threads REQUIRED) + target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) +elseif (UNIX) + target_link_libraries(${EXECUTABLE} ${Boost_SYSTEM_LIBRARY}) + target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARY}) + target_link_libraries(${EXECUTABLE} ${Boost_FILESYSTEM_LIBRARY}) + target_link_libraries(${EXECUTABLE} ${Boost_THREAD_LIBRARY}) + target_link_libraries(${EXECUTABLE} ${Boost_DATE_TIME_LIBRARY}) + target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) +else () + target_link_libraries(${EXECUTABLE} boost_system) + target_link_libraries(${EXECUTABLE} boost_regex) + target_link_libraries(${EXECUTABLE} boost_filesystem) + target_link_libraries(${EXECUTABLE} boost_thread) + find_package(Threads REQUIRED) + target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) +endif () + +install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) +install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) + diff --git a/libp2p/Capability.cpp b/libp2p/Capability.cpp new file mode 100644 index 000000000..5ca674120 --- /dev/null +++ b/libp2p/Capability.cpp @@ -0,0 +1,58 @@ +/* + 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 . +*/ +/** @file Capability.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "Capability.h" + +#include "Session.h" +using namespace std; +using namespace dev; +using namespace dev::p2p; + +void Capability::disable(std::string const& _problem) +{ + clog(NetConnect) << "Disabling capability '" << m_host->name() << "'. Reason:" << _problem; + m_enabled = false; +} + +RLPStream& Capability::prep(RLPStream& _s) +{ + return Session::prep(_s); +} + +void Capability::sealAndSend(RLPStream& _s) +{ + m_session->sealAndSend(_s); +} + +void Capability::sendDestroy(bytes& _msg) +{ + m_session->sendDestroy(_msg); +} + +void Capability::send(bytesConstRef _msg) +{ + m_session->send(_msg); +} + +void Capability::addRating(unsigned _r) +{ + m_session->addRating(_r); +} diff --git a/libp2p/Capability.h b/libp2p/Capability.h new file mode 100644 index 000000000..bffd38c79 --- /dev/null +++ b/libp2p/Capability.h @@ -0,0 +1,65 @@ +/* + 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 . +*/ +/** @file Capability.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include "Common.h" +#include "HostCapability.h" + +namespace dev +{ +namespace p2p +{ + +class Capability +{ + friend class Session; + +public: + Capability(Session* _s, HostCapabilityFace* _h): m_session(_s), m_host(_h) {} + virtual ~Capability() {} + + /// Must return the capability name. + static std::string name() { return ""; } + + Session* session() const { return m_session; } + HostCapabilityFace* hostCapability() const { return m_host; } + +protected: + virtual bool interpret(RLP const&) = 0; + + void disable(std::string const& _problem); + + static RLPStream& prep(RLPStream& _s); + void sealAndSend(RLPStream& _s); + void sendDestroy(bytes& _msg); + void send(bytesConstRef _msg); + + void addRating(unsigned _r); + +private: + Session* m_session; + HostCapabilityFace* m_host; + bool m_enabled = true; +}; + +} +} diff --git a/libethereum/PeerNetwork.cpp b/libp2p/Common.cpp similarity index 87% rename from libethereum/PeerNetwork.cpp rename to libp2p/Common.cpp index 2f93ee74c..21f23696a 100644 --- a/libethereum/PeerNetwork.cpp +++ b/libp2p/Common.cpp @@ -14,20 +14,21 @@ You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ -/** @file PeerNetwork.cpp +/** @file Common.cpp * @author Gav Wood * @date 2014 */ -#include "PeerNetwork.h" +#include "Common.h" using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::p2p; // Helper function to determine if an address falls within one of the reserved ranges // For V4: // Class A "10.*", Class B "172.[16->31].*", Class C "192.168.*" // Not implemented yet for V6 -bool eth::isPrivateAddress(bi::address _addressToCheck) +bool p2p::isPrivateAddress(bi::address _addressToCheck) { if (_addressToCheck.is_v4()) { @@ -35,7 +36,7 @@ bool eth::isPrivateAddress(bi::address _addressToCheck) bi::address_v4::bytes_type bytesToCheck = v4Address.to_bytes(); if (bytesToCheck[0] == 10 || bytesToCheck[0] == 127) return true; - if (bytesToCheck[0] == 172 && (bytesToCheck[1] >= 16 && bytesToCheck[1] <=31)) + if (bytesToCheck[0] == 172 && (bytesToCheck[1] >= 16 && bytesToCheck[1] <= 31)) return true; if (bytesToCheck[0] == 192 && bytesToCheck[1] == 168) return true; @@ -43,7 +44,7 @@ bool eth::isPrivateAddress(bi::address _addressToCheck) return false; } -std::string eth::reasonOf(DisconnectReason _r) +std::string p2p::reasonOf(DisconnectReason _r) { switch (_r) { @@ -53,10 +54,8 @@ std::string eth::reasonOf(DisconnectReason _r) case UselessPeer: return "Peer had no use for this node."; case TooManyPeers: return "Peer had too many connections."; case DuplicatePeer: return "Peer was already connected."; - case WrongGenesis: return "Disagreement over genesis block."; case IncompatibleProtocol: return "Peer protocol versions are incompatible."; case ClientQuit: return "Peer is exiting."; default: return "Unknown reason."; } } - diff --git a/libethereum/PeerNetwork.h b/libp2p/Common.h similarity index 72% rename from libethereum/PeerNetwork.h rename to libp2p/Common.h index 7f6b00b5d..a83ac03de 100644 --- a/libethereum/PeerNetwork.h +++ b/libp2p/Common.h @@ -14,11 +14,11 @@ You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ -/** @file PeerNetwork.h +/** @file Common.h * @author Gav Wood * @date 2014 * - * Miscellanea required for the PeerServer/PeerSession classes. + * Miscellanea required for the Host/Session classes. */ #pragma once @@ -27,31 +27,38 @@ #include #include #include -#include -#include +#include +#include +#include namespace ba = boost::asio; namespace bi = boost::asio::ip; -namespace eth +namespace dev +{ + +class RLP; +class RLPStream; + +namespace p2p { bool isPrivateAddress(bi::address _addressToCheck); -class OverlayDB; -class BlockChain; -class TransactionQueue; -class PeerServer; -class PeerSession; +class UPnP; +class Capability; +class Host; +class Session; struct NetWarn: public LogChannel { static const char* name() { return "!N!"; } static const int verbosity = 0; }; struct NetNote: public LogChannel { static const char* name() { return "*N*"; } static const int verbosity = 1; }; struct NetMessageSummary: public LogChannel { static const char* name() { return "-N-"; } static const int verbosity = 2; }; -struct NetMessageDetail: public LogChannel { static const char* name() { return "=N="; } static const int verbosity = 3; }; -struct NetTriviaSummary: public LogChannel { static const char* name() { return "-N-"; } static const int verbosity = 4; }; -struct NetTriviaDetail: public LogChannel { static const char* name() { return "=N="; } static const int verbosity = 5; }; -struct NetAllDetail: public LogChannel { static const char* name() { return "=N="; } static const int verbosity = 6; }; -struct NetRight: public LogChannel { static const char* name() { return ">N>"; } static const int verbosity = 8; }; -struct NetLeft: public LogChannel { static const char* name() { return "N>"; } static const int verbosity = 14; }; +struct NetLeft: public LogChannel { static const char* name() { return " caps; }; -class UPnP; - -enum class NodeMode -{ - Full, - PeerServer -}; - +} } diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp new file mode 100644 index 000000000..9d033e6f8 --- /dev/null +++ b/libp2p/Host.cpp @@ -0,0 +1,556 @@ +/* + 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 . +*/ +/** @file Host.cpp + * @authors: + * Gav Wood + * Eric Lombrozo (Windows version of populateAddresses()) + * @date 2014 + */ + +#include "Host.h" + +#include +#ifdef _WIN32 +// winsock is already included +// #include +#else +#include +#endif + +#include +#include +#include +#include +#include +#include +#include "Session.h" +#include "Capability.h" +#include "UPnP.h" +using namespace std; +using namespace dev; +using namespace dev::p2p; + +// Addresses we will skip during network interface discovery +// Use a vector as the list is small +// Why this and not names? +// Under MacOSX loopback (127.0.0.1) can be named lo0 and br0 are bridges (0.0.0.0) +static const set c_rejectAddresses = { + {bi::address_v4::from_string("127.0.0.1")}, + {bi::address_v6::from_string("::1")}, + {bi::address_v4::from_string("0.0.0.0")}, + {bi::address_v6::from_string("::")} +}; + +Host::Host(std::string const& _clientVersion, NetworkPreferences const& _n, bool _start): + Worker("p2p"), + m_clientVersion(_clientVersion), + m_netPrefs(_n), + m_acceptor(m_ioService), + m_socket(m_ioService), + m_id(h512::random()) +{ + populateAddresses(); + m_lastPeersRequest = chrono::steady_clock::time_point::min(); + clog(NetNote) << "Id:" << m_id.abridged(); + if (_start) + start(); +} + +Host::~Host() +{ + stop(); +} + +void Host::start() +{ + if (isWorking()) + stop(); + + for (unsigned i = 0; i < 2; ++i) + { + bi::tcp::endpoint endpoint(bi::tcp::v4(), i ? 0 : m_netPrefs.listenPort); + try + { + m_acceptor.open(endpoint.protocol()); + m_acceptor.set_option(ba::socket_base::reuse_address(true)); + m_acceptor.bind(endpoint); + m_acceptor.listen(); + m_listenPort = i ? m_acceptor.local_endpoint().port() : m_netPrefs.listenPort; + break; + } + catch (...) + { + if (i) + { + cwarn << "Couldn't start accepting connections on host. Something very wrong with network?"; + return; + } + m_acceptor.close(); + continue; + } + } + + determinePublic(m_netPrefs.publicIP, m_netPrefs.upnp); + ensureAccepting(); + + m_incomingPeers.clear(); + m_freePeers.clear(); + + m_lastPeersRequest = chrono::steady_clock::time_point::min(); + clog(NetNote) << "Id:" << m_id.abridged(); + + for (auto const& h: m_capabilities) + h.second->onStarting(); + + startWorking(); +} + +void Host::stop() +{ + for (auto const& h: m_capabilities) + h.second->onStopping(); + + stopWorking(); + + if (m_acceptor.is_open()) + { + if (m_accepting) + m_acceptor.cancel(); + m_acceptor.close(); + m_accepting = false; + } + if (m_socket.is_open()) + m_socket.close(); + disconnectPeers(); + + m_ioService.reset(); +} + +unsigned Host::protocolVersion() const +{ + return 0; +} + +void Host::registerPeer(std::shared_ptr _s, vector const& _caps) +{ + { + Guard l(x_peers); + m_peers[_s->m_id] = _s; + } + for (auto const& i: _caps) + if (haveCapability(i)) + _s->m_capabilities[i] = shared_ptr(m_capabilities[i]->newPeerCapability(_s.get())); +} + +void Host::disconnectPeers() +{ + for (unsigned n = 0;; n = 0) + { + { + Guard l(x_peers); + for (auto i: m_peers) + if (auto p = i.second.lock()) + { + p->disconnect(ClientQuit); + n++; + } + } + if (!n) + break; + m_ioService.poll(); + this_thread::sleep_for(chrono::milliseconds(100)); + } + + delete m_upnp; +} + +void Host::seal(bytes& _b) +{ + _b[0] = 0x22; + _b[1] = 0x40; + _b[2] = 0x08; + _b[3] = 0x91; + uint32_t len = (uint32_t)_b.size() - 8; + _b[4] = (len >> 24) & 0xff; + _b[5] = (len >> 16) & 0xff; + _b[6] = (len >> 8) & 0xff; + _b[7] = len & 0xff; +} + +void Host::determinePublic(string const& _publicAddress, bool _upnp) +{ + if (_upnp) + try + { + m_upnp = new UPnP; + } + catch (NoUPnPDevice) {} // let m_upnp continue as null - we handle it properly. + + bi::tcp::resolver r(m_ioService); + if (m_upnp && m_upnp->isValid() && m_peerAddresses.size()) + { + clog(NetNote) << "External addr:" << m_upnp->externalIP(); + int p = m_upnp->addRedirect(m_peerAddresses[0].to_string().c_str(), m_listenPort); + if (p) + clog(NetNote) << "Punched through NAT and mapped local port" << m_listenPort << "onto external port" << p << "."; + else + { + // couldn't map + clog(NetWarn) << "Couldn't punch through NAT (or no NAT in place). Assuming" << m_listenPort << "is local & external port."; + p = m_listenPort; + } + + auto eip = m_upnp->externalIP(); + if (eip == string("0.0.0.0") && _publicAddress.empty()) + m_public = bi::tcp::endpoint(bi::address(), (unsigned short)p); + else + { + m_public = bi::tcp::endpoint(bi::address::from_string(_publicAddress.empty() ? eip : _publicAddress), (unsigned short)p); + m_addresses.push_back(m_public.address().to_v4()); + } + } + else + { + // No UPnP - fallback on given public address or, if empty, the assumed peer address. + m_public = bi::tcp::endpoint(_publicAddress.size() ? bi::address::from_string(_publicAddress) + : m_peerAddresses.size() ? m_peerAddresses[0] + : bi::address(), m_listenPort); + m_addresses.push_back(m_public.address().to_v4()); + } +} + +void Host::populateAddresses() +{ +#ifdef _WIN32 + WSAData wsaData; + if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) + throw NoNetworking(); + + char ac[80]; + if (gethostname(ac, sizeof(ac)) == SOCKET_ERROR) + { + clog(NetWarn) << "Error " << WSAGetLastError() << " when getting local host name."; + WSACleanup(); + throw NoNetworking(); + } + + struct hostent* phe = gethostbyname(ac); + if (phe == 0) + { + clog(NetWarn) << "Bad host lookup."; + WSACleanup(); + throw NoNetworking(); + } + + for (int i = 0; phe->h_addr_list[i] != 0; ++i) + { + struct in_addr addr; + memcpy(&addr, phe->h_addr_list[i], sizeof(struct in_addr)); + char *addrStr = inet_ntoa(addr); + bi::address ad(bi::address::from_string(addrStr)); + m_addresses.push_back(ad.to_v4()); + bool isLocal = std::find(c_rejectAddresses.begin(), c_rejectAddresses.end(), ad) != c_rejectAddresses.end(); + if (!isLocal) + m_peerAddresses.push_back(ad.to_v4()); + clog(NetNote) << "Address: " << ac << " = " << m_addresses.back() << (isLocal ? " [LOCAL]" : " [PEER]"); + } + + WSACleanup(); +#else + ifaddrs* ifaddr; + if (getifaddrs(&ifaddr) == -1) + throw NoNetworking(); + + bi::tcp::resolver r(m_ioService); + + for (ifaddrs* ifa = ifaddr; ifa; ifa = ifa->ifa_next) + { + if (!ifa->ifa_addr) + continue; + if (ifa->ifa_addr->sa_family == AF_INET) + { + char host[NI_MAXHOST]; + if (getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST)) + continue; + try + { + auto it = r.resolve({host, "30303"}); + bi::tcp::endpoint ep = it->endpoint(); + bi::address ad = ep.address(); + m_addresses.push_back(ad.to_v4()); + bool isLocal = std::find(c_rejectAddresses.begin(), c_rejectAddresses.end(), ad) != c_rejectAddresses.end(); + if (!isLocal) + m_peerAddresses.push_back(ad.to_v4()); + clog(NetNote) << "Address: " << host << " = " << m_addresses.back() << (isLocal ? " [LOCAL]" : " [PEER]"); + } + catch (...) + { + clog(NetNote) << "Couldn't resolve: " << host; + } + } + } + + freeifaddrs(ifaddr); +#endif +} + +std::map Host::potentialPeers() +{ + std::map ret; + if (!m_public.address().is_unspecified()) + ret.insert(make_pair(m_id, m_public)); + Guard l(x_peers); + for (auto i: m_peers) + if (auto j = i.second.lock()) + { + auto ep = j->endpoint(); +// cnote << "Checking potential peer" << j->m_listenPort << j->endpoint() << isPrivateAddress(ep.address()) << ep.port() << j->m_id.abridged(); + // Skip peers with a listen port of zero or are on a private network + bool peerOnNet = (j->m_listenPort != 0 && (!isPrivateAddress(ep.address()) || m_netPrefs.localNetworking)); + if (!peerOnNet && m_incomingPeers.count(j->m_id)) + { + ep = m_incomingPeers.at(j->m_id).first; + peerOnNet = (j->m_listenPort != 0 && (!isPrivateAddress(ep.address()) || m_netPrefs.localNetworking)); + } + if (peerOnNet && ep.port() && j->m_id) + ret.insert(make_pair(i.first, ep)); + } + return ret; +} + +void Host::ensureAccepting() +{ + if (!m_accepting) + { + clog(NetConnect) << "Listening on local port " << m_listenPort << " (public: " << m_public << ")"; + m_accepting = true; + m_acceptor.async_accept(m_socket, [=](boost::system::error_code ec) + { + if (!ec) + try + { + try { + clog(NetConnect) << "Accepted connection from " << m_socket.remote_endpoint(); + } catch (...){} + bi::address remoteAddress = m_socket.remote_endpoint().address(); + // Port defaults to 0 - we let the hello tell us which port the peer listens to + auto p = std::make_shared(this, std::move(m_socket), remoteAddress); + p->start(); + } + catch (std::exception const& _e) + { + clog(NetWarn) << "ERROR: " << _e.what(); + } + m_accepting = false; + if (ec.value() < 1) + ensureAccepting(); + }); + } +} + +string Host::pocHost() +{ + vector strs; + boost::split(strs, dev::Version, boost::is_any_of(".")); + return "poc-" + strs[1] + ".ethdev.com"; +} + +void Host::connect(std::string const& _addr, unsigned short _port) noexcept +{ + for (int i = 0; i < 2; ++i) + try + { + if (i == 0) + { + bi::tcp::resolver r(m_ioService); + connect(r.resolve({_addr, toString(_port)})->endpoint()); + } + else + connect(bi::tcp::endpoint(bi::address::from_string(_addr), _port)); + } + catch (exception const& e) + { + // Couldn't connect + clog(NetConnect) << "Bad host " << _addr << " (" << e.what() << ")"; + } +} + +void Host::connect(bi::tcp::endpoint const& _ep) +{ + clog(NetConnect) << "Attempting connection to " << _ep; + bi::tcp::socket* s = new bi::tcp::socket(m_ioService); + s->async_connect(_ep, [=](boost::system::error_code const& ec) + { + if (ec) + { + clog(NetConnect) << "Connection refused to " << _ep << " (" << ec.message() << ")"; + for (auto i = m_incomingPeers.begin(); i != m_incomingPeers.end(); ++i) + if (i->second.first == _ep && i->second.second < 3) + { + m_freePeers.push_back(i->first); + goto OK; + } + // for-else + clog(NetConnect) << "Giving up."; + OK:; + } + else + { + auto p = make_shared(this, std::move(*s), _ep.address(), _ep.port()); + clog(NetConnect) << "Connected to " << _ep; + p->start(); + } + delete s; + }); +} + +bool Host::havePeer(h512 _id) const +{ + Guard l(x_peers); + + // Remove dead peers from list. + for (auto i = m_peers.begin(); i != m_peers.end();) + if (i->second.lock().get()) + ++i; + else + i = m_peers.erase(i); + + return !!m_peers.count(_id); +} + +void Host::growPeers() +{ + Guard l(x_peers); + while (m_peers.size() < m_idealPeerCount) + { + if (m_freePeers.empty()) + { + if (chrono::steady_clock::now() > m_lastPeersRequest + chrono::seconds(10)) + { + RLPStream s; + bytes b; + (Session::prep(s).appendList(1) << GetPeersPacket).swapOut(b); + seal(b); + for (auto const& i: m_peers) + if (auto p = i.second.lock()) + if (p->isOpen()) + p->send(&b); + m_lastPeersRequest = chrono::steady_clock::now(); + } + + if (!m_accepting) + ensureAccepting(); + + break; + } + + auto x = time(0) % m_freePeers.size(); + m_incomingPeers[m_freePeers[x]].second++; + if (!m_peers.count(m_freePeers[x])) + connect(m_incomingPeers[m_freePeers[x]].first); + m_freePeers.erase(m_freePeers.begin() + x); + } +} + +void Host::prunePeers() +{ + Guard l(x_peers); + // We'll keep at most twice as many as is ideal, halfing what counts as "too young to kill" until we get there. + for (unsigned old = 15000; m_peers.size() > m_idealPeerCount * 2 && old > 100; old /= 2) + while (m_peers.size() > m_idealPeerCount) + { + // look for worst peer to kick off + // first work out how many are old enough to kick off. + shared_ptr worst; + unsigned agedPeers = 0; + for (auto i: m_peers) + if (auto p = i.second.lock()) + if (/*(m_mode != NodeMode::Host || p->m_caps != 0x01) &&*/ chrono::steady_clock::now() > p->m_connect + chrono::milliseconds(old)) // don't throw off new peers; peer-servers should never kick off other peer-servers. + { + ++agedPeers; + if ((!worst || p->m_rating < worst->m_rating || (p->m_rating == worst->m_rating && p->m_connect > worst->m_connect))) // kill older ones + worst = p; + } + if (!worst || agedPeers <= m_idealPeerCount) + break; + worst->disconnect(TooManyPeers); + } + + // Remove dead peers from list. + for (auto i = m_peers.begin(); i != m_peers.end();) + if (i->second.lock().get()) + ++i; + else + i = m_peers.erase(i); +} + +std::vector Host::peers(bool _updatePing) const +{ + Guard l(x_peers); + if (_updatePing) + const_cast(this)->pingAll(); + this_thread::sleep_for(chrono::milliseconds(200)); + std::vector ret; + for (auto& i: m_peers) + if (auto j = i.second.lock()) + if (j->m_socket.is_open()) + ret.push_back(j->m_info); + return ret; +} + +void Host::doWork() +{ + growPeers(); + prunePeers(); + m_ioService.poll(); +} + +void Host::pingAll() +{ + Guard l(x_peers); + for (auto& i: m_peers) + if (auto j = i.second.lock()) + j->ping(); +} + +bytes Host::savePeers() const +{ + Guard l(x_peers); + RLPStream ret; + int n = 0; + for (auto& i: m_peers) + if (auto p = i.second.lock()) + if (p->m_socket.is_open() && p->endpoint().port()) + { + ret.appendList(3) << p->endpoint().address().to_v4().to_bytes() << p->endpoint().port() << p->m_id; + n++; + } + return RLPStream(n).appendRaw(ret.out(), n).out(); +} + +void Host::restorePeers(bytesConstRef _b) +{ + for (auto i: RLP(_b)) + { + auto k = (h512)i[2]; + if (!m_incomingPeers.count(k)) + { + m_incomingPeers.insert(make_pair(k, make_pair(bi::tcp::endpoint(bi::address_v4(i[0].toArray()), i[1].toInt()), 0))); + m_freePeers.push_back(k); + } + } +} diff --git a/libp2p/Host.h b/libp2p/Host.h new file mode 100644 index 000000000..f5f2f9e97 --- /dev/null +++ b/libp2p/Host.h @@ -0,0 +1,175 @@ +/* + 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 . +*/ +/** @file EthereumHost.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "HostCapability.h" +namespace ba = boost::asio; +namespace bi = boost::asio::ip; + +namespace dev +{ + +class RLPStream; + +namespace p2p +{ + +struct NetworkPreferences +{ + NetworkPreferences(unsigned short p = 30303, std::string i = std::string(), bool u = true, bool l = false): listenPort(p), publicIP(i), upnp(u), localNetworking(l) {} + + unsigned short listenPort = 30303; + std::string publicIP; + bool upnp = true; + bool localNetworking = false; +}; + +/** + * @brief The Host class + * Capabilities should be registered prior to startNetwork, since m_capabilities is not thread-safe. + */ +class Host: public Worker +{ + friend class Session; + friend class HostCapabilityFace; + +public: + /// Start server, listening for connections on the given port. + Host(std::string const& _clientVersion, NetworkPreferences const& _n = NetworkPreferences(), bool _start = false); + + /// Will block on network process events. + virtual ~Host(); + + /// Closes all peers. + void disconnectPeers(); + + /// Basic peer network protocol version. + unsigned protocolVersion() const; + + /// Register a peer-capability; all new peer connections will have this capability. + template std::shared_ptr registerCapability(T* _t) { _t->m_host = this; auto ret = std::shared_ptr(_t); m_capabilities[T::staticName()] = ret; return ret; } + + bool haveCapability(std::string const& _name) const { return m_capabilities.count(_name) != 0; } + std::vector caps() const { std::vector ret; for (auto const& i: m_capabilities) ret.push_back(i.first); return ret; } + template std::shared_ptr cap() const { try { return std::static_pointer_cast(m_capabilities.at(T::staticName())); } catch (...) { return nullptr; } } + + /// Connect to a peer explicitly. + static std::string pocHost(); + void connect(std::string const& _addr, unsigned short _port = 30303) noexcept; + void connect(bi::tcp::endpoint const& _ep); + + /// @returns true iff we have the a peer of the given id. + bool havePeer(h512 _id) const; + + /// Set ideal number of peers. + void setIdealPeerCount(unsigned _n) { m_idealPeerCount = _n; } + + /// Get peer information. + std::vector peers(bool _updatePing = false) const; + + /// Get number of peers connected; equivalent to, but faster than, peers().size(). + size_t peerCount() const { Guard l(x_peers); return m_peers.size(); } + + /// Ping the peers, to update the latency information. + void pingAll(); + + /// Get the port we're listening on currently. + unsigned short listenPort() const { return m_public.port(); } + + /// Serialise the set of known peers. + bytes savePeers() const; + + /// Deserialise the data and populate the set of known peers. + void restorePeers(bytesConstRef _b); + + void setNetworkPreferences(NetworkPreferences const& _p) { stop(); m_netPrefs = _p; start(); } + + void start(); + void stop(); + bool isStarted() const { return isWorking(); } + + h512 id() const { return m_id; } + + void registerPeer(std::shared_ptr _s, std::vector const& _caps); + +private: + /// Called when the session has provided us with a new peer we can connect to. + void noteNewPeers() {} + + void seal(bytes& _b); + void populateAddresses(); + void determinePublic(std::string const& _publicAddress, bool _upnp); + void ensureAccepting(); + + void growPeers(); + void prunePeers(); + + /// Conduct I/O, polling, syncing, whatever. + /// Ideally all time-consuming I/O is done in a background thread or otherwise asynchronously, but you get this call every 100ms or so anyway. + /// This won't touch alter the blockchain. + virtual void doWork(); + + std::map potentialPeers(); + + std::string m_clientVersion; + + NetworkPreferences m_netPrefs; + + static const int NetworkStopped = -1; + int m_listenPort = NetworkStopped; + + ba::io_service m_ioService; + bi::tcp::acceptor m_acceptor; + bi::tcp::socket m_socket; + + UPnP* m_upnp = nullptr; + bi::tcp::endpoint m_public; + h512 m_id; + + mutable std::mutex x_peers; + mutable std::map> m_peers; // mutable because we flush zombie entries (null-weakptrs) as regular maintenance from a const method. + + std::map> m_incomingPeers; // TODO: does this need a lock? + std::vector m_freePeers; + + std::chrono::steady_clock::time_point m_lastPeersRequest; + unsigned m_idealPeerCount = 5; + + std::vector m_addresses; + std::vector m_peerAddresses; + + std::map> m_capabilities; + + bool m_accepting = false; +}; + +} +} diff --git a/libp2p/HostCapability.cpp b/libp2p/HostCapability.cpp new file mode 100644 index 000000000..a3a47cd5c --- /dev/null +++ b/libp2p/HostCapability.cpp @@ -0,0 +1,44 @@ +/* + 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 . +*/ +/** @file HostCapability.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "HostCapability.h" + +#include "Session.h" +#include "Host.h" +using namespace std; +using namespace dev; +using namespace dev::p2p; + +void HostCapabilityFace::seal(bytes& _b) +{ + m_host->seal(_b); +} + +std::vector > HostCapabilityFace::peers() const +{ + Guard l(m_host->x_peers); + std::vector > ret; + for (auto const& i: m_host->m_peers) + if (std::shared_ptr p = i.second.lock()) + if (p->m_capabilities.count(name())) + ret.push_back(p); + return ret; +} diff --git a/libp2p/HostCapability.h b/libp2p/HostCapability.h new file mode 100644 index 000000000..1c532788b --- /dev/null +++ b/libp2p/HostCapability.h @@ -0,0 +1,77 @@ +/* + 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 . +*/ +/** @file Common.h + * @author Gav Wood + * @date 2014 + * + * Miscellanea required for the Host/Session classes. + */ + +#pragma once + +#include "Common.h" + +namespace dev +{ + +namespace p2p +{ + +class HostCapabilityFace +{ + friend class Host; + template friend class HostCapability; + friend class Capability; + +public: + HostCapabilityFace() {} + virtual ~HostCapabilityFace() {} + + Host* host() const { return m_host; } + + std::vector > peers() const; + +protected: + virtual std::string name() const = 0; + virtual Capability* newPeerCapability(Session* _s) = 0; + + virtual void onStarting() {} + virtual void onStopping() {} + + void seal(bytes& _b); + +private: + Host* m_host = nullptr; +}; + +template +class HostCapability: public HostCapabilityFace +{ +public: + HostCapability() {} + virtual ~HostCapability() {} + + static std::string staticName() { return PeerCap::name(); } + +protected: + virtual std::string name() const { return PeerCap::name(); } + virtual Capability* newPeerCapability(Session* _s) { return new PeerCap(_s, this); } +}; + +} + +} diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp new file mode 100644 index 000000000..2dc60e0de --- /dev/null +++ b/libp2p/Session.cpp @@ -0,0 +1,431 @@ +/* + 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 . +*/ +/** @file Session.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "Session.h" + +#include +#include +#include +#include "Host.h" +#include "Capability.h" +using namespace std; +using namespace dev; +using namespace dev::p2p; + +#define clogS(X) dev::LogOutputStream(false) << "| " << std::setw(2) << m_socket.native_handle() << "] " + +Session::Session(Host* _s, bi::tcp::socket _socket, bi::address _peerAddress, unsigned short _peerPort): + m_server(_s), + m_socket(std::move(_socket)), + m_listenPort(_peerPort), + m_rating(0) +{ + m_disconnect = std::chrono::steady_clock::time_point::max(); + m_connect = std::chrono::steady_clock::now(); + m_info = PeerInfo({"?", _peerAddress.to_string(), m_listenPort, std::chrono::steady_clock::duration(0)}); +} + +Session::~Session() +{ + // Read-chain finished for one reason or another. + for (auto& i: m_capabilities) + i.second.reset(); + + try + { + if (m_socket.is_open()) + m_socket.close(); + } + catch (...){} +} + +bi::tcp::endpoint Session::endpoint() const +{ + if (m_socket.is_open()) + try + { + return bi::tcp::endpoint(m_socket.remote_endpoint().address(), m_listenPort); + } + catch (...){} + + return bi::tcp::endpoint(); +} + +bool Session::interpret(RLP const& _r) +{ + clogS(NetRight) << _r; + switch (_r[0].toInt()) + { + case HelloPacket: + { + m_protocolVersion = _r[1].toInt(); + auto clientVersion = _r[2].toString(); + auto caps = _r[3].toVector(); + m_listenPort = _r[4].toInt(); + m_id = _r[5].toHash(); + + clogS(NetMessageSummary) << "Hello: " << clientVersion << "V[" << m_protocolVersion << "]" << m_id.abridged() << showbase << hex << caps << dec << m_listenPort; + + if (m_server->havePeer(m_id)) + { + // Already connected. + cwarn << "Already have peer id" << m_id.abridged();// << "at" << l->endpoint() << "rather than" << endpoint(); + disconnect(DuplicatePeer); + return false; + } + if (!m_id) + { + disconnect(InvalidIdentity); + return false; + } + if (m_protocolVersion != m_server->protocolVersion()) + { + disconnect(IncompatibleProtocol); + return false; + } + try + { m_info = PeerInfo({clientVersion, m_socket.remote_endpoint().address().to_string(), m_listenPort, std::chrono::steady_clock::duration(), _r[3].toSet()}); } + catch (...) + { + disconnect(BadProtocol); + return false; + } + + m_server->registerPeer(shared_from_this(), caps); + break; + } + case DisconnectPacket: + { + string reason = "Unspecified"; + if (_r[1].isInt()) + reason = reasonOf((DisconnectReason)_r[1].toInt()); + + clogS(NetMessageSummary) << "Disconnect (reason: " << reason << ")"; + if (m_socket.is_open()) + clogS(NetNote) << "Closing " << m_socket.remote_endpoint(); + else + clogS(NetNote) << "Remote closed."; + m_socket.close(); + return false; + } + case PingPacket: + { + clogS(NetTriviaSummary) << "Ping"; + RLPStream s; + sealAndSend(prep(s).appendList(1) << PongPacket); + break; + } + case PongPacket: + m_info.lastPing = std::chrono::steady_clock::now() - m_ping; + clogS(NetTriviaSummary) << "Latency: " << chrono::duration_cast(m_info.lastPing).count() << " ms"; + break; + case GetPeersPacket: + { + clogS(NetTriviaSummary) << "GetPeers"; + auto peers = m_server->potentialPeers(); + RLPStream s; + prep(s).appendList(peers.size() + 1); + s << PeersPacket; + for (auto i: peers) + { + clogS(NetTriviaDetail) << "Sending peer " << i.first.abridged() << i.second; + s.appendList(3) << bytesConstRef(i.second.address().to_v4().to_bytes().data(), 4) << i.second.port() << i.first; + } + sealAndSend(s); + break; + } + case PeersPacket: + clogS(NetTriviaSummary) << "Peers (" << dec << (_r.itemCount() - 1) << " entries)"; + for (unsigned i = 1; i < _r.itemCount(); ++i) + { + bi::address_v4 peerAddress(_r[i][0].toHash>().asArray()); + auto ep = bi::tcp::endpoint(peerAddress, _r[i][1].toInt()); + h512 id = _r[i][2].toHash(); + clogS(NetAllDetail) << "Checking: " << ep << "(" << id.abridged() << ")" << isPrivateAddress(peerAddress) << m_id.abridged() << isPrivateAddress(endpoint().address()) << m_server->m_incomingPeers.count(id) << (m_server->m_incomingPeers.count(id) ? isPrivateAddress(m_server->m_incomingPeers.at(id).first.address()) : -1); + + if (isPrivateAddress(peerAddress) && !m_server->m_netPrefs.localNetworking) + goto CONTINUE; + + // check that it's not us or one we already know: + if (!(m_id == id && isPrivateAddress(endpoint().address()) && (!m_server->m_incomingPeers.count(id) || isPrivateAddress(m_server->m_incomingPeers.at(id).first.address()))) && (!id || m_server->m_id == id || m_server->m_incomingPeers.count(id))) + goto CONTINUE; + + // check that we're not already connected to addr: + if (!ep.port()) + goto CONTINUE; + for (auto i: m_server->m_addresses) + if (ep.address() == i && ep.port() == m_server->listenPort()) + goto CONTINUE; + for (auto i: m_server->m_incomingPeers) + if (i.second.first == ep) + goto CONTINUE; + m_server->m_incomingPeers[id] = make_pair(ep, 0); + m_server->m_freePeers.push_back(id); + m_server->noteNewPeers(); + clogS(NetTriviaDetail) << "New peer: " << ep << "(" << id .abridged()<< ")"; + CONTINUE:; + } + break; + default: + for (auto const& i: m_capabilities) + if (i.second->m_enabled && i.second->interpret(_r)) + return true; + return false; + } + return true; +} + +void Session::ping() +{ + RLPStream s; + sealAndSend(prep(s).appendList(1) << PingPacket); + m_ping = std::chrono::steady_clock::now(); +} + +void Session::getPeers() +{ + RLPStream s; + sealAndSend(prep(s).appendList(1) << GetPeersPacket); +} + +RLPStream& Session::prep(RLPStream& _s) +{ + return _s.appendRaw(bytes(8, 0)); +} + +void Session::sealAndSend(RLPStream& _s) +{ + bytes b; + _s.swapOut(b); + m_server->seal(b); + sendDestroy(b); +} + +bool Session::checkPacket(bytesConstRef _msg) +{ + if (_msg.size() < 8) + return false; + if (!(_msg[0] == 0x22 && _msg[1] == 0x40 && _msg[2] == 0x08 && _msg[3] == 0x91)) + return false; + uint32_t len = ((_msg[4] * 256 + _msg[5]) * 256 + _msg[6]) * 256 + _msg[7]; + if (_msg.size() != len + 8) + return false; + RLP r(_msg.cropped(8)); + if (r.actualSize() != len) + return false; + return true; +} + +void Session::sendDestroy(bytes& _msg) +{ + clogS(NetLeft) << RLP(bytesConstRef(&_msg).cropped(8)); + + if (!checkPacket(bytesConstRef(&_msg))) + { + cwarn << "INVALID PACKET CONSTRUCTED!"; + } + + bytes buffer = bytes(std::move(_msg)); + writeImpl(buffer); +} + +void Session::send(bytesConstRef _msg) +{ + clogS(NetLeft) << RLP(_msg.cropped(8)); + + if (!checkPacket(_msg)) + { + cwarn << "INVALID PACKET CONSTRUCTED!"; + } + + bytes buffer = bytes(_msg.toBytes()); + writeImpl(buffer); +} + +void Session::writeImpl(bytes& _buffer) +{ +// cerr << (void*)this << " writeImpl" << endl; + if (!m_socket.is_open()) + return; + + bool doWrite = false; + { + lock_guard l(m_writeLock); + m_writeQueue.push_back(_buffer); + doWrite = (m_writeQueue.size() == 1); + } + + if (doWrite) + write(); +} + +void Session::write() +{ + const bytes& bytes = m_writeQueue[0]; + auto self(shared_from_this()); + ba::async_write(m_socket, ba::buffer(bytes), [this, self](boost::system::error_code ec, std::size_t /*length*/) + { +// cerr << (void*)this << " write.callback" << endl; + + // must check queue, as write callback can occur following dropped() + if (ec) + { + cwarn << "Error sending: " << ec.message(); + dropped(); + return; + } + else + { + lock_guard l(m_writeLock); + m_writeQueue.pop_front(); + if (m_writeQueue.empty()) + return; + } + write(); + }); +} + +void Session::dropped() +{ +// cerr << (void*)this << " dropped" << endl; + if (m_socket.is_open()) + try + { + clogS(NetConnect) << "Closing " << m_socket.remote_endpoint(); + m_socket.close(); + } + catch (...) {} +} + +void Session::disconnect(int _reason) +{ + clogS(NetConnect) << "Disconnecting (reason:" << reasonOf((DisconnectReason)_reason) << ")"; + if (m_socket.is_open()) + { + if (m_disconnect == chrono::steady_clock::time_point::max()) + { + RLPStream s; + prep(s); + s.appendList(2) << DisconnectPacket << _reason; + sealAndSend(s); + m_disconnect = chrono::steady_clock::now(); + } + else + dropped(); + } +} + +void Session::start() +{ + RLPStream s; + prep(s); + s.appendList(6) << HelloPacket + << m_server->protocolVersion() + << m_server->m_clientVersion + << m_server->caps() + << m_server->m_public.port() + << m_server->m_id; + sealAndSend(s); + ping(); + getPeers(); + + doRead(); +} + +void Session::doRead() +{ + // ignore packets received while waiting to disconnect + if (chrono::steady_clock::now() - m_disconnect > chrono::seconds(0)) + return; + + auto self(shared_from_this()); + m_socket.async_read_some(boost::asio::buffer(m_data), [this,self](boost::system::error_code ec, std::size_t length) + { + // If error is end of file, ignore + if (ec && ec.category() != boost::asio::error::get_misc_category() && ec.value() != boost::asio::error::eof) + { + // got here with length of 1241... + cwarn << "Error reading: " << ec.message(); + dropped(); + } + else if (ec && length == 0) + { + return; + } + else + { + try + { + m_incoming.resize(m_incoming.size() + length); + memcpy(m_incoming.data() + m_incoming.size() - length, m_data.data(), length); + while (m_incoming.size() > 8) + { + if (m_incoming[0] != 0x22 || m_incoming[1] != 0x40 || m_incoming[2] != 0x08 || m_incoming[3] != 0x91) + { + cwarn << "INVALID SYNCHRONISATION TOKEN; expected = 22400891; received = " << toHex(bytesConstRef(m_incoming.data(), 4)); + disconnect(BadProtocol); + return; + } + else + { + uint32_t len = fromBigEndian(bytesConstRef(m_incoming.data() + 4, 4)); + uint32_t tlen = len + 8; + if (m_incoming.size() < tlen) + break; + + // enough has come in. + auto data = bytesConstRef(m_incoming.data(), tlen); + if (!checkPacket(data)) + { + cerr << "Received " << len << ": " << toHex(bytesConstRef(m_incoming.data() + 8, len)) << endl; + cwarn << "INVALID MESSAGE RECEIVED"; + disconnect(BadProtocol); + return; + } + else + { + RLP r(data.cropped(8)); + if (!interpret(r)) + { + // error + dropped(); + return; + } + } + memmove(m_incoming.data(), m_incoming.data() + tlen, m_incoming.size() - tlen); + m_incoming.resize(m_incoming.size() - tlen); + } + } + doRead(); + } + catch (Exception const& _e) + { + clogS(NetWarn) << "ERROR: " << _e.description(); + dropped(); + } + catch (std::exception const& _e) + { + clogS(NetWarn) << "ERROR: " << _e.what(); + dropped(); + } + } + }); +} diff --git a/libethereum/PeerSession.h b/libp2p/Session.h similarity index 65% rename from libethereum/PeerSession.h rename to libp2p/Session.h index 1a28c66ec..7c3fc3732 100644 --- a/libethereum/PeerSession.h +++ b/libp2p/Session.h @@ -14,7 +14,7 @@ You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ -/** @file PeerSession.h +/** @file Session.h * @author Gav Wood * @date 2014 */ @@ -23,23 +23,31 @@ #include #include +#include #include #include #include -#include -#include -#include "PeerNetwork.h" +#include +#include "Common.h" -namespace eth +namespace dev { -class PeerSession: public std::enable_shared_from_this +namespace p2p { - friend class PeerServer; + +/** + * @brief The Session class + * @todo Document fully. + */ +class Session: public std::enable_shared_from_this +{ + friend class Host; + friend class HostCapabilityFace; public: - PeerSession(PeerServer* _server, bi::tcp::socket _socket, uint _rNId, bi::address _peerAddress, unsigned short _peerPort = 0); - ~PeerSession(); + Session(Host* _server, bi::tcp::socket _socket, bi::address _peerAddress, unsigned short _peerPort = 0); + virtual ~Session(); void start(); void disconnect(int _reason); @@ -48,51 +56,58 @@ public: bool isOpen() const { return m_socket.is_open(); } + h512 id() const { return m_id; } + unsigned socketId() const { return m_socket.native_handle(); } + bi::tcp::endpoint endpoint() const; ///< for other peers to connect to. + template + std::shared_ptr cap() const { try { return std::static_pointer_cast(m_capabilities.at(PeerCap::name())); } catch (...) { return nullptr; } } + + static RLPStream& prep(RLPStream& _s); + void sealAndSend(RLPStream& _s); + void sendDestroy(bytes& _msg); + void send(bytesConstRef _msg); + + void addRating(unsigned _r) { m_rating += _r; } + private: void dropped(); void doRead(); void doWrite(std::size_t length); + void writeImpl(bytes& _buffer); + void write(); + + void getPeers(); bool interpret(RLP const& _r); /// @returns true iff the _msg forms a valid message for sending or receiving on the network. static bool checkPacket(bytesConstRef _msg); - static RLPStream& prep(RLPStream& _s); - void sealAndSend(RLPStream& _s); - void sendDestroy(bytes& _msg); - void send(bytesConstRef _msg); - void writeImpl(bytes& _buffer); - void write(); - PeerServer* m_server; + Host* m_server; - std::recursive_mutex m_writeLock; + std::mutex m_writeLock; std::deque m_writeQueue; - bi::tcp::socket m_socket; + mutable bi::tcp::socket m_socket; ///< Mutable to ask for native_handle(). std::array m_data; PeerInfo m_info; - Public m_id; + h512 m_id; bytes m_incoming; - uint m_protocolVersion; - uint m_networkId; - uint m_reqNetworkId; + unsigned m_protocolVersion; unsigned short m_listenPort; ///< Port that the remote client is listening on for connections. Useful for giving to peers. - uint m_caps; std::chrono::steady_clock::time_point m_ping; std::chrono::steady_clock::time_point m_connect; std::chrono::steady_clock::time_point m_disconnect; - uint m_rating; - bool m_requireTransactions; + unsigned m_rating; - std::set m_knownBlocks; - std::set m_knownTransactions; + std::map> m_capabilities; bool m_willBeDeleted = false; ///< True if we already posted a deleter on the strand. }; } +} diff --git a/libethcore/UPnP.cpp b/libp2p/UPnP.cpp similarity index 96% rename from libethcore/UPnP.cpp rename to libp2p/UPnP.cpp index 427450e03..4fe01159a 100644 --- a/libethcore/UPnP.cpp +++ b/libp2p/UPnP.cpp @@ -29,11 +29,12 @@ #include #include #endif -#include -#include -#include +#include +#include +#include using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::p2p; UPnP::UPnP() { @@ -126,7 +127,7 @@ int UPnP::addRedirect(char const* _addr, int _port) return _port; // Failed - now try (random external, port internal) and cycle up to 10 times. - for (uint i = 0; i < 10; ++i) + for (unsigned i = 0; i < 10; ++i) { _port = rand() % 65535 - 1024 + 1024; sprintf(port_str, "%d", _port); diff --git a/libethcore/UPnP.h b/libp2p/UPnP.h similarity index 97% rename from libethcore/UPnP.h rename to libp2p/UPnP.h index 836e350b0..0031298a6 100644 --- a/libethcore/UPnP.h +++ b/libp2p/UPnP.h @@ -29,7 +29,9 @@ struct UPNPUrls; struct IGDdatas; -namespace eth +namespace dev +{ +namespace p2p { class UPnP @@ -51,3 +53,4 @@ public: }; } +} diff --git a/libp2p/_libp2p.cpp b/libp2p/_libp2p.cpp new file mode 100644 index 000000000..440ba362b --- /dev/null +++ b/libp2p/_libp2p.cpp @@ -0,0 +1,9 @@ +#ifdef _MSC_VER +#include "All.h" +#include "Capability.cpp" +#include "Common.cpp" +#include "Host.cpp" +#include "HostCapability.cpp" +#include "Session.cpp" +#include "UPnP.cpp" +#endif diff --git a/libpyserpent/CMakeLists.txt b/libpyserpent/CMakeLists.txt index fb0c101e9..5108000f4 100644 --- a/libpyserpent/CMakeLists.txt +++ b/libpyserpent/CMakeLists.txt @@ -14,7 +14,7 @@ include_directories(..) target_link_libraries(${EXECUTABLE} serpent) target_link_libraries(${EXECUTABLE} lll) target_link_libraries(${EXECUTABLE} evmface) -target_link_libraries(${EXECUTABLE} ethential) +target_link_libraries(${EXECUTABLE} devcore) target_link_libraries(${EXECUTABLE} ${PYTHON_LS}) if("${TARGET_PLATFORM}" STREQUAL "w64") diff --git a/libpyserpent/pyserpent.cpp b/libpyserpent/pyserpent.cpp index 64fa82ded..d97fd8fed 100644 --- a/libpyserpent/pyserpent.cpp +++ b/libpyserpent/pyserpent.cpp @@ -7,9 +7,15 @@ #include #define PYMETHOD(name, FROM, method, TO) \ - static PyObject * name(PyObject *self, PyObject *args) { \ + static PyObject * name(PyObject *, PyObject *args) { \ + try { \ FROM(med) \ return TO(method(med)); \ + } \ + catch (std::string e) { \ + PyErr_SetString(PyExc_Exception, e.c_str()); \ + return NULL; \ + } \ } #define FROMSTR(v) \ @@ -128,7 +134,7 @@ PYMETHOD(ps_parse_lll, FROMSTR, parseLLL, pyifyNode) static PyMethodDef PyextMethods[] = { {"compile", ps_compile, METH_VARARGS, "Compile code."}, - {"compile_to_lll", ps_parse, METH_VARARGS, + {"compile_to_lll", ps_compile_to_lll, METH_VARARGS, "Compile code to LLL."}, {"compile_lll", ps_compile_lll, METH_VARARGS, "Compile LLL to EVM."}, @@ -149,7 +155,7 @@ static PyMethodDef PyextMethods[] = { {NULL, NULL, 0, NULL} /* Sentinel */ }; -PyMODINIT_FUNC initpyext(void) +PyMODINIT_FUNC initserpent_pyext(void) { - PyObject *m = Py_InitModule( "pyext", PyextMethods ); + Py_InitModule( "serpent_pyext", PyextMethods ); } diff --git a/libqethereum/QEthereum.cpp b/libqethereum/QEthereum.cpp index 520547f64..714081dfe 100644 --- a/libqethereum/QEthereum.cpp +++ b/libqethereum/QEthereum.cpp @@ -1,219 +1,44 @@ -#if ETH_QTQML -#include -#endif #include #include -#include +#include #include #include #include #include -#include +#include #include "QEthereum.h" using namespace std; +using namespace dev; +using namespace dev::eth; -// types -using eth::bytes; -using eth::bytesConstRef; -using eth::h160; -using eth::h256; -using eth::u160; -using eth::u256; -using eth::u256s; -using eth::Address; -using eth::BlockInfo; -using eth::Client; -using eth::Instruction; -using eth::KeyPair; -using eth::NodeMode; -using eth::PeerInfo; -using eth::RLP; -using eth::Secret; -using eth::Transaction; - -// functions -using eth::toHex; -using eth::disassemble; -using eth::formatBalance; -using eth::fromHex; -using eth::right160; -using eth::simpleDebugOut; -using eth::toLog2; -using eth::toString; -using eth::units; - -// vars -using eth::g_logPost; -using eth::g_logVerbosity; -using eth::c_instructionInfo; - -// Horrible global for the mainwindow. Needed for the QmlEthereums to find the Main window which acts as multiplexer for now. -// Can get rid of this once we've sorted out ITC for signalling & multiplexed querying. -eth::Client* g_qmlClient; -QObject* g_qmlMain; - -QmlAccount::QmlAccount(QObject*) -{ -} - -QmlAccount::~QmlAccount() -{ -} - -void QmlAccount::setEthereum(QmlEthereum* _eth) -{ - if (m_eth == _eth) - return; - if (m_eth) - disconnect(m_eth, SIGNAL(changed()), this, SIGNAL(changed())); - m_eth = _eth; - if (m_eth) - connect(m_eth, SIGNAL(changed()), this, SIGNAL(changed())); - ethChanged(); - changed(); -} - -eth::u256 QmlAccount::balance() const -{ - if (m_eth) - return m_eth->balanceAt(m_address); - return u256(0); -} - -double QmlAccount::txCount() const -{ - if (m_eth) - return m_eth->txCountAt(m_address); - return 0; -} - -bool QmlAccount::isContract() const -{ - if (m_eth) - return m_eth->isContractAt(m_address); - return 0; -} - -QmlEthereum::QmlEthereum(QObject* _p): QObject(_p) -{ - connect(g_qmlMain, SIGNAL(changed()), SIGNAL(changed())); -} - -QmlEthereum::~QmlEthereum() -{ -} - -Client* QmlEthereum::client() const -{ - return g_qmlClient; -} - -Address QmlEthereum::coinbase() const -{ - return client()->address(); -} - -void QmlEthereum::setCoinbase(Address _a) -{ - if (client()->address() != _a) - { - client()->setAddress(_a); - changed(); - } -} - -u256 QmlEthereum::balanceAt(Address _a) const -{ - return client()->postState().balance(_a); -} - -bool QmlEthereum::isContractAt(Address _a) const -{ - return client()->postState().addressHasCode(_a); -} - -bool QmlEthereum::isMining() const -{ - return client()->isMining(); -} - -bool QmlEthereum::isListening() const -{ - return client()->haveNetwork(); -} - -void QmlEthereum::setMining(bool _l) -{ - if (_l) - client()->startMining(); - else - client()->stopMining(); -} - -void QmlEthereum::setListening(bool _l) -{ - if (_l) - client()->startNetwork(); - else - client()->stopNetwork(); -} - -double QmlEthereum::txCountAt(Address _a) const -{ - return (double)client()->postState().transactionsFrom(_a); -} - -unsigned QmlEthereum::peerCount() const -{ - return (unsigned)client()->peerCount(); -} - -void QmlEthereum::transact(Secret _secret, u256 _amount, u256 _gasPrice, u256 _gas, QByteArray _init) -{ - client()->transact(_secret, _amount, bytes(_init.data(), _init.data() + _init.size()), _gas, _gasPrice); -} - -void QmlEthereum::transact(Secret _secret, Address _dest, u256 _amount, u256 _gasPrice, u256 _gas, QByteArray _data) -{ - client()->transact(_secret, _amount, _dest, bytes(_data.data(), _data.data() + _data.size()), _gas, _gasPrice); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -eth::bytes toBytes(QString const& _s) +dev::bytes toBytes(QString const& _s) { if (_s.startsWith("0x")) // Hex - return eth::fromHex(_s.mid(2).toStdString()); + return dev::fromHex(_s.mid(2).toStdString()); else if (!_s.contains(QRegExp("[^0-9]"))) // Decimal - return eth::toCompactBigEndian(eth::bigint(_s.toStdString())); + return dev::toCompactBigEndian(dev::bigint(_s.toStdString())); else + { // Binary + cwarn << "THIS FUNCTIONALITY IS DEPRECATED. DO NOT ASSUME ASCII/BINARY-STRINGS WILL BE ACCEPTED. USE eth.fromAscii()."; return asBytes(_s); + } } QString padded(QString const& _s, unsigned _l, unsigned _r) { - eth::bytes b = toBytes(_s); + dev::bytes b = toBytes(_s); while (b.size() < _l) b.insert(b.begin(), 0); while (b.size() < _r) b.push_back(0); - return asQString(eth::asBytes(eth::asString(b).substr(b.size() - max(_l, _r)))); + return asQString(dev::asBytes(dev::asString(b).substr(b.size() - max(_l, _r)))); } //"0xff".bin().unbin() -QString QEthereum::secretToAddress(QString _s) const -{ - return toQJS(KeyPair(toSecret(_s)).address()); -} - QString padded(QString const& _s, unsigned _l) { if (_s.startsWith("0x") || !_s.contains(QRegExp("[^0-9]"))) @@ -231,48 +56,80 @@ QString unpadded(QString _s) return _s; } -QEthereum::QEthereum(QObject* _p, Client* _c, QList _accounts): QObject(_p), m_client(_c), m_accounts(_accounts) +QEthereum::QEthereum(QObject* _p, eth::Interface* _c, QList _accounts): + QObject(_p), m_client(_c), m_accounts(_accounts) { // required to prevent crash on osx when performing addto/evaluatejavascript calls - this->moveToThread(_p->thread()); + moveToThread(_p->thread()); } QEthereum::~QEthereum() { + clearWatches(); } -void QEthereum::setup(QWebFrame*) +void QEthereum::clientDieing() { - // Alex: JS codes moved to mainwin until qtwebkit bugs are resolved (#245) + clearWatches(); + m_client = nullptr; } -void QEthereum::teardown(QWebFrame*) +void QEthereum::clearWatches() { + if (m_client) + for (auto i: m_watches) + m_client->uninstallWatch(i); + m_watches.clear(); } -Client* QEthereum::client() const +QString QEthereum::secretToAddress(QString _s) const +{ + return toQJS(KeyPair(toSecret(_s)).address()); +} + +eth::Interface* QEthereum::client() const { return m_client; } QString QEthereum::lll(QString _s) const { - return asQString(eth::compileLLL(_s.toStdString())); + return toQJS(dev::eth::compileLLL(_s.toStdString())); } QString QEthereum::sha3(QString _s) const { - return toQJS(eth::sha3(asBytes(_s))); + return toQJS(dev::eth::sha3(toBytes(_s))); +} + +QString QEthereum::sha3(QString _s1, QString _s2) const +{ + return toQJS(dev::eth::sha3(asBytes(padded(_s1, 32)) + asBytes(padded(_s2, 32)))); +} + +QString QEthereum::sha3(QString _s1, QString _s2, QString _s3) const +{ + return toQJS(dev::eth::sha3(asBytes(padded(_s1, 32)) + asBytes(padded(_s2, 32)) + asBytes(padded(_s3, 32)))); +} + +QString QEthereum::sha3old(QString _s) const +{ + return toQJS(dev::eth::sha3(asBytes(_s))); +} + +QString QEthereum::offset(QString _s, int _i) const +{ + return toQJS(toU256(_s) + _i); } QString QEthereum::coinbase() const { - return toQJS(client()->address()); + return m_client ? toQJS(client()->address()) : ""; } QString QEthereum::number() const { - return QString::number(client()->blockChain().number() + 1); + return m_client ? QString::number(client()->number() + 1) : ""; } QString QEthereum::account() const @@ -307,74 +164,69 @@ QStringList QEthereum::keys() const void QEthereum::setCoinbase(QString _a) { - if (client()->address() != toAddress(_a)) + if (m_client && client()->address() != toAddress(_a)) { client()->setAddress(toAddress(_a)); - changed(); + coinbaseChanged(); } } -QString QEthereum::balanceAt(QString _a) const -{ - return toQJS(client()->postState().balance(toAddress(_a))); -} - -QString QEthereum::storageAt(QString _a, QString _p) const +void QEthereum::setDefault(int _block) { - eth::ClientGuard l(const_cast(m_client)); - return toQJS(client()->postState().storage(toAddress(_a), toU256(_p))); + if (m_client) + m_client->setDefault(_block); } -double QEthereum::txCountAt(QString _a) const +int QEthereum::getDefault() const { - return (double)client()->postState().transactionsFrom(toAddress(_a)); + return m_client ? m_client->getDefault() : 0; } -bool QEthereum::isContractAt(QString _a) const +QString QEthereum::balanceAt(QString _a) const { - return client()->postState().addressHasCode(toAddress(_a)); + return m_client ? toQJS(client()->balanceAt(toAddress(_a))) : ""; } -u256 QEthereum::balanceAt(Address _a) const +QString QEthereum::balanceAt(QString _a, int _block) const { - return client()->postState().balance(_a); + return m_client ? toQJS(client()->balanceAt(toAddress(_a), _block)) : ""; } -double QEthereum::txCountAt(Address _a) const +QString QEthereum::stateAt(QString _a, QString _p) const { - return (double)client()->postState().transactionsFrom(_a); + return m_client ? toQJS(client()->stateAt(toAddress(_a), toU256(_p))) : ""; } -bool QEthereum::isContractAt(Address _a) const +QString QEthereum::stateAt(QString _a, QString _p, int _block) const { - return client()->postState().addressHasCode(_a); + return m_client ? toQJS(client()->stateAt(toAddress(_a), toU256(_p), _block)) : ""; } -QString QEthereum::balanceAt(QString _a, int _block) const +QString QEthereum::codeAt(QString _a) const { - return toQJS(client()->balanceAt(toAddress(_a), _block)); + return m_client ? ::fromBinary(client()->codeAt(toAddress(_a))) : ""; } -QString QEthereum::stateAt(QString _a, QString _p, int _block) const +QString QEthereum::codeAt(QString _a, int _block) const { - return toQJS(client()->stateAt(toAddress(_a), toU256(_p), _block)); + return m_client ? ::fromBinary(client()->codeAt(toAddress(_a), _block)) : ""; } -QString QEthereum::codeAt(QString _a, int _block) const +double QEthereum::countAt(QString _a) const { - return ::fromBinary(client()->codeAt(toAddress(_a), _block)); + return m_client ? (double)(uint64_t)client()->countAt(toAddress(_a)) : 0; } double QEthereum::countAt(QString _a, int _block) const { - return (double)(uint64_t)client()->countAt(toAddress(_a), _block); + return m_client ? (double)(uint64_t)client()->countAt(toAddress(_a), _block) : 0; } -QString QEthereum::getTransactions(QString _a) const +static dev::eth::MessageFilter toMessageFilter(QString _json) { - eth::TransactionFilter filter; + dev::eth::MessageFilter filter; - QJsonObject f = QJsonDocument::fromJson(_a.toUtf8()).object(); + QJsonObject f = QJsonDocument::fromJson(_json.toUtf8()).object(); if (f.contains("earliest")) filter.withEarliest(f["earliest"].toInt()); if (f.contains("latest")) @@ -413,75 +265,215 @@ QString QEthereum::getTransactions(QString _a) const else filter.altered(toAddress(f["altered"].toString())); } + return filter; +} + +struct TransactionSkeleton +{ + Secret from; + Address to; + u256 value; + bytes data; + u256 gas; + u256 gasPrice; +}; + +static TransactionSkeleton toTransaction(QString _json) +{ + TransactionSkeleton ret; - QJsonArray ret; - for (eth::PastMessage const& t: m_client->transactions(filter)) + QJsonObject f = QJsonDocument::fromJson(_json.toUtf8()).object(); + if (f.contains("from")) + ret.from = toSecret(f["from"].toString()); + if (f.contains("to")) + ret.to = toAddress(f["to"].toString()); + if (f.contains("value")) + ret.value = toU256(f["value"].toString()); + if (f.contains("gas")) + ret.gas = toU256(f["gas"].toString()); + if (f.contains("gasPrice")) + ret.gasPrice = toU256(f["gasPrice"].toString()); + if (f.contains("data") || f.contains("code") || f.contains("dataclose")) + { + if (f["data"].isString()) + ret.data = toBytes(f["data"].toString()); + else if (f["code"].isString()) + ret.data = toBytes(f["code"].toString()); + else if (f["data"].isArray()) + for (auto i: f["data"].toArray()) + dev::operator +=(ret.data, asBytes(padded(i.toString(), 32))); + else if (f["code"].isArray()) + for (auto i: f["code"].toArray()) + dev::operator +=(ret.data, asBytes(padded(i.toString(), 32))); + else if (f["dataclose"].isArray()) + for (auto i: f["dataclose"].toArray()) + dev::operator +=(ret.data, toBytes(i.toString())); + } + return ret; +} + +static QString toJson(dev::eth::PastMessages const& _pms) +{ + QJsonArray jsonArray; + for (dev::eth::PastMessage const& t: _pms) { QJsonObject v; - v["data"] = ::fromBinary(t.input); v["input"] = ::fromBinary(t.input); v["output"] = ::fromBinary(t.output); v["to"] = toQJS(t.to); v["from"] = toQJS(t.from); v["origin"] = toQJS(t.origin); v["timestamp"] = (int)t.timestamp; + v["coinbase"] = toQJS(t.coinbase); v["block"] = toQJS(t.block); QJsonArray path; for (int i: t.path) path.append(i); v["path"] = path; - v["age"] = (int)t.age; - ret.append(v); + v["number"] = (int)t.number; + jsonArray.append(v); } - return QString::fromUtf8(QJsonDocument(ret).toJson()); + return QString::fromUtf8(QJsonDocument(jsonArray).toJson()); +} + +QString QEthereum::getMessages(QString _json) const +{ + return m_client ? toJson(m_client->messages(toMessageFilter(_json))) : ""; } bool QEthereum::isMining() const { - return client()->isMining(); + return m_client ? client()->isMining() : false; } bool QEthereum::isListening() const { - return client()->haveNetwork(); + return /*m_client ? client()->haveNetwork() :*/ false; } void QEthereum::setMining(bool _l) { - if (_l) - client()->startMining(); - else - client()->stopMining(); + if (m_client) + { + if (_l) + client()->startMining(); + else + client()->stopMining(); + } } -void QEthereum::setListening(bool _l) +void QEthereum::setListening(bool) { - if (_l) + if (!m_client) + return; +/* if (_l) client()->startNetwork(); else - client()->stopNetwork(); + client()->stopNetwork();*/ } unsigned QEthereum::peerCount() const { - return (unsigned)client()->peerCount(); + return /*m_client ? (unsigned)client()->peerCount() :*/ 0; } QString QEthereum::doCreate(QString _secret, QString _amount, QString _init, QString _gas, QString _gasPrice) { - client()->changed(); + if (!m_client) + return ""; auto ret = toQJS(client()->transact(toSecret(_secret), toU256(_amount), toBytes(_init), toU256(_gas), toU256(_gasPrice))); - while (!client()->peekChanged()) - this_thread::sleep_for(chrono::milliseconds(10)); + client()->flushTransactions(); return ret; } void QEthereum::doTransact(QString _secret, QString _amount, QString _dest, QString _data, QString _gas, QString _gasPrice) { - client()->changed(); + if (!m_client) + return; client()->transact(toSecret(_secret), toU256(_amount), toAddress(_dest), toBytes(_data), toU256(_gas), toU256(_gasPrice)); - while (!client()->peekChanged()) - this_thread::sleep_for(chrono::milliseconds(10)); + client()->flushTransactions(); +} + +QString QEthereum::doTransact(QString _json) +{ + QString ret; + if (!m_client) + return ret; + TransactionSkeleton t = toTransaction(_json); + if (!t.from && m_accounts.size()) + { + auto b = m_accounts.first(); + for (auto a: m_accounts) + if (client()->balanceAt(KeyPair(a).address()) > client()->balanceAt(KeyPair(b).address())) + b = a; + t.from = b.secret(); + } + if (!t.gasPrice) + t.gasPrice = 10 * dev::eth::szabo; + if (!t.gas) + t.gas = min(client()->gasLimitRemaining(), client()->balanceAt(KeyPair(t.from).address()) / t.gasPrice); + if (t.to) + client()->transact(t.from, t.value, t.to, t.data, t.gas, t.gasPrice); + else + ret = toQJS(client()->transact(t.from, t.value, t.data, t.gas, t.gasPrice)); + client()->flushTransactions(); + return ret; +} + +QString QEthereum::doCall(QString _json) +{ + if (!m_client) + return QString(); + TransactionSkeleton t = toTransaction(_json); + if (!t.to) + return QString(); + if (!t.from && m_accounts.size()) + t.from = m_accounts[0].secret(); + if (!t.gasPrice) + t.gasPrice = 10 * dev::eth::szabo; + if (!t.gas) + t.gas = client()->balanceAt(KeyPair(t.from).address()) / t.gasPrice; + bytes out = client()->call(t.from, t.value, t.to, t.data, t.gas, t.gasPrice); + return asQString(out); +} + +unsigned QEthereum::newWatch(QString _json) +{ + if (!m_client) + return (unsigned)-1; + unsigned ret; + if (_json == "chain") + ret = m_client->installWatch(dev::eth::ChainChangedFilter); + else if (_json == "pending") + ret = m_client->installWatch(dev::eth::PendingChangedFilter); + else + ret = m_client->installWatch(toMessageFilter(_json)); + m_watches.push_back(ret); + return ret; +} + +QString QEthereum::watchMessages(unsigned _w) +{ + if (!m_client) + return ""; + return toJson(m_client->messages(_w)); +} + +void QEthereum::killWatch(unsigned _w) +{ + if (!m_client) + return; + m_client->uninstallWatch(_w); + std::remove(m_watches.begin(), m_watches.end(), _w); +} + +void QEthereum::poll() +{ + if (!m_client) + return; + for (auto w: m_watches) + if (m_client->checkWatch(w)) + emit watchChanged(w); } // extra bits needed to link on VS diff --git a/libqethereum/QEthereum.h b/libqethereum/QEthereum.h index 3b0eb34de..50f2d96a5 100644 --- a/libqethereum/QEthereum.h +++ b/libqethereum/QEthereum.h @@ -1,302 +1,30 @@ #pragma once -#include -#if ETH_QTQML -#include -#endif -#include +#include +#include +#include +#include #include -namespace eth { -class Client; -class State; -} +namespace dev { namespace eth { +class Interface; +}} -class QQmlEngine; class QJSEngine; class QWebFrame; class QEthereum; -class QmlAccount; -class QmlEthereum; - -extern eth::Client* g_qmlClient; -extern QObject* g_qmlMain; - -Q_DECLARE_METATYPE(eth::u256) -Q_DECLARE_METATYPE(eth::Address) -Q_DECLARE_METATYPE(eth::Secret) -Q_DECLARE_METATYPE(eth::KeyPair) -Q_DECLARE_METATYPE(QEthereum*) -Q_DECLARE_METATYPE(QmlAccount*) -Q_DECLARE_METATYPE(QmlEthereum*) - -class QmlU256Helper: public QObject -{ - Q_OBJECT - -public: - QmlU256Helper(QObject* _p = nullptr): QObject(_p) {} - - Q_INVOKABLE eth::u256 add(eth::u256 _a, eth::u256 _b) const { return _a + _b; } - Q_INVOKABLE eth::u256 sub(eth::u256 _a, eth::u256 _b) const { return _a - _b; } - Q_INVOKABLE eth::u256 mul(eth::u256 _a, int _b) const { return _a * _b; } - Q_INVOKABLE eth::u256 mul(int _a, eth::u256 _b) const { return _a * _b; } - Q_INVOKABLE eth::u256 div(eth::u256 _a, int _b) const { return _a / _b; } - - Q_INVOKABLE eth::u256 wei(double _s) const { return (eth::u256)_s; } - Q_INVOKABLE eth::u256 szabo(double _s) const { return (eth::u256)(_s * (double)eth::szabo); } - Q_INVOKABLE eth::u256 finney(double _s) const { return (eth::u256)(_s * (double)eth::finney); } - Q_INVOKABLE eth::u256 ether(double _s) const { return (eth::u256)(_s * (double)eth::ether); } - Q_INVOKABLE eth::u256 wei(unsigned _s) const { return (eth::u256)_s; } - Q_INVOKABLE eth::u256 szabo(unsigned _s) const { return (eth::u256)(_s * eth::szabo); } - Q_INVOKABLE eth::u256 finney(unsigned _s) const { return (eth::u256)(_s * eth::finney); } - Q_INVOKABLE eth::u256 ether(unsigned _s) const { return (eth::u256)(_s * eth::ether); } - Q_INVOKABLE double toWei(eth::u256 _t) const { return (double)_t; } - Q_INVOKABLE double toSzabo(eth::u256 _t) const { return toWei(_t) / (double)eth::szabo; } - Q_INVOKABLE double toFinney(eth::u256 _t) const { return toWei(_t) / (double)eth::finney; } - Q_INVOKABLE double toEther(eth::u256 _t) const { return toWei(_t) / (double)eth::ether; } - - Q_INVOKABLE double value(eth::u256 _t) const { return (double)_t; } - - Q_INVOKABLE QString stringOf(eth::u256 _t) const { return QString::fromStdString(eth::formatBalance(_t)); } -}; - -class QmlKeyHelper: public QObject -{ - Q_OBJECT - -public: - QmlKeyHelper(QObject* _p = nullptr): QObject(_p) {} - - Q_INVOKABLE eth::KeyPair create() const { return eth::KeyPair::create(); } - Q_INVOKABLE eth::Address address(eth::KeyPair _p) const { return _p.address(); } - Q_INVOKABLE eth::Secret secret(eth::KeyPair _p) const { return _p.secret(); } - Q_INVOKABLE eth::KeyPair keypair(eth::Secret _k) const { return eth::KeyPair(_k); } - - Q_INVOKABLE bool isNull(eth::Address _a) const { return !_a; } - - Q_INVOKABLE eth::Address addressOf(QString _s) const { return eth::Address(_s.toStdString()); } - Q_INVOKABLE QString stringOf(eth::Address _a) const { return QString::fromStdString(eth::toHex(_a.asArray())); } - Q_INVOKABLE QString toAbridged(eth::Address _a) const { return QString::fromStdString(_a.abridged()); } -}; - -class QmlAccount: public QObject -{ - Q_OBJECT - -public: - QmlAccount(QObject* _p = nullptr); - virtual ~QmlAccount(); - - Q_INVOKABLE QmlEthereum* ethereum() const { return m_eth; } - Q_INVOKABLE eth::u256 balance() const; - Q_INVOKABLE double txCount() const; - Q_INVOKABLE bool isContract() const; - Q_INVOKABLE eth::Address address() const { return m_address; } - - // TODO: past transactions models. - -public slots: - void setEthereum(QmlEthereum* _eth); - void setAddress(eth::Address _a) { m_address = _a; } - -signals: - void changed(); - void ethChanged(); - -private: - QmlEthereum* m_eth = nullptr; - eth::Address m_address; - - Q_PROPERTY(eth::u256 balance READ balance NOTIFY changed STORED false) - Q_PROPERTY(double txCount READ txCount NOTIFY changed STORED false) - Q_PROPERTY(bool isContract READ isContract NOTIFY changed STORED false) - Q_PROPERTY(eth::Address address READ address WRITE setAddress NOTIFY changed) - Q_PROPERTY(QmlEthereum* ethereum READ ethereum WRITE setEthereum NOTIFY ethChanged) -}; - -class QmlEthereum: public QObject -{ - Q_OBJECT - -public: - QmlEthereum(QObject* _p = nullptr); - virtual ~QmlEthereum(); - - eth::Client* client() const; - - static QObject* constructU256Helper(QQmlEngine*, QJSEngine*) { return new QmlU256Helper; } - static QObject* constructKeyHelper(QQmlEngine*, QJSEngine*) { return new QmlKeyHelper; } - - Q_INVOKABLE eth::Address coinbase() const; - - Q_INVOKABLE bool isListening() const; - Q_INVOKABLE bool isMining() const; - - Q_INVOKABLE eth::u256 balanceAt(eth::Address _a) const; - Q_INVOKABLE double txCountAt(eth::Address _a) const; - Q_INVOKABLE bool isContractAt(eth::Address _a) const; - - Q_INVOKABLE unsigned peerCount() const; - - Q_INVOKABLE QmlEthereum* self() { return this; } - -public slots: - void transact(eth::Secret _secret, eth::Address _dest, eth::u256 _amount, eth::u256 _gasPrice, eth::u256 _gas, QByteArray _data); - void transact(eth::Secret _secret, eth::u256 _amount, eth::u256 _gasPrice, eth::u256 _gas, QByteArray _init); - void setCoinbase(eth::Address); - void setMining(bool _l); - - void setListening(bool _l); - -signals: - void changed(); -// void netChanged(); -// void miningChanged(); - -private: - Q_PROPERTY(eth::Address coinbase READ coinbase WRITE setCoinbase NOTIFY changed) - Q_PROPERTY(bool listening READ isListening WRITE setListening) - Q_PROPERTY(bool mining READ isMining WRITE setMining) -}; - -#if 0 -template T to(QVariant const& _s) { if (_s.type() != QVariant::String) return T(); auto s = _s.toString().toLatin1(); assert(s.size() == sizeof(T)); return *(T*)s.data(); } -template QVariant toQJS(T const& _s) { QLatin1String ret((char*)&_s, sizeof(T)); assert(QVariant(QString(ret)).toString().toLatin1().size() == sizeof(T)); assert(*(T*)(QVariant(QString(ret)).toString().toLatin1().data()) == _s); return QVariant(QString(ret)); } -class U256Helper: public QObject +inline dev::bytes asBytes(QString const& _s) { - Q_OBJECT - -public: - U256Helper(QObject* _p = nullptr): QObject(_p) {} - - static eth::u256 in(QVariant const& _s) { return to(_s); } - static QVariant out(eth::u256 const& _s) { return toQJS(_s); } - - Q_INVOKABLE QVariant add(QVariant _a, QVariant _b) const { return out(in(_a) + in(_b)); } - Q_INVOKABLE QVariant sub(QVariant _a, QVariant _b) const { return out(in(_a) - in(_b)); } - Q_INVOKABLE QVariant mul(QVariant _a, int _b) const { return out(in(_a) * in(_b)); } - Q_INVOKABLE QVariant mul(int _a, QVariant _b) const { return out(in(_a) * in(_b)); } - Q_INVOKABLE QVariant div(QVariant _a, int _b) const { return out(in(_a) / in(_b)); } - - Q_INVOKABLE QVariant wei(double _s) const { return out(eth::u256(_s)); } - Q_INVOKABLE QVariant szabo(double _s) const { return out(eth::u256(_s * (double)eth::szabo)); } - Q_INVOKABLE QVariant finney(double _s) const { return out(eth::u256(_s * (double)eth::finney)); } - Q_INVOKABLE QVariant ether(double _s) const { return out(eth::u256(_s * (double)eth::ether)); } - Q_INVOKABLE QVariant wei(unsigned _s) const { return value(_s); } - Q_INVOKABLE QVariant szabo(unsigned _s) const { return out(eth::u256(_s) * eth::szabo); } - Q_INVOKABLE QVariant finney(unsigned _s) const { return out(eth::u256(_s) * eth::finney); } - Q_INVOKABLE QVariant ether(unsigned _s) const { return out(eth::u256(_s) * eth::ether); } - Q_INVOKABLE double toWei(QVariant _t) const { return toValue(_t); } - Q_INVOKABLE double toSzabo(QVariant _t) const { return toWei(_t) / (double)eth::szabo; } - Q_INVOKABLE double toFinney(QVariant _t) const { return toWei(_t) / (double)eth::finney; } - Q_INVOKABLE double toEther(QVariant _t) const { return toWei(_t) / (double)eth::ether; } - - Q_INVOKABLE QVariant value(unsigned _s) const { return out(eth::u256(_s)); } - Q_INVOKABLE double toValue(QVariant _t) const { return (double)in(_t); } - - Q_INVOKABLE QString ethOf(QVariant _t) const { return QString::fromStdString(eth::formatBalance(in(_t))); } - Q_INVOKABLE QString stringOf(QVariant _t) const { return QString::fromStdString(eth::toString(in(_t))); } - - Q_INVOKABLE QByteArray bytesOf(QVariant _t) const { eth::h256 b = in(_t); return QByteArray((char const*)&b, sizeof(eth::h256)); } - Q_INVOKABLE QVariant fromHex(QString _s) const { return out((eth::u256)eth::h256(_s.toStdString())); } - - Q_INVOKABLE QVariant fromAddress(QVariant/*eth::Address*/ _a) const { return out((eth::u160)to(_a)); } - Q_INVOKABLE QVariant toAddress(QVariant/*eth::Address*/ _a) const { return toQJS((eth::u160)in(_a)); } - - Q_INVOKABLE bool isNull(QVariant/*eth::Address*/ _a) const { return !in(_a); } -}; - -class KeyHelper: public QObject -{ - Q_OBJECT - -public: - KeyHelper(QObject* _p = nullptr): QObject(_p) {} - - static eth::Address in(QVariant const& _s) { return to(_s); } - static QVariant out(eth::Address const& _s) { return toQJS(_s); } - - Q_INVOKABLE QVariant/*eth::KeyPair*/ create() const { return toQJS(eth::KeyPair::create()); } - Q_INVOKABLE QVariant/*eth::Address*/ address(QVariant/*eth::KeyPair*/ _p) const { return out(to(_p).address()); } - Q_INVOKABLE QVariant/*eth::Secret*/ secret(QVariant/*eth::KeyPair*/ _p) const { return toQJS(to(_p).secret()); } - Q_INVOKABLE QVariant/*eth::KeyPair*/ keypair(QVariant/*eth::Secret*/ _k) const { return toQJS(eth::KeyPair(to(_k))); } - - Q_INVOKABLE bool isNull(QVariant/*eth::Address*/ _a) const { return !in(_a); } - - Q_INVOKABLE QVariant/*eth::Address*/ addressOf(QString _s) const { return out(eth::Address(_s.toStdString())); } - Q_INVOKABLE QString stringOf(QVariant/*eth::Address*/ _a) const { return QString::fromStdString(eth::toHex(in(_a).asArray())); } - Q_INVOKABLE QString toAbridged(QVariant/*eth::Address*/ _a) const { return QString::fromStdString(in(_a).abridged()); } - -}; - -class BytesHelper: public QObject -{ - Q_OBJECT - -public: - BytesHelper(QObject* _p = nullptr): QObject(_p) {} - - Q_INVOKABLE QByteArray concat(QVariant _v, QVariant _w) const - { - QByteArray ba; - if (_v.type() == QVariant::ByteArray) - ba = _v.toByteArray(); - else - ba = _v.toString().toLatin1(); - QByteArray ba2; - if (_w.type() == QVariant::ByteArray) - ba2 = _w.toByteArray(); - else - ba2 = _w.toString().toLatin1(); - ba.append(ba2); - return QByteArray(ba); - } - Q_INVOKABLE QByteArray concat(QByteArray _v, QByteArray _w) const - { - _v.append(_w); - return _v; - } - Q_INVOKABLE QByteArray fromString(QString _s) const - { - return _s.toLatin1(); - } - Q_INVOKABLE QByteArray fromString(QString _s, unsigned _padding) const - { - QByteArray b = _s.toLatin1(); - for (unsigned i = b.size(); i < _padding; ++i) - b.append((char)0); - b.resize(_padding); - return b; - } - Q_INVOKABLE QString toString(QByteArray _b) const - { - while (_b.size() && !_b[_b.size() - 1]) - _b.resize(_b.size() - 1); - return QString::fromLatin1(_b); - } - Q_INVOKABLE QVariant u256of(QByteArray _s) const - { - while (_s.size() < 32) - _s.append((char)0); - eth::h256 ret((uint8_t const*)_s.data(), eth::h256::ConstructFromPointer); - return toQJS(ret); - } -}; -#endif - -inline eth::bytes asBytes(QString const& _s) -{ - eth::bytes ret; + dev::bytes ret; ret.reserve(_s.size()); for (QChar c: _s) ret.push_back(c.cell()); return ret; } -inline QString asQString(eth::bytes const& _s) +inline QString asQString(dev::bytes const& _s) { QString ret; ret.reserve(_s.size()); @@ -305,62 +33,64 @@ inline QString asQString(eth::bytes const& _s) return ret; } -eth::bytes toBytes(QString const& _s); +dev::bytes toBytes(QString const& _s); QString padded(QString const& _s, unsigned _l, unsigned _r); QString padded(QString const& _s, unsigned _l); QString unpadded(QString _s); -template eth::FixedHash toFixed(QString const& _s) +template dev::FixedHash toFixed(QString const& _s) { if (_s.startsWith("0x")) // Hex - return eth::FixedHash(_s.mid(2).toStdString()); + return dev::FixedHash(_s.mid(2).toStdString()); else if (!_s.contains(QRegExp("[^0-9]"))) // Decimal - return (typename eth::FixedHash::Arith)(_s.toStdString()); + return (typename dev::FixedHash::Arith)(_s.toStdString()); else // Binary - return eth::FixedHash(asBytes(padded(_s, N))); + return dev::FixedHash(asBytes(padded(_s, N))); } -template boost::multiprecision::number> toInt(QString const& _s) -{ - if (_s.startsWith("0x")) - return eth::fromBigEndian>>(eth::fromHex(_s.toStdString().substr(2))); - else if (!_s.contains(QRegExp("[^0-9]"))) - // Hex or Decimal - return boost::multiprecision::number>(_s.toStdString()); - else - // Binary - return eth::fromBigEndian>>(asBytes(padded(_s, N))); -} +template inline boost::multiprecision::number> toInt(QString const& _s); -inline eth::Address toAddress(QString const& _s) { return toFixed<20>(_s); } -inline eth::Secret toSecret(QString const& _s) { return toFixed<32>(_s); } -inline eth::u256 toU256(QString const& _s) { return toInt<32>(_s); } +inline dev::Address toAddress(QString const& _s) { return toFixed<20>(_s); } +inline dev::Secret toSecret(QString const& _s) { return toFixed<32>(_s); } +inline dev::u256 toU256(QString const& _s) { return toInt<32>(_s); } -template QString toQJS(eth::FixedHash const& _h) { return QString::fromStdString("0x" + toHex(_h.ref())); } -template QString toQJS(boost::multiprecision::number> const& _n) { return QString::fromStdString("0x" + eth::toHex(eth::toCompactBigEndian(_n))); } +template QString toQJS(dev::FixedHash const& _h) { return QString::fromStdString("0x" + toHex(_h.ref())); } +template QString toQJS(boost::multiprecision::number> const& _n) { return QString::fromStdString("0x" + dev::toHex(dev::toCompactBigEndian(_n))); } +inline QString toQJS(dev::bytes const& _n) { return "0x" + QString::fromStdString(dev::toHex(_n)); } inline QString toBinary(QString const& _s) { - return asQString(toBytes(_s)); + return unpadded(asQString(toBytes(_s))); } inline QString toDecimal(QString const& _s) { - return QString::fromStdString(eth::toString(toU256(_s))); + return QString::fromStdString(dev::toString(toU256(_s))); +} + +inline double fromFixed(QString const& _s) +{ + return (double)toU256(_s) / (double)(dev::u256(1) << 128); } -inline QString fromBinary(eth::bytes const& _s) +inline QString toFixed(double _s) { - return QString::fromStdString("0x" + eth::toHex(_s)); + return toQJS(dev::u256(_s * (double)(dev::u256(1) << 128))); } -inline QString fromBinary(QString const& _s) +inline QString fromBinary(dev::bytes _s, unsigned _padding = 32) { - return fromBinary(asBytes(_s)); + _s.resize(std::max(_s.size(), _padding)); + return QString::fromStdString("0x" + dev::toHex(_s)); +} + +inline QString fromBinary(QString const& _s, unsigned _padding = 32) +{ + return fromBinary(asBytes(_s), _padding); } class QEthereum: public QObject @@ -368,15 +98,16 @@ class QEthereum: public QObject Q_OBJECT public: - QEthereum(QObject* _p, eth::Client* _c, QList _accounts); + QEthereum(QObject* _p, dev::eth::Interface* _c, QList _accounts); virtual ~QEthereum(); - eth::Client* client() const; + dev::eth::Interface* client() const; + void setClient(dev::eth::Interface* _c) { m_client = _c; } - void setup(QWebFrame* _e); - void teardown(QWebFrame* _e); + /// Call when the client() is going to be deleted to make this object useless but safe. + void clientDieing(); - void setAccounts(QList _l) { m_accounts = _l; this->changed(); } + void setAccounts(QList _l) { m_accounts = _l; keysChanged(); } Q_INVOKABLE QString ethTest() const { return "Hello world!"; } Q_INVOKABLE QEthereum* self() { return this; } @@ -385,87 +116,127 @@ public: Q_INVOKABLE QString lll(QString _s) const; Q_INVOKABLE QString sha3(QString _s) const; + Q_INVOKABLE QString sha3(QString _s1, QString _s2) const; + Q_INVOKABLE QString sha3(QString _s1, QString _s2, QString _s3) const; + Q_INVOKABLE QString sha3old(QString _s) const; + Q_INVOKABLE QString offset(QString _s, int _offset) const; + Q_INVOKABLE QString pad(QString _s, unsigned _l) const { return padded(_s, _l); } Q_INVOKABLE QString pad(QString _s, unsigned _l, unsigned _r) const { return padded(_s, _l, _r); } Q_INVOKABLE QString unpad(QString _s) const { return unpadded(_s); } - Q_INVOKABLE QString toBinary(QString _s) const { return ::toBinary(_s); } - Q_INVOKABLE QString fromBinary(QString _s) const { return ::fromBinary(_s); } - Q_INVOKABLE QString toDecimal(QString _s) const { return ::toDecimal(_s); } - // [OLD API] - Don't use this. - Q_INVOKABLE QString/*eth::u256*/ balanceAt(QString/*eth::Address*/ _a) const; - Q_INVOKABLE QString/*eth::u256*/ storageAt(QString/*eth::Address*/ _a, QString/*eth::u256*/ _p) const; - Q_INVOKABLE double txCountAt(QString/*eth::Address*/ _a) const; - Q_INVOKABLE bool isContractAt(QString/*eth::Address*/ _a) const; + Q_INVOKABLE QString toAscii(QString _s) const { return ::toBinary(_s); } + Q_INVOKABLE QString fromAscii(QString _s) const { return ::fromBinary(_s, 32); } + Q_INVOKABLE QString fromAscii(QString _s, unsigned _padding) const { return ::fromBinary(_s, _padding); } + Q_INVOKABLE QString toDecimal(QString _s) const { return ::toDecimal(_s); } + Q_INVOKABLE double fromFixed(QString _s) const { return ::fromFixed(_s); } + Q_INVOKABLE QString toFixed(double _d) const { return ::toFixed(_d); } // [NEW API] - Use this instead. - Q_INVOKABLE QString/*eth::u256*/ balanceAt(QString/*eth::Address*/ _a, int _block) const; - Q_INVOKABLE double countAt(QString/*eth::Address*/ _a, int _block) const; - Q_INVOKABLE QString/*eth::u256*/ stateAt(QString/*eth::Address*/ _a, QString/*eth::u256*/ _p, int _block) const; - Q_INVOKABLE QString/*eth::u256*/ codeAt(QString/*eth::Address*/ _a, int _block) const; - Q_INVOKABLE QString getTransactions(QString _attribs) const; + Q_INVOKABLE QString/*dev::u256*/ balanceAt(QString/*dev::Address*/ _a, int _block) const; + Q_INVOKABLE double countAt(QString/*dev::Address*/ _a, int _block) const; + Q_INVOKABLE QString/*dev::u256*/ stateAt(QString/*dev::Address*/ _a, QString/*dev::u256*/ _p, int _block) const; + Q_INVOKABLE QString/*dev::u256*/ codeAt(QString/*dev::Address*/ _a, int _block) const; + + Q_INVOKABLE QString/*dev::u256*/ balanceAt(QString/*dev::Address*/ _a) const; + Q_INVOKABLE double countAt(QString/*dev::Address*/ _a) const; + Q_INVOKABLE QString/*dev::u256*/ stateAt(QString/*dev::Address*/ _a, QString/*dev::u256*/ _p) const; + Q_INVOKABLE QString/*dev::u256*/ codeAt(QString/*dev::Address*/ _a) const; + + Q_INVOKABLE QString/*json*/ getMessages(QString _attribs/*json*/) const; Q_INVOKABLE QString doCreate(QString _secret, QString _amount, QString _init, QString _gas, QString _gasPrice); Q_INVOKABLE void doTransact(QString _secret, QString _amount, QString _dest, QString _data, QString _gas, QString _gasPrice); + Q_INVOKABLE QString doTransact(QString _json); + Q_INVOKABLE QString doCall(QString _json); + + Q_INVOKABLE unsigned newWatch(QString _json); + Q_INVOKABLE QString watchMessages(unsigned _w); + Q_INVOKABLE void killWatch(unsigned _w); + void clearWatches(); bool isListening() const; bool isMining() const; - QString/*eth::Address*/ coinbase() const; - QString/*eth::u256*/ gasPrice() const { return toQJS(10 * eth::szabo); } + QString/*dev::Address*/ coinbase() const; + QString/*dev::u256*/ gasPrice() const { return toQJS(10 * dev::eth::szabo); } + QString/*dev::u256*/ number() const; + int getDefault() const; - QString number() const; - eth::u256 balanceAt(eth::Address _a) const; - double txCountAt(eth::Address _a) const; - bool isContractAt(eth::Address _a) const; - - QString/*eth::KeyPair*/ key() const; - QStringList/*list of eth::KeyPair*/ keys() const; - QString/*eth::Address*/ account() const; - QStringList/*list of eth::Address*/ accounts() const; + QString/*dev::KeyPair*/ key() const; + QStringList/*list of dev::KeyPair*/ keys() const; + QString/*dev::Address*/ account() const; + QStringList/*list of dev::Address*/ accounts() const; unsigned peerCount() const; public slots: - void setCoinbase(QString/*eth::Address*/); + void setCoinbase(QString/*dev::Address*/); void setMining(bool _l); void setListening(bool _l); + void setDefault(int _block); + + /// Check to see if anything has changed, fire off signals if so. + /// @note Must be called in the QObject's thread. + void poll(); signals: - void changed(); -// void netChanged(); -// void miningChanged(); + void watchChanged(unsigned _w); + void coinbaseChanged(); + void keysChanged(); + void netChanged(); + void miningChanged(); private: - Q_PROPERTY(QString number READ number NOTIFY changed) - Q_PROPERTY(QString coinbase READ coinbase WRITE setCoinbase NOTIFY changed) - Q_PROPERTY(bool listening READ isListening WRITE setListening) - Q_PROPERTY(bool mining READ isMining WRITE setMining) - Q_PROPERTY(QString gasPrice READ gasPrice NOTIFY changed) - Q_PROPERTY(QString key READ key NOTIFY changed) - Q_PROPERTY(QStringList keys READ keys NOTIFY changed) - Q_PROPERTY(unsigned peerCount READ peerCount) - - eth::Client* m_client; - QList m_accounts; + Q_PROPERTY(QString number READ number NOTIFY watchChanged) + Q_PROPERTY(QString coinbase READ coinbase WRITE setCoinbase NOTIFY coinbaseChanged) + Q_PROPERTY(QString gasPrice READ gasPrice) + Q_PROPERTY(QString key READ key NOTIFY keysChanged) + Q_PROPERTY(QStringList keys READ keys NOTIFY keysChanged) + Q_PROPERTY(bool mining READ isMining WRITE setMining NOTIFY netChanged) + Q_PROPERTY(bool listening READ isListening WRITE setListening NOTIFY netChanged) + Q_PROPERTY(unsigned peerCount READ peerCount NOTIFY miningChanged) + Q_PROPERTY(int defaultBlock READ getDefault WRITE setDefault) + + dev::eth::Interface* m_client; + std::vector m_watches; + QList m_accounts; }; -#define QETH_INSTALL_JS_NAMESPACE [f, eth, this]() \ +#define QETH_INSTALL_JS_NAMESPACE(frame, eth, env) [frame, eth, env]() \ { \ - f->disconnect(); \ - f->addToJavaScriptWindowObject("env", this, QWebFrame::QtOwnership); \ - f->addToJavaScriptWindowObject("eth", eth, QWebFrame::ScriptOwnership); \ - f->evaluateJavaScript("eth.watch = function(a, s, f) { eth.changed.connect(f ? f : s) }"); \ - f->evaluateJavaScript("eth.newBlock = function(f) { eth.changed.connect(f) }"); \ - \ - f->evaluateJavaScript("eth.create = function(s, v, c, g, p, f) { var v = eth.doCreate(s, v, c, g, p); if (f) f(v) }"); \ - f->evaluateJavaScript("eth.transact = function(s, v, t, d, g, p, f) { eth.doTransact(s, v, t, d, g, p); if (f) f() }"); \ - f->evaluateJavaScript("eth.transactions = function(a) { return JSON.parse(eth.getTransactions(JSON.stringify(a))); }"); \ - f->evaluateJavaScript("String.prototype.pad = function(l, r) { return eth.pad(this, l, r) }"); \ - f->evaluateJavaScript("String.prototype.bin = function() { return eth.toBinary(this) }"); \ - f->evaluateJavaScript("String.prototype.unbin = function(l) { return eth.fromBinary(this) }"); \ - f->evaluateJavaScript("String.prototype.unpad = function(l) { return eth.unpad(this) }"); \ - f->evaluateJavaScript("String.prototype.dec = function() { return eth.toDecimal(this) }"); \ - f->evaluateJavaScript("String.prototype.sha3 = function() { return eth.sha3(this) }"); \ + frame->disconnect(); \ + frame->addToJavaScriptWindowObject("env", env, QWebFrame::QtOwnership); \ + frame->addToJavaScriptWindowObject("eth", eth, QWebFrame::ScriptOwnership); \ + frame->evaluateJavaScript("eth.makeWatch = function(a) { var ww = eth.newWatch(a); var ret = { w: ww }; ret.uninstall = function() { eth.killWatch(w); }; ret.changed = function(f) { eth.watchChanged.connect(function(nw) { if (nw == ww) f() }); }; ret.messages = function() { return JSON.parse(eth.watchMessages(this.w)) }; return ret; }"); \ + frame->evaluateJavaScript("eth.watch = function(a) { return eth.makeWatch(JSON.stringify(a)) }"); \ + frame->evaluateJavaScript("eth.watchChain = function() { env.warn('THIS CALL IS DEPRECATED. USE eth.watch('chain') INSTEAD.'); return eth.makeWatch('chain') }"); \ + frame->evaluateJavaScript("eth.watchPending = function() { env.warn('THIS CALL IS DEPRECATED. USE eth.watch('pending') INSTEAD.'); return eth.makeWatch('pending') }"); \ + frame->evaluateJavaScript("eth.create = function(s, v, c, g, p, f) { env.warn('THIS CALL IS DEPRECATED. USE eth.transact INSTEAD.'); var v = eth.doCreate(s, v, c, g, p); if (f) f(v) }"); \ + frame->evaluateJavaScript("eth.transact = function(a_s, f_v, t, d, g, p, f) { if (t == null) { var r = eth.doTransact(JSON.stringify(a_s)); if (f_v) f_v(r); } else { env.warn('THIS FORM OF THIS CALL IS DEPRECATED.'); eth.doTransact(a_s, f_v, t, d, g, p); if (f) f() } }"); \ + frame->evaluateJavaScript("eth.call = function(a, f) { var ret = eth.doCallJson(JSON.stringify(a)); if (f) f(ret); return ret; }"); \ + frame->evaluateJavaScript("eth.messages = function(a) { return JSON.parse(eth.getMessages(JSON.stringify(a))); }"); \ + frame->evaluateJavaScript("eth.transactions = function(a) { env.warn('THIS CALL IS DEPRECATED. USE eth.messages INSTEAD.'); return JSON.parse(eth.getMessages(JSON.stringify(a))); }"); \ + frame->evaluateJavaScript("String.prototype.pad = function(l, r) { env.warn('THIS CALL IS DEPRECATED. USE eth.* INSTEAD.'); return eth.pad(this, l, r) }"); \ + frame->evaluateJavaScript("String.prototype.bin = function() { env.warn('THIS CALL IS DEPRECATED. USE eth.* INSTEAD.'); return eth.toAscii(this) }"); \ + frame->evaluateJavaScript("String.prototype.unbin = function(l) { env.warn('THIS CALL IS DEPRECATED. USE eth.* INSTEAD.'); return eth.fromAscii(this) }"); \ + frame->evaluateJavaScript("String.prototype.unpad = function(l) { env.warn('THIS CALL IS DEPRECATED. USE eth.* INSTEAD.'); return eth.unpad(this) }"); \ + frame->evaluateJavaScript("String.prototype.dec = function() { env.warn('THIS CALL IS DEPRECATED. USE eth.* INSTEAD.'); return eth.toDecimal(this) }"); \ + frame->evaluateJavaScript("String.prototype.fix = function() { env.warn('THIS CALL IS DEPRECATED. USE eth.* INSTEAD.'); return eth.toFixed(this) }"); \ + frame->evaluateJavaScript("String.prototype.sha3 = function() { env.warn('THIS CALL IS DEPRECATED. USE eth.* INSTEAD.'); return eth.sha3old(this) }"); \ + frame->evaluateJavaScript("console.log = env.note"); \ + frame->evaluateJavaScript("window.onerror = function (errorMsg, url, lineNumber, column, errorObj) { env.warn('Error: ' + errorMsg + ', Script: ' + url + ', Line: ' + lineNumber + ', Column: ' + column + ', StackTrace: ' + String(errorObj)) }"); \ +} + +template inline boost::multiprecision::number> toInt(QString const& _s) +{ + if (_s.startsWith("0x")) + return dev::fromBigEndian>>(dev::fromHex(_s.toStdString().substr(2))); + else if (!_s.contains(QRegExp("[^0-9]"))) + // Hex or Decimal + return boost::multiprecision::number>(_s.toStdString()); + else + // Binary + return dev::fromBigEndian>>(asBytes(padded(_s, N))); } diff --git a/libqethereum/QmlEthereum.cpp b/libqethereum/QmlEthereum.cpp new file mode 100644 index 000000000..89578a29a --- /dev/null +++ b/libqethereum/QmlEthereum.cpp @@ -0,0 +1,189 @@ +#if ETH_QTQML +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include "QmlEthereum.h" +using namespace std; + +// types +using dev::bytes; +using dev::bytesConstRef; +using dev::h160; +using dev::h256; +using dev::u160; +using dev::u256; +using dev::u256s; +using dev::RLP; +using dev::Address; +using dev::eth::BlockInfo; +using dev::eth::Client; +using dev::eth::Instruction; +using dev::KeyPair; +using dev::eth::NodeMode; +using dev::Secret; +using dev::eth::Transaction; +using dev::p2p::PeerInfo; + +// functions +using dev::toHex; +using dev::fromHex; +using dev::right160; +using dev::simpleDebugOut; +using dev::toLog2; +using dev::toString; +using dev::eth::disassemble; +using dev::eth::formatBalance; +using dev::eth::units; + +// vars +using dev::g_logPost; +using dev::g_logVerbosity; + +// Horrible global for the mainwindow. Needed for the QmlEthereums to find the Main window which acts as multiplexer for now. +// Can get rid of this once we've sorted out ITC for signalling & multiplexed querying. +dev::eth::Client* g_qmlClient; +QObject* g_qmlMain; +#if 0 +QmlAccount::QmlAccount(QObject*) +{ +} + +QmlAccount::~QmlAccount() +{ +} + +void QmlAccount::setEthereum(QmlEthereum* _eth) +{ + if (m_eth == _eth) + return; +// if (m_eth) +// disconnect(m_eth, SIGNAL(changed()), this, SIGNAL(changed())); + m_eth = _eth; +// if (m_eth) +// connect(m_eth, SIGNAL(changed()), this, SIGNAL(changed())); + ethChanged(); +// changed(); +} + +dev::u256 QmlAccount::balance() const +{ + if (m_eth) + return m_eth->balanceAt(m_address); + return u256(0); +} + +double QmlAccount::txCount() const +{ + if (m_eth) + return m_eth->txCountAt(m_address); + return 0; +} + +bool QmlAccount::isContract() const +{ + if (m_eth) + return m_eth->isContractAt(m_address); + return 0; +} + +QmlEthereum::QmlEthereum(QObject* _p): QObject(_p) +{ +// connect(g_qmlMain, SIGNAL(changed()), SIGNAL(changed())); +} + +QmlEthereum::~QmlEthereum() +{ +} + +Client* QmlEthereum::client() const +{ + return g_qmlClient; +} + +Address QmlEthereum::coinbase() const +{ + return client()->address(); +} + +void QmlEthereum::setCoinbase(Address _a) +{ + if (client()->address() != _a) + { + client()->setAddress(_a); +// changed(); + } +} + +u256 QmlEthereum::balanceAt(Address _a) const +{ + return client()->postState().balance(_a); +} + +bool QmlEthereum::isContractAt(Address _a) const +{ + return client()->postState().addressHasCode(_a); +} + +bool QmlEthereum::isMining() const +{ + return client()->isMining(); +} + +bool QmlEthereum::isListening() const +{ + return client()->haveNetwork(); +} + +void QmlEthereum::setMining(bool _l) +{ + if (_l) + client()->startMining(); + else + client()->stopMining(); +} + +void QmlEthereum::setListening(bool _l) +{ + if (_l) + client()->startNetwork(); + else + client()->stopNetwork(); +} + +double QmlEthereum::txCountAt(Address _a) const +{ + return (double)client()->postState().transactionsFrom(_a); +} + +unsigned QmlEthereum::peerCount() const +{ + return (unsigned)client()->peerCount(); +} + +void QmlEthereum::transact(Secret _secret, u256 _amount, u256 _gasPrice, u256 _gas, QByteArray _init) +{ + client()->transact(_secret, _amount, bytes(_init.data(), _init.data() + _init.size()), _gas, _gasPrice); +} + +void QmlEthereum::transact(Secret _secret, Address _dest, u256 _amount, u256 _gasPrice, u256 _gas, QByteArray _data) +{ + client()->transact(_secret, _amount, _dest, bytes(_data.data(), _data.data() + _data.size()), _gas, _gasPrice); +} + +#endif + +// extra bits needed to link on VS +#ifdef _MSC_VER + +// include moc file, ofuscated to hide from automoc +#include\ +"moc_QmlEthereum.cpp" + +#endif diff --git a/libqethereum/QmlEthereum.h b/libqethereum/QmlEthereum.h new file mode 100644 index 000000000..d242540a1 --- /dev/null +++ b/libqethereum/QmlEthereum.h @@ -0,0 +1,283 @@ +#pragma once + +#include +#if ETH_QTQML +#include +#endif +#include +#include + +namespace dev { namespace eth { +class Client; +class State; +}} + +class QQmlEngine; +class QmlAccount; +class QmlEthereum; + +extern dev::eth::Client* g_qmlClient; +extern QObject* g_qmlMain; + +Q_DECLARE_METATYPE(dev::u256) +Q_DECLARE_METATYPE(dev::Address) +Q_DECLARE_METATYPE(dev::Secret) +Q_DECLARE_METATYPE(dev::KeyPair) +//Q_DECLARE_METATYPE(QmlAccount*) +//Q_DECLARE_METATYPE(QmlEthereum*) + +class QmlU256Helper: public QObject +{ + Q_OBJECT + +public: + QmlU256Helper(QObject* _p = nullptr): QObject(_p) {} + + Q_INVOKABLE dev::u256 add(dev::u256 _a, dev::u256 _b) const { return _a + _b; } + Q_INVOKABLE dev::u256 sub(dev::u256 _a, dev::u256 _b) const { return _a - _b; } + Q_INVOKABLE dev::u256 mul(dev::u256 _a, int _b) const { return _a * _b; } + Q_INVOKABLE dev::u256 mul(int _a, dev::u256 _b) const { return _a * _b; } + Q_INVOKABLE dev::u256 div(dev::u256 _a, int _b) const { return _a / _b; } + + Q_INVOKABLE dev::u256 wei(double _s) const { return (dev::u256)_s; } + Q_INVOKABLE dev::u256 szabo(double _s) const { return (dev::u256)(_s * (double)dev::eth::szabo); } + Q_INVOKABLE dev::u256 finney(double _s) const { return (dev::u256)(_s * (double)dev::eth::finney); } + Q_INVOKABLE dev::u256 ether(double _s) const { return (dev::u256)(_s * (double)dev::eth::ether); } + Q_INVOKABLE dev::u256 wei(unsigned _s) const { return (dev::u256)_s; } + Q_INVOKABLE dev::u256 szabo(unsigned _s) const { return (dev::u256)(_s * dev::eth::szabo); } + Q_INVOKABLE dev::u256 finney(unsigned _s) const { return (dev::u256)(_s * dev::eth::finney); } + Q_INVOKABLE dev::u256 ether(unsigned _s) const { return (dev::u256)(_s * dev::eth::ether); } + Q_INVOKABLE double toWei(dev::u256 _t) const { return (double)_t; } + Q_INVOKABLE double toSzabo(dev::u256 _t) const { return toWei(_t) / (double)dev::eth::szabo; } + Q_INVOKABLE double toFinney(dev::u256 _t) const { return toWei(_t) / (double)dev::eth::finney; } + Q_INVOKABLE double toEther(dev::u256 _t) const { return toWei(_t) / (double)dev::eth::ether; } + + Q_INVOKABLE double value(dev::u256 _t) const { return (double)_t; } + + Q_INVOKABLE QString stringOf(dev::u256 _t) const { return QString::fromStdString(dev::eth::formatBalance(_t)); } +}; + +class QmlKeyHelper: public QObject +{ + Q_OBJECT + +public: + QmlKeyHelper(QObject* _p = nullptr): QObject(_p) {} + + Q_INVOKABLE dev::KeyPair create() const { return dev::KeyPair::create(); } + Q_INVOKABLE dev::Address address(dev::KeyPair _p) const { return _p.address(); } + Q_INVOKABLE dev::Secret secret(dev::KeyPair _p) const { return _p.secret(); } + Q_INVOKABLE dev::KeyPair keypair(dev::Secret _k) const { return dev::KeyPair(_k); } + + Q_INVOKABLE bool isNull(dev::Address _a) const { return !_a; } + + Q_INVOKABLE dev::Address addressOf(QString _s) const { return dev::Address(_s.toStdString()); } + Q_INVOKABLE QString stringOf(dev::Address _a) const { return QString::fromStdString(dev::toHex(_a.asArray())); } + Q_INVOKABLE QString toAbridged(dev::Address _a) const { return QString::fromStdString(_a.abridged()); } +}; +#if 0 +class QmlAccount: public QObject +{ + Q_OBJECT + +public: + QmlAccount(QObject* _p = nullptr); + virtual ~QmlAccount(); + + Q_INVOKABLE QmlEthereum* ethereum() const { return m_eth; } + Q_INVOKABLE dev::u256 balance() const; + Q_INVOKABLE double txCount() const; + Q_INVOKABLE bool isContract() const; + Q_INVOKABLE dev::Address address() const { return m_address; } + + // TODO: past transactions models. + +public slots: + void setEthereum(QmlEthereum* _eth); + void setAddress(dev::Address _a) { m_address = _a; } + +signals: + void changed(); + void ethChanged(); + +private: + QmlEthereum* m_eth = nullptr; + dev::Address m_address; + + Q_PROPERTY(dev::u256 balance READ balance NOTIFY changed STORED false) + Q_PROPERTY(double txCount READ txCount NOTIFY changed STORED false) + Q_PROPERTY(bool isContract READ isContract NOTIFY changed STORED false) + Q_PROPERTY(dev::Address address READ address WRITE setAddress NOTIFY changed) + Q_PROPERTY(QmlEthereum* ethereum READ ethereum WRITE setEthereum NOTIFY ethChanged) +}; + +class QmlEthereum: public QObject +{ + Q_OBJECT + +public: + QmlEthereum(QObject* _p = nullptr); + virtual ~QmlEthereum(); + + dev::eth::Client* client() const; + + static QObject* constructU256Helper(QQmlEngine*, QJSEngine*) { return new QmlU256Helper; } + static QObject* constructKeyHelper(QQmlEngine*, QJSEngine*) { return new QmlKeyHelper; } + + Q_INVOKABLE dev::Address coinbase() const; + + Q_INVOKABLE bool isListening() const; + Q_INVOKABLE bool isMining() const; + + Q_INVOKABLE dev::u256 balanceAt(dev::Address _a) const; + Q_INVOKABLE double txCountAt(dev::Address _a) const; + Q_INVOKABLE bool isContractAt(dev::Address _a) const; + + Q_INVOKABLE unsigned peerCount() const; + + Q_INVOKABLE QmlEthereum* self() { return this; } + +public slots: + void transact(dev::Secret _secret, dev::Address _dest, dev::u256 _amount, dev::u256 _gasPrice, dev::u256 _gas, QByteArray _data); + void transact(dev::Secret _secret, dev::u256 _amount, dev::u256 _gasPrice, dev::u256 _gas, QByteArray _init); + void setCoinbase(dev::Address); + void setMining(bool _l); + + void setListening(bool _l); + +signals: + void coinbaseChanged(); +// void netChanged(); +// void miningChanged(); + +private: + Q_PROPERTY(dev::Address coinbase READ coinbase WRITE setCoinbase NOTIFY coinbaseChanged) + Q_PROPERTY(bool listening READ isListening WRITE setListening) + Q_PROPERTY(bool mining READ isMining WRITE setMining) +}; +#endif +#if 0 +template T to(QVariant const& _s) { if (_s.type() != QVariant::String) return T(); auto s = _s.toString().toLatin1(); assert(s.size() == sizeof(T)); return *(T*)s.data(); } +template QVariant toQJS(T const& _s) { QLatin1String ret((char*)&_s, sizeof(T)); assert(QVariant(QString(ret)).toString().toLatin1().size() == sizeof(T)); assert(*(T*)(QVariant(QString(ret)).toString().toLatin1().data()) == _s); return QVariant(QString(ret)); } + +class U256Helper: public QObject +{ + Q_OBJECT + +public: + U256Helper(QObject* _p = nullptr): QObject(_p) {} + + static dev::u256 in(QVariant const& _s) { return to(_s); } + static QVariant out(dev::u256 const& _s) { return toQJS(_s); } + + Q_INVOKABLE QVariant add(QVariant _a, QVariant _b) const { return out(in(_a) + in(_b)); } + Q_INVOKABLE QVariant sub(QVariant _a, QVariant _b) const { return out(in(_a) - in(_b)); } + Q_INVOKABLE QVariant mul(QVariant _a, int _b) const { return out(in(_a) * in(_b)); } + Q_INVOKABLE QVariant mul(int _a, QVariant _b) const { return out(in(_a) * in(_b)); } + Q_INVOKABLE QVariant div(QVariant _a, int _b) const { return out(in(_a) / in(_b)); } + + Q_INVOKABLE QVariant wei(double _s) const { return out(dev::u256(_s)); } + Q_INVOKABLE QVariant szabo(double _s) const { return out(dev::u256(_s * (double)dev::eth::szabo)); } + Q_INVOKABLE QVariant finney(double _s) const { return out(dev::u256(_s * (double)dev::eth::finney)); } + Q_INVOKABLE QVariant ether(double _s) const { return out(dev::u256(_s * (double)dev::eth::ether)); } + Q_INVOKABLE QVariant wei(unsigned _s) const { return value(_s); } + Q_INVOKABLE QVariant szabo(unsigned _s) const { return out(dev::u256(_s) * dev::eth::szabo); } + Q_INVOKABLE QVariant finney(unsigned _s) const { return out(dev::u256(_s) * dev::eth::finney); } + Q_INVOKABLE QVariant ether(unsigned _s) const { return out(dev::u256(_s) * dev::eth::ether); } + Q_INVOKABLE double toWei(QVariant _t) const { return toValue(_t); } + Q_INVOKABLE double toSzabo(QVariant _t) const { return toWei(_t) / (double)dev::eth::szabo; } + Q_INVOKABLE double toFinney(QVariant _t) const { return toWei(_t) / (double)dev::eth::finney; } + Q_INVOKABLE double toEther(QVariant _t) const { return toWei(_t) / (double)dev::eth::ether; } + + Q_INVOKABLE QVariant value(unsigned _s) const { return out(dev::u256(_s)); } + Q_INVOKABLE double toValue(QVariant _t) const { return (double)in(_t); } + + Q_INVOKABLE QString ethOf(QVariant _t) const { return QString::fromStdString(dev::eth::formatBalance(in(_t))); } + Q_INVOKABLE QString stringOf(QVariant _t) const { return QString::fromStdString(dev::eth::toString(in(_t))); } + + Q_INVOKABLE QByteArray bytesOf(QVariant _t) const { dev::h256 b = in(_t); return QByteArray((char const*)&b, sizeof(dev::h256)); } + Q_INVOKABLE QVariant fromHex(QString _s) const { return out((dev::u256)dev::h256(_s.toStdString())); } + + Q_INVOKABLE QVariant fromAddress(QVariant/*dev::Address*/ _a) const { return out((dev::eth::u160)to(_a)); } + Q_INVOKABLE QVariant toAddress(QVariant/*dev::Address*/ _a) const { return toQJS((dev::eth::u160)in(_a)); } + + Q_INVOKABLE bool isNull(QVariant/*dev::Address*/ _a) const { return !in(_a); } +}; + +class KeyHelper: public QObject +{ + Q_OBJECT + +public: + KeyHelper(QObject* _p = nullptr): QObject(_p) {} + + static dev::Address in(QVariant const& _s) { return to(_s); } + static QVariant out(dev::Address const& _s) { return toQJS(_s); } + + Q_INVOKABLE QVariant/*dev::KeyPair*/ create() const { return toQJS(dev::KeyPair::create()); } + Q_INVOKABLE QVariant/*dev::Address*/ address(QVariant/*dev::KeyPair*/ _p) const { return out(to(_p).address()); } + Q_INVOKABLE QVariant/*dev::Secret*/ secret(QVariant/*dev::KeyPair*/ _p) const { return toQJS(to(_p).secret()); } + Q_INVOKABLE QVariant/*dev::KeyPair*/ keypair(QVariant/*dev::Secret*/ _k) const { return toQJS(dev::KeyPair(to(_k))); } + + Q_INVOKABLE bool isNull(QVariant/*dev::Address*/ _a) const { return !in(_a); } + + Q_INVOKABLE QVariant/*dev::Address*/ addressOf(QString _s) const { return out(dev::Address(_s.toStdString())); } + Q_INVOKABLE QString stringOf(QVariant/*dev::Address*/ _a) const { return QString::fromStdString(dev::eth::toHex(in(_a).asArray())); } + Q_INVOKABLE QString toAbridged(QVariant/*dev::Address*/ _a) const { return QString::fromStdString(in(_a).abridged()); } + +}; + +class BytesHelper: public QObject +{ + Q_OBJECT + +public: + BytesHelper(QObject* _p = nullptr): QObject(_p) {} + + Q_INVOKABLE QByteArray concat(QVariant _v, QVariant _w) const + { + QByteArray ba; + if (_v.type() == QVariant::ByteArray) + ba = _v.toByteArray(); + else + ba = _v.toString().toLatin1(); + QByteArray ba2; + if (_w.type() == QVariant::ByteArray) + ba2 = _w.toByteArray(); + else + ba2 = _w.toString().toLatin1(); + ba.append(ba2); + return QByteArray(ba); + } + Q_INVOKABLE QByteArray concat(QByteArray _v, QByteArray _w) const + { + _v.append(_w); + return _v; + } + Q_INVOKABLE QByteArray fromString(QString _s) const + { + return _s.toLatin1(); + } + Q_INVOKABLE QByteArray fromString(QString _s, unsigned _padding) const + { + QByteArray b = _s.toLatin1(); + for (unsigned i = b.size(); i < _padding; ++i) + b.append((char)0); + b.resize(_padding); + return b; + } + Q_INVOKABLE QString toString(QByteArray _b) const + { + while (_b.size() && !_b[_b.size() - 1]) + _b.resize(_b.size() - 1); + return QString::fromLatin1(_b); + } + Q_INVOKABLE QVariant u256of(QByteArray _s) const + { + while (_s.size() < 32) + _s.append((char)0); + dev::h256 ret((uint8_t const*)_s.data(), dev::h256::ConstructFromPointer); + return toQJS(ret); + } +}; +#endif diff --git a/libserpent/CMakeLists.txt b/libserpent/CMakeLists.txt index 0d392bd49..0d66393b2 100644 --- a/libserpent/CMakeLists.txt +++ b/libserpent/CMakeLists.txt @@ -19,7 +19,7 @@ include_directories(..) target_link_libraries(${EXECUTABLE} lll) target_link_libraries(${EXECUTABLE} evmface) -target_link_libraries(${EXECUTABLE} ethential) +target_link_libraries(${EXECUTABLE} devcore) if("${TARGET_PLATFORM}" STREQUAL "w64") target_link_libraries(${EXECUTABLE} boost_thread_win32-mt-s) diff --git a/libserpent/bignum.cpp b/libserpent/bignum.cpp index 29315b871..877808ead 100644 --- a/libserpent/bignum.cpp +++ b/libserpent/bignum.cpp @@ -5,9 +5,9 @@ #include "bignum.h" //Integer to string conversion -std::string intToDecimal(int branch) { +std::string unsignedToDecimal(unsigned branch) { if (branch < 10) return nums.substr(branch, 1); - else return intToDecimal(branch / 10) + nums.substr(branch % 10,1); + else return unsignedToDecimal(branch / 10) + nums.substr(branch % 10,1); } //Add two strings representing decimal values @@ -91,8 +91,8 @@ std::string decimalMod(std::string a, std::string b) { } //String to int conversion -int decimalToInt(std::string a) { +unsigned decimalToUnsigned(std::string a) { if (a.size() == 0) return 0; else return (a[a.size() - 1] - '0') - + decimalToInt(a.substr(0,a.size()-1)) * 10; + + decimalToUnsigned(a.substr(0,a.size()-1)) * 10; } diff --git a/libserpent/bignum.h b/libserpent/bignum.h index a12929752..6656fdaec 100644 --- a/libserpent/bignum.h +++ b/libserpent/bignum.h @@ -11,7 +11,7 @@ const std::string tt255 = "57896044618658097711785492504343953926634992332820282019728792003956564819968" ; -std::string intToDecimal(int branch); +std::string unsignedToDecimal(unsigned branch); std::string decimalAdd(std::string a, std::string b); @@ -25,6 +25,6 @@ std::string decimalMod(std::string a, std::string b); bool decimalGt(std::string a, std::string b, bool eqAllowed=false); -int decimalToInt(std::string a); +unsigned decimalToUnsigned(std::string a); #endif diff --git a/libserpent/compiler.cpp b/libserpent/compiler.cpp index 959d2993b..4360bfba6 100644 --- a/libserpent/compiler.cpp +++ b/libserpent/compiler.cpp @@ -17,6 +17,7 @@ struct programAux { struct programData { programAux aux; Node code; + int outs; }; programAux Aux() { @@ -27,10 +28,11 @@ programAux Aux() { return o; } -programData pd(programAux aux = Aux(), Node code=token("_")) { +programData pd(programAux aux = Aux(), Node code=token("_"), int outs=0) { programData o; o.aux = aux; o.code = code; + o.outs = outs; return o; } @@ -44,151 +46,249 @@ Node multiToken(Node nodes[], int len, Metadata met) { Node finalize(programData c); +Node popwrap(Node node) { + Node nodelist[] = { + node, + token("POP", node.metadata) + }; + return multiToken(nodelist, 2, node.metadata); +} + // Turns LLL tree into tree of code fragments -programData opcodeify(Node node, programAux aux=Aux()) { +programData opcodeify(Node node, + programAux aux=Aux(), + int height=0, + std::map dupvars= + std::map()) { std::string symb = "_"+mkUniqueToken(); Metadata m = node.metadata; // Numbers if (node.type == TOKEN) { - return pd(aux, nodeToNumeric(node)); + return pd(aux, nodeToNumeric(node), 1); } else if (node.val == "ref" || node.val == "get" || node.val == "set") { std::string varname = node.args[0].val; if (!aux.vars.count(varname)) { - aux.vars[varname] = intToDecimal(aux.vars.size() * 32); + aux.vars[varname] = unsignedToDecimal(aux.vars.size() * 32); } - if (varname == "msg.data") aux.calldataUsed = true; + if (varname == "'msg.data") aux.calldataUsed = true; // Set variable if (node.val == "set") { - programData sub = opcodeify(node.args[1], aux); - Node nodelist[] = { - sub.code, - token(aux.vars[varname], m), - token("MSTORE", m), - }; - return pd(sub.aux, multiToken(nodelist, 3, m)); + programData sub = opcodeify(node.args[1], aux, height, dupvars); + if (!sub.outs) + err("Value to set variable must have nonzero arity!", m); + if (dupvars.count(node.args[0].val)) { + int h = height - dupvars[node.args[0].val]; + if (h > 16) err("Too deep for stack variable (max 16)", m); + Node nodelist[] = { + sub.code, + token("SWAP"+unsignedToDecimal(h), m), + token("POP", m) + }; + return pd(sub.aux, multiToken(nodelist, 3, m), 0); + } + Node nodelist[] = { + sub.code, + token(sub.aux.vars[varname], m), + token("MSTORE", m), + }; + return pd(sub.aux, multiToken(nodelist, 3, m), 0); } // Get variable else if (node.val == "get") { - Node nodelist[] = - { token(aux.vars[varname], m), token("MLOAD", m) }; - return pd(aux, multiToken(nodelist, 2, m)); + if (dupvars.count(node.args[0].val)) { + int h = height - dupvars[node.args[0].val]; + if (h > 16) err("Too deep for stack variable (max 16)", m); + return pd(aux, token("DUP"+unsignedToDecimal(h)), 1); + } + Node nodelist[] = + { token(aux.vars[varname], m), token("MLOAD", m) }; + return pd(aux, multiToken(nodelist, 2, m), 1); } // Refer variable - else return pd(aux, token(aux.vars[varname], m)); + else { + if (dupvars.count(node.args[0].val)) + err("Cannot ref stack variable!", m); + return pd(aux, token(aux.vars[varname], m), 1); + } } // Code blocks if (node.val == "lll" && node.args.size() == 2) { if (node.args[1].val != "0") aux.allocUsed = true; std::vector o; o.push_back(finalize(opcodeify(node.args[0]))); - programData sub = opcodeify(node.args[1], aux); + programData sub = opcodeify(node.args[1], aux, height, dupvars); Node code = astnode("____CODE", o, m); Node nodelist[] = { - token("$begincode"+symb+".endcode"+symb, m), token("DUP", m), + token("$begincode"+symb+".endcode"+symb, m), token("DUP1", m), token("$begincode"+symb, m), sub.code, token("CODECOPY", m), token("$endcode"+symb, m), token("JUMP", m), token("~begincode"+symb, m), code, token("~endcode"+symb, m) }; - return pd(sub.aux, multiToken(nodelist, 10, m)); + return pd(sub.aux, multiToken(nodelist, 10, m), 1); } - std::vector subs; - for (unsigned i = 0; i < node.args.size(); i++) { - programData sub = opcodeify(node.args[i], aux); - aux = sub.aux; - subs.push_back(sub.code); - } - // Debug - if (node.val == "debug") { + // Stack variables + if (node.val == "with") { + std::map dupvars2 = dupvars; + dupvars2[node.args[0].val] = height; + programData initial = opcodeify(node.args[1], aux, height, dupvars); + if (!initial.outs) + err("Initial variable value must have nonzero arity!", m); + programData sub = opcodeify(node.args[2], initial.aux, height + 1, dupvars2); Node nodelist[] = { - subs[0], - token("DUP", m), token("POP", m), token("POP", m) + initial.code, + sub.code }; - return pd(aux, multiToken(nodelist, 4, m)); + programData o = pd(sub.aux, multiToken(nodelist, 2, m), sub.outs); + if (sub.outs) + o.code.args.push_back(token("SWAP1", m)); + o.code.args.push_back(token("POP", m)); + return o; } // Seq of multiple statements if (node.val == "seq") { - return pd(aux, astnode("_", subs, m)); + std::vector children; + int lastOut = 0; + for (unsigned i = 0; i < node.args.size(); i++) { + programData sub = opcodeify(node.args[i], aux, height, dupvars); + aux = sub.aux; + if (sub.outs == 1) { + if (i < node.args.size() - 1) sub.code = popwrap(sub.code); + else lastOut = 1; + } + children.push_back(sub.code); + } + return pd(aux, astnode("_", children, m), lastOut); } // 2-part conditional (if gets rewritten to unless in rewrites) else if (node.val == "unless" && node.args.size() == 2) { + programData cond = opcodeify(node.args[0], aux, height, dupvars); + programData action = opcodeify(node.args[1], cond.aux, height, dupvars); + aux = action.aux; + if (!cond.outs) err("Condition of if/unless statement has arity 0", m); + if (action.outs) action.code = popwrap(action.code); Node nodelist[] = { - subs[0], + cond.code, token("$endif"+symb, m), token("JUMPI", m), - subs[1], + action.code, token("~endif"+symb, m) }; - return pd(aux, multiToken(nodelist, 5, m)); + return pd(aux, multiToken(nodelist, 5, m), 0); } // 3-part conditional else if (node.val == "if" && node.args.size() == 3) { + programData ifd = opcodeify(node.args[0], aux, height, dupvars); + programData thend = opcodeify(node.args[1], ifd.aux, height, dupvars); + programData elsed = opcodeify(node.args[2], thend.aux, height, dupvars); + aux = elsed.aux; + if (!ifd.outs) + err("Condition of if/unless statement has arity 0", m); + // Handle cases where one conditional outputs something + // and the other does not + int outs = (thend.outs && elsed.outs) ? 1 : 0; + if (thend.outs > outs) thend.code = popwrap(thend.code); + if (elsed.outs > outs) elsed.code = popwrap(elsed.code); Node nodelist[] = { - subs[0], + ifd.code, token("NOT", m), token("$else"+symb, m), token("JUMPI", m), - subs[1], + thend.code, token("$endif"+symb, m), token("JUMP", m), token("~else"+symb, m), - subs[2], + elsed.code, token("~endif"+symb, m) }; - return pd(aux, multiToken(nodelist, 10, m)); + return pd(aux, multiToken(nodelist, 10, m), outs); } // While (rewritten to this in rewrites) else if (node.val == "until") { + programData cond = opcodeify(node.args[0], aux, height, dupvars); + programData action = opcodeify(node.args[1], cond.aux, height, dupvars); + aux = action.aux; + if (!cond.outs) + err("Condition of while/until loop has arity 0", m); + if (action.outs) action.code = popwrap(action.code); Node nodelist[] = { token("~beg"+symb, m), - subs[0], + cond.code, token("$end"+symb, m), token("JUMPI", m), - subs[1], + action.code, token("$beg"+symb, m), token("JUMP", m), token("~end"+symb, m) }; return pd(aux, multiToken(nodelist, 8, m)); } // Memory allocations else if (node.val == "alloc") { + programData bytez = opcodeify(node.args[0], aux, height, dupvars); + aux = bytez.aux; + if (!bytez.outs) + err("Alloc input has arity 0", m); aux.allocUsed = true; Node nodelist[] = { - subs[0], - token("MSIZE", m), token("SWAP", m), token("MSIZE", m), + bytez.code, + token("MSIZE", m), token("SWAP1", m), token("MSIZE", m), token("ADD", m), - token("0", m), token("SWAP", m), token("MSTORE", m) + token("0", m), token("SWAP1", m), token("MSTORE", m) }; - return pd(aux, multiToken(nodelist, 8, m)); + return pd(aux, multiToken(nodelist, 8, m), 1); } // Array literals else if (node.val == "array_lit") { aux.allocUsed = true; std::vector nodes; - if (!subs.size()) { + if (!node.args.size()) { nodes.push_back(token("MSIZE", m)); return pd(aux, astnode("_", nodes, m)); } nodes.push_back(token("MSIZE", m)); nodes.push_back(token("0", m)); nodes.push_back(token("MSIZE", m)); - nodes.push_back(token(intToDecimal(subs.size() * 32 - 1), m)); + nodes.push_back(token(unsignedToDecimal(node.args.size() * 32 - 1), m)); nodes.push_back(token("ADD", m)); nodes.push_back(token("MSTORE8", m)); - for (unsigned i = 0; i < subs.size(); i++) { - nodes.push_back(token("DUP", m)); - nodes.push_back(subs[i]); - nodes.push_back(token("SWAP", m)); + for (unsigned i = 0; i < node.args.size(); i++) { + Metadata m2 = node.args[i].metadata; + nodes.push_back(token("DUP1", m2)); + programData sub = opcodeify(node.args[i], aux, height + 2, dupvars); + if (!sub.outs) + err("Array_lit item " + unsignedToDecimal(i) + " has zero arity", m2); + aux = sub.aux; + nodes.push_back(sub.code); + nodes.push_back(token("SWAP1", m2)); if (i > 0) { - nodes.push_back(token(intToDecimal(i * 32), m)); - nodes.push_back(token("ADD", m)); + nodes.push_back(token(unsignedToDecimal(i * 32), m2)); + nodes.push_back(token("ADD", m2)); } - nodes.push_back(token("MSTORE", m)); + nodes.push_back(token("MSTORE", m2)); } - return pd(aux, astnode("_", nodes, m)); + return pd(aux, astnode("_", nodes, m), 1); } // All other functions/operators else { - std::vector subs2; - while (subs.size()) { - subs2.push_back(subs.back()); - subs.pop_back(); + std::vector subs2; + int depth = opinputs(upperCase(node.val)); + if (node.val != "debug") { + if (depth == -1) + err("Not a function or opcode: "+node.val, m); + if ((int)node.args.size() != depth) + err("Invalid arity for "+node.val, m); + } + for (int i = node.args.size() - 1; i >= 0; i--) { + programData sub = opcodeify(node.args[i], + aux, + height - i - 1 + node.args.size(), + dupvars); + aux = sub.aux; + if (!sub.outs) + err("Input "+unsignedToDecimal(i)+" has arity 0", sub.code.metadata); + subs2.push_back(sub.code); + } + if (node.val == "debug") { + subs2.push_back(token("DUP"+unsignedToDecimal(node.args.size()), m)); + for (int i = 0; i <= (int)node.args.size(); i++) + subs2.push_back(token("POP", m)); } - subs2.push_back(token(upperCase(node.val), m)); - return pd(aux, astnode("_", subs2, m)); + else subs2.push_back(token(upperCase(node.val), m)); + int outdepth = node.val == "debug" ? 0 : opoutputs(upperCase(node.val)); + return pd(aux, astnode("_", subs2, m), outdepth); } } @@ -201,7 +301,7 @@ Node finalize(programData c) { if (c.aux.allocUsed && c.aux.vars.size() > 0) { Node nodelist[] = { token("0", m), - token(intToDecimal(c.aux.vars.size() * 32 - 1)), + token(unsignedToDecimal(c.aux.vars.size() * 32 - 1)), token("MSTORE8", m) }; bottom.push_back(multiToken(nodelist, 3, m)); @@ -211,7 +311,7 @@ Node finalize(programData c) { Node nodelist[] = { token("MSIZE", m), token("CALLDATASIZE", m), token("MSIZE", m), token("0", m), token("CALLDATACOPY", m), - token(c.aux.vars["msg.data"], m), token("MSTORE", m) + token(c.aux.vars["'msg.data"], m), token("MSTORE", m) }; bottom.push_back(multiToken(nodelist, 7, m)); } @@ -235,7 +335,7 @@ programAux buildDict(Node program, programAux aux, int labelLength) { aux.step += 1 + toByteArr(program.val, m).size(); } else if (program.val[0] == '~') { - aux.vars[program.val.substr(1)] = intToDecimal(aux.step); + aux.vars[program.val.substr(1)] = unsignedToDecimal(aux.step); } else if (program.val[0] == '$') { aux.step += labelLength + 1; @@ -245,7 +345,7 @@ programAux buildDict(Node program, programAux aux, int labelLength) { // A sub-program (ie. LLL) else if (program.val == "____CODE") { programAux auks = Aux(); - for (unsigned i = 0; i < program.args.size(); i++) { + for (unsigned i = 0; i < program.args.size(); i++) { auks = buildDict(program.args[i], auks, labelLength); } for (std::map::iterator it=auks.vars.begin(); @@ -257,7 +357,7 @@ programAux buildDict(Node program, programAux aux, int labelLength) { } // Normal sub-block else { - for (unsigned i = 0; i < program.args.size(); i++) { + for (unsigned i = 0; i < program.args.size(); i++) { aux = buildDict(program.args[i], aux, labelLength); } } @@ -271,7 +371,7 @@ Node substDict(Node program, programAux aux, int labelLength) { std::vector inner; if (program.type == TOKEN) { if (program.val[0] == '$') { - std::string tokStr = "PUSH"+intToDecimal(labelLength); + std::string tokStr = "PUSH"+unsignedToDecimal(labelLength); out.push_back(token(tokStr, m)); int dotLoc = program.val.find('.'); if (dotLoc == -1) { @@ -289,13 +389,13 @@ Node substDict(Node program, programAux aux, int labelLength) { else if (program.val[0] == '~') { } else if (isNumberLike(program)) { inner = toByteArr(program.val, m); - out.push_back(token("PUSH"+intToDecimal(inner.size()))); + out.push_back(token("PUSH"+unsignedToDecimal(inner.size()))); out.push_back(astnode("_", inner, m)); } else return program; } else { - for (unsigned i = 0; i < program.args.size(); i++) { + for (unsigned i = 0; i < program.args.size(); i++) { Node n = substDict(program.args[i], aux, labelLength); if (n.type == TOKEN || n.args.size()) out.push_back(n); } @@ -319,9 +419,9 @@ std::vector flatten(Node derefed) { o.push_back(derefed); } else { - for (unsigned i = 0; i < derefed.args.size(); i++) { + for (unsigned i = 0; i < derefed.args.size(); i++) { std::vector oprime = flatten(derefed.args[i]); - for (unsigned j = 0; j < oprime.size(); j++) o.push_back(oprime[j]); + for (unsigned j = 0; j < oprime.size(); j++) o.push_back(oprime[j]); } } return o; @@ -330,13 +430,13 @@ std::vector flatten(Node derefed) { // Opcodes -> bin std::string serialize(std::vector codons) { std::string o; - for (unsigned i = 0; i < codons.size(); i++) { + for (unsigned i = 0; i < codons.size(); i++) { int v; if (isNumberLike(codons[i])) { - v = decimalToInt(codons[i].val); + v = decimalToUnsigned(codons[i].val); } else if (codons[i].val.substr(0,4) == "PUSH") { - v = 95 + decimalToInt(codons[i].val.substr(4)); + v = 95 + decimalToUnsigned(codons[i].val.substr(4)); } else { v = opcode(codons[i].val); @@ -350,14 +450,14 @@ std::string serialize(std::vector codons) { std::vector deserialize(std::string ser) { std::vector o; int backCount = 0; - for (unsigned i = 0; i < ser.length(); i++) { + for (unsigned i = 0; i < ser.length(); i++) { unsigned char v = (unsigned char)ser[i]; std::string oper = op((int)v); if (oper != "" && backCount <= 0) o.push_back(token(oper)); else if (v >= 96 && v < 128 && backCount <= 0) { - o.push_back(token("PUSH"+intToDecimal(v - 95))); + o.push_back(token("PUSH"+unsignedToDecimal(v - 95))); } - else o.push_back(token(intToDecimal(v))); + else o.push_back(token(unsignedToDecimal(v))); if (v >= 96 && v < 128 && backCount <= 0) { backCount = v - 95; } @@ -389,10 +489,10 @@ std::vector prettyCompileLLL(Node program) { // Converts a list of integer values to binary transaction data std::string encodeDatalist(std::vector vals) { std::string o; - for (unsigned i = 0; i < vals.size(); i++) { + for (unsigned i = 0; i < vals.size(); i++) { std::vector n = toByteArr(strToNumeric(vals[i]), Metadata(), 32); - for (unsigned j = 0; j < n.size(); j++) { - int v = decimalToInt(n[j].val); + for (unsigned j = 0; j < n.size(); j++) { + int v = decimalToUnsigned(n[j].val); o += (char)v; } } @@ -402,11 +502,11 @@ std::string encodeDatalist(std::vector vals) { // Converts binary transaction data into a list of integer values std::vector decodeDatalist(std::string ser) { std::vector out; - for (unsigned i = 0; i < ser.length(); i+= 32) { + for (unsigned i = 0; i < ser.length(); i+= 32) { std::string o = "0"; for (unsigned j = i; j < i + 32; j++) { int vj = (int)(unsigned char)ser[j]; - o = decimalAdd(decimalMul(o, "256"), intToDecimal(vj)); + o = decimalAdd(decimalMul(o, "256"), unsignedToDecimal(vj)); } out.push_back(o); } diff --git a/libserpent/opcodes.h b/libserpent/opcodes.h index 6b42df97a..f55834efa 100644 --- a/libserpent/opcodes.h +++ b/libserpent/opcodes.h @@ -6,86 +6,124 @@ #include #include -std::map opcodes; +class Mapping { + public: + Mapping(std::string Op, int Opcode, int In, int Out) { + op = Op; + opcode = Opcode; + in = In; + out = Out; + } + std::string op; + int opcode; + int in; + int out; +}; + +Mapping mapping[] = { + Mapping("STOP", 0x00, 0, 0), + Mapping("ADD", 0x01, 2, 1), + Mapping("MUL", 0x02, 2, 1), + Mapping("SUB", 0x03, 2, 1), + Mapping("DIV", 0x04, 2, 1), + Mapping("SDIV", 0x05, 2, 1), + Mapping("MOD", 0x06, 2, 1), + Mapping("SMOD", 0x07, 2, 1), + Mapping("EXP", 0x08, 2, 1), + Mapping("NEG", 0x09, 1, 1), + Mapping("LT", 0x0a, 2, 1), + Mapping("GT", 0x0b, 2, 1), + Mapping("SLT", 0x0c, 2, 1), + Mapping("SGT", 0x0d, 2, 1), + Mapping("EQ", 0x0e, 2, 1), + Mapping("NOT", 0x0f, 1, 1), + Mapping("AND", 0x10, 2, 1), + Mapping("OR", 0x11, 2, 1), + Mapping("XOR", 0x12, 2, 1), + Mapping("BYTE", 0x13, 2, 1), + Mapping("ADDMOD", 0x14, 3, 1), + Mapping("MULMOD", 0x15, 3, 1), + Mapping("SHA3", 0x20, 2, 1), + Mapping("ADDRESS", 0x30, 0, 1), + Mapping("BALANCE", 0x31, 1, 1), + Mapping("ORIGIN", 0x32, 0, 1), + Mapping("CALLER", 0x33, 0, 1), + Mapping("CALLVALUE", 0x34, 0, 1), + Mapping("CALLDATALOAD", 0x35, 1, 1), + Mapping("CALLDATASIZE", 0x36, 0, 1), + Mapping("CALLDATACOPY", 0x37, 3, 1), + Mapping("CODESIZE", 0x38, 0, 1), + Mapping("CODECOPY", 0x39, 3, 1), + Mapping("GASPRICE", 0x3a, 0, 1), + Mapping("PREVHASH", 0x40, 0, 1), + Mapping("COINBASE", 0x41, 0, 1), + Mapping("TIMESTAMP", 0x42, 0, 1), + Mapping("NUMBER", 0x43, 0, 1), + Mapping("DIFFICULTY", 0x44, 0, 1), + Mapping("GASLIMIT", 0x45, 0, 1), + Mapping("POP", 0x50, 1, 0), + Mapping("MLOAD", 0x53, 1, 1), + Mapping("MSTORE", 0x54, 2, 0), + Mapping("MSTORE8", 0x55, 2, 0), + Mapping("SLOAD", 0x56, 1, 1), + Mapping("SSTORE", 0x57, 2, 0), + Mapping("JUMP", 0x58, 1, 0), + Mapping("JUMPI", 0x59, 2, 0), + Mapping("PC", 0x5a, 0, 1), + Mapping("MSIZE", 0x5b, 0, 1), + Mapping("GAS", 0x5c, 0, 1), + Mapping("CREATE", 0xf0, 3, 1), + Mapping("CALL", 0xf1, 7, 1), + Mapping("RETURN", 0xf2, 2, 0), + Mapping("POST", 0xf3, 5, 0), + Mapping("CALL_STATELESS", 0xf4, 7, 1), + Mapping("SUICIDE", 0xff, 1, 0), + Mapping("---END---", 0x00, 0, 0), +}; + +std::map > opcodes; std::map reverseOpcodes; // Fetches everything EXCEPT PUSH1..32 -std::pair _opcode(std::string ops, int opi) { +std::pair > _opdata(std::string ops, int opi) { if (!opcodes.size()) { - opcodes["STOP"] = 0x00; - opcodes["ADD"] = 0x01; - opcodes["MUL"] = 0x02; - opcodes["SUB"] = 0x03; - opcodes["DIV"] = 0x04; - opcodes["SDIV"] = 0x05; - opcodes["MOD"] = 0x06; - opcodes["SMOD"] = 0x07; - opcodes["EXP"] = 0x08; - opcodes["NEG"] = 0x09; - opcodes["LT"] = 0x0a; - opcodes["GT"] = 0x0b; - opcodes["SLT"] = 0x0c; - opcodes["SGT"] = 0x0d; - opcodes["EQ"] = 0x0e; - opcodes["NOT"] = 0x0f; - opcodes["AND"] = 0x10; - opcodes["OR"] = 0x11; - opcodes["XOR"] = 0x12; - opcodes["BYTE"] = 0x13; - opcodes["SHA3"] = 0x20; - opcodes["ADDRESS"] = 0x30; - opcodes["BALANCE"] = 0x31; - opcodes["ORIGIN"] = 0x32; - opcodes["CALLER"] = 0x33; - opcodes["CALLVALUE"] = 0x34; - opcodes["CALLDATALOAD"] = 0x35; - opcodes["CALLDATASIZE"] = 0x36; - opcodes["CALLDATACOPY"] = 0x37; - opcodes["CODESIZE"] = 0x38; - opcodes["CODECOPY"] = 0x39; - opcodes["GASPRICE"] = 0x3a; - opcodes["PREVHASH"] = 0x40; - opcodes["COINBASE"] = 0x41; - opcodes["TIMESTAMP"] = 0x42; - opcodes["NUMBER"] = 0x43; - opcodes["DIFFICULTY"] = 0x44; - opcodes["GASLIMIT"] = 0x45; - opcodes["POP"] = 0x50; - opcodes["DUP"] = 0x51; - opcodes["SWAP"] = 0x52; - opcodes["MLOAD"] = 0x53; - opcodes["MSTORE"] = 0x54; - opcodes["MSTORE8"] = 0x55; - opcodes["SLOAD"] = 0x56; - opcodes["SSTORE"] = 0x57; - opcodes["JUMP"] = 0x58; - opcodes["JUMPI"] = 0x59; - opcodes["PC"] = 0x5a; - opcodes["MSIZE"] = 0x5b; - opcodes["GAS"] = 0x5c; - opcodes["CREATE"] = 0xf0; - opcodes["CALL"] = 0xf1; - opcodes["RETURN"] = 0xf2; - opcodes["SUICIDE"] = 0xff; - for (std::map::iterator it=opcodes.begin(); + int i = 0; + while (mapping[i].op != "---END---") { + Mapping mi = mapping[i]; + opcodes[mi.op] = triple(mi.opcode, mi.in, mi.out); + i++; + } + for (i = 1; i <= 16; i++) { + opcodes["DUP"+unsignedToDecimal(i)] = triple(0x7f + i, i, i+1); + opcodes["SWAP"+unsignedToDecimal(i)] = triple(0x8f + i, i+1, i+1); + } + for (std::map >::iterator it=opcodes.begin(); it != opcodes.end(); it++) { - reverseOpcodes[(*it).second] = (*it).first; + reverseOpcodes[(*it).second[0]] = (*it).first; } } std::string op; - int opcode; + std::vector opdata; op = reverseOpcodes.count(opi) ? reverseOpcodes[opi] : ""; - opcode = opcodes.count(ops) ? opcodes[ops] : -1; - return std::pair(op, opcode); + opdata = opcodes.count(ops) ? opcodes[ops] : triple(-1, -1, -1); + return std::pair >(op, opdata); } int opcode(std::string op) { - return _opcode(op, 0).second; + return _opdata(op, -1).second[0]; +} + +int opinputs(std::string op) { + return _opdata(op, -1).second[1]; +} + +int opoutputs(std::string op) { + return _opdata(op, -1).second[2]; } std::string op(int opcode) { - return _opcode("", opcode).first; + return _opdata("", opcode).first; } #endif diff --git a/libserpent/parser.cpp b/libserpent/parser.cpp index 38fdca6ed..5adf1672d 100644 --- a/libserpent/parser.cpp +++ b/libserpent/parser.cpp @@ -85,8 +85,11 @@ std::vector shuntingYard(std::vector tokens) { } oq.push_back(tok); } + else if (toktyp == UNARY_OP) { + stack.push_back(tok); + } // If binary op, keep popping from stack while higher bedmas precedence - else if (toktyp == UNARY_OP || toktyp == BINARY_OP) { + else if (toktyp == BINARY_OP) { if (tok.val == "-" && prevtyp != ALPHANUM && prevtyp != RPAREN) { oq.push_back(token("0", tok.metadata)); } @@ -150,10 +153,10 @@ Node treefy(std::vector stream) { else if (typ == RPAREN) { std::vector args; while (1) { + if (!oq.size()) err("Bracket without matching", tok.metadata); if (toktype(oq.back()) == LPAREN) break; args.push_back(oq.back()); oq.pop_back(); - if (!oq.size()) err("Bracket without matching", tok.metadata); } oq.pop_back(); args.push_back(oq.back()); @@ -239,14 +242,16 @@ int spaceCount(std::string s) { // Is this a command that takes an argument on the same line? bool bodied(std::string tok) { - return tok == "if" || tok == "elif" || tok == "while"; + return tok == "if" || tok == "elif" || tok == "while" + || tok == "with" || tok == "def"; } // Is this a command that takes an argument as a child block? bool childBlocked(std::string tok) { return tok == "if" || tok == "elif" || tok == "else" || tok == "code" || tok == "shared" || tok == "init" - || tok == "while" || tok == "repeat" || tok == "for"; + || tok == "while" || tok == "repeat" || tok == "for" + || tok == "with" || tok == "def"; } // Are the two commands meant to continue each other? diff --git a/libserpent/rewriter.cpp b/libserpent/rewriter.cpp index 00712b871..72feb1277 100644 --- a/libserpent/rewriter.cpp +++ b/libserpent/rewriter.cpp @@ -17,8 +17,12 @@ std::string valid[][3] = { { "alloc", "1", "1" }, { "array", "1", "1" }, { "call", "2", "4" }, + { "call_stateless", "2", "4" }, + { "post", "4", "5" }, + { "postcall", "3", "4" }, { "create", "1", "4" }, { "msg", "4", "6" }, + { "msg_stateless", "4", "6" }, { "getch", "2", "2" }, { "setch", "3", "3" }, { "sha3", "1", "2" }, @@ -78,6 +82,10 @@ std::string macros[][2] = { "(access msg.data $ind)", "(calldataload (mul 32 $ind))" }, + { + "(slice $arr $pos)", + "(add $arr (mul 32 $pos))", + }, { "(array $len)", "(alloc (mul 32 $len))" @@ -126,6 +134,22 @@ std::string macros[][2] = { "(send $to $value)", "(call (sub (gas) 25) $to $value 0 0 0 0)" }, + { + "(post $gas $to $value $datain $datainsz)", + "(~post $gas $to $value $datain (mul $datainsz 32))" + }, + { + "(post $gas $to $value $datain)", + "(seq (set $1 $datain) (~post $gas $to $value (ref $1) 32))" + }, + { + "(postcall $gas $to $datain)", + "(post $gas $to 0 $datain)", + }, + { + "(postcall $gas $to $datain $datainsz)", + "(post $gas $to 0 $datain $datainsz)", + }, { "(send $gas $to $value)", "(call $gas $to $value 0 0 0 0)" @@ -156,7 +180,7 @@ std::string macros[][2] = { }, { "(|| $x $y)", - "(seq (set $1 $x) (if (get $1) (get $1) $y))" + "(with $1 $x (if (get $1) (get $1) $y))" }, { "(>= $x $y)", @@ -180,8 +204,9 @@ std::string macros[][2] = { }, { "(create $endowment $code)", - "(seq (set $1 (msize)) (create $endowment (get $1) (lll (outer $code) (msize))))" + "(with $1 (msize) (create $endowment (get $1) (lll (outer $code) (msize))))" }, + // Call and msg { "(call $f $dataval)", "(msg (sub (gas) 45) $f 0 $dataval)" @@ -192,7 +217,7 @@ std::string macros[][2] = { }, { "(call $f $inp $inpsz $outsz)", - "(seq (set $1 $outsz) (set $2 (alloc (mul 32 (get $1)))) (pop (call (sub (gas) (add 25 (get $1))) $f 0 $inp (mul 32 $inpsz) (get $2) (mul 32 (get $1)))) (get $2))" + "(with $1 $outsz (with $2 (alloc (mul 32 (get $1))) (seq (call (sub (gas) (add 25 (get $1))) $f 0 $inp (mul 32 $inpsz) (get $2) (mul 32 (get $1))) (get $2))))" }, { "(msg $gas $to $val $inp $inpsz)", @@ -204,8 +229,34 @@ std::string macros[][2] = { }, { "(msg $gas $to $val $inp $inpsz $outsz)", - "(seq (set $1 (mul 32 $outsz)) (set $2 (alloc (get $1))) (pop (call $gas $to $val $inp (mul 32 $inpsz) (get $2) (get $1))) (get $2))" + "(with $1 (mul 32 $outsz) (with $2 (alloc (get $1)) (call $gas $to $val $inp (mul 32 $inpsz) (get $2) (get $1)) (get $2)))" }, + // Call stateless and msg stateless + { + "(call_stateless $f $dataval)", + "(msg_stateless (sub (gas) 45) $f 0 $dataval)" + }, + { + "(call_stateless $f $inp $inpsz)", + "(msg_stateless (sub (gas) 25) $f 0 $inp $inpsz)" + }, + { + "(call_stateless $f $inp $inpsz $outsz)", + "(with $1 $outsz (with $2 (alloc (mul 32 (get $1))) (seq (call_stateless (sub (gas) (add 25 (get $1))) $f 0 $inp (mul 32 $inpsz) (get $2) (mul 32 (get $1))) (get $2))))" + }, + { + "(msg_stateless $gas $to $val $inp $inpsz)", + "(seq (call_stateless $gas $to $val $inp (mul 32 $inpsz) (ref $1) 32) (get $1))" + }, + { + "(msg_stateless $gas $to $val $dataval)", + "(seq (set $1 $dataval) (call_stateless $gas $to $val (ref $1) 32 (ref $2) 32) (get $2))" + }, + { + "(msg_stateless $gas $to $val $inp $inpsz $outsz)", + "(with $1 (mul 32 $outsz) (with $2 (alloc (get $1)) (call_stateless $gas $to $val $inp (mul 32 $inpsz) (get $2) (get $1)) (get $2)))" + }, + // Wrappers { "(outer (init $init $code))", "(seq $init (~return 0 (lll $code 0)))" @@ -228,7 +279,11 @@ std::string macros[][2] = { }, { "(create $x)", - "(seq (set $1 (msize)) (create $val (get $1) (lll $code (get $1))))" + "(with $1 (msize) (create $val (get $1) (lll $code (get $1))))" + }, + { + "(with (= $var $val) $cond)", + "(with $var $val $cond)" }, { "msg.datasize", "(div (calldatasize) 32)" }, { "msg.sender", "(caller)" }, @@ -253,6 +308,8 @@ std::vector > nodeMacros; std::string synonyms[][2] = { { "or", "||" }, { "and", "&&" }, + { "|", "~or" }, + { "&", "~and" }, { "elif", "if" }, { "!", "not" }, { "string", "alloc" }, @@ -344,7 +401,7 @@ Node subst(Node pattern, Node array_lit_transform(Node node) { std::vector o1; - o1.push_back(token(intToDecimal(node.args.size() * 32), node.metadata)); + o1.push_back(token(unsignedToDecimal(node.args.size() * 32), node.metadata)); std::vector o2; std::string symb = "_temp"+mkUniqueToken()+"_0"; o2.push_back(token(symb, node.metadata)); @@ -357,7 +414,7 @@ Node array_lit_transform(Node node) { o5.push_back(token(symb, node.metadata)); std::vector o6; o6.push_back(astnode("get", o5, node.metadata)); - o6.push_back(token(intToDecimal(i * 32), node.metadata)); + o6.push_back(token(unsignedToDecimal(i * 32), node.metadata)); std::vector o7; o7.push_back(astnode("add", o6)); o7.push_back(node.args[i]); @@ -407,11 +464,12 @@ Node apply_rules(Node node) { node = array_lit_transform(node); if (node.type == ASTNODE) { unsigned i = 0; - if (node.val == "set" || node.val == "ref" || node.val == "get") { + if (node.val == "set" || node.val == "ref" + || node.val == "get" || node.val == "with") { node.args[0].val = "'" + node.args[0].val; i = 1; } - for (i = i; i < node.args.size(); i++) { + for (; i < node.args.size(); i++) { node.args[i] = apply_rules(node.args[i]); } } @@ -430,7 +488,12 @@ Node apply_rules(Node node) { } Node optimize(Node inp) { - if (inp.type == TOKEN) return tryNumberize(inp); + if (inp.type == TOKEN) { + Node o = tryNumberize(inp); + if (decimalGt(o.val, tt256, true)) + err("Value too large (exceeds 32 bytes or 2^256)", inp.metadata); + return o; + } for (unsigned i = 0; i < inp.args.size(); i++) { inp.args[i] = optimize(inp.args[i]); } @@ -474,10 +537,10 @@ Node validate(Node inp) { int i = 0; while(valid[i][0] != "---END---") { if (inp.val == valid[i][0]) { - if (decimalGt(valid[i][1], intToDecimal(inp.args.size()))) { + if (decimalGt(valid[i][1], unsignedToDecimal(inp.args.size()))) { err("Too few arguments for "+inp.val, inp.metadata); } - if (decimalGt(intToDecimal(inp.args.size()), valid[i][2])) { + if (decimalGt(unsignedToDecimal(inp.args.size()), valid[i][2])) { err("Too many arguments for "+inp.val, inp.metadata); } } diff --git a/libserpent/tokenize.cpp b/libserpent/tokenize.cpp index fc33d5dad..a5d3f1c5b 100644 --- a/libserpent/tokenize.cpp +++ b/libserpent/tokenize.cpp @@ -85,9 +85,10 @@ std::vector tokenize(std::string inp, Metadata metadata, bool lispMode) { } } // Special case the minus sign - if (cur.length() > 1 && cur[cur.length() - 1] == '-') { + if (cur.length() > 1 && (cur.substr(cur.length() - 1) == "-" + || cur.substr(cur.length() - 1) == "!")) { out.push_back(token(cur.substr(0, cur.length() - 1), metadata)); - out.push_back(token("-", metadata)); + out.push_back(token(cur.substr(cur.length() - 1), metadata)); cur = ""; } // Boundary between different char types diff --git a/libserpent/util.cpp b/libserpent/util.cpp index 6ca39de9d..0f7570a18 100644 --- a/libserpent/util.cpp +++ b/libserpent/util.cpp @@ -60,8 +60,8 @@ std::string printAST(Node ast, bool printMetadata) { std::string o = "("; if (printMetadata) { o += ast.metadata.file + " "; - o += intToDecimal(ast.metadata.ln) + " "; - o += intToDecimal(ast.metadata.ch) + ": "; + o += unsignedToDecimal(ast.metadata.ln) + " "; + o += unsignedToDecimal(ast.metadata.ch) + ": "; } o += ast.val; std::vector subs; @@ -132,14 +132,14 @@ std::string strToNumeric(std::string inp) { else if ((inp[0] == '"' && inp[inp.length()-1] == '"') || (inp[0] == '\'' && inp[inp.length()-1] == '\'')) { for (unsigned i = 1; i < inp.length() - 1; i++) { - o = decimalAdd(decimalMul(o,"256"), intToDecimal(inp[i])); + o = decimalAdd(decimalMul(o,"256"), unsignedToDecimal((unsigned char)inp[i])); } } else if (inp.substr(0,2) == "0x") { for (unsigned i = 2; i < inp.length(); i++) { int dig = std::string("0123456789abcdef").find(inp[i]); if (dig < 0) return ""; - o = decimalAdd(decimalMul(o,"16"), intToDecimal(dig)); + o = decimalAdd(decimalMul(o,"16"), unsignedToDecimal(dig)); } } else { @@ -188,7 +188,7 @@ int counter = 0; //Makes a unique token std::string mkUniqueToken() { counter++; - return intToDecimal(counter); + return unsignedToDecimal(counter); } //Does a file exist? http://stackoverflow.com/questions/12774207 @@ -217,7 +217,7 @@ std::string get_file_contents(std::string filename) //Report error void err(std::string errtext, Metadata met) { std::string err = "Error (file \"" + met.file + "\", line " + - intToDecimal(met.ln) + ", char " + intToDecimal(met.ch) + + unsignedToDecimal(met.ln + 1) + ", char " + unsignedToDecimal(met.ch) + "): " + errtext; std::cerr << err << "\n"; throw(err); @@ -254,3 +254,10 @@ std::string upperCase(std::string inp) { } return o; } + +//Three-int vector +std::vector triple(int a, int b, int c) { + std::vector o; + o.push_back(a); o.push_back(b); o.push_back(c); + return o; +} diff --git a/libserpent/util.h b/libserpent/util.h index a593d7451..4fb19bb98 100644 --- a/libserpent/util.h +++ b/libserpent/util.h @@ -103,4 +103,7 @@ std::string hexToBin(std::string inp); //Lower to upper std::string upperCase(std::string inp); +//Three-int vector +std::vector triple(int a, int b, int c); + #endif diff --git a/libwebthree/CMakeLists.txt b/libwebthree/CMakeLists.txt new file mode 100644 index 000000000..d3332d8b0 --- /dev/null +++ b/libwebthree/CMakeLists.txt @@ -0,0 +1,70 @@ +cmake_policy(SET CMP0015 NEW) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB") + +aux_source_directory(. SRC_LIST) + +set(EXECUTABLE webthree) + +# set(CMAKE_INSTALL_PREFIX ../lib) +if(ETH_STATIC) + add_library(${EXECUTABLE} STATIC ${SRC_LIST}) +else() + add_library(${EXECUTABLE} SHARED ${SRC_LIST}) +endif() + +file(GLOB HEADERS "*.h") + +include_directories(..) + +target_link_libraries(${EXECUTABLE} ethereum) +target_link_libraries(${EXECUTABLE} evm) +target_link_libraries(${EXECUTABLE} lll) +target_link_libraries(${EXECUTABLE} whisper) +target_link_libraries(${EXECUTABLE} p2p) +target_link_libraries(${EXECUTABLE} ethcore) +target_link_libraries(${EXECUTABLE} devcrypto) +target_link_libraries(${EXECUTABLE} secp256k1) +if(MINIUPNPC_LS) +target_link_libraries(${EXECUTABLE} ${MINIUPNPC_LS}) +endif() +target_link_libraries(${EXECUTABLE} ${LEVELDB_LS}) +target_link_libraries(${EXECUTABLE} ${CRYPTOPP_LS}) +target_link_libraries(${EXECUTABLE} gmp) + +if("${TARGET_PLATFORM}" STREQUAL "w64") + target_link_libraries(${EXECUTABLE} boost_system-mt-s) + target_link_libraries(${EXECUTABLE} boost_regex-mt-s) + target_link_libraries(${EXECUTABLE} boost_filesystem-mt-s) + target_link_libraries(${EXECUTABLE} boost_thread_win32-mt-s) + target_link_libraries(${EXECUTABLE} iphlpapi) + target_link_libraries(${EXECUTABLE} ws2_32) + target_link_libraries(${EXECUTABLE} mswsock) + target_link_libraries(${EXECUTABLE} shlwapi) +elseif (APPLE) + # Latest mavericks boost libraries only come with -mt + target_link_libraries(${EXECUTABLE} boost_system-mt) + target_link_libraries(${EXECUTABLE} boost_regex-mt) + target_link_libraries(${EXECUTABLE} boost_filesystem-mt) + target_link_libraries(${EXECUTABLE} boost_thread-mt) + find_package(Threads REQUIRED) + target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) +elseif (UNIX) + target_link_libraries(${EXECUTABLE} ${Boost_SYSTEM_LIBRARY}) + target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARY}) + target_link_libraries(${EXECUTABLE} ${Boost_FILESYSTEM_LIBRARY}) + target_link_libraries(${EXECUTABLE} ${Boost_THREAD_LIBRARY}) + target_link_libraries(${EXECUTABLE} ${Boost_DATE_TIME_LIBRARY}) + target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) +else () + target_link_libraries(${EXECUTABLE} boost_system) + target_link_libraries(${EXECUTABLE} boost_regex) + target_link_libraries(${EXECUTABLE} boost_filesystem) + target_link_libraries(${EXECUTABLE} boost_thread) + find_package(Threads REQUIRED) + target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) +endif () + +install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) +install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) + diff --git a/libwebthree/WebThree.cpp b/libwebthree/WebThree.cpp new file mode 100644 index 000000000..55e82a36f --- /dev/null +++ b/libwebthree/WebThree.cpp @@ -0,0 +1,84 @@ +/* + 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 . +*/ +/** @file WebThree.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "WebThree.h" + +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std; +using namespace dev; +using namespace dev::p2p; +using namespace dev::eth; +using namespace dev::shh; + +WebThreeDirect::WebThreeDirect(std::string const& _clientVersion, std::string const& _dbPath, bool _forceClean, std::set const& _interfaces, NetworkPreferences const& _n): + m_clientVersion(_clientVersion), + m_net(_clientVersion, _n) +{ + if (_dbPath.size()) + Defaults::setDBPath(_dbPath); + + if (_interfaces.count("eth")) + m_ethereum.reset(new eth::Client(&m_net, _dbPath, _forceClean)); + +// if (_interfaces.count("shh")) +// m_whisper = new eth::Whisper(m_net.get()); +} + +WebThreeDirect::~WebThreeDirect() +{ +} + +std::vector WebThreeDirect::peers() +{ + return m_net.peers(); +} + +size_t WebThreeDirect::peerCount() const +{ + return m_net.peerCount(); +} + +void WebThreeDirect::setIdealPeerCount(size_t _n) +{ + return m_net.setIdealPeerCount(_n); +} + +bytes WebThreeDirect::savePeers() +{ + return m_net.savePeers(); +} + +void WebThreeDirect::restorePeers(bytesConstRef _saved) +{ + return m_net.restorePeers(_saved); +} + +void WebThreeDirect::connect(std::string const& _seedHost, unsigned short _port) +{ + m_net.connect(_seedHost, _port); +} diff --git a/libwebthree/WebThree.h b/libwebthree/WebThree.h new file mode 100644 index 000000000..06717ee26 --- /dev/null +++ b/libwebthree/WebThree.h @@ -0,0 +1,218 @@ +/* + 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 . +*/ +/** @file Client.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace dev +{ + +class InterfaceNotSupported: public Exception { public: InterfaceNotSupported(std::string _f): m_f(_f) {} virtual std::string description() const { return "Interface " + m_f + " not supported."; } private: std::string m_f; }; + +enum WorkState +{ + Active = 0, + Deleting, + Deleted +}; + +namespace eth { class Interface; } +namespace shh { class Interface; } +namespace bzz { class Interface; } + +/** + * @brief Main API hub for interfacing with Web 3 components. This doesn't do any local multiplexing, so you can only have one + * running on any given machine for the provided DB path. + * + * Keeps a libp2p Host going (administering the work thread with m_workNet). + * + * Encapsulates a bunch of P2P protocols (interfaces), each using the same underlying libp2p Host. + * + * Provides a baseline for the multiplexed multi-protocol session class, WebThree. + */ +class WebThreeDirect +{ +public: + /// Constructor for private instance. If there is already another process on the machine using @a _dbPath, then this will throw an exception. + /// ethereum() may be safely static_cast()ed to a eth::Client*. + WebThreeDirect(std::string const& _clientVersion, std::string const& _dbPath, bool _forceClean = false, std::set const& _interfaces = {"eth", "shh"}, p2p::NetworkPreferences const& _n = p2p::NetworkPreferences()); + + /// Destructor. + ~WebThreeDirect(); + + // The mainline interfaces: + + eth::Client* ethereum() const { if (!m_ethereum) throw InterfaceNotSupported("eth"); return m_ethereum.get(); } + shh::WhisperHost* whisper() const { if (!m_whisper) throw InterfaceNotSupported("shh"); return m_whisper.get(); } + bzz::Interface* swarm() const { throw InterfaceNotSupported("bzz"); } + + // Misc stuff: + + void setClientVersion(std::string const& _name) { m_clientVersion = _name; } + + // Network stuff: + + /// Get information on the current peer set. + std::vector peers(); + + /// Same as peers().size(), but more efficient. + size_t peerCount() const; + + /// Connect to a particular peer. + void connect(std::string const& _seedHost, unsigned short _port = 30303); + + /// Is the network subsystem up? + bool haveNetwork() { return peerCount(); } + + /// Save peers + dev::bytes savePeers(); + + /// Restore peers + void restorePeers(bytesConstRef _saved); + + /// Sets the ideal number of peers. + void setIdealPeerCount(size_t _n); + + bool haveNetwork() const { return m_net.isStarted(); } + + void setNetworkPreferences(p2p::NetworkPreferences const& _n) { auto had = haveNetwork(); if (had) stopNetwork(); m_net.setNetworkPreferences(_n); if (had) startNetwork(); } + + /// Start the network subsystem. + void startNetwork() { m_net.start(); } + + /// Stop the network subsystem. + void stopNetwork() { m_net.stop(); } + +private: + std::string m_clientVersion; ///< Our end-application client's name/version. + + std::unique_ptr m_ethereum; ///< Main interface for Ethereum ("eth") protocol. + std::unique_ptr m_whisper; ///< Main interface for Whisper ("shh") protocol. + + p2p::Host m_net; ///< Should run in background and send us events when blocks found and allow us to send blocks as required. +}; + + + +// TODO, probably move into libdevrpc: + +class RPCSlave {}; +class RPCMaster {}; + +// TODO, probably move into eth: + +class EthereumSlave: public eth::Interface +{ +public: + EthereumSlave(RPCSlave*) {} + + // TODO: implement all of the virtuals with the RLPClient link. +}; + +class EthereumMaster +{ +public: + EthereumMaster(RPCMaster*) {} + + // TODO: implement the master-end of whatever the RLPClient link will send over. +}; + +// TODO, probably move into shh: + +class WhisperSlave: public shh::Interface +{ +public: + WhisperSlave(RPCSlave*) {} + + // TODO: implement all of the virtuals with the RLPClient link. +}; + +class WhisperMaster +{ +public: + WhisperMaster(RPCMaster*) {} + + // TODO: implement the master-end of whatever the RLPClient link will send over. +}; + +/** + * @brief Main API hub for interfacing with Web 3 components. + * + * This does transparent local multiplexing, so you can have as many running on the + * same machine all working from a single DB path. + */ +class WebThree +{ +public: + /// Constructor for public instance. This will be shared across the local machine. + WebThree(); + + /// Destructor. + ~WebThree(); + + // The mainline interfaces. + + eth::Interface* ethereum() const { if (!m_ethereum) throw InterfaceNotSupported("eth"); return m_ethereum; } + shh::Interface* whisper() const { if (!m_whisper) throw InterfaceNotSupported("shh"); return m_whisper; } + bzz::Interface* swarm() const { throw InterfaceNotSupported("bzz"); } + + // Peer network stuff - forward through RPCSlave, probably with P2PNetworkSlave/Master classes like Whisper & Ethereum. + + /// Get information on the current peer set. + std::vector peers(); + + /// Same as peers().size(), but more efficient. + size_t peerCount() const; + + /// Connect to a particular peer. + void connect(std::string const& _seedHost, unsigned short _port = 30303); + + /// Is the network subsystem up? + bool haveNetwork(); + + /// Save peers + dev::bytes savePeers(); + + /// Restore peers + void restorePeers(bytesConstRef _saved); + +private: + EthereumSlave* m_ethereum = nullptr; + WhisperSlave* m_whisper = nullptr; + + // TODO: + RPCSlave m_rpcSlave; +}; + +} diff --git a/libwhisper/CMakeLists.txt b/libwhisper/CMakeLists.txt new file mode 100644 index 000000000..49857d16a --- /dev/null +++ b/libwhisper/CMakeLists.txt @@ -0,0 +1,67 @@ +cmake_policy(SET CMP0015 NEW) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB") + +aux_source_directory(. SRC_LIST) + +set(EXECUTABLE whisper) + +# set(CMAKE_INSTALL_PREFIX ../lib) +if(ETH_STATIC) + add_library(${EXECUTABLE} STATIC ${SRC_LIST}) +else() + add_library(${EXECUTABLE} SHARED ${SRC_LIST}) +endif() + +file(GLOB HEADERS "*.h") + +include_directories(..) + +target_link_libraries(${EXECUTABLE} ethcore) +target_link_libraries(${EXECUTABLE} devcrypto) +target_link_libraries(${EXECUTABLE} devcore) +target_link_libraries(${EXECUTABLE} p2p) +target_link_libraries(${EXECUTABLE} secp256k1) +if(MINIUPNPC_LS) +target_link_libraries(${EXECUTABLE} ${MINIUPNPC_LS}) +endif() +target_link_libraries(${EXECUTABLE} ${LEVELDB_LS}) +target_link_libraries(${EXECUTABLE} ${CRYPTOPP_LS}) +target_link_libraries(${EXECUTABLE} gmp) + +if("${TARGET_PLATFORM}" STREQUAL "w64") + target_link_libraries(${EXECUTABLE} boost_system-mt-s) + target_link_libraries(${EXECUTABLE} boost_regex-mt-s) + target_link_libraries(${EXECUTABLE} boost_filesystem-mt-s) + target_link_libraries(${EXECUTABLE} boost_thread_win32-mt-s) + target_link_libraries(${EXECUTABLE} iphlpapi) + target_link_libraries(${EXECUTABLE} ws2_32) + target_link_libraries(${EXECUTABLE} mswsock) + target_link_libraries(${EXECUTABLE} shlwapi) +elseif (APPLE) + # Latest mavericks boost libraries only come with -mt + target_link_libraries(${EXECUTABLE} boost_system-mt) + target_link_libraries(${EXECUTABLE} boost_regex-mt) + target_link_libraries(${EXECUTABLE} boost_filesystem-mt) + target_link_libraries(${EXECUTABLE} boost_thread-mt) + find_package(Threads REQUIRED) + target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) +elseif (UNIX) + target_link_libraries(${EXECUTABLE} ${Boost_SYSTEM_LIBRARY}) + target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARY}) + target_link_libraries(${EXECUTABLE} ${Boost_FILESYSTEM_LIBRARY}) + target_link_libraries(${EXECUTABLE} ${Boost_THREAD_LIBRARY}) + target_link_libraries(${EXECUTABLE} ${Boost_DATE_TIME_LIBRARY}) + target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) +else () + target_link_libraries(${EXECUTABLE} boost_system) + target_link_libraries(${EXECUTABLE} boost_regex) + target_link_libraries(${EXECUTABLE} boost_filesystem) + target_link_libraries(${EXECUTABLE} boost_thread) + find_package(Threads REQUIRED) + target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) +endif () + +install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) +install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) + diff --git a/libwhisper/Common.cpp b/libwhisper/Common.cpp new file mode 100644 index 000000000..52bed0742 --- /dev/null +++ b/libwhisper/Common.cpp @@ -0,0 +1,28 @@ +/* + 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 . +*/ +/** @file Common.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "Common.h" + +using namespace std; +using namespace dev; +using namespace dev::p2p; +using namespace dev::shh; + diff --git a/libwhisper/Common.h b/libwhisper/Common.h new file mode 100644 index 000000000..ba4285f2b --- /dev/null +++ b/libwhisper/Common.h @@ -0,0 +1,61 @@ +/* + 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 . +*/ +/** @file Common.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace dev +{ +namespace shh +{ + +/* this makes these symbols ambiguous on VS2013 +using h256 = dev::h256; +using h512 = dev::h512; +using h256s = dev::h256s; +using bytes = dev::bytes; +using RLPStream = dev::RLPStream; +using RLP = dev::RLP; +using bytesRef = dev::bytesRef; +using bytesConstRef = dev::bytesConstRef; +using h256Set = dev::h256Set; +*/ + +class WhisperHost; +class WhisperPeer; +class Whisper; + +enum WhisperPacket +{ + StatusPacket = 0x20, + MessagesPacket, + AddFilterPacket, + RemoveFilterPacket +}; + +} +} diff --git a/libwhisper/WhisperPeer.cpp b/libwhisper/WhisperPeer.cpp new file mode 100644 index 000000000..a1f9c99b8 --- /dev/null +++ b/libwhisper/WhisperPeer.cpp @@ -0,0 +1,217 @@ +/* + 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 . +*/ +/** @file Whisper.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "WhisperPeer.h" + +#include +#include +using namespace std; +using namespace dev; +using namespace dev::p2p; +using namespace dev::shh; + +#define clogS(X) dev::LogOutputStream(false) << "| " << std::setw(2) << session()->socketId() << "] " + +WhisperPeer::WhisperPeer(Session* _s, HostCapabilityFace* _h): Capability(_s, _h) +{ + RLPStream s; + prep(s); + sealAndSend(s.appendList(2) << StatusPacket << host()->protocolVersion()); +} + +WhisperPeer::~WhisperPeer() +{ +} + +WhisperHost* WhisperPeer::host() const +{ + return static_cast(Capability::hostCapability()); +} + +bool WhisperPeer::interpret(RLP const& _r) +{ + switch (_r[0].toInt()) + { + case StatusPacket: + { + auto protocolVersion = _r[1].toInt(); + + clogS(NetMessageSummary) << "Status: " << protocolVersion; + + if (protocolVersion != host()->protocolVersion()) + disable("Invalid protocol version."); + + if (session()->id() < host()->host()->id()) + sendMessages(); + break; + } + case MessagesPacket: + { + unsigned n = 0; + for (auto i: _r) + if (n++) + host()->inject(Message(i), this); + sendMessages(); + break; + } + default: + return false; + } + return true; +} + +void WhisperPeer::sendMessages() +{ + RLPStream amalg; + unsigned n = 0; + + Guard l(x_unseen); + while (m_unseen.size()) + { + auto p = *m_unseen.begin(); + m_unseen.erase(m_unseen.begin()); + host()->streamMessage(p.second, amalg); + n++; + } + + // pause before sending if no messages to send + if (!n) + this_thread::sleep_for(chrono::milliseconds(100)); + + RLPStream s; + prep(s); + s.appendList(n + 1) << MessagesPacket; + s.appendRaw(amalg.out(), n); + sealAndSend(s); +} + +void WhisperPeer::noteNewMessage(h256 _h, Message const& _m) +{ + Guard l(x_unseen); + m_unseen[rating(_m)] = _h; +} + +WhisperHost::WhisperHost() +{ +} + +WhisperHost::~WhisperHost() +{ +} + +void WhisperHost::streamMessage(h256 _m, RLPStream& _s) const +{ + UpgradableGuard l(x_messages); + if (m_messages.count(_m)) + { + UpgradeGuard ll(l); + m_messages.at(_m).streamOut(_s); + } +} + +void WhisperHost::inject(Message const& _m, WhisperPeer* _p) +{ + auto h = _m.sha3(); + { + UpgradableGuard l(x_messages); + if (m_messages.count(h)) + return; + UpgradeGuard ll(l); + m_messages[h] = _m; + } + + if (_p) + { + Guard l(m_filterLock); + for (auto const& f: m_filters) + if (f.second.filter.matches(_m)) + noteChanged(h, f.first); + } + + for (auto& i: peers()) + if (i->cap().get() == _p) + i->addRating(1); + else + i->cap()->noteNewMessage(h, _m); +} + +void WhisperHost::noteChanged(h256 _messageHash, h256 _filter) +{ + for (auto& i: m_watches) + if (i.second.id == _filter) + { + cwatshh << "!!!" << i.first << i.second.id; + i.second.changes.push_back(_messageHash); + } +} + +bool MessageFilter::matches(Message const& _m) const +{ + for (auto const& t: m_topicMasks) + { + if (t.first.size() != t.second.size() || _m.topic.size() < t.first.size()) + continue; + for (unsigned i = 0; i < t.first.size(); ++i) + if (((t.first[i] ^ _m.topic[i]) & t.second[i]) != 0) + goto NEXT; + return true; + NEXT:; + } + return false; +} + +unsigned WhisperHost::installWatch(h256 _h) +{ + auto ret = m_watches.size() ? m_watches.rbegin()->first + 1 : 0; + m_watches[ret] = ClientWatch(_h); + cwatshh << "+++" << ret << _h; + return ret; +} + +unsigned WhisperHost::installWatch(shh::MessageFilter const& _f) +{ + Guard l(m_filterLock); + + h256 h = _f.sha3(); + + if (!m_filters.count(h)) + m_filters.insert(make_pair(h, _f)); + + return installWatch(h); +} + +void WhisperHost::uninstallWatch(unsigned _i) +{ + cwatshh << "XXX" << _i; + + Guard l(m_filterLock); + + auto it = m_watches.find(_i); + if (it == m_watches.end()) + return; + auto id = it->second.id; + m_watches.erase(it); + + auto fit = m_filters.find(id); + if (fit != m_filters.end()) + if (!--fit->second.refCount) + m_filters.erase(fit); +} diff --git a/libwhisper/WhisperPeer.h b/libwhisper/WhisperPeer.h new file mode 100644 index 000000000..94d3233c1 --- /dev/null +++ b/libwhisper/WhisperPeer.h @@ -0,0 +1,222 @@ +/* + 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 . +*/ +/** @file Whisper.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include "Common.h" + +namespace dev +{ +namespace shh +{ + +using p2p::Session; +using p2p::HostCapabilityFace; +using p2p::HostCapability; +using p2p::Capability; + +struct Message +{ + unsigned expiry = 0; + unsigned ttl = 0; + bytes topic; + bytes payload; + + Message() {} + Message(unsigned _exp, unsigned _ttl, bytes const& _topic, bytes const& _payload): expiry(_exp), ttl(_ttl), topic(_topic), payload(_payload) {} + Message(RLP const& _m) + { + expiry = _m[0].toInt(); + ttl = _m[1].toInt(); + topic = _m[2].toBytes(); + payload = _m[3].toBytes(); + } + + operator bool () const { return !!expiry; } + + void streamOut(RLPStream& _s) const { _s.appendList(4) << expiry << ttl << topic << payload; } + h256 sha3() const { RLPStream s; streamOut(s); return dev::eth::sha3(s.out()); } +}; + +/** + */ +class WhisperPeer: public Capability +{ + friend class WhisperHost; + +public: + WhisperPeer(Session* _s, HostCapabilityFace* _h); + virtual ~WhisperPeer(); + + static std::string name() { return "shh"; } + + WhisperHost* host() const; + +private: + virtual bool interpret(RLP const&); + + void sendMessages(); + + unsigned rating(Message const&) const { return 0; } // TODO + void noteNewMessage(h256 _h, Message const& _m); + + mutable dev::Mutex x_unseen; + std::map m_unseen; ///< Rated according to what they want. +}; + +class MessageFilter +{ +public: + MessageFilter() {} + MessageFilter(std::vector > const& _m): m_topicMasks(_m) {} + MessageFilter(RLP const& _r): m_topicMasks((std::vector>)_r) {} + + void fillStream(RLPStream& _s) const { _s << m_topicMasks; } + h256 sha3() const { RLPStream s; fillStream(s); return dev::eth::sha3(s.out()); } + + bool matches(Message const& _m) const; + +private: + std::vector > m_topicMasks; +}; + +struct InstalledFilter +{ + InstalledFilter(MessageFilter const& _f): filter(_f) {} + + MessageFilter filter; + unsigned refCount = 1; +}; + +struct ClientWatch +{ + ClientWatch() {} + explicit ClientWatch(h256 _id): id(_id) {} + + h256 id; + h256s changes; +}; + +class Interface +{ +public: + virtual ~Interface() {} + + virtual void inject(Message const& _m, WhisperPeer* _from = nullptr) = 0; + + virtual unsigned installWatch(MessageFilter const& _filter) = 0; + virtual unsigned installWatch(h256 _filterId) = 0; + virtual void uninstallWatch(unsigned _watchId) = 0; + virtual h256s peekWatch(unsigned _watchId) const = 0; + virtual h256s checkWatch(unsigned _watchId) = 0; + + virtual Message message(h256 _m) const = 0; + + virtual void sendRaw(bytes const& _payload, bytes const& _topic, unsigned _ttl) = 0; +}; + +class WhisperHost: public HostCapability, public Interface +{ + friend class WhisperPeer; + +public: + WhisperHost(); + virtual ~WhisperHost(); + + unsigned protocolVersion() const { return 0; } + + void inject(Message const& _m, WhisperPeer* _from = nullptr); + + unsigned installWatch(MessageFilter const& _filter); + unsigned installWatch(h256 _filterId); + void uninstallWatch(unsigned _watchId); + h256s peekWatch(unsigned _watchId) const { dev::Guard l(m_filterLock); try { return m_watches.at(_watchId).changes; } catch (...) { return h256s(); } } + h256s checkWatch(unsigned _watchId) { dev::Guard l(m_filterLock); h256s ret; try { ret = m_watches.at(_watchId).changes; m_watches.at(_watchId).changes.clear(); } catch (...) {} return ret; } + + Message message(h256 _m) const { try { dev::ReadGuard l(x_messages); return m_messages.at(_m); } catch (...) { return Message(); } } + + void sendRaw(bytes const& _payload, bytes const& _topic, unsigned _ttl) { inject(Message(time(0) + _ttl, _ttl, _topic, _payload)); } + +private: + void streamMessage(h256 _m, RLPStream& _s) const; + + void noteChanged(h256 _messageHash, h256 _filter); + + mutable dev::SharedMutex x_messages; + std::map m_messages; + + mutable dev::Mutex m_filterLock; + std::map m_filters; + std::map m_watches; +}; + +struct WatshhChannel: public dev::LogChannel { static const char* name() { return "shh"; } static const int verbosity = 1; }; +#define cwatshh dev::LogOutputStream() + +class Watch; + +} +} +/* +namespace std { void swap(shh::Watch& _a, shh::Watch& _b); } + +namespace shh +{ + +class Watch: public boost::noncopyable +{ + friend void std::swap(Watch& _a, Watch& _b); + +public: + Watch() {} + Watch(Whisper& _c, h256 _f): m_c(&_c), m_id(_c.installWatch(_f)) {} + Watch(Whisper& _c, MessageFilter const& _tf): m_c(&_c), m_id(_c.installWatch(_tf)) {} + ~Watch() { if (m_c) m_c->uninstallWatch(m_id); } + + bool check() { return m_c ? m_c->checkWatch(m_id) : false; } + bool peek() { return m_c ? m_c->peekWatch(m_id) : false; } + +private: + Whisper* m_c; + unsigned m_id; +}; + +} + +namespace shh +{ + +inline void swap(shh::Watch& _a, shh::Watch& _b) +{ + swap(_a.m_c, _b.m_c); + swap(_a.m_id, _b.m_id); +} + +} +*/ diff --git a/libwhisper/_libwhisper.cpp b/libwhisper/_libwhisper.cpp new file mode 100644 index 000000000..58bd97213 --- /dev/null +++ b/libwhisper/_libwhisper.cpp @@ -0,0 +1,5 @@ +#ifdef _MSC_VER +#include "All.h" +#include "Common.cpp" +#include "WhisperPeer.cpp" +#endif diff --git a/lllc/CMakeLists.txt b/lllc/CMakeLists.txt index 97eaea7ac..9d5e8c5ff 100644 --- a/lllc/CMakeLists.txt +++ b/lllc/CMakeLists.txt @@ -10,7 +10,7 @@ add_executable(${EXECUTABLE} ${SRC_LIST}) target_link_libraries(${EXECUTABLE} lll) target_link_libraries(${EXECUTABLE} evmface) -target_link_libraries(${EXECUTABLE} ethential) +target_link_libraries(${EXECUTABLE} devcore) if ("${TARGET_PLATFORM}" STREQUAL "w64") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++") diff --git a/lllc/main.cpp b/lllc/main.cpp index 22d40d2da..a4c9df78c 100644 --- a/lllc/main.cpp +++ b/lllc/main.cpp @@ -23,12 +23,13 @@ #include #include #include -#include -#include +#include +#include #include #include "BuildInfo.h" using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; void help() { @@ -46,9 +47,9 @@ void help() void version() { - cout << "LLLC, the Lovely Little Language Compiler " << eth::EthVersion << endl; + cout << "LLLC, the Lovely Little Language Compiler " << dev::Version << endl; cout << " By Gav Wood, (c) 2014." << endl; - cout << "Build: " << ETH_QUOTED(ETH_BUILD_PLATFORM) << "/" << ETH_QUOTED(ETH_BUILD_TYPE) << endl; + cout << "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl; exit(0); } diff --git a/neth/CMakeLists.txt b/neth/CMakeLists.txt index 3e8dab70d..2b3f92947 100644 --- a/neth/CMakeLists.txt +++ b/neth/CMakeLists.txt @@ -11,6 +11,7 @@ set(EXECUTABLE neth) add_executable(${EXECUTABLE} ${SRC_LIST}) +target_link_libraries(${EXECUTABLE} webthree) target_link_libraries(${EXECUTABLE} ethereum) target_link_libraries(${EXECUTABLE} secp256k1) target_link_libraries(${EXECUTABLE} gmp) diff --git a/neth/main.cpp b/neth/main.cpp index 01199b7fc..0dc552e39 100644 --- a/neth/main.cpp +++ b/neth/main.cpp @@ -29,13 +29,9 @@ #if ETH_JSONRPC #include #endif -#include +#include #include -#include -#include -#include -#include -#include +#include #if ETH_JSONRPC #include #include @@ -43,6 +39,7 @@ #include #include #endif +#include #include "BuildInfo.h" #undef KEY_EVENT // from windows.h @@ -52,10 +49,11 @@ #undef OK using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; +using namespace p2p; using namespace boost::algorithm; -using eth::Instruction; -using eth::c_instructionInfo; +using dev::eth::Instruction; bool isTrue(std::string const& _m) { @@ -125,18 +123,18 @@ string credits() { std::ostringstream ccout; ccout - << "NEthereum (++) " << eth::EthVersion << endl + << "NEthereum (++) " << dev::Version << endl << " Code by Gav Wood & , (c) 2013, 2014." << endl << " Based on a design by Vitalik Buterin." << endl << endl; - string vs = toString(eth::EthVersion); + string vs = toString(dev::Version); vs = vs.substr(vs.find_first_of('.') + 1)[0]; int pocnumber = stoi(vs); string m_servers; - if (pocnumber == 4) - m_servers = "54.72.31.55"; - else + if (pocnumber == 5) m_servers = "54.72.69.180"; + else + m_servers = "54.76.56.74"; ccout << "Type 'netstart 30303' to start networking" << endl; ccout << "Type 'connect " << m_servers << " 30303' to connect" << endl; @@ -146,13 +144,14 @@ string credits() void version() { - cout << "neth version " << eth::EthVersion << endl; - cout << "Build: " << ETH_QUOTED(ETH_BUILD_PLATFORM) << "/" << ETH_QUOTED(ETH_BUILD_TYPE) << endl; + cout << "neth version " << dev::Version << endl; + cout << "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl; exit(0); } -Address c_config = Address("9ef0f0d81e040012600b0c1abdef7c48f720f88a"); -string pretty(h160 _a, eth::State _st) +Address c_config = Address("661005d2720d855f1d9976f88bb10c1a3398c77f"); + +string pretty(h160 _a, dev::eth::State _st) { string ns; h256 n; @@ -298,8 +297,6 @@ int nc_window_streambuf::sync() } vector form_dialog(vector _sfields, vector _lfields, vector _bfields, int _cols, int _rows, string _post_form); -bytes parse_data(string _args); - int main(int argc, char** argv) { @@ -308,7 +305,6 @@ int main(int argc, char** argv) unsigned short remotePort = 30303; string dbPath; bool mining = false; - NodeMode mode = NodeMode::Full; unsigned peers = 5; #if ETH_JSONRPC int jsonrpc = 8080; @@ -393,19 +389,6 @@ int main(int argc, char** argv) g_logVerbosity = atoi(argv[++i]); else if ((arg == "-x" || arg == "--peers") && i + 1 < argc) peers = atoi(argv[++i]); - else if ((arg == "-o" || arg == "--mode") && i + 1 < argc) - { - string m = argv[++i]; - if (m == "full") - mode = NodeMode::Full; - else if (m == "peer") - mode = NodeMode::PeerServer; - else - { - cerr << "Unknown mode: " << m << endl; - return -1; - } - } else if (arg == "-h" || arg == "--help") help(); else if (arg == "-V" || arg == "--version") @@ -416,8 +399,12 @@ int main(int argc, char** argv) if (!clientName.empty()) clientName += "/"; - Client c("NEthereum(++)/" + clientName + "v" + eth::EthVersion + "/" ETH_QUOTED(ETH_BUILD_TYPE) "/" ETH_QUOTED(ETH_BUILD_PLATFORM), coinbase, dbPath); - c.start(); + + WebThreeDirect web3("NEthereum(++)/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), dbPath); + Client& c = *web3.ethereum(); + + c.setForceMining(true); + cout << credits(); std::ostringstream ccout; @@ -480,18 +467,20 @@ int main(int argc, char** argv) wmove(mainwin, 1, 4); if (!remoteHost.empty()) - c.startNetwork(listenPort, remoteHost, remotePort, mode, peers, publicIP, upnp); - if (mining) { - ClientGuard g(&c); - c.startMining(); + web3.setIdealPeerCount(peers); + web3.setNetworkPreferences(NetworkPreferences(listenPort, publicIP, upnp)); + web3.startNetwork(); + web3.connect(remoteHost, remotePort); } + if (mining) + c.startMining(); #if ETH_JSONRPC auto_ptr jsonrpcServer; if (jsonrpc > -1) { - jsonrpcServer = auto_ptr(new EthStubServer(new jsonrpc::HttpServer(jsonrpc), c)); + jsonrpcServer = auto_ptr(new EthStubServer(new jsonrpc::HttpServer(jsonrpc), web3)); jsonrpcServer->setKeys({us}); jsonrpcServer->StartListening(); } @@ -530,32 +519,28 @@ int main(int argc, char** argv) if (cmd == "netstart") { - eth::uint port; + unsigned port; iss >> port; - ClientGuard g(&c); - c.startNetwork((short)port); + web3.setNetworkPreferences(NetworkPreferences((short)port, publicIP, upnp)); + web3.startNetwork(); } else if (cmd == "connect") { string addr; - eth::uint port; + unsigned port; iss >> addr >> port; - ClientGuard g(&c); - c.connect(addr, (short)port); + web3.connect(addr, (short)port); } else if (cmd == "netstop") { - ClientGuard g(&c); - c.stopNetwork(); + web3.stopNetwork(); } else if (cmd == "minestart") { - ClientGuard g(&c); c.startMining(); } else if (cmd == "minestop") { - ClientGuard g(&c); c.stopMining(); } #if ETH_JSONRPC @@ -569,7 +554,7 @@ int main(int argc, char** argv) { if (jsonrpc < 0) jsonrpc = 8080; - jsonrpcServer = auto_ptr(new EthStubServer(new jsonrpc::HttpServer(jsonrpc), c)); + jsonrpcServer = auto_ptr(new EthStubServer(new jsonrpc::HttpServer(jsonrpc), web3)); jsonrpcServer->setKeys({us}); jsonrpcServer->StartListening(); } @@ -592,26 +577,25 @@ int main(int argc, char** argv) } else if (cmd == "block") { - eth::uint n = c.blockChain().details().number; + unsigned n = c.blockChain().details().number; ccout << "Current block # "; ccout << toString(n) << endl; } else if (cmd == "peers") { - for (auto it: c.peers()) + for (auto it: web3.peers()) cout << it.host << ":" << it.port << ", " << it.clientVersion << ", " << std::chrono::duration_cast(it.lastPing).count() << "ms" << endl; } else if (cmd == "balance") { - u256 balance = c.state().balance(us.address()); + u256 balance = c.balanceAt(us.address()); ccout << "Current balance:" << endl; ccout << toString(balance) << endl; } else if (cmd == "transact") { - ClientGuard g(&c); auto const& bc = c.blockChain(); auto h = bc.currentHash(); auto blockData = bc.block(h); @@ -656,7 +640,7 @@ int main(int argc, char** argv) string sdata = fields[5]; cnote << "Data:"; cnote << sdata; - bytes data = parse_data(sdata); + bytes data = dev::eth::parseData(sdata); cnote << "Bytes:"; string sbd = asString(data); bytes bbd = asBytes(sbd); @@ -664,16 +648,16 @@ int main(int argc, char** argv) ssbd << bbd; cnote << ssbd.str(); int ssize = fields[4].length(); - u256 minGas = (u256)c.state().callGas(data.size(), 0); + u256 minGas = (u256)Client::txGas(data.size(), 0); if (size < 40) { if (size > 0) - cwarn << "Invalid address length: " << size; + cwarn << "Invalid address length:" << size; } else if (gasPrice < info.minGasPrice) - cwarn << "Minimum gas price is " << info.minGasPrice; + cwarn << "Minimum gas price is" << info.minGasPrice; else if (gas < minGas) - cwarn << "Minimum gas amount is " << minGas; + cwarn << "Minimum gas amount is" << minGas; else if (ssize < 40) { if (ssize > 0) @@ -689,7 +673,6 @@ int main(int argc, char** argv) } else if (cmd == "send") { - ClientGuard g(&c); vector s; s.push_back("Address"); vector l; @@ -713,7 +696,7 @@ int main(int argc, char** argv) if (size < 40) { if (size > 0) - cwarn << "Invalid address length: " << size; + cwarn << "Invalid address length:" << size; } else { @@ -721,7 +704,7 @@ int main(int argc, char** argv) auto h = bc.currentHash(); auto blockData = bc.block(h); BlockInfo info(blockData); - u256 minGas = (u256)c.state().callGas(0, 0); + u256 minGas = (u256)Client::txGas(0, 0); Address dest = h160(fromHex(fields[0])); c.transact(us.secret(), amount, dest, bytes(), minGas, info.minGasPrice); } @@ -729,7 +712,6 @@ int main(int argc, char** argv) } else if (cmd == "contract") { - ClientGuard g(&c); auto const& bc = c.blockChain(); auto h = bc.currentHash(); auto blockData = bc.block(h); @@ -770,7 +752,7 @@ int main(int argc, char** argv) bytes init; cnote << "Init:"; cnote << sinit; - cnote << "Code size: " << size; + cnote << "Code size:" << size; if (size < 1) cwarn << "No code submitted"; else @@ -783,13 +765,13 @@ int main(int argc, char** argv) cnote << "Init:"; cnote << ssc.str(); } - u256 minGas = (u256)c.state().createGas(init.size(), 0); + u256 minGas = (u256)Client::txGas(init.size(), 0); if (endowment < 0) cwarn << "Invalid endowment"; else if (gasPrice < info.minGasPrice) - cwarn << "Minimum gas price is " << info.minGasPrice; + cwarn << "Minimum gas price is" << info.minGasPrice; else if (gas < minGas) - cwarn << "Minimum gas amount is " << minGas; + cwarn << "Minimum gas amount is" << minGas; else { c.transact(us.secret(), endowment, init, gas, gasPrice); @@ -805,20 +787,28 @@ int main(int argc, char** argv) cwarn << "Invalid address length"; else { - ClientGuard g(&c); - auto h = h160(fromHex(rechex)); + auto address = h160(fromHex(rechex)); stringstream s; - auto mem = c.state().storage(h); - - for (auto const& i: mem) - s << "@" << showbase << hex << i.first << " " << showbase << hex << i.second << endl; - s << endl << disassemble(c.state().code(h)); - string outFile = getDataDir() + "/" + rechex + ".evm"; - ofstream ofs; - ofs.open(outFile, ofstream::binary); - ofs.write(s.str().c_str(), s.str().length()); - ofs.close(); + try + { + auto storage = c.storageAt(address); + for (auto const& i: storage) + s << "@" << showbase << hex << i.first << " " << showbase << hex << i.second << endl; + s << endl << disassemble(c.codeAt(address)) << endl; + + string outFile = getDataDir() + "/" + rechex + ".evm"; + ofstream ofs; + ofs.open(outFile, ofstream::binary); + ofs.write(s.str().c_str(), s.str().length()); + ofs.close(); + + cnote << "Saved" << rechex << "to" << outFile; + } + catch (dev::eth::InvalidTrie) + { + cwarn << "Corrupted trie."; + } } } else if (cmd == "reset") @@ -840,9 +830,6 @@ int main(int argc, char** argv) // Lock to prevent corrupt block-chain errors - ClientGuard g(&c); - - auto const& st = c.state(); auto const& bc = c.blockChain(); ccout << "Genesis hash: " << bc.genesisHash() << endl; @@ -861,7 +848,7 @@ int main(int argc, char** argv) auto s = t.receiveAddress ? boost::format(" %1% %2%> %3%: %4% [%5%]") % toString(t.safeSender()) % - (st.addressHasCode(t.receiveAddress) ? '*' : '-') % + (c.codeAt(t.receiveAddress, 0).size() ? '*' : '-') % toString(t.receiveAddress) % toString(formatBalance(t.value)) % toString((unsigned)t.nonce) : @@ -881,23 +868,21 @@ int main(int argc, char** argv) // Pending y = 1; - auto aps = c.pending(); - for (auto const& t: aps) + for (Transaction const& t: c.pending()) { - if (t.receiveAddress) - auto s = boost::format("%1% %2%> %3%: %4% [%5%]") % + auto s = t.receiveAddress ? + boost::format("%1% %2%> %3%: %4% [%5%]") % toString(t.safeSender()) % - (st.addressHasCode(t.receiveAddress) ? '*' : '-') % + (c.codeAt(t.receiveAddress, 0).size() ? '*' : '-') % toString(t.receiveAddress) % toString(formatBalance(t.value)) % - toString((unsigned)t.nonce); - else - auto s = boost::format("%1% +> %2%: %3% [%4%]") % + toString((unsigned)t.nonce) : + boost::format("%1% +> %2%: %3% [%4%]") % toString(t.safeSender()) % toString(right160(sha3(rlpList(t.safeSender(), t.nonce)))) % toString(formatBalance(t.value)) % toString((unsigned)t.nonce); - mvwaddnstr(pendingwin, y++, x, s.c_str(), qwidth); + mvwaddnstr(pendingwin, y++, x, s.str().c_str(), qwidth); if (y > height * 1 / 5 - 4) break; } @@ -906,48 +891,42 @@ int main(int argc, char** argv) // Contracts and addresses y = 1; int cc = 1; - auto acs = st.addresses(); + auto acs = c.addresses(); for (auto const& i: acs) - { - auto r = i.first; - - if (st.addressHasCode(r)) + if (c.codeAt(i, 0).size()) { auto s = boost::format("%1%%2% : %3% [%4%]") % - toString(r) % - pretty(r, st) % - toString(formatBalance(i.second)) % - toString((unsigned)st.transactionsFrom(i.first)); + toString(i) % + pretty(i, c.postState()) % + toString(formatBalance(c.balanceAt(i))) % + toString((unsigned)c.countAt(i, 0)); mvwaddnstr(contractswin, cc++, x, s.str().c_str(), qwidth); if (cc > qheight - 2) break; } - } for (auto const& i: acs) - { - auto r = i.first; - if (!st.addressHasCode(r)) { + if (c.codeAt(i, 0).empty()) + { auto s = boost::format("%1%%2% : %3% [%4%]") % - toString(r) % - pretty(r, st) % - toString(formatBalance(i.second)) % - toString((unsigned)st.transactionsFrom(i.first)); + toString(i) % + pretty(i, c.postState()) % + toString(formatBalance(c.balanceAt(i))) % + toString((unsigned)c.countAt(i, 0)); mvwaddnstr(addswin, y++, x, s.str().c_str(), width / 2 - 4); if (y > height * 3 / 5 - 4) break; } - } // Peers y = 1; - string psc; - string pss; - auto cp = c.peers(); - psc = toString(cp.size()) + " peer(s)"; - for (PeerInfo const& i: cp) + for (PeerInfo const& i: web3.peers()) { - pss = toString(chrono::duration_cast(i.lastPing).count()) + " ms - " + i.host + ":" + toString(i.port) + " - " + i.clientVersion; - mvwaddnstr(peerswin, y++, x, pss.c_str(), qwidth); + auto s = boost::format("%1% ms - %2%:%3% - %4%") % + toString(chrono::duration_cast(i.lastPing).count()) % + i.host % + toString(i.port) % + i.clientVersion; + mvwaddnstr(peerswin, y++, x, s.str().c_str(), qwidth); if (y > height * 2 / 5 - 4) break; } @@ -962,15 +941,16 @@ int main(int argc, char** argv) // Balance stringstream ssb; - u256 balance = c.state().balance(us.address()); - Address gavCoin("0115554959f43bf1d04cd7e3749d00fb0623ce1f"); - u256 totalGavCoinBalance = st.storage(gavCoin, (u160)us.address()); + u256 balance = c.balanceAt(us.address()); + Address coinsAddr = right160(c.stateAt(c_config, 1)); + Address gavCoin = right160(c.stateAt(coinsAddr, c.stateAt(coinsAddr, 1))); + u256 totalGavCoinBalance = c.stateAt(gavCoin, (u160)us.address()); ssb << "Balance: " << formatBalance(balance) << " | " << totalGavCoinBalance << " GAV"; mvwprintw(consolewin, 0, x, ssb.str().c_str()); // Block mvwprintw(blockswin, 0, x, "Block # "); - eth::uint n = c.blockChain().details().number; + unsigned n = c.blockChain().details().number; mvwprintw(blockswin, 0, 10, toString(n).c_str()); // Pending @@ -985,13 +965,13 @@ int main(int argc, char** argv) // Peers mvwprintw(peerswin, 0, x, "Peers: "); - mvwprintw(peerswin, 0, 9, toString(c.peers().size()).c_str()); + mvwprintw(peerswin, 0, 9, toString(web3.peers().size()).c_str()); // Mining flag if (c.isMining()) { mvwprintw(consolewin, qheight - 1, width / 4 - 11, "Mining ON"); - eth::MineProgress p = c.miningProgress(); + dev::eth::MineProgress p = c.miningProgress(); auto speed = boost::format("%2% kH/s @ %1%s") % (p.ms / 1000) % (p.ms ? p.hashes / p.ms : 0); mvwprintw(consolewin, qheight - 2, width / 4 - speed.str().length() - 2, speed.str().c_str()); } @@ -1216,59 +1196,3 @@ vector form_dialog(vector _sv, vector _lv, vector> arg) - { - int al = arg.length(); - if (boost::starts_with(arg, "0x")) - { - bytes bs = fromHex(arg); - m_data += bs; - } - else if (arg[0] == '@') - { - arg = arg.substr(1, arg.length()); - if (boost::starts_with(arg, "0x")) - { - cnote << "hex: " << arg; - bytes bs = fromHex(arg); - int size = bs.size(); - if (size < 32) - for (auto i = 0; i < 32 - size; ++i) - m_data.push_back(0); - m_data += bs; - } - else if (boost::starts_with(arg, "\"") && boost::ends_with(arg, "\"")) - { - arg = arg.substr(1, arg.length() - 2); - cnote << "string: " << arg; - if (al < 32) - for (int i = 0; i < 32 - al; ++i) - m_data.push_back(0); - for (int i = 0; i < al; ++i) - m_data.push_back(arg[i]); - } - else - { - cnote << "value: " << arg; - bytes bs = toBigEndian(u256(arg)); - int size = bs.size(); - if (size < 32) - for (auto i = 0; i < 32 - size; ++i) - m_data.push_back(0); - m_data += bs; - } - } - else - for (int i = 0; i < al; ++i) - m_data.push_back(arg[i]); - cc++; - } - return m_data; -} diff --git a/package.sh b/package.sh index 681c20096..f6d45b07c 100755 --- a/package.sh +++ b/package.sh @@ -3,7 +3,7 @@ opwd="$PWD" br=$(git branch | grep '\*' | sed 's/^..//') -n=cpp-ethereum-src-$(date "+%Y%m%d%H%M%S" --date="1970-01-01 $(git log -1 --date=short --pretty=format:%ct) sec GMT")-$(grep "EthVersion = " libethential/Common.cpp | sed 's/^[^"]*"//' | sed 's/".*$//')-$(git rev-parse HEAD | cut -c1-6) +n=cpp-ethereum-src-$(date "+%Y%m%d%H%M%S" --date="1970-01-01 $(git log -1 --date=short --pretty=format:%ct) sec GMT")-$(grep "Version = " libdevcore/Common.cpp | sed 's/^[^"]*"//' | sed 's/".*$//')-$(git rev-parse HEAD | cut -c1-6) cd /tmp git clone "$opwd" $n diff --git a/sc/CMakeLists.txt b/sc/CMakeLists.txt index 20a8c23f3..3cacf78e6 100644 --- a/sc/CMakeLists.txt +++ b/sc/CMakeLists.txt @@ -11,7 +11,7 @@ add_executable(${EXECUTABLE} ${SRC_LIST}) target_link_libraries(${EXECUTABLE} serpent) target_link_libraries(${EXECUTABLE} lll) target_link_libraries(${EXECUTABLE} evmface) -target_link_libraries(${EXECUTABLE} ethential) +target_link_libraries(${EXECUTABLE} devcore) if ("${TARGET_PLATFORM}" STREQUAL "w64") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++") diff --git a/sc/cmdline.cpp b/sc/cmdline.cpp index fc78f74f2..b7a15634a 100644 --- a/sc/cmdline.cpp +++ b/sc/cmdline.cpp @@ -65,7 +65,7 @@ int main(int argv, char** argc) { std::cout << assemble(parseLLL(input, true)) << "\n"; } else if (command == "serialize") { - std::cout << binToHex(serialize(tokenize(input))) << "\n"; + std::cout << binToHex(serialize(tokenize(input, Metadata(), false))) << "\n"; } else if (command == "flatten") { std::cout << printTokens(flatten(parseLLL(input, true))) << "\n"; @@ -97,7 +97,7 @@ int main(int argv, char** argc) { else if (command == "biject") { if (argv == 3) std::cerr << "Not enough arguments for biject\n"; - int pos = decimalToInt(secondInput); + int pos = decimalToUnsigned(secondInput); std::vector n = prettyCompile(input); if (pos >= (int)n.size()) std::cerr << "Code position too high\n"; diff --git a/secp256k1/impl/num_gmp.h b/secp256k1/impl/num_gmp.h index 75ef9dcf6..067c15180 100644 --- a/secp256k1/impl/num_gmp.h +++ b/secp256k1/impl/num_gmp.h @@ -279,7 +279,7 @@ void static secp256k1_num_set_hex(secp256k1_num_t *r, const char *a, int alen) { 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0 }; - char num[257] = {}; + unsigned char num[257] = {}; for (int i=0; i= (rateof @item) @irate)) {} { + (set 'offerA (min @xoffer (wantof @item))) + (set 'wantA (fpmul @offerA (rateof @item))) + + (set 'xoffer (- @xoffer @offerA)) + (set 'xwant (- @xwant @wantA)) + + (deductwant @item @offerA) + + (xfer @offer (idof @item) @offerA) + (xfer @want (caller) @wantA) + + (unless @xoffer (stop)) + + (set 'item @@ @item) + [[ @last ]] @item + }) + + (set 'last @list) + (set 'item @@ @last) + + (set 'newpos (newitem @rate (caller) @xwant @list)) + + (for {} (&& @item (!= @item @newpos) (>= (rateof @item) @rate)) { (set 'last @item) (inc item) } {}) + (if (= @item @newpos) + (addwant @item @wantx) + (stitchitem @last @newpos) + ) + (stop) + }) + (when (= $0 'delete) { + (set 'offer $32) + (set 'want $64) + (set 'rate $96) + (set 'list (sha3pair @offer @want)) + (set 'last @list) + (set 'item @@ @last) + (for {} (&& @item (!= (idof @item) (caller)) (!= (rateof @item) @rate)) { (set 'last @item) (inc item) } {}) + (when @item { + (set 'xoffer (fpmul (wantof @item) (rateof @item))) + [[ @last ]] @@ @item + (xfer @offer (caller) @xoffer) + }) + (stop) + }) + (when (= $0 'price) { + (set 'offer $32) + (set 'want $96) + (set 'item (head (sha3pair @offer @want))) + (return (if @item (rateof @list) 0)) + }) +}) +} +"); + +var exchange; +env.note('Create Exchange...') +eth.transact({ 'code': exchangeCode }, function(a) { exchange = a; }); + +env.note('Register Exchange...') +eth.transact({ 'to': config, 'data': ['3', exchange] }); + + + env.note('Register my name...') -eth.transact(eth.key, '0', nameReg, "register".pad(32) + "Gav".pad(32), 10000, eth.gasPrice); +eth.transact({ 'to': nameReg, 'data': [ eth.fromAscii('register'), eth.fromAscii('Gav') ] }); + +env.note('Dole out ETH to other address...') +eth.transact({ 'value': '100000000000000000000', 'to': eth.secretToAddress(eth.keys[1]) }); + +env.note('Register my other name...') +eth.transact({ 'from': eth.keys[1], 'to': nameReg, 'data': [ eth.fromAscii('register'), eth.fromAscii("Gav Would") ] }); + +env.note('Approve Exchange...') +eth.transact({ 'to': gavCoin, 'data': [ eth.fromAscii('approve'), exchange ] }); + +env.note('Approve Exchange on other address...') +eth.transact({ 'from': eth.keys[1], 'to': gavCoin, 'data': [ eth.fromAscii('approve'), exchange ] }); + +env.note('Make offer 5000GAV/5ETH...') +eth.transact({ 'to': exchange, 'data': [eth.fromAscii('new'), gavCoin, '5000', '0', '5000000000000000000'] }); + +env.note('Register gav.eth...') +eth.transact({ 'to': dnsReg, 'data': [eth.fromAscii('register'), eth.fromAscii('gav'), eth.fromAscii('opensecrecy.com')] }); env.note('All done.') diff --git a/test/MemTrie.cpp b/test/MemTrie.cpp index 5c819ffbd..d654179f3 100644 --- a/test/MemTrie.cpp +++ b/test/MemTrie.cpp @@ -21,13 +21,14 @@ #include "MemTrie.h" -#include -#include +#include +#include #include using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; -namespace eth +namespace dev { #define ENABLE_DEBUG_PRINT 0 @@ -54,7 +55,7 @@ public: #endif /// 256-bit hash of the node - this is a SHA-3/256 hash of the RLP of the node. - h256 hash256() const { RLPStream s; makeRLP(s); return eth::sha3(s.out()); } + h256 hash256() const { RLPStream s; makeRLP(s); return dev::eth::sha3(s.out()); } bytes rlp() const { RLPStream s; makeRLP(s); return s.out(); } void mark() { m_hash256 = h256(); } @@ -199,7 +200,7 @@ void MemTrieNode::putRLP(RLPStream& _parentStream) const if (s.out().size() < 32) _parentStream.APPEND_CHILD(s.out()); else - _parentStream << eth::sha3(s.out()); + _parentStream << dev::eth::sha3(s.out()); } void TrieBranchNode::makeRLP(RLPStream& _intoStream) const @@ -228,7 +229,7 @@ void TrieInfixNode::makeRLP(RLPStream& _intoStream) const MemTrieNode* MemTrieNode::newBranch(bytesConstRef _k1, std::string const& _v1, bytesConstRef _k2, std::string const& _v2) { - uint prefix = commonPrefix(_k1, _k2); + unsigned prefix = commonPrefix(_k1, _k2); MemTrieNode* ret; if (_k1.size() == prefix) @@ -347,7 +348,7 @@ MemTrieNode* TrieInfixNode::insert(bytesConstRef _key, std::string const& _value } else { - uint prefix = commonPrefix(_key, m_ext); + unsigned prefix = commonPrefix(_key, m_ext); if (prefix) { // one infix becomes two infixes, then insert into the second @@ -478,3 +479,4 @@ void MemTrie::remove(std::string const& _key) } } + diff --git a/test/MemTrie.h b/test/MemTrie.h index 8c90f2f3f..86b09540f 100644 --- a/test/MemTrie.h +++ b/test/MemTrie.h @@ -21,10 +21,10 @@ #pragma once -#include -#include +#include +#include -namespace eth +namespace dev { class MemTrieNode; diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index 06f188a89..20c42a9b8 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -19,11 +19,14 @@ * @date 2014 */ +#include "TestHelper.h" + #include #include #include -#include "TestHelper.h" +namespace dev +{ namespace eth { @@ -39,11 +42,15 @@ void mine(Client& c, int numBlocks) void connectClients(Client& c1, Client& c2) { + // TODO: Move to WebThree. eth::Client no longer handles networking. +#if 0 short c1Port = 20000; short c2Port = 21000; c1.startNetwork(c1Port); c2.startNetwork(c2Port); c2.connect("127.0.0.1", c1Port); +#endif } } +} diff --git a/test/TestHelper.h b/test/TestHelper.h index 748373baa..d6924a17c 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -21,10 +21,15 @@ #pragma once +namespace dev +{ namespace eth { +class Client; + void mine(Client& c, int numBlocks); void connectClients(Client& c1, Client& c2); } +} diff --git a/test/TrieHash.cpp b/test/TrieHash.cpp index 8f4161a17..af32e870d 100644 --- a/test/TrieHash.cpp +++ b/test/TrieHash.cpp @@ -21,13 +21,14 @@ #include "TrieHash.h" -#include -#include +#include +#include #include using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; -namespace eth +namespace dev { /*/ @@ -67,12 +68,12 @@ void hash256rlp(HexMap const& _s, HexMap::const_iterator _begin, HexMap::const_i { // find the number of common prefix nibbles shared // i.e. the minimum number of nibbles shared at the beginning between the first hex string and each successive. - uint sharedPre = (uint)-1; - uint c = 0; + unsigned sharedPre = (unsigned)-1; + unsigned c = 0; for (auto i = std::next(_begin); i != _end && sharedPre; ++i, ++c) { - uint x = std::min(sharedPre, std::min((uint)_begin->first.size(), (uint)i->first.size())); - uint shared = _preLen; + unsigned x = std::min(sharedPre, std::min((unsigned)_begin->first.size(), (unsigned)i->first.size())); + unsigned shared = _preLen; for (; shared < x && _begin->first[shared] == i->first[shared]; ++shared) {} sharedPre = std::min(shared, sharedPre); } diff --git a/test/TrieHash.h b/test/TrieHash.h index 391c42633..be1d84480 100644 --- a/test/TrieHash.h +++ b/test/TrieHash.h @@ -21,10 +21,10 @@ #pragma once -#include -#include +#include +#include -namespace eth +namespace dev { bytes rlp256(StringMap const& _s); diff --git a/test/crypto.cpp b/test/crypto.cpp index c39de78d7..d2943984e 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -22,14 +22,15 @@ #include #include -#include -#include -#include +#include +#include +#include #include #include using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; BOOST_AUTO_TEST_CASE(crypto_tests) @@ -145,7 +146,7 @@ int cryptoTest() int ret = secp256k1_ecdsa_recover_compact((byte const*)hmsg.data(), (int)hmsg.size(), (byte const*)sig64.data(), pubkey.data(), &pubkeylen, 0, (int)t.vrs.v - 27); pubkey.resize(pubkeylen); cout << "RECPUB: " << dec << ret << " " << pubkeylen << " " << toHex(pubkey) << endl; - cout << "SENDER: " << hex << toAddress(eth::sha3(bytesConstRef(&pubkey).cropped(1))) << dec << endl; + cout << "SENDER: " << hex << toAddress(dev::eth::sha3(bytesConstRef(&pubkey).cropped(1))) << dec << endl; } #endif return 0; diff --git a/test/dagger.cpp b/test/dagger.cpp index 4f65b47aa..555c5d776 100644 --- a/test/dagger.cpp +++ b/test/dagger.cpp @@ -21,11 +21,12 @@ */ #include -#include +#include #include using namespace std; using namespace std::chrono; -using namespace eth; +using namespace dev; +using namespace dev::eth; int daggerTest() { diff --git a/test/fork.cpp b/test/fork.cpp index 09a866fb1..1cdb8822c 100644 --- a/test/fork.cpp +++ b/test/fork.cpp @@ -24,10 +24,11 @@ #include #include #include -#include +#include #include "TestHelper.h" using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; // Disabled since tests shouldn't block. Need a short cut to avoid real mining. /* diff --git a/test/genesis.cpp b/test/genesis.cpp index 441826e78..5f17d2762 100644 --- a/test/genesis.cpp +++ b/test/genesis.cpp @@ -23,12 +23,13 @@ #include #include #include "JsonSpiritHeaders.h" -#include +#include #include #include using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; namespace js = json_spirit; diff --git a/test/hexPrefix.cpp b/test/hexPrefix.cpp index 5ea670923..9b65db0e4 100644 --- a/test/hexPrefix.cpp +++ b/test/hexPrefix.cpp @@ -22,12 +22,13 @@ #include #include "JsonSpiritHeaders.h" -#include -#include +#include +#include #include using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; namespace js = json_spirit; BOOST_AUTO_TEST_CASE(hexPrefix_test) diff --git a/test/main.cpp b/test/main.cpp index 1497f2981..ef009e299 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -20,7 +20,7 @@ * Main test functions. */ -#include +#include #include "TrieHash.h" #include "MemTrie.h" @@ -35,10 +35,11 @@ int vmTest(); int hexPrefixTest(); int peerTest(int argc, char** argv); -#include +#include #include using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; BOOST_AUTO_TEST_CASE(basic_tests) { diff --git a/test/network.cpp b/test/network.cpp index 2a1614187..acdd649d9 100644 --- a/test/network.cpp +++ b/test/network.cpp @@ -24,10 +24,11 @@ #include #include #include -#include +#include #include "TestHelper.h" using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; // Disabled since tests shouldn't block (not the worst offender, but timeout should be reduced anyway). /* diff --git a/test/peer.cpp b/test/peer.cpp index 7370df34b..a99ce7201 100644 --- a/test/peer.cpp +++ b/test/peer.cpp @@ -22,12 +22,10 @@ #include #include -#include -#include -#include +#include using namespace std; -using namespace eth; -using boost::asio::ip::tcp; +using namespace dev; +using namespace dev::p2p; int peerTest(int argc, char** argv) { @@ -48,18 +46,16 @@ int peerTest(int argc, char** argv) remoteHost = argv[i]; } - BlockChain ch(boost::filesystem::temp_directory_path().string()); - PeerServer pn("Test", ch, 0, listenPort); + Host ph("Test", NetworkPreferences(listenPort)); if (!remoteHost.empty()) - pn.connect(remoteHost, remotePort); + ph.connect(remoteHost, remotePort); for (int i = 0; ; ++i) { this_thread::sleep_for(chrono::milliseconds(100)); -// pn.sync(); if (!(i % 10)) - pn.pingAll(); + ph.pingAll(); } return 0; diff --git a/test/rlp.cpp b/test/rlp.cpp index 548be8d9b..b51f3a225 100644 --- a/test/rlp.cpp +++ b/test/rlp.cpp @@ -23,17 +23,17 @@ #include #include #include "JsonSpiritHeaders.h" -#include -#include -#include +#include +#include +#include #include #include using namespace std; -using namespace eth; +using namespace dev; namespace js = json_spirit; -namespace eth +namespace dev { namespace test { @@ -116,7 +116,7 @@ namespace eth BOOST_CHECK( !u.isData() ); js::mArray& arr = v.get_array(); BOOST_CHECK( u.itemCount() == arr.size() ); - uint i; + unsigned i; for( i = 0; i < arr.size(); i++ ) { RLP item = u[i]; @@ -137,16 +137,16 @@ BOOST_AUTO_TEST_CASE(rlp_encoding_test) { cnote << "Testing RLP Encoding..."; js::mValue v; - eth::test::getRLPTestCases(v); + dev::test::getRLPTestCases(v); for (auto& i: v.get_obj()) { js::mObject& o = i.second.get_obj(); cnote << i.first; - eth::test::checkRLPTestCase(o); + dev::test::checkRLPTestCase(o); RLPStream s; - eth::test::buildRLP(o["in"], s); + dev::test::buildRLP(o["in"], s); std::string expectedText(o["out"].get_str()); std::transform(expectedText.begin(), expectedText.end(), expectedText.begin(), ::tolower ); @@ -173,19 +173,19 @@ BOOST_AUTO_TEST_CASE(rlp_decoding_test) // and then compare the output structure to the json of the // input object. js::mValue v; - eth::test::getRLPTestCases(v); + dev::test::getRLPTestCases(v); for (auto& i: v.get_obj()) { js::mObject& o = i.second.get_obj(); cnote << i.first; - eth::test::checkRLPTestCase(o); + dev::test::checkRLPTestCase(o); js::mValue& inputData = o["in"]; bytes payloadToDecode = fromHex(o["out"].get_str()); RLP payload(payloadToDecode); - eth::test::checkRLPAgainstJson(inputData, payload); + dev::test::checkRLPAgainstJson(inputData, payload); } } diff --git a/test/state.cpp b/test/state.cpp index 8d82b0c2b..99ce30957 100644 --- a/test/state.cpp +++ b/test/state.cpp @@ -26,7 +26,8 @@ #include #include using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; int stateTest() { diff --git a/test/trie.cpp b/test/trie.cpp index b28be8b18..cdebd09fc 100644 --- a/test/trie.cpp +++ b/test/trie.cpp @@ -23,28 +23,29 @@ #include #include #include "JsonSpiritHeaders.h" -#include +#include #include "TrieHash.h" #include "MemTrie.h" #include using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; namespace js = json_spirit; -namespace eth -{ - namespace test - { - static unsigned fac(unsigned _i) - { - return _i > 2 ? _i * fac(_i - 1) : _i; - } +namespace dev +{ +namespace test +{ - } +static unsigned fac(unsigned _i) +{ + return _i > 2 ? _i * fac(_i - 1) : _i; } +} +} BOOST_AUTO_TEST_CASE(trie_tests) { @@ -66,7 +67,7 @@ BOOST_AUTO_TEST_CASE(trie_tests) if (!ss.back().second.find("0x")) ss.back().second = asString(fromHex(ss.back().second.substr(2))); } - for (unsigned j = 0; j < min(1000u, eth::test::fac((unsigned)ss.size())); ++j) + for (unsigned j = 0; j < min(1000u, dev::test::fac((unsigned)ss.size())); ++j) { next_permutation(ss.begin(), ss.end()); MemoryDB m; diff --git a/test/txTest.cpp b/test/txTest.cpp index 53df93a61..cc78c26a2 100644 --- a/test/txTest.cpp +++ b/test/txTest.cpp @@ -24,10 +24,11 @@ #include #include #include -#include +#include #include "TestHelper.h" using namespace std; -using namespace eth; +using namespace dev; +using namespace dev::eth; // Disabled since tests shouldn't block. Need a short cut to avoid real mining. /* @@ -46,7 +47,7 @@ BOOST_AUTO_TEST_CASE(mine_local_simple_tx) //send c2 some eth from c1 auto txAmount = c1bal / 2u; auto gasPrice = 10 * szabo; - auto gas = eth::c_callGas; + auto gas = dev::eth::c_callGas; c1.transact(kp1.secret(), txAmount, kp2.address(), bytes(), gas, gasPrice); //mine some more to include the transaction on chain @@ -74,7 +75,7 @@ BOOST_AUTO_TEST_CASE(mine_and_send_to_peer) //send c2 some eth from c1 auto txAmount = c1bal / 2u; auto gasPrice = 10 * szabo; - auto gas = eth::c_callGas; + auto gas = dev::eth::c_callGas; c1.transact(kp1.secret(), txAmount, kp2.address(), bytes(), gas, gasPrice); //mine some more to include the transaction on chain @@ -105,7 +106,7 @@ BOOST_AUTO_TEST_CASE(mine_and_send_to_peer_fee_check) //send c2 some eth from c1 auto txAmount = c1StartBalance / 2u; auto gasPrice = 10 * szabo; - auto gas = eth::c_callGas; + auto gas = dev::eth::c_callGas; c1.transact(kp1.secret(), txAmount, c2.address(), bytes(), gas, gasPrice); //mine some more, this time with second client (so he can get fees from first client's tx) diff --git a/test/vm.cpp b/test/vm.cpp index e54967a59..8100ee822 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include #include @@ -33,9 +33,10 @@ using namespace std; using namespace json_spirit; -using namespace eth; +using namespace dev; +using namespace dev::eth; -namespace eth { namespace test { +namespace dev { namespace test { class FakeExtVM: public ExtVMFace { @@ -80,7 +81,7 @@ public: return na; } - bool call(Address _receiveAddress, u256 _value, bytesConstRef _data, u256* _gas, bytesRef _out, OnOpFunc) + bool call(Address _receiveAddress, u256 _value, bytesConstRef _data, u256* _gas, bytesRef _out, OnOpFunc, Address, Address) { /* if (get<0>(addresses[myAddress]) >= _value) { @@ -218,13 +219,13 @@ public: if (li) store[curKey] = curVal; li = s.first; - curKey = toString(li); + curKey = "0x"+toHex(toCompactBigEndian(li)); curVal = mArray(); } else for (; li != s.first; ++li) curVal.push_back(0); - push(curVal, s.second); + curVal.push_back("0x"+toHex(toCompactBigEndian(s.second))); ++li; } if (li) @@ -387,7 +388,7 @@ void doTests(json_spirit::mValue& v, bool _fillin) BOOST_REQUIRE(o.count("exec") > 0); VM vm; - eth::test::FakeExtVM fev; + dev::test::FakeExtVM fev; fev.importEnv(o["env"].get_obj()); fev.importState(o["pre"].get_obj()); @@ -419,7 +420,7 @@ void doTests(json_spirit::mValue& v, bool _fillin) BOOST_REQUIRE(o.count("out") > 0); BOOST_REQUIRE(o.count("gas") > 0); - eth::test::FakeExtVM test; + dev::test::FakeExtVM test; test.importState(o["post"].get_obj()); test.importCallCreates(o["callcreates"].get_array()); int i = 0; @@ -473,18 +474,19 @@ void doTests(json_spirit::mValue& v, bool _fillin) BOOST_AUTO_TEST_CASE(vm_tests) { + /* // Populate tests first: -// try + try { cnote << "Populating VM tests..."; json_spirit::mValue v; string s = asString(contents("../../../cpp-ethereum/test/vmtests.json")); BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of 'vmtests.json' is empty."); json_spirit::read_string(s, v); - eth::test::doTests(v, true); + dev::test::doTests(v, true); writeFile("../../../tests/vmtests.json", asBytes(json_spirit::write_string(v, true))); } -/* catch (std::exception const& e) + catch (std::exception const& e) { BOOST_ERROR("Failed VM Test with Exception: " << e.what()); }*/ @@ -496,10 +498,37 @@ BOOST_AUTO_TEST_CASE(vm_tests) string s = asString(contents("../../../tests/vmtests.json")); BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of 'vmtests.json' is empty. Have you cloned the 'tests' repo branch develop?"); json_spirit::read_string(s, v); - eth::test::doTests(v, false); + dev::test::doTests(v, false); } catch (std::exception const& e) { BOOST_ERROR("Failed VM Test with Exception: " << e.what()); } } + +BOOST_AUTO_TEST_CASE(vmArithmeticTest) +{ + /* + cnote << "Populating VM tests..."; + json_spirit::mValue v; + string s = asString(contents("../../../cpp-ethereum/test/vmArithmeticTestFiller.json")); + BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of 'vmtests.json' is empty."); + json_spirit::read_string(s, v); + dev::test::doTests(v, true); + writeFile("../../../tests/vmArithmeticTest.json", asBytes(json_spirit::write_string(v, true))); + */ + + try + { + cnote << "Testing VM arithmetic commands..."; + json_spirit::mValue v; + string s = asString(contents("../../../tests/vmArithmeticTest.json")); + BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of 'vmArithmeticTest.json' is empty. Have you cloned the 'tests' repo branch develop?"); + json_spirit::read_string(s, v); + dev::test::doTests(v, false); + } + catch (std::exception const& e) + { + BOOST_ERROR("Failed VM arithmetic test with Exception: " << e.what()); + } +} diff --git a/test/vmArithmeticTestFiller.json b/test/vmArithmeticTestFiller.json new file mode 100644 index 000000000..d7826d198 --- /dev/null +++ b/test/vmArithmeticTestFiller.json @@ -0,0 +1,2055 @@ +{ + "stop": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x00", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "add0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (+ 115792089237316195423570985008687907853269984665640564039457584007913129639935 115792089237316195423570985008687907853269984665640564039457584007913129639935) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "add1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (+ 115792089237316195423570985008687907853269984665640564039457584007913129639935 4) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "add2": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (+ 115792089237316195423570985008687907853269984665640564039457584007913129639936 1) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "add3": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (+ 0 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "add4": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (+ 1 115792089237316195423570985008687907853269984665640564039457584007913129639935) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + + "mul0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (* 2 3) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + + "mul1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (* 115792089237316195423570985008687907853269984665640564039457584007913129639935 115792089237316195423570985008687907853269984665640564039457584007913129639935) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "mul2": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (* 0 23) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "mul3": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (* 23 1) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "sub0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (- 23 1) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "sub1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (- 2 3) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "sub2": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (- 0 23) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "sub3": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (- 0 115792089237316195423570985008687907853269984665640564039457584007913129639935) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "sub4": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (- 115792089237316195423570985008687907853269984665640564039457584007913129639935 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "divByZero": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (/ 2 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "divByNonZero0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (/ 5 2) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "divByNonZero1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (/ 23 24) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "divByNonZero2": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (/ 0 24) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "divByNonZero3": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (/ 1 1) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "sdivByZero0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (SDIV (- 0 3) (- 0 0)) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "sdivByZero1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (SDIV (- 0 115792089237316195423570985008687907853269984665640564039457584007913129639935) 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "sdiv0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (SDIV (- 0 115792089237316195423570985008687907853269984665640564039457584007913129639935) 115792089237316195423570985008687907853269984665640564039457584007913129639935) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "sdiv1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (SDIV 115792089237316195423570985008687907853269984665640564039457584007913129639935 (- 0 115792089237316195423570985008687907853269984665640564039457584007913129639935) ) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "sdiv2": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (SDIV (- 0 2) (- 0 4) ) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "sdiv3": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (SDIV 4 (- 0 2) ) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "mod0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (% 2 3 ) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "mod1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (% 115792089237316195423570985008687907853269984665640564039457584007913129639935 2 ) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "mod2": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (% 0 115792089237316195423570985008687907853269984665640564039457584007913129639935 ) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "mod3": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (% 3 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "mod4": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (% (- 0 2) 3) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "smod0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (SMOD (- 0 5) (- 0 3))}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "smod1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (SMOD 5 (- 0 3))}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "smod2": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (SMOD (- 0 5) 3)}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "smod3": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (SMOD (- 0 2) 115792089237316195423570985008687907853269984665640564039457584007913129639935)}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "smod4": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (SMOD (- 0 2) 0)}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "exp0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (EXP 2 2)}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "exp1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (EXP 115792089237316195423570985008687907853269984665640564039457584007913129639935 115792089237316195423570985008687907853269984665640564039457584007913129639934 )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "exp2": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (EXP 2147483647 2147483647)}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "exp3": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (EXP 0 2147483647)}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "exp4": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (EXP 2147483647 0)}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "exp5": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (EXP 257 1)}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "exp6": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (EXP 1 257)}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "exp7": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (EXP 2 257)}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + + "neg0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (NEG 0 )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "neg1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (NEG 2 )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "neg2": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (NEG 115792089237316195423570985008687907853269984665640564039457584007913129639935 )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "neg3": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (NEG (- 0 2) )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "neg4": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (NEG (- 0 115792089237316195423570985008687907853269984665640564039457584007913129639935) )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "neg5": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (NEG (- 0 0) )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "lt0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (LT (- 0 2) 0 )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "lt1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (LT 0 (- 0 2) )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "lt2": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (LT 115792089237316195423570985008687907853269984665640564039457584007913129639935 0 )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + + "lt3": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (LT 0 115792089237316195423570985008687907853269984665640564039457584007913129639935 )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "gt0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] ( GT (- 0 2) 0 )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "gt1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (GT 0 (- 0 2) )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "gt2": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (GT 115792089237316195423570985008687907853269984665640564039457584007913129639935 0 )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + + "gt3": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (GT 0 115792089237316195423570985008687907853269984665640564039457584007913129639935 )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "slt0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (SLT (- 0 2) 0 )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "slt1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (SLT 0 (- 0 2) )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "slt2": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (SLT 115792089237316195423570985008687907853269984665640564039457584007913129639935 0 )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + + "slt3": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (SLT 0 115792089237316195423570985008687907853269984665640564039457584007913129639935 )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "slt4": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (SLT (- 0 5) (- 0 3) )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "sgt0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (SGT (- 0 2) 0 )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "sgt1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (SGT 0 (- 0 2) )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "sgt2": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (SGT 115792089237316195423570985008687907853269984665640564039457584007913129639935 0 )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + + "sgt3": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (SGT 0 115792089237316195423570985008687907853269984665640564039457584007913129639935 )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "sgt4": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (SGT (- 0 5) (- 0 3) )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "eq0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (EQ (- 0 5) (- 0 3) )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "eq1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (EQ 0 0)}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "eq2": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (EQ 115792089237316195423570985008687907853269984665640564039457584007913129639935 115792089237316195423570985008687907853269984665640564039457584007913129639935 )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "not0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (NOT 115792089237316195423570985008687907853269984665640564039457584007913129639935 )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "not1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (NOT 0 )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + + +} diff --git a/third/CMakeLists.txt b/third/CMakeLists.txt new file mode 100644 index 000000000..77b2bb496 --- /dev/null +++ b/third/CMakeLists.txt @@ -0,0 +1,100 @@ +set(CMAKE_INCLUDE_CURRENT_DIR ON) +aux_source_directory(. SRC_LIST) +include_directories(..) + +if (APPLE) + # Add homebrew path for qt5 + set(CMAKE_PREFIX_PATH /usr/local/opt/qt5) + include_directories(/usr/local/opt/qt5/include /usr/local/include) +elseif ("${TARGET_PLATFORM}" STREQUAL "w64") + set(SRC_LIST ${SRC_LIST} ../windows/qt_plugin_import.cpp) + include_directories(/usr/x86_64-w64-mingw32/include /usr/x86_64-w64-mingw32/include/QtCore /usr/x86_64-w64-mingw32/include/QtGui /usr/x86_64-w64-mingw32/include/QtQuick /usr/x86_64-w64-mingw32/include/QtQml /usr/x86_64-w64-mingw32/include/QtNetwork /usr/x86_64-w64-mingw32/include/QtWidgets /usr/x86_64-w64-mingw32/include/QtWebKit /usr/x86_64-w64-mingw32/include/QtWebKitWidgets) +elseif (UNIX) + set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ";$ENV{QTDIR}/lib/cmake") +endif () + +find_package(Qt5Core REQUIRED) +find_package(Qt5Gui REQUIRED) +find_package(Qt5Quick REQUIRED) +find_package(Qt5Qml REQUIRED) +find_package(Qt5Network REQUIRED) +find_package(Qt5Widgets REQUIRED) +find_package(Qt5WebKit REQUIRED) +find_package(Qt5WebKitWidgets REQUIRED) + +qt5_wrap_ui(ui_Main.h Main.ui) + +# Set name of binary and add_executable() +if (APPLE) + set(EXECUTEABLE Third) + set(BIN_INSTALL_DIR ".") + set(DOC_INSTALL_DIR ".") + + set(PROJECT_VERSION "${ETH_VERSION}") + set(MACOSX_BUNDLE_INFO_STRING "${PROJECT_NAME} ${PROJECT_VERSION}") + set(MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_NAME} ${PROJECT_VERSION}") + set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_NAME} ${PROJECT_VERSION}") + set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION}") + set(MACOSX_BUNDLE_COPYRIGHT "${PROJECT_COPYRIGHT_YEAR} ${PROJECT_VENDOR}") + set(MACOSX_BUNDLE_GUI_IDENTIFIER "${PROJECT_DOMAIN_SECOND}.${PROJECT_DOMAIN_FIRST}") + set(MACOSX_BUNDLE_BUNDLE_NAME ${EXECUTEABLE}) + set(MACOSX_BUNDLE_ICON_FILE third) + include(BundleUtilities) + + add_executable(${EXECUTEABLE} MACOSX_BUNDLE third.icns Main.ui ${SRC_LIST}) + set_target_properties(${EXECUTEABLE} PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/EthereumMacOSXBundleInfo.plist.in") + SET_SOURCE_FILES_PROPERTIES(${EXECUTEABLE} PROPERTIES MACOSX_PACKAGE_LOCATION MacOS) + SET_SOURCE_FILES_PROPERTIES(${MACOSX_BUNDLE_ICON_FILE}.icns PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") + +else () + set(EXECUTEABLE third) + add_executable(${EXECUTEABLE} Main.ui ${SRC_LIST}) +endif () + +qt5_use_modules(${EXECUTEABLE} Core)# Gui Widgets Network WebKit WebKitWidgets) +target_link_libraries(${EXECUTEABLE} webthree qethereum ethereum evm ethcore secp256k1 gmp ${CRYPTOPP_LS} serpent lll evmface devcore) + +if (APPLE) + # First have qt5 install plugins and frameworks + add_custom_command(TARGET ${EXECUTEABLE} POST_BUILD + COMMAND /usr/local/opt/qt5/bin/macdeployqt ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTEABLE}.app + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + + # This tool and next will inspect linked libraries in order to determine which dependencies are required + if (${CMAKE_CFG_INTDIR} STREQUAL ".") + set(APP_BUNDLE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${EXECUTEABLE}.app") + else () + set(APP_BUNDLE_PATH "${CMAKE_CURRENT_BINARY_DIR}/\$ENV{CONFIGURATION}/${EXECUTEABLE}.app") + endif () + install(CODE " + include(BundleUtilities) + set(BU_CHMOD_BUNDLE_ITEMS 1) + fixup_bundle(\"${APP_BUNDLE_PATH}\" \"${BUNDLELIBS}\" \"../libqethereum ../libethereum ../secp256k1\") + " COMPONENT RUNTIME ) + # Cleanup duplicate libs from macdeployqt + install(CODE " + file(GLOB LINGER_RM \"${APP_BUNDLE_PATH}/Contents/Frameworks/*.dylib\") + if (LINGER_RM) + file(REMOVE \${LINGER_RM}) + endif () + ") +elseif ("${TARGET_PLATFORM}" STREQUAL "w64") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-keep-inline-dllexport -static-libgcc -static-libstdc++ -static") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-s -Wl,-subsystem,windows -mthreads -L/usr/x86_64-w64-mingw32/plugins/platforms") + target_link_libraries(${EXECUTEABLE} gcc) + target_link_libraries(${EXECUTEABLE} mingw32 qtmain mswsock iphlpapi qwindows shlwapi Qt5PlatformSupport opengl32 gdi32 comdlg32 oleaut32 imm32 winmm ole32 uuid ws2_32) + target_link_libraries(${EXECUTEABLE} boost_system-mt-s) + target_link_libraries(${EXECUTEABLE} boost_filesystem-mt-s) + target_link_libraries(${EXECUTEABLE} boost_thread_win32-mt-s) + target_link_libraries(${EXECUTEABLE} crypt32) + target_link_libraries(${EXECUTEABLE} Qt5PlatformSupport) + set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS) +elseif (UNIX) +else () + target_link_libraries(${EXECUTEABLE} boost_system) + target_link_libraries(${EXECUTEABLE} boost_filesystem) + find_package(Threads REQUIRED) + target_link_libraries(${EXECUTEABLE} ${CMAKE_THREAD_LIBS_INIT}) + install( TARGETS ${EXECUTEABLE} RUNTIME DESTINATION bin ) +endif () + diff --git a/third/EthereumMacOSXBundleInfo.plist.in b/third/EthereumMacOSXBundleInfo.plist.in new file mode 100644 index 000000000..684ad7908 --- /dev/null +++ b/third/EthereumMacOSXBundleInfo.plist.in @@ -0,0 +1,38 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleGetInfoString + ${MACOSX_BUNDLE_INFO_STRING} + CFBundleIconFile + ${MACOSX_BUNDLE_ICON_FILE} + CFBundleIdentifier + ${MACOSX_BUNDLE_GUI_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLongVersionString + ${MACOSX_BUNDLE_LONG_VERSION_STRING} + CFBundleName + ${MACOSX_BUNDLE_BUNDLE_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} + CFBundleSignature + ???? + CFBundleVersion + ${MACOSX_BUNDLE_BUNDLE_VERSION} + CSResourcesFileMapped + + LSRequiresCarbon + + NSHumanReadableCopyright + ${MACOSX_BUNDLE_COPYRIGHT} + NSHighResolutionCapable + + + diff --git a/third/Main.ui b/third/Main.ui new file mode 100644 index 000000000..52ad30e7b --- /dev/null +++ b/third/Main.ui @@ -0,0 +1,480 @@ + + + Main + + + + 0 + 0 + 1711 + 1138 + + + + Third + + + true + + + QMainWindow::AllowNestedDocks|QMainWindow::AllowTabbedDocks|QMainWindow::VerticalTabs + + + true + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + 0 wei + + + + + + + 0 peers + + + + + + + 1 block + + + + + + + + + + + + + + 0 + + + true + + + true + + + + Tab 1 + + + + 0 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + + + + + about:blank + + + + + + + + + + + + + + + + 0 + 0 + 1711 + 25 + + + + + &File + + + + + + T&ools + + + + + + + + + + &Help + + + + + + &Network + + + + + + + + + + + + toolBar + + + TopToolBarArea + + + false + + + + + + QDockWidget::DockWidgetFeatureMask + + + Owned Accounts + + + 1 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::NoFocus + + + QFrame::NoFrame + + + QAbstractItemView::InternalMove + + + true + + + + + + + + + &Quit + + + Ctrl+Q + + + + + true + + + true + + + Use &UPnP + + + + + &Connect to Peer... + + + + + true + + + Enable &Network + + + + + true + + + &Mine + + + + + &New Address + + + + + &About... + + + + + true + + + &Preview + + + + + false + + + &Step Over + + + F10 + + + + + true + + + Mining &Paranoia + + + + + &Kill Blockchain + + + + + &Import Key... + + + + + &Export Selected Key... + + + + + &Inject Transaction + + + + + true + + + Show Ancient &Blocks + + + + + true + + + Show Anonymous &Accounts + + + + + true + + + Use &Past Peers + + + + + &Load Javascript... + + + + + false + + + Step Over &Backwards + + + Ctrl+F10 + + + + + true + + + &Force Mining + + + + + false + + + Standard with &Storage... + + + + + false + + + Step &Into + + + F11 + + + + + false + + + Step &Out + + + Shift+F11 + + + + + false + + + S&tandard... + + + + + false + + + Step Into Backwards + + + Ctrl+F11 + + + + + false + + + Step Out Backwards + + + Ctrl+Shift+F11 + + + + + false + + + Debu&g Current Transaction + + + + + false + + + D&ump Current Transaction State (post) + + + + + false + + + D&ump Current Transaction State (pre) + + + + + false + + + &Pretty... + + + + + &Refresh + + + + + &New Tab + + + + + + + QWebView + QWidget +
QtWebKitWidgets/QWebView
+
+
+ + tabWidget + urlEdit + + + +
diff --git a/third/MainWin.cpp b/third/MainWin.cpp new file mode 100644 index 000000000..be8420426 --- /dev/null +++ b/third/MainWin.cpp @@ -0,0 +1,603 @@ +/* + 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 . +*/ +/** @file MainWin.cpp + * @author Gav Wood + * @date 2014 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "BuildInfo.h" +#include "MainWin.h" +#include "ui_Main.h" +using namespace std; +using namespace dev; +using namespace dev::eth; +using namespace dev::p2p; + +static QString fromRaw(dev::h256 _n, unsigned* _inc = nullptr) +{ + if (_n) + { + std::string s((char const*)_n.data(), 32); + auto l = s.find_first_of('\0'); + if (!l) + return QString(); + if (l != string::npos) + { + auto p = s.find_first_not_of('\0', l); + if (!(p == string::npos || (_inc && p == 31))) + return QString(); + if (_inc) + *_inc = (byte)s[31]; + s.resize(l); + } + for (auto i: s) + if (i < 32) + return QString(); + return QString::fromStdString(s); + } + return QString(); +} + + +Address c_config = Address("661005d2720d855f1d9976f88bb10c1a3398c77f"); + +Main::Main(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::Main) +{ + setWindowFlags(Qt::Window); + ui->setupUi(this); + + cerr << "State root: " << BlockChain::genesis().stateRoot << endl; + cerr << "Block Hash: " << sha3(BlockChain::createGenesisBlock()) << endl; + cerr << "Block RLP: " << RLP(BlockChain::createGenesisBlock()) << endl; + cerr << "Block Hex: " << toHex(BlockChain::createGenesisBlock()) << endl; + cerr << "Network protocol version: " << dev::eth::c_protocolVersion << endl; + cerr << "Client database version: " << dev::eth::c_databaseVersion << endl; + + ui->ownedAccountsDock->hide(); + + statusBar()->addPermanentWidget(ui->balance); + statusBar()->addPermanentWidget(ui->peerCount); + statusBar()->addPermanentWidget(ui->mineStatus); + statusBar()->addPermanentWidget(ui->blockCount); + + connect(ui->ourAccounts->model(), SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)), SLOT(ourAccountsRowsMoved())); + + m_web3.reset(new WebThreeDirect("Third", getDataDir() + "/Third", false, {"eth", "shh"})); + m_web3->connect(Host::pocHost()); + + connect(ui->webView, &QWebView::loadStarted, [this]() + { + // NOTE: no need to delete as QETH_INSTALL_JS_NAMESPACE adopts it. + m_ethereum = new QEthereum(this, ethereum(), owned()); + + QWebFrame* f = ui->webView->page()->mainFrame(); + f->disconnect(SIGNAL(javaScriptWindowObjectCleared())); + auto qeth = m_ethereum; + connect(f, &QWebFrame::javaScriptWindowObjectCleared, QETH_INSTALL_JS_NAMESPACE(f, qeth, this)); + }); + + connect(ui->webView, &QWebView::loadFinished, [=]() + { + m_ethereum->poll(); + }); + + connect(ui->webView, &QWebView::titleChanged, [=]() + { + ui->tabWidget->setTabText(0, ui->webView->title()); + }); + + readSettings(); + + installWatches(); + + startTimer(100); + + { + QSettings s("ethereum", "third"); + if (s.value("splashMessage", true).toBool()) + { + QMessageBox::information(this, "Here Be Dragons!", "This is proof-of-concept software. The project as a whole is not even at the alpha-testing stage. It is here to show you, if you have a technical bent, the sort of thing that might be possible down the line.\nPlease don't blame us if it does something unexpected or if you're underwhelmed with the user-experience. We have great plans for it in terms of UX down the line but right now we just want to get the groundwork sorted. We welcome contributions, be they in code, testing or documentation!\nAfter you close this message it won't appear again."); + s.setValue("splashMessage", false); + } + } +} + +Main::~Main() +{ + // Must do this here since otherwise m_ethereum'll be deleted (and therefore clearWatches() called by the destructor) + // *after* the client is dead. + m_ethereum->clientDieing(); + + writeSettings(); +} + +eth::Client* Main::ethereum() const +{ + return m_web3->ethereum(); +} + +void Main::onKeysChanged() +{ + installBalancesWatch(); +} + +unsigned Main::installWatch(dev::eth::MessageFilter const& _tf, std::function const& _f) +{ + auto ret = ethereum()->installWatch(_tf); + m_handlers[ret] = _f; + return ret; +} + +unsigned Main::installWatch(dev::h256 _tf, std::function const& _f) +{ + auto ret = ethereum()->installWatch(_tf); + m_handlers[ret] = _f; + return ret; +} + +void Main::installWatches() +{ + installWatch(dev::eth::MessageFilter().altered(c_config, 0), [=](){ installNameRegWatch(); }); + installWatch(dev::eth::MessageFilter().altered(c_config, 1), [=](){ installCurrenciesWatch(); }); + installWatch(dev::eth::ChainChangedFilter, [=](){ onNewBlock(); }); +} + +void Main::installNameRegWatch() +{ + ethereum()->uninstallWatch(m_nameRegFilter); + m_nameRegFilter = installWatch(dev::eth::MessageFilter().altered((u160)ethereum()->stateAt(c_config, 0)), [=](){ onNameRegChange(); }); +} + +void Main::installCurrenciesWatch() +{ + ethereum()->uninstallWatch(m_currenciesFilter); + m_currenciesFilter = installWatch(dev::eth::MessageFilter().altered((u160)ethereum()->stateAt(c_config, 1)), [=](){ onCurrenciesChange(); }); +} + +void Main::installBalancesWatch() +{ + dev::eth::MessageFilter tf; + + vector
altCoins; + Address coinsAddr = right160(ethereum()->stateAt(c_config, 1)); + for (unsigned i = 0; i < ethereum()->stateAt(coinsAddr, 0); ++i) + altCoins.push_back(right160(ethereum()->stateAt(coinsAddr, i + 1))); + for (auto i: m_myKeys) + { + tf.altered(i.address()); + for (auto c: altCoins) + tf.altered(c, (u160)i.address()); + } + + ethereum()->uninstallWatch(m_balancesFilter); + m_balancesFilter = installWatch(tf, [=](){ onBalancesChange(); }); +} + +void Main::onNameRegChange() +{ + cwatch << "NameReg changed!"; + + // update any namereg-dependent stuff - for now force a full update. + refreshAll(); +} + +void Main::onCurrenciesChange() +{ + cwatch << "Currencies changed!"; + installBalancesWatch(); + + // TODO: update any currency-dependent stuff? +} + +void Main::onBalancesChange() +{ + cwatch << "Our balances changed!"; + + refreshBalances(); +} + +void Main::onNewBlock() +{ + cwatch << "Blockchain changed!"; + + // update blockchain dependent views. + refreshBlockCount(); +} + +void Main::note(QString _s) +{ + cnote << _s.toStdString(); +} + +void Main::debug(QString _s) +{ + cdebug << _s.toStdString(); +} + +void Main::warn(QString _s) +{ + cwarn << _s.toStdString(); +} + +void Main::eval(QString const& _js) +{ + if (_js.trimmed().isEmpty()) + return; + ui->webView->page()->currentFrame()->evaluateJavaScript("___RET=(" + _js + ")"); +} + +QString Main::pretty(dev::Address _a) const +{ + h256 n; + + if (h160 nameReg = (u160)ethereum()->stateAt(c_config, 0)) + n = ethereum()->stateAt(nameReg, (u160)(_a)); + + return fromRaw(n); +} + +QString Main::render(dev::Address _a) const +{ + QString p = pretty(_a); + if (!p.isNull()) + return p + " (" + QString::fromStdString(_a.abridged()) + ")"; + return QString::fromStdString(_a.abridged()); +} + +Address Main::fromString(QString const& _a) const +{ + if (_a == "(Create Contract)") + return Address(); + + string sn = _a.toStdString(); + if (sn.size() > 32) + sn.resize(32); + h256 n; + memcpy(n.data(), sn.data(), sn.size()); + memset(n.data() + sn.size(), 0, 32 - sn.size()); + if (_a.size()) + { + if (h160 nameReg = (u160)ethereum()->stateAt(c_config, 0)) + if (h256 a = ethereum()->stateAt(nameReg, n)) + return right160(a); + } + if (_a.size() == 40) + return Address(fromHex(_a.toStdString())); + else + return Address(); +} + +QString Main::lookup(QString const& _a) const +{ + if (!_a.endsWith(".eth")) + return _a; + + string sn = _a.mid(0, _a.size() - 4).toStdString(); + if (sn.size() > 32) + sn = sha3(sn, false); + h256 n; + memcpy(n.data(), sn.data(), sn.size()); + +/* string sn2 = _a.toStdString(); + if (sn2.size() > 32) + sn2 = sha3(sn2, false); + h256 n2; + memcpy(n2.data(), sn2.data(), sn2.size()); +*/ + + h256 ret; + if (h160 dnsReg = (u160)ethereum()->stateAt(c_config, 4, 0)) + ret = ethereum()->stateAt(dnsReg, n); +/* if (!ret) + if (h160 nameReg = (u160)ethereum()->stateAt(c_config, 0, 0)) + ret = ethereum()->stateAt(nameReg, n2); +*/ + if (ret && !((u256)ret >> 32)) + return QString("%1.%2.%3.%4").arg((int)ret[28]).arg((int)ret[29]).arg((int)ret[30]).arg((int)ret[31]); + // TODO: support IPv6. + else if (ret) + return fromRaw(ret); + else + return _a; +} + +void Main::on_about_triggered() +{ + QMessageBox::about(this, "About Third PoC-" + QString(dev::Version).section('.', 1, 1), QString("Third/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) "\n" DEV_QUOTED(ETH_COMMIT_HASH) + (ETH_CLEAN_REPO ? "\nCLEAN" : "\n+ LOCAL CHANGES") + "\n\nBy Gav Wood, 2014.\nBased on a design by Vitalik Buterin.\n\nThanks to the various contributors including: Alex Leverington, Tim Hughes, caktux, Eric Lombrozo, Marko Simovic."); +} + +void Main::writeSettings() +{ + QSettings s("ethereum", "third"); + QByteArray b; + b.resize(sizeof(Secret) * m_myKeys.size()); + auto p = b.data(); + for (auto i: m_myKeys) + { + memcpy(p, &(i.secret()), sizeof(Secret)); + p += sizeof(Secret); + } + s.setValue("address", b); + s.setValue("url", ui->urlEdit->text()); + + bytes d = m_web3->savePeers(); + if (d.size()) + m_peers = QByteArray((char*)d.data(), (int)d.size()); + s.setValue("peers", m_peers); + + s.setValue("geometry", saveGeometry()); + s.setValue("windowState", saveState()); +} + +void Main::readSettings(bool _skipGeometry) +{ + QSettings s("ethereum", "third"); + + if (!_skipGeometry) + restoreGeometry(s.value("geometry").toByteArray()); + restoreState(s.value("windowState").toByteArray()); + + m_myKeys.clear(); + QByteArray b = s.value("address").toByteArray(); + if (b.isEmpty()) + m_myKeys.append(KeyPair::create()); + else + { + h256 k; + for (unsigned i = 0; i < b.size() / sizeof(Secret); ++i) + { + memcpy(&k, b.data() + i * sizeof(Secret), sizeof(Secret)); + if (!count(m_myKeys.begin(), m_myKeys.end(), KeyPair(k))) + m_myKeys.append(KeyPair(k)); + } + } + ethereum()->setAddress(m_myKeys.back().address()); + m_peers = s.value("peers").toByteArray(); + ui->urlEdit->setText(s.value("url", "about:blank").toString()); //http://gavwood.com/gavcoin.html + on_urlEdit_returnPressed(); +} + +void Main::on_importKey_triggered() +{ + QString s = QInputDialog::getText(this, "Import Account Key", "Enter account's secret key"); + bytes b = fromHex(s.toStdString()); + if (b.size() == 32) + { + auto k = KeyPair(h256(b)); + if (std::find(m_myKeys.begin(), m_myKeys.end(), k) == m_myKeys.end()) + { + m_myKeys.append(k); + refreshBalances(); + } + else + QMessageBox::warning(this, "Already Have Key", "Could not import the secret key: we already own this account."); + } + else + QMessageBox::warning(this, "Invalid Entry", "Could not import the secret key; invalid key entered. Make sure it is 64 hex characters (0-9 or A-F)."); +} + +void Main::on_exportKey_triggered() +{ + if (ui->ourAccounts->currentRow() >= 0 && ui->ourAccounts->currentRow() < m_myKeys.size()) + { + auto k = m_myKeys[ui->ourAccounts->currentRow()]; + QMessageBox::information(this, "Export Account Key", "Secret key to account " + render(k.address()) + " is:\n" + QString::fromStdString(toHex(k.sec().ref()))); + } +} + +void Main::on_urlEdit_returnPressed() +{ + QString s = ui->urlEdit->text(); + QRegExp r("([a-z]+://)?([^/]*)(.*)"); + if (r.exactMatch(s)) + if (r.cap(2).isEmpty()) + s = (r.cap(1).isEmpty() ? "file://" : r.cap(1)) + r.cap(3); + else + s = (r.cap(1).isEmpty() ? "http://" : r.cap(1)) + lookup(r.cap(2)) + r.cap(3); + else{} + qDebug() << s; + ui->webView->setUrl(s); +} + +void Main::refreshMining() +{ + dev::eth::MineProgress p = ethereum()->miningProgress(); + ui->mineStatus->setText(ethereum()->isMining() ? QString("%1s @ %2kH/s").arg(p.ms / 1000).arg(p.ms ? p.hashes / p.ms : 0) : "Not mining"); +} + +void Main::refreshBalances() +{ + cwatch << "refreshBalances()"; + // update all the balance-dependent stuff. + ui->ourAccounts->clear(); + u256 totalBalance = 0; + map> altCoins; + Address coinsAddr = right160(ethereum()->stateAt(c_config, 1)); + for (unsigned i = 0; i < ethereum()->stateAt(coinsAddr, 0); ++i) + altCoins[right160(ethereum()->stateAt(coinsAddr, ethereum()->stateAt(coinsAddr, i + 1)))] = make_pair(fromRaw(ethereum()->stateAt(coinsAddr, i + 1)), 0); + for (auto i: m_myKeys) + { + u256 b = ethereum()->balanceAt(i.address()); + (new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(b).c_str()).arg(render(i.address())).arg((unsigned)ethereum()->countAt(i.address())), ui->ourAccounts)) + ->setData(Qt::UserRole, QByteArray((char const*)i.address().data(), Address::size)); + totalBalance += b; + + for (auto& c: altCoins) + c.second.second += (u256)ethereum()->stateAt(c.first, (u160)i.address()); + } + + QString b; + for (auto const& c: altCoins) + if (c.second.second) + b += QString::fromStdString(toString(c.second.second)) + " " + c.second.first.toUpper() + " | "; + ui->balance->setText(b + QString::fromStdString(formatBalance(totalBalance))); +} + +void Main::refreshNetwork() +{ + auto ps = m_web3->peers(); + + ui->peerCount->setText(QString::fromStdString(toString(ps.size())) + " peer(s)"); +} + +void Main::refreshAll() +{ + refreshBlockCount(); + refreshBalances(); +} + +void Main::refreshBlockCount() +{ + cwatch << "refreshBlockCount()"; + auto d = ethereum()->blockChain().details(); + auto diff = BlockInfo(ethereum()->blockChain().block()).difficulty; + ui->blockCount->setText(QString("#%1 @%3 T%2 N%4 D%5").arg(d.number).arg(toLog2(d.totalDifficulty)).arg(toLog2(diff)).arg(dev::eth::c_protocolVersion).arg(dev::eth::c_databaseVersion)); +} + +void Main::timerEvent(QTimerEvent*) +{ + // 7/18, Alex: aggregating timers, prelude to better threading? + // Runs much faster on slower dual-core processors + static int interval = 100; + + // refresh mining every 200ms + if (interval / 100 % 2 == 0) + refreshMining(); + + // refresh peer list every 1000ms, reset counter + if (interval == 1000) + { + interval = 0; + ensureNetwork(); + refreshNetwork(); + } + else + interval += 100; + + if (m_ethereum) + m_ethereum->poll(); + + for (auto const& i: m_handlers) + if (ethereum()->checkWatch(i.first)) + i.second(); +} + +void Main::ourAccountsRowsMoved() +{ + QList myKeys; + for (int i = 0; i < ui->ourAccounts->count(); ++i) + { + auto hba = ui->ourAccounts->item(i)->data(Qt::UserRole).toByteArray(); + auto h = Address((byte const*)hba.data(), Address::ConstructFromPointer); + for (auto i: m_myKeys) + if (i.address() == h) + myKeys.push_back(i); + } + m_myKeys = myKeys; + if (m_ethereum) + m_ethereum->setAccounts(myKeys); +} + +void Main::on_ourAccounts_doubleClicked() +{ + auto hba = ui->ourAccounts->currentItem()->data(Qt::UserRole).toByteArray(); + auto h = Address((byte const*)hba.data(), Address::ConstructFromPointer); + qApp->clipboard()->setText(QString::fromStdString(toHex(h.asArray()))); +} + +void Main::ensureNetwork() +{ + string n = string("Third/v") + dev::Version; + n += "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM); + web3()->setClientVersion(n); + + int pocnumber = QString(dev::Version).section('.', 1, 1).toInt(); + string defPeer; + if (pocnumber == 5) + defPeer = "54.72.69.180"; + else if (pocnumber == 6) + defPeer = "54.76.56.74"; + + if (!web3()->haveNetwork()) + { + web3()->startNetwork(); + web3()->connect(defPeer); + } + else + if (!m_web3->peerCount()) + m_web3->connect(defPeer); + if (m_peers.size()) + m_web3->restorePeers(bytesConstRef((byte*)m_peers.data(), m_peers.size())); +} + +void Main::on_connect_triggered() +{ + bool ok = false; + QString s = QInputDialog::getItem(this, "Connect to a Network Peer", "Enter a peer to which a connection may be made:", m_servers, m_servers.count() ? rand() % m_servers.count() : 0, true, &ok); + if (ok && s.contains(":")) + { + string host = s.section(":", 0, 0).toStdString(); + unsigned short port = s.section(":", 1).toInt(); + web3()->connect(host, port); + } +} + +void Main::on_mine_triggered() +{ + if (ui->mine->isChecked()) + { + ethereum()->setAddress(m_myKeys.last().address()); + ethereum()->startMining(); + } + else + ethereum()->stopMining(); +} + +// extra bits needed to link on VS +#ifdef _MSC_VER + +// include moc file, ofuscated to hide from automoc +#include\ +"moc_MainWin.cpp" + +#include\ +"moc_MiningView.cpp" + +#endif diff --git a/third/MainWin.h b/third/MainWin.h new file mode 100644 index 000000000..df05d6828 --- /dev/null +++ b/third/MainWin.h @@ -0,0 +1,131 @@ +/* + 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 . +*/ +/** @file MainWin.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Ui { +class Main; +} + +namespace dev { class WebThreeDirect; +namespace eth { +class Client; +class State; +class MessageFilter; +}} + +class QQuickView; + +class Main : public QMainWindow +{ + Q_OBJECT + +public: + explicit Main(QWidget *parent = 0); + ~Main(); + + dev::WebThreeDirect* web3() const { return m_web3.get(); } + dev::eth::Client* ethereum() const; + + QList const& owned() const { return m_myKeys; } + +public slots: + void note(QString _entry); + void debug(QString _entry); + void warn(QString _entry); + void eval(QString const& _js); + + void onKeysChanged(); + +private slots: + void on_mine_triggered(); + void on_ourAccounts_doubleClicked(); + void ourAccountsRowsMoved(); + void on_about_triggered(); + void on_connect_triggered(); + void on_quit_triggered() { close(); } + void on_urlEdit_returnPressed(); + void on_importKey_triggered(); + void on_exportKey_triggered(); + +signals: + void poll(); + +private: + QString pretty(dev::Address _a) const; + QString render(dev::Address _a) const; + dev::Address fromString(QString const& _a) const; + QString lookup(QString const& _n) const; + + void readSettings(bool _skipGeometry = false); + void writeSettings(); + + unsigned installWatch(dev::eth::MessageFilter const& _tf, std::function const& _f); + unsigned installWatch(dev::h256 _tf, std::function const& _f); + + void onNewBlock(); + void onNameRegChange(); + void onCurrenciesChange(); + void onBalancesChange(); + + void installWatches(); + void installCurrenciesWatch(); + void installNameRegWatch(); + void installBalancesWatch(); + + virtual void timerEvent(QTimerEvent*); + void ensureNetwork(); + + void refreshAll(); + void refreshBlockCount(); + void refreshBalances(); + void refreshNetwork(); + void refreshMining(); + + std::unique_ptr ui; + + std::unique_ptr m_web3; + + QList m_myKeys; + + std::map> m_handlers; + unsigned m_nameRegFilter = (unsigned)-1; + unsigned m_currenciesFilter = (unsigned)-1; + unsigned m_balancesFilter = (unsigned)-1; + + QByteArray m_peers; + QStringList m_servers; + + QNetworkAccessManager m_webCtrl; + + QEthereum* m_ethereum = nullptr; +}; diff --git a/third/alethzero.icns b/third/alethzero.icns new file mode 100644 index 000000000..105afb3d7 Binary files /dev/null and b/third/alethzero.icns differ diff --git a/third/main.cpp b/third/main.cpp new file mode 100644 index 000000000..42afd5e66 --- /dev/null +++ b/third/main.cpp @@ -0,0 +1,11 @@ +#include "MainWin.h" +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + Main w; + w.show(); + + return a.exec(); +} diff --git a/third/third.icns b/third/third.icns new file mode 100644 index 000000000..105afb3d7 Binary files /dev/null and b/third/third.icns differ diff --git a/walleth/MainWin.cpp b/walleth/MainWin.cpp index 5343ab240..3fa5a9388 100644 --- a/walleth/MainWin.cpp +++ b/walleth/MainWin.cpp @@ -7,50 +7,49 @@ #include #include #include -#include +#include #include #include #include -#include +#include #include "BuildInfo.h" #include "MainWin.h" #include "ui_Main.h" using namespace std; // types -using eth::bytes; -using eth::bytesConstRef; -using eth::h160; -using eth::h256; -using eth::u160; -using eth::u256; -using eth::u256s; -using eth::Address; -using eth::BlockInfo; -using eth::Client; -using eth::Instruction; -using eth::KeyPair; -using eth::NodeMode; -using eth::PeerInfo; -using eth::RLP; -using eth::Secret; -using eth::Transaction; +using dev::bytes; +using dev::bytesConstRef; +using dev::h160; +using dev::h256; +using dev::u160; +using dev::u256; +using dev::u256s; +using dev::Address; +using dev::eth::BlockInfo; +using dev::eth::Client; +using dev::eth::Instruction; +using dev::KeyPair; +using dev::eth::NodeMode; +using dev::p2p::PeerInfo; +using dev::RLP; +using dev::Secret; +using dev::eth::Transaction; // functions -using eth::toHex; -using eth::disassemble; -using eth::formatBalance; -using eth::fromHex; -using eth::right160; -using eth::simpleDebugOut; -using eth::toLog2; -using eth::toString; -using eth::units; +using dev::toHex; +using dev::fromHex; +using dev::right160; +using dev::simpleDebugOut; +using dev::toLog2; +using dev::toString; +using dev::eth::units; +using dev::eth::disassemble; +using dev::eth::formatBalance; // vars -using eth::g_logPost; -using eth::g_logVerbosity; -using eth::c_instructionInfo; +using dev::g_logPost; +using dev::g_logVerbosity; Main::Main(QWidget *parent) : QMainWindow(parent), @@ -62,15 +61,14 @@ Main::Main(QWidget *parent) : g_qmlMain = this; - m_client.reset(new Client("Walleth", Address(), eth::getDataDir() + "/Walleth")); - m_client->start(); + m_client.reset(new Client("Walleth", Address(), dev::getDataDir() + "/Walleth")); g_qmlClient = m_client.get(); - qRegisterMetaType("eth::u256"); - qRegisterMetaType("eth::KeyPair"); - qRegisterMetaType("eth::Secret"); - qRegisterMetaType("eth::Address"); + qRegisterMetaType("dev::u256"); + qRegisterMetaType("dev::KeyPair"); + qRegisterMetaType("dev::Secret"); + qRegisterMetaType("dev::Address"); qRegisterMetaType("QmlAccount*"); qRegisterMetaType("QmlEthereum*"); @@ -105,8 +103,6 @@ Main::Main(QWidget *parent) : connect(m_refreshNetwork, SIGNAL(timeout()), SLOT(refreshNetwork())); m_refreshNetwork->start(1000); - connect(this, SIGNAL(changed()), SLOT(refresh())); - connect(&m_webCtrl, &QNetworkAccessManager::finished, [&](QNetworkReply* _r) { m_servers = QString::fromUtf8(_r->readAll()).split("\n", QString::SkipEmptyParts); @@ -116,7 +112,7 @@ Main::Main(QWidget *parent) : on_net_triggered(true); } }); - QNetworkRequest r(QUrl("http://www.ethereum.org/servers.poc" + QString(eth::EthVersion).section('.', 1, 1) + ".txt")); + QNetworkRequest r(QUrl("http://www.ethereum.org/servers.poc" + QString(dev::Version).section('.', 1, 1) + ".txt")); r.setHeader(QNetworkRequest::UserAgentHeader, "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1712.0 Safari/537.36"); m_webCtrl.get(r); srand(time(0)); @@ -135,13 +131,12 @@ Main::~Main() void Main::timerEvent(QTimerEvent *) { - if (m_client->changed()) - changed(); + } void Main::on_about_triggered() { - QMessageBox::about(this, "About Walleth PoC-" + QString(eth::EthVersion).section('.', 1, 1), QString("Walleth/v") + eth::EthVersion + "/" ETH_QUOTED(ETH_BUILD_TYPE) "/" ETH_QUOTED(ETH_BUILD_PLATFORM) " - " ETH_QUOTED(ETH_COMMIT_HASH) "\nBy Gav Wood, 2014.\nBased on a design by Vitalik Buterin.\n\nTeam Ethereum++ includes: Tim Hughes, Eric Lombrozo, Marko Simovic, Alex Leverington and several others."); + QMessageBox::about(this, "About Walleth PoC-" + QString(dev::Version).section('.', 1, 1), QString("Walleth/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) "\n" DEV_QUOTED(ETH_COMMIT_HASH) + (ETH_CLEAN_REPO ? "\nCLEAN" : "\n+ LOCAL CHANGES") + "\n\nBy Gav Wood, 2014.\nBased on a design by Vitalik Buterin.\n\nThanks to the various contributors including: Alex Leverington, Tim Hughes, caktux, Eric Lombrozo, Marko Simovic."); } void Main::writeSettings() @@ -162,12 +157,9 @@ void Main::writeSettings() s.setValue("idealPeers", m_idealPeers); s.setValue("port", m_port); - if (client()->peerServer()) - { - bytes d = client()->peerServer()->savePeers(); + bytes d = client()->savePeers(); + if (d.size()) m_peers = QByteArray((char*)d.data(), (int)d.size()); - - } s.setValue("peers", m_peers); s.setValue("geometry", saveGeometry()); @@ -207,16 +199,8 @@ void Main::refreshNetwork() ui->peerCount->setText(QString::fromStdString(toString(ps.size())) + " peer(s)"); } -eth::State const& Main::state() const -{ - return ui->preview->isChecked() ? client()->postState() : client()->state(); -} - void Main::refresh() { - eth::ClientGuard l(client()); - auto const& st = state(); - auto d = client()->blockChain().details(); auto diff = BlockInfo(client()->blockChain().block()).difficulty; ui->blockCount->setText(QString("#%1 @%3 T%2").arg(d.number).arg(toLog2(d.totalDifficulty)).arg(toLog2(diff))); @@ -225,7 +209,7 @@ void Main::refresh() u256 totalBalance = 0; for (auto i: m_myKeys) { - u256 b = st.balance(i.address()); + u256 b = m_client->balanceAt(i.address()); totalBalance += b; } ui->balance->setText(QString::fromStdString(formatBalance(totalBalance))); @@ -233,10 +217,10 @@ void Main::refresh() void Main::on_net_triggered(bool _auto) { - string n = string("Walleth/v") + eth::EthVersion; + string n = string("Walleth/v") + dev::Version; if (m_clientName.size()) n += "/" + m_clientName.toStdString(); - n += "/" ETH_QUOTED(ETH_BUILD_TYPE) "/" ETH_QUOTED(ETH_BUILD_PLATFORM); + n += "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM); client()->setClientVersion(n); if (ui->net->isChecked()) { @@ -248,7 +232,7 @@ void Main::on_net_triggered(bool _auto) else client()->startNetwork(m_port, string(), 0, NodeMode::Full, m_idealPeers, "", ui->upnp->isChecked()); if (m_peers.size()) - client()->peerServer()->restorePeers(bytesConstRef((byte*)m_peers.data(), m_peers.size())); + client()->restorePeers(bytesConstRef((byte*)m_peers.data(), m_peers.size())); } else client()->stopNetwork(); diff --git a/walleth/MainWin.h b/walleth/MainWin.h index 61891e0a3..860db8bc9 100644 --- a/walleth/MainWin.h +++ b/walleth/MainWin.h @@ -6,16 +6,16 @@ #include #include #include -#include +#include namespace Ui { class Main; } -namespace eth { +namespace dev { namespace eth { class Client; class State; -} +}} class QQuickView; class QQmlEngine; @@ -29,7 +29,7 @@ public: explicit Main(QWidget *parent = 0); ~Main(); - eth::Client* client() const { return m_client.get(); } + dev::eth::Client* client() const { return m_client.get(); } private slots: void on_connect_triggered(); @@ -43,26 +43,23 @@ private slots: void refresh(); void refreshNetwork(); -signals: - void changed(); - protected: virtual void timerEvent(QTimerEvent *); private: -/* QString pretty(eth::Address _a) const; - QString render(eth::Address _a) const; - eth::Address fromString(QString const& _a) const; +/* QString pretty(dev::Address _a) const; + QString render(dev::Address _a) const; + dev::Address fromString(QString const& _a) const; */ - eth::State const& state() const; + dev::eth::State const& state() const; void updateFee(); void readSettings(); void writeSettings(); - eth::u256 fee() const; - eth::u256 total() const; - eth::u256 value() const; + dev::u256 fee() const; + dev::u256 total() const; + dev::u256 value() const; std::unique_ptr ui; @@ -70,7 +67,7 @@ private: QMutex m_guiLock; QTimer* m_refresh; QTimer* m_refreshNetwork; - QVector m_myKeys; + QVector m_myKeys; bool m_keysChanged = false; int m_port; int m_idealPeers; @@ -81,7 +78,7 @@ private: QNetworkAccessManager m_webCtrl; - std::unique_ptr m_client; + std::unique_ptr m_client; }; #endif // MAIN_H diff --git a/windows/Alethzero.vcxproj b/windows/Alethzero.vcxproj index 06c78c4da..d418e5d03 100644 --- a/windows/Alethzero.vcxproj +++ b/windows/Alethzero.vcxproj @@ -87,7 +87,7 @@ Use Disabled - WIN32;_DEBUG;%(PreprocessorDefinitions) + WIN32;_WINSOCK_DEPRECATED_NO_WARNINGS;_DEBUG;%(PreprocessorDefinitions) MultiThreadedDebugDLL true stdafx.h @@ -126,7 +126,7 @@ MaxSpeed true true - WIN32;NDEBUG;%(PreprocessorDefinitions) + WIN32;_WINSOCK_DEPRECATED_NO_WARNINGS;NDEBUG;%(PreprocessorDefinitions) MultiThreadedDLL true AnySuitable @@ -209,6 +209,24 @@ + + $(Lua) moc.lua "$(QtBin)/moc" "$(IntDir)moc_%(FileName).cpp" "@(ClCompile->'%(AdditionalIncludeDirectories)');$(IncludePath)" "@(ClCompile->'%(PreprocessorDefinitions)');_MSC_VER=1800" "%(FullPath)" + + + $(Lua) moc.lua "$(QtBin)/moc" "$(IntDir)moc_%(FileName).cpp" "@(ClCompile->'%(AdditionalIncludeDirectories)');$(IncludePath)" "@(ClCompile->'%(PreprocessorDefinitions)');_MSC_VER=1800" "%(FullPath)" + + + $(Lua) moc.lua "$(QtBin)/moc" "$(IntDir)moc_%(FileName).cpp" "@(ClCompile->'%(AdditionalIncludeDirectories)');$(IncludePath)" "@(ClCompile->'%(PreprocessorDefinitions)');_MSC_VER=1800" "%(FullPath)" + + + $(Lua) moc.lua "$(QtBin)/moc" "$(IntDir)moc_%(FileName).cpp" "@(ClCompile->'%(AdditionalIncludeDirectories)');$(IncludePath)" "@(ClCompile->'%(PreprocessorDefinitions)');_MSC_VER=1800" "%(FullPath)" + + + $(IntDir)moc_%(FileName).cpp + $(IntDir)moc_%(FileName).cpp + $(IntDir)moc_%(FileName).cpp + $(IntDir)moc_%(FileName).cpp + $(Lua) moc.lua "$(QtBin)/moc" "$(IntDir)moc_%(FileName).cpp" "@(ClCompile->'%(AdditionalIncludeDirectories)');$(IncludePath)" "@(ClCompile->'%(PreprocessorDefinitions)');_MSC_VER=1800" "%(FullPath)" @@ -289,4 +307,4 @@ - \ No newline at end of file + diff --git a/windows/Ethereum.sln b/windows/Ethereum.sln index 6403d7fad..26fe794c2 100644 --- a/windows/Ethereum.sln +++ b/windows/Ethereum.sln @@ -23,8 +23,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LibMiniUPnPc", "LibMiniUPnP EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AlethZero", "Alethzero.vcxproj", "{BFCFFC46-78A3-4350-9938-0EA3A2B1CCCD}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Walleth", "Walleth.vcxproj", "{326EF470-463F-4751-A22A-48BBAAD8B143}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LibQEthereum", "LibQEthereum.vcxproj", "{DF3C5B07-A1A2-4F16-B37F-A27333622CDD}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "eth", "Eth.vcxproj", "{C60C065C-2135-4B2B-AFD4-35FD7AC56B40}" diff --git a/windows/LibEthereum.vcxproj b/windows/LibEthereum.vcxproj index f782cca17..c87eaebcc 100644 --- a/windows/LibEthereum.vcxproj +++ b/windows/LibEthereum.vcxproj @@ -19,49 +19,153 @@ - - - - - - - - - - - - - - + true true true true - - + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + + + + + + + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + + + + + + + - - - + + + + - - - + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + + true + true + true + true + + stdafx.h Create @@ -71,46 +175,165 @@ - - - - - - - - - - - - - - - - - - - - - - + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + + + + + + + + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + + + + + + +<<<<<<< HEAD +======= + + + + + +>>>>>>> develop + - - - - + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + @@ -118,6 +341,60 @@ + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + @@ -257,4 +534,4 @@ - \ No newline at end of file + diff --git a/windows/LibEthereum.vcxproj.filters b/windows/LibEthereum.vcxproj.filters index 922aa4d9e..b6e2dc070 100644 --- a/windows/LibEthereum.vcxproj.filters +++ b/windows/LibEthereum.vcxproj.filters @@ -22,24 +22,84 @@ libethereum - + libethereum - + libethereum - + libethereum - + + libevm + + + libevm + + + libevm + + + libevmface + + + liblll + + + liblll + + + liblll + + + liblll + + + liblll + + libethereum - + libethereum - + + libethereum + + + libethereum + + + libethereum + + libethereum + + libethereum + + + libdevcrypto + + + libdevcrypto + + + libdevcrypto + + + libdevcrypto + + + libdevcrypto + + + libdevcrypto + + + libdevcrypto + libethcore @@ -49,71 +109,71 @@ libethcore - - libevm + + libdevcore - - libevm + + libdevcore - - libevm + + libdevcore - - libethential + + libdevcore - - libethential + + libdevcore - - libethential + + libdevcore - - libethential + + libdevcore - - libethential + + libethcore - - libethential + + libevm - - libevmface + + libethereum - - libethcore + + libethereum - - libethcore + + libethereum - - libethcore + + libethereum - - libethcore + + libethereum - - libethcore + + libp2p - - libethcore + + libp2p - - libethcore + + libp2p - - liblll + + libp2p - - liblll + + libp2p - - liblll + + libp2p - - liblll + + libwhisper - - liblll + + libwhisper libethereum @@ -141,15 +201,6 @@ libethereum - - libethereum - - - libethereum - - - libethereum - libethereum @@ -159,18 +210,6 @@ libethereum - - libethcore - - - libethcore - - - libethcore - - - libethcore - libevm @@ -180,86 +219,173 @@ libevm - - libethential + + libevm - - libethential + + libevmface - - libethential + + liblll - - libethential + + liblll - - libethential + + liblll - - libethential + + liblll - - libethential + + liblll - - libethential + + liblll - - libethential + + liblll - - libevm + + libethereum - - libevmface + + libethereum + + + libethereum + + + libethereum + + + libethereum + + + libethereum + + + libethereum + + + libdevcrypto + + + libdevcrypto + + + libdevcrypto + + + libdevcrypto + + + libdevcrypto + + + libdevcrypto + + + libdevcrypto + + + libdevcrypto + + + libdevcrypto libethcore - + libethcore - + libethcore - + libethcore - + libethcore - + libethcore - - libethcore + + libdevcore - - libethcore + + libdevcore - - libethcore + + libdevcore - - liblll + + libdevcore - - liblll + + libdevcore - - liblll + + libdevcore - - liblll + + libdevcore - - liblll + + libdevcore - - liblll + + libdevcore - - liblll + + libdevcore + + + libethereum + + + libethereum + + + libethereum + + + libethereum + + + libethereum + + + libethereum + + + libp2p + + + libp2p + + + libp2p + + + libp2p + + + libp2p + + + libp2p + + + libp2p + + + libwhisper + + + libwhisper libethereum @@ -281,11 +407,20 @@ {ed9ad1b3-700c-47f9-8548-a90b5ef179ac} - - {35c32f6c-3f19-4603-8084-1b88ec9ae498} - {e6332606-e0ca-48aa-8a6b-303971ba7a93} + + {fae2102b-d574-40fc-9f90-1b9ed0d117ac} + + + {35c32f6c-3f19-4603-8084-1b88ec9ae498} + + + {fc2cb618-ab0c-45b6-8eb9-6d88e0336fa9} + + + {36748e80-c977-4fee-84e6-699c039dff87} + \ No newline at end of file diff --git a/windows/LibMiniUPnPc.vcxproj b/windows/LibMiniUPnPc.vcxproj index 865b6c74d..7e5e44412 100644 --- a/windows/LibMiniUPnPc.vcxproj +++ b/windows/LibMiniUPnPc.vcxproj @@ -106,7 +106,7 @@ Disabled MultiThreadedDebugDLL - _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) 4100;4244;4245;4267;4389;%(DisableSpecificWarnings) @@ -142,7 +142,7 @@ true true MultiThreadedDLL - _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) 4100;4244;4245;4267;4389;%(DisableSpecificWarnings) diff --git a/windows/LibQEthereum.props b/windows/LibQEthereum.props index bdf8eadae..08ba05c69 100644 --- a/windows/LibQEthereum.props +++ b/windows/LibQEthereum.props @@ -17,6 +17,7 @@ ..;$(QtInclude);%(AdditionalIncludeDirectories) 4718;%(DisableSpecificWarnings) + ETH_QTQML=1;%(PreprocessorDefinitions) diff --git a/windows/LibQEthereum.vcxproj b/windows/LibQEthereum.vcxproj index 24b0643a6..20b29e06e 100644 --- a/windows/LibQEthereum.vcxproj +++ b/windows/LibQEthereum.vcxproj @@ -25,6 +25,7 @@ + Create Create @@ -53,6 +54,24 @@ + + $(Lua) moc.lua "$(QtBin)/moc" "$(IntDir)moc_%(FileName).cpp" "@(ClCompile->'%(AdditionalIncludeDirectories)');$(IncludePath)" "@(ClCompile->'%(PreprocessorDefinitions)');_MSC_VER=1800" "%(FullPath)" + + + $(Lua) moc.lua "$(QtBin)/moc" "$(IntDir)moc_%(FileName).cpp" "@(ClCompile->'%(AdditionalIncludeDirectories)');$(IncludePath)" "@(ClCompile->'%(PreprocessorDefinitions)');_MSC_VER=1800" "%(FullPath)" + + + $(Lua) moc.lua "$(QtBin)/moc" "$(IntDir)moc_%(FileName).cpp" "@(ClCompile->'%(AdditionalIncludeDirectories)');$(IncludePath)" "@(ClCompile->'%(PreprocessorDefinitions)');_MSC_VER=1800" "%(FullPath)" + + + $(Lua) moc.lua "$(QtBin)/moc" "$(IntDir)moc_%(FileName).cpp" "@(ClCompile->'%(AdditionalIncludeDirectories)');$(IncludePath)" "@(ClCompile->'%(PreprocessorDefinitions)');_MSC_VER=1800" "%(FullPath)" + + + $(IntDir)moc_%(FileName).cpp + $(IntDir)moc_%(FileName).cpp + $(IntDir)moc_%(FileName).cpp + $(IntDir)moc_%(FileName).cpp + diff --git a/windows/LibQEthereum.vcxproj.filters b/windows/LibQEthereum.vcxproj.filters index b3ef93314..e38730504 100644 --- a/windows/LibQEthereum.vcxproj.filters +++ b/windows/LibQEthereum.vcxproj.filters @@ -10,11 +10,13 @@ Windows + Windows +