├── .gitignore ├── LICENSE ├── README.md ├── SUMMARY.md ├── add-precompiled-contract-in-cpp-ethereum.md ├── application ├── README.md └── how-to-implete-a-decentralized-file-storage.md ├── book.json ├── ethereum-core-storage.md ├── ethereum-state-clearing.md ├── evm-learn.md ├── golem-multisig-wallet.md ├── how-to-sign-and-hash-tx.md ├── images ├── 2016 │ └── 10 │ │ ├── CodeCogsEqn-19.gif │ │ ├── borgcube.jpg │ │ ├── fourpoints.png │ │ ├── haha.jpg │ │ ├── merkle.png │ │ ├── merkle_verify.png │ │ ├── threepoints.png │ │ └── twopoints.png ├── 2017 │ ├── 10 │ │ ├── 3.png │ │ ├── eth-sign-tx-hash.png │ │ ├── image001.png │ │ ├── image003.png │ │ ├── image005.png │ │ ├── image009.png │ │ ├── image011.png │ │ ├── image013.png │ │ ├── image015.png │ │ ├── image017.png │ │ ├── image019.png │ │ └── image021.png │ ├── .DS_Store │ ├── 05 │ │ ├── deploy-contract-in-console.png │ │ └── simple_contract.png │ └── 06 │ │ └── bitcoin_pubkey.png └── .DS_Store ├── remix-local-env.md ├── smart-contract-battle1.md ├── smart-contract-battle2.md ├── smart-contract-battle3.md ├── smart-contract-best-practices.md ├── solidity-dynamic-array.md ├── solidity-learn-note.md └── 以太坊核心存储结构剖析.docx /.gitignore: -------------------------------------------------------------------------------- 1 | # Node rules: 2 | ## Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 3 | .grunt 4 | 5 | ## Dependency directory 6 | ## Commenting this out is preferred by some people, see 7 | ## https://docs.npmjs.com/misc/faq#should-i-check-my-node_modules-folder-into-git 8 | node_modules 9 | 10 | # Book build output 11 | _book 12 | 13 | # eBook build output 14 | *.epub 15 | *.mobi 16 | *.pdf 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | {one line to give the program's name and a brief idea of what it does.} 635 | Copyright (C) {year} {name of author} 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | {project} Copyright (C) {year} {fullname} 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ethereum-learn 2 | learn about ethereum 3 | 4 | read on https://ethereum.iethpay.com/ 5 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [Introduction](README.md) 4 | * [应用程序](application/README.md) 5 | * [如何实现一个去中心的 Dropbox 存储](application/how-to-implete-a-decentralized-file-storage.md) 6 | * [智能合约实战(一)](smart-contract-battle1.md) 7 | * [智能合约实战(二)](smart-contract-battle2.md) 8 | * [智能合约实战(三)](smart-contract-battle3.md) 9 | * [Golem 多方签名钱包解析](golem-multisig-wallet.md) 10 | * [交易签名以及哈希值的计算](how-to-sign-and-hash-tx.md) 11 | * [智能合约最佳实践](smart-contract-best-practices.md) 12 | * [solidity 学习笔记](solidity-learn-note.md) 13 | * [智能合约中的动态数组](solidity-dynamic-array.md) 14 | * [EVM 学习](evm-learn.md) 15 | * [Ethereum 状态清理](ethereum-state-clearing.md) 16 | * [以太坊核心存储结构分析](ethereum-core-storage.md) 17 | * [remix 本地环境搭建](remix-local-env.md) 18 | * [在 cpp-ethereum 中添加预编译合约](add-precompiled-contract-in-cpp-ethereum.md) 19 | -------------------------------------------------------------------------------- /add-precompiled-contract-in-cpp-ethereum.md: -------------------------------------------------------------------------------- 1 | #### 在 cpp-ethereum 中添加预编译合约 2 | 3 | 作者:李康 4 | 5 | 现有的以太坊中存在一系列的预编译合约,包括 `ecrecover`、`sha256`、`ripemd160`、`identity`、`modexp`、`alt_bn128_G1_add`、`alt_bn128_G1_mul`、`alt_bn128_pairing_product`。预编译合约存在的作用在于将一些常用、使用 solidity 语言实现极其复杂并且效率低下的功能固化在协议中,另外由于 EVM 操作码只有 256 个,是比较稀少的,应该尽量留出来给必要的功能。 6 | 7 | 那么如何在以太坊中添加自己的预编译合约并且在 solidity 中调用该合约呢?下面以 cpp-ethereum 为例,来讲解如何操作。首先来看一下我们操作过程中会使用到的文件: 8 | 9 | - `ethvm/main.cpp` : 该文件是 cpp-ethereum 编译成功后生成的可执行命令 ethvm 的入口文件 10 | 11 | - `libethcore/Precompiled.cpp` : 该文件包含了所有预编译合约的实现,我们增添的预编译合约放在该文件实现 12 | 13 | - `libethashseal/genesis/test/byzantiumTest.cpp` : 该文件是测试以太坊大都会拜占庭分叉的配置文件,我们需要在给配置文件中的 `accounts` 字段下,增加我们的预编译合约声明。**注 :** `ethvm` 可执行命令可自由选择加载某一配置文件,我们需要在加载的配置文件中添加预编译合约的声明,在此,我选择了 `byzantiumTest` 配置文件 14 | 15 | 下面来看一下具体的改变,所有改变已上传至 https://github.com/lightning-li/cpp-ethereum 16 | 17 | ``` 18 | // libethashseal/genesis/test/byzantiumTest.cpp 19 | "accounts": { 20 | "0000000000000000000000000000000000000001": { "wei": "1", "precompiled": { "name": "ecrecover", "linear": { "base": 3000, "word": 0 } } }, 21 | "0000000000000000000000000000000000000002": { "wei": "1", "precompiled": { "name": "sha256", "linear": { "base": 60, "word": 12 } } }, 22 | "0000000000000000000000000000000000000003": { "wei": "1", "precompiled": { "name": "ripemd160", "linear": { "base": 600, "word": 120 } } }, 23 | "0000000000000000000000000000000000000004": { "wei": "1", "precompiled": { "name": "identity", "linear": { "base": 15, "word": 3 } } }, 24 | "0000000000000000000000000000000000000005": { "wei": "1", "precompiled": { "name": "modexp" } }, 25 | "0000000000000000000000000000000000000006": { "wei": "1", "precompiled": { "name": "alt_bn128_G1_add", "linear": { "base": 500, "word": 0 } } }, 26 | "0000000000000000000000000000000000000007": { "wei": "1", "precompiled": { "name": "alt_bn128_G1_mul", "linear": { "base": 2000, "word": 0 } } }, 27 | "0000000000000000000000000000000000000008": { "wei": "1", "precompiled": { "name": "alt_bn128_pairing_product" } }, 28 | "000000000000000000000000000000000000000f": { "wei": "1", "precompiled": { "name": "selfadd", "linear": { "base": 3000, "word": 0 }}} 29 | } 30 | ``` 31 | 32 | 在地址 "000000000000000000000000000000000000000f" 出添加了名为 selfadd 的预编译合约,其中 "linear" 的含义是执行该预编译合约的 gas 花费,其中 base 代表的是基本的花费,word 指的是该预编译合约的入参大小每 32 个字节所需的花费,因此总的花费为 `base + (_in.size() + 31) / 32 * word`。因此如若不指定 "linear" 字段,我们在 Precompiled.cpp 中还需增加额外的 ETH_REGISTER_PRECOMPILED_PRICER,用来供 evm 判断执行预编译合约所需的 gas 花费 33 | 34 | ``` 35 | // libethcore/Precompiled.cpp 36 | 37 | // selfadd precompiled contract 38 | // in : u256 a 39 | // out : (a + a) % 256 40 | 41 | ETH_REGISTER_PRECOMPILED(selfadd)(bytesConstRef _in) { 42 | int a = *(_in.data() + 31); 43 | 44 | std::vector res(1, 0); 45 | res[0] = (a + a) % 256; 46 | cout << "selfadd res " << int(res[0]) << endl; 47 | return {true, res}; 48 | } 49 | ``` 50 | 51 | 该预编译合约实现的功能及其简单,要求传进的参数是 256 位,然后取其最后一个字节的整数值,然后 double 并且对 256 取余,然后返回结果,结果是一个 pair 实例,第一个元素代表该合约执行成功与否,第二个参数是一个 vector 变量,代表输出的结果。 52 | 53 | ``` 54 | // ethvm/main.cpp 55 | 56 | if (mode == Mode::Statistics) 57 | { 58 | cout << "Gas used: " << res.gasUsed << " (+" << t.baseGasRequired(se->evmSchedule(envInfo.number())) << " for transaction, -" << res.gasRefunded << " refunded)\n"; 59 | cout << "Output: " << toHex(output) << "\n"; 60 | LogEntries logs = executive.logs(); 61 | cout << logs.size() << " logs" << (logs.empty() ? "." : ":") << "\n"; 62 | for (LogEntry const& l: logs) 63 | { 64 | cout << " " << l.address.hex() << ": " << toHex(t.data()) << "\n"; 65 | cout << "log data field "; 66 | string ss = ""; 67 | for (auto i = l.data.begin(); i != l.data.end(); ++i) { 68 | if (int(*i) / 16 < 10) { 69 | ss.push_back(int(*i) / 16 + 48); 70 | } else { 71 | ss.push_back(int(*i) / 16 - 10 + 97); 72 | } 73 | if (int(*i) % 16 < 10) { 74 | ss.push_back(int(*i) % 16 + 48); 75 | } else { 76 | ss.push_back(int(*i) % 16 - 10 + 97); 77 | } 78 | } 79 | cout << ss << endl; 80 | for (h256 const& t: l.topics) 81 | cout << " " << t.hex() << "\n"; 82 | } 83 | 84 | cout << total << " operations in " << execTime << " seconds.\n"; 85 | cout << "Maximum memory usage: " << memTotal * 32 << " bytes\n"; 86 | cout << "Expensive operations:\n"; 87 | for (auto const& c: {Instruction::SSTORE, Instruction::SLOAD, Instruction::CALL, Instruction::CREATE, Instruction::CALLCODE, Instruction::DELEGATECALL, Instruction::MSTORE8, Instruction::MSTORE, Instruction::MLOAD, Instruction::SHA3}) 88 | if (!!counts[(byte)c].first) 89 | cout << " " << instructionInfo(c).name << " x " << counts[(byte)c].first << " (" << counts[(byte)c].second << " gas)\n"; 90 | } 91 | ``` 92 | 93 | 在 ethvm/main.cpp 中添加输出日志的代码,便于我们查证合约执行结果 94 | 95 | 接下来我们需要在 solidity 中演示如何调用我们刚刚已经写好的预编译合约。 96 | 97 | ``` 98 | pragma solidity ^0.4.11; 99 | 100 | contract R { 101 | bool public flag; 102 | event Res(bytes32 haha); 103 | 104 | function selfadd(uint8 v) { 105 | // We do our own memory management here. Solidity uses memory offset 106 | // 0x40 to store the current end of memory. We write past it (as 107 | // writes are memory extensions), but don't update the offset so 108 | // Solidity will reuse it. The memory used here is only needed for 109 | // this context. 110 | 111 | // FIXME: inline assembly can't access return values 112 | bool ret; 113 | 114 | bytes32 haha; 115 | assembly { 116 | let size := mload(0x40) 117 | mstore(size, v) 118 | // NOTE: we can reuse the request memory because we deal with 119 | // the return code 120 | ret := call(3000, 15, 0, size, 32, add(size, 32), 32) 121 | haha := mload(add(size, 32)) 122 | } 123 | Res(haha); 124 | flag = ret; 125 | } 126 | 127 | } 128 | ``` 129 | 130 | 关于 assembly 的语法请查看 http://solidity.readthedocs.io/en/develop/assembly.html 。 131 | 132 | 在 EVM 中存在三种类型的存储,memory、stack、storage,memory 用于存储一些临时变量,stack 用于 EVM 的执行环境,storage 用于存储永久的临时变量,solidity 在 EVM 底层的 memory 的 0x40 位置处存放了当前消耗的 memory 大小,每当所需的内存扩大时,solidity 会更新 memory 0x40 处的值。`mload(0x40)` 作用是将 memory 0x40 处的值压到 stack 上,比如 solidity 编译器会将它翻译成 `PUSH1 40 MLOAD`。`mstore(size, v)` 作用是将 v 存储在 memory[size...size+31] 处。`call(3000, 15, 0, size, 32, add(size, 32), 32)` 意思是提供 3000 gas 调用地址位于 15 处的智能合约,0 代表传给该智能合约的 ether value。第一个 size 代表 input 起始位置,紧随其后的 32 代表将 input (包括本身) 往后的 32 个字节作为输入传递给智能合约。add(size, 32) 代表智能合约返回结果的 output 起始地址,紧随其后的 32 代表返回结果的大小,即将返回结果赋予 memory[add(size, 32)...add(size, 63)]。 133 | 134 | 将该代码编译成字节码,即 135 | 136 | ` 60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806317b020ab14610048578063890eba681461006e57600080fd5b341561005357600080fd5b61006c600480803560ff1690602001909190505061009b565b005b341561007957600080fd5b61008161011b565b604051808215151515815260200191505060405180910390f35b60008060405183815260208082016020836000600f610bb8f1925060208101519150507f9d720c3d3607e5e125654a222aa9aa0dcadc78fe38dd87183bcd002043086bd08160405180826000191660001916815260200191505060405180910390a1816000806101000a81548160ff021916908315150217905550505050565b6000809054906101000a900460ff16815600a165627a7a7230582013d98234d627d3cc0ccf04c3301571e481f0423ec3cc1befc12855d1edda77190029 137 | ` 138 | 将该字节码保存在 build/ethvm/contract1.hex 文件中 139 | 140 | 通过编译 cpp-ethereum 生成的 ethvm 命令来调用该合约代码,如下: 141 | 142 | ``` 143 | ➜ /Users/likang/cpp-ethereum/build git:(develop) ✗ >> ethvm/ethvm ethvm/contract1.hex --input "0x17b020ab0000000000000000000000000000000000000000000000000000000000000082" --network Byzantium 144 | 145 | // output result 146 | 147 | NoProof 148 | 0000000000000000000000000000000000000045 has 0 wei 149 | 32 0 0 150 | Does tx have zero signature : 0 151 | selfadd res 4 152 | -------SLOAD------- 153 | 0 154 | 0 155 | -------FINISH------ 156 | Gas used: 25277 (+21464 for transaction, -0 refunded) 157 | Output: 158 | 1 logs: 159 | 1122334455667788991011121314151617181920: 17b020ab0000000000000000000000000000000000000000000000000000000000000082 160 | log data field 0400000000000000000000000000000000000000000000000000000000000000 161 | 9d720c3d3607e5e125654a222aa9aa0dcadc78fe38dd87183bcd002043086bd0 162 | 118 operations in 0.000189 seconds. 163 | Maximum memory usage: 0 bytes 164 | Expensive operations: 165 | SSTORE x 1 (20000 gas) 166 | SLOAD x 1 (200 gas) 167 | CALL x 1 (0 gas) 168 | MSTORE x 3 (9 gas) 169 | MLOAD x 4 (12 gas) 170 | ``` 171 | 172 | 可以发现打出的 log 日志中,data field 为 0400000000000000000000000000000000000000000000000000000000000000,可见系统将其转换成了小端模式,印证了预编译合约的功能 : (0x82 + 0x82) % 256 = 4 173 | 174 | 未来工作展望: 175 | 176 | 将预编译合约以关键字的形式实现,例如在 solidity 中直接使用 `selfadd` 关键字,应该会需要修改 solidity 编译器。 177 | -------------------------------------------------------------------------------- /application/README.md: -------------------------------------------------------------------------------- 1 | #### application based on ethereum 2 | -------------------------------------------------------------------------------- /application/how-to-implete-a-decentralized-file-storage.md: -------------------------------------------------------------------------------- 1 | ### 如何实现一个去中心化的 Dropbox 存储 2 | 3 | 原标题:Secret Sharing and Erasure Coding: A Guide for the Aspiring Dropbox Decentralizer 4 | 5 | 作者:Vitalik Buterin       译者:李康 6 | 7 | 本文翻译自 https://blog.ethereum.org/2014/08/16/secret-sharing-erasure-coding-guide-aspiring-dropbox-decentralizer/ 8 | 9 | 在去中心化计算的应用中,有一个激动人心的应用,在过去的一年里引起了相当大的兴趣,那就是受激励的去中心化在线文件存储系统的概念。目前,如果你想你的文件或者数据安全地在云端备份,你有3种选择:(1). 上传它们到自己的服务器,(2). 使用一个中心化的应用,如 Google drive 或者 Dropbox,或者是 (3). 使用已经存在的去中心化的应用,如 Freenet。这些方法都有它们自己的缺点:第一种方法有着昂贵的建立和维护费用;第二种方法依赖于一个单可信任实体,并且常常涉及重大价格上涨;第三种方法速度慢,对每一位用户在空间容量方面有着很高的限制,因为它依赖于用户自愿奉献存储空间。受激励的文件存储协议有潜力成为第四种方法,通过无中心化地激励执行者 (存储用户数据的客户) 参与其中,提供高容量存储与高质量服务。 10 | 11 | 大量的平台,包括 [Storj](http://storj.io/)、[Maidsafe](http://maidsafe.net/),某种程度上,[Permacoin](http://cs.umd.edu/~amiller/permacoin.pdf) 与 [Filecoin](http://filecoin.io/),正在尝试处理这个问题,该问题在某种意义上看起来是简单的,所有的工具要么已经存在了,要么正在构建的过程中,我们所需要的就是实现而已。然而,该问题其中的一小部分尤其重要:我们如何合适地引进冗余性?冗余对于安全来说至关重要,尤其是在一个去中心化的网络中,业余爱好者与临时性用户占大部分,我们绝对不能依赖于单节点保持在线。我们可以简单地复制用户数据,让一些节点存储单独的拷贝,问题是:我们能做的更好吗?事实证明,我们当然可以。 12 | 13 | #### Merkle Trees 与 Challenge-Response 协议 14 | 15 | 在我们进入最为重要的冗余性部分之前,我们首先讲解一些更容易的部分:我们如何创建一个至少激励一个实体保持文件的最为基本的系统?没有激励的系统中,问题将会变得更加容易,你上传文件,等待其它的用户来下载它,当你再次需要该文件的时候,通过文件的哈希来发出一个查询请求。如果我们想引进激励机制,问题某种程度上变得更加困难,但是在大事的计划中,依旧不是那么难。 16 | 17 | 在文件存储的上下文中,有两种实体你可以激励。第一种是你发出一个下载文件的请求时,实际向你发送文件的实体。这很容易可以做;最好的策略是一种类似于简单的 tit-for-tat 游戏,发送者发送 32 kb,你发送 0.0001 个币,然后发送者发送另一 32 kb,以此类推。注意在没有冗余以及大文件的情形下,这个策略是极易受到敲诈勒索攻击的。一个文件的 99.99% 对于你来说是毫无用处的,所以存储者有机会敲诈你,让你为文件的最后一部分付出高昂的费用。对该问题最聪明的解决办法是,让文件是自冗余的,使用一种特殊的编码来扩展该文件,如文件 11.11% 自冗余,那么该文件的任意 90% 都可以来恢复原文件的内容,并且对存储者隐藏该文件的自冗余百分比。然而,随后针对不同的目的,我们将讨论一个非常类似的算法。所以现在,接受该问题已被解决。 18 | 19 | 第二种我们可以激励的行为实体是长期存储该文件的实体。该问题有些困难,如何在不真正传输整个文件的情况下,证明你存储着该文件?幸运地是,有一个不是很难实现的解决方案,使用在加密经济中,被用来构建信誉的结构:Merkle trees。 20 | 21 | ![haha](/images/2016/10/haha.jpg) 22 | 23 | 准确来说,某些情况下,Patricia Merkle 是更好的,尽管古老原始的 Merkle 树也能胜任。 24 | 25 | 基本的方法就是这个。首先,将文件分散为小块。或许每一块在 32 bytes 与 1024 bytes 之间,添加全 0 的块,直到块数量达到 `n = 2 ^ k` (添补的步骤是可以避免的,但是它使得算法编码和解释更加简单)。接下来,我们构建树。重命名 `n` 个块为 `chunk[n]` 到 `chunk[2n-1]`,并且使用以下规则重新构建块 `1` 到 `n-1`:`chunk[i] = sha3([chunk[2*i], chunk[2*i+1]])`。这使得你计算出块 `n/2` 到 `n-1`,然后 `n/4` 到 `n/2 - 1`,然后重复上述步骤直至得到一个 *root*, `chunk[1]`。 26 | 27 | ![Merkle](/images/2016/10/merkle.png) 28 | 29 | 现在,注意如果你仅仅存储根节点,忘记 `chunk[2]...chunk[2n-1]`,存储其它块的实体通过几百字节就能向你证明,他们拥有某个特定块。这个算法相对来说比较简单。首先,我们定义一个函数 `partner(n)`,如果 `n` 是奇数,输出 `n-1`,如果 `n` 是偶数,输出 `n+1` - 简单来说,就是给定一个块,找出和该块链接在一起产生父区块的块。然后,如果你想证明 `chunk[k]` (`n <= k <= 2n - 1`) 的所有权,提交 `chunk[partner(k)]`,`chunk[partner(k/2)]` (除法在这里意味着向下取整,例如 `11 / 2 = 5`),`chunk[partner(k/4)]` 等等直到 `chunk[1]`,与真正的 `chunk[k]` 一起。本质上,我们提供的是从块 `k` 到根节点的整个分支。验证者使用 `chunk[k]` 与 `chunk[partner(k)]` 构建 `chunk[k/2]`,使用刚生成的 `chunk[k/2]` 与 `chunk[partner(k/2)]` 构建 `chunk[k/4]`,直到验证者得到 `chunk[1]`,该树的根节点,如果根节点与验证者自存的根节点匹配,那么该证据就是有效的,否则无效。 30 | 31 | ![Merkle_verify](/images/2016/10/merkle_verify.png) 32 | 33 | 上图是块 10 的证据,包括 (1). chunk 10,(2). chunk 11 (`11=partner(10)`),4 (`4 = partner(10/2)`),3 (`3 = partner(10/4)`)。验证过程从块 10 开始,使用它们的 partner 来产生父区块,直到产生根节点 chunk 1,看是否匹配验证者节点自存储的根节点。 34 | 35 | 注意证据隐式地包含了索引--在哈希之前有时你需要将 partner 加到左边,有的时候是右边,如果用来验证证据的索引是不同的,那么该证据将不再匹配。因此,如果我要求一个 `chunk 422` 的证据,你提供了一个即使有效的 `chunk 587` 的证据,我也会注意到出了错误。同样地,如果没有 Merkle Tree 的整个相关的部分,那么你没有任何办法提供一个有效证据;如果你试图传输伪造的数据,在某一点,哈希将不会匹配,导致最终的根节点会是不同的。 36 | 37 | 现在,让我们回顾一下这个协议。按照上述描述,我在文件之外构建了一棵 Merkle 树,然后上传给某个实体。然后,每 12 个小时,我找一个位于 `[0, 2^k-1]` 中的随机数,提交该随机数要求存储者提供证据。如果存储者回复了相应 Merkle tree 证据,然后我验证该证据,如果该证据是正确的,则发送 0.001 BTC (或者 ETH,或 storjcoin,或者任意使用的代币)。如果我没有接收到相应证据或者证据是无效的,那么我不会发送 BTC。如果存储者存储了整个文件,他们将会在所有的验证时间点成功,如果他们存储了文件的 50%,他们将会有 50% 的验证时间点是成功的,等等。如果我们想让它要么全部成功要么失败,那么我们可以简单地要求存储者提供 10 个连续的证据,这样存储者才可以得到奖励。存储者仍然可以就传输文件的 99% 进行敲诈勒索,但是我们可以利用我上面提到的冗余编码策略,在任何情况下文件的 90% 就足以恢复出原文件,这种策略将在下面描述。 38 | 39 | 这时你可能会关心的一点是隐私 - 如果你使用一个加密协议来让任意节点存储你的文件以获得奖励,难道那不意味着你的文件散落在互联网的各个角落,任何节点可以潜在地访问这些文件?幸运的是,该问题的答案是简单的:在发送文件之前,加密整个文件。从这一点上,我们假设所有的数据都是加密的,忽略掉隐私问题,因为目前的加密方法几乎完全解决了该问题 (几乎是因为,文件的大小,你访问文件的时间,依旧是公开的)。 40 | 41 | #### 深入到去中心化 (Looking to Dencentralize) 42 | 43 | 现在我们有一个使人们存储你的数据来获得奖励的协议;该算法甚至可以通过将其放入 Ethereum 合约中来实现无需信任 (trust-free),使用 `block.prevhash` 作为生成挑战 (注:挑战指向存储者索要某一特定块的 Merkle Tree 证据) 的随机数源。现在,让我们进行下一步:指出如何去中心化存储和增加冗余。去中心化的最简单方式是简单的复制:取而代之一个节点存储一份文件的拷贝,我们可以让五个节点分别都存储一份文件的拷贝。然而,如果我们简单地遵循上述提到的天真协议,我们会有一个问题:一个节点可以假装成五个节点,得到五倍的回报。一个快速解决该问题的办法是五次加密原文件 44 | ,那么一个存储者将不能注意到五份文件其实是相同的,从而存储它们一次但是索要 5 倍奖励。 45 | 46 | 但是即使这样,我们依然有两个问题。第一个,没有任何办法验证文件的五份拷贝被五个分开的用户存储,如果你想让你的文件被去中心化的云来备份,你就会为去中心化的服务付款;如果所有的五个用户实际上是通过 Google 或者 Amazon 存储任何数据,这将会使得该协议没有多大实用性。这是一个非常困难的问题,尽管五次加密文件,假装你正在存储五个不同的文件,可以阻止参与者使用一份存储来获取五倍奖励,却不能阻止一个参与者使用五份存储来获取五倍奖励,并且从存储者的角度来看,经济规模甚至意味着这种情形是可取的。第二个,存在一种问题,你花费了巨大开销,尤其是,你从并没有获取到太多冗余性的存储者那里花费了不应该的冗余性支出 - 例如,如果一个单节点有 50% 的可能会离线 (非常合理的一个预估,如果我们正在讨论的是一个在个人硬盘剩余空间存储的文件网络),那么你有 6.25% 的可能会完全不能访问该文件 (译者注:这里指的可能是四个存储者的情况)。 47 | 48 | 针对第一个问题,有一种解决方案,尽管它是有缺陷的并且不清楚带来的好处是否值得这样去做。思想就是使用**权益证明 (proof of stake)**协议与**[保管证明 (proof of custody)](https://docs.google.com/a/ethereum.org/document/d/1F81ulKEZFPIGNEVRsx0H1gl2YRtf0mUMsX011BzSjnY/edit) - 一种文件与私钥同时拥有的证明 **的结合。如果你想存储你的文件,想法是随机选择某个数量的某种货币的权益持有者,通过他们拥有的货币数量来赋予他们被选择的可能性权重。用 Ethereum 合约的方式来实现权益证明可能包括,让参与者存放 ether (以太币) 到合约中 (记住,这里存放 ether 是无需信任的,如果合约提供了一种赎回 ether 的方式),然后给每一个账户与他们存款成比例的可能性,接下来这些权益持有者将会有机会存储文件。然后,取而代之上面提到的简单 Merkle 树检测,保管证明协议会被使用。 49 | 50 | 保管证明协议有 non-outsourceable 的好处,也就是,如果不给服务器访问你的私钥,没有任何方法可以将文件放入服务器中。这意味着,至少在理论上,用户将不会倾向于在中心化的云计算系统中存储大量的文件。当然,协议完成这个是以验证需要更高花费为代价的。所以,留下一个开放的问题:我们是想要保管证明以及其带来的验证开销,还是以防万一而引进额外的冗余 51 | 所带来的存储开销? 52 | 53 | #### M of N 54 | 55 | 不管保管证明是否是一个好主意,下一步是看看我们在冗余性方面能否比原生且幼稚的复制范例 (简单地将文件复制分发到各个节点) 做的更好。首先,我们分析一下原生的复制范例有多么好。假定每个节点有 50% 的可用时间,并且你愿意花费 4 倍开销。在这些情况下,失败的可能性有 `0.5 ^ 4 = 0.0625` - 跟中心化服务提供的 4 个 9 (例如,99.99% 可用时间) 相比还是挺高的,一些中心化服务甚至提供 5 个 或 6 个 9 以上的可用时间,但是由于[黑天鹅理论 (Talebian black swan considerations)](https://en.wikipedia.org/wiki/Black_swan_theory),任何超过 3 个 9 的承诺都可以被认为是废话;因为去中心化的网络不依赖于任何具体公司的存在或者行为或者是具体的软件包,去中心化系统实际可论证地可以合理提供 4 个 9 以上的承诺。如果我们假设参与去中心化网络的用户是类似专业的挖矿者,那么我们可以降低节点不可用时间的比例,如到 10%。在这种情况下,我们实际可以得到 `1 - 0.1 ^ 4 = 0.9999` 可用时间,但是理应假设更悲观的情况。 56 | 57 | 现在我们需要的是某种 M-of-N 协议,就像[比特币中的多方签名 (multisig for Bitcoin) ](http://bitcoinmagazine.com/11108/multisig-future-bitcoin/)。那么首先我们描述一下我们理想中的协议应该是怎样的,然后随后再考虑它是否是可行的。假定我们有一个大小为 1G 的文件,我们想"多方签名"它到 20-of-60 中。我们将文件分割为 60 块,每块大小为 50 MB (总共 3 GB),如此这 60 块中的任意 20 块足够重建原始文件。这是信息理论中最优情况;你不能从少于 1 G 字节的信息中恢复出 1 G 字节的信息,但是从 1 G 字节的信息中恢复出 1 G 字节的信息是完全可能的。如果我们有一种类似的协议,我们可以使用该协议将文件分割为 60 块,单独得加密这 60 块,使得它们看起来像独立的文件,然后单独地对这 60 块分别应用受激励的文件存储协议。 58 | 59 | 现在,有趣的部分来了:这样的一个协议确实存在。在文章的下一部分,我们将要描一点数学知识,被称作为**"secret sharing"** 或者**"纠删码(erasure coding)"**。这些名字使用的算法基本是相同的,除了一些实现细节。为了开始,我们回顾一个简单的定理:两点决定一条直线。 60 | 61 | ![twopoints](/images/2016/10/twopoints.png) 62 | 63 | 尤其是,注意到,恰好有一条直线穿过其中任意两点,但是有无穷条直线穿过其中一点。由这个简单的定理,我们可以制定一个受限制的 2-of-n 版本:把文件的一半看作是坐标系中的一点 `(x=1, y=文件一半的内容)`,把文件的另一半看作是坐标系中另一点 `(x=2, y=文件另一半的内容)`,画出穿过两点的直线,并且取 x 轴为 3 与 4 的点,等等。这样,任意两块能被用来重建这条直线,从这条直线中获取 `x=1, x=2` 对应的 y 轴,从而恢复原文件。 64 | 65 | 数学上,有两种方法完成这个。第一种是相对简单的方法,包括一个线性方程的系统即可。假定我们想分割的文件内容是数字 "1321"。左半部分是 13,右半部分是 21,所以直线穿过 `(1, 13), (2, 21)` 两点。如果我们想知道斜率以及这条直线上 y 轴特点,我们可以解决该线性方程系统: 66 | 67 | ``` 68 | 1 * a + b = 13 69 | 2 * a + b = 21 70 | ``` 71 | 72 | 让第二个方程减去第一个方程,得到: 73 | 74 | ``` 75 | a = 8 76 | ``` 77 | 78 | 将其带入第一个方程,得到: 79 | 80 | ``` 81 | b = 5 82 | ``` 83 | 84 | 所以我们得到了等式,`y = 8 * x + 5`。我们可以生成新点:`(3, 29)`,`(4, 27)`,等等。从这些点中任意两点,我们都可以恢复原始的等式。 85 | 86 | 现在,让我们更进一步,生成 m-of-n 版本。事实证明,这是更复杂的,但是也不是那么难,我们知道两点决定一条直线。我们同时也知道三点决定一条抛物线。 87 | 88 | ![threepoints](/images/2016/10/threepoints.png) 89 | 90 | 因此,对于 3-of-n,我们就可以将文件分割为 3 块,由 `x = 1, 2, 3` 三点组成抛物线,然后在该抛物线上取第四个点或更多的点作为附加块。如果我们想要 4-of-n,就要使用三次多项式。我们具体看一下后一种情况,我们仍然假设原始文件为 “1321”,但是我们使用 4-of-7 来将该文件分割。我们的四个点为 `(1, 1)`,`(2, 3)`,`(3, 2)`,`(4, 1)`。所以我们有: 91 | 92 | ``` 93 | a * 1 ^ 3 + b * 1 ^ 2 + c * 1 + d = 1 94 | a * 2 ^ 3 + b * 2 ^ 2 + c * 2 + d = 3 95 | a * 3 ^ 3 + b * 3 ^ 2 + c * 3 + d = 2 96 | a * 4 ^ 3 + b * 4 ^ 2 + c * 4 + d = 1 97 | ``` 98 | 99 | 哦!让我们开始进行减法,我们将使用等式 2 减去等式 1,等式 3 减去等式 2,等式 4 减去等式 3,这样就可以将 4 个等式转换为 3 个等式,然后一次一次重复上述步骤。 100 | 101 | ``` 102 | a * 7 + b * 3 + c = 2 103 | a * 19 + b * 5 + c = -1 104 | a * 37 + b * 7 + c = -1 105 | ``` 106 | 107 | ``` 108 | a * 12 + b * 2 = -3 109 | a * 18 + b * 2 = 0 110 | ``` 111 | 112 | ``` 113 | a * 6 = 3 114 | ``` 115 | 116 | 所以 `a = 1/2`,现在我们逐步解开此方程,得到: 117 | 118 | ``` 119 | 1/2 * 12 + b * 2 = -3 120 | ``` 121 | 122 | 所以 `b = -9/2`,然后: 123 | 124 | ``` 125 | 1/2 * 7 - 9/2 * 3 + c = 2 126 | ``` 127 | 128 | 所以 `c = 12`,然后: 129 | 130 | ``` 131 | 1/2 - 9/2 + 12 + d = 1 132 | ``` 133 | 134 | 所以 `a = 0.5`,`b = -4.5`,`c = 12`,`d = -7`。这是形象化的可爱多项式: 135 | 136 | ![fourpoints](/images/2016/10/fourpoints.png) 137 | 138 | 我创建了一个 Python 版实用工具来帮助你完成这些操作 (这个工具能做一些更高级的操作,但是我们随后深入它);你可以在[这里](https://github.com/ethereum/economic-modeling/tree/master/erasure_code)下载它**(这个版本在 python 2.7.13下运行会报错,我将修改过后的放在了[这里](https://github.com/lightning-li/blockchain-knife/blob/master/ethereum-learn-share.py))**,如果你想快速解决上面提到的等式方程组,你可以: 139 | 140 | ``` 141 | > import share 142 | > share.sys_solve([[1.0, 1.0, 1.0, 1.0, -1.0], [8.0, 4.0, 2.0, 1.0, -3.0], [27.0, 9.0, 3.0, 1.0, -2.0], [64.0, 16.0, 4.0, 1.0, -1.0]]) 143 | [0.5, -4.5, 12.0, -7.0] 144 | ``` 145 | 146 | 注意把值设置为浮点数是必须的。如果你使用整数,Python 的整数除法会将所有的事情搞乱。 147 | 148 | 现在,我们介绍一种更容易的方式来完成它,**拉格朗日插值法 (Lagrange interpolation)**。这里思想是非常聪明的:我们提出一个三次多项式,它在 `x = 1` 处的值为 `1`,在 `x = 2, 3, 4` 处的值为 `0`,然后针对每一个其它的 x 坐标做相同的操作 (`x = 2` 处为 `1`,`x = 1, 3, 4` 处为 `0`)。然后,我们做乘法,并将多项式方程加在一起;例如,为了匹配 `(1, 3, 2, 1)`,我们简单地用 1 乘以多项式 (它的 y 轴为 (1, 0, 0, 0)),用 3 乘以多项式 (它的 y 轴为 (0, 1, 0, 0)),用 2 乘以多项式 (它的 y 轴为 (0, 0, 1, 0)),用 1 乘以多项式 (它的 y 轴为 (0, 0, 0, 1)),然后将这些多项式方程加在一起来得到目标多项式 (它的 y 轴为 (1, 3, 2, 1))。这个小技巧是有效的,因为四个点唯一决定一个三次多项式方程。这看起来不是那么简单,因为我们目前这种用点带入多项式方程的方法太笨拙了,但是幸运的是,对于它,我们有一个显示的构建方法: 149 | 150 | ![CodeCogsEqn-19](/images/2016/10/CodeCogsEqn-19.gif) 151 | 152 | 在 `x = 1` 处,注意分子与分母是一样的,因此 `y = 1`。在 `x = 2, 3, 4` 处,分子中有一个为 0,因此 `y = 0`。这些多项式相乘并相加耗费二次方的时间 (例如,4 个等式需要 16 步),然而,我们原先的步骤耗费三次方的时间 (4 个等式需要 64 步),所以它是一个显著的提升,尤其是我们开始讨论数量大的分割时候,像 20-of-60。python 工具也同样支持该算法: 153 | 154 | ``` 155 | > import share 156 | > share.lagrange_interp([1.0, 3.0, 2.0, 1.0], [1.0, 2.0, 3.0, 4.0]) 157 | [-7.0, 12.000000000000002, -4.5, 0.4999999999999999] 158 | ``` 159 | 160 | 第一个参数是 y 坐标,第二个参数是 x 坐标。注意这里的相反顺序,python 模块里的代码将多项式中最低次数对应的系数放在首位,即由上面结果可得 `0.5 * x ^ 3 - 4.5 * x ^ 2 + 12 * x - 7 = y`。最后,让我们得到我们需要的附加值: 161 | 162 | ``` 163 | > share.eval_poly_at([-7.0, 12.0, -4.5, 0.5], 5) 164 | 3.0。 165 | > share.eval_poly_at([-7.0, 12.0, -4.5, 0.5], 6) 166 | 11.0 167 | > share.eval_poly_at([-7.0, 12.0, -4.5, 0.5], 7) 168 | 28.0 169 | ``` 170 | 171 | 那么,这里我们立即能发现两个问题。首先,看起来计算机化的浮点数不是无限精确的;12 转变为 12.000000000000002。第二,当我们将 x 坐标变大时,块开始变得很大;在 `x = 10` 处,它变大为 163。这在某种程度上打破了你需要用相同大小的文件来恢复原始文件的承诺;如果我们丢失了 `x = 1, 2, 3, 4`,那么你需要 8 个数字来使原始文件恢复而不是 4 个数字。这些都是严重的问题,随后我们将使用一些数学上的高级知识来解决这些问题,但是目前我们先把这些问题放在一边。 172 | 173 | 即使有这些问题遗留,我们基本上已经取得了胜利,所以我们来计算一下我们的收获。如果我们使用 20-of-60 的分割,每一个节点有 50% 的时间在线,那么我们能使用组合数学 - 具体来说,[二项式分布定理](https://en.wikipedia.org/wiki/Binomial_distribution) - 来计算我们数据是完好的概率,首先,做好准备工作: 174 | 175 | ``` 176 | > def fac(n): return 1 if n==0 else n * fac(n-1) 177 | > def choose(n,k): return fac(n) / fac(k) / fac(n-k) 178 | > def prob(n,k,p): return choose(n,k) * p ** k * (1-p) ** (n-k) 179 | ``` 180 | 181 | 最后一个函数计算 n 台服务器中恰好 k 台在线时数据完好的可能性大小,假设每台服务器在线的可能性为 p。现在,我们做: 182 | 183 | ``` 184 | > sum([prob(60, k, 0.5) for k in range(0, 20)]) 185 | 0.0031088013296633353 186 | ``` 187 | 188 | 仅仅 3 倍冗余我们就获得了 99.7% 的可用时间 - 比起简单的 3 倍复制带给我们的 87.5% 可用时间有了很大一步提升。如果我们将冗余度提升为 4 倍,那么我们就可以得到 6 个 9 的可用时间。这里我们就可以停下了,因为整个 Ethereum 或者整个 internet 完全宕掉的可能性比 0.0001% 高 (实际上,[明天你很可能死去的概率](https://en.wikipedia.org/wiki/Micromort))。哦!如果我们假设每台机器都有 90% 的可用时间,那么使用 1.5 倍冗余 20-0f-30协议,我们绝对会得到超过 12 个 9 的可用时间。信誉系统可被用来追踪每一个节点在线的频率。 189 | 190 | #### 处理错误 (Dealing with Errors) 191 | 192 | 我们将用该篇文章剩余部分讨论关于该模式的三种扩展。第一种是阅读上述描述过程中你可能忽略掉的问题,但是它是很重要的:如果一些节点尝试欺骗,会发生什么?上述算法能从任意 20 块恢复出原文件数据 (使用 20-of-60 协议)。但是如果其中的一个数据提供者是恶意的并且尝试提供伪造数据来使算法混乱该怎么办?攻击向量是相当吸引人的: 193 | 194 | ``` 195 | > share.lagrange_interp([1.0, 3.0, 2.0, 5.0], [1.0, 2.0, 3.0, 4.0]) 196 | [-11.0, 19.333333333333336, -8.5, 1.1666666666666665] 197 | ``` 198 | 199 | 使用上述多项式的四个点,但是改变最后一个值为 5,会得到一个完全不同的结果。有两种方法可以处理这个问题。一种是很明显的方法,另一种是数学上聪明的方法。明显的方法是显而易见的:当分割一个文件的时候,保存每一个块的哈希,当接收到块的时候,将它的哈希与本地存储的哈希进行比较,如果不一致则丢弃掉。 200 | 201 | 聪明的方法在某种程度上是更聪明的;它包含一种令人生畏的数学方法,被称作[伯利坎普-韦尔奇算法](http://en.wikipedia.org/wiki/Berlekamp%E2%80%93Welch_algorithm#Example)。思想是取而代之仅仅找到一个多项式,`P`,我们想象存在两个多项式,`Q` 与 `E`,使得 `Q(x) = P(x) * E(x)`,尝试同时解决 `Q` 与 `E`,然后我们计算 `P = Q / E`。思想是如果这个等式保持正确,那么对于所有的 `x`,要么 `P(x) = Q(x) / E(x)`,要么 `E(x) = 0`;因此,除了计算原始的多项式,我们神奇地隔离了错误是什么。这里我不会举一个例子,维基百科上已经有一个极好的示例,你能自己尝试: 202 | 203 | ``` 204 | > map(lambda x: share.eval_poly_at([-7.0, 12.0, -4.5, 0.5], x), [1, 2, 3, 4, 5, 6]) 205 | [1.0, 3.0, 2.0, 1.0, 3.0, 11.0] 206 | > share.berlekamp_welch_attempt([1.0, 3.0, 18018.0, 1.0, 3.0, 11.0], [1, 2, 3, 4, 5, 6], 3) 207 | [-7.0, 12.0, -4.5, 0.5] 208 | // 第三个参数 3 表示最高次幂为 3 209 | > share.berlekamp_welch_attempt([1.0, 3.0, 2.0, 1.0, 3.0, 0.0], [1, 2, 3, 4, 5, 6], 3) 210 | [-7.0, 12.0, -4.5, 0.5] 211 | ``` 212 | 213 | 译者注:已知信道会错误传输 `k` 这个值,那么 `P(x)` 可以通过传输其上的 `n + 2k` 个点来纠正其中 `k`个错误,因此上述函数第一个参数中 6 个点中可以允许 1 个错误的点,7 个点可以允许 2 个错误的点,并可以纠正它(其中 n 为最高次幂 + 1。k 的结果向上取整,比如,最高次幂是 2,那么 6 个点可以允许 2 个错误的点,见下面实验)。 214 | 215 | 正如我提到的,这个数学小技巧对于文件存储来说,并不是真正需要的;上面提到的更简单的方法,即存储哈希值,并抛弃与之不匹配的块,可以工作的很好。但是却附带地对另一个应用非常有用:自治愈的比特币地址。比特币有一个 [base58check](https://en.bitcoin.it/wiki/Base58Check_encoding) 编码算法,可以被用来检测一个比特币地址被错误地拼写的情形,并且返回一个错误,这样你就不会偶然地发送数千美元到万劫不复的深渊。然后,使用我们了解到的,我们实际上能够做的更好,设计一种算法,不仅能检测错误的拼写,而且可以在我们不知道的情形下纠正这些错误。我们没有在以太坊中使用任何聪明的地址编码算法,因为我们更喜爱鼓励可选的基于注册名称的使用,但是如果一种地址编码算法模式确实需要,上述提到的也许会被使用。 216 | 217 | #### 有限域 218 | 219 | 现在,我们回到第二个问题:一旦我们的 x 坐标变大一点,相应的 y 坐标就开始迅速得变为无穷之大。为了解决这个问题,我们将要做的就是完全重新定义一套算术规则,正如我们知道的那样。具体点来说,我们重新定义我们的算术操作如下: 220 | 221 | ``` 222 | a + b := (a + b) % 11 223 | a - b := (a - b) % 11 224 | a * b := (a * b) % 11 225 | a / b := (a * b ** 9) % 11 226 | ``` 227 | 228 | 这里百分号的意思是取余,所以我们有 `7 + 5 = 1`,`6 * 6 = 3` (并且它的推论,`3 / 6 = 6`),等等。我们现在只被允许处理数字 0、1、2、3、4、5、6、7、8、9、10。令人惊讶的事情是,即使我们这样做了,所有传统算术规则对于我们新的算术规则仍然是有效的;`(a * b) * c = a * (b * c)`,`(a + b) * c = (a * c) + (b * c)`,如果 `b != 0`,`a / b * b = a`,`(a^2 - b^2) = (a + b) * (a - b)`,等等。因此,我们可以使用上面提到的抛物线方程,将其转变到我们的新系统中。即使抛物线曲线的直观感受被完全破坏了 - 我们现在处理的是抽象的数学对象,不是任何类似的平面上实际的点 - 因为我们的新代数是自统一的,公式仍然是有效的,那就是我们想要的。 229 | 230 | ``` 231 | > e = share.mkModuloClass(11) 232 | > P = share.lagrange_interp(map(e, [1, 3, 2, 1]), map(e, [1, 2, 3, 4])) 233 | > P 234 | [4, 1, 1, 6] 235 | > map(lambda x: share.eval_poly_at(map(e, P), e(x)), range(1, 9)) 236 | [1, 3, 2, 1, 3, 0, 6, 2] 237 | > share.berlekamp_welch_attempt(map(e, [1, 9, 9, 1, 3, 0, 6, 2]), map(e, [1, 2, 3, 4, 5, 6, 7, 8]), 3) 238 | [4, 1, 1, 6] 239 | ``` 240 | `map(e, [v1, v2, v3])` 用来转变普通的整数到新域里的元素;该软件库包含了一种对 11 取余类的实现,该类提供了无缝的算术操作接口,所以我们能够互换它们,例如 `print e(6) * e(6)` 返回 `3`。你可以看到,所有的一切仍然工作 - 除了那个,因为我们对加减乘除操作进行了重新定义,因此所有的操作返回的整数都在 `[0 ... 10]` 范围内。我们从不需要担心浮点数精度问题或者是当 x 轴变大之后,相应 y 轴值变得无限大。 241 | 242 | 现在,现实中,这些相对简单的取余有限域并不是我们真正需要用在错误检测代码中的。更喜爱的通用构建方法被称作[伽罗瓦域](https://en.wikipedia.org/wiki/Galois_field) (技术上,任何拥有有限个数元素的域都可以称之为伽罗瓦域,但是有时候这个术语被用来更具体地指向基于多项式的域,就正如我们将要描述的)。思想是,域中的元素现在是多项式,系数是整数域中的自身值对 2 取余 (例,`a + b := (a + b) % 2`,等等)。加法和减法就像正常的那样工作,但是乘法是它自身对某一个多项式取余,具体点来说,是 `x^8 + x^4 + x^3 + x + 1`。这个更为复杂的多层构建让我们拥有了一个恰好有 256 个元素的域。如果我们想一次性地在多个字节上应用,我们仅需要并行地应用该模式 (例,如果每一块是 1024 个字节,决定 10 个多项式,每一个对应一个字节,分开扩展它们,然后结合多项式在每一个 x 轴处的值来得到附加块)。 243 | 244 | **译者注:** 245 | 246 | --- 247 | 本人认为如果每一个块对应 1024 个字节,应该使用 1024 个多项式,每一个多项式对应一个字节;举一个简单的例子来说,假设一个块对应 4 个字节,现在需要 4 个多项式,假设文件内容是 "abcdefghijkl",那分割该文件为 2 块,块 1 为 "abcd",块 2 为 "efgh",块 3 为 "ijkl"。那第一个多项式由 `(1, a)`、`(2, e)`、`(3, i)` 决定;第二个多项式由 `(1, b)`、`(2, f)`、`(3, j)` 决定,以此类推。 248 | 249 | ``` 250 | > P1 = share.lagrange_interp(map(share.Galois, [ord("a"), ord("e"), ord("i")]), map(share.Galois, [1, 2, 3])) 251 | [109, 253, 241] 252 | > P2 = share.lagrange_interp(map(share.Galois, [ord("b"), ord("f"), ord("j")]), map(share.Galois, [1, 2, 3])) 253 | [110, 253, 241] 254 | > P3 = share.lagrange_interp(map(share.Galois, [ord("c"), ord("g"), ord("k")]), map(share.Galois, [1, 2, 3])) 255 | [111, 253, 241] 256 | > P4 = share.lagrange_interp(map(share.Galois, [ord("d"), ord("h"), ord("l")]), map(share.Galois, [1, 2, 3])) 257 | [96, 4, 0] 258 | 259 | > map(lambda x : share.eval_poly_at(map(share.Galois,P1),share.Galois(x)), range(1, 7)) 260 | [97, 101, 105, 61, 49, 53] 261 | > map(lambda x : share.eval_poly_at(map(share.Galois,P2),share.Galois(x)), range(1, 7)) 262 | [98, 102, 106, 62, 50, 54] 263 | > map(lambda x : share.eval_poly_at(map(share.Galois,P3),share.Galois(x)), range(1, 7)) 264 | [99, 103, 107, 63, 51, 55] 265 | > map(lambda x : share.eval_poly_at(map(share.Galois,P4),share.Galois(x)), range(1, 7)) 266 | [100, 104, 108, 112, 116, 120] 267 | 268 | > share.berlekamp_welch_attempt(map(share.Galois,[97, 101, 105, 61, 4, 5]), map(share.Galois,[1, 2, 3, 4, 5, 6]), 2) 269 | [109, 253, 241] 270 | 271 | > map(chr, [97, 101, 105, 61, 49, 53]) 272 | ['a', 'e', 'i', '=', '1', '5'] 273 | >map(chr, [98, 102, 106, 62, 50, 54]) 274 | ['b', 'f', 'j', '>', '2', '6'] 275 | > map(chr, [99, 103, 107, 63, 51, 55]) 276 | ['c', 'g', 'k', '?', '3', '7'] 277 | > map(chr, [100, 104, 108, 112, 116, 120]) 278 | ['d', 'h', 'l', 'p', 't', 'x'] 279 | ``` 280 | 281 | --- 282 | 283 | 由上述运行代码看出,我们可以附加 3 个块,分别为 "=>?p"、"123t"、"567x",现在我们可以有 3-of-6 的协议,即得到上传的 6 块中的任意 3 块,我们都可以恢复出原内容,比如我们得到了块 1 "abcd"、块 4 "=>?p"、块 5 "123t": 284 | 285 | ``` 286 | > P1 = share.lagrange_interp(map(share.Galois,map(ord, ["a", "=", "1"])), map(share.Galois, [1,4,5])) 287 | [109, 253, 241] 288 | > P2 = share.lagrange_interp(map(share.Galois,map(ord, ["b", ">", "2"])), map(share.Galois, [1,4,5])) 289 | [110, 253, 241] 290 | ... 291 | > map(lambda x : share.eval_poly_at(map(share.Galois,P1),share.Galois(x)), range(1, 4)) 292 | [97, 101, 105] -> ["a", "e", "i"] 293 | ... 294 | ``` 295 | 296 | 由上述代码就可以复原出原来的文件内容为 "abcdefghijkl"。 297 | 298 | 但是知道它具体工作原理并不是很重要,这部分工作的要点是我们能够以这种方式重新定义 `+`、`-`、`*` 和 `/` 操作,并且他们仍然是自统一的,但是却可以使用和输出字节。 299 | 300 | #### 走向多维:自治愈立方体 (Going Multimensional: The Self-Healing Cube) 301 | 302 | 现在,我们使用有限域,并且我们能够处理错误,但是有一个问题依旧存在:当节点宕机的时候发生了什么?在任意时间点,你能依赖于有 50% 的存储你的文件的节点在线,但是你不能依赖的是相同的节点一直保持在线 - 最终,一小部分节点退出,然后另外一小部分节点退出,然后再另一小部分节点退出,直到最终没有足够多的原始节点保持在线。我们如何处理这种渐变的消耗?一种策略是,你监视奖励每一个独立存储文件的实例节点的智能合约,了解节点会停止接收该奖励的时间,然后重新上传该文件。然而,有一个问题:为了重新上传文件,你需要重新构建文件的全部,对于满足人们强烈视觉要求的高分辨率的电影是一种潜在性困难的任务;还有,理想状态下,我们是希望网络自己能够解决该问题,而不借助中心化的资源,甚至是文件的拥有者。 303 | 304 | 幸运地是,这样一种算法是存在的,我们所需要做的就是实现上面提到的错误检测码的一种巧妙的扩展。关键思想是,多项式的错误检测码是线性的,这是我们可信赖的事实。线性是数学术语,意味着对于乘法和加法来说有着良好的互操作性。例如,考虑: 305 | 306 | ``` 307 | > share.lagrange_interp([1.0, 3.0, 2.0, 1.0], [1.0, 2.0, 3.0, 4.0]) 308 | [-7.0, 12.000000000000002, -4.5, 0.4999999999999999] 309 | > share.lagrange_interp([10.0, 5.0, 5.0, 10.0], [1.0, 2.0, 3.0, 4.0]) 310 | [20.0, -12.5, 2.5, 0.0] 311 | > share.lagrange_interp([11.0, 8.0, 7.0, 11.0], [1.0, 2.0, 3.0, 4.0]) 312 | [13.0, -0.5, -2.0, 0.5000000000000002] 313 | > share.lagrange_interp([22.0, 16.0, 14.0, 22.0], [1.0, 2.0, 3.0, 4.0]) 314 | [26.0, -1.0, -4.0, 1.0000000000000004] 315 | ``` 316 | 317 | 可以观察到,第三行代码的输入是第一行代码与第二行代码输入之和,输出也是前两行输出之和。当我们加输入变为 2 倍过后,输出也变为了原先的 2 倍。那么,这样的好处是什么?好吧,这里是一个巧妙的小技巧。**Erasure codeing** 自身是一种线性的公式;它仅仅依赖于乘法和加法。因此,我们将*应用 纠删码(erasure coding) 到其自身*。那么,我们该如何去做?这里是一种可能的策略。 318 | 319 | 首先,我们使用我们的 4 数字文件,然后把它放入到一个 2X2 的网格中。 320 | 321 | ``` 322 | 1 3 323 | 2 1 324 | ``` 325 | 326 | 接下来,我们使用相同的多项式插值法和上面提到的扩展程序沿着 x 和 y 轴来扩展文件: 327 | 328 | ``` 329 | 1 3 5 7 330 | 2 1 0 10 331 | 3 10 332 | 4 8 333 | ``` 334 | 335 | 并且,我们应用相同的过程来得到剩下的 4 个值: 336 | 337 | ``` 338 | 1 3 5 7 339 | 2 1 0 10 340 | 3 10 6 2 341 | 4 8 1 5 342 | ``` 343 | 344 | --- 345 | 译者注,使用的方法如下: 346 | 347 | ``` 348 | In [77]: e = share.mkModuloClass(11) 349 | 350 | In [78]: share.extend([1,3],2,e) 351 | Out[78]: [1, 3, 5, 7] 352 | 353 | In [79]: share.extend([2,1],2,e) 354 | Out[79]: [2, 1, 0, 10] 355 | 356 | In [80]: share.extend([1,2],2,e) 357 | Out[80]: [1, 2, 3, 4] 358 | 359 | In [81]: share.extend([3,1],2,e) 360 | Out[81]: [3, 1, 10, 8] 361 | 362 | In [82]: share.extend([5,0],2,e) 363 | Out[82]: [5, 0, 6, 1] 364 | 365 | In [83]: share.extend([7,10],2,e) 366 | Out[83]: [7, 10, 2, 5] 367 | ``` 368 | 其中 `extend` 函数的第一个参数代表要扩展的值列表,第二个参数代表需要生成多少数量的值附加在原列表上,第三个参数代表使用的有限域类型。 369 | 370 | --- 371 | 372 | 注意,我们计算最后 4 个值的时候,无论是从水平扩展还是纵向扩展都是没有关系的,因为 `secret coding` 是线性自交换的,所以无论从哪个方向扩展,得到的结果都是一样的。现在假设我们丢失了数字 6。那好,我们可以在纵向上修复该问题: 373 | 374 | ``` 375 | In [90]: share.repair([5,0,None,1],2,e) 376 | Out[90]: [5, 0, 6, 1] 377 | ``` 378 | 379 | 或者横向修复该问题: 380 | 381 | ``` 382 | In [94]: share.repair([3,10,None,2],2,e) 383 | Out[94]: [3, 10, 6, 2] 384 | ``` 385 | 其中,`repair` 函数的第一个参数代表要修复的列表,第二个参数代表多项式的次数,即两个点决定一个 2 次多项式,第三个参数代表有限域类型。 386 | 387 | 无论从哪一种方向修复,我们都得到了 `6`。这是一件令人惊奇的事情:多项式在 x 和 y 轴上工作地一样好。因此,如果我们从该网格中使用这 16 块,将它们分散到 16 个不同的节点上,当这些节点中的一个消失的时候,然后和消失的节点处于同一 x 轴或者 y 轴的节点就可以联合起来,重建被消失的节点所持有的数据,并且可以索要存储该数据应得的奖励。理想地,我们可以扩展该处理过程,从 2 维的平面到 3 维的立方体,或者 4 维的超立方体,甚至更高维 - 使用更多维的一个好处是使得重新构建变得更加容易,代价是一个比较低程度的冗余。因此,我们所拥有的是一种信息理论的等价物,听起来像是直接出自科幻小说:一个高度冗余,互连,多部件组成的自治愈立方体,它能快速检测错误,并且能够自己修复错误,即使在该立方体大部分区域损坏的情况。 388 | 389 | ![borgcube](/images/2016/10/borgcube.jpg) 390 | 391 | **the cube can still function even if up to 78% of it were to be destroyed...** 392 | 393 | 那么,让我们汇总一下。你拥有一个 10 GB 大小的文件,你想将其分散到网络中。首先,你加密该文件,然后你分割该文件为 125 块。你安排这些块组成一个 3 维的 5X5X5 的立方体,指出每一个轴的多项式,并且扩展每一个轴,到最后你可以得到一个 7X7X7 的立方体。你可以寻找乐意存储这些块的 343 个节点,并且只告诉每一个节点它属于那一轴的节点们的实体信息 (我们希望努力避免单个节点将整个线,方形或立方体聚集在一起并存储它并根据需要实时计算任何冗余块,从而索要存储全部块的奖励,实际上却没有提供任何的冗余性)。 394 | 395 | 为了下载整个文件,你会针对所有块发出一个请求,然后查看进来的哪一块拥有最高的带宽,你可以使用 *pay-per-chunk* 协议来为数据的发送付款;敲诈勒索将不再是一个问题,因为你拥有如此高的冗余度以至于没有人可以拥有垄断的权利从而拒绝向你提供文件。只要满足最小数量的块到达,你可以使用数学运算来解密该文件,并且在本地复原该文件。或许,如果编码是 *per-byte*,你甚至可以应用它到类似 Youtube 流式实现,一次恢复一个字节。 396 | 397 | 在某种意义上,在自愈和对这种伪造冗余的脆弱性之间存在不可避免的权衡:如果网络的部分节点联合在一起,恢复一个丢失的块来提供冗余性,那么网络中恶意的大量参与者就可以凭空恢复丢失的块提供给客户端,像客户端索要伪造冗余的费用。或许,一些模式包括在每一块之上增加另外一层加密,隐藏加密的密钥和在另一轴存储块的参与者的地址,仅在某些特定时间激励披露过程来形成一个最佳的平衡。 398 | 399 | #### Secret Sharing 400 | 401 | 在文章的开始,我提到了 `纠删码(erasure coding)` 概念的另一名称,`secret sharing`。从该名字可以看出,这两者如何关联:如果你有一个分散数据到 9 个节点中,其中 5 个节点就可以恢复出原始文件,但是 4 个却不可以的算法,那么另外一个明显的使用情况是,使用相同的算法来存储私钥 - 分割你比特币钱包备份为 9 块,将其中一块给你的妈妈,一块给你的上司,一块给你的律师,将其中 3 块放入一个安全的保管箱里,等等。如果你忘记了你的密码,你可以分别向他们索要其中一部分,他们看起来不会互相串通。这是一个非常合理的办法,但是还需包括一个实现细节来使其正常工作。 402 | 403 | 这个问题是:即使 4-of-9 不能不能恢复出原始的密钥,4-of-9 仍然可以联合,并且可以得到关于密钥的大量信息 - 具体点来说,四个线性等式已经明了,第五个不清楚。这可以约减选择空间的维度到五分之一,所以代替需要搜索 `2^256` 次的私钥空间被约减到只需要搜索 `2^51` 次。如果你的私钥是 180 位,那么就约减到 `2^36` - 对于一台普通电脑来说,是绰绰有余可以解决该复杂度的问题。我们处理该问题的解决方法是不是对私钥进行 `纠删码(erasure coding)`,而是对私钥再加上 4 被随机的冗余进行编码。更精确来说,让私钥是多项式中 0 次方的系数,随机找 4 个值成为接下来的四个系数,从该多项式中获取值。这使得制作每一块花费五倍的时间,但是却有即使 4-of-9 也需要有 `2^180` 或者 `2^256` 的搜索空间。 404 | 405 | #### 结论 406 | 407 | 那么我们来到了这里,这是对 `纠删码(erasure coding)` 能力的一个简介 - arguably the single most underhyped set of algorithms (except perhaps SCIP) in computer science or cryptography。这个思想相对于文件存储来说就相当于多签名对于智能合约来说,允许你最大程度地获得安全性和你能接受的文件冗余程度。它是一种解决文件可用性的一种有效方法,可以取代简单地分割与复制方法 (的确,如果你想应用 1-of-n 策略,实际上就是简单复制方法);它能够用来概括和处理冗余中的一些问题,就像加密用来概括和解决隐私性中的一些问题一样。 408 | 409 | 去中心化文件存储仍然是远未解决的问题;尽管核心技术的大部分,包括 *[Tahoe-LAFS](https://tahoe-lafs.org/trac/tahoe-lafs)* 中的 `纠删码(erasure coding)`,已经被实现,但仍然有大量小的或者不是那么小的实现细节需要被解决来使系统实际可用。一个有效的信誉系统将会被需要用来测量节点的服务质量 (例如,一个高达 99% 可用时间的节点至少值 3 个 50% 可用时间的节点)。在某些方面,受激励的文件存储甚至依赖于实际的区块链扩展性;不得不每小时隐式地对 343 个与验证合约交互的交易费用买单这种方法将不会工作,直到交易费远远比现在的交易费要低,并且直到那时,需要一些更粗糙的妥协。但是再一次,几乎加密货币领域里的每一个问题依旧有很长的一段路要走。 410 | -------------------------------------------------------------------------------- /book.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Kang Li", 3 | "description": "This is a book created by likang", 4 | "extension": null, 5 | "generator": "site", 6 | "links": { 7 | "sharing": { 8 | "all": null, 9 | "facebook": null, 10 | "google": null, 11 | "twitter": null, 12 | "weibo": null 13 | }, 14 | "sidebar": { 15 | "Kang Li's Website": "http://www.iethpay.com" 16 | } 17 | }, 18 | "output": null, 19 | "pdf": { 20 | "fontSize": 12, 21 | "footerTemplate": null, 22 | "headerTemplate": null, 23 | "margin": { 24 | "bottom": 36, 25 | "left": 62, 26 | "right": 62, 27 | "top": 36 28 | }, 29 | "pageNumbers": false, 30 | "paperSize": "a4" 31 | }, 32 | "plugins": [], 33 | "title": "ethereum-learn", 34 | "variables": {} 35 | } 36 | -------------------------------------------------------------------------------- /ethereum-core-storage.md: -------------------------------------------------------------------------------- 1 | #### 以太坊核心存储结构分析 2 | 3 | **作者:李康** 4 | 5 | 以太坊核心存储结构:Merkle-Patricia-Tree(前缀树与默克尔树的结合体),以太坊中的交易树、交易收据树、账户树以及合约存储树均使用该树索引。 6 | 7 | 该树分为三种类型节点:branch(分支,17个元素的元组)、extension(扩展,2个元素的元组)、leaf(叶子节点,2个元素的元组),因此为了区分 extension 与 leaf 节点,使用 key 的第一个 16 进制字符,其中 0000 与 0001 均代表扩展节点,0010 与 0011 均代表叶子节点,也就是说使用倒数第二位来区分 extension 与 leaf 节点。最后一位的 0、1 分别表示了该 key 原先为偶数个 16 进制字符与奇数个 16 进制字符,也就意味着为 0 时,需要填充另外的0000。 8 | 9 | ##### 账户在以太坊中的存储 10 | 11 | 下面来看一下以太坊底层存储中是如何实现账户存储的: 12 | 13 | ![账户存储](/images/2017/10/image001.png) 14 | 15 | 目前以太坊中存在两个账户: 16 | 0xa3ac96fbe4b0dce5f6f89a715ca00934d68f6c37 17 | 0x0f5578914288da3b9a3f43ba41a2e6b4d3dd587a 18 | 19 | 通过使用编写的拉取以太坊底层存储的 [python 脚本](https://github.com/lightning-li/blockchain-knife/blob/master/detect_internal_storage.py),拉取目前底层存储的账户数据(脚本的具体用法有所改变,详见[这里](https://github.com/lightning-li/blockchain-knife)): 20 | 21 | ![账户数据](/images/2017/10/image003.png) 22 | 23 | 其中前一项是以太坊在底层中真正存储的 key,与账户地址的对应关系如下: 24 | 25 | ![](/images/2017/10/image005.png) 26 | 27 | 也就是说,以太坊存储账户数据的时候,会将账户地址进行一次keccak256 哈希计算 28 | 29 | 此时,账户树的形状如下: 30 | 31 | ![](/images/2017/10/3.png) 32 | 33 | 以太坊中会对节点使用 RLP 编码来存储在底层数据 leveldb 或 rocksdb 中,存储形式为 。所以在上图的槽中,slot 1 实际存储的是 sha3(rlp(leaf1))。 34 | 35 | ##### 合约在以太坊中的存储 36 | 37 | 下面来看一下智能合约中的数据是如何存储在以太坊中的: 38 | 智能合约中的每一个状态变量都有一个 position,以太坊中每一个 39 | storage slot 为 32 个字节,即 256 位,solidity 编译器会尽量将变量装到同一个 storage slot 中去,对于装不下的,会重新分配 storage slot,遇到 mapping、struct 等类型的变量时,编译器会自动重新分配 storage slot。 40 | 41 | ![](/images/2017/10/image009.png) 42 | 43 | 该代码存在于账户下,该合约的地址为 0xfe5eeb229738ab87753623a81a42656bcde30a67,contract address = sha3(rlp.encode([creator address, nonce])) 44 | 45 | ![](/images/2017/10/image011.png) 46 | 47 | 该账户在底层数据库中存储的 key 为 48 | 49 | ``` 50 | // geth console 环境中 51 | > web3.sha3("0xfe5eeb229738ab87753623a81a42656bcde30a67", {encoding : "hex"}) 52 | 0x886f7bfb7a4887d716ec4fbb06a8bf35fc1972d2962590248ffe6271e77ac7c1 53 | ``` 54 | 55 | ![](/images/2017/10/image013.png) 56 | 57 | ``` 58 | // python 59 | In [50]: '\xed\xa8}\x9d\xeb\xa5\xbb\xc6O\xa7\'B\xf5\x84"\xaa\xf4f\x9e\xaai)\xe2\xf2_\xa60D\x8a\x0c\x7fJ'.encode("hex") 60 | Out[50]: 'eda87d9deba5bbc64fa72742f58422aaf4669eaa6929e2f25fa630448a0c7f4a' 61 | ``` 62 | 63 | ![](/images/2017/10/image015.png) 64 | 65 | 我们来一一分析: 66 | 67 | ![](/images/2017/10/image017.png) 68 | 69 | 我们可以看到在相应的位置上分别存储了相应的值, 70 | 对于 mapping 来说,其中元素存储的position如下: 71 | 72 | sha3(LeftPad32(key, 0), LeftPad32(map position, 0)) 73 | 74 | 所以有: 75 | 76 | ![](/images/2017/10/image019.png) 77 | 78 | 由于 mapping 中 value 为一个 struct,size 大于 256 位,因此,在存储的位置按顺位加 1,如上图所示。 79 | 来看一下,动态数组在以太坊底层存储形式: 80 | 81 | ![](/images/2017/10/image021.png) 82 | 83 | 其中,在位置 5 存储动态数组的长度,然后以位置 sha3(5) 开始顺序存放数组元素。 84 | 85 | --- update --- 86 | 87 | mapping中的 key 也可以是 string 形式,假设 mapping(string => string),key = "11", value = "22", 则在 eth 底层中存储的 key 为: 88 | 89 | ``` 90 | // 3131 代表字符串 "11",后面的 32 个 0 代表 map 的 position 91 | > web3.sha3("31310000000000000000000000000000000000000000000000000000000000000000", {encoding : "hex"}) 92 | "0x756ab6158180196289fbd030ff61972bf49c0e51dbf603d0dfaf6b1d3f0e49a6" 93 | 94 | // 0x3232...04 是 bytes(string) 在底层的存储表达形式,后面的 04 代表字符串的长度为 2, 前面的 3232 代表真正存储的字符串 "22" 95 | > eth.getStorageAt("0xf8a7e4fb488d5e0426012592c5d66e44dffa6cb7", "0x756ab6158180196289fbd030ff61972bf49c0e51dbf603d0dfaf6b1d3f0e49a6") 96 | "0x3232000000000000000000000000000000000000000000000000000000000004" 97 | ``` 98 | 99 | key = "1111111111111111111111111111111111", len(key) = 34; value = "2222222222222222222222222222222", len(value) = 31 100 | 101 | ``` 102 | > web3.sha3("313131313131313131313131313131313131313131313131313131313131313131310000000000000000000000000000000000000000000000000000000000000000", {encoding : "hex"}) 103 | "0xeb5b36d98f0c746023b3b0e91319a7ee8cb743f75df9f8ce513c5120487cdac3" 104 | 105 | // 最后的 3e 代表 31 个字节长 106 | > eth.getStorageAt("0xf8a7e4fb488d5e0426012592c5d66e44dffa6cb7", "0xeb5b36d98f0c746023b3b0e91319a7ee8cb743f75df9f8ce513c5120487cdac3") 107 | "0x323232323232323232323232323232323232323232323232323232323232323e" 108 | ``` 109 | 110 | key = "1111111111111111111111111111111111", len(key) = 34; value = "22222222222222222222222222222222", len(value) = 32 111 | 112 | ``` 113 | // 41 代表字符串长度,为了与小于32个字节的长度区分,这里加了 1,所以算长度时:(0x41-1)/2 = 32 个字节 114 | > eth.getStorageAt("0xf8a7e4fb488d5e0426012592c5d66e44dffa6cb7", "0xeb5b36d98f0c746023b3b0e91319a7ee8cb743f75df9f8ce513c5120487cdac3") 115 | "0x0000000000000000000000000000000000000000000000000000000000000041" 116 | 117 | // 对该 value 对应的 key 再次进行哈希,用于存放真正字符串 118 | > web3.sha3("0xeb5b36d98f0c746023b3b0e91319a7ee8cb743f75df9f8ce513c5120487cdac3", {encoding : "hex"}) 119 | "0x2b3b0a6d0771d1a8fa6f89276ead655b7a0684e2a22d9290bcf4f8944f05b504" 120 | 121 | > eth.getStorageAt("0xf8a7e4fb488d5e0426012592c5d66e44dffa6cb7", "0x2b3b0a6d0771d1a8fa6f89276ead655b7a0684e2a22d9290bcf4f8944f05b504") 122 | "0x3232323232323232323232323232323232323232323232323232323232323232" 123 | ``` 124 | 125 | **(完)** 126 | -------------------------------------------------------------------------------- /ethereum-state-clearing.md: -------------------------------------------------------------------------------- 1 | #### 以太坊状态清理 2 | 3 | **作者:李康** 4 | 5 | 在 2016 年 9 月份,以太坊网络遭遇了 DDOS 攻击,由于以太坊协议的漏洞(可以使用 SUICIDE 操作码廉价地生成新账户),使得攻击者可以在很少的花费下创建大量的空账户 - empty account(zero balance、nonce、code),空账户功能性上与未存在账户 - non-existence account 相似,但是不同点在于空账户会存在于以太坊的 account state trie 中,所以这一次 DDOS 攻击导致以太坊节点存储空间爆炸(大概增加了 1900 万空账户,但是账户与 account state trie 中的节点不是 1:1 的关系,因为有中间节点,所以真正增加的存储空间大于 1900 万账户所需空间)。为了解决该问题,以太坊进行了硬分叉 spurious dragon,旨在清除空账户,减小以太坊节点所需存储空间,修补协议漏洞(SUICIDE 创建新账户时需要额外的 25000 gas),并且引进了新的协议规则:1). empty account 不再允许被创建(原先可以导致空账户产生的操作将会使账户变为 non-existence)。 2). 可以通过交易与空账户交互(touch)来删除它们。 6 | 7 | 一个在硬分叉 spurious dragon 之前可以产生大量空账户的合约例子: 8 | 9 | ``` 10 | pragma solidity ^0.4.15; 11 | 12 | contract C { 13 | function del(address s) { 14 | selfdestruct(s); 15 | } 16 | } 17 | 18 | contract A { 19 | uint public a; 20 | C cc; 21 | event Log(address); 22 | 23 | function() payable{ 24 | 25 | } 26 | function set(address s) { 27 | cc = C(s); 28 | a = 5; 29 | } 30 | 31 | function haha() { 32 | a = 3; 33 | Log(msg.sender); 34 | 35 | } 36 | function des() { 37 | for (uint i = 0; i < 100; ++i) { 38 | cc.del(address(i + 6)); 39 | } 40 | a = 100; 41 | } 42 | } 43 | ``` 44 | 45 | 合约 A 调用 C 的 del 函数时,会调用 selfdestruct 函数,根据黄皮书对 SUICIDE 操作码的解释 "Halt execution and register account for later deletion",意味着 C 合约的执行会立即终止,但是在该交易执行完毕后,才会删除该账户,也就意味着可以多次调用 del 即 selfdestruct 函数,导致产生大量的空账户。 46 | 47 | --- 48 | 参考文献: 49 | 1. https://ethereum.stackexchange.com/questions/11312/how-was-the-state-bloat-attack-that-led-to-the-eip-150-hardfork-performed 50 | 51 | 2. https://ethereum.stackexchange.com/questions/10134/why-were-empty-accounts-allowed-to-be-on-the-blockchain 52 | 53 | 3. https://www.reddit.com/r/ethereum/comments/5es5g4/a_state_clearing_faq/ 54 | -------------------------------------------------------------------------------- /evm-learn.md: -------------------------------------------------------------------------------- 1 | ### EVM 学习 2 | 3 | **作者:李康** 4 | 5 | **使用 cpp-ethereum** 6 | -------------------------------------------------------------------------------- /golem-multisig-wallet.md: -------------------------------------------------------------------------------- 1 | #### Golem 多方签名钱包解析 2 | 3 | **作者:李康** 4 | 5 | **注意:修改了 submitTransactionWithSignatures 函数,由于 web3.eth.sign 行为发生了改变,见 https://github.com/ethereum/EIPs/issues/191** 6 | ``` 7 | pragma solidity ^0.4.8; 8 | 9 | 10 | /// @title Multisignature wallet - Allows multiple parties to agree on transactions before execution. 11 | /// @author Stefan George - 12 | contract MultiSigWallet { 13 | 14 | event Confirmation(address sender, bytes32 transactionHash); 15 | event Revocation(address sender, bytes32 transactionHash); 16 | event Submission(bytes32 transactionHash); 17 | event Execution(bytes32 transactionHash); 18 | event Deposit(address sender, uint value); 19 | event OwnerAddition(address owner); 20 | event OwnerRemoval(address owner); 21 | event RequiredUpdate(uint required); 22 | 23 | mapping (bytes32 => Transaction) public transactions; 24 | mapping (bytes32 => mapping (address => bool)) public confirmations; 25 | mapping (address => bool) public isOwner; 26 | address[] owners; 27 | bytes32[] transactionList; 28 | uint public required; 29 | 30 | struct Transaction { 31 | address destination; 32 | uint value; 33 | bytes data; 34 | uint nonce; 35 | bool executed; 36 | } 37 | 38 | modifier onlyWallet() { 39 | if (msg.sender != address(this)) 40 | throw; 41 | _; 42 | } 43 | 44 | modifier signaturesFromOwners(bytes32 transactionHash, uint8[] v, bytes32[] rs) { 45 | for (uint i=0; i _ownerCount 89 | || _required == 0 90 | || _ownerCount == 0) 91 | throw; 92 | _; 93 | } 94 | 95 | function addOwner(address owner) 96 | external 97 | onlyWallet 98 | ownerDoesNotExist(owner) 99 | { 100 | isOwner[owner] = true; 101 | owners.push(owner); 102 | OwnerAddition(owner); 103 | } 104 | 105 | function removeOwner(address owner) 106 | external 107 | onlyWallet 108 | ownerExists(owner) 109 | { 110 | isOwner[owner] = false; 111 | for (uint i=0; i owners.length) 118 | updateRequired(owners.length); 119 | OwnerRemoval(owner); 120 | } 121 | 122 | function updateRequired(uint _required) 123 | public 124 | onlyWallet 125 | validRequired(owners.length, _required) 126 | { 127 | required = _required; 128 | RequiredUpdate(_required); 129 | } 130 | 131 | function addTransaction(address destination, uint value, bytes data, uint nonce) 132 | private 133 | notNull(destination) 134 | returns (bytes32 transactionHash) 135 | { 136 | transactionHash = sha3(destination, value, data, nonce); 137 | if (transactions[transactionHash].destination == 0) { 138 | transactions[transactionHash] = Transaction({ 139 | destination: destination, 140 | value: value, 141 | data: data, 142 | nonce: nonce, 143 | executed: false 144 | }); 145 | transactionList.push(transactionHash); 146 | Submission(transactionHash); 147 | } 148 | } 149 | 150 | function submitTransaction(address destination, uint value, bytes data, uint nonce) 151 | external 152 | returns (bytes32 transactionHash) 153 | { 154 | transactionHash = addTransaction(destination, value, data, nonce); 155 | confirmTransaction(transactionHash); 156 | } 157 | 158 | function submitTransactionWithSignatures(address destination, uint value, bytes data, uint nonce, uint8[] v, bytes32[] rs) 159 | external 160 | returns (bytes32 transactionHash) 161 | { 162 | transactionHash = addTransaction(destination, value, data, nonce); 163 | bytes memory prefix = "\x19Ethereum Signed Message:\n32"; 164 | bytes32 prefixedHash = sha3(prefix, transactionHash); 165 | confirmTransactionWithSignatures(transactionHash, prefixedHash, v, rs); 166 | } 167 | 168 | 169 | function addConfirmation(bytes32 transactionHash, address owner) 170 | private 171 | notConfirmed(transactionHash, owner) 172 | { 173 | confirmations[transactionHash][owner] = true; 174 | Confirmation(owner, transactionHash); 175 | } 176 | 177 | function confirmTransaction(bytes32 transactionHash) 178 | public 179 | ownerExists(msg.sender) 180 | { 181 | addConfirmation(transactionHash, msg.sender); 182 | executeTransaction(transactionHash); 183 | } 184 | 185 | function confirmTransactionWithSignatures(bytes32 transactionHash, bytes32 prefixedHash, uint8[] v, bytes32[] rs) 186 | public 187 | signaturesFromOwners(prefixedHash, v, rs) 188 | { 189 | for (uint i=0; i 0) 230 | Deposit(msg.sender, msg.value); 231 | } 232 | 233 | function isConfirmed(bytes32 transactionHash) 234 | public 235 | constant 236 | returns (bool) 237 | { 238 | uint count = 0; 239 | for (uint i=0; i 0) 272 | _transactionList[i] = _transactionListTemp[i]; 273 | } 274 | 275 | function getPendingTransactions() 276 | external 277 | constant 278 | returns (bytes32[] _transactionList) 279 | { 280 | return filterTransactions(true); 281 | } 282 | 283 | function getExecutedTransactions() 284 | external 285 | constant 286 | returns (bytes32[] _transactionList) 287 | { 288 | return filterTransactions(false); 289 | } 290 | } 291 | ``` 292 | 在 https://remix.ethereum.org/ 中编译该合约,在右侧 Execution environment 中选择 Web3 provider,输入本地节点监听的端口,即可连接到本地搭建的私有网络中。 293 | 294 | 1. 首先部署该合约,在右侧合约下方有 Create 按钮,输入合约构造函数所需的参数,然后点击该按钮部署至私有链中,注意在命令行终端中,将创建合约的账户解锁 `personal.unlockAccount`,接着启动挖矿功能 `miner.start()`。本例中使用 2-of-3 的多方签名模式: 295 | 296 | ``` 297 | Create : ["0xa3ac96fbe4b0dce5f6f89a715ca00934d68f6c37", "0x0f5578914288da3b9a3f43ba41a2e6b4d3dd587a", "0x802c12459243cdafd34112561c478cefe3e714db"], 2 298 | ``` 299 | 其中 "0xa3ac96fbe4b0dce5f6f89a715ca00934d68f6c37" 设为账户 A,"0xa3ac96fbe4b0dce5f6f89a715ca00934d68f6c37" 设为账户 B,"0x802c12459243cdafd34112561c478cefe3e714db" 设为账户 C。 300 | 部署成功之后,可得到合约地址 `0x9f94a748d21961f9dafc96274834793e16a4fac0`,在终端可以得到该合约实例: 301 | 302 | ``` 303 | > var multisigwallet_sol_multisigwalletContract = web3.eth.contract([{"constant":true,"inputs":[{"name":"","type":"bytes32"},{"name":"","type":"address"}],"name":"confirmations","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"destination","type":"address"},{"name":"value","type":"uint256"},{"name":"data","type":"bytes"},{"name":"nonce","type":"uint256"}],"name":"submitTransaction","outputs":[{"name":"transactionHash","type":"bytes32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"owner","type":"address"}],"name":"removeOwner","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"isOwner","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"transactionHash","type":"bytes32"}],"name":"confirmationCount","outputs":[{"name":"count","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_required","type":"uint256"}],"name":"updateRequired","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"transactions","outputs":[{"name":"destination","type":"address"},{"name":"value","type":"uint256"},{"name":"data","type":"bytes"},{"name":"nonce","type":"uint256"},{"name":"executed","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"transactionHash","type":"bytes32"}],"name":"isConfirmed","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"owner","type":"address"}],"name":"addOwner","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"transactionHash","type":"bytes32"}],"name":"confirmTransaction","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"destination","type":"address"},{"name":"value","type":"uint256"},{"name":"data","type":"bytes"},{"name":"nonce","type":"uint256"},{"name":"v","type":"uint8[]"},{"name":"rs","type":"bytes32[]"}],"name":"submitTransactionWithSignatures","outputs":[{"name":"transactionHash","type":"bytes32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"transactionHash","type":"bytes32"},{"name":"prefixedHash","type":"bytes32"},{"name":"v","type":"uint8[]"},{"name":"rs","type":"bytes32[]"}],"name":"confirmTransactionWithSignatures","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"transactionHash","type":"bytes32"}],"name":"executeTransaction","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"getPendingTransactions","outputs":[{"name":"_transactionList","type":"bytes32[]"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"required","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"getExecutedTransactions","outputs":[{"name":"_transactionList","type":"bytes32[]"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"transactionHash","type":"bytes32"}],"name":"revokeConfirmation","outputs":[],"payable":false,"type":"function"},{"inputs":[{"name":"_owners","type":"address[]"},{"name":"_required","type":"uint256"}],"payable":false,"type":"constructor"},{"payable":true,"type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"transactionHash","type":"bytes32"}],"name":"Confirmation","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"transactionHash","type":"bytes32"}],"name":"Revocation","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"transactionHash","type":"bytes32"}],"name":"Submission","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"transactionHash","type":"bytes32"}],"name":"Execution","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"}],"name":"OwnerAddition","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"}],"name":"OwnerRemoval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"required","type":"uint256"}],"name":"RequiredUpdate","type":"event"}]); 304 | > var multiSigWalletInstance = multisigwallet_sol_multisigwalletContract.at("0x9f94a748d21961f9dafc96274834793e16a4fac0") 305 | ``` 306 | 307 | 2. 接着我们向该多方签名钱包存入一笔钱,调用该合约的 fallback 函数(在调用合约不存在的函数时,也会调用默认的 fallback 函数): 308 | 309 | ``` 310 | > personal.unlockAccount(eth.coinbase) 311 | Unlock account 0xa3ac96fbe4b0dce5f6f89a715ca00934d68f6c37 312 | Passphrase: 313 | true 314 | > eth.sendTransaction({from : eth.coinbase, to : "0x9f94a748d21961f9dafc96274834793e16a4fac0", value : web3.toWei(9, "ether")}) 315 | "0xc6dd7d6cf4cbfcde3f4b1c4c0cd09eeaf115eb5f5d4d594fc486230bfb185919" 316 | > miner.start() 317 | null 318 | > eth.getBalance("0x9f94a748d21961f9dafc96274834793e16a4fac0") 319 | 9000000000000000000 320 | > miner.stop() 321 | true 322 | ``` 323 | 324 | 3. 有两种模式提交交易来转移资金:第一种, 使用 `submitTransaction` 函数,即 A、B、C 三方中任意两方使用相同的参数(接收方、金额、data、nonce,4 元组来标识唯一一笔交易)来调用该函数,一旦达到多方签名阈值,钱包自动将以太币转入接收方账户中;第二种,使用 `submitTransactionWithSignatures` ,A、B、C 任意一方调用该函数,均可在参数中添加他人对钱包转账交易的签名,达到阈值即可。 325 | 326 | 第一种方法:账户 A 与账户 B 均提交一笔确认交易 327 | ``` 328 | > miner.start() 329 | > eth.coinbase 330 | "0xa3ac96fbe4b0dce5f6f89a715ca00934d68f6c37" 331 | > personal.unlockAccount(eth.coinbase) 332 | Unlock account 0xa3ac96fbe4b0dce5f6f89a715ca00934d68f6c37 333 | Passphrase: 334 | true 335 | > multiSigWalletInstance.submitTransaction.sendTransaction("0x703bfe10ec3da7b6655e9787f21ccd05b53265e5", 100, "ts", 0, {from : eth.coinbase, gas : 1000000}) 336 | "0x9483344d99cc903169136ad84f458297cec19f777385a30432b001c6382fb26a" 337 | > eth.getBalance("0x703bfe10ec3da7b6655e9787f21ccd05b53265e5") 338 | 0 339 | > personal.unlockAccount("0x0f5578914288da3b9a3f43ba41a2e6b4d3dd587a") 340 | Unlock account 0x0f5578914288da3b9a3f43ba41a2e6b4d3dd587a 341 | Passphrase: 342 | true 343 | > > multiSigWalletInstance.submitTransaction.sendTransaction("0x703bfe10ec3da7b6655e9787f21ccd05b53265e5", 100, "ts", 0, {from : "0x0f5578914288da3b9a3f43ba41a2e6b4d3dd587a", gas : 1000000}) 344 | "0xf6938769a24b3aed8030c20534e0a1964e4d6e616d2b0ac94901211904c83d88" 345 | > > eth.getBalance("0x703bfe10ec3da7b6655e9787f21ccd05b53265e5") 346 | 100 347 | > miner.stop() 348 | ``` 349 | 350 | 第二种方法:账户 A 提交一笔交易,其中交易的数据中包含账户 A 与 B 对同一笔转账交易(四元组)哈希的签名 351 | ``` 352 | //获取要签名的转账交易 <"0x703bfe10ec3da7b6655e9787f21ccd05b53265e5", 200, "ts2", 1> 哈希值,其中地址为 160 位,200 为 uint256 类型,"ts2" 为 bytes 类型, 1 为 uint256 类型,solidity-sha3 与 web3.sha3 略有差别,详细见 https://github.com/raineorshine/solidity-sha3/blob/master/src/index.js 353 | > web3.toHex(200) 354 | "0xc8" 355 | > web3.toHex("ts2") 356 | "0x747332" 357 | // 将 value 与 nonce 值分别扩充到 256位,其余不变 358 | > txHash = web3.sha3("703bfe10ec3da7b6655e9787f21ccd05b53265e500000000000000000000000000000000000000000000000000000000000000c87473320000000000000000000000000000000000000000000000000000000000000001",{encoding:"hex"}) 359 | "0xd0dcee45718975f75da779890289089a71657e8923b81329473a6bb17c521c5c" 360 | > Asig = web3.eth.sign(eth.coinbase, txHash) 361 | "0xefa03c079a7631cb31250994412deac02f9aa41ac8faf0519a0f51dea17c715a60f31bc4808f16a1a16c63a7b5afbaca1d86c91bbec0c9b4f46cd38869691cf91c" 362 | > Bsig = web3.eth.sign(personal.listAccounts[1], txHash) 363 | "0xbcc7f2cc99b587f0cb2207470e811c1705997f1b37838c1a98c7e905fd5229b8083a0638f01fd53f11fb9f4f4d023cfaa4e4a5a5d931fe125d58a08b6f8e144e1b" 364 | > Ar = Asig.slice(0, 66) 365 | "0xefa03c079a7631cb31250994412deac02f9aa41ac8faf0519a0f51dea17c715a" 366 | > As = "0x" + Asig.slice(66, 130) 367 | "0x60f31bc4808f16a1a16c63a7b5afbaca1d86c91bbec0c9b4f46cd38869691cf9" 368 | > Av = 28 369 | 28 370 | > Br = Bsig.slice(0, 66) 371 | "0xbcc7f2cc99b587f0cb2207470e811c1705997f1b37838c1a98c7e905fd5229b8" 372 | > Bs = "0x" + Bsig.slice(66, 130) 373 | "0x083a0638f01fd53f11fb9f4f4d023cfaa4e4a5a5d931fe125d58a08b6f8e144e" 374 | > Bv = 27 375 | 27 376 | > multiSigWalletInstance.submitTransactionWithSignatures.sendTransaction("0x703bfe10ec3da7b6655e9787f21ccd05b53265e5", 200, "ts2", 1, [Av, Bv], [Ar, Br, As, Bs], {from : eth.coinbase, gas : 1000000}) 377 | "0x442c0533f482ffdcc14c334f36b5d9f8c39a41a4407cfb742bdebf9ad7fa4180" 378 | > miner.start() 379 | null 380 | > eth.getTransactionReceipt("0x442c0533f482ffdcc14c334f36b5d9f8c39a41a4407cfb742bdebf9ad7fa4180") 381 | { 382 | blockHash: "0xf48630e309251ef21cc05b943982fea52358ec7788b4ecc76c4359032f87fc63", 383 | blockNumber: 4754, 384 | contractAddress: null, 385 | cumulativeGasUsed: 246394, 386 | from: "0xa3ac96fbe4b0dce5f6f89a715ca00934d68f6c37", 387 | gasUsed: 246394, 388 | logs: [{ 389 | address: "0x9f94a748d21961f9dafc96274834793e16a4fac0", 390 | blockHash: "0xf48630e309251ef21cc05b943982fea52358ec7788b4ecc76c4359032f87fc63", 391 | blockNumber: 4754, 392 | data: "0xd0dcee45718975f75da779890289089a71657e8923b81329473a6bb17c521c5c", 393 | logIndex: 0, 394 | removed: false, 395 | topics: ["0x1b15da2a2b1f440c8fb970f04466e7ccd3a8215634645d232bbc23c75785b5bb"], 396 | transactionHash: "0x442c0533f482ffdcc14c334f36b5d9f8c39a41a4407cfb742bdebf9ad7fa4180", 397 | transactionIndex: 0 398 | }, { 399 | address: "0x9f94a748d21961f9dafc96274834793e16a4fac0", 400 | blockHash: "0xf48630e309251ef21cc05b943982fea52358ec7788b4ecc76c4359032f87fc63", 401 | blockNumber: 4754, 402 | data: "0x000000000000000000000000a3ac96fbe4b0dce5f6f89a715ca00934d68f6c37d0dcee45718975f75da779890289089a71657e8923b81329473a6bb17c521c5c", 403 | logIndex: 1, 404 | removed: false, 405 | topics: ["0xe1c52dc63b719ade82e8bea94cc41a0d5d28e4aaf536adb5e9cccc9ff8c1aeda"], 406 | transactionHash: "0x442c0533f482ffdcc14c334f36b5d9f8c39a41a4407cfb742bdebf9ad7fa4180", 407 | transactionIndex: 0 408 | }, { 409 | address: "0x9f94a748d21961f9dafc96274834793e16a4fac0", 410 | blockHash: "0xf48630e309251ef21cc05b943982fea52358ec7788b4ecc76c4359032f87fc63", 411 | blockNumber: 4754, 412 | data: "0x0000000000000000000000000f5578914288da3b9a3f43ba41a2e6b4d3dd587ad0dcee45718975f75da779890289089a71657e8923b81329473a6bb17c521c5c", 413 | logIndex: 2, 414 | removed: false, 415 | topics: ["0xe1c52dc63b719ade82e8bea94cc41a0d5d28e4aaf536adb5e9cccc9ff8c1aeda"], 416 | transactionHash: "0x442c0533f482ffdcc14c334f36b5d9f8c39a41a4407cfb742bdebf9ad7fa4180", 417 | transactionIndex: 0 418 | }, { 419 | address: "0x9f94a748d21961f9dafc96274834793e16a4fac0", 420 | blockHash: "0xf48630e309251ef21cc05b943982fea52358ec7788b4ecc76c4359032f87fc63", 421 | blockNumber: 4754, 422 | data: "0xd0dcee45718975f75da779890289089a71657e8923b81329473a6bb17c521c5c", 423 | logIndex: 3, 424 | removed: false, 425 | topics: ["0x7e9e1cb65db4927b1815f498cbaa226a15c277816f7df407573682110522c9b1"], 426 | transactionHash: "0x442c0533f482ffdcc14c334f36b5d9f8c39a41a4407cfb742bdebf9ad7fa4180", 427 | transactionIndex: 0 428 | }], 429 | logsBloom: "0x00000000000000000000100000000000000000000000000000020000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000400000000000000000000000400000000000080400000000001000000000000000000000000000000000000000200000020000000000040000000000000000", 430 | root: "0xe364f018cc9cd462d46a7180c0eb49ca010dc283821844c3e5023f260db33517", 431 | to: "0x9f94a748d21961f9dafc96274834793e16a4fac0", 432 | transactionHash: "0x442c0533f482ffdcc14c334f36b5d9f8c39a41a4407cfb742bdebf9ad7fa4180", 433 | transactionIndex: 0 434 | } 435 | > eth.getBalance("0x703bfe10ec3da7b6655e9787f21ccd05b53265e5") 436 | 300 437 | > miner.stop() 438 | true 439 | ``` 440 | 441 | **注意** 442 | 443 | 上述 `web3.eth.sign(msg)` 函数实际进行的操作是 sign(keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))),而 solidity 中 `ecrecover` 函数的第一个参数是真正被签名的数据,即需要在合约的 `submitTransactionWithSignatures` 函数中进行转换: 444 | ``` 445 | transactionHash = addTransaction(destination, value, data, nonce); 446 | bytes memory prefix = "\x19Ethereum Signed Message:\n32"; 447 | bytes32 prefixedHash = sha3(prefix, transactionHash); 448 | ``` 449 | 450 | prefixedHash 也可在节点客户端计算出: 451 | ``` 452 | > prefix = "\x19Ethereum Signed Message:\n32"; 453 | "\x19Ethereum Signed Message:\n32" 454 | > prefix = web3.toHex(prefix) 455 | "0x19457468657265756d205369676e6564204d6573736167653a0a3332" 456 | > web3.sha3(prefix + txHash.slice(2), {encoding : "hex"}) 457 | "0xdffc185d39cf96dadf4a067c541a03d57155c81bd166fe22a4aee33d13bbad0b" 458 | ``` 459 | 460 | 关于 solidity 中 ecrecover 与 web3.eth.sign 使用问题可参考 [Use solidity ecrecover with signature calculated with eth_sign](https://gist.github.com/bas-vk/d46d83da2b2b4721efb0907aecdb7ebd) 461 | 462 | --- 463 | 下面看这样一个问题: 464 | 465 | 1. A、B、C 拥有一个 2-of-3 的钱包 W1; 466 | 467 | 2. A、B、D 拥有一个 2-of-3 的钱包 W2; 468 | 469 | 3. A、B 决定通过 W1 使用 `submitTransactionWithSignatures` 函数向 E 转一笔以太币,参数中包含了 A、B 对四元组转账交易的签名; 470 | 471 | 4. 则 A、B 或者 D 均可通过上述交易触发 W2 转账给 E,而无需任意其它两者的同意,形成重放攻击。 472 | 473 | 可修改 `submitTransactionWithSignatures` 如下来避免这个问题: 474 | 475 | ``` 476 | function submitTransactionWithSignatures(address destination, uint value, bytes data, uint nonce, uint8[] v, bytes32[] rs) 477 | external 478 | returns (bytes32 transactionHash) 479 | { 480 | transactionHash = addTransaction(destination, value, data, nonce); 481 | bytes memory prefix = "\x19Ethereum Signed Message:\n32"; 482 | bytes32 prefixedHash = sha3(prefix, this, transactionHash); 483 | confirmTransactionWithSignatures(transactionHash, prefixedHash, v, rs); 484 | } 485 | ``` 486 | 487 | 在 prefixedHash 中添加了 this, 即该合约的地址,在 console 中签名时,将合约地址追加进去,即: 488 | 489 | ``` 490 | > multiSigContractAddress = "0x9f94a748d21961f9dafc96274834793e16a4fac0" 491 | "0x9f94a748d21961f9dafc96274834793e16a4fac0" 492 | > personal.unlockAccount(eth.coinbase) 493 | Unlock account 0xa3ac96fbe4b0dce5f6f89a715ca00934d68f6c37 494 | Passphrase: 495 | true 496 | > web3.eth.sign(eth.coinbase, multiSigContractAddress + txHash.slice(2, 66)) 497 | "0xe7df062a2877a7805dd81256b6e1aa5ba7c4737b262c06197a58ced9b9099a786493ee088622365b5570142863ac2b8140720faf7bb81b21ca7906113169936d1c" 498 | ``` 499 | 500 | 由于合约地址不同,可避免重放攻击问题。 501 | -------------------------------------------------------------------------------- /how-to-sign-and-hash-tx.md: -------------------------------------------------------------------------------- 1 | ### 交易签名以及哈希值的计算 2 | 3 | **作者:李康** 4 | 5 | **参考 [eip155](https://github.com/ethereum/eips/issues/155)** 6 | 7 | 在以太坊中,tx = [nonce, gasPrice, gas, to, value, data, v, r, s],其中 eip155 之前,(r, s, v) = sign(sha3(rlp.encode(tx[:-3]))),eip155 之后,(r, s, v) = sign(sha3(rlp.encode(tx[:-3] + [chainId, 0, 0]))),hash(tx) = keccak256(rlp.encode(tx));使用 [pyethereum](https://github.com/ethereum/pyethereum) 库计算交易哈希值: 8 | 9 | ``` 10 | In [1]: import rlp 11 | 12 | In [2]: from ethereum import transactions, utils 13 | 14 | In [3]: nonce = 1 15 | 16 | In [4]: gasPrice = 50 * pow(10, 9) 17 | 18 | In [5]: to = "2525252525252525252525252525252525252525" 19 | 20 | In [6]: private_key = "5757575757575757575757575757575757575757575757575757575757575757" 21 | 22 | In [7]: gas = 200000 23 | 24 | In [8]: data = '' 25 | 26 | In [9]: value = pow(10, 18) 27 | 28 | In [12]: tx = transactions.Transaction(nonce, gasPrice, gas, to, value, data, 1, 0, 0) 29 | 30 | In [13]: sig = tx.sign(private_key) 31 | 32 | In [14]: tx.v, tx.r, tx.s 33 | Out[14]: 34 | (28, 35 | 101912087796757166702718993441740664977869670498269350823539550609489070477520L, 36 | 7977227560646687443041581723152133713969920118482322767730773813537862499586L) 37 | 38 | In [15]: tx.hash 39 | Out[15]: '\xa5_\xd2\xbf\xce1\xc1\xe5\xb6u\xd9\x19`\xfa~\x9c\xd7\x8a\xa8Z\x8d\x82\x91\xd7\x19\x03\xbc2KfJ\xf7' 40 | 41 | In [16]: tx.hash.encode("hex") 42 | Out[16]: 'a55fd2bfce31c1e5b675d91960fa7e9cd78aa85a8d8291d71903bc324b664af7' 43 | 44 | In [17]: utils.sha3(rlp.encode(tx)).encode("hex") 45 | Out[17]: 'a55fd2bfce31c1e5b675d91960fa7e9cd78aa85a8d8291d71903bc324b664af7' 46 | 47 | // eip155 提议增加chainId 48 | In [18]: sig = tx.sign(private_key, network_id=1) 49 | 50 | In [19]: sig.v, sig.r, sig.s 51 | Out[19]: 52 | (38, 53 | 107391112126426437763509853950770984364199519236549915339101439338348479768362L, 54 | 18088074028265079293467323141260314974426590311632536364616548465792139408866L) 55 | 56 | In [20]: tx.hash.encode("hex") 57 | Out[20]: 'dbe9621bb3c5e3ae8553b0cdb2885f6fd3604093ecd01f6c596fcdc157c8253e' 58 | 59 | In [21]: utils.sha3(rlp.encode(tx)).encode("hex") 60 | Out[21]: 'dbe9621bb3c5e3ae8553b0cdb2885f6fd3604093ecd01f6c596fcdc157c8253e' 61 | 62 | // 得到公钥地址 63 | In [23]: tx.sender.encode("hex") 64 | Out[23]: '8b75db8a458f25a728dcbc237c10e89cea11d176' 65 | 66 | // privtopub 是从 bitcoin 中库导入的函数,得出的公钥是未压缩的,前缀是 04,后面跟着两个 256 bit,分别代表公钥的 x 坐标与 y 坐标,如附图所示 67 | In [31]: x, y = utils.privtopub(private_key.decode("hex")) 68 | In [32]: utils.sha3(utils.encode_int32(x) + utils.encode_int32(y))[12:].encode("hex") 69 | Out[32]: '8b75db8a458f25a728dcbc237c10e89cea11d176' 70 | 71 | // privtoaddr 中使用到了 privtopub 函数 72 | In [33]: utils.privtoaddr(private_key).encode("hex") 73 | Out[33]: '8b75db8a458f25a728dcbc237c10e89cea11d176' 74 | ``` 75 | 76 | **Note1:** 在 web3.js 提供的 personal.sign 以及 eth.sign API 中,会对将要进行签名的数据 msg 进行变化:keccak256("\x19Ethereum Signed Message:\n" + len(msg) + msg),见 https://github.com/ethereum/EIPs/issues/191 77 | 78 | **Note2:** 也可使用 web3.js 库中的 eth.sendTransaction API,具体程序见 https://github.com/lightning-li/blockchain-knife/blob/master/ethereum_tx_hash.js 79 | 80 | ---- 81 | 82 | ![比特币未压缩公钥示意图](/images/2017/06/bitcoin_pubkey.png) 83 | 84 | ![以太坊交易签名变化图](/images/2017/10/eth-sign-tx-hash.png) 85 | -------------------------------------------------------------------------------- /images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightning-li/ethereum-learn/0a6a74bebf50dc325cd14ee8b2492dc323a4d408/images/.DS_Store -------------------------------------------------------------------------------- /images/2016/10/CodeCogsEqn-19.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightning-li/ethereum-learn/0a6a74bebf50dc325cd14ee8b2492dc323a4d408/images/2016/10/CodeCogsEqn-19.gif -------------------------------------------------------------------------------- /images/2016/10/borgcube.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightning-li/ethereum-learn/0a6a74bebf50dc325cd14ee8b2492dc323a4d408/images/2016/10/borgcube.jpg -------------------------------------------------------------------------------- /images/2016/10/fourpoints.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightning-li/ethereum-learn/0a6a74bebf50dc325cd14ee8b2492dc323a4d408/images/2016/10/fourpoints.png -------------------------------------------------------------------------------- /images/2016/10/haha.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightning-li/ethereum-learn/0a6a74bebf50dc325cd14ee8b2492dc323a4d408/images/2016/10/haha.jpg -------------------------------------------------------------------------------- /images/2016/10/merkle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightning-li/ethereum-learn/0a6a74bebf50dc325cd14ee8b2492dc323a4d408/images/2016/10/merkle.png -------------------------------------------------------------------------------- /images/2016/10/merkle_verify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightning-li/ethereum-learn/0a6a74bebf50dc325cd14ee8b2492dc323a4d408/images/2016/10/merkle_verify.png -------------------------------------------------------------------------------- /images/2016/10/threepoints.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightning-li/ethereum-learn/0a6a74bebf50dc325cd14ee8b2492dc323a4d408/images/2016/10/threepoints.png -------------------------------------------------------------------------------- /images/2016/10/twopoints.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightning-li/ethereum-learn/0a6a74bebf50dc325cd14ee8b2492dc323a4d408/images/2016/10/twopoints.png -------------------------------------------------------------------------------- /images/2017/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightning-li/ethereum-learn/0a6a74bebf50dc325cd14ee8b2492dc323a4d408/images/2017/.DS_Store -------------------------------------------------------------------------------- /images/2017/05/deploy-contract-in-console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightning-li/ethereum-learn/0a6a74bebf50dc325cd14ee8b2492dc323a4d408/images/2017/05/deploy-contract-in-console.png -------------------------------------------------------------------------------- /images/2017/05/simple_contract.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightning-li/ethereum-learn/0a6a74bebf50dc325cd14ee8b2492dc323a4d408/images/2017/05/simple_contract.png -------------------------------------------------------------------------------- /images/2017/06/bitcoin_pubkey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightning-li/ethereum-learn/0a6a74bebf50dc325cd14ee8b2492dc323a4d408/images/2017/06/bitcoin_pubkey.png -------------------------------------------------------------------------------- /images/2017/10/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightning-li/ethereum-learn/0a6a74bebf50dc325cd14ee8b2492dc323a4d408/images/2017/10/3.png -------------------------------------------------------------------------------- /images/2017/10/eth-sign-tx-hash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightning-li/ethereum-learn/0a6a74bebf50dc325cd14ee8b2492dc323a4d408/images/2017/10/eth-sign-tx-hash.png -------------------------------------------------------------------------------- /images/2017/10/image001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightning-li/ethereum-learn/0a6a74bebf50dc325cd14ee8b2492dc323a4d408/images/2017/10/image001.png -------------------------------------------------------------------------------- /images/2017/10/image003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightning-li/ethereum-learn/0a6a74bebf50dc325cd14ee8b2492dc323a4d408/images/2017/10/image003.png -------------------------------------------------------------------------------- /images/2017/10/image005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightning-li/ethereum-learn/0a6a74bebf50dc325cd14ee8b2492dc323a4d408/images/2017/10/image005.png -------------------------------------------------------------------------------- /images/2017/10/image009.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightning-li/ethereum-learn/0a6a74bebf50dc325cd14ee8b2492dc323a4d408/images/2017/10/image009.png -------------------------------------------------------------------------------- /images/2017/10/image011.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightning-li/ethereum-learn/0a6a74bebf50dc325cd14ee8b2492dc323a4d408/images/2017/10/image011.png -------------------------------------------------------------------------------- /images/2017/10/image013.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightning-li/ethereum-learn/0a6a74bebf50dc325cd14ee8b2492dc323a4d408/images/2017/10/image013.png -------------------------------------------------------------------------------- /images/2017/10/image015.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightning-li/ethereum-learn/0a6a74bebf50dc325cd14ee8b2492dc323a4d408/images/2017/10/image015.png -------------------------------------------------------------------------------- /images/2017/10/image017.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightning-li/ethereum-learn/0a6a74bebf50dc325cd14ee8b2492dc323a4d408/images/2017/10/image017.png -------------------------------------------------------------------------------- /images/2017/10/image019.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightning-li/ethereum-learn/0a6a74bebf50dc325cd14ee8b2492dc323a4d408/images/2017/10/image019.png -------------------------------------------------------------------------------- /images/2017/10/image021.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightning-li/ethereum-learn/0a6a74bebf50dc325cd14ee8b2492dc323a4d408/images/2017/10/image021.png -------------------------------------------------------------------------------- /remix-local-env.md: -------------------------------------------------------------------------------- 1 | #### remix 本地环境搭建 2 | 3 | 作者:李康 4 | 5 | 由于一些原因,我们可能不能很顺畅地访问 remix IDE 的[在线版本](https://remix.ethereum.org/),因此我们需要搭建 remix 的本地版,代码仓库在 https://github.com/ethereum/browser-solidity . 6 | 7 | 我们下载 gh-pages 分支,然后修改 build/app.js 文件,找到调用 loadVersion 函数的位置,此函数用于自动加载选择好的 solidity 编译器版本,我们避免在线加载 github 上的 solidity 编译器,因此将 loadVersion 函数的第一个参数改为 "buildin",意味着加载本地下载好的 solidity 编译器。 8 | 9 | 我们从 https://github.com/ethereum/browser-solidity 处下载的代码仓库包含 soljson.js,但是加载该文件会出现错误(原因不详)。因此我们要手动下载 solidity 的编译器,首先访问 https://ethereum.github.io/solc-bin/bin/list.json ,得到当前支持的编译版本以及下载路径(path 字段),然后访问 **https://ethereum.github.io/solc-bin/bin/ + path** 处(例如 https://ethereum.github.io/solc-bin/bin/soljson-v0.4.17+commit.bdeb9e52.js )下载想要的版本的编译器,然后将其重命名为 soljson.js,覆盖原先的文件。此时直接访问 index.html 就可以访问本地 remix 编译环境了。 10 | -------------------------------------------------------------------------------- /smart-contract-battle1.md: -------------------------------------------------------------------------------- 1 | #### 智能合约实战(一) 2 | 3 | **作者:李康** 4 | 5 | ##### 1. 搭建以太坊私链 6 | 7 | 使用 `geth` 客户端搭建以太坊私链,首先定制私链的创世区块文件 -- `genesis.json` 文件,如下: 8 | 9 | ``` 10 | { 11 | "config": { 12 | "chainId": 15, 13 | "homesteadBlock": 0, 14 | "eip155Block": 0, 15 | "eip158Block": 0 16 | }, 17 | "nonce": "0x0000000000000042", 18 | "timestamp": "0x00", 19 | "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", 20 | "extraData": "0x00", 21 | "gasLimit": "0x08000000", 22 | "difficulty": "0x020000", 23 | "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", 24 | "coinbase": "0x3333333333333333333333333333333333333333", 25 | "alloc": { 26 | } 27 | } 28 | ``` 29 | 然后使用 `geth --datadir YourDataDir init genesis.json` 初始化私链工作/数据目录。 30 | 31 | 可使用如下命令启动以太坊客户端: 32 | 33 | `alias lkgeth='geth --verbosity 6 --identity "likang" --rpc --rpcport "20000" --etherbase "0x2e64a19086f352f74c28fd48e5eb19aa78cd66de" --rpccorsdomain "*" --datadir "/Users/likang/private_ethereum/likang_ethereum" --port "40000" --nodiscover --rpcapi "db,eth,net,web3" --networkid 2000 --nat "any" console 2>> "/Users/likang/private_ethereum/likang_ethereum/lk.log"'` 34 | 35 | `alias ssgeth='geth --verbosity 6 --identity "ss" --rpc --rpcport "20001" --etherbase "0xfacb6077bc44242a8d52457614785a5768de7655" --rpccorsdomain "*" --datadir "/Users/likang/private_ethereum/ss_ethereum" --port "40001" --nodiscover --rpcapi "db,eth,net,web3" --networkid 2000 --nat "any" console 2>> "/Users/likang/private_ethereum/ss_ethereum/ss.log"'` 36 | 37 | 至此,一个私链网络搭建完成~ 38 | 39 | 具体详见 https://media.consensys.net/how-to-build-a-private-ethereum-blockchain-fbf3904f337 40 | 41 | ##### 2. geth 常用命令 42 | 43 | 可以通过 `IPC`、`RPC`、`Console` 三种形式与 `geth` 进行交互,其中 `IPC` 与 `RPC` 使用 JSON-RPC 协议,JSON-RPC 是一种无状态、轻量级的远程过程调用协议,它对传输协议透明,可以在同一个进程里使用,也可以通过 socket 或者 HTTP 或者其它各种各样的消息传递环境,它使用 JSON ([RFC 4627](http://www.ietf.org/rfc/rfc4627.txt)) 作为数据格式。如: 44 | 45 | ``` 46 | Console : eth.blockNumber // return 196 47 | 48 | IPC : echo '{"jsonrpc" : "2.0", "method" : "eth_blockNumber", "params" : [], "id" : 1}' | nc -U geth.ipc // return {"jsonrpc":"2.0","id":1,"result":"0xc4"} 49 | 50 | HTTP : curl -X POST --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":74}' localhost:20000 //return {"jsonrpc":"2.0","id":74,"result":"0xc4"} 51 | ``` 52 | 53 | 详细命令见[官方 wiki](https://github.com/ethereum/wiki/wiki/JSON-RPC) 54 | 55 | ##### 3. 智能合约实战 56 | **注意: 后面我将智能合约状态变量的值设置为 32,32 对应的字符是空格,导致随后读取值的时候,读出来是空格,效果不明显~ 可以换成 97 等,最后读出来的值是 a** 57 | 58 | 编写简单智能合约 59 | 60 | ``` 61 | pragma solidity ^0.4.8; 62 | 63 | contract SimpleStorage { 64 | uint public storedData; 65 | mapping (address => uint) haha; 66 | function set(uint a) { 67 | storedData = a; 68 | haha[msg.sender] = a; 69 | } 70 | function del() { 71 | delete storedData; 72 | delete haha[msg.sender]; 73 | } 74 | } 75 | ``` 76 | 77 | 将该合约放入在线编译器 https://remix.ethereum.org 中,在 Contract details 处得到关于该合约的字节码、接口等信息: 78 | 79 | ``` 80 | Bytecode : 6060604052341561000c57fe5b5b6101768061001c6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632a1afcd91461005157806360fe47b114610077578063b6588ffd14610097575bfe5b341561005957fe5b6100616100a9565b6040518082815260200191505060405180910390f35b341561007f57fe5b61009560048080359060200190919050506100af565b005b341561009f57fe5b6100a76100fe565b005b60005481565b8060008190555080600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b50565b600060009055600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600090555b5600a165627a7a7230582028790275dfe0683f0a565b1eb14cd2c285165f67a6706396005047f7338ac0170029 81 | 82 | Interface : [{"constant":true,"inputs":[],"name":"storedData","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"del","outputs":[],"payable":false,"type":"function"}] 83 | 84 | Web3 deploy : 85 | 86 | var ballot_sol_simplestorageContract = web3.eth.contract([{"constant":true,"inputs":[],"name":"storedData","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"del","outputs":[],"payable":false,"type":"function"}]); 87 | 88 | var ballot_sol_simplestorage = ballot_sol_simplestorageContract.new( 89 | { 90 | from: web3.eth.accounts[0], 91 | data: '0x6060604052341561000c57fe5b5b6101768061001c6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632a1afcd91461005157806360fe47b114610077578063b6588ffd14610097575bfe5b341561005957fe5b6100616100a9565b6040518082815260200191505060405180910390f35b341561007f57fe5b61009560048080359060200190919050506100af565b005b341561009f57fe5b6100a76100fe565b005b60005481565b8060008190555080600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b50565b600060009055600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600090555b5600a165627a7a7230582028790275dfe0683f0a565b1eb14cd2c285165f67a6706396005047f7338ac0170029', 92 | gas: '4700000' 93 | }, function (e, contract){ 94 | console.log(e, contract); 95 | if (typeof contract.address !== 'undefined') { 96 | console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash); 97 | } 98 | 99 | Runtime Bytecode: 60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632a1afcd91461005157806360fe47b114610077578063b6588ffd14610097575bfe5b341561005957fe5b6100616100a9565b6040518082815260200191505060405180910390f35b341561007f57fe5b61009560048080359060200190919050506100af565b005b341561009f57fe5b6100a76100fe565b005b60005481565b8060008190555080600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b50565b600060009055600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600090555b5600a165627a7a7230582028790275dfe0683f0a565b1eb14cd2c285165f67a6706396005047f7338ac0170029 100 | 101 | Functions: 102 | b6588ffd del() 103 | 60fe47b1 set(uint256) 104 | 2a1afcd9 storedData() 105 | 106 | ``` 107 | 108 | 也可以通过本地 solidity 编译器对该智能合约进行编译,具体过程见 https://ethereum.stackexchange.com/questions/15435/how-to-compile-solidity-contracts-with-geth-v1-6 109 | 110 | 有了以上信息,就可以在 console 中部署智能合约了,如下图所示: 111 | 112 | ![deploy-contract-in-console](images/2017/05/deploy-contract-in-console.png) 113 | 114 | 当控制台输出 `Contract mined! address: 0x45e7e78f91e1cd68ed5cdd800788855f03d4c79d transactionHash: 0x67103acb2aed57f5d5146c96166815608b38514493fcb5fb81fea5ba619d7dc8`,证明给合约部署交易已被矿工打包并写入区块链,如下: 115 | 116 | ``` 117 | > eth.getCode("0x45e7e78f91e1cd68ed5cdd800788855f03d4c79d") 118 | "0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632a1afcd91461005157806360fe47b114610077578063b6588ffd14610097575bfe5b341561005957fe5b6100616100a9565b6040518082815260200191505060405180910390f35b341561007f57fe5b61009560048080359060200190919050506100af565b005b341561009f57fe5b6100a76100fe565b005b60005481565b8060008190555080600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b50565b600060009055600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600090555b5600a165627a7a7230582028790275dfe0683f0a565b1eb14cd2c285165f67a6706396005047f7338ac0170029" 119 | ``` 120 | 121 | 这与 remix 在线编译器给出的 Runtime Bytecode 相同,注意,我们在部署智能合约时使用的 data 字段是 Bytecode 而不是 Runtime Bytecode,这是因为以太坊上的节点收到合约部署交易时,运行 data 指定的字节码,执行完毕后返回的代码才是真正的合约代码,即 Runtime Bytecode。 122 | 123 | 接下来,调用智能合约,并解析智能合约数据: 124 | 125 | ``` 126 | // eth.call 会在本地节点模拟执行智能合约,不会产生调用交易以及消耗燃料,也不会将数据写入区块链,因此 getStorageAt 不会得到修改过后的值。 127 | > eth.call({to : "0x45e7e78f91e1cd68ed5cdd800788855f03d4c79d", data : "0x60fe47b10000000000000000000000000000000000000000000000000000000000000020"}) 128 | "0x" 129 | > eth.getStorageAt("0x45e7e78f91e1cd68ed5cdd800788855f03d4c79d", "0x0") 130 | "0x0000000000000000000000000000000000000000000000000000000000000000" 131 | 132 | // 发起一笔合约调用交易 133 | > personal.unlockAccount(eth.coinbase) 134 | Unlock account 0x2e64a19086f352f74c28fd48e5eb19aa78cd66de 135 | Passphrase: 136 | true 137 | > eth.sendTransaction({from : eth.coinbase, to : "0x45e7e78f91e1cd68ed5cdd800788855f03d4c79d", data : "0x60fe47b10000000000000000000000000000000000000000000000000000000000000020"}) 138 | 139 | "0xf571c5a284cddb6cb6d430ef9bb6c52b47edd794b06afee71d7489d21f52413a" 140 | > txpool.status 141 | { 142 | pending: 1, 143 | queued: 0 144 | } 145 | > miner.start() 146 | null 147 | > eth.blockNumber 148 | 223 149 | > eth.blockNumber 150 | 224 151 | > miner.stop() 152 | true 153 | // 获取 storedData 的值 154 | > eth.getStorageAt("0x45e7e78f91e1cd68ed5cdd800788855f03d4c79d", "0x0") 155 | "0x0000000000000000000000000000000000000000000000000000000000000020" 156 | 157 | // 获取 haha[eth.coinbase] 对应的值,下面有详细解释 158 | > var key = "0000000000000000000000002e64a19086f352f74c28fd48e5eb19aa78cd66de" + "0000000000000000000000000000000000000000000000000000000000000001" 159 | > web3.sha3(key, {"encoding": "hex"}) 160 | "0x621bc01d6f4018e84f13e9bf6fe833dcaaef01fd1865261c8162d206c9fd5d56" 161 | 162 | > web3.eth.getStorageAt("0x45e7e78f91e1cd68ed5cdd800788855f03d4c79d", "0x621bc01d6f4018e84f13e9bf6fe833dcaaef01fd1865261c8162d206c9fd5d56") 163 | "0x0000000000000000000000000000000000000000000000000000000000000020" 164 | ``` 165 | 166 | 调用智能合约交易的 data 字段按照如下规则得出: 167 | keccak(function definition) ==> web3.sha3("set(uint256)"), 取前四个字节,即 8 个 16 进制 60fe47b1 168 | 169 | ``` 170 | > web3.sha3("set(uint256)") 171 | "0x60fe47b16ed402aae66ca03d2bfc51478ee897c26a1158669c7058d5f24898f4" 172 | ``` 173 | 174 | 然后,将传进函数的参数补至 256 位,这里我们把值设置为 32,即 0000000000000000000000000000000000000000000000000000000000000020,所以 data = "0x" + "60fe47b1" + "0000000000000000000000000000000000000000000000000000000000000020" 175 | 176 | solidity 中每一个状态变量都会在区块链中永久存储,我们可以通过 eth.getStorageAt 函数来得到相关状态变量的值,状态变量在区块链中的存储规则如下: 177 | 178 | 变量按照合约中声明的顺序,位置 position 依次加 1,从 0 开始。 179 | 对于 mapping 等复杂类型,其中变量的 position 较为复杂,例如,mapping 中,key 的 position 为 keccack(LeftPad32(key, 0), LeftPad32(map position, 0)),即把 key 的值向左补 0 扩充至 256 位,将 map position(这里 map 的 position 是 1)向左补 0 扩充至 256 位,然后将两部分串接起来,做 keccak256 哈希运算,得到的哈希值即为 key 对应的 position。 180 | 181 | 对状态变量的 position 再次进行哈希,即得到在区块链中 storage Trie 的 key : 182 | 183 | ``` 184 | > web3.sha3("0000000000000000000000000000000000000000000000000000000000000000", {"encoding" : "hex"}) 185 | "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563" 186 | 187 | > web3.sha3("621bc01d6f4018e84f13e9bf6fe833dcaaef01fd1865261c8162d206c9fd5d56", {"encoding" : "hex"}) 188 | "0xc856d73c44a1a222a93133fb3eafcff7f7dfe7faecfbbf59e52e0eef953fdb9f" 189 | ``` 190 | 191 | 我们可以对区块中保存的 stateRoot 进行解析,得到所有的账户信息,再根据合约账户中的 storageRoot 进行解析,得到合约账户存储的所有数据,为此,我编写了一小段脚本 detect_internal_storage.py,输入根哈希值,得到所有叶子节点信息,代码如下所示: 192 | 193 | ``` 194 | #!/usr/bin/env python 195 | # coding=utf-8 196 | 197 | import leveldb 198 | from ethereum import utils 199 | import rlp 200 | import sys 201 | 202 | db = leveldb.LevelDB("/Users/likang/private_ethereum/ss_ethereum/geth/chaindata") 203 | 204 | accountHash2content = {} 205 | 206 | def getAccountInfoByStateRoot(st, key) : 207 | #print "root : " + st.encode("hex") 208 | root_node = rlp.decode(db.Get(st)) 209 | 210 | if len(root_node) == 2 : 211 | if root_node[0][0].encode("hex")[0] == "2" : 212 | key += root_node[0].encode("hex")[2:] 213 | accountHash2content[key] = rlp.decode(root_node[1]) 214 | #print key 215 | return 216 | elif root_node[0][0].encode("hex")[0] == "3" : 217 | key += root_node[0].encode("hex")[1:] 218 | #print key 219 | accountHash2content[key] = rlp.decode(root_node[1]) 220 | return 221 | else : 222 | if root_node[0][0].encode("hex")[0] == "0" : 223 | key += root_node[0].encode("hex")[2:] 224 | else : 225 | key += root_node[0].encode("hex")[1:] 226 | 227 | getAccountInfoByStateRoot(root_node[1], key) 228 | else : 229 | for i in range(10) : 230 | if root_node[i] != "" : 231 | getAccountInfoByStateRoot(root_node[i], key + str(i)) 232 | for i in range(6): 233 | if root_node[10 + i] != "" : 234 | getAccountInfoByStateRoot(root_node[10 + i], key + chr(97 + i)) 235 | if root_node[16] != "" : 236 | getAccountInfoByStateRoot(root_node[16], key) 237 | 238 | if __name__ == "__main__" : 239 | 240 | getAccountInfoByStateRoot(sys.argv[1].decode("hex"), "") 241 | for key in accountHash2content : 242 | print key + " : ", 243 | print accountHash2content[key] 244 | ``` 245 | 246 | 成功运行上述脚本需要安装 [pyethereum](https://github.com/ethereum/pyethereum) 247 | 以及 rlp、leveldb 等工具 248 | 249 | ``` 250 | > eth.getBlock(eth.blockNumber) 251 | { 252 | difficulty: 131456, 253 | extraData: "0xd683010601846765746885676f312e378664617277696e", 254 | gasLimit: 106684369, 255 | gasUsed: 0, 256 | hash: "0xe2b0a1ce5b060db14c523f32fdb219755f7e8d8fb7559333f33cbd8602eb5fde", 257 | logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 258 | miner: "0x2e64a19086f352f74c28fd48e5eb19aa78cd66de", 259 | mixHash: "0x432257401a83d3b0e8c7e11b62de3749bb0248d90cb5de7952d334b5a3cdaf91", 260 | nonce: "0x2289a73d6cf70dfb", 261 | number: 235, 262 | parentHash: "0x9325d8b67934fb9c1a46a6741a78b57bb14e48074c61044004fcf2a315f23a28", 263 | receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", 264 | sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", 265 | size: 536, 266 | stateRoot: "0xe9586fa791e60f6a1c424f55628d25d059082504763cdee6adf8388655c32cf8", 267 | timestamp: 1494233287, 268 | totalDifficulty: 31051136, 269 | transactions: [], 270 | transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", 271 | uncles: [] 272 | } 273 | ``` 274 | 上述命令得到最新区块的信息,根据其中的 stateRoot 得到所有账户信息 : 275 | 276 | ``` 277 | ➜ /Users/likang/private_ethereum >>python detect_internal_storage.py e9586fa791e60f6a1c424f55628d25d059082504763cdee6adf8388655c32cf8 278 | 279 | cd8e455fd822175c870454e876414a4f1da2c531e377c908312b266ee3685a48 : ['\x01', '', '\x02G\xab\t\x83I\xab\xbe?\x94\x80\xa8J1\x8bK\xd6\x91\x15\xcf\x8c\xfb\xb9\x07\x1b|U\xc8\x96\x1f\xb8\xd0', "/j+)xf\xaa6\xc0\x8a\xe3c\xdf\x86#4s\xec\x00\xaf\xe7\xa2'\xf1\xd3\xcc\xa7\xb9\xffv\xd2>"] 280 | 281 | 23ce1bb14b8dd1a4717ea90b75b7f4021debafaab9ca04fcf2b10d22f66cf88f : ['\x01', '', 'q\xd5\xe8\xb3\x9a\xbb#D\xb2^D\xf7Y3\x90\xecU\xa6\xb7\x82a\xf2,\x00E\xe56$\x17 [\xd6', "g\xf4C\x88\xe3'_M\xcb\xff\xd6\xef"]" 293 | 294 | 其中 "\x01" 代表该账户的 nonce 值是 1,合约账户的 nonce 值只有在该合约创建另一个合约时,才会加 1,初始由于该合约账户创建了自己,因此 nonce 值为 1。第二个元素''代表账户的 balance,该合约账户的 balance 为 0。第三个元素代表 storageRoot,第四个元素代表合约代码的哈希值。 295 | 296 | 我们根据 storageRoot 来获取所有变量的 key、value,: 297 | 298 | ``` 299 | > '\x02G\xab\t\x83I\xab\xbe?\x94\x80\xa8J1\x8bK\xd6\x91\x15\xcf\x8c\xfb\xb9\x07\x1b|U\xc8\x96\x1f\xb8\xd0'.encode("hex") 300 | '0247ab098349abbe3f9480a84a318b4bd69115cf8cfbb9071b7c55c8961fb8d0' 301 | 302 | ``` 303 | 304 | ``` 305 | ➜ /Users/likang/private_ethereum >>python detect_internal_storage.py '0247ab098349abbe3f9480a84a318b4bd69115cf8cfbb9071b7c55c8961fb8d0' 306 | 290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563 : 307 | 308 | c856d73c44a1a222a93133fb3eafcff7f7dfe7faecfbbf59e52e0eef953fdb9f : 309 | ``` 310 | 311 | 我犯了一个错误,由于我设置的值是 32,对应的字符是空格,导致显示出的是空格,这不是错误。。由于上面都是按照 32 写的,我就懒得改了。。 312 | 313 | 调用合约的 del 函数: 314 | 315 | ``` 316 | > personal.unlockAccount(eth.coinbase) 317 | Unlock account 0x2e64a19086f352f74c28fd48e5eb19aa78cd66de 318 | Passphrase: 319 | true 320 | > eth.sendTransaction({from : eth.coinbase, to : "0x45e7e78f91e1cd68ed5cdd800788855f03d4c79d", data : "0xb6588ffd"}) 321 | > miner.start() 322 | > miner.stop() 323 | ``` 324 | 325 | 调用完成之后,使用 detect_internal_storage.py 脚本按照上述方法获取该合约账户的 storageRoot,此时 storageRoot 为'V\xe8\x1f\x17\x1b\xccU\xa6\xff\x83E\xe6\x92\xc0\xf8n[H\xe0\x1b\x99l\xad\xc0\x01b/\xb5\xe3c\xb4!',这是存储为空时对应的根哈希值,如果你留意一下,会发现所有非合约账户的 storageRoot 均一样。 326 | 327 | --- 328 | 329 | cpp-ethereum 搭建: 330 | 331 | ``` 332 | { 333 | "sealEngine": "Ethash", 334 | "params": { 335 | "accountStartNonce": "0x00", 336 | "homsteadForkBlock": "0x118c30", 337 | "daoHardforkBlock": "0x1d4c00", 338 | "EIP150ForkBlock": "0x259518", 339 | "EIP158ForkBlock": "0x28d138", 340 | "metropolisForkBlock": "0xffffffffffffffffff", 341 | "networkID" : "0x07d0", 342 | "chainID": "0x01", 343 | "maximumExtraDataSize": "0x20", 344 | "tieBreakingGas": false, 345 | "minGasLimit": "0x1388", 346 | "maxGasLimit": "7fffffffffffffff", 347 | "gasLimitBoundDivisor": "0x0400", 348 | "minimumDifficulty": "0x020000", 349 | "difficultyBoundDivisor": "0x0800", 350 | "durationLimit": "0x0d", 351 | "blockReward": "0x4563918244F40000" 352 | }, 353 | "genesis": { 354 | "nonce": "0x0000000000000042", 355 | "difficulty": "0x020000", 356 | "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", 357 | "author": "0x0000000000000000000000000000000000000000", 358 | "timestamp": "0x00", 359 | "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", 360 | "extraData": "0x00", 361 | "gasLimit": "0x08000000" 362 | }, 363 | "accounts": { 364 | "0000000000000000000000000000000000000001": { "precompiled": { "name": "ecrecover", "linear": { "base": 3000, "word": 0 } } }, 365 | "0000000000000000000000000000000000000002": { "precompiled": { "name": "sha256", "linear": { "base": 60, "word": 12 } } }, 366 | "0000000000000000000000000000000000000003": { "precompiled": { "name": "ripemd160", "linear": { "base": 600, "word": 120 } } }, 367 | "0000000000000000000000000000000000000004": { "precompiled": { "name": "identity", "linear": { "base": 15, "word": 3 } } }, 368 | "0000000000000000000000000000000000000005": { "precompiled": { "name": "modexp", "startBlock" : "0x2dc6c0" } } 369 | "0000000000000000000000000000000000000006": { "precompiled": { "name": "alt_bn128_G1_add", "startBlock" : "0x2dc6c0", "linear": { "base": 500, "word": 0 } } }, 370 | "0000000000000000000000000000000000000007": { "precompiled": { "name": "alt_bn128_G1_mul", "startBlock" : "0x2dc6c0", "linear": { "base": 2000, "word": 0 } } }, 371 | "0000000000000000000000000000000000000008": { "precompiled": { "name": "alt_bn128_pairing_product", "startBlock" : "0x2dc6c0" } } 372 | } 373 | } 374 | ``` 375 | 376 | 启动命令:`eth --config cppgenesis.json --no-discovery -j --mining on -v 8 -a 0x8a25ac495ff4366e72e039a9280e0bb990f23760 -C` 377 | -------------------------------------------------------------------------------- /smart-contract-battle2.md: -------------------------------------------------------------------------------- 1 | #### 智能合约实战(二) 2 | 3 | **作者:李康** 4 | 5 | ##### 事件(Event) 6 | 7 | *关于事件的详细信息请阅读 https://media.consensys.net/technical-introduction-to-events-and-logs-in-ethereum-a074d65dd61e* 8 | 9 | 下面是一个简单使用事件机制的智能合约: 10 | 11 | ``` 12 | pragma solidity ^0.4.8; 13 | 14 | contract SimpleStorage { 15 | uint public storedData; 16 | uint public extraData; 17 | mapping (address => uint) public haha; 18 | event valueChanged(address indexed sender, uint indexed a, uint b); 19 | function set(uint a, uint b) returns (uint) { 20 | storedData = a; 21 | haha[msg.sender] = a + b; 22 | valueChanged(msg.sender, a, b); 23 | return a + b; 24 | } 25 | function del() { 26 | delete storedData; 27 | delete haha[msg.sender]; 28 | } 29 | } 30 | ``` 31 | 将该代码粘贴至 https://remix.ethereum.org 得到关于该智能合约的 ABI 等信息,如下: 32 | 33 | ``` 34 | Interface : 35 | [{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"haha","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"a","type":"uint256"},{"name":"b","type":"uint256"}],"name":"set","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"storedData","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"extraData","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"del","outputs":[],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"},{"indexed":true,"name":"a","type":"uint256"},{"indexed":false,"name":"b","type":"uint256"}],"name":"valueChanged","type":"event"}] 36 | 37 | 38 | Web3 deploy : 39 | var ballot_sol_simplestorageContract = web3.eth.contract([{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"haha","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"a","type":"uint256"},{"name":"b","type":"uint256"}],"name":"set","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"storedData","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"extraData","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"del","outputs":[],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"},{"indexed":true,"name":"a","type":"uint256"},{"indexed":false,"name":"b","type":"uint256"}],"name":"valueChanged","type":"event"}]); 40 | var ballot_sol_simplestorage = ballot_sol_simplestorageContract.new( 41 | { 42 | from: web3.eth.accounts[0], 43 | data: '0x6060604052341561000c57fe5b5b6102998061001c6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063107b401f146100675780631ab06ee5146100b15780632a1afcd9146100ee578063609d333414610114578063b6588ffd1461013a575bfe5b341561006f57fe5b61009b600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061014c565b6040518082815260200191505060405180910390f35b34156100b957fe5b6100d86004808035906020019091908035906020019091905050610164565b6040518082815260200191505060405180910390f35b34156100f657fe5b6100fe610215565b6040518082815260200191505060405180910390f35b341561011c57fe5b61012461021b565b6040518082815260200191505060405180910390f35b341561014257fe5b61014a610221565b005b60026020528060005260406000206000915090505481565b60008260008190555081600181905550818301600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550823373ffffffffffffffffffffffffffffffffffffffff167f11ee77a57d176eec0c855796ed2e9f1be7314279895c6cc8a5e823e426b16d10846040518082815260200191505060405180910390a381830190505b92915050565b60005481565b60015481565b600060009055600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600090555b5600a165627a7a72305820d2858f6d255b8be7c65827872a00b70610d3b58074b9face780e483cf8ba37f00029', 44 | gas: '4700000' 45 | }, function (e, contract){ 46 | console.log(e, contract); 47 | if (typeof contract.address !== 'undefined') { 48 | console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash); 49 | } 50 | }) 51 | 52 | Functions : 53 | b6588ffd del() 54 | 609d3334 extraData() 55 | 107b401f haha(address) 56 | 1ab06ee5 set(uint256,uint256) 57 | 2a1afcd9 storedData() 58 | ``` 59 | 60 | 在 geth console 中进行部署该合约: 61 | 62 | ![simple-contract](images/2017/05/simple_contract.png) 63 | 64 | 部署完毕之后,我们来采用与智能合约实战(一)不同的调用智能合约的方法,并进行 valueChanged 事件的监听: 65 | 66 | ``` 67 | > var myContractInstance = ballot_sol_simplestorageContract.at("0xd704c9bc7dd13d894ae1bd0ff9ecb6035e272f05") 68 | undefined 69 | > myContractInstance.storedData() 70 | 0 71 | > myContractInstance.extraData() 72 | 0 73 | //当监听到 sender 为 eth.coinbase 并且 a 的值是 100 时,才会输出相关日志信息 74 | > var myValueChangedEvent = myContractInstance.valueChanged({sender : eth.coinbase, a : 100}) 75 | undefined 76 | > myValueChangedEvent.watch(function(error, res) {if (!error) {console.log("sender : " + res.args.sender + " a : " + res.args.a);} else {console.log(error);}}) 77 | { 78 | callbacks: [function(error, res)], 79 | filterId: "0xf3ccf4fd44e9ddea7780fe9fa35a9a35", 80 | getLogsCallbacks: [], 81 | implementation: { 82 | getLogs: function(), 83 | newFilter: function(), 84 | poll: function(), 85 | uninstallFilter: function() 86 | }, 87 | options: { 88 | address: "0xd704c9bc7dd13d894ae1bd0ff9ecb6035e272f05", 89 | from: undefined, 90 | fromBlock: undefined, 91 | to: undefined, 92 | toBlock: undefined, 93 | topics: ["0x11ee77a57d176eec0c855796ed2e9f1be7314279895c6cc8a5e823e426b16d10", "0x000000000000000000000000a3ac96fbe4b0dce5f6f89a715ca00934d68f6c37", "0x0000000000000000000000000000000000000000000000000000000000000064"] 94 | }, 95 | pollFilters: [], 96 | requestManager: { 97 | polls: { 98 | 0xf3ccf4fd44e9ddea7780fe9fa35a9a35: { 99 | data: {...}, 100 | id: "0xf3ccf4fd44e9ddea7780fe9fa35a9a35", 101 | callback: function(error, messages), 102 | uninstall: function() 103 | } 104 | }, 105 | provider: { 106 | newAccount: function(), 107 | send: function github.com/ethereum/go-ethereum/console.(*bridge).Send-fm(), 108 | sendAsync: function github.com/ethereum/go-ethereum/console.(*bridge).Send-fm(), 109 | sign: function(), 110 | unlockAccount: function() 111 | }, 112 | timeout: {}, 113 | poll: function(), 114 | reset: function(keepIsSyncing), 115 | send: function(data), 116 | sendAsync: function(data, callback), 117 | sendBatch: function(data, callback), 118 | setProvider: function(p), 119 | startPolling: function(data, pollId, callback, uninstall), 120 | stopPolling: function(pollId) 121 | }, 122 | formatter: function(), 123 | get: function(callback), 124 | stopWatching: function(callback), 125 | watch: function(callback) 126 | } 127 | //发送交易来调用合约的 set 函数,因为 a 的值是 16,所以不会输出日志信息 128 | > miner.start() 129 | > myContractInstance.set.sendTransaction(16, 32, {from : eth.coinbase}) 130 | "0x5198bf58f46daf035562129ec5ac3e26c328e202d30a55b0f2be642bccb314fe" 131 | > miner.stop() 132 | true 133 | > myContractInstance.storedData() 134 | 16 135 | > myContractInstance.extraData() 136 | 32 137 | > personal.unlockAccount(eth.coinbase) 138 | Unlock account 0xa3ac96fbe4b0dce5f6f89a715ca00934d68f6c37 139 | Passphrase: 140 | true 141 | > myContractInstance.set.sendTransaction(100, 1, {from : eth.coinbase}) 142 | "0xc79ec4805cba04b95e2632f7dd08ae547dd888072201bf6feabdab2201ad864a" 143 | > miner.start() 144 | null 145 | > sender : 0xa3ac96fbe4b0dce5f6f89a715ca00934d68f6c37 a : 100 146 | > miner.stop() 147 | true 148 | > myContractInstance.storedData() 149 | 100 150 | > myContractInstance.extraData() 151 | 1 152 | //通过交易哈希查询交易以及收据等信息 153 | > eth.getTransactionReceipt("0xc79ec4805cba04b95e2632f7dd08ae547dd888072201bf6feabdab2201ad864a") 154 | { 155 | blockHash: "0x506f5f7bb2d6e0336f58e9f4925256338faf1966a293309188d30602b7066adf", 156 | blockNumber: 251, 157 | contractAddress: null, 158 | cumulativeGasUsed: 38882, 159 | from: "0xa3ac96fbe4b0dce5f6f89a715ca00934d68f6c37", 160 | gasUsed: 38882, 161 | logs: [{ 162 | address: "0xd704c9bc7dd13d894ae1bd0ff9ecb6035e272f05", 163 | blockHash: "0x506f5f7bb2d6e0336f58e9f4925256338faf1966a293309188d30602b7066adf", 164 | blockNumber: 251, 165 | data: "0x0000000000000000000000000000000000000000000000000000000000000001", 166 | logIndex: 0, 167 | removed: false, 168 | topics: ["0x11ee77a57d176eec0c855796ed2e9f1be7314279895c6cc8a5e823e426b16d10", "0x000000000000000000000000a3ac96fbe4b0dce5f6f89a715ca00934d68f6c37", "0x0000000000000000000000000000000000000000000000000000000000000064"], 169 | transactionHash: "0xc79ec4805cba04b95e2632f7dd08ae547dd888072201bf6feabdab2201ad864a", 170 | transactionIndex: 0 171 | }], 172 | logsBloom: "0x00000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000100002010000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000040002000000000000000000000000000000000000000000008000000000000000", 173 | root: "0xf76837d5476d02f39867e5bfb71d8cf9893f71596782ba3305c738baff319683", 174 | to: "0xd704c9bc7dd13d894ae1bd0ff9ecb6035e272f05", 175 | transactionHash: "0xc79ec4805cba04b95e2632f7dd08ae547dd888072201bf6feabdab2201ad864a", 176 | transactionIndex: 0 177 | } 178 | > eth.getTransaction("0xc79ec4805cba04b95e2632f7dd08ae547dd888072201bf6feabdab2201ad864a") 179 | { 180 | blockHash: "0x506f5f7bb2d6e0336f58e9f4925256338faf1966a293309188d30602b7066adf", 181 | blockNumber: 251, 182 | from: "0xa3ac96fbe4b0dce5f6f89a715ca00934d68f6c37", 183 | gas: 90000, 184 | gasPrice: 20000000000, 185 | hash: "0xc79ec4805cba04b95e2632f7dd08ae547dd888072201bf6feabdab2201ad864a", 186 | input: "0x1ab06ee500000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000001", 187 | nonce: 5, 188 | r: "0xb22fc40a72099401249bcb60d0fb63f78e75b17aed4e60e6b642a319f5bb8a6a", 189 | s: "0x5a9719f9d9e29d5d26e332e991c0e4e7822ee74386fef68fbfb298d35381ce9a", 190 | to: "0xd704c9bc7dd13d894ae1bd0ff9ecb6035e272f05", 191 | transactionIndex: 0, 192 | v: "0x41", 193 | value: 0 194 | } 195 | ``` 196 | 交易收据各字段的意思是: 197 | ``` 198 | Returns 199 | 200 | Object - A transaction receipt object, or null when no receipt was found: 201 | 202 | blockHash: String, 32 Bytes - hash of the block where this transaction was in. 203 | blockNumber: Number - block number where this transaction was in. 204 | transactionHash: String, 32 Bytes - hash of the transaction. 205 | transactionIndex: Number - integer of the transactions index position in the block. 206 | from: String, 20 Bytes - address of the sender. 207 | to: String, 20 Bytes - address of the receiver. null when its a contract creation transaction. 208 | cumulativeGasUsed: Number - The total amount of gas used when this transaction was executed in the block. 209 | gasUsed: Number - The amount of gas used by this specific transaction alone. 210 | contractAddress: String - 20 Bytes - The contract address created, if the transaction was a contract creation, otherwise null. 211 | logs: Array - Array of log objects, which this transaction generated. 212 | ``` 213 | 上面获取到的交易收据中有 logs 与 logsBloom 两项,其中 logs 中有 topics,topics 最多有 3 个元素,第一个元素是合约事件函数的 keccak256 哈希值,即 214 | ``` 215 | > web3.sha3("valueChanged(address,uint256,uint256)") 216 | "0x11ee77a57d176eec0c855796ed2e9f1be7314279895c6cc8a5e823e426b16d10" 217 | ``` 218 | 第二个元素是合约事件函数中第一个使用 indexed 关键字标记的 sender,第三个元素是 a,由于 b 未被 indexed 关键字标记(即使被标记,也不好使,因为 topics 只能容纳 3 个元素),所以 b 位于 logs 中的 data 字段。logsBloom 由 logs 中的 address 与 topics 共同决定,详细请看以太坊黄皮书,作用是便于快速查找监听的事件是否在该交易中产生。 219 | 220 | 利用智能合约实战(一)中提到的脚本,获取现在区块链底层存储的数据: 221 | 222 | ``` 223 | > web3.sha3("0xd704c9bc7dd13d894ae1bd0ff9ecb6035e272f05", {"encoding" : "hex"}) 224 | "0xae795730ce8c58473c4c4cdfd0ad09fa4f372ee9e96cb9c395c241111e341569" 225 | ``` 226 | 227 | ``` 228 | ➜ /Users/likang/private_ethereum >>python detect_internal_storage.py ad812fb06fc580a18820c0b44fda887517cc903b4217e83e02c26d8926499a96 229 | 568b1c89f37932c37da09b86b2f54d98967b44cadc58173afcb890af3cbf426d : ['\x01', '', "\xa22,\xea&\xfbg\xb3e9J~\x83\xdeLJ'B\x0c\x10\x15\xf2\xd9\xe8\xb3\xe7!8\xd0\xf5\xde\xdf", '6\x04\x84\xe2\xe9d\x1ba\xe4\xb9D\x86\xd8\xb9H\xe7\xf6I\xab \x8e\xac\xab\x98A-\x92h\x95\xe0o\x15'] 230 | 231 | 727522965583e311a2ddb8b3499955ef767018cc1dcb47922697fc481f31bb82 : ['\x06', 'C}\xd8\x87\xb5\x11T\x00\x00', 'V\xe8\x1f\x17\x1b\xccU\xa6\xff\x83E\xe6\x92\xc0\xf8n[H\xe0\x1b\x99l\xad\xc0\x01b/\xb5\xe3c\xb4!', "\xc5\xd2F\x01\x86\xf7#<\x92~}\xb2\xdc\xc7\x03\xc0\xe5\x00\xb6S\xca\x82';{\xfa\xd8\x04]\x85\xa4p"] 232 | 233 | ae795730ce8c58473c4c4cdfd0ad09fa4f372ee9e96cb9c395c241111e341569 : ['\x01', '', '\xc8D\x02{Lwhf\x03\xcdA\x9f\xd7V\x0f7\xf9\x01HsS\xf7\x8e\xb2Hc\xc6\xe0\x80\x97\xa90', '+\xcdYT\x1b\x84\xae~\x00d\x11\xce3\xccnx\x0f\xe8\xdd\xa2`\xb06\xe6K\x03\xed\xf4\x16\xa8T\xcc'] 234 | 235 | 75cd2892199f9be180f5ae96b181d9cb5aba7118581d1b250cd2849fa6d3771f : ['', '\x8a\xc7#\x04\x89\xe8\x00\x00', 'V\xe8\x1f\x17\x1b\xccU\xa6\xff\x83E\xe6\x92\xc0\xf8n[H\xe0\x1b\x99l\xad\xc0\x01b/\xb5\xe3c\xb4!', "\xc5\xd2F\x01\x86\xf7#<\x92~}\xb2\xdc\xc7\x03\xc0\xe5\x00\xb6S\xca\x82';{\xfa\xd8\x04]\x85\xa4p"] 236 | 237 | ➜ /Users/likang/private_ethereum >>ipython 238 | Python 2.7.13 (default, May 4 2017, 09:45:36) 239 | Type "copyright", "credits" or "license" for more information. 240 | 241 | IPython 5.3.0 -- An enhanced Interactive Python. 242 | ? -> Introduction and overview of IPython's features. 243 | %quickref -> Quick reference. 244 | help -> Python's own help system. 245 | object? -> Details about 'object', use 'object??' for extra details. 246 | 247 | In [1]: '\xc8D\x02{Lwhf\x03\xcdA\x9f\xd7V\x0f7\xf9\x01HsS\xf7\x8e\xb2Hc\xc6\xe0\x80\x97\xa90'.encode("hex") 248 | Out[1]: 'c844027b4c77686603cd419fd7560f37f901487353f78eb24863c6e08097a930' 249 | 250 | ➜ /Users/likang/private_ethereum >>python detect_internal_storage.py c844027b4c77686603cd419fd7560f37f901487353f78eb24863c6e08097a930 251 | 35769f93790013def002a4a48146e8e4fbe475fb2a1b90f50429da4bca166765 : e 252 | 253 | 290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563 : d 254 | 255 | b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6 : 256 | ``` 257 | 上述的 3 个 key 是这样得到的: 258 | ``` 259 | // storedData 的 position 是 0 260 | > web3.sha3("0000000000000000000000000000000000000000000000000000000000000000", {"encoding" : "hex"}) 261 | "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563" 262 | // extraData 的 position 是 1 263 | > web3.sha3("0000000000000000000000000000000000000000000000000000000000000001", {"encoding" : "hex"}) 264 | "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6" 265 | // map 的 position 是 2,且 key 是 eth.coinbase 266 | > eth.coinbase 267 | "0xa3ac96fbe4b0dce5f6f89a715ca00934d68f6c37" 268 | > var left = "000000000000000000000000a3ac96fbe4b0dce5f6f89a715ca00934d68f6c37" 269 | undefined 270 | > var right = "0000000000000000000000000000000000000000000000000000000000000002" 271 | undefined 272 | > web3.sha3(web3.sha3(left+right, {"encoding" : "hex"}), {"encoding" : "hex"}) 273 | "0x35769f93790013def002a4a48146e8e4fbe475fb2a1b90f50429da4bca166765" 274 | ``` 275 | 与预期结果相符~ bingo~ 276 | -------------------------------------------------------------------------------- /smart-contract-battle3.md: -------------------------------------------------------------------------------- 1 | #### 智能合约实战(三) 2 | 3 | **作者:李康** 4 | 5 | **本文主要介绍合约创建合约,合约与合约之间的相互调用,以及转移以太币到账户等操作** 6 | 7 | 以下是一个简单的合约示例: 8 | 9 | ``` 10 | pragma solidity ^0.4.8; 11 | 12 | contract SimpleStorage { 13 | uint public storedData; 14 | uint public extraData; 15 | SimpleStorageCreator creator; 16 | mapping (address => uint) public haha; 17 | event valueChanged(address sender, uint a, uint b); 18 | event fallbackCalled(address caller); 19 | event constructorCalled(address caller); 20 | event constructbySelfCalled(address caller, bytes32 name); 21 | 22 | function () payable{ 23 | fallbackCalled(msg.sender); 24 | } 25 | function SimpleStorage() { 26 | constructorCalled(msg.sender); 27 | creator = SimpleStorageCreator(msg.sender); 28 | } 29 | function set(uint a, uint b) returns (uint) { 30 | storedData = a; 31 | extraData = b; 32 | haha[msg.sender] = a + b; 33 | valueChanged(msg.sender, a, b); 34 | return a + b; 35 | } 36 | function pay() payable { 37 | haha[msg.sender] = msg.value; 38 | } 39 | function constructbySelf(bytes32 name) { 40 | creator.create(name); 41 | constructbySelfCalled(msg.sender, name); 42 | } 43 | function del() { 44 | delete storedData; 45 | delete haha[msg.sender]; 46 | } 47 | } 48 | 49 | contract SimpleStorageCaller { 50 | event RecvEther(address sender, uint value); 51 | 52 | function set(address ssaddress, uint a, uint b) { 53 | SimpleStorage s = SimpleStorage(ssaddress); 54 | s.set(a, b); 55 | 56 | } 57 | function setbyAddress(address ssaddress) { 58 | //can't use hex string to call function 59 | //if (!ssaddress.call.gas(1000000).value(0)("0x1ab06ee500000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020")) 60 | //if (!ssaddress.call.gas(10000000).value(1000000)("0x1b9265b8")) 61 | //call.value default gas is 34050 62 | if (!ssaddress.call.value(0)(bytes4(bytes32(sha3('set(uint256,uint256)'))), 32, 64)) 63 | throw; 64 | if (!ssaddress.call.value(2 ether)("\x1b\x92e\xb8")) 65 | throw; 66 | if (!ssaddress.send(2 ether)) 67 | throw; 68 | } 69 | function recvEther() payable { 70 | RecvEther(msg.sender, msg.value); 71 | } 72 | } 73 | 74 | contract SimpleStorageCreator { 75 | mapping (bytes32 => address) public deployedContracts; 76 | event contractCreated(bytes32 indexed name, address indexed c); 77 | function create(bytes32 name) returns (address ca){ 78 | deployedContracts[name] = new SimpleStorage(); 79 | contractCreated(name, deployedContracts[name]); 80 | return deployedContracts[name]; 81 | } 82 | 83 | } 84 | ``` 85 | 86 | 将其放入[在线编译器](https://remix.ethereum.org/)中,得到合约的 ABI 等信息,在本地私有链网络中部署: 87 | 88 | 1. 首先部署 SimpleStorageCreator 合约,然后调用该合约的 create 函数创建一个 SimpleStorage 合约的实例; 89 | 90 | 2. 接着部署 SimpleStorageCaller 合约,然后调用该合约的 recvEther 函数接收以太币,接着通过调用 set 与 setbyAddress 函数来改变第 1 步中产生的 SimpleStorage 实例的状态; 91 | 92 | 3. 最后使用生成的 SimpleStorage 实例调用 SimpleStorageCreator 再次生成一个新的 SimpleStorage 实例 93 | 94 | ``` 95 | // deploy SimpleStorageCreator contract 96 | > var ballot_sol_simplestoragecreatorContract = web3.eth.contract([{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"deployedContracts","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"name","type":"bytes32"}],"name":"create","outputs":[{"name":"ca","type":"address"}],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"},{"indexed":true,"name":"c","type":"address"}],"name":"contractCreated","type":"event"}]); 97 | > var ballot_sol_simplestoragecreator = ballot_sol_simplestoragecreatorContract.new( 98 | { 99 | from: web3.eth.accounts[0], 100 | data: '0x6060604052341561000c57fe5b5b6108688061001c6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806323b94b54146100465780637368a8ce146100aa575bfe5b341561004e57fe5b61006860048080356000191690602001909190505061010e565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100b257fe5b6100cc600480803560001916906020019091905050610141565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60006020528060005260406000206000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600061014b610284565b809050604051809103906000f080151561016157fe5b60006000846000191660001916815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060006000836000191660001916815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1682600019167f2ab63b650b80cd61a629723d60e296d9f72bafe303d88536727e84377708b73960405180905060405180910390a360006000836000191660001916815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505b919050565b6040516105a8806102958339019056006060604052341561000c57fe5b5b7f46062ebc56804d834366ac861d4e38edf94333ab8ccca994d5b3b0b311dfd0d733604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a133600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b5b6104e6806100c26000396000f30060606040523615610081576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063107b401f146100ed5780631ab06ee5146101375780631b9265b8146101745780632a1afcd91461017e5780635dab8af6146101a4578063609d3334146101c8578063b6588ffd146101ee575b6100eb5b7fb86da4c4811a5ccb3d035797a21634b07481267aba3135b71b181ac96345cf2833604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a15b565b005b34156100f557fe5b610121600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610200565b6040518082815260200191505060405180910390f35b341561013f57fe5b61015e6004808035906020019091908035906020019091905050610218565b6040518082815260200191505060405180910390f35b61017c6102ed565b005b341561018657fe5b61018e610334565b6040518082815260200191505060405180910390f35b34156101ac57fe5b6101c660048080356000191690602001909190505061033a565b005b34156101d057fe5b6101d8610468565b6040518082815260200191505060405180910390f35b34156101f657fe5b6101fe61046e565b005b60036020528060005260406000206000915090505481565b60008260008190555081600181905550818301600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055507f11ee77a57d176eec0c855796ed2e9f1be7314279895c6cc8a5e823e426b16d10338484604051808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001838152602001828152602001935050505060405180910390a181830190505b92915050565b34600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b565b60005481565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16637368a8ce826000604051602001526040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808260001916600019168152602001915050602060405180830381600087803b15156103d857fe5b6102c65a03f115156103e657fe5b50505060405180519050507fc741b49632e665de715035d7664543151610b8b242d71fe7eb5120cdafa7a8d63382604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182600019166000191681526020019250505060405180910390a15b50565b60015481565b600060009055600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600090555b5600a165627a7a723058207b7ecf199a0f3c5ece72a982609c856c3da393a4d8d78b694c49ebaa28a7bff60029a165627a7a72305820c687e4906e825de4ff53122103711057b2266c1bb8818235d7ca71343ec4fd4d0029', 101 | gas: '4700000' 102 | }, function (e, contract){ 103 | console.log(e, contract); 104 | if (typeof contract.address !== 'undefined') { 105 | console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash); 106 | } 107 | }) 108 | 109 | > miner.start() 110 | null 111 | > null [object Object] 112 | Contract mined! address: 0x2a7e33dac11c8e6164acb448637092be816b1488 transactionHash: 0x21c9a676cae0a501829a4bc788adc4b9dc851c4c0c622b7997405cbce08807c1 113 | 114 | > miner.stop() 115 | true 116 | 117 | //call create function to create SimpleStorage Instance 118 | > var simpleStorageCreatorInstance = ballot_sol_simplestoragecreatorContract.at("0x2a7e33dac11c8e6164acb448637092be816b1488") 119 | undefined 120 | > personal.unlockAccount(eth.coinbase) 121 | Unlock account 0xa3ac96fbe4b0dce5f6f89a715ca00934d68f6c37 122 | Passphrase: 123 | true 124 | 125 | //notice : set the transaction gasLimit, because default gasLimit is not enough to create contract 126 | > simpleStorageCreatorInstance.create.sendTransaction("aaa", {from : eth.coinbase, gas : 1000000}) 127 | "0x698a40f0884fd5111fbd5c1e3dd91d13c7df4a0f09a8565cb068da4bd1945654" 128 | > miner.start() 129 | null 130 | > miner.stop() 131 | true 132 | 133 | // 创建的 SimpleStorage 合约地址 134 | > simpleStorageCreatorInstance.deployedContracts("aaa") 135 | "0xe0b759ca7448210b1e73fb32c8b12f880130aad2" 136 | 137 | //logs 中的第一项是 SimpleStorage 构造函数中发生的事件函数: constructorCalled(address) 138 | //第二项是自身发生的事件函数:contractCreated。通过 topics 中的第一项可以看出。 139 | 140 | > web3.sha3("constructorCalled(address)") 141 | "0x46062ebc56804d834366ac861d4e38edf94333ab8ccca994d5b3b0b311dfd0d7" 142 | > web3.sha3("contractCreated(bytes32,address)") 143 | "0x2ab63b650b80cd61a629723d60e296d9f72bafe303d88536727e84377708b739" 144 | 145 | > eth.getTransactionReceipt("0x698a40f0884fd5111fbd5c1e3dd91d13c7df4a0f09a8565cb068da4bd1945654") 146 | { 147 | blockHash: "0x89243162564fb7be70e8e1d5b6fc6151fc8ba4069b6e2526420bd5894e4b6184", 148 | blockNumber: 142, 149 | contractAddress: null, 150 | cumulativeGasUsed: 349238, 151 | from: "0xa3ac96fbe4b0dce5f6f89a715ca00934d68f6c37", 152 | gasUsed: 349238, 153 | logs: [{ 154 | address: "0xe0b759ca7448210b1e73fb32c8b12f880130aad2", 155 | blockHash: "0x89243162564fb7be70e8e1d5b6fc6151fc8ba4069b6e2526420bd5894e4b6184", 156 | blockNumber: 142, 157 | data: "0x0000000000000000000000002a7e33dac11c8e6164acb448637092be816b1488", 158 | logIndex: 0, 159 | removed: false, 160 | topics: ["0x46062ebc56804d834366ac861d4e38edf94333ab8ccca994d5b3b0b311dfd0d7"], 161 | transactionHash: "0x698a40f0884fd5111fbd5c1e3dd91d13c7df4a0f09a8565cb068da4bd1945654", 162 | transactionIndex: 0 163 | }, { 164 | address: "0x2a7e33dac11c8e6164acb448637092be816b1488", 165 | blockHash: "0x89243162564fb7be70e8e1d5b6fc6151fc8ba4069b6e2526420bd5894e4b6184", 166 | blockNumber: 142, 167 | data: "0x", 168 | logIndex: 1, 169 | removed: false, 170 | topics: ["0x2ab63b650b80cd61a629723d60e296d9f72bafe303d88536727e84377708b739", "0x6161610000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000e0b759ca7448210b1e73fb32c8b12f880130aad2"], 171 | transactionHash: "0x698a40f0884fd5111fbd5c1e3dd91d13c7df4a0f09a8565cb068da4bd1945654", 172 | transactionIndex: 0 173 | }], 174 | logsBloom: "0x0000008000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000a800000000000000000000000000000080000000000040000000000400000000002000100000000000000000000000020000000000000000000000000040000000000000000000000000000000000000000000000000020000000000000400000000000000000000000000000000001000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000", 175 | root: "0x6156ba393a7edca776920e38ec7df6fc9da533dc5d24d975eb465ff6eeab551a", 176 | to: "0x2a7e33dac11c8e6164acb448637092be816b1488", 177 | transactionHash: "0x698a40f0884fd5111fbd5c1e3dd91d13c7df4a0f09a8565cb068da4bd1945654", 178 | transactionIndex: 0 179 | } 180 | 181 | // deploy SimpleStorageCaller contract 182 | > var ballot_sol_simplestoragecallerContract = web3.eth.contract([{"constant":false,"inputs":[],"name":"recvEther","outputs":[],"payable":true,"type":"function"},{"constant":false,"inputs":[{"name":"ssaddress","type":"address"},{"name":"a","type":"uint256"},{"name":"b","type":"uint256"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"ssaddress","type":"address"}],"name":"setbyAddress","outputs":[],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"RecvEther","type":"event"}]); 183 | > var ballot_sol_simplestoragecaller = ballot_sol_simplestoragecallerContract.new( 184 | { 185 | from: web3.eth.accounts[0], 186 | data: '0x6060604052341561000c57fe5b5b6103ae8061001c6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680635c890712146100515780638308d7e91461005b578063b8518596146100a3575bfe5b6100596100d9565b005b341561006357fe5b6100a1600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091908035906020019091905050610147565b005b34156100ab57fe5b6100d7600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506101e8565b005b7ff26e958dbf975a119527fcf7e79a1c63c6245ee7206c2393ac4ebb7b3d434ee73334604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019250505060405180910390a15b565b60008390508073ffffffffffffffffffffffffffffffffffffffff16631ab06ee584846000604051602001526040518363ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018083815260200182815260200192505050602060405180830381600087803b15156101c857fe5b6102c65a03f115156101d657fe5b50505060405180519050505b50505050565b8073ffffffffffffffffffffffffffffffffffffffff16600060405180807f7365742875696e743235362c75696e7432353629000000000000000000000000815250601401905060405180910390207c0100000000000000000000000000000000000000000000000000000000900490602060406040518463ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808360ff1681526020018260ff1681526020019250505060006040518083038185886187965a03f1935050505015156102c35760006000fd5b8073ffffffffffffffffffffffffffffffffffffffff16671bc16d674ec8000060405180807f1b9265b800000000000000000000000000000000000000000000000000000000815250602001905060006040518083038185876187965a03f19250505015156103325760006000fd5b8073ffffffffffffffffffffffffffffffffffffffff166108fc671bc16d674ec800009081150290604051809050600060405180830381858888f19350505050151561037e5760006000fd5b5b505600a165627a7a723058201dc97e127cda430da996488f5aacb680af1cf34fa6e046118c7293ed652484e30029', 187 | gas: '4700000' 188 | }, function (e, contract){ 189 | console.log(e, contract); 190 | if (typeof contract.address !== 'undefined') { 191 | console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash); 192 | } 193 | }) 194 | > miner.start() 195 | null 196 | > null [object Object] 197 | Contract mined! address: 0xce0963867986bd654631b5f373c571c6599c41d8 transactionHash: 0x394f7a75ec92947bb5bc341e5402334b7240158812bd29c99873f75e540f2e9f 198 | 199 | > miner.stop() 200 | true 201 | 202 | //call recvEther function 203 | > var simpleStorageCallerInstance = ballot_sol_simplestoragecallerContract.at("0xce0963867986bd654631b5f373c571c6599c41d8") 204 | undefined 205 | > simpleStorageCallerInstance.recvEther.sendTransaction({from : eth.coinbase, value : web3.toWei(7, "ether")}) 206 | "0x2a2fde605785cdb98781a75302515ef49f7f6adc00544e571b3a643ada435773" 207 | > miner.start() 208 | null 209 | > miner.stop() 210 | true 211 | > eth.getBalance("0xce0963867986bd654631b5f373c571c6599c41d8") 212 | 7000000000000000000 213 | 214 | // call set function 215 | > simpleStorageCallerInstance.set.sendTransaction("0xe0b759ca7448210b1e73fb32c8b12f880130aad2", 10, 20, {from : eth.coinbase}) 216 | "0x18ad69f414e031238f67e6f99cff0e94eabd349bdc467beb5dd8024b1691d361" 217 | > miner.start() 218 | null 219 | > miner.stop() 220 | true 221 | 222 | //验证被调用合约的状态是否正确改变 223 | > var ballot_sol_simplestorageContract = web3.eth.contract([{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"haha","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"a","type":"uint256"},{"name":"b","type":"uint256"}],"name":"set","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"pay","outputs":[],"payable":true,"type":"function"},{"constant":true,"inputs":[],"name":"storedData","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"name","type":"bytes32"}],"name":"constructbySelf","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"extraData","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"del","outputs":[],"payable":false,"type":"function"},{"inputs":[],"payable":false,"type":"constructor"},{"payable":true,"type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"a","type":"uint256"},{"indexed":false,"name":"b","type":"uint256"}],"name":"valueChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"caller","type":"address"}],"name":"fallbackCalled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"caller","type":"address"}],"name":"constructorCalled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"caller","type":"address"},{"indexed":false,"name":"name","type":"bytes32"}],"name":"constructbySelfCalled","type":"event"}]); 224 | undefined 225 | > var simpleStorageInstance = ballot_sol_simplestorageContract.at("0xe0b759ca7448210b1e73fb32c8b12f880130aad2") 226 | undefined 227 | > simpleStorageInstance.storedData 228 | function() 229 | > simpleStorageInstance.storedData() 230 | 10 231 | > simpleStorageInstance.extraData() 232 | 20 233 | > simpleStorageInstance.haha("0xce0963867986bd654631b5f373c571c6599c41d8") 234 | 30 235 | 236 | // call setbyAddress function 237 | > simpleStorageCallerInstance.setbyAddress.sendTransaction("0xe0b759ca7448210b1e73fb32c8b12f880130aad2", {from : eth.coinbase}) 238 | "0x6bba77c6532b4319451eba12d3a4545cf73020d4f5ffca65a7cd60af7c930d0b" 239 | > miner.start() 240 | null 241 | > miner.stop() 242 | true 243 | 244 | // 转账成功 245 | > eth.getBalance("0xce0963867986bd654631b5f373c571c6599c41d8") 246 | 3000000000000000000 247 | > eth.getBalance("0xe0b759ca7448210b1e73fb32c8b12f880130aad2") 248 | 4000000000000000000 249 | > simpleStorageInstance.storedData() 250 | 32 251 | > simpleStorageInstance.extraData() 252 | 64 253 | > simpleStorageInstance.haha("0xce0963867986bd654631b5f373c571c6599c41d8") 254 | 2000000000000000000 255 | 256 | // 交易收据: 其中 logs 有两项,一项是由 ssaddress.call.value(0)(bytes4(bytes32(sha3('set(uint256,uint256)'))), 32, 64) 触发的 valueChanged 事件,另一项是由 ssaddress.send(2 ether) 触发的 SimpleContract 的 fallback 函数引起的事件,即 fallbackCalled 事件 257 | > web3.sha3("fallbackCalled(address)") 258 | "0xb86da4c4811a5ccb3d035797a21634b07481267aba3135b71b181ac96345cf28" 259 | > web3.sha3("valueChanged(address,uint256,uint256)") 260 | "0x11ee77a57d176eec0c855796ed2e9f1be7314279895c6cc8a5e823e426b16d10" 261 | > eth.getTransactionReceipt("0x6bba77c6532b4319451eba12d3a4545cf73020d4f5ffca65a7cd60af7c930d0b") 262 | { 263 | blockHash: "0x60a6b55888b7439a169b60ca4e7464bb29c818417b95038e40ab37274aa6c2ae", 264 | blockNumber: 181, 265 | contractAddress: null, 266 | cumulativeGasUsed: 62306, 267 | from: "0xa3ac96fbe4b0dce5f6f89a715ca00934d68f6c37", 268 | gasUsed: 62306, 269 | logs: [{ 270 | address: "0xe0b759ca7448210b1e73fb32c8b12f880130aad2", 271 | blockHash: "0x60a6b55888b7439a169b60ca4e7464bb29c818417b95038e40ab37274aa6c2ae", 272 | blockNumber: 181, 273 | data: "0x000000000000000000000000ce0963867986bd654631b5f373c571c6599c41d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040", 274 | logIndex: 0, 275 | removed: false, 276 | topics: ["0x11ee77a57d176eec0c855796ed2e9f1be7314279895c6cc8a5e823e426b16d10"], 277 | transactionHash: "0x6bba77c6532b4319451eba12d3a4545cf73020d4f5ffca65a7cd60af7c930d0b", 278 | transactionIndex: 0 279 | }, { 280 | address: "0xe0b759ca7448210b1e73fb32c8b12f880130aad2", 281 | blockHash: "0x60a6b55888b7439a169b60ca4e7464bb29c818417b95038e40ab37274aa6c2ae", 282 | blockNumber: 181, 283 | data: "0x000000000000000000000000ce0963867986bd654631b5f373c571c6599c41d8", 284 | logIndex: 1, 285 | removed: false, 286 | topics: ["0xb86da4c4811a5ccb3d035797a21634b07481267aba3135b71b181ac96345cf28"], 287 | transactionHash: "0x6bba77c6532b4319451eba12d3a4545cf73020d4f5ffca65a7cd60af7c930d0b", 288 | transactionIndex: 0 289 | }], 290 | logsBloom: "0x00000000000000000000000000000000000000000000000000000080000000100000000000000000000000000000000000000000000002000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000040000000000000000000000000000000000000000000000000000000000000000", 291 | root: "0xc9eb43e1ac6c51c20e05939da1036f45ba9ac030160b89e0b44c96737363be5b", 292 | to: "0xce0963867986bd654631b5f373c571c6599c41d8", 293 | transactionHash: "0x6bba77c6532b4319451eba12d3a4545cf73020d4f5ffca65a7cd60af7c930d0b", 294 | transactionIndex: 0 295 | } 296 | 297 | // SimpleStorage call SimpleStorageCreator 298 | > simpleStorageInstance.constructbySelf.sendTransaction("bbb", {from : eth.coinbase, gas : 1000000}) 299 | "0x778b61d675b67f786a03033caa384eb6ee9c27c1ab68d2d8a9119b0e02a8799b" 300 | > miner.start() 301 | null 302 | > miner.stop() 303 | true 304 | 305 | // 第二次创建生成合约的地址,与下面交易收据 logs 第二项中 topics 的第二项、第三项相同 306 | > simpleStorageCreatorInstance.deployedContracts("bbb") 307 | "0x0e344a0a77447b01c0991335301b79e1d052734a" 308 | // 交易收据:logs 有三项,分别是 constructorCalled、contractCreated、constructbySelfCalled 事件 309 | > web3.sha3("constructorCalled(address)") 310 | "0x46062ebc56804d834366ac861d4e38edf94333ab8ccca994d5b3b0b311dfd0d7" 311 | > web3.sha3("contractCreated(bytes32,address)") 312 | "0x2ab63b650b80cd61a629723d60e296d9f72bafe303d88536727e84377708b739" 313 | > web3.sha3("constructbySelfCalled(address,bytes32)") 314 | "0xc741b49632e665de715035d7664543151610b8b242d71fe7eb5120cdafa7a8d6" 315 | > eth.getTransactionReceipt("0x778b61d675b67f786a03033caa384eb6ee9c27c1ab68d2d8a9119b0e02a8799b") 316 | { 317 | blockHash: "0xee6ccffe8225635a69f193d9283e265dbfb98845bf29d705e753216944bc2683", 318 | blockNumber: 188, 319 | contractAddress: null, 320 | cumulativeGasUsed: 352719, 321 | from: "0xa3ac96fbe4b0dce5f6f89a715ca00934d68f6c37", 322 | gasUsed: 352719, 323 | logs: [{ 324 | address: "0x0e344a0a77447b01c0991335301b79e1d052734a", 325 | blockHash: "0xee6ccffe8225635a69f193d9283e265dbfb98845bf29d705e753216944bc2683", 326 | blockNumber: 188, 327 | data: "0x0000000000000000000000002a7e33dac11c8e6164acb448637092be816b1488", 328 | logIndex: 0, 329 | removed: false, 330 | topics: ["0x46062ebc56804d834366ac861d4e38edf94333ab8ccca994d5b3b0b311dfd0d7"], 331 | transactionHash: "0x778b61d675b67f786a03033caa384eb6ee9c27c1ab68d2d8a9119b0e02a8799b", 332 | transactionIndex: 0 333 | }, { 334 | address: "0x2a7e33dac11c8e6164acb448637092be816b1488", 335 | blockHash: "0xee6ccffe8225635a69f193d9283e265dbfb98845bf29d705e753216944bc2683", 336 | blockNumber: 188, 337 | data: "0x", 338 | logIndex: 1, 339 | removed: false, 340 | topics: ["0x2ab63b650b80cd61a629723d60e296d9f72bafe303d88536727e84377708b739", "0x6262620000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000e344a0a77447b01c0991335301b79e1d052734a"], 341 | transactionHash: "0x778b61d675b67f786a03033caa384eb6ee9c27c1ab68d2d8a9119b0e02a8799b", 342 | transactionIndex: 0 343 | }, { 344 | address: "0xe0b759ca7448210b1e73fb32c8b12f880130aad2", 345 | blockHash: "0xee6ccffe8225635a69f193d9283e265dbfb98845bf29d705e753216944bc2683", 346 | blockNumber: 188, 347 | data: "0x000000000000000000000000a3ac96fbe4b0dce5f6f89a715ca00934d68f6c376262620000000000000000000000000000000000000000000000000000000000", 348 | logIndex: 2, 349 | removed: false, 350 | topics: ["0xc741b49632e665de715035d7664543151610b8b242d71fe7eb5120cdafa7a8d6"], 351 | transactionHash: "0x778b61d675b67f786a03033caa384eb6ee9c27c1ab68d2d8a9119b0e02a8799b", 352 | transactionIndex: 0 353 | }], 354 | logsBloom: "0x00000080000000000000000000080000000100000000000000000000000000000000000000000000000000000000000000000000040002800000000000000000000040000000000000000000840000000000400000000000000100000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000400000000000000000000004000000000021000400000000000000000000010000001000000020000000000000000800000000000000004000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000001000000000000", 355 | root: "0x3f15654a1db329092852c31ab221828d3a7f29275c84558bba96d866d8d287b9", 356 | to: "0xe0b759ca7448210b1e73fb32c8b12f880130aad2", 357 | transactionHash: "0x778b61d675b67f786a03033caa384eb6ee9c27c1ab68d2d8a9119b0e02a8799b", 358 | transactionIndex: 0 359 | } 360 | ``` 361 | 362 | bingo~ 363 | -------------------------------------------------------------------------------- /smart-contract-best-practices.md: -------------------------------------------------------------------------------- 1 | ## 智能合约最佳实践 2 | 3 | 翻译自 https://github.com/ConsenSys/smart-contract-best-practices 4 | 5 | **译者:李康** 6 | 7 | ### 使用 solidity 编写智能合约的安全建议 8 | --- 9 | #### 外部调用 10 | 11 | ##### 尽可能避免外部调用 12 | 13 | (TODO) 14 | 15 | ##### 注意 `send()`,`transfer()`,`call.value()()` 三者之间的权衡 16 | 17 | 当发送以太币的时候,请注意使用 `someAddress.send()`,`someAddress.transfer()` 与 `someAddress.call.value()()` 之间的相对权衡。 18 | 19 | - `x.transfer(y)` 等价于 `if (!x.send(y)) throw;` Send 是 transfer 的底层实现,建议尽可能地使用 `transfer` 20 | 21 | - `someAddress.send()` 与 `someAddress.transfer()` 对于[重入]()攻击来说是安全的。尽管这些方法仍然会触发代码的执行,但是被调用的合约只被允许花费 2300 gas,这些 gas 目前仅仅够记录一个事件 22 | 23 | - `someAddress.call.value()()` 将会发送提供的以太币并且触发代码执行。执行的代码被给予了当前所有剩余可用的 gas,使得这种类型的价值转移易受到重入攻击 24 | 25 | 使用 `send()` 或 `transfer()` 将会防止重入攻击,但是这么做的代价是与默认函数(fallback funtion) 花费超过 2300 gas 的合约是不兼容的, 26 | 27 | 一种尝试平衡该折衷的模式是实现 [push and pull]() 机制,使用 `send()` 或 `transfer()` 来实现 push 部分,使用 `call.value()()` 来实现 pull 部分。 28 | 29 | 值得指出的是,使用 `send()` 或 `transfer()` 进行价值转移本身并不能使合约对重入攻击保持安全性,而只能使特定的价值转移对重入攻击保持安全性。 30 | -------------------------------------------------------------------------------- /solidity-dynamic-array.md: -------------------------------------------------------------------------------- 1 | ## 智能合约中的动态数组 2 | 3 | **作者:李康** 4 | 5 | 以 status 众筹合约中的动态上限合约为例,讲解一下智能合约中动态数组的使用,合约源代码如下: 6 | 7 | ``` 8 | pragma solidity ^0.4.12; 9 | 10 | 11 | /// @dev `Owned` is a base level contract that assigns an `owner` that can be 12 | /// later changed 13 | contract Owned { 14 | 15 | /// @dev `owner` is the only address that can call a function with this 16 | /// modifier 17 | modifier onlyOwner() { 18 | require(msg.sender == owner); 19 | _; 20 | } 21 | 22 | address public owner; 23 | 24 | /// @notice The Constructor assigns the message sender to be `owner` 25 | function Owned() { 26 | owner = msg.sender; 27 | } 28 | 29 | address public newOwner; 30 | 31 | /// @notice `owner` can step down and assign some other address to this role 32 | /// @param _newOwner The address of the new owner. 0x0 can be used to create 33 | /// an unowned neutral vault, however that cannot be undone 34 | function changeOwner(address _newOwner) onlyOwner { 35 | newOwner = _newOwner; 36 | } 37 | 38 | 39 | function acceptOwnership() { 40 | if (msg.sender == newOwner) { 41 | owner = newOwner; 42 | } 43 | } 44 | } 45 | 46 | 47 | 48 | 49 | /** 50 | * Math operations with safety checks 51 | */ 52 | library SafeMath { 53 | function mul(uint a, uint b) internal returns (uint) { 54 | uint c = a * b; 55 | assert(a == 0 || c / a == b); 56 | return c; 57 | } 58 | 59 | function div(uint a, uint b) internal returns (uint) { 60 | // assert(b > 0); // Solidity automatically throws when dividing by 0 61 | uint c = a / b; 62 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 63 | return c; 64 | } 65 | 66 | function sub(uint a, uint b) internal returns (uint) { 67 | assert(b <= a); 68 | return a - b; 69 | } 70 | 71 | function add(uint a, uint b) internal returns (uint) { 72 | uint c = a + b; 73 | assert(c >= a); 74 | return c; 75 | } 76 | 77 | function max64(uint64 a, uint64 b) internal constant returns (uint64) { 78 | return a >= b ? a : b; 79 | } 80 | 81 | function min64(uint64 a, uint64 b) internal constant returns (uint64) { 82 | return a < b ? a : b; 83 | } 84 | 85 | function max256(uint256 a, uint256 b) internal constant returns (uint256) { 86 | return a >= b ? a : b; 87 | } 88 | 89 | function min256(uint256 a, uint256 b) internal constant returns (uint256) { 90 | return a < b ? a : b; 91 | } 92 | } 93 | 94 | 95 | /* 96 | Copyright 2017, Jordi Baylina 97 | 98 | This program is free software: you can redistribute it and/or modify 99 | it under the terms of the GNU General Public License as published by 100 | the Free Software Foundation, either version 3 of the License, or 101 | (at your option) any later version. 102 | 103 | This program is distributed in the hope that it will be useful, 104 | but WITHOUT ANY WARRANTY; without even the implied warranty of 105 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 106 | GNU General Public License for more details. 107 | 108 | You should have received a copy of the GNU General Public License 109 | along with this program. If not, see . 110 | */ 111 | 112 | /// @title DynamicCeiling Contract 113 | /// @author Jordi Baylina 114 | /// @dev This contract calculates the ceiling from a series of curves. 115 | /// These curves are committed first and revealed later. 116 | /// All the curves must be in increasing order and the last curve is marked 117 | /// as the last one. 118 | /// This contract allows to hide and reveal the ceiling at will of the owner. 119 | 120 | 121 | 122 | contract DynamicCeiling is Owned { 123 | using SafeMath for uint256; 124 | 125 | struct Curve { 126 | bytes32 hash; 127 | // Absolute limit for this curve 128 | uint256 limit; 129 | // The funds remaining to be collected are divided by `slopeFactor` smooth ceiling 130 | // with a long tail where big and small buyers can take part. 131 | uint256 slopeFactor; 132 | // This keeps the curve flat at this number, until funds to be collected is less than this 133 | uint256 collectMinimum; 134 | } 135 | 136 | address public contribution; 137 | 138 | Curve[] public curves; 139 | uint256 public currentIndex; 140 | uint256 public revealedCurves; 141 | bool public allRevealed; 142 | 143 | /// @dev `contribution` is the only address that can call a function with this 144 | /// modifier 145 | modifier onlyContribution { 146 | require(msg.sender == contribution); 147 | _; 148 | } 149 | 150 | function DynamicCeiling(address _owner, address _contribution) { 151 | owner = _owner; 152 | contribution = _contribution; 153 | } 154 | 155 | /// @notice This should be called by the creator of the contract to commit 156 | /// all the curves. 157 | /// @param _curveHashes Array of hashes of each curve. Each hash is calculated 158 | /// by the `calculateHash` method. More hashes than actual curves can be 159 | /// committed in order to hide also the number of curves. 160 | /// The remaining hashes can be just random numbers. 161 | function setHiddenCurves(bytes32[] _curveHashes) public onlyOwner { 162 | require(curves.length == 0); 163 | 164 | curves.length = _curveHashes.length; 165 | for (uint256 i = 0; i < _curveHashes.length; i = i.add(1)) { 166 | curves[i].hash = _curveHashes[i]; 167 | } 168 | } 169 | 170 | 171 | /// @notice Anybody can reveal the next curve if he knows it. 172 | /// @param _limit Ceiling cap. 173 | /// (must be greater or equal to the previous one). 174 | /// @param _last `true` if it's the last curve. 175 | /// @param _salt Random number used to commit the curve 176 | function revealCurve(uint256 _limit, uint256 _slopeFactor, uint256 _collectMinimum, 177 | bool _last, bytes32 _salt) public { 178 | require(!allRevealed); 179 | 180 | require(curves[revealedCurves].hash == calculateHash(_limit, _slopeFactor, _collectMinimum, 181 | _last, _salt)); 182 | 183 | require(_limit != 0 && _slopeFactor != 0 && _collectMinimum != 0); 184 | if (revealedCurves > 0) { 185 | require(_limit >= curves[revealedCurves.sub(1)].limit); 186 | } 187 | 188 | curves[revealedCurves].limit = _limit; 189 | curves[revealedCurves].slopeFactor = _slopeFactor; 190 | curves[revealedCurves].collectMinimum = _collectMinimum; 191 | revealedCurves = revealedCurves.add(1); 192 | 193 | if (_last) allRevealed = true; 194 | } 195 | 196 | /// @notice Reveal multiple curves at once 197 | function revealMulti(uint256[] _limits, uint256[] _slopeFactors, uint256[] _collectMinimums, 198 | bool[] _lasts, bytes32[] _salts) public { 199 | // Do not allow none and needs to be same length for all parameters 200 | require(_limits.length != 0 && 201 | _limits.length == _slopeFactors.length && 202 | _limits.length == _collectMinimums.length && 203 | _limits.length == _lasts.length && 204 | _limits.length == _salts.length); 205 | 206 | for (uint256 i = 0; i < _limits.length; i = i.add(1)) { 207 | revealCurve(_limits[i], _slopeFactors[i], _collectMinimums[i], 208 | _lasts[i], _salts[i]); 209 | } 210 | } 211 | 212 | /// @notice Move to curve, used as a failsafe 213 | function moveTo(uint256 _index) public onlyOwner { 214 | require(_index < revealedCurves && // No more curves 215 | _index == currentIndex.add(1)); // Only move one index at a time 216 | currentIndex = _index; 217 | } 218 | 219 | /// @return Return the funds to collect for the current point on the curve 220 | /// (or 0 if no curves revealed yet) 221 | function toCollect(uint256 collected) public onlyContribution returns (uint256) { 222 | if (revealedCurves == 0) return 0; 223 | 224 | // Move to the next curve 225 | if (collected >= curves[currentIndex].limit) { // Catches `limit == 0` 226 | uint256 nextIndex = currentIndex.add(1); 227 | if (nextIndex >= revealedCurves) return 0; // No more curves 228 | currentIndex = nextIndex; 229 | if (collected >= curves[currentIndex].limit) return 0; // Catches `limit == 0` 230 | } 231 | 232 | // Everything left to collect from this limit 233 | uint256 difference = curves[currentIndex].limit.sub(collected); 234 | 235 | // Current point on the curve 236 | uint256 collect = difference.div(curves[currentIndex].slopeFactor); 237 | 238 | // Prevents paying too much fees vs to be collected; breaks long tail 239 | if (collect <= curves[currentIndex].collectMinimum) { 240 | if (difference > curves[currentIndex].collectMinimum) { 241 | return curves[currentIndex].collectMinimum; 242 | } else { 243 | return difference; 244 | } 245 | } else { 246 | return collect; 247 | } 248 | } 249 | 250 | /// @notice Calculates the hash of a curve. 251 | /// @param _limit Ceiling cap. 252 | /// @param _last `true` if it's the last curve. 253 | /// @param _salt Random number that will be needed to reveal this curve. 254 | /// @return The calculated hash of this curve to be used in the `setHiddenCurves` method 255 | function calculateHash(uint256 _limit, uint256 _slopeFactor, uint256 _collectMinimum, 256 | bool _last, bytes32 _salt) public constant returns (bytes32) { 257 | return keccak256(_limit, _slopeFactor, _collectMinimum, _last, _salt); 258 | } 259 | 260 | /// @return Return the total number of curves committed 261 | /// (can be larger than the number of actual curves on the curve to hide 262 | /// the real number of curves) 263 | function nCurves() public constant returns (uint256) { 264 | return curves.length; 265 | } 266 | 267 | } 268 | ``` 269 | 270 | 首先在 remix 可视化界面创建合约,得到合约地址 0xcdbab7ad4c77046f61d7846a48e5edbe423c2775 271 | 272 | 假设设置 2 个真实隐藏上限以及 1 个冗余隐藏上限,其中真实隐藏上限为[100, 5, 2, false, 10000]、[200, 10, 4, true, 20000],首先计算出真实隐藏上限哈希值: 273 | 274 | ``` 275 | // 进入 geth console 276 | // 哈希的形式是 keccak256(100, 5, 2, false, 10000) 277 | // 根据合约代码,除去 _lasts 变量,其余均为 256 位,bool 类型占一个字节,因此是 0x00 或者 0x01 278 | 279 | > web3.toHex(100) 280 | "0x64" 281 | > web3.toHex(200) 282 | "0xc8" 283 | > web3.toHex(10000) 284 | "0x2710" 285 | > web3.toHex(20000) 286 | "0x4e20" 287 | 288 | > web3.sha3("000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000002710",{encoding:"hex"}) 289 | "0xd995b218064414479be1e91d49a91c4543c87a1835fc9ceb6168cc3232efdf4b" 290 | 291 | > web3.sha3("00000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000004010000000000000000000000000000000000000000000000000000000000004e20",{encoding:"hex"}) 292 | "0x1002d8c03b240668e2261b55118f91030c88018d687e3341c9b18af0cc313a83" 293 | ``` 294 | 295 | 接着在 geth console 中调用 `setHiddenCurves` 函数 296 | 297 | ``` 298 | > var browser_dynamicceiling_sol_dynamicceilingContract = web3.eth.contract([{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"curves","outputs":[{"name":"hash","type":"bytes32"},{"name":"limit","type":"uint256"},{"name":"slopeFactor","type":"uint256"},{"name":"collectMinimum","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"currentIndex","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"nCurves","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"allRevealed","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"contribution","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_curveHashes","type":"bytes32[]"}],"name":"setHiddenCurves","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_limits","type":"uint256[]"},{"name":"_slopeFactors","type":"uint256[]"},{"name":"_collectMinimums","type":"uint256[]"},{"name":"_lasts","type":"bool[]"},{"name":"_salts","type":"bytes32[]"}],"name":"revealMulti","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_limit","type":"uint256"},{"name":"_slopeFactor","type":"uint256"},{"name":"_collectMinimum","type":"uint256"},{"name":"_last","type":"bool"},{"name":"_salt","type":"bytes32"}],"name":"revealCurve","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"revealedCurves","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"acceptOwnership","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_limit","type":"uint256"},{"name":"_slopeFactor","type":"uint256"},{"name":"_collectMinimum","type":"uint256"},{"name":"_last","type":"bool"},{"name":"_salt","type":"bytes32"}],"name":"calculateHash","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"collected","type":"uint256"}],"name":"toCollect","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_newOwner","type":"address"}],"name":"changeOwner","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_index","type":"uint256"}],"name":"moveTo","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"newOwner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"inputs":[{"name":"_owner","type":"address"},{"name":"_contribution","type":"address"}],"payable":false,"type":"constructor"}]); 299 | undefined 300 | 301 | > personal.unlockAccount(eth.coinbase) 302 | Unlock account 0xa3ac96fbe4b0dce5f6f89a715ca00934d68f6c37 303 | Passphrase: 304 | true 305 | > miner.start() 306 | null 307 | > var myInstance = browser_dynamicceiling_sol_dynamicceilingContract.at("0xcdbab7ad4c77046f61d7846a48e5edbe423c2775") 308 | undefined 309 | > myInstance.setHiddenCurves.sendTransaction(["0xd995b218064414479be1e91d49a91c4543c87a1835fc9ceb6168cc3232efdf4b", "0x1002d8c03b240668e2261b55118f91030c88018d687e3341c9b18af0cc313a83", "0x00000000000000000000000000000000000000000000000000123456789abcde"], {from: eth.coinbase, gas : 1000000}) 310 | "0x296c1ed8cc13e51cc72180858c49cfe07e47f05a4ff23795113670e539d30cab" 311 | > miner.stop() 312 | true 313 | 314 | > myInstance.nCurves() 315 | 3 316 | 317 | // 我们来看一下真正的交易,可以发现 data 字段多了 0x00...20 与 0x00...03,这是由于函数的参数是动态数组,0x00...20 表示数组从第 32 个字节处开始,接下来是数组元素个数 3 318 | > eth.getTransaction("0x296c1ed8cc13e51cc72180858c49cfe07e47f05a4ff23795113670e539d30cab") 319 | { 320 | blockHash: "0x54904ffbd1105a68a24d38edf376ab6264bc9c0b5992c32fba8e3d12463b0a01", 321 | blockNumber: 3787, 322 | from: "0xa3ac96fbe4b0dce5f6f89a715ca00934d68f6c37", 323 | gas: 1000000, 324 | gasPrice: 50000000000, 325 | hash: "0x296c1ed8cc13e51cc72180858c49cfe07e47f05a4ff23795113670e539d30cab", 326 | input: "0x54657f0a00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003d995b218064414479be1e91d49a91c4543c87a1835fc9ceb6168cc3232efdf4b1002d8c03b240668e2261b55118f91030c88018d687e3341c9b18af0cc313a8300000000000000000000000000000000000000000000000000123456789abcde", 327 | nonce: 36, 328 | r: "0xcff1d48e39fddf4110a8dcae35022da0306aea65b34e992c5c377e444d15cda4", 329 | s: "0x2aa7ea973fc63010cce7a7ab106ead4278b251c08ef4accf27d9f189703e984e", 330 | to: "0xcdbab7ad4c77046f61d7846a48e5edbe423c2775", 331 | transactionIndex: 0, 332 | v: "0x26", 333 | value: 0 334 | } 335 | // 披露上限, 其中 data 字段为: 336 | //In [34]: for i in range(len(a) / 64) : 337 | ...: print a[i*64:(i+1)*64] 338 | // 00000000000000000000000000000000000000000000000000000000000000a0 339 | // 0000000000000000000000000000000000000000000000000000000000000100 340 | // 0000000000000000000000000000000000000000000000000000000000000160 341 | // 00000000000000000000000000000000000000000000000000000000000001c0 342 | // 0000000000000000000000000000000000000000000000000000000000000220 343 | // 0000000000000000000000000000000000000000000000000000000000000002 344 | // 0000000000000000000000000000000000000000000000000000000000000064 345 | // 00000000000000000000000000000000000000000000000000000000000000c8 346 | // 0000000000000000000000000000000000000000000000000000000000000002 347 | // 0000000000000000000000000000000000000000000000000000000000000005 348 | // 000000000000000000000000000000000000000000000000000000000000000a 349 | // 0000000000000000000000000000000000000000000000000000000000000002 350 | // 0000000000000000000000000000000000000000000000000000000000000002 351 | // 0000000000000000000000000000000000000000000000000000000000000004 352 | // 0000000000000000000000000000000000000000000000000000000000000002 353 | // 0000000000000000000000000000000000000000000000000000000000000000 354 | // 0000000000000000000000000000000000000000000000000000000000000001 355 | // 0000000000000000000000000000000000000000000000000000000000000002 356 | // 0000000000000000000000000000000000000000000000000000000000002710 357 | // 0000000000000000000000000000000000000000000000000000000000004e20 358 | 359 | // 前四项代表四个数组的起始位置,分别为 160、256、352、448 360 | // 后面每三项构成一个数组,第一项是数组元素的个数 361 | 362 | > myInstance.revealMulti.sendTransaction([100, 200], [5, 10], [2, 4], [0, 1], ["0x0000000000000000000000000000000000000000000000000000000000002710", "0x0000000000000000000000000000000000000000000000000000000000004e20"], {from : eth.coinbase, gas : 1000000}) 363 | "0x5d07eb8ff661bc77b078cf7bd416f92036af2a3bf5d4bac41756c073ba929a9f" 364 | 365 | > eth.getTransaction("0x5d07eb8ff661bc77b078cf7bd416f92036af2a3bf5d4bac41756c073ba929a9f") 366 | { 367 | blockHash: "0x2160f4bc8aeee99eb7841eaa4ad0f587c54227b2d97987b753dce46a2b89ca65", 368 | blockNumber: 3802, 369 | from: "0xa3ac96fbe4b0dce5f6f89a715ca00934d68f6c37", 370 | gas: 1000000, 371 | gasPrice: 50000000000, 372 | hash: "0x5d07eb8ff661bc77b078cf7bd416f92036af2a3bf5d4bac41756c073ba929a9f", 373 | input: "0x627adaa600000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000004e20", 374 | nonce: 38, 375 | r: "0x85eacb0db5e79f65a684efd23917db108e5a776f872a3dd87f1a505d8b5a3126", 376 | s: "0x323f52a60dd317dc957c241afae6b399cc67d24a1599d98a57739fe70846c62", 377 | to: "0xcdbab7ad4c77046f61d7846a48e5edbe423c2775", 378 | transactionIndex: 0, 379 | v: "0x26", 380 | value: 0 381 | } 382 | ``` 383 | -------------------------------------------------------------------------------- /solidity-learn-note.md: -------------------------------------------------------------------------------- 1 | ## Solidity 学习笔记 2 | 3 | **作者:李康** 4 | 5 | #### 1. 可见性 6 | 7 | 函数可以被定义为 `external`,`public`,`internal` 或者 `private`,默认是 `public`;对于状态变量来说,没有 `external` 类型,默认是 `internal`。 8 | 9 | - `external`:External 函数是合约接口的一部分,意味着它们可以被其它合约调用,也可以通过交易来调用。一个 external 函数 f 不能在内部调用(例如,f() 是不可行的,但是 this.f() 是可行的)。当接收大型数据数组的时候,external 函数有时是更高效的。 10 | 11 | - `public`:对于函数来说,可以在内部调用,或者通过消息调用,也可以被其它合约调用;对于状态变量来说,会自动生成一个 `getter` 函数 12 | 13 | - `internal`:这些类型的函数和状态变量只能够从内部访问(例如,从当前合约内部,或者是子合约的内部),无需使用 this 14 | 15 | - `private`:这些类型的函数和状态变量只能够从定义它们合约的内部访问 16 | 17 | 对于 public 和 external 类型的函数来说,区别在于一个可以在内部调用,一个不可以,而且 external 在传递大型数组时效率更高,具体原因请查看 18 | https://ethereum.stackexchange.com/questions/19380/external-vs-public-best-practices 19 | 20 | #### 2. call/callcode/delegatecall 21 | 22 | **delegatecall 的意思是:我是一个合约,我允许(委托)你对我的存储做任何事情,所以必须信任被调用合约不会随意修改调用合约的存储** 23 | 24 | delegatecall 是 callcode 的一个 bug fix,delegatecall 保留了 callcode 没有保留的 msg.sender 与 msg.value,下面来看一个具体的例子: 25 | 26 | ``` 27 | contract D { 28 | uint public n; 29 | address public sender; 30 | function callSetN(address _e, uint _n) { 31 | _e.call(bytes4(sha3("setN(uint256)")), _n); // E's storage is set, D is not modified 32 | } 33 | 34 | function callcodeSetN(address _e, uint _n) { 35 | _e.callcode(bytes4(sha3("setN(uint256)")), _n); // D's storage is set, E is not modified 36 | } 37 | 38 | function delegatecallSetN(address _e, uint _n) { 39 | _e.delegatecall(bytes4(sha3("setN(uint256)")), _n); // D's storage is set, E is not modified 40 | } 41 | } 42 | 43 | contract E { 44 | uint public n; 45 | address public sender; 46 | function setN(uint _n) { 47 | n = _n; 48 | sender = msg.sender; 49 | // msg.sender is D if invoked by D's callcodeSetN. None of E's storage is updated 50 | // msg.sender is C if invoked by C.foo(). None of E's storage is updated 51 | } 52 | } 53 | 54 | contract C { 55 | function foo(D _d, E _e, uint _n) { 56 | _d.delegatecallSetN(_e, _n); 57 | } 58 | } 59 | ``` 60 | 61 | 当合约 D CALL 合约 E 的时候,代码运行在 E 的上下文环境中:合约 E 的存储被使用。 62 | 63 | 当合约 D CALLCODE 合约 E 的时候,代码运行在 D 的上下文环境中,即合约 E 的代码存在于合约 D 中。当该代码修改存储的时候,发生变化的是合约 D 的存储,而不是 E 的。 64 | 65 | 当合约 D CALLCODE 合约 E 的时候,在 E 中调用 `msg.sender`,得到的结果是 D。 66 | 67 | 当一个合约 C 调用 D,然后 D 再 DELEGATECALL 合约 E,此时在 E 中调用 `msg.sender`,得到的结果是 C,也就是说 E 中有着与 D 中相同的 `msg.sender` 与 `msg.value`。 68 | 69 | #### 3. 异常处理 70 | 71 | 在智能合约中,我们常常会去调用某一个合约的函数,这时候通常有两种方法: 72 | 73 | - 使用 call/callcode/delegatecall/send/transfer 底层函数; 74 | 75 | - 使用被调用合约的名称以及它的函数名,算是高层次的调用 76 | 77 | 当被调用的合约函数出现异常时,这两种方法在 solidity 编译器处的处理逻辑是不一样的,底层函数的使用,会返回 true 或 false 交由调用合约来处理;高层次的调用会 bubbling up,意思是会导致调用合约抛出异常,进而回滚整个交易的操作,具体见 https://solidity.readthedocs.io/en/latest/control-structures.html?highlight=send#exceptions 78 | -------------------------------------------------------------------------------- /以太坊核心存储结构剖析.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightning-li/ethereum-learn/0a6a74bebf50dc325cd14ee8b2492dc323a4d408/以太坊核心存储结构剖析.docx --------------------------------------------------------------------------------