├── .gitignore ├── AUTHORS ├── CHANGES.txt ├── INSTALL ├── LICENSE.txt ├── MANIFEST.in ├── README.md ├── README.txt ├── examples ├── bip32-brainwallet.py ├── chain-test.py ├── fetch_block_header.py ├── fetch_history.py ├── fetch_tx.py ├── mktx.py ├── pubsub.py ├── query-blockchain.py ├── select_network.py ├── select_unspent.py ├── sign.py ├── subscribe_address.py └── total_connections.py ├── obelisk ├── __init__.py ├── binary.py ├── bitcoin.py ├── bitset.py ├── bittree.py ├── client.py ├── config.py ├── deserialize.py ├── error_code.py ├── models.py ├── numbertheory.py ├── serialize.py ├── test │ └── __init__.py ├── transaction.py ├── util.py ├── zmq_fallback.py └── zmqbase.py ├── requirements.txt └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | 47 | # Translations 48 | *.mo 49 | *.pot 50 | 51 | # Django stuff: 52 | *.log 53 | 54 | # Sphinx documentation 55 | docs/_build/ 56 | 57 | # PyBuilder 58 | target/ 59 | 60 | .idea/ 61 | _trial_temp/ -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | * Pablo Martin (caedes) 2 | * ThomasV, Electrum 3 | * Robert Williamson, blockalchemy (bobalot) 4 | * Amir Taaki (genjix) 5 | * Chris Pacia 6 | 7 | -------------------------------------------------------------------------------- /CHANGES.txt: -------------------------------------------------------------------------------- 1 | v0.1.3, 2014-08-12 -- Fix inclusion of package list of project so that library is importable, include twisted as dependency. 2 | v0.1.2, 2014-08-10 -- Include ecdsa and zmq requirements in installation. 3 | v0.1.1, 2014-08-10 -- Package as pip package. 4 | v0.1.0, 2013-09-30 -- Initial release. 5 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Install a few basic dependencies: 2 | 3 | # apt-get install python-pip python-twisted 4 | # pip install ecdsa 5 | # apt-get install cython 6 | 7 | Then install pyzmq. 8 | 9 | On Debian stable (wheezy), cython version is 0.15.1, which is insufficient 10 | to build the latest development version from git. Therefore, build a 11 | pre-packaged version (download from https://github.com/zeromq/pyzmq/releases). 12 | 13 | $ wget "https://github.com/zeromq/pyzmq/releases/download/v14.1.1/pyzmq-14.1.1.tar.gz" 14 | $ tar xf pyzmq-14.1.1.tar.gz 15 | $ cd pyzmq-14.1.1 16 | $ python setup.py configure --zmq=/usr/local/ 17 | # python setup.py install 18 | 19 | On Debian testing (jessie), a more up-to-date version of cython is 20 | available, therefore it is possible to build from git: 21 | 22 | $ git clone https://github.com/zeromq/pyzmq 23 | $ cd pyzmq 24 | $ python setup.py configure --zmq=/usr/local/ 25 | # python setup.py install 26 | 27 | Change the path /usr/local if you have zeromq installed elsewhere (for 28 | example, /home/user/usr/. 29 | 30 | It may also be possible to istall using pip (PyPI). 31 | 32 | Fially, install the package: 33 | 34 | # python setup.py install 35 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | GNU AFFERO GENERAL PUBLIC LICENSE 2 | Version 3, 19 November 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 Affero General Public License is a free, copyleft license for 11 | software and other kinds of works, specifically designed to ensure 12 | cooperation with the community in the case of network server software. 13 | 14 | The licenses for most software and other practical works are designed 15 | to take away your freedom to share and change the works. By contrast, 16 | our General Public Licenses are intended to guarantee your freedom to 17 | share and change all versions of a program--to make sure it remains free 18 | software for all its users. 19 | 20 | When we speak of free software, we are referring to freedom, not 21 | price. Our General Public Licenses are designed to make sure that you 22 | have the freedom to distribute copies of free software (and charge for 23 | them if you wish), that you receive source code or can get it if you 24 | want it, that you can change the software or use pieces of it in new 25 | free programs, and that you know you can do these things. 26 | 27 | Developers that use our General Public Licenses protect your rights 28 | with two steps: (1) assert copyright on the software, and (2) offer 29 | you this License which gives you legal permission to copy, distribute 30 | and/or modify the software. 31 | 32 | A secondary benefit of defending all users' freedom is that 33 | improvements made in alternate versions of the program, if they 34 | receive widespread use, become available for other developers to 35 | incorporate. Many developers of free software are heartened and 36 | encouraged by the resulting cooperation. However, in the case of 37 | software used on network servers, this result may fail to come about. 38 | The GNU General Public License permits making a modified version and 39 | letting the public access it on a server without ever releasing its 40 | source code to the public. 41 | 42 | The GNU Affero General Public License is designed specifically to 43 | ensure that, in such cases, the modified source code becomes available 44 | to the community. It requires the operator of a network server to 45 | provide the source code of the modified version running there to the 46 | users of that server. Therefore, public use of a modified version, on 47 | a publicly accessible server, gives the public access to the source 48 | code of the modified version. 49 | 50 | An older license, called the Affero General Public License and 51 | published by Affero, was designed to accomplish similar goals. This is 52 | a different license, not a version of the Affero GPL, but Affero has 53 | released a new version of the Affero GPL which permits relicensing under 54 | this license. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | TERMS AND CONDITIONS 60 | 61 | 0. Definitions. 62 | 63 | "This License" refers to version 3 of the GNU Affero General Public License. 64 | 65 | "Copyright" also means copyright-like laws that apply to other kinds of 66 | works, such as semiconductor masks. 67 | 68 | "The Program" refers to any copyrightable work licensed under this 69 | License. Each licensee is addressed as "you". "Licensees" and 70 | "recipients" may be individuals or organizations. 71 | 72 | To "modify" a work means to copy from or adapt all or part of the work 73 | in a fashion requiring copyright permission, other than the making of an 74 | exact copy. The resulting work is called a "modified version" of the 75 | earlier work or a work "based on" the earlier work. 76 | 77 | A "covered work" means either the unmodified Program or a work based 78 | on the Program. 79 | 80 | To "propagate" a work means to do anything with it that, without 81 | permission, would make you directly or secondarily liable for 82 | infringement under applicable copyright law, except executing it on a 83 | computer or modifying a private copy. Propagation includes copying, 84 | distribution (with or without modification), making available to the 85 | public, and in some countries other activities as well. 86 | 87 | To "convey" a work means any kind of propagation that enables other 88 | parties to make or receive copies. Mere interaction with a user through 89 | a computer network, with no transfer of a copy, is not conveying. 90 | 91 | An interactive user interface displays "Appropriate Legal Notices" 92 | to the extent that it includes a convenient and prominently visible 93 | feature that (1) displays an appropriate copyright notice, and (2) 94 | tells the user that there is no warranty for the work (except to the 95 | extent that warranties are provided), that licensees may convey the 96 | work under this License, and how to view a copy of this License. If 97 | the interface presents a list of user commands or options, such as a 98 | menu, a prominent item in the list meets this criterion. 99 | 100 | 1. Source Code. 101 | 102 | The "source code" for a work means the preferred form of the work 103 | for making modifications to it. "Object code" means any non-source 104 | form of a work. 105 | 106 | A "Standard Interface" means an interface that either is an official 107 | standard defined by a recognized standards body, or, in the case of 108 | interfaces specified for a particular programming language, one that 109 | is widely used among developers working in that language. 110 | 111 | The "System Libraries" of an executable work include anything, other 112 | than the work as a whole, that (a) is included in the normal form of 113 | packaging a Major Component, but which is not part of that Major 114 | Component, and (b) serves only to enable use of the work with that 115 | Major Component, or to implement a Standard Interface for which an 116 | implementation is available to the public in source code form. A 117 | "Major Component", in this context, means a major essential component 118 | (kernel, window system, and so on) of the specific operating system 119 | (if any) on which the executable work runs, or a compiler used to 120 | produce the work, or an object code interpreter used to run it. 121 | 122 | The "Corresponding Source" for a work in object code form means all 123 | the source code needed to generate, install, and (for an executable 124 | work) run the object code and to modify the work, including scripts to 125 | control those activities. However, it does not include the work's 126 | System Libraries, or general-purpose tools or generally available free 127 | programs which are used unmodified in performing those activities but 128 | which are not part of the work. For example, Corresponding Source 129 | includes interface definition files associated with source files for 130 | the work, and the source code for shared libraries and dynamically 131 | linked subprograms that the work is specifically designed to require, 132 | such as by intimate data communication or control flow between those 133 | subprograms and other parts of the work. 134 | 135 | The Corresponding Source need not include anything that users 136 | can regenerate automatically from other parts of the Corresponding 137 | Source. 138 | 139 | The Corresponding Source for a work in source code form is that 140 | same work. 141 | 142 | 2. Basic Permissions. 143 | 144 | All rights granted under this License are granted for the term of 145 | copyright on the Program, and are irrevocable provided the stated 146 | conditions are met. This License explicitly affirms your unlimited 147 | permission to run the unmodified Program. The output from running a 148 | covered work is covered by this License only if the output, given its 149 | content, constitutes a covered work. This License acknowledges your 150 | rights of fair use or other equivalent, as provided by copyright law. 151 | 152 | You may make, run and propagate covered works that you do not 153 | convey, without conditions so long as your license otherwise remains 154 | in force. You may convey covered works to others for the sole purpose 155 | of having them make modifications exclusively for you, or provide you 156 | with facilities for running those works, provided that you comply with 157 | the terms of this License in conveying all material for which you do 158 | not control copyright. Those thus making or running the covered works 159 | for you must do so exclusively on your behalf, under your direction 160 | and control, on terms that prohibit them from making any copies of 161 | your copyrighted material outside their relationship with you. 162 | 163 | Conveying under any other circumstances is permitted solely under 164 | the conditions stated below. Sublicensing is not allowed; section 10 165 | makes it unnecessary. 166 | 167 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 168 | 169 | No covered work shall be deemed part of an effective technological 170 | measure under any applicable law fulfilling obligations under article 171 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 172 | similar laws prohibiting or restricting circumvention of such 173 | measures. 174 | 175 | When you convey a covered work, you waive any legal power to forbid 176 | circumvention of technological measures to the extent such circumvention 177 | is effected by exercising rights under this License with respect to 178 | the covered work, and you disclaim any intention to limit operation or 179 | modification of the work as a means of enforcing, against the work's 180 | users, your or third parties' legal rights to forbid circumvention of 181 | technological measures. 182 | 183 | 4. Conveying Verbatim Copies. 184 | 185 | You may convey verbatim copies of the Program's source code as you 186 | receive it, in any medium, provided that you conspicuously and 187 | appropriately publish on each copy an appropriate copyright notice; 188 | keep intact all notices stating that this License and any 189 | non-permissive terms added in accord with section 7 apply to the code; 190 | keep intact all notices of the absence of any warranty; and give all 191 | recipients a copy of this License along with the Program. 192 | 193 | You may charge any price or no price for each copy that you convey, 194 | and you may offer support or warranty protection for a fee. 195 | 196 | 5. Conveying Modified Source Versions. 197 | 198 | You may convey a work based on the Program, or the modifications to 199 | produce it from the Program, in the form of source code under the 200 | terms of section 4, provided that you also meet all of these conditions: 201 | 202 | a) The work must carry prominent notices stating that you modified 203 | it, and giving a relevant date. 204 | 205 | b) The work must carry prominent notices stating that it is 206 | released under this License and any conditions added under section 207 | 7. This requirement modifies the requirement in section 4 to 208 | "keep intact all notices". 209 | 210 | c) You must license the entire work, as a whole, under this 211 | License to anyone who comes into possession of a copy. This 212 | License will therefore apply, along with any applicable section 7 213 | additional terms, to the whole of the work, and all its parts, 214 | regardless of how they are packaged. This License gives no 215 | permission to license the work in any other way, but it does not 216 | invalidate such permission if you have separately received it. 217 | 218 | d) If the work has interactive user interfaces, each must display 219 | Appropriate Legal Notices; however, if the Program has interactive 220 | interfaces that do not display Appropriate Legal Notices, your 221 | work need not make them do so. 222 | 223 | A compilation of a covered work with other separate and independent 224 | works, which are not by their nature extensions of the covered work, 225 | and which are not combined with it such as to form a larger program, 226 | in or on a volume of a storage or distribution medium, is called an 227 | "aggregate" if the compilation and its resulting copyright are not 228 | used to limit the access or legal rights of the compilation's users 229 | beyond what the individual works permit. Inclusion of a covered work 230 | in an aggregate does not cause this License to apply to the other 231 | parts of the aggregate. 232 | 233 | 6. Conveying Non-Source Forms. 234 | 235 | You may convey a covered work in object code form under the terms 236 | of sections 4 and 5, provided that you also convey the 237 | machine-readable Corresponding Source under the terms of this License, 238 | in one of these ways: 239 | 240 | a) Convey the object code in, or embodied in, a physical product 241 | (including a physical distribution medium), accompanied by the 242 | Corresponding Source fixed on a durable physical medium 243 | customarily used for software interchange. 244 | 245 | b) Convey the object code in, or embodied in, a physical product 246 | (including a physical distribution medium), accompanied by a 247 | written offer, valid for at least three years and valid for as 248 | long as you offer spare parts or customer support for that product 249 | model, to give anyone who possesses the object code either (1) a 250 | copy of the Corresponding Source for all the software in the 251 | product that is covered by this License, on a durable physical 252 | medium customarily used for software interchange, for a price no 253 | more than your reasonable cost of physically performing this 254 | conveying of source, or (2) access to copy the 255 | Corresponding Source from a network server at no charge. 256 | 257 | c) Convey individual copies of the object code with a copy of the 258 | written offer to provide the Corresponding Source. This 259 | alternative is allowed only occasionally and noncommercially, and 260 | only if you received the object code with such an offer, in accord 261 | with subsection 6b. 262 | 263 | d) Convey the object code by offering access from a designated 264 | place (gratis or for a charge), and offer equivalent access to the 265 | Corresponding Source in the same way through the same place at no 266 | further charge. You need not require recipients to copy the 267 | Corresponding Source along with the object code. If the place to 268 | copy the object code is a network server, the Corresponding Source 269 | may be on a different server (operated by you or a third party) 270 | that supports equivalent copying facilities, provided you maintain 271 | clear directions next to the object code saying where to find the 272 | Corresponding Source. Regardless of what server hosts the 273 | Corresponding Source, you remain obligated to ensure that it is 274 | available for as long as needed to satisfy these requirements. 275 | 276 | e) Convey the object code using peer-to-peer transmission, provided 277 | you inform other peers where the object code and Corresponding 278 | Source of the work are being offered to the general public at no 279 | charge under subsection 6d. 280 | 281 | A separable portion of the object code, whose source code is excluded 282 | from the Corresponding Source as a System Library, need not be 283 | included in conveying the object code work. 284 | 285 | A "User Product" is either (1) a "consumer product", which means any 286 | tangible personal property which is normally used for personal, family, 287 | or household purposes, or (2) anything designed or sold for incorporation 288 | into a dwelling. In determining whether a product is a consumer product, 289 | doubtful cases shall be resolved in favor of coverage. For a particular 290 | product received by a particular user, "normally used" refers to a 291 | typical or common use of that class of product, regardless of the status 292 | of the particular user or of the way in which the particular user 293 | actually uses, or expects or is expected to use, the product. A product 294 | is a consumer product regardless of whether the product has substantial 295 | commercial, industrial or non-consumer uses, unless such uses represent 296 | the only significant mode of use of the product. 297 | 298 | "Installation Information" for a User Product means any methods, 299 | procedures, authorization keys, or other information required to install 300 | and execute modified versions of a covered work in that User Product from 301 | a modified version of its Corresponding Source. The information must 302 | suffice to ensure that the continued functioning of the modified object 303 | code is in no case prevented or interfered with solely because 304 | modification has been made. 305 | 306 | If you convey an object code work under this section in, or with, or 307 | specifically for use in, a User Product, and the conveying occurs as 308 | part of a transaction in which the right of possession and use of the 309 | User Product is transferred to the recipient in perpetuity or for a 310 | fixed term (regardless of how the transaction is characterized), the 311 | Corresponding Source conveyed under this section must be accompanied 312 | by the Installation Information. But this requirement does not apply 313 | if neither you nor any third party retains the ability to install 314 | modified object code on the User Product (for example, the work has 315 | been installed in ROM). 316 | 317 | The requirement to provide Installation Information does not include a 318 | requirement to continue to provide support service, warranty, or updates 319 | for a work that has been modified or installed by the recipient, or for 320 | the User Product in which it has been modified or installed. Access to a 321 | network may be denied when the modification itself materially and 322 | adversely affects the operation of the network or violates the rules and 323 | protocols for communication across the network. 324 | 325 | Corresponding Source conveyed, and Installation Information provided, 326 | in accord with this section must be in a format that is publicly 327 | documented (and with an implementation available to the public in 328 | source code form), and must require no special password or key for 329 | unpacking, reading or copying. 330 | 331 | 7. Additional Terms. 332 | 333 | "Additional permissions" are terms that supplement the terms of this 334 | License by making exceptions from one or more of its conditions. 335 | Additional permissions that are applicable to the entire Program shall 336 | be treated as though they were included in this License, to the extent 337 | that they are valid under applicable law. If additional permissions 338 | apply only to part of the Program, that part may be used separately 339 | under those permissions, but the entire Program remains governed by 340 | this License without regard to the additional permissions. 341 | 342 | When you convey a copy of a covered work, you may at your option 343 | remove any additional permissions from that copy, or from any part of 344 | it. (Additional permissions may be written to require their own 345 | removal in certain cases when you modify the work.) You may place 346 | additional permissions on material, added by you to a covered work, 347 | for which you have or can give appropriate copyright permission. 348 | 349 | Notwithstanding any other provision of this License, for material you 350 | add to a covered work, you may (if authorized by the copyright holders of 351 | that material) supplement the terms of this License with terms: 352 | 353 | a) Disclaiming warranty or limiting liability differently from the 354 | terms of sections 15 and 16 of this License; or 355 | 356 | b) Requiring preservation of specified reasonable legal notices or 357 | author attributions in that material or in the Appropriate Legal 358 | Notices displayed by works containing it; or 359 | 360 | c) Prohibiting misrepresentation of the origin of that material, or 361 | requiring that modified versions of such material be marked in 362 | reasonable ways as different from the original version; or 363 | 364 | d) Limiting the use for publicity purposes of names of licensors or 365 | authors of the material; or 366 | 367 | e) Declining to grant rights under trademark law for use of some 368 | trade names, trademarks, or service marks; or 369 | 370 | f) Requiring indemnification of licensors and authors of that 371 | material by anyone who conveys the material (or modified versions of 372 | it) with contractual assumptions of liability to the recipient, for 373 | any liability that these contractual assumptions directly impose on 374 | those licensors and authors. 375 | 376 | All other non-permissive additional terms are considered "further 377 | restrictions" within the meaning of section 10. If the Program as you 378 | received it, or any part of it, contains a notice stating that it is 379 | governed by this License along with a term that is a further 380 | restriction, you may remove that term. If a license document contains 381 | a further restriction but permits relicensing or conveying under this 382 | License, you may add to a covered work material governed by the terms 383 | of that license document, provided that the further restriction does 384 | not survive such relicensing or conveying. 385 | 386 | If you add terms to a covered work in accord with this section, you 387 | must place, in the relevant source files, a statement of the 388 | additional terms that apply to those files, or a notice indicating 389 | where to find the applicable terms. 390 | 391 | Additional terms, permissive or non-permissive, may be stated in the 392 | form of a separately written license, or stated as exceptions; 393 | the above requirements apply either way. 394 | 395 | 8. Termination. 396 | 397 | You may not propagate or modify a covered work except as expressly 398 | provided under this License. Any attempt otherwise to propagate or 399 | modify it is void, and will automatically terminate your rights under 400 | this License (including any patent licenses granted under the third 401 | paragraph of section 11). 402 | 403 | However, if you cease all violation of this License, then your 404 | license from a particular copyright holder is reinstated (a) 405 | provisionally, unless and until the copyright holder explicitly and 406 | finally terminates your license, and (b) permanently, if the copyright 407 | holder fails to notify you of the violation by some reasonable means 408 | prior to 60 days after the cessation. 409 | 410 | Moreover, your license from a particular copyright holder is 411 | reinstated permanently if the copyright holder notifies you of the 412 | violation by some reasonable means, this is the first time you have 413 | received notice of violation of this License (for any work) from that 414 | copyright holder, and you cure the violation prior to 30 days after 415 | your receipt of the notice. 416 | 417 | Termination of your rights under this section does not terminate the 418 | licenses of parties who have received copies or rights from you under 419 | this License. If your rights have been terminated and not permanently 420 | reinstated, you do not qualify to receive new licenses for the same 421 | material under section 10. 422 | 423 | 9. Acceptance Not Required for Having Copies. 424 | 425 | You are not required to accept this License in order to receive or 426 | run a copy of the Program. Ancillary propagation of a covered work 427 | occurring solely as a consequence of using peer-to-peer transmission 428 | to receive a copy likewise does not require acceptance. However, 429 | nothing other than this License grants you permission to propagate or 430 | modify any covered work. These actions infringe copyright if you do 431 | not accept this License. Therefore, by modifying or propagating a 432 | covered work, you indicate your acceptance of this License to do so. 433 | 434 | 10. Automatic Licensing of Downstream Recipients. 435 | 436 | Each time you convey a covered work, the recipient automatically 437 | receives a license from the original licensors, to run, modify and 438 | propagate that work, subject to this License. You are not responsible 439 | for enforcing compliance by third parties with this License. 440 | 441 | An "entity transaction" is a transaction transferring control of an 442 | organization, or substantially all assets of one, or subdividing an 443 | organization, or merging organizations. If propagation of a covered 444 | work results from an entity transaction, each party to that 445 | transaction who receives a copy of the work also receives whatever 446 | licenses to the work the party's predecessor in interest had or could 447 | give under the previous paragraph, plus a right to possession of the 448 | Corresponding Source of the work from the predecessor in interest, if 449 | the predecessor has it or can get it with reasonable efforts. 450 | 451 | You may not impose any further restrictions on the exercise of the 452 | rights granted or affirmed under this License. For example, you may 453 | not impose a license fee, royalty, or other charge for exercise of 454 | rights granted under this License, and you may not initiate litigation 455 | (including a cross-claim or counterclaim in a lawsuit) alleging that 456 | any patent claim is infringed by making, using, selling, offering for 457 | sale, or importing the Program or any portion of it. 458 | 459 | 11. Patents. 460 | 461 | A "contributor" is a copyright holder who authorizes use under this 462 | License of the Program or a work on which the Program is based. The 463 | work thus licensed is called the contributor's "contributor version". 464 | 465 | A contributor's "essential patent claims" are all patent claims 466 | owned or controlled by the contributor, whether already acquired or 467 | hereafter acquired, that would be infringed by some manner, permitted 468 | by this License, of making, using, or selling its contributor version, 469 | but do not include claims that would be infringed only as a 470 | consequence of further modification of the contributor version. For 471 | purposes of this definition, "control" includes the right to grant 472 | patent sublicenses in a manner consistent with the requirements of 473 | this License. 474 | 475 | Each contributor grants you a non-exclusive, worldwide, royalty-free 476 | patent license under the contributor's essential patent claims, to 477 | make, use, sell, offer for sale, import and otherwise run, modify and 478 | propagate the contents of its contributor version. 479 | 480 | In the following three paragraphs, a "patent license" is any express 481 | agreement or commitment, however denominated, not to enforce a patent 482 | (such as an express permission to practice a patent or covenant not to 483 | sue for patent infringement). To "grant" such a patent license to a 484 | party means to make such an agreement or commitment not to enforce a 485 | patent against the party. 486 | 487 | If you convey a covered work, knowingly relying on a patent license, 488 | and the Corresponding Source of the work is not available for anyone 489 | to copy, free of charge and under the terms of this License, through a 490 | publicly available network server or other readily accessible means, 491 | then you must either (1) cause the Corresponding Source to be so 492 | available, or (2) arrange to deprive yourself of the benefit of the 493 | patent license for this particular work, or (3) arrange, in a manner 494 | consistent with the requirements of this License, to extend the patent 495 | license to downstream recipients. "Knowingly relying" means you have 496 | actual knowledge that, but for the patent license, your conveying the 497 | covered work in a country, or your recipient's use of the covered work 498 | in a country, would infringe one or more identifiable patents in that 499 | country that you have reason to believe are valid. 500 | 501 | If, pursuant to or in connection with a single transaction or 502 | arrangement, you convey, or propagate by procuring conveyance of, a 503 | covered work, and grant a patent license to some of the parties 504 | receiving the covered work authorizing them to use, propagate, modify 505 | or convey a specific copy of the covered work, then the patent license 506 | you grant is automatically extended to all recipients of the covered 507 | work and works based on it. 508 | 509 | A patent license is "discriminatory" if it does not include within 510 | the scope of its coverage, prohibits the exercise of, or is 511 | conditioned on the non-exercise of one or more of the rights that are 512 | specifically granted under this License. You may not convey a covered 513 | work if you are a party to an arrangement with a third party that is 514 | in the business of distributing software, under which you make payment 515 | to the third party based on the extent of your activity of conveying 516 | the work, and under which the third party grants, to any of the 517 | parties who would receive the covered work from you, a discriminatory 518 | patent license (a) in connection with copies of the covered work 519 | conveyed by you (or copies made from those copies), or (b) primarily 520 | for and in connection with specific products or compilations that 521 | contain the covered work, unless you entered into that arrangement, 522 | or that patent license was granted, prior to 28 March 2007. 523 | 524 | Nothing in this License shall be construed as excluding or limiting 525 | any implied license or other defenses to infringement that may 526 | otherwise be available to you under applicable patent law. 527 | 528 | 12. No Surrender of Others' Freedom. 529 | 530 | If conditions are imposed on you (whether by court order, agreement or 531 | otherwise) that contradict the conditions of this License, they do not 532 | excuse you from the conditions of this License. If you cannot convey a 533 | covered work so as to satisfy simultaneously your obligations under this 534 | License and any other pertinent obligations, then as a consequence you may 535 | not convey it at all. For example, if you agree to terms that obligate you 536 | to collect a royalty for further conveying from those to whom you convey 537 | the Program, the only way you could satisfy both those terms and this 538 | License would be to refrain entirely from conveying the Program. 539 | 540 | 13. Remote Network Interaction; Use with the GNU General Public License. 541 | 542 | Notwithstanding any other provision of this License, if you modify the 543 | Program, your modified version must prominently offer all users 544 | interacting with it remotely through a computer network (if your version 545 | supports such interaction) an opportunity to receive the Corresponding 546 | Source of your version by providing access to the Corresponding Source 547 | from a network server at no charge, through some standard or customary 548 | means of facilitating copying of software. This Corresponding Source 549 | shall include the Corresponding Source for any work covered by version 3 550 | of the GNU General Public License that is incorporated pursuant to the 551 | following paragraph. 552 | 553 | Notwithstanding any other provision of this License, you have 554 | permission to link or combine any covered work with a work licensed 555 | under version 3 of the GNU General Public License into a single 556 | combined work, and to convey the resulting work. The terms of this 557 | License will continue to apply to the part which is the covered work, 558 | but the work with which it is combined will remain governed by version 559 | 3 of the GNU General Public License. 560 | 561 | 14. Revised Versions of this License. 562 | 563 | The Free Software Foundation may publish revised and/or new versions of 564 | the GNU Affero General Public License from time to time. Such new versions 565 | will be similar in spirit to the present version, but may differ in detail to 566 | address new problems or concerns. 567 | 568 | Each version is given a distinguishing version number. If the 569 | Program specifies that a certain numbered version of the GNU Affero General 570 | Public License "or any later version" applies to it, you have the 571 | option of following the terms and conditions either of that numbered 572 | version or of any later version published by the Free Software 573 | Foundation. If the Program does not specify a version number of the 574 | GNU Affero General Public License, you may choose any version ever published 575 | by the Free Software Foundation. 576 | 577 | If the Program specifies that a proxy can decide which future 578 | versions of the GNU Affero General Public License can be used, that proxy's 579 | public statement of acceptance of a version permanently authorizes you 580 | to choose that version for the Program. 581 | 582 | Later license versions may give you additional or different 583 | permissions. However, no additional obligations are imposed on any 584 | author or copyright holder as a result of your choosing to follow a 585 | later version. 586 | 587 | 15. Disclaimer of Warranty. 588 | 589 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 590 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 591 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 592 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 593 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 594 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 595 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 596 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 597 | 598 | 16. Limitation of Liability. 599 | 600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 602 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 603 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 604 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 605 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 606 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 607 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 608 | SUCH DAMAGES. 609 | 610 | 17. Interpretation of Sections 15 and 16. 611 | 612 | If the disclaimer of warranty and limitation of liability provided 613 | above cannot be given local legal effect according to their terms, 614 | reviewing courts shall apply local law that most closely approximates 615 | an absolute waiver of all civil liability in connection with the 616 | Program, unless a warranty or assumption of liability accompanies a 617 | copy of the Program in return for a fee. 618 | 619 | END OF TERMS AND CONDITIONS 620 | 621 | How to Apply These Terms to Your New Programs 622 | 623 | If you develop a new program, and you want it to be of the greatest 624 | possible use to the public, the best way to achieve this is to make it 625 | free software which everyone can redistribute and change under these terms. 626 | 627 | To do so, attach the following notices to the program. It is safest 628 | to attach them to the start of each source file to most effectively 629 | state the exclusion of warranty; and each file should have at least 630 | the "copyright" line and a pointer to where the full notice is found. 631 | 632 | 633 | Copyright (C) 634 | 635 | This program is free software: you can redistribute it and/or modify 636 | it under the terms of the GNU Affero General Public License as published by 637 | the Free Software Foundation, either version 3 of the License, or 638 | (at your option) any later version. 639 | 640 | This program is distributed in the hope that it will be useful, 641 | but WITHOUT ANY WARRANTY; without even the implied warranty of 642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 643 | GNU Affero General Public License for more details. 644 | 645 | You should have received a copy of the GNU Affero General Public License 646 | along with this program. If not, see . 647 | 648 | Also add information on how to contact you by electronic and paper mail. 649 | 650 | If your software can interact with users remotely through a computer 651 | network, you should also make sure that it provides a way for users to 652 | get its source. For example, if your program is a web application, its 653 | interface could display a "Source" link that leads users to an archive 654 | of the code. There are many ways you could offer source, and different 655 | solutions will be better for different programs; see section 13 for the 656 | specific requirements. 657 | 658 | You should also get your employer (if you work as a programmer) or school, 659 | if any, to sign a "copyright disclaimer" for the program, if necessary. 660 | For more information on this, and how to apply and follow the GNU AGPL, see 661 | . 662 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.txt 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Obelisk zmq native client 2 | =============================== 3 | 4 | Python native client for the obelisk blockchain server. 5 | 6 | 7 | Dependencies: 8 | ------------------ 9 | * python-twisted 10 | * python-ecdsa 11 | * python-zmqproto 12 | 13 | 14 | Notes: 15 | ------------------ 16 | Don't forget to check out these other python projects: 17 | 18 | * electrum 19 | * pybitcointools 20 | 21 | -- 22 | 23 | unsystem dev 24 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | ========================= 2 | Obelisk zmq native client 3 | ========================= 4 | 5 | Python native client for the obelisk blockchain server. 6 | -------------------------------------------------------------------------------- /examples/bip32-brainwallet.py: -------------------------------------------------------------------------------- 1 | import obelisk 2 | 3 | 4 | def test_bip32(seed, sequence): 5 | """ 6 | run a test vector, 7 | see https://en.bitcoin.it/wiki/BIP_0032_TestVectors 8 | """ 9 | 10 | wallet = obelisk.HighDefWallet.root(seed) 11 | 12 | print "secret key", wallet.secret.encode('hex') 13 | print "chain code", wallet.chain.encode('hex') 14 | 15 | print "keyid", wallet.key_id.encode("hex") 16 | print "base58" 17 | print "address", wallet.address 18 | print "secret key", wallet.secret_key 19 | 20 | s = ['m'] 21 | for n in sequence.split('/'): 22 | s.append(n) 23 | print "Chain [%s]" % '/'.join(s) 24 | 25 | if n[-1] == "'": 26 | n = int(n[:-1]) 27 | wallet = wallet.branch_prime(n) 28 | else: 29 | n = int(n) 30 | wallet = wallet.branch(n) 31 | 32 | print "* Identifier" 33 | print " * (main addr)", wallet.address 34 | 35 | print "* Secret Key" 36 | print " * (hex)", wallet.secret.encode("hex") 37 | print " * (wif)", wallet.secret_key 38 | 39 | print "* Chain Code" 40 | print " * (hex)", wallet.chain.encode("hex") 41 | print "----" 42 | 43 | if __name__ == '__main__': 44 | test_bip32("000102030405060708090a0b0c0d0e0f", "0'/1/2'/2/1000000000") 45 | test_bip32("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542", "0/2147483647'/1/2147483646'/2") 46 | -------------------------------------------------------------------------------- /examples/chain-test.py: -------------------------------------------------------------------------------- 1 | import obelisk 2 | from twisted.internet import reactor 3 | 4 | 5 | def tx_fetched(ec, tx): 6 | print "Tx:", ec, tx.encode("hex") 7 | 8 | 9 | def spend_fetched(ec, spend): 10 | print "Spend:", ec, spend 11 | 12 | 13 | def txidx_fetched(ec, height, index): 14 | print "Tx index:", ec, height, index 15 | 16 | 17 | def txhashes_fetched(ec, hashes): 18 | print "Tx hashes:", [h.encode("hex") for h in hashes] 19 | 20 | 21 | def height_fetched(ec, height): 22 | print "Height:", height 23 | 24 | def history_fetched(ec, history): 25 | print "History:" 26 | for id, hash, index, height, value in history: 27 | print id, hash.encode("hex") + ":" + str(index), height, value 28 | if id == obelisk.PointIdent.Output: 29 | print " checksum =", obelisk.spend_checksum(hash, index) 30 | # elif id == obelisk.PointIdent.Spend: 31 | # ... do something 32 | 33 | def stealth_fetched(ec, result): 34 | print "Stealth:" 35 | for ephemkey, address, tx_hash in result: 36 | print " ephemkey:", ephemkey.encode("hex"), \ 37 | "address:", address.encode("hex"), \ 38 | "tx_hash:", tx_hash.encode("hex") 39 | 40 | 41 | if __name__ == '__main__': 42 | client = obelisk.ObeliskOfLightClient("tcp://localhost:9091") 43 | tx_hash = "e9a66845e05d5abc0ad04ec80f774a7e" \ 44 | "585c6e8db975962d069a522137b80c1d".decode("hex") 45 | client.fetch_transaction(tx_hash, tx_fetched) 46 | outpoint = obelisk.OutPoint() 47 | outpoint.hash = "f4515fed3dc4a19b90a317b9840c243b" \ 48 | "ac26114cf637522373a7d486b372600b".decode("hex") 49 | outpoint.index = 0 50 | client.fetch_spend(outpoint, spend_fetched) 51 | client.fetch_transaction_index(outpoint.hash, txidx_fetched) 52 | client.fetch_block_transaction_hashes(tx_hash, txhashes_fetched) 53 | blk_hash = "000000000003ba27aa200b1cecaad478" \ 54 | "d2b00432346c3f1f3986da1afd33e506".decode("hex") 55 | client.fetch_block_height(blk_hash, height_fetched) 56 | client.fetch_last_height(height_fetched) 57 | client.fetch_history("13cbkpKW2DdhEUmeGC2h9HyxbBdtc7Wcth", history_fetched) 58 | # You can compare with blockchain db using libbitcoin-blockchain/tools/ 59 | # $ stealth_db scan stealth_index stealth_rows 101010101 0 60 | client.fetch_stealth("101010101", stealth_fetched) 61 | reactor.run() 62 | 63 | -------------------------------------------------------------------------------- /examples/fetch_block_header.py: -------------------------------------------------------------------------------- 1 | import obelisk 2 | from twisted.internet import reactor 3 | 4 | if __name__ == '__main__': 5 | c = obelisk.ObeliskOfLightClient("tcp://localhost:9091") 6 | h = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f" 7 | 8 | def cb(*args): 9 | ec, result = args 10 | print result.encode("hex") 11 | reactor.stop() 12 | 13 | c.fetch_block_header(h.decode("hex"), cb) 14 | 15 | reactor.run() 16 | -------------------------------------------------------------------------------- /examples/fetch_history.py: -------------------------------------------------------------------------------- 1 | import obelisk 2 | import sys 3 | from twisted.internet import reactor 4 | 5 | def main(address): 6 | c = obelisk.ObeliskOfLightClient("tcp://localhost:9091") 7 | 8 | def history_fetched(ec, history): 9 | if ec: 10 | print ec 11 | else: 12 | for id, hash, index, height, value in history: 13 | if id == obelisk.PointIdent.Output: 14 | type = "Output" 15 | #print " checksum =", obelisk.spend_checksum(hash, index) 16 | else: # == obelisk.PointIdent.Spend 17 | type = "Spend " 18 | print type, hash.encode("hex") + ":" + str(index), height, value 19 | reactor.stop() 20 | 21 | c.fetch_history2(address, history_fetched) 22 | 23 | reactor.run() 24 | 25 | if __name__ == '__main__': 26 | if len(sys.argv) != 2: 27 | print >> sys.stderr, "Need ADDRESS argument." 28 | else: 29 | main(sys.argv[1]) 30 | 31 | -------------------------------------------------------------------------------- /examples/fetch_tx.py: -------------------------------------------------------------------------------- 1 | import obelisk 2 | import sys 3 | from twisted.internet import reactor 4 | 5 | def main(txhash): 6 | c = obelisk.ObeliskOfLightClient("tcp://localhost:9091") 7 | txhash = txhash.decode("hex") 8 | 9 | def cb_txpool(ec, result): 10 | if ec: 11 | c.fetch_transaction(txhash, cb_chain) 12 | else: 13 | print "From Txpool:" 14 | print result.encode("hex") 15 | reactor.stop() 16 | 17 | def cb_chain(ec, result): 18 | if ec: 19 | print ec 20 | else: 21 | print "From Blockchain:" 22 | print result.encode("hex") 23 | reactor.stop() 24 | 25 | c.fetch_txpool_transaction(txhash, cb_txpool) 26 | 27 | reactor.run() 28 | 29 | if __name__ == '__main__': 30 | if len(sys.argv) != 2: 31 | print >> sys.stderr, "Need TXHASH argument." 32 | else: 33 | main(sys.argv[1]) 34 | 35 | -------------------------------------------------------------------------------- /examples/mktx.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import obelisk 3 | from decimal import Decimal as D 4 | 5 | 6 | def main(argv): 7 | tx = obelisk.Transaction() 8 | for switch, param in zip(argv[1::2], argv[2::2]): 9 | if switch == "-i": 10 | tx_hash, tx_index = param.split(":") 11 | tx_hash = tx_hash.decode("hex") 12 | tx_index = int(tx_index) 13 | add_input(tx, tx_hash, tx_index) 14 | elif switch == "-o": 15 | address, value = param.split(":") 16 | value = D(value) 17 | add_output(tx, address, value) 18 | # initialize signing key 19 | key = obelisk.EllipticCurveKey() 20 | secret = "59cd7a1d11ef24a1687b7c20bdb9f3bb" \ 21 | "1cb93908401c503f8d69521dbfcd1c6d".decode("hex") 22 | assert len(secret) == 32 23 | key.set_secret(secret) 24 | # sign input 0 25 | obelisk.sign_transaction_input(tx, 0, key) 26 | print tx 27 | print tx.serialize().encode("hex") 28 | 29 | 30 | def add_input(tx, tx_hash, tx_index): 31 | input = obelisk.TxIn() 32 | input.previous_output.hash = tx_hash 33 | input.previous_output.index = tx_index 34 | tx.inputs.append(input) 35 | 36 | 37 | def add_output(tx, address, value): 38 | output = obelisk.TxOut() 39 | output.value = int(value * 10**8) 40 | output.script = obelisk.output_script(address) 41 | tx.outputs.append(output) 42 | 43 | 44 | if __name__ == "__main__": 45 | sys.exit(main(sys.argv)) 46 | -------------------------------------------------------------------------------- /examples/pubsub.py: -------------------------------------------------------------------------------- 1 | from twisted.internet import reactor 2 | 3 | import obelisk 4 | 5 | #################################################### 6 | # Class to receive pubsub callbacks 7 | # 8 | # When you pass the block and tx addresses in the client constructor 9 | # the base class automatically calls 'on_raw_block' and 'on_raw_transaction' 10 | # callbacks when notifications arrive. 11 | # 12 | # If you prefer to pass your own callbacks, don't pass the addresses to 13 | # the constructor, and call 'setup_block_sub' and/or 14 | # 'setup_transaction_sub' yourself. 15 | 16 | 17 | class ObeliskPubSubClient(obelisk.ObeliskOfLightClient): 18 | def on_raw_block(self, height, hash, header, tx_num, tx_hashes): 19 | print "* block", height, len(tx_hashes) 20 | 21 | def on_raw_transaction(self, tx_data): 22 | tx = obelisk.serialize.deser_tx(tx_data) 23 | outputs = [] 24 | for output in tx.outputs: 25 | outputs.append(obelisk.util.format_satoshis(output.value)) 26 | print "* tx", ", ".join(outputs) 27 | 28 | if __name__ == '__main__': 29 | c = ObeliskPubSubClient( 30 | 'tcp://85.25.198.97:9091', 31 | 'tcp://85.25.198.97:9093', 32 | 'tcp://85.25.198.97:9094' 33 | ) 34 | reactor.run() 35 | -------------------------------------------------------------------------------- /examples/query-blockchain.py: -------------------------------------------------------------------------------- 1 | import obelisk 2 | import os 3 | import sys 4 | 5 | from twisted.internet import reactor 6 | 7 | from obelisk.util import to_btc 8 | 9 | #################################################### 10 | # Testing Code 11 | height = 0 12 | 13 | 14 | def age(blocks): 15 | return blocks / (7 * 24) 16 | 17 | 18 | def print_height(ec, data): 19 | global height 20 | print 'height', data 21 | height = data 22 | 23 | 24 | def print_blk_header(ec, header): 25 | print 'version', header.version 26 | print 'previous block hash', header.previous_block_hash.encode("hex") 27 | print 'merkle', header.merkle.encode("hex") 28 | print 'timestamp', header.timestamp 29 | print 'bits', header.bits 30 | print 'nonce', header.nonce 31 | print header 32 | 33 | 34 | def print_history(ec, history, address): 35 | global height 36 | print 'history', address, len(history) 37 | if not '--full' in sys.argv: 38 | return 39 | for row in history: 40 | o_hash, o_index, o_height, value, s_hash, s_index, s_height = row 41 | 42 | print "+", to_btc(value), age(height - o_height), 'confirms' 43 | if s_index != obelisk.MAX_UINT32: 44 | print "-", to_btc(value), age(height - s_height), 'confirms' 45 | 46 | 47 | def bootstrap_address(address): 48 | def print_history_address(ec, history): 49 | print_history(ec, history, address) 50 | 51 | c.fetch_history(address, print_history_address) 52 | #c.subscribe_address(addr) 53 | 54 | 55 | def dummy(*args): 56 | pass 57 | 58 | 59 | def poll_latest(client): 60 | print "poll_latest..." 61 | 62 | def last_height_fetched(ec, height): 63 | client.fetch_block_header(height, header_fetched) 64 | 65 | def header_fetched(ec, header): 66 | print "Last:", header 67 | 68 | client.fetch_last_height(last_height_fetched) 69 | reactor.callLater(20, poll_latest, client) 70 | 71 | 72 | if __name__ == '__main__': 73 | c = obelisk.ObeliskOfLightClient('tcp://localhost:9091') 74 | #c.fetch_last_height(print_height) 75 | #blk_hash = "000000000000000471988cc24941335b" \ 76 | # "91d35d646971b7de682b4236dc691919".decode("hex") 77 | #assert len(blk_hash) == 32 78 | #c.fetch_block_header(blk_hash, print_blk_header) 79 | #c.fetch_block_header(270778, print_blk_header) 80 | 81 | addresses = ['1Evy47MqD82HGx6n1KHkHwBgCwbsbQQT8m', 82 | '1GUUpMm899Tr1w5mMvwnXcxbs77fmspTVC'] 83 | if os.path.exists('addresses.txt'): 84 | f = open('addresses.txt') 85 | addresses = map(lambda s: s.strip(), f.readlines()) 86 | addresses = filter(lambda s: s, addresses) 87 | f.close() 88 | 89 | for hash in addresses: 90 | # Subscribe to an address to receive updates. 91 | bootstrap_address(hash) 92 | reactor.callLater(0, poll_latest, c) 93 | reactor.run() 94 | -------------------------------------------------------------------------------- /examples/select_network.py: -------------------------------------------------------------------------------- 1 | import obelisk 2 | import obelisk.config 3 | 4 | if __name__ == "__main__": 5 | obelisk.select_network("testnet") 6 | assert \ 7 | obelisk.config.chain.magic_bytes == \ 8 | obelisk.config.testnet_chain.magic_bytes,\ 9 | "select testnet fail" 10 | 11 | obelisk.select_network("mainnet") 12 | assert \ 13 | obelisk.config.chain.magic_bytes == \ 14 | obelisk.config.mainnet_chain.magic_bytes,\ 15 | "select mainnet fail" 16 | -------------------------------------------------------------------------------- /examples/select_unspent.py: -------------------------------------------------------------------------------- 1 | import obelisk 2 | import sys 3 | from twisted.internet import reactor 4 | 5 | 6 | def build_output_info_list(unspent_rows): 7 | unspent_infos = [] 8 | for row in unspent_rows: 9 | assert len(row) == 4 10 | outpoint = obelisk.OutPoint() 11 | outpoint.hash = row[0] 12 | outpoint.index = row[1] 13 | value = row[3] 14 | unspent_infos.append( 15 | obelisk.OutputInfo(outpoint, value)) 16 | return unspent_infos 17 | 18 | 19 | def history_fetched(ec, history): 20 | if ec is not None: 21 | print >> sys.stderr, "Error fetching history:", ec 22 | return 23 | unspent_rows = [row[:4] for row in history if row[4] is None] 24 | unspent = build_output_info_list(unspent_rows) 25 | print obelisk.select_outputs(unspent, 10000) 26 | 27 | if __name__ == '__main__': 28 | client = obelisk.ObeliskOfLightClient("tcp://85.25.198.97:9091") 29 | client.fetch_history("1PRBVdCHoPPD3bz8sCZRTm6iAtuoqFctvx", history_fetched) 30 | reactor.run() 31 | -------------------------------------------------------------------------------- /examples/sign.py: -------------------------------------------------------------------------------- 1 | import obelisk 2 | 3 | secret = "6aa6f40c1ad36825bf7be87226f42a72" \ 4 | "f5ae28e7daa737611c8c971e4d462a18".decode("hex") 5 | key = obelisk.EllipticCurveKey() 6 | key.set_secret(secret) 7 | 8 | digest = obelisk.Hash("message") 9 | signature = key.sign(digest) 10 | assert key.verify(digest, signature) 11 | -------------------------------------------------------------------------------- /examples/subscribe_address.py: -------------------------------------------------------------------------------- 1 | import obelisk 2 | 3 | from twisted.internet import reactor 4 | 5 | #################################################### 6 | # Testing Code 7 | 8 | 9 | def print_event(address_version, address_hash, height, block_hash, tx): 10 | address = obelisk.bitcoin.hash_160_to_bc_address( 11 | address_hash, address_version 12 | ) 13 | print "update for", address, height 14 | 15 | if __name__ == '__main__': 16 | c = obelisk.ObeliskOfLightClient('tcp://localhost:9091') 17 | c.subscribe_address("1LuckyY9fRzcJre7aou7ZhWVXktxjjBb9S", print_event) 18 | 19 | reactor.run() 20 | -------------------------------------------------------------------------------- /examples/total_connections.py: -------------------------------------------------------------------------------- 1 | import obelisk 2 | from twisted.internet import reactor 3 | 4 | def height_fetched(ec, height): 5 | print "Height:", height 6 | 7 | def connections_fetched(ec, height): 8 | print "Total connections:", height 9 | 10 | if __name__ == '__main__': 11 | client = obelisk.ObeliskOfLightClient("tcp://localhost:9091") 12 | client.fetch_last_height(height_fetched) 13 | client.total_connections(connections_fetched) 14 | reactor.run() 15 | 16 | -------------------------------------------------------------------------------- /obelisk/__init__.py: -------------------------------------------------------------------------------- 1 | from binary import * 2 | from bitcoin import * 3 | from client import * 4 | from models import * 5 | from transaction import * 6 | from zmqbase import MAX_UINT32 7 | from twisted.internet import reactor 8 | 9 | def select_network(network): 10 | import config 11 | if "test" in network.lower(): 12 | config.chain = config.testnet_chain 13 | else: 14 | config.chain = config.mainnet_chain 15 | 16 | def start(): 17 | reactor.run() 18 | def stop(): 19 | reactor.stop() 20 | 21 | -------------------------------------------------------------------------------- /obelisk/binary.py: -------------------------------------------------------------------------------- 1 | def binary_str_to_bytes(str): 2 | split = lambda str: [str[x:x + 8] for x in range(0, len(str), 8)] 3 | add_padding = lambda str: str + ((8 - len(str)) * "0") 4 | result = "" 5 | for bin_byte in split(str): 6 | bin_byte = add_padding(bin_byte) 7 | value = int(bin_byte, 2) 8 | assert value < 256 9 | result += chr(value) 10 | return result 11 | 12 | # For more info see libbitcoin/include/bitcoin/bitcoin/utility/binary.hpp 13 | class Binary: 14 | 15 | BitsPerBlock = 8 16 | 17 | @staticmethod 18 | def blocks_size(bitsize): 19 | if bitsize == 0: 20 | return 0; 21 | return (bitsize - 1) / Binary.BitsPerBlock + 1 22 | 23 | def __init__(self, size, blocks): 24 | """Set bitsize and block data.""" 25 | self._size = size 26 | self._blocks = blocks 27 | 28 | @classmethod 29 | def from_string(cls, repr): 30 | return cls(len(repr), binary_str_to_bytes(repr)) 31 | 32 | def resize(self, size): 33 | self._size = size 34 | blks_size = Binary.blocks_size(self._size) 35 | self._blocks = self._blocks[:blks_size] 36 | # Pad with zero bytes any remaining space. 37 | self._blocks += "\x00" * (blks_size - len(self._blocks)) 38 | 39 | def __getitem__(self, i): 40 | assert i < self._size 41 | block_index = i / Binary.BitsPerBlock 42 | block = ord(self._blocks[block_index]) 43 | offset = i - (block_index * Binary.BitsPerBlock) 44 | bitmask = 1 << (Binary.BitsPerBlock - offset - 1) 45 | return (block & bitmask) > 0 46 | 47 | @property 48 | def blocks(self): 49 | """Return block data.""" 50 | return self._blocks 51 | @property 52 | def size(self): 53 | """Return bitsize of binary value.""" 54 | return self._size 55 | 56 | def __repr__(self): 57 | result = "" 58 | for i in range(self._size): 59 | if self[i]: 60 | result += "1" 61 | else: 62 | result += "0" 63 | return result 64 | 65 | def __eq__(self, other): 66 | min_size = min(self._size, other._size) 67 | for i in range(min_size): 68 | if self[i] != other[i]: 69 | return False 70 | return True 71 | 72 | if __name__ == "__main__": 73 | print Binary.blocks_size(100) 74 | b = Binary.from_string("101110101") 75 | print b 76 | c = Binary.from_string("101110101") 77 | d = Binary.from_string("101001101") 78 | # Same as b but with more bits 79 | b_ext = Binary.from_string("10111010111") 80 | assert b == c 81 | assert b != d 82 | assert b == b_ext 83 | print "(%s: %s)" % (b.size, b.blocks.encode("hex")) 84 | print "Resizing", b, "to 3 bits..." 85 | b.resize(3) 86 | print "Result:", b 87 | 88 | -------------------------------------------------------------------------------- /obelisk/bitcoin.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Electrum - lightweight Bitcoin client 4 | # Copyright (C) 2011 thomasv@gitorious 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | 19 | 20 | import hashlib 21 | import base64 22 | import ecdsa 23 | import re 24 | from util import print_error 25 | import config 26 | import models 27 | import numbertheory 28 | import os 29 | 30 | 31 | def rev_hex(s): 32 | return s.decode('hex')[::-1].encode('hex') 33 | 34 | 35 | def int_to_hex(i, length=1): 36 | s = hex(i)[2:].rstrip('L') 37 | s = "0"*(2*length - len(s)) + s 38 | return rev_hex(s) 39 | 40 | 41 | def var_int(i): 42 | # https://en.bitcoin.it/wiki/Protocol_specification#Variable_length_integer 43 | if i < 0xfd: 44 | return int_to_hex(i) 45 | elif i <= 0xffff: 46 | return "fd" + int_to_hex(i, 2) 47 | elif i <= 0xffffffff: 48 | return "fe" + int_to_hex(i, 4) 49 | else: 50 | return "ff" + int_to_hex(i, 8) 51 | 52 | 53 | def op_push(i): 54 | if i < 0x4c: 55 | return int_to_hex(i) 56 | elif i < 0xff: 57 | return '4c' + int_to_hex(i) 58 | elif i < 0xffff: 59 | return '4d' + int_to_hex(i, 2) 60 | else: 61 | return '4e' + int_to_hex(i, 4) 62 | 63 | 64 | Hash = lambda x: hashlib.sha256(hashlib.sha256(x).digest()).digest() 65 | hash_encode = lambda x: x[::-1].encode('hex') 66 | hash_decode = lambda x: x.decode('hex')[::-1] 67 | 68 | 69 | # pywallet openssl private key implementation 70 | 71 | def i2d_ECPrivateKey(pkey, compressed=False): 72 | if compressed: 73 | key = '3081d30201010420' + \ 74 | '%064x' % pkey.secret + \ 75 | 'a081a53081a2020101302c06072a8648ce3d0101022100' + \ 76 | '%064x' % _p + \ 77 | '3006040100040107042102' + \ 78 | '%064x' % _Gx + \ 79 | '022100' + \ 80 | '%064x' % _r + \ 81 | '020101a124032200' 82 | else: 83 | key = '308201130201010420' + \ 84 | '%064x' % pkey.secret + \ 85 | 'a081a53081a2020101302c06072a8648ce3d0101022100' + \ 86 | '%064x' % _p + \ 87 | '3006040100040107044104' + \ 88 | '%064x' % _Gx + \ 89 | '%064x' % _Gy + \ 90 | '022100' + \ 91 | '%064x' % _r + \ 92 | '020101a144034200' 93 | 94 | return key.decode('hex') + i2o_ECPublicKey(pkey.pubkey, compressed) 95 | 96 | 97 | def i2o_ECPublicKey(pubkey, compressed=False): 98 | # public keys are 65 bytes long (520 bits) 99 | # 0x04 + 32-byte X-coordinate + 32-byte Y-coordinate 100 | # 0x00 = point at infinity, 0x02 and 0x03 = compressed, 0x04 = uncompressed 101 | # compressed keys: 102 | # where is 0x02 if y is even and 0x03 if y is odd 103 | if compressed: 104 | if pubkey.point.y() & 1: 105 | key = '03' + '%064x' % pubkey.point.x() 106 | else: 107 | key = '02' + '%064x' % pubkey.point.x() 108 | else: 109 | key = '04' + \ 110 | '%064x' % pubkey.point.x() + \ 111 | '%064x' % pubkey.point.y() 112 | 113 | return key.decode('hex') 114 | 115 | # end pywallet openssl private key implementation 116 | 117 | ############ functions from pywallet ##################### 118 | 119 | 120 | def hash_160(public_key): 121 | try: 122 | md = hashlib.new('ripemd160') 123 | md.update(hashlib.sha256(public_key).digest()) 124 | return md.digest() 125 | except: 126 | import ripemd 127 | md = ripemd.new(hashlib.sha256(public_key).digest()) 128 | return md.digest() 129 | 130 | 131 | def public_key_to_bc_address(public_key): 132 | h160 = hash_160(public_key) 133 | return hash_160_to_bc_address(h160) 134 | 135 | 136 | def hash_160_to_bc_address(h160, addrtype=config.chain.pubkey_version): 137 | vh160 = chr(addrtype) + h160 138 | h = Hash(vh160) 139 | addr = vh160 + h[0:4] 140 | return b58encode(addr) 141 | 142 | 143 | def bc_address_to_hash_160(addr): 144 | bytes = b58decode(addr, 25) 145 | return ord(bytes[0]), bytes[1:21] 146 | 147 | 148 | def encode_point(pubkey, compressed=False): 149 | order = generator_secp256k1.order() 150 | p = pubkey.pubkey.point 151 | x_str = ecdsa.util.number_to_string(p.x(), order) 152 | if compressed: 153 | return chr(2 + (p.y() & 1)) + x_str 154 | else: 155 | # x_str + y_str 156 | return chr(4) + pubkey.to_string() 157 | 158 | __b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' 159 | __b58base = len(__b58chars) 160 | 161 | 162 | def b58encode(v): 163 | """ encode v, which is a string of bytes, to base58.""" 164 | 165 | long_value = 0L 166 | for (i, c) in enumerate(v[::-1]): 167 | long_value += (256**i) * ord(c) 168 | 169 | result = '' 170 | while long_value >= __b58base: 171 | div, mod = divmod(long_value, __b58base) 172 | result = __b58chars[mod] + result 173 | long_value = div 174 | result = __b58chars[long_value] + result 175 | 176 | # Bitcoin does a little leading-zero-compression: 177 | # leading 0-bytes in the input become leading-1s 178 | nPad = 0 179 | for c in v: 180 | if c == '\0': 181 | nPad += 1 182 | else: 183 | break 184 | 185 | return (__b58chars[0]*nPad) + result 186 | 187 | 188 | def b58decode(v, length): 189 | """ decode v into a string of len bytes.""" 190 | long_value = 0L 191 | for (i, c) in enumerate(v[::-1]): 192 | long_value += __b58chars.find(c) * (__b58base**i) 193 | 194 | result = '' 195 | while long_value >= 256: 196 | div, mod = divmod(long_value, 256) 197 | result = chr(mod) + result 198 | long_value = div 199 | result = chr(long_value) + result 200 | 201 | nPad = 0 202 | for c in v: 203 | if c == __b58chars[0]: 204 | nPad += 1 205 | else: 206 | break 207 | 208 | result = chr(0)*nPad + result 209 | if length is not None and len(result) != length: 210 | return None 211 | 212 | return result 213 | 214 | 215 | def EncodeBase58Check(vchIn): 216 | hash = Hash(vchIn) 217 | return b58encode(vchIn + hash[0:4]) 218 | 219 | 220 | def DecodeBase58Check(psz): 221 | vchRet = b58decode(psz, None) 222 | key = vchRet[0:-4] 223 | csum = vchRet[-4:] 224 | hash = Hash(key) 225 | cs32 = hash[0:4] 226 | if cs32 != csum: 227 | return None 228 | else: 229 | return key 230 | 231 | 232 | def PrivKeyToSecret(privkey): 233 | return privkey[9:9+32] 234 | 235 | 236 | def SecretToASecret(secret, 237 | compressed=False, 238 | addrtype=config.chain.pubkey_version): 239 | vchIn = chr((addrtype + 128) & 255) + secret 240 | if compressed: 241 | vchIn += '\01' 242 | return EncodeBase58Check(vchIn) 243 | 244 | 245 | def ASecretToSecret(key, addrtype=config.chain.pubkey_version): 246 | vch = DecodeBase58Check(key) 247 | if vch and vch[0] == chr((addrtype + 128) & 255): 248 | return vch[1:] 249 | return False 250 | 251 | 252 | def regenerate_key(sec): 253 | b = ASecretToSecret(sec) 254 | if not b: 255 | return False 256 | b = b[0:32] 257 | secret = int('0x' + b.encode('hex'), 16) 258 | return EC_KEY(secret) 259 | 260 | 261 | def GetPubKey(pubkey, compressed=False): 262 | return i2o_ECPublicKey(pubkey, compressed) 263 | 264 | 265 | def GetPrivKey(pkey, compressed=False): 266 | return i2d_ECPrivateKey(pkey, compressed) 267 | 268 | 269 | def GetSecret(pkey): 270 | return ('%064x' % pkey.secret).decode('hex') 271 | 272 | 273 | def is_compressed(sec): 274 | b = ASecretToSecret(sec) 275 | return len(b) == 33 276 | 277 | 278 | def address_from_private_key(sec): 279 | # rebuild public key from private key, compressed or uncompressed 280 | pkey = regenerate_key(sec) 281 | assert pkey 282 | 283 | # figure out if private key is compressed 284 | compressed = is_compressed(sec) 285 | 286 | # rebuild private and public key from regenerated secret 287 | public_key = GetPubKey(pkey.pubkey, compressed) 288 | address = public_key_to_bc_address(public_key) 289 | return address 290 | 291 | 292 | def is_valid(addr): 293 | ADDRESS_RE = re.compile('[1-9A-HJ-NP-Za-km-z]{26,}\\Z') 294 | if not ADDRESS_RE.match(addr): 295 | return False 296 | try: 297 | addrtype, h = bc_address_to_hash_160(addr) 298 | except: 299 | return False 300 | return addr == hash_160_to_bc_address(h, addrtype) 301 | 302 | 303 | ########### end pywallet functions ####################### 304 | 305 | # secp256k1, http://www.oid-info.com/get/1.3.132.0.10 306 | _p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2FL 307 | _r = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141L 308 | _b = 0x0000000000000000000000000000000000000000000000000000000000000007L 309 | _a = 0x0000000000000000000000000000000000000000000000000000000000000000L 310 | _Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798L 311 | _Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8L 312 | curve_secp256k1 = ecdsa.ellipticcurve.CurveFp(_p, _a, _b) 313 | generator_secp256k1 = ecdsa.ellipticcurve.Point(curve_secp256k1, _Gx, _Gy, _r) 314 | oid_secp256k1 = (1, 3, 132, 0, 10) 315 | SECP256k1 = ecdsa.curves.Curve( 316 | "SECP256k1", 317 | curve_secp256k1, 318 | generator_secp256k1, 319 | oid_secp256k1 320 | ) 321 | ec_order = _r 322 | 323 | from ecdsa.util import string_to_number, number_to_string 324 | 325 | 326 | def msg_magic(message): 327 | return "\x18Bitcoin Signed Message:\n" + chr(len(message)) + message 328 | 329 | 330 | class EC_KEY(object): 331 | def __init__(self, secret): 332 | self.pubkey = ecdsa.ecdsa.Public_key( 333 | generator_secp256k1, 334 | generator_secp256k1 * secret 335 | ) 336 | self.privkey = ecdsa.ecdsa.Private_key(self.pubkey, secret) 337 | self.secret = secret 338 | 339 | def sign_message(self, message, compressed, address): 340 | private_key = ecdsa.SigningKey.from_secret_exponent( 341 | self.secret, curve=SECP256k1 342 | ) 343 | public_key = private_key.get_verifying_key() 344 | signature = private_key.sign_digest( 345 | Hash(msg_magic(message)), sigencode=ecdsa.util.sigencode_string 346 | ) 347 | assert public_key.verify_digest( 348 | signature, 349 | Hash(msg_magic(message)), 350 | sigdecode=ecdsa.util.sigdecode_string 351 | ) 352 | for i in range(4): 353 | sig = base64.b64encode( 354 | chr(27 + i + (4 if compressed else 0)) + signature 355 | ) 356 | try: 357 | self.verify_message(address, sig, message) 358 | return sig 359 | except: 360 | continue 361 | else: 362 | raise BaseException("error: cannot sign message") 363 | 364 | @classmethod 365 | def verify_message(self, address, signature, message): 366 | """See http://www.secg.org/download/aid-780/sec1-v2.pdf 367 | for the math""" 368 | import msqr 369 | curve = curve_secp256k1 370 | G = generator_secp256k1 371 | order = G.order() 372 | # extract r,s from signature 373 | sig = base64.b64decode(signature) 374 | if len(sig) != 65: 375 | raise BaseException("Wrong encoding") 376 | r, s = ecdsa.util.sigdecode_string(sig[1:], order) 377 | nV = ord(sig[0]) 378 | if nV < 27 or nV >= 35: 379 | raise BaseException("Bad encoding") 380 | if nV >= 31: 381 | compressed = True 382 | nV -= 4 383 | else: 384 | compressed = False 385 | 386 | recid = nV - 27 387 | # 1.1 388 | x = r + (recid/2) * order 389 | # 1.3 390 | alpha = (x * x * x + curve.a() * x + curve.b()) % curve.p() 391 | beta = msqr.modular_sqrt(alpha, curve.p()) 392 | y = beta if (beta - recid) % 2 == 0 else curve.p() - beta 393 | # 1.4 the constructor checks that nR is at infinity 394 | R = ecdsa.ellipticcurve.Point(curve, x, y, order) 395 | # 1.5 compute e from message: 396 | h = Hash(msg_magic(message)) 397 | e = string_to_number(h) 398 | minus_e = -e % order 399 | # 1.6 compute Q = r^-1 (sR - eG) 400 | inv_r = ecdsa.numbertheory.inverse_mod(r, order) 401 | Q = inv_r * (s * R + minus_e * G) 402 | public_key = ecdsa.VerifyingKey.from_public_point(Q, curve=SECP256k1) 403 | # check that Q is the public key 404 | public_key.verify_digest( 405 | sig[1:], h, sigdecode=ecdsa.util.sigdecode_string 406 | ) 407 | # check that we get the original signing address 408 | addr = public_key_to_bc_address(encode_point(public_key, compressed)) 409 | if address != addr: 410 | raise BaseException("Bad signature") 411 | 412 | 413 | ###################################### BIP32 ############################## 414 | 415 | random_seed = lambda n: "%032x" % ecdsa.util.randrange(pow(2, n)) 416 | BIP32_PRIME = 0x80000000 417 | 418 | 419 | def bip32_init(seed): 420 | import hmac 421 | seed = seed.decode('hex') 422 | I = hmac.new("Bitcoin seed", seed, hashlib.sha512).digest() 423 | 424 | master_secret = I[0:32] 425 | master_chain = I[32:] 426 | 427 | K, K_compressed = get_pubkeys_from_secret(master_secret) 428 | return master_secret, master_chain, K, K_compressed 429 | 430 | 431 | def get_pubkeys_from_secret(secret): 432 | # public key 433 | curve = SECP256k1 434 | private_key = ecdsa.SigningKey.from_string(secret, curve=curve) 435 | public_key = private_key.get_verifying_key() 436 | K = public_key.to_string() 437 | K_compressed = GetPubKey(public_key.pubkey, True) 438 | return K, K_compressed 439 | 440 | 441 | def CKD(k, c, n): 442 | import hmac 443 | order = generator_secp256k1.order() 444 | keypair = EC_KEY(ecdsa.util.string_to_number(k)) 445 | K = GetPubKey(keypair.pubkey, True) 446 | 447 | if n & BIP32_PRIME: 448 | data = chr(0) + k + rev_hex(int_to_hex(n, 4)).decode('hex') 449 | I = hmac.new( 450 | c, data, hashlib.sha512 451 | ).digest() 452 | else: 453 | I = hmac.new( 454 | c, K + rev_hex(int_to_hex(n, 4)).decode('hex'), hashlib.sha512 455 | ).digest() 456 | 457 | k_n = ecdsa.util.number_to_string( 458 | (ecdsa.util.string_to_number(I[0:32]) + ecdsa.util.string_to_number(k)) % order, order 459 | ) 460 | c_n = I[32:] 461 | return k_n, c_n 462 | 463 | 464 | def CKD_prime(K, c, n): 465 | import hmac 466 | 467 | if n & BIP32_PRIME: 468 | raise 469 | 470 | K_public_key = ecdsa.VerifyingKey.from_string(K, curve=SECP256k1) 471 | K_compressed = GetPubKey(K_public_key.pubkey, True) 472 | 473 | I = hmac.new( 474 | c, 475 | K_compressed + rev_hex(int_to_hex(n, 4)).decode('hex'), 476 | hashlib.sha512 477 | ).digest() 478 | 479 | curve = SECP256k1 480 | pubkey_point = ecdsa.util.string_to_number(I[0:32]) * curve.generator + K_public_key.pubkey.point 481 | public_key = ecdsa.VerifyingKey.from_public_point( 482 | pubkey_point, curve=curve 483 | ) 484 | 485 | K_n = public_key.to_string() 486 | K_n_compressed = GetPubKey(public_key.pubkey, True) 487 | c_n = I[32:] 488 | 489 | return K_n, K_n_compressed, c_n 490 | 491 | 492 | class ElectrumSequence: 493 | """ Privatekey(type,n) = Master_private_key + H(n|S|type) """ 494 | 495 | def __init__(self, mpk, mpk2=None, mpk3=None): 496 | self.mpk = mpk 497 | self.mpk2 = mpk2 498 | self.mpk3 = mpk3 499 | self.master_public_key = ecdsa.VerifyingKey.from_string( 500 | mpk.decode('hex'), curve=SECP256k1 501 | ) 502 | 503 | @classmethod 504 | def mpk_from_seed(klass, seed): 505 | curve = SECP256k1 506 | secexp = klass.stretch_key(seed) 507 | master_private_key = ecdsa.SigningKey.from_secret_exponent( 508 | secexp, curve=curve 509 | ) 510 | master_public_key = master_private_key.get_verifying_key().to_string().encode('hex') 511 | return master_public_key 512 | 513 | @classmethod 514 | def stretch_key(self, seed): 515 | oldseed = seed 516 | for i in range(100000): 517 | seed = hashlib.sha256(seed + oldseed).digest() 518 | return string_to_number(seed) 519 | 520 | def get_sequence(self, sequence, mpk): 521 | for_change, n = sequence 522 | return string_to_number( 523 | Hash("%d:%d:" % (n, for_change) + mpk.decode('hex')) 524 | ) 525 | 526 | def get_address(self, sequence): 527 | if not self.mpk2: 528 | pubkey = self.get_pubkey(sequence) 529 | address = public_key_to_bc_address(pubkey.decode('hex')) 530 | elif not self.mpk3: 531 | pubkey1 = self.get_pubkey(sequence) 532 | pubkey2 = self.get_pubkey(sequence, mpk=self.mpk2) 533 | script = Transaction.multisig_script([pubkey1, pubkey2], 2) 534 | address = script["address"] 535 | else: 536 | pubkey1 = self.get_pubkey(sequence) 537 | pubkey2 = self.get_pubkey(sequence, mpk=self.mpk2) 538 | pubkey3 = self.get_pubkey(sequence, mpk=self.mpk3) 539 | script = Transaction.multisig_script( 540 | [pubkey1, pubkey2, pubkey3], 2 541 | ) 542 | address = script["address"] 543 | return address 544 | 545 | def get_pubkey(self, sequence, mpk=None): 546 | curve = SECP256k1 547 | if mpk is None: 548 | mpk = self.mpk 549 | z = self.get_sequence(sequence, mpk) 550 | master_public_key = self.master_public_key 551 | pubkey_point = master_public_key.pubkey.point + z*curve.generator 552 | public_key2 = ecdsa.VerifyingKey.from_public_point( 553 | pubkey_point, curve=curve 554 | ) 555 | return '04' + public_key2.to_string().encode('hex') 556 | 557 | def get_private_key_from_stretched_exponent(self, sequence, secexp): 558 | order = generator_secp256k1.order() 559 | secexp = (secexp + self.get_sequence(sequence, self.mpk)) % order 560 | pk = number_to_string(secexp, generator_secp256k1.order()) 561 | compressed = False 562 | return SecretToASecret(pk, compressed) 563 | 564 | def get_private_key(self, sequence, seed): 565 | secexp = self.stretch_key(seed) 566 | return self.get_private_key_from_stretched_exponent(sequence, secexp) 567 | 568 | def get_private_keys(self, sequence_list, seed): 569 | secexp = self.stretch_key(seed) 570 | return [ 571 | self.get_private_key_from_stretched_exponent(sequence, secexp) 572 | for sequence in sequence_list 573 | ] 574 | 575 | def check_seed(self, seed): 576 | curve = SECP256k1 577 | secexp = self.stretch_key(seed) 578 | master_private_key = ecdsa.SigningKey.from_secret_exponent( 579 | secexp, curve=curve 580 | ) 581 | master_public_key = master_private_key.get_verifying_key().to_string().encode('hex') 582 | if master_public_key != self.mpk: 583 | print_error('invalid password (mpk)') 584 | raise BaseException('Invalid password') 585 | return True 586 | 587 | def get_input_info(self, sequence): 588 | if not self.mpk2: 589 | pk_addr = self.get_address(sequence) 590 | redeemScript = None 591 | elif not self.mpk3: 592 | pubkey1 = self.get_pubkey(sequence) 593 | pubkey2 = self.get_pubkey(sequence, mpk=self.mpk2) 594 | # we need to return that address to get the right private key 595 | pk_addr = public_key_to_bc_address(pubkey1.decode('hex')) 596 | redeemScript = Transaction.multisig_script( 597 | [pubkey1, pubkey2], 2 598 | )['redeemScript'] 599 | else: 600 | pubkey1 = self.get_pubkey(sequence) 601 | pubkey2 = self.get_pubkey(sequence, mpk=self.mpk2) 602 | pubkey3 = self.get_pubkey(sequence, mpk=self.mpk3) 603 | pk_addr = public_key_to_bc_address(pubkey1.decode('hex')) # we need to return that address to get the right private key 604 | redeemScript = Transaction.multisig_script( 605 | [pubkey1, pubkey2, pubkey3], 2 606 | )['redeemScript'] 607 | return pk_addr, redeemScript 608 | 609 | 610 | class BIP32Sequence: 611 | 612 | def __init__(self, mpk, mpk2=None, mpk3=None): 613 | self.mpk = mpk 614 | self.mpk2 = mpk2 615 | self.mpk3 = mpk3 616 | 617 | @classmethod 618 | def mpk_from_seed(klass, seed): 619 | master_secret, master_chain, master_public_key, master_public_key_compressed = bip32_init(seed) 620 | return master_public_key.encode('hex'), master_chain.encode('hex') 621 | 622 | def get_pubkey(self, sequence, mpk=None): 623 | if not mpk: 624 | mpk = self.mpk 625 | master_public_key, master_chain = mpk 626 | K = master_public_key.decode('hex') 627 | chain = master_chain.decode('hex') 628 | for i in sequence: 629 | K, K_compressed, chain = CKD_prime(K, chain, i) 630 | return K_compressed.encode('hex') 631 | 632 | def get_address(self, sequence): 633 | if not self.mpk2: 634 | pubkey = self.get_pubkey(sequence) 635 | address = public_key_to_bc_address(pubkey.decode('hex')) 636 | elif not self.mpk3: 637 | pubkey1 = self.get_pubkey(sequence) 638 | pubkey2 = self.get_pubkey(sequence, mpk=self.mpk2) 639 | address = Transaction.multisig_script( 640 | [pubkey1, pubkey2], 2 641 | )["address"] 642 | else: 643 | pubkey1 = self.get_pubkey(sequence) 644 | pubkey2 = self.get_pubkey(sequence, mpk=self.mpk2) 645 | pubkey3 = self.get_pubkey(sequence, mpk=self.mpk3) 646 | address = Transaction.multisig_script( 647 | [pubkey1, pubkey2, pubkey3], 2 648 | )["address"] 649 | return address 650 | 651 | def get_private_key(self, sequence, seed): 652 | master_secret, master_chain, master_public_key, master_public_key_compressed = bip32_init(seed) 653 | chain = master_chain 654 | k = master_secret 655 | for i in sequence: 656 | k, chain = CKD(k, chain, i) 657 | return SecretToASecret(k, True) 658 | 659 | def get_private_keys(self, sequence_list, seed): 660 | return [ 661 | self.get_private_key(sequence, seed) for sequence in sequence_list 662 | ] 663 | 664 | def check_seed(self, seed): 665 | master_secret, master_chain, master_public_key, master_public_key_compressed = bip32_init(seed) 666 | assert self.mpk == ( 667 | master_public_key.encode('hex'), master_chain.encode('hex') 668 | ) 669 | 670 | def get_input_info(self, sequence): 671 | if not self.mpk2: 672 | pk_addr = self.get_address(sequence) 673 | redeemScript = None 674 | elif not self.mpk3: 675 | pubkey1 = self.get_pubkey(sequence) 676 | pubkey2 = self.get_pubkey(sequence, mpk=self.mpk2) 677 | # we need to return that address to get the right private key 678 | pk_addr = public_key_to_bc_address(pubkey1.decode('hex')) 679 | redeemScript = Transaction.multisig_script([pubkey1, pubkey2], 2)['redeemScript'] 680 | else: 681 | pubkey1 = self.get_pubkey(sequence) 682 | pubkey2 = self.get_pubkey(sequence, mpk=self.mpk2) 683 | pubkey3 = self.get_pubkey(sequence, mpk=self.mpk3) 684 | pk_addr = public_key_to_bc_address(pubkey1.decode('hex')) # we need to return that address to get the right private key 685 | redeemScript = Transaction.multisig_script( 686 | [pubkey1, pubkey2, pubkey3], 2 687 | )['redeemScript'] 688 | return pk_addr, redeemScript 689 | 690 | ################################## transactions 691 | 692 | MIN_RELAY_TX_FEE = 10000 693 | 694 | 695 | class Transaction: 696 | def __init__(self, raw): 697 | self.raw = raw 698 | self.deserialize() 699 | self.inputs = self.d['inputs'] 700 | self.outputs = self.d['outputs'] 701 | self.outputs = map(lambda x: (x['address'], x['value']), self.outputs) 702 | self.input_info = None 703 | self.is_complete = True 704 | 705 | @classmethod 706 | def from_io(klass, inputs, outputs): 707 | # for_sig=-1 means do not sign 708 | raw = klass.serialize(inputs, outputs, for_sig=-1) 709 | self = klass(raw) 710 | self.is_complete = False 711 | self.inputs = inputs 712 | self.outputs = outputs 713 | extras = [] 714 | for i in self.inputs: 715 | e = { 716 | 'txid': i['tx_hash'], 717 | 'vout': i['index'], 718 | 'scriptPubKey': i.get('raw_output_script') 719 | } 720 | extras.append(e) 721 | self.input_info = extras 722 | return self 723 | 724 | def __str__(self): 725 | return self.raw 726 | 727 | @classmethod 728 | def multisig_script(klass, public_keys, num=None): 729 | n = len(public_keys) 730 | if num is None: 731 | num = n 732 | # supports only "2 of 2", and "2 of 3" transactions 733 | assert num <= n and n in [2, 3] 734 | 735 | if num == 2: 736 | s = '52' 737 | elif num == 3: 738 | s = '53' 739 | else: 740 | raise 741 | 742 | for k in public_keys: 743 | s += var_int(len(k)/2) 744 | s += k 745 | if n == 2: 746 | s += '52' 747 | elif n == 3: 748 | s += '53' 749 | else: 750 | raise 751 | s += 'ae' 752 | 753 | out = { 754 | "address": hash_160_to_bc_address(hash_160(s.decode('hex')), config.chain.script_version), 755 | "redeemScript": s 756 | } 757 | return out 758 | 759 | @classmethod 760 | def serialize(klass, inputs, outputs, for_sig=None): 761 | 762 | s = int_to_hex(1, 4) # version 763 | s += var_int(len(inputs)) # number of inputs 764 | for i in range(len(inputs)): 765 | txin = inputs[i] 766 | s += txin['tx_hash'].decode('hex')[::-1].encode('hex') # prev hash 767 | s += int_to_hex(txin['index'], 4) # prev index 768 | 769 | if for_sig is None: 770 | pubkeysig = txin.get('pubkeysig') 771 | if pubkeysig: 772 | pubkey, sig = pubkeysig[0] 773 | sig = sig + chr(1) # hashtype 774 | script = op_push(len(sig)) 775 | script += sig.encode('hex') 776 | script += op_push(len(pubkey)) 777 | script += pubkey.encode('hex') 778 | else: 779 | signatures = txin['signatures'] 780 | pubkeys = txin['pubkeys'] 781 | script = '00' # op_0 782 | for sig in signatures: 783 | sig = sig + '01' 784 | script += op_push(len(sig)/2) 785 | script += sig 786 | 787 | redeem_script = klass.multisig_script( 788 | pubkeys, 2 789 | ).get('redeemScript') 790 | script += op_push(len(redeem_script) / 2) 791 | script += redeem_script 792 | 793 | elif for_sig == i: 794 | if txin.get('redeemScript'): 795 | script = txin['redeemScript'] # p2sh uses the inner script 796 | else: 797 | script = txin['raw_output_script'] # scriptsig 798 | else: 799 | script = '' 800 | s += var_int(len(script)/2) # script length 801 | s += script 802 | s += "ffffffff" # sequence 803 | 804 | s += var_int(len(outputs)) # number of outputs 805 | for output in outputs: 806 | addr, amount = output 807 | s += int_to_hex(amount, 8) # amount 808 | addrtype, hash_160 = bc_address_to_hash_160(addr) 809 | if addrtype == config.chain.pubkey_version: 810 | script = '76a9' # op_dup, 811 | # op_hash_160 812 | script += '14' # push 0x14 bytes 813 | script += hash_160.encode('hex') 814 | script += '88ac' # op_equalverify, 815 | # op_checksig 816 | elif addrtype == config.chain.script_version: 817 | script = 'a9' # op_hash_160 818 | script += '14' # push 0x14 bytes 819 | script += hash_160.encode('hex') 820 | script += '87' # op_equal 821 | else: 822 | raise 823 | 824 | s += var_int(len(script)/2) # script length 825 | s += script # script 826 | s += int_to_hex(0, 4) # lock time 827 | if for_sig is not None and for_sig != -1: 828 | s += int_to_hex(1, 4) # hash type 829 | return s 830 | 831 | def for_sig(self, i): 832 | return self.serialize(self.inputs, self.outputs, for_sig=i) 833 | 834 | def hash(self): 835 | return Hash(self.raw.decode('hex'))[::-1].encode('hex') 836 | 837 | def sign(self, private_keys): 838 | import deserialize 839 | 840 | for i in range(len(self.inputs)): 841 | txin = self.inputs[i] 842 | tx_for_sig = self.serialize(self.inputs, self.outputs, for_sig=i) 843 | 844 | if txin.get('redeemScript'): 845 | # 1 parse the redeem script 846 | num, redeem_pubkeys = deserialize.parse_redeemScript( 847 | txin.get('redeemScript') 848 | ) 849 | self.inputs[i]["pubkeys"] = redeem_pubkeys 850 | 851 | # build list of public/private keys 852 | keypairs = {} 853 | for sec in private_keys.values(): 854 | compressed = is_compressed(sec) 855 | pkey = regenerate_key(sec) 856 | pubkey = GetPubKey(pkey.pubkey, compressed) 857 | keypairs[pubkey.encode('hex')] = sec 858 | 859 | # list of already existing signatures 860 | signatures = txin.get("signatures", []) 861 | print_error("signatures", signatures) 862 | 863 | for pubkey in redeem_pubkeys: 864 | public_key = ecdsa.VerifyingKey.from_string( 865 | pubkey[2:].decode('hex'), curve=SECP256k1 866 | ) 867 | for s in signatures: 868 | try: 869 | public_key.verify_digest( 870 | s.decode('hex')[:-1], 871 | Hash(tx_for_sig.decode('hex')), 872 | sigdecode=ecdsa.util.sigdecode_der 873 | ) 874 | break 875 | except ecdsa.keys.BadSignatureError: 876 | continue 877 | else: 878 | # check if we have a key 879 | # corresponding to the redeem script 880 | if pubkey in keypairs.keys(): 881 | # add signature 882 | sec = keypairs[pubkey] 883 | compressed = is_compressed(sec) 884 | pkey = regenerate_key(sec) 885 | secexp = pkey.secret 886 | private_key = ecdsa.SigningKey.from_secret_exponent( 887 | secexp, curve=SECP256k1 888 | ) 889 | public_key = private_key.get_verifying_key() 890 | sig = private_key.sign_digest( 891 | Hash(tx_for_sig.decode('hex')), 892 | sigencode=ecdsa.util.sigencode_der 893 | ) 894 | assert public_key.verify_digest( 895 | sig, 896 | Hash(tx_for_sig.decode('hex')), 897 | sigdecode=ecdsa.util.sigdecode_der 898 | ) 899 | signatures.append(sig.encode('hex')) 900 | 901 | # for p2sh, pubkeysig is a tuple (may be incomplete) 902 | self.inputs[i]["signatures"] = signatures 903 | print_error("signatures", signatures) 904 | self.is_complete = len(signatures) == num 905 | 906 | else: 907 | sec = private_keys[txin['address']] 908 | compressed = is_compressed(sec) 909 | pkey = regenerate_key(sec) 910 | secexp = pkey.secret 911 | 912 | private_key = ecdsa.SigningKey.from_secret_exponent( 913 | secexp, curve=SECP256k1 914 | ) 915 | public_key = private_key.get_verifying_key() 916 | pkey = EC_KEY(secexp) 917 | pubkey = GetPubKey(pkey.pubkey, compressed) 918 | sig = private_key.sign_digest( 919 | Hash(tx_for_sig.decode('hex')), 920 | sigencode=ecdsa.util.sigencode_der 921 | ) 922 | assert public_key.verify_digest( 923 | sig, 924 | Hash(tx_for_sig.decode('hex')), 925 | sigdecode=ecdsa.util.sigdecode_der 926 | ) 927 | 928 | self.inputs[i]["pubkeysig"] = [(pubkey, sig)] 929 | self.is_complete = True 930 | 931 | self.raw = self.serialize(self.inputs, self.outputs) 932 | 933 | def deserialize(self): 934 | import deserialize 935 | vds = deserialize.BCDataStream() 936 | vds.write(self.raw.decode('hex')) 937 | self.d = deserialize.parse_Transaction(vds) 938 | return self.d 939 | 940 | def has_address(self, addr): 941 | found = False 942 | for txin in self.inputs: 943 | if addr == txin.get('address'): 944 | found = True 945 | break 946 | for txout in self.outputs: 947 | if addr == txout[0]: 948 | found = True 949 | break 950 | return found 951 | 952 | def get_value(self, addresses, prevout_values): 953 | # return the balance for that tx 954 | is_relevant = False 955 | is_send = False 956 | is_pruned = False 957 | is_partial = False 958 | v_in = v_out = v_out_mine = 0 959 | 960 | for item in self.inputs: 961 | addr = item.get('address') 962 | if addr in addresses: 963 | is_send = True 964 | is_relevant = True 965 | key = item['prevout_hash'] + ':%d' % item['prevout_n'] 966 | value = prevout_values.get(key) 967 | if value is None: 968 | is_pruned = True 969 | else: 970 | v_in += value 971 | else: 972 | is_partial = True 973 | 974 | if not is_send: 975 | is_partial = False 976 | 977 | for item in self.outputs: 978 | addr, value = item 979 | v_out += value 980 | if addr in addresses: 981 | v_out_mine += value 982 | is_relevant = True 983 | 984 | if is_pruned: 985 | # some inputs are mine: 986 | fee = None 987 | if is_send: 988 | v = v_out_mine - v_out 989 | else: 990 | # no input is mine 991 | v = v_out_mine 992 | 993 | else: 994 | v = v_out_mine - v_in 995 | 996 | if is_partial: 997 | # some inputs are mine, but not all 998 | fee = None 999 | is_send = v < 0 1000 | else: 1001 | # all inputs are mine 1002 | fee = v_out - v_in 1003 | 1004 | return is_relevant, is_send, v, fee 1005 | 1006 | def as_dict(self): 1007 | import json 1008 | out = { 1009 | "hex": self.raw, 1010 | "complete": self.is_complete 1011 | } 1012 | if not self.is_complete: 1013 | extras = [] 1014 | for i in self.inputs: 1015 | e = { 1016 | 'txid': i['tx_hash'], 1017 | 'vout': i['index'], 1018 | 'scriptPubKey': i.get('raw_output_script'), 1019 | 'KeyID': i.get('KeyID'), 1020 | 'redeemScript': i.get('redeemScript'), 1021 | 'signatures': i.get('signatures'), 1022 | 'pubkeys': i.get('pubkeys'), 1023 | } 1024 | extras.append(e) 1025 | self.input_info = extras 1026 | 1027 | if self.input_info: 1028 | out['input_info'] = json.dumps(self.input_info) 1029 | out['input_info'] = out['input_info'].replace(' ', '') 1030 | 1031 | return out 1032 | 1033 | def requires_fee(self, verifier): 1034 | # see https://en.bitcoin.it/wiki/Transaction_fees 1035 | threshold = 57600000 1036 | size = len(self.raw)/2 1037 | if size >= 10000: 1038 | return True 1039 | 1040 | for o in self.outputs: 1041 | value = o[1] 1042 | if value < 1000000: 1043 | return True 1044 | sum = 0 1045 | for i in self.inputs: 1046 | age = verifier.get_confirmations(i["tx_hash"])[0] 1047 | sum += i["value"] * age 1048 | priority = sum / size 1049 | print_error(priority, threshold) 1050 | return priority < threshold 1051 | 1052 | 1053 | class HighDefWallet: 1054 | 1055 | def __init__(self, secret, chain, mpk, mpk_compressed): 1056 | self.secret, self.chain, self.mpk, self.mpk_compressed = \ 1057 | secret, chain, mpk, mpk_compressed 1058 | 1059 | @property 1060 | def key_id(self): 1061 | return hash_160(self.mpk_compressed) 1062 | 1063 | @property 1064 | def address(self): 1065 | return hash_160_to_bc_address(self.key_id) 1066 | 1067 | @property 1068 | def secret_key(self): 1069 | return SecretToASecret(self.secret, True) 1070 | 1071 | def branch(self, n): 1072 | secret, chain = CKD(self.secret, self.chain, n) 1073 | mpk, mpk_compressed = get_pubkeys_from_secret(secret) 1074 | return HighDefWallet(secret, chain, mpk, mpk_compressed) 1075 | 1076 | def branch_prime(self, n): 1077 | return self.branch(n + BIP32_PRIME) 1078 | 1079 | @staticmethod 1080 | def root(seed): 1081 | args = bip32_init(seed) 1082 | return HighDefWallet(*args) 1083 | 1084 | 1085 | class EllipticCurveKey: 1086 | 1087 | def __init__(self): 1088 | self._secret = None 1089 | self._private_key = None 1090 | self._public_key = None 1091 | 1092 | def new_key_pair(self): 1093 | secret = os.urandom(32) 1094 | self.set_secret(secret) 1095 | 1096 | def set_secret(self, secret): 1097 | self._secret = secret 1098 | secret = string_to_number(secret) 1099 | pkey = EC_KEY(secret) 1100 | 1101 | #sec = "L5KhaMvPYRW1ZoFmRjUtxxPypQ94m6BcDrPhqArhggdaTbbAFJEF" 1102 | #pkey = obelisk.regenerate_key(sec) 1103 | 1104 | secexp = pkey.secret 1105 | self._private_key = ecdsa.SigningKey.from_secret_exponent( 1106 | secexp, curve=SECP256k1) 1107 | self._public_key = self._private_key.get_verifying_key() 1108 | 1109 | def sign(self, digest): 1110 | return self._private_key.sign_digest_deterministic( 1111 | digest, hashfunc=hashlib.sha256, 1112 | sigencode=ecdsa.util.sigencode_der) 1113 | 1114 | def verify(self, digest, signature): 1115 | return self._public_key.verify_digest( 1116 | signature, digest, sigdecode=ecdsa.util.sigdecode_der) 1117 | 1118 | @property 1119 | def secret(self): 1120 | return self._secret 1121 | 1122 | @property 1123 | def public_key(self): 1124 | return GetPubKey(self._public_key.pubkey, True) 1125 | 1126 | @property 1127 | def key_id(self): 1128 | return hash_160(self.public_key) 1129 | 1130 | @property 1131 | def address(self): 1132 | return hash_160_to_bc_address(self.key_id) 1133 | 1134 | 1135 | def output_script(address): 1136 | addrtype, hash_160 = bc_address_to_hash_160(address) 1137 | assert addrtype == config.chain.pubkey_version 1138 | script = '\x76\xa9' # op_dup, op_hash_160 1139 | script += '\x14' # push 0x14 bytes 1140 | script += hash_160 1141 | script += '\x88\xac' # op_equalverify, op_checksig 1142 | return script 1143 | 1144 | 1145 | def input_script(signature, public_key): 1146 | script = op_push(len(signature)).decode("hex") 1147 | script += signature 1148 | script += op_push(len(public_key)).decode("hex") 1149 | script += public_key 1150 | return script 1151 | 1152 | 1153 | def sign_transaction_input(tx, input_index, key): 1154 | sighash = generate_signature_hash(tx, input_index, key.address) 1155 | # Add sighash::all to end of signature. 1156 | signature = key.sign(sighash) + "\x01" 1157 | public_key = key.public_key 1158 | tx.inputs[input_index].script = input_script(signature, public_key) 1159 | 1160 | 1161 | def copy_tx(tx): 1162 | # This is a hack. 1163 | raw_tx = tx.serialize() 1164 | return models.Transaction.deserialize(raw_tx) 1165 | 1166 | 1167 | def generate_signature_hash(parent_tx, input_index, prevout_address): 1168 | script_code = output_script(prevout_address) 1169 | tx = copy_tx(parent_tx) 1170 | if input_index >= len(tx.inputs): 1171 | return None 1172 | for input in tx.inputs: 1173 | input.script = "" 1174 | tx.inputs[input_index].script = script_code 1175 | raw_tx = tx.serialize() + "\x01\x00\x00\x00" 1176 | return Hash(raw_tx) 1177 | 1178 | 1179 | def _derive_y_from_x(x, is_even): 1180 | alpha = (pow(x, 3, _p) + _a * x + _b) % _p 1181 | beta = numbertheory.modular_sqrt(alpha, _p) 1182 | if is_even == bool(beta & 1): 1183 | return _p - beta 1184 | return beta 1185 | 1186 | 1187 | def decompress_public_key(public_key): 1188 | prefix = public_key[0] 1189 | if prefix == "\x04": 1190 | return public_key 1191 | assert prefix == "\x02" or prefix == "\x03" 1192 | x = int("0x" + public_key[1:].encode("hex"), 16) 1193 | y = _derive_y_from_x(x, prefix == "\x02") 1194 | key = '04' + \ 1195 | '%064x' % x + \ 1196 | '%064x' % y 1197 | return key.decode("hex") 1198 | 1199 | 1200 | def diffie_hellman(e, Q): 1201 | Q = decompress_public_key(Q) 1202 | curve = SECP256k1 1203 | public_key = ecdsa.VerifyingKey.from_string(Q[1:], curve=curve) 1204 | point = public_key.pubkey.point 1205 | #e_int = int("0x" + e.encode("hex"), 16) 1206 | e_int = string_to_number(e) 1207 | point = e_int * point 1208 | # convert x point to bytes 1209 | result = "\x03" + ("%x" % point.x()).decode("hex") 1210 | assert len(result) == 33 1211 | return result 1212 | 1213 | 1214 | def convert_point(Q): 1215 | Q = decompress_public_key(Q)[1:] 1216 | assert len(Q) == 64 1217 | Q_x = Q[:32] 1218 | Q_y = Q[32:] 1219 | assert len(Q_x) == 32 1220 | assert len(Q_y) == 32 1221 | Q_x = string_to_number(Q_x) 1222 | Q_y = string_to_number(Q_y) 1223 | curve = curve_secp256k1 1224 | return ecdsa.ellipticcurve.Point(curve, Q_x, Q_y, ec_order) 1225 | 1226 | 1227 | def point_add(Q, c): 1228 | Q = convert_point(Q) 1229 | c = string_to_number(c) 1230 | return Q + c * generator_secp256k1 1231 | 1232 | 1233 | def get_point_pubkey(point, compressed=False): 1234 | if compressed: 1235 | if point.y() & 1: 1236 | key = '03' + '%064x' % point.x() 1237 | else: 1238 | key = '02' + '%064x' % point.x() 1239 | else: 1240 | key = '04' + \ 1241 | '%064x' % point.x() + \ 1242 | '%064x' % point.y() 1243 | return key.decode('hex') 1244 | 1245 | 1246 | def add_mod_n(d, c): 1247 | assert len(d) == 32 1248 | # Truncate prefix byte 1249 | order = generator_secp256k1.order() 1250 | d = string_to_number(d) 1251 | c = string_to_number(c) 1252 | return number_to_string((d + c) % order, order) 1253 | -------------------------------------------------------------------------------- /obelisk/bitset.py: -------------------------------------------------------------------------------- 1 | def hex2(n): 2 | x = '%x' % (n,) 3 | return ('0' * (len(x) % 2)) + x 4 | 5 | 6 | class Bitset: 7 | 8 | def __init__(self, size=0, value=0, binary=None): 9 | self._size = size 10 | self._value = value 11 | if binary is not None: 12 | self._size = len(binary) 13 | self._value = int(binary, 2) 14 | 15 | def deserialize(self, data): 16 | self._value = int(data[::-1].encode("hex"), 16) 17 | 18 | def serialize(self): 19 | return hex2(self._value).decode("hex")[::-1] 20 | 21 | @property 22 | def size(self): 23 | return self._size 24 | 25 | def __str__(self): 26 | # TODO: use format instead 27 | self_str = bin(self._value)[2:] 28 | if len(self_str) <= self._size: 29 | # Pad with zeros 30 | self_str = "0" * (self._size - len(self_str)) + self_str 31 | assert len(self_str) == self._size 32 | else: 33 | self_str = self_str[-self._size:] 34 | return self_str 35 | 36 | def __repr__(self): 37 | return "Bitset(%s)" % str(self) 38 | 39 | def __eq__(self, other): 40 | return self._size == other._size and \ 41 | self._value == other._value 42 | 43 | if __name__ == "__main__": 44 | a = Bitset(binary="001101110001010101111010101010011101") 45 | assert a.serialize().encode("hex") == "9daa577103" 46 | assert a.size == 36 47 | assert str(a) == "001101110001010101111010101010011101" 48 | b = Bitset(36) 49 | b.deserialize("9daa577103".decode("hex")) 50 | assert b.size == 36 51 | assert str(b) == "001101110001010101111010101010011101" 52 | c = Bitset(8) 53 | c.deserialize("9daa577103".decode("hex")) 54 | assert c.size == 8 55 | assert str(c) == "10011101" 56 | assert a == b 57 | assert c != a 58 | assert c != b 59 | -------------------------------------------------------------------------------- /obelisk/bittree.py: -------------------------------------------------------------------------------- 1 | class OneZero: 2 | 3 | def __init__(self): 4 | self.one = None 5 | self.zero = None 6 | 7 | def __getitem__(self, key): 8 | assert key == "1" or key == "0" 9 | if key == "1": 10 | return self.one 11 | elif key == "0": 12 | return self.zero 13 | 14 | def __setitem__(self, key, value): 15 | assert key == "1" or key == "0" 16 | if key == "1": 17 | self.one = value 18 | elif key == "0": 19 | self.zero = value 20 | 21 | def empty(self): 22 | return self.zero is None and self.one is None 23 | 24 | def __repr__(self): 25 | return "<%s, %s>" % (self.zero, self.one) 26 | 27 | 28 | class BitTree: 29 | 30 | def __init__(self): 31 | self._branch = OneZero() 32 | self._leaf = OneZero() 33 | 34 | def add(self, binary, value): 35 | branch = binary[0] 36 | binary = binary[1:] 37 | assert branch == "1" or branch == "0" 38 | if not binary: 39 | self._add_leaf(branch, value) 40 | else: 41 | self._add_branch(branch, binary, value) 42 | 43 | def _add_leaf(self, branch, value): 44 | if self._leaf[branch] is None: 45 | self._leaf[branch] = [] 46 | self._leaf[branch].append(value) 47 | 48 | def _add_branch(self, branch, binary, value): 49 | if self._branch[branch] is None: 50 | self._branch[branch] = BitTree() 51 | self._branch[branch].add(binary, value) 52 | 53 | def lookup(self, binary): 54 | branch = binary[0] 55 | binary = binary[1:] 56 | result = self._lookup_leaf(branch) 57 | if not binary: 58 | return result + self._children(branch) 59 | # Continue recursive lookup 60 | return result + self._lookup_branch(branch, binary) 61 | 62 | def _lookup_leaf(self, branch): 63 | result = self._leaf[branch] 64 | if result is None: 65 | return [] 66 | return result 67 | 68 | def _children(self, branch): 69 | if self._branch[branch] is not None: 70 | return self._branch[branch]._all_children() 71 | return [] 72 | 73 | def _all_children(self): 74 | result = [] 75 | if self._leaf.zero is not None: 76 | result.extend(self._leaf.zero) 77 | if self._leaf.one is not None: 78 | result.extend(self._leaf.one) 79 | if self._branch.zero is not None: 80 | result.extend(self._branch.zero._all_children()) 81 | if self._branch.one is not None: 82 | result.extend(self._branch.one._all_children()) 83 | return result 84 | 85 | def _lookup_branch(self, branch, binary): 86 | if self._branch[branch] is None: 87 | return [] 88 | return self._branch[branch].lookup(binary) 89 | 90 | def delete(self, binary, value): 91 | branch = binary[0] 92 | binary = binary[1:] 93 | assert branch == "1" or branch == "0" 94 | if not binary: 95 | self._delete_leaf(branch, value) 96 | else: 97 | self._delete_branch(branch, binary, value) 98 | return self._empty() 99 | 100 | def _delete_leaf(self, branch, value): 101 | if self._leaf[branch] is None: 102 | return 103 | self._leaf[branch].remove(value) 104 | if not self._leaf[branch]: 105 | self._leaf[branch] = None 106 | 107 | def _delete_branch(self, branch, binary, value): 108 | is_empty = self._branch[branch].delete(binary, value) 109 | if is_empty: 110 | self._branch[branch] = None 111 | 112 | def _empty(self): 113 | return self._branch.empty() and self._leaf.empty() 114 | 115 | def __repr__(self): 116 | return "" % (self._branch, self._leaf) 117 | 118 | if __name__ == "__main__": 119 | tree = BitTree() 120 | tree.add("010101", 666) 121 | tree.add("010101", 888) 122 | tree.add("101", 110) 123 | print tree 124 | tree.add("10111", 116) 125 | print tree.lookup("101") 126 | print tree.lookup("10111") 127 | print tree.lookup("010") 128 | print tree.lookup("010101") 129 | print tree 130 | tree.delete("10111", 116) 131 | print tree 132 | print tree.lookup("101") 133 | print tree.lookup("0") 134 | print tree.lookup("1") 135 | print "------------" 136 | tree = BitTree() 137 | tree.add("10", 777) 138 | tree.add("101", 333) 139 | tree.add("1011", 222) 140 | tree.add("00", 666) 141 | print tree.lookup("1011") 142 | -------------------------------------------------------------------------------- /obelisk/client.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | from zmqbase import ClientBase 4 | from zmq_fallback import ZmqSocket 5 | from twisted.internet import reactor, task 6 | from binascii import unhexlify 7 | 8 | import time 9 | import zmq 10 | import bitcoin 11 | import serialize 12 | import error_code 13 | 14 | 15 | def unpack_error(data): 16 | value = struct.unpack_from(' time.time(): 131 | self.renew_address(address) 132 | else: 133 | self._subscriptions["address"].pop(address) 134 | 135 | # Command implementations 136 | def renew_address(self, address, cb=None): 137 | address_version, address_hash = \ 138 | bitcoin.bc_address_to_hash_160(address) 139 | # prepare parameters 140 | data = struct.pack('B', 0) # type = address 141 | data += struct.pack('B', 160) # bitsize 142 | data += address_hash # address 143 | 144 | # run command 145 | self.send_command('address.renew', data, cb) 146 | 147 | def subscribe_address(self, address, notification_cb=None, cb=None): 148 | address_version, address_hash = \ 149 | bitcoin.bc_address_to_hash_160(address) 150 | # Prepare parameters. Use full prefix for now. 151 | # Type. 0 is address, 1 is stealth. 152 | data = struct.pack('B', 0) 153 | # Bitsize 154 | data += struct.pack('B', 160) 155 | # Hash bytes 156 | data += address_hash 157 | 158 | # run command 159 | self.send_command('address.subscribe', data, cb) 160 | if notification_cb: 161 | subscriptions = self._subscriptions['address'] 162 | if address not in subscriptions: 163 | subscriptions[address] = { 164 | "expiration": time.time() + 86400, 165 | "callbacks": [] 166 | } 167 | subscriptions = self._subscriptions['address'][address] 168 | if notification_cb not in subscriptions["callbacks"]: 169 | subscriptions["callbacks"].append(notification_cb) 170 | 171 | def subscribe_prefix(self, prefix, notification_cb=None, cb=None): 172 | # prefix = obelisk.Binary.from_string("1011110101") 173 | # https://wiki.unsystem.net/en/index.php/DarkWallet/Subscriber 174 | 175 | # Prepare parameters. 176 | # Type. 0 is address, 1 is stealth. 177 | data = struct.pack('B', 0) 178 | # Bitsize 179 | data += struct.pack('B', prefix.size) 180 | # Blocks 181 | 182 | # run command 183 | self.send_command('address.subscribe', data, cb) 184 | data += prefix.blocks 185 | 186 | def subscribe_stealth(self, prefix, notification_cb=None, cb=None): 187 | # prefix = obelisk.Binary.from_string("1011110101") 188 | # https://wiki.unsystem.net/en/index.php/DarkWallet/Subscriber 189 | 190 | # Prepare parameters. 191 | # Type. 0 is address, 1 is stealth. 192 | data = struct.pack('B', 1) 193 | # Bitsize 194 | data += struct.pack('B', prefix.size) 195 | # Blocks 196 | 197 | # run command 198 | self.send_command('address.subscribe', data, cb) 199 | data += prefix.blocks 200 | 201 | def unsubscribe_address(self, address, subscribed_cb, cb=None): 202 | address_version, address_hash = \ 203 | bitcoin.bc_address_to_hash_160(address) 204 | 205 | subscriptions = self._subscriptions['address'] 206 | if address in subscriptions: 207 | if subscribed_cb in subscriptions[address]["callbacks"]: 208 | subscriptions[address]["callbacks"].remove(subscribed_cb) 209 | if len(subscriptions[address]["callbacks"]) == 0: 210 | subscriptions.pop(address) 211 | if cb: 212 | cb(None, address) 213 | if self.subscribed > 0: 214 | self.subscribed -= 1 215 | 216 | def fetch_block_header(self, index, cb): 217 | """Fetches the block header by height.""" 218 | data = pack_block_index(index) 219 | self.send_command('blockchain.fetch_block_header', data, cb) 220 | 221 | def fetch_history2(self, address, cb, from_height=0): 222 | """Fetches history for an address. cb is a callback which 223 | accepts an error code, and a list of rows consisting of: 224 | 225 | id (obelisk.PointIdent.output or spend) 226 | point (hash and index) 227 | block height 228 | value / checksum 229 | 230 | If the row is for an output then the last item is the value. 231 | Otherwise it is a checksum of the previous output point, so 232 | spends can be matched to the rows they spend. 233 | Use spend_checksum(output_hash, output_index) to compute 234 | output point checksums.""" 235 | address_version, address_hash = \ 236 | bitcoin.bc_address_to_hash_160(address) 237 | 238 | # prepare parameters 239 | data = struct.pack('B', address_version) # address version 240 | data += address_hash # address 241 | data += struct.pack('= 1 325 | number_bits = prefix[0] 326 | data = struct.pack('= opcodes.OP_SINGLEBYTE_END: 338 | opcode <<= 8 339 | opcode |= ord(bytes[i]) 340 | i += 1 341 | 342 | if opcode <= opcodes.OP_PUSHDATA4: 343 | nSize = opcode 344 | if opcode == opcodes.OP_PUSHDATA1: 345 | nSize = ord(bytes[i]) 346 | i += 1 347 | elif opcode == opcodes.OP_PUSHDATA2: 348 | (nSize,) = struct.unpack_from(' 0: 367 | result += " " 368 | if opcode <= opcodes.OP_PUSHDATA4: 369 | result += "%d:" % (opcode,) 370 | result += short_hex(vch) 371 | else: 372 | result += script_GetOpName(opcode) 373 | return result 374 | 375 | 376 | def match_decoded(decoded, to_match): 377 | if len(decoded) != len(to_match): 378 | return False 379 | for i in range(len(decoded)): 380 | if to_match[i] == opcodes.OP_PUSHDATA4 and\ 381 | decoded[i][0] <= opcodes.OP_PUSHDATA4 and\ 382 | decoded[i][0] > 0: 383 | # Opcodes below OP_PUSHDATA4 all just push data 384 | # onto stack, and are equivalent. 385 | continue 386 | if to_match[i] != decoded[i][0]: 387 | return False 388 | return True 389 | 390 | 391 | def get_address_from_input_script(bytes): 392 | try: 393 | decoded = [x for x in script_GetOp(bytes)] 394 | except: 395 | # coinbase transactions raise an exception 396 | print_error("cannot find address in input script", bytes.encode('hex')) 397 | return [], [], "(None)" 398 | 399 | # non-generated TxIn transactions push a signature 400 | # (seventy-something bytes) and then their public key 401 | # (65 bytes) onto the stack: 402 | match = [opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4] 403 | if match_decoded(decoded, match): 404 | return ([decoded[1][1].encode('hex')], 405 | [decoded[0][1].encode('hex')], 406 | public_key_to_bc_address(decoded[1][1])) 407 | 408 | # p2sh transaction, 2 of n 409 | match = [opcodes.OP_0] 410 | while len(match) < len(decoded): 411 | match.append(opcodes.OP_PUSHDATA4) 412 | 413 | if match_decoded(decoded, match): 414 | redeemScript = decoded[-1][1] 415 | signatures = map(lambda x: x[1].encode('hex'), decoded[1:-1]) 416 | 417 | dec2 = [x for x in script_GetOp(redeemScript)] 418 | 419 | # 2 of 2 420 | match2 = [opcodes.OP_2, 421 | opcodes.OP_PUSHDATA4, 422 | opcodes.OP_PUSHDATA4, 423 | opcodes.OP_2, 424 | opcodes.OP_CHECKMULTISIG] 425 | if match_decoded(dec2, match2): 426 | pubkeys = [dec2[1][1].encode('hex'), 427 | dec2[2][1].encode('hex')] 428 | return (pubkeys, 429 | signatures, 430 | hash_160_to_bc_address(hash_160(redeemScript), chain.script_version)) 431 | 432 | # 2 of 3 433 | match2 = [opcodes.OP_2, 434 | opcodes.OP_PUSHDATA4, 435 | opcodes.OP_PUSHDATA4, 436 | opcodes.OP_PUSHDATA4, 437 | opcodes.OP_3, 438 | opcodes.OP_CHECKMULTISIG] 439 | if match_decoded(dec2, match2): 440 | pubkeys = [dec2[1][1].encode('hex'), 441 | dec2[2][1].encode('hex'), 442 | dec2[3][1].encode('hex')] 443 | return (pubkeys, 444 | signatures, 445 | hash_160_to_bc_address(hash_160(redeemScript), chain.script_version)) 446 | 447 | print_error("cannot find address in input script", bytes.encode('hex')) 448 | return [], [], "(None)" 449 | 450 | 451 | def get_address_from_output_script(bytes): 452 | decoded = [x for x in script_GetOp(bytes)] 453 | 454 | # The Genesis Block, self-payments, 455 | # and pay-by-IP-address payments look like: 456 | # 65 BYTES:... CHECKSIG 457 | match = [opcodes.OP_PUSHDATA4, opcodes.OP_CHECKSIG] 458 | if match_decoded(decoded, match): 459 | return public_key_to_bc_address(decoded[0][1]) 460 | 461 | # Pay-by-Bitcoin-address TxOuts look like: 462 | # DUP HASH160 20 BYTES:... EQUALVERIFY CHECKSIG 463 | match = [opcodes.OP_DUP, 464 | opcodes.OP_HASH160, 465 | opcodes.OP_PUSHDATA4, 466 | opcodes.OP_EQUALVERIFY, 467 | opcodes.OP_CHECKSIG] 468 | if match_decoded(decoded, match): 469 | return hash_160_to_bc_address(decoded[2][1]) 470 | 471 | # p2sh 472 | match = [opcodes.OP_HASH160, opcodes.OP_PUSHDATA4, opcodes.OP_EQUAL] 473 | if match_decoded(decoded, match): 474 | return hash_160_to_bc_address(decoded[1][1], chain.script_version) 475 | 476 | return "(None)" 477 | -------------------------------------------------------------------------------- /obelisk/error_code.py: -------------------------------------------------------------------------------- 1 | 2 | class obelisk_exception(Exception): 3 | pass 4 | 5 | 6 | class error_code(object): 7 | 8 | service_stopped = 1 9 | operation_failed = 2 10 | 11 | # blockchain errors 12 | not_found = 3 13 | duplicate = 4 14 | unspent_output = 5 15 | unsupported_payment_type = 6 16 | 17 | # network errors 18 | resolve_failed = 7 19 | network_unreachable = 8 20 | address_in_use = 9 21 | listen_failed = 10 22 | accept_failed = 11 23 | bad_stream = 12 24 | channel_timeout = 13 25 | 26 | # transaction pool 27 | blockchain_reorganized = 14 28 | pool_filled = 15 29 | 30 | # validate tx 31 | coinbase_transaction = 16 32 | is_not_standard = 17 33 | double_spend = 18 34 | input_not_found = 19 35 | 36 | # check_transaction() 37 | empty_transaction = 20 38 | output_value_overflow = 21 39 | invalid_coinbase_script_size = 22 40 | previous_output_null = 23 41 | 42 | # validate block 43 | previous_block_invalid = 24 44 | 45 | # check_block() 46 | size_limits = 25 47 | proof_of_work = 26 48 | futuristic_timestamp = 27 49 | first_not_coinbase = 28 50 | extra_coinbases = 29 51 | too_many_sigs = 30 52 | merkle_mismatch = 31 53 | 54 | # accept_block() 55 | incorrect_proof_of_work = 32 56 | timestamp_too_early = 33 57 | non_final_transaction = 34 58 | checkpoints_failed = 35 59 | old_version_block = 36 60 | coinbase_height_mismatch = 37 61 | 62 | # connect_block() 63 | duplicate_or_spent = 38 64 | validate_inputs_failed = 39 65 | fees_out_of_range = 40 66 | coinbase_too_large = 41 67 | 68 | @staticmethod 69 | def name_from_id(id): 70 | for key, value in error_code.__dict__.iteritems(): 71 | if value == id: 72 | return key 73 | return None 74 | -------------------------------------------------------------------------------- /obelisk/models.py: -------------------------------------------------------------------------------- 1 | import bitcoin 2 | import struct 3 | import serialize 4 | 5 | 6 | class BlockHeader: 7 | 8 | def __init__(self): 9 | self.height = None 10 | 11 | @classmethod 12 | def deserialize(cls, raw): 13 | assert len(raw) == 80 14 | self = cls() 15 | self.version = struct.unpack('' % (self.hash.encode("hex"),) 33 | 34 | 35 | #missing object at serialize.py 36 | class Block: 37 | pass 38 | 39 | 40 | class OutPoint(object): 41 | def __init__(self): 42 | self.hash = None 43 | self.index = None 44 | 45 | def is_null(self): 46 | return (len(self.hash) == 0) and (self.index == 0xffffffff) 47 | 48 | def __repr__(self): 49 | return "OutPoint(hash=%s, index=%i)" % \ 50 | (self.hash.encode("hex"), self.index) 51 | 52 | def serialize(self): 53 | return serialize.ser_output_point(self) 54 | 55 | @staticmethod 56 | def deserialize(bytes): 57 | return serialize.deser_output_point(bytes) 58 | 59 | 60 | class TxOut(object): 61 | def __init__(self): 62 | self.value = None 63 | self.script = "" 64 | 65 | def __repr__(self): 66 | return "TxOut(value=%i.%08i script=%s)" % \ 67 | (self.value // 100000000, 68 | self.value % 100000000, 69 | self.script.encode("hex")) 70 | 71 | def serialize(self): 72 | return serialize.ser_txout(self) 73 | 74 | @staticmethod 75 | def deserialize(bytes): 76 | return serialize.deser_txout(bytes) 77 | 78 | 79 | class TxIn(object): 80 | def __init__(self): 81 | self.previous_output = OutPoint() 82 | self.script = "" 83 | self.sequence = 0xffffffff 84 | 85 | def is_final(self): 86 | return self.sequence == 0xffffffff 87 | 88 | def __repr__(self): 89 | return "TxIn(previous_output=%s script=%s sequence=%i)" %\ 90 | (repr(self.previous_output), 91 | self.script.encode("hex"), 92 | self.sequence) 93 | 94 | def serialize(self): 95 | return serialize.ser_txin(self) 96 | 97 | @staticmethod 98 | def deserialize(bytes): 99 | return serialize.deser_txin(bytes) 100 | 101 | 102 | class Transaction: 103 | def __init__(self): 104 | self.version = 1 105 | self.locktime = 0 106 | self.inputs = [] 107 | self.outputs = [] 108 | 109 | def is_final(self): 110 | for tin in self.vin: 111 | if not tin.is_final(): 112 | return False 113 | return True 114 | 115 | def is_coinbase(self): 116 | return len(self.vin) == 1 and self.vin[0].prevout.is_null() 117 | 118 | def __repr__(self): 119 | return "Transaction(version=%i inputs=%s outputs=%s locktime=%i)" %\ 120 | (self.version, 121 | repr(self.inputs), 122 | repr(self.outputs), 123 | self.locktime) 124 | 125 | def serialize(self): 126 | return serialize.ser_tx(self) 127 | 128 | @staticmethod 129 | def deserialize(bytes): 130 | return serialize.deser_tx(bytes) 131 | -------------------------------------------------------------------------------- /obelisk/numbertheory.py: -------------------------------------------------------------------------------- 1 | 2 | def inverse_mod(a, m): 3 | """Inverse of a mod m.""" 4 | 5 | if a < 0 or m <= a: 6 | a = a % m 7 | 8 | # From Ferguson and Schneier, roughly: 9 | 10 | c, d = a, m 11 | uc, vc, ud, vd = 1, 0, 0, 1 12 | while c != 0: 13 | q, c, d = divmod(d, c) + (c, ) 14 | uc, vc, ud, vd = ud - q*uc, vd - q*vc, uc, vc 15 | 16 | # At this point, d is the GCD, and ud*a+vd*m = d. 17 | # If d == 1, this means that ud is a inverse. 18 | 19 | assert d == 1 20 | if ud > 0: 21 | return ud 22 | return ud + m 23 | 24 | # from http://eli.thegreenplace.net/2009/03/07/computing-modular-square-roots-in-python/ 25 | 26 | 27 | def modular_sqrt(a, p): 28 | """ Find a quadratic residue (mod p) of 'a'. p 29 | must be an odd prime. 30 | 31 | Solve the congruence of the form: 32 | x^2 = a (mod p) 33 | And returns x. Note that p - x is also a root. 34 | 35 | 0 is returned is no square root exists for 36 | these a and p. 37 | 38 | The Tonelli-Shanks algorithm is used (except 39 | for some simple cases in which the solution 40 | is known from an identity). This algorithm 41 | runs in polynomial time (unless the 42 | generalized Riemann hypothesis is false). 43 | """ 44 | # Simple cases 45 | # 46 | if legendre_symbol(a, p) != 1: 47 | return 0 48 | elif a == 0: 49 | return 0 50 | elif p == 2: 51 | return p 52 | elif p % 4 == 3: 53 | return pow(a, (p + 1) // 4, p) 54 | 55 | # Partition p-1 to s * 2^e for an odd s (i.e. 56 | # reduce all the powers of 2 from p-1) 57 | # 58 | s = p - 1 59 | e = 0 60 | while s % 2 == 0: 61 | s /= 2 62 | e += 1 63 | 64 | # Find some 'n' with a legendre symbol n|p = -1. 65 | # Shouldn't take long. 66 | # 67 | n = 2 68 | while legendre_symbol(n, p) != -1: 69 | n += 1 70 | 71 | # Here be dragons! 72 | # Read the paper "Square roots from 1; 24, 51, 73 | # 10 to Dan Shanks" by Ezra Brown for more 74 | # information 75 | # 76 | 77 | # x is a guess of the square root that gets better 78 | # with each iteration. 79 | # b is the "fudge factor" - by how much we're off 80 | # with the guess. The invariant x^2 = ab (mod p) 81 | # is maintained throughout the loop. 82 | # g is used for successive powers of n to update 83 | # both a and b 84 | # r is the exponent - decreases with each update 85 | # 86 | x = pow(a, (s + 1) // 2, p) 87 | b = pow(a, s, p) 88 | g = pow(n, s, p) 89 | r = e 90 | 91 | while True: 92 | t = b 93 | m = 0 94 | for m in xrange(r): 95 | if t == 1: 96 | break 97 | t = pow(t, 2, p) 98 | 99 | if m == 0: 100 | return x 101 | 102 | gs = pow(g, 2 ** (r - m - 1), p) 103 | g = (gs * gs) % p 104 | x = (x * gs) % p 105 | b = (b * g) % p 106 | r = m 107 | 108 | 109 | def legendre_symbol(a, p): 110 | """ Compute the Legendre symbol a|p using 111 | Euler's criterion. p is a prime, a is 112 | relatively prime to p (if p divides 113 | a, then a|p = 0) 114 | 115 | Returns 1 if a has a square root modulo 116 | p, -1 otherwise. 117 | """ 118 | ls = pow(a, (p - 1) // 2, p) 119 | return -1 if ls == p - 1 else ls 120 | -------------------------------------------------------------------------------- /obelisk/serialize.py: -------------------------------------------------------------------------------- 1 | __author__ = 'bobalot' 2 | 3 | import struct 4 | import io 5 | import hashlib 6 | from binascii import hexlify, unhexlify 7 | from hashlib import sha256 8 | import models 9 | 10 | 11 | # Py3 compatibility 12 | import sys 13 | bchr = chr 14 | if sys.version > '3': 15 | bchr = lambda x: bytes([x]) 16 | 17 | 18 | def deser_uint32(f): 19 | if type(f) is not io.BytesIO: 20 | f = io.BytesIO(f) 21 | 22 | return struct.unpack(b">= 32 73 | return rs 74 | 75 | 76 | def ser_uint160(u): 77 | rs = b"" 78 | for i in range(5): 79 | rs += struct.pack(b">= 32 81 | return rs 82 | 83 | 84 | def uint160_from_str(s): 85 | r = 0 86 | t = struct.unpack(b"> 24) & 0xFF 102 | v = (c & 0xFFFFFF) << (8 * (nbytes - 3)) 103 | return v 104 | 105 | 106 | def uint256_to_shortstr(u): 107 | s = "%064x" % (u,) 108 | return s[:16] 109 | 110 | 111 | def deser_variable_uint(f): 112 | length = struct.unpack(b"= min_value: 64 | result.change = accum - min_value 65 | return result 66 | return None 67 | -------------------------------------------------------------------------------- /obelisk/util.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import re 4 | import platform 5 | import shutil 6 | from datetime import datetime 7 | from decimal import Decimal 8 | 9 | is_verbose = True 10 | 11 | btc = Decimal('1'+'0'*8) 12 | 13 | 14 | def to_btc(value): 15 | return Decimal(value)/btc 16 | 17 | 18 | def set_verbosity(b): 19 | global is_verbose 20 | is_verbose = b 21 | 22 | 23 | def print_error(*args): 24 | if not is_verbose: 25 | return 26 | args = [str(item) for item in args] 27 | sys.stderr.write(" ".join(args) + "\n") 28 | sys.stderr.flush() 29 | 30 | 31 | def print_msg(*args): 32 | # Stringify args 33 | args = [str(item) for item in args] 34 | sys.stdout.write(" ".join(args) + "\n") 35 | sys.stdout.flush() 36 | 37 | 38 | def print_json(obj): 39 | import json 40 | s = json.dumps(obj, sort_keys=True, indent=4) 41 | sys.stdout.write(s + "\n") 42 | sys.stdout.flush() 43 | 44 | 45 | def check_windows_wallet_migration(): 46 | if platform.release() != "XP": 47 | path = os.path.join(os.environ["LOCALAPPDATA"], "Electrum") 48 | if os.path.exists(path): 49 | if os.path.exists(os.path.join(os.environ["APPDATA"], "Electrum")): 50 | print_msg("Two Electrum folders have been found, the " + 51 | "default Electrum location for Windows has " + 52 | "changed from %s to %s since Electrum 1.7, " + 53 | "please check your wallets and fix the problem " + 54 | "manually." % 55 | (os.environ["LOCALAPPDATA"], os.environ["APPDATA"])) 56 | sys.exit() 57 | try: 58 | shutil.move(path, os.path.join(os.environ["APPDATA"])) 59 | print_msg("Your wallet has been moved from %s to %s." % 60 | (os.environ["LOCALAPPDATA"], os.environ["APPDATA"])) 61 | except: 62 | print_msg("Failed to move your wallet.") 63 | 64 | 65 | def user_dir(): 66 | if "HOME" in os.environ: 67 | return os.path.join(os.environ["HOME"], ".electrum") 68 | elif "APPDATA" in os.environ: 69 | return os.path.join(os.environ["APPDATA"], "Electrum") 70 | elif "LOCALAPPDATA" in os.environ: 71 | return os.path.join(os.environ["LOCALAPPDATA"], "Electrum") 72 | else: 73 | return 74 | 75 | 76 | def appdata_dir(): 77 | """Find the path to the application data directory; 78 | add an electrum folder and return path.""" 79 | if platform.system() == "Windows": 80 | return os.path.join(os.environ["APPDATA"], "Electrum") 81 | elif platform.system() == "Linux": 82 | return os.path.join(sys.prefix, "share", "electrum") 83 | elif (platform.system() == "Darwin" or 84 | platform.system() == "DragonFly" or 85 | platform.system() == "NetBSD"): 86 | return "/Library/Application Support/Electrum" 87 | else: 88 | raise Exception("Unknown system") 89 | 90 | 91 | def get_resource_path(*args): 92 | return os.path.join(".", *args) 93 | 94 | 95 | def local_data_dir(): 96 | """Return path to the data folder.""" 97 | assert sys.argv 98 | prefix_path = os.path.dirname(sys.argv[0]) 99 | local_data = os.path.join(prefix_path, "data") 100 | return local_data 101 | 102 | 103 | def format_satoshis(x, is_diff=False, 104 | num_zeros=0, decimal_point=8, 105 | whitespaces=False): 106 | from decimal import Decimal 107 | s = Decimal(x) 108 | sign, digits, exp = s.as_tuple() 109 | digits = map(str, digits) 110 | while len(digits) < decimal_point + 1: 111 | digits.insert(0, '0') 112 | digits.insert(-decimal_point, '.') 113 | s = ''.join(digits).rstrip('0') 114 | if sign: 115 | s = '-' + s 116 | elif is_diff: 117 | s = "+" + s 118 | 119 | p = s.find('.') 120 | s += "0" * (1 + num_zeros - (len(s) - p)) 121 | if whitespaces: 122 | s += " " * (1 + decimal_point - (len(s) - p)) 123 | s = " " * (13 - decimal_point - (p)) + s 124 | return s 125 | 126 | 127 | # Takes a timestamp and returns a string with the approximation of the age 128 | def age(from_date, since_date=None, target_tz=None, include_seconds=False): 129 | if from_date is None: 130 | return "Unknown" 131 | 132 | from_date = datetime.fromtimestamp(from_date) 133 | if since_date is None: 134 | since_date = datetime.now(target_tz) 135 | 136 | distance_in_time = since_date - from_date 137 | distance_in_seconds = distance_in_time.days * 86400 138 | distance_in_seconds += distance_in_time.seconds 139 | distance_in_seconds = int(round(abs(distance_in_seconds))) 140 | distance_in_minutes = int(round(distance_in_seconds/60)) 141 | 142 | if distance_in_minutes <= 1: 143 | if include_seconds: 144 | for remainder in [5, 10, 20]: 145 | if distance_in_seconds < remainder: 146 | return "less than %s seconds ago" % remainder 147 | if distance_in_seconds < 40: 148 | return "half a minute ago" 149 | elif distance_in_seconds < 60: 150 | return "less than a minute ago" 151 | else: 152 | return "1 minute ago" 153 | else: 154 | if distance_in_minutes == 0: 155 | return "less than a minute ago" 156 | else: 157 | return "1 minute ago" 158 | elif distance_in_minutes < 45: 159 | return "%s minutes ago" % distance_in_minutes 160 | elif distance_in_minutes < 90: 161 | return "about 1 hour ago" 162 | elif distance_in_minutes < 1440: 163 | return "about %d hours ago" % (round(distance_in_minutes / 60.0)) 164 | elif distance_in_minutes < 2880: 165 | return "1 day ago" 166 | elif distance_in_minutes < 43220: 167 | return "%d days ago" % (round(distance_in_minutes / 1440)) 168 | elif distance_in_minutes < 86400: 169 | return "about 1 month ago" 170 | elif distance_in_minutes < 525600: 171 | return "%d months ago" % (round(distance_in_minutes / 43200)) 172 | elif distance_in_minutes < 1051200: 173 | return "about 1 year ago" 174 | else: 175 | return "over %d years ago" % (round(distance_in_minutes / 525600)) 176 | 177 | # URL decode 178 | _ud = re.compile('%([0-9a-hA-H]{2})', re.MULTILINE) 179 | urldecode = lambda x: _ud.sub(lambda m: chr(int(m.group(1), 16)), x) 180 | 181 | 182 | def parse_url(url): 183 | o = url[8:].split('?') 184 | address = o[0] 185 | if len(o) > 1: 186 | params = o[1].split('&') 187 | else: 188 | params = [] 189 | 190 | amount = label = message = signature = identity = '' 191 | for p in params: 192 | k, v = p.split('=') 193 | uv = urldecode(v) 194 | if k == 'amount': 195 | amount = uv 196 | elif k == 'message': 197 | message = uv 198 | elif k == 'label': 199 | label = uv 200 | elif k == 'signature': 201 | identity, signature = uv.split(':') 202 | url = url.replace('&%s=%s' % (k, v), '') 203 | else: 204 | print k, v 205 | 206 | return address, amount, label, message, signature, identity, url 207 | -------------------------------------------------------------------------------- /obelisk/zmq_fallback.py: -------------------------------------------------------------------------------- 1 | import zmq 2 | from twisted.internet import task 3 | 4 | # Some versions of ZMQ have the error in a different module. 5 | try: 6 | zmq.error 7 | except AttributeError: 8 | zmq.error = zmq.core.error 9 | 10 | 11 | class ZmqSocket: 12 | 13 | context = zmq.Context(1) 14 | 15 | def __init__(self, cb, version, type=zmq.DEALER): 16 | self._cb = cb 17 | self._type = type 18 | if self._type == 'SUB': 19 | self._type = zmq.SUB 20 | self.public_key, self.secret_key = zmq.curve_keypair() 21 | 22 | def connect(self, address, public_key=None): 23 | self._socket = ZmqSocket.context.socket(self._type) 24 | if public_key: 25 | self._socket.curve_serverkey = public_key 26 | self._socket.curve_publickey = self.public_key 27 | self._socket.curve_secretkey = self.secret_key 28 | self._socket.connect(address) 29 | if self._type == zmq.SUB: 30 | self._socket.setsockopt(zmq.SUBSCRIBE, '') 31 | self.loop = task.LoopingCall(self.poll) 32 | self.loop.start(0.1) 33 | 34 | def poll(self): 35 | # keep polling till we have no more data 36 | while self.poll_once(): 37 | pass 38 | 39 | def poll_once(self): 40 | try: 41 | data = self._socket.recv(flags=zmq.NOBLOCK) 42 | except zmq.error.ZMQError: 43 | return False 44 | more = self._socket.getsockopt(zmq.RCVMORE) 45 | self._cb(data, more) 46 | return True 47 | 48 | def send(self, data, more=0): 49 | if more: 50 | more = zmq.SNDMORE 51 | self._socket.send(data, more) 52 | 53 | def close(self): 54 | self.loop.stop() 55 | self._socket.close() 56 | -------------------------------------------------------------------------------- /obelisk/zmqbase.py: -------------------------------------------------------------------------------- 1 | import random 2 | import struct 3 | import logging 4 | 5 | # Broken for ZMQ 4 6 | #try: 7 | # from zmqproto import ZmqSocket 8 | #except ImportError: 9 | # from zmq_fallback import ZmqSocket 10 | from zmq_fallback import ZmqSocket 11 | from twisted.internet import defer, reactor 12 | 13 | 14 | SNDMORE = 1 15 | 16 | MAX_UINT32 = 4294967295 17 | 18 | 19 | class ClientBase(object): 20 | 21 | valid_messages = [] 22 | 23 | def __init__(self, 24 | address, 25 | public_key=None, 26 | block_address=None, 27 | tx_address=None, 28 | version=3): 29 | self._messages = [] 30 | self._tx_messages = [] 31 | self._block_messages = [] 32 | self.zmq_version = version 33 | self.address = address 34 | self.public_key = public_key 35 | self._socket = self.setup(address, public_key) 36 | if block_address: 37 | self._socket_block = self.setup_block_sub( 38 | block_address, self.on_raw_block) 39 | if tx_address: 40 | self._socket_tx = self.setup_transaction_sub( 41 | tx_address, self.on_raw_transaction) 42 | self._subscriptions = {'address': {}} 43 | self._timeouts = {} 44 | 45 | # Message arrived 46 | def on_raw_message(self, id, cmd, data): 47 | res = None 48 | short_cmd = cmd.split('.')[-1] 49 | if short_cmd in self.valid_messages: 50 | res = getattr(self, '_on_'+short_cmd)(data) 51 | else: 52 | logging.warning("Unknown Message " + cmd) 53 | if res: 54 | self.trigger_callbacks(id, *res) 55 | 56 | def on_raw_block(self, height, hash, header, tx_num, tx_hashes): 57 | print "block", height, len(tx_hashes) 58 | 59 | def on_raw_transaction(self, tx_data): 60 | print "tx", tx_data.encode('hex') 61 | 62 | # Base Api 63 | def send_command(self, command, data='', cb=None): 64 | tx_id = random.randint(0, MAX_UINT32) 65 | 66 | # command 67 | self.send(command, SNDMORE) 68 | # id (random) 69 | self.send(struct.pack('I', tx_id), SNDMORE) 70 | # data 71 | self.send(data, 0) 72 | 73 | if cb: 74 | self._subscriptions[tx_id] = cb 75 | timeout = reactor.callLater(4, self._reconnect) 76 | self._timeouts[tx_id] = [timeout, command, data, cb] 77 | return tx_id 78 | 79 | def unsubscribe(self, cb): 80 | for sub_id in self._subscriptions.keys(): 81 | if self._subscriptions[sub_id] == cb: 82 | self._subscriptions.pop(sub_id) 83 | 84 | def trigger_callbacks(self, tx_id, *args): 85 | if tx_id in self._timeouts: 86 | self._timeouts[tx_id][0].cancel() 87 | del self._timeouts[tx_id] 88 | if tx_id in self._subscriptions: 89 | self._subscriptions[tx_id](*args) 90 | del self._subscriptions[tx_id] 91 | 92 | def _reconnect(self): 93 | if self.log: 94 | self.log.error("Libbitcoin server timed out. Refreshing socket and resending requests.") 95 | self._socket.close() 96 | self._socket = self.setup(self.address, self.public_key) 97 | for tx_id in self._timeouts.keys(): 98 | v = self._timeouts[tx_id] 99 | if v[0].active(): 100 | v[0].cancel() 101 | self.send_command(v[1], data=v[2], cb=v[3]) 102 | if tx_id in self._timeouts: 103 | del self._timeouts[tx_id] 104 | if tx_id in self._subscriptions: 105 | del self._subscriptions[tx_id] 106 | for address in self._subscriptions["address"].keys(): 107 | callbacks = self._subscriptions["address"][address]["callbacks"] 108 | for callback in callbacks: 109 | self.subscribe_address(address, callback) 110 | 111 | # Low level zmq abstraction into obelisk frames 112 | def send(self, *args, **kwargs): 113 | self._socket.send(*args, **kwargs) 114 | 115 | def frame_received(self, frame, more): 116 | self._messages.append(frame) 117 | if not more: 118 | if not len(self._messages) == 3: 119 | print "Sequence with wrong messages", len(self._messages) 120 | print [m.encode("hex") for m in self._messages] 121 | self._messages = [] 122 | return 123 | command, id, data = self._messages 124 | self._messages = [] 125 | id = struct.unpack('I', id)[0] 126 | self.on_raw_message(id, command, data) 127 | 128 | def block_received(self, frame, more): 129 | self._block_messages.append(frame) 130 | if not more: 131 | nblocks = struct.unpack('Q', self._block_messages[3])[0] 132 | if not len(self._block_messages) == 4 + nblocks: 133 | print "Sequence with wrong messages",\ 134 | len(self._block_messages),\ 135 | 4 + nblocks 136 | self._block_messages = [] 137 | return 138 | height, hash, header, tx_num = self._block_messages[:4] 139 | tx_hashes = self._block_messages[5:] 140 | if len(tx_num) >= 4: 141 | tx_num = struct.unpack_from('I', tx_num, 0)[0] 142 | else: 143 | print "wrong tx_num length", len(tx_num), tx_num 144 | tx_num = struct.unpack('I', tx_num.zfill(4))[0] 145 | self._block_messages = [] 146 | height = struct.unpack('I', height)[0] 147 | self._block_cb(height, hash, header, tx_num, tx_hashes) 148 | 149 | def transaction_received(self, frame, more): 150 | self._tx_messages.append(frame) 151 | if not more: 152 | if not len(self._tx_messages) == 1: 153 | print "Sequence with wrong messages", len(self._tx_messages) 154 | self._tx_messages = [] 155 | return 156 | tx_data = self._tx_messages[0] 157 | self._tx_messages = [] 158 | self._tx_cb(tx_data) 159 | 160 | def setup(self, address, public_key=None): 161 | s = ZmqSocket(self.frame_received, self.zmq_version) 162 | s.connect(address, public_key) 163 | return s 164 | 165 | def setup_block_sub(self, address, cb): 166 | s = ZmqSocket(self.block_received, self.zmq_version, type='SUB') 167 | s.connect(address) 168 | self._block_cb = cb 169 | return s 170 | 171 | def setup_transaction_sub(self, address, cb): 172 | s = ZmqSocket(self.transaction_received, self.zmq_version, type='SUB') 173 | s.connect(address) 174 | self._tx_cb = cb 175 | return s 176 | 177 | # Low level packing 178 | def get_error(data): 179 | return struct.unpack_from('