├── .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 | *.pyc
2 | .gitignore
3 | *.egg-info
4 | src/
5 | .*.sw*
6 |
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 | * Pablo Martin (caedes)
2 | * ThomasV, Electrum
3 | * Robert Williamson, blockalchemy (bobalot)
4 | * Amir Taaki (genjix)
5 |
6 |
--------------------------------------------------------------------------------
/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 |
5 | import bitcoin
6 | import serialize
7 | import error_code
8 |
9 |
10 | def unpack_error(data):
11 | value = struct.unpack_from('= 1
244 | number_bits = prefix[0]
245 | 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 |
21 | def connect(self, address):
22 | self._socket = ZmqSocket.context.socket(self._type)
23 | self._socket.connect(address)
24 | if self._type == zmq.SUB:
25 | self._socket.setsockopt(zmq.SUBSCRIBE, '')
26 | l = task.LoopingCall(self.poll)
27 | l.start(0.1)
28 |
29 | def poll(self):
30 | # keep polling till we have no more data
31 | while self.poll_once():
32 | pass
33 |
34 | def poll_once(self):
35 | try:
36 | data = self._socket.recv(flags=zmq.NOBLOCK)
37 | except zmq.error.ZMQError:
38 | return False
39 | more = self._socket.getsockopt(zmq.RCVMORE)
40 | self._cb(data, more)
41 | return True
42 |
43 | def send(self, data, more=0):
44 | if more:
45 | more = zmq.SNDMORE
46 | self._socket.send(data, more)
47 |
--------------------------------------------------------------------------------
/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 |
12 |
13 | SNDMORE = 1
14 |
15 | MAX_UINT32 = 4294967295
16 |
17 |
18 | class ClientBase(object):
19 |
20 | valid_messages = []
21 |
22 | def __init__(self,
23 | address,
24 | block_address=None,
25 | tx_address=None,
26 | version=3):
27 | self._messages = []
28 | self._tx_messages = []
29 | self._block_messages = []
30 | self.zmq_version = version
31 | self.running = 1
32 | self._socket = self.setup(address)
33 | if block_address:
34 | self._socket_block = self.setup_block_sub(
35 | block_address, self.on_raw_block)
36 | if tx_address:
37 | self._socket_tx = self.setup_transaction_sub(
38 | tx_address, self.on_raw_transaction)
39 | self._subscriptions = {'address': {}}
40 |
41 | # Message arrived
42 | def on_raw_message(self, id, cmd, data):
43 | res = None
44 | short_cmd = cmd.split('.')[-1]
45 | if short_cmd in self.valid_messages:
46 | res = getattr(self, '_on_'+short_cmd)(data)
47 | else:
48 | logging.warning("Unknown Message " + cmd)
49 | if res:
50 | self.trigger_callbacks(id, *res)
51 |
52 | def on_raw_block(self, height, hash, header, tx_num, tx_hashes):
53 | print "block", height, len(tx_hashes)
54 |
55 | def on_raw_transaction(self, tx_data):
56 | print "tx", tx_data.encode('hex')
57 |
58 | # Base Api
59 | def send_command(self, command, data='', cb=None):
60 | tx_id = random.randint(0, MAX_UINT32)
61 |
62 | # command
63 | self.send(command, SNDMORE)
64 | # id (random)
65 | self.send(struct.pack('I', tx_id), SNDMORE)
66 | # data
67 | self.send(data, 0)
68 |
69 | if cb:
70 | self._subscriptions[tx_id] = cb
71 | return tx_id
72 |
73 | def unsubscribe(self, cb):
74 | for sub_id in self._subscriptions.keys():
75 | if self._subscriptions[sub_id] == cb:
76 | self._subscriptions.pop(sub_id)
77 |
78 | def trigger_callbacks(self, tx_id, *args):
79 | if tx_id in self._subscriptions:
80 | self._subscriptions[tx_id](*args)
81 | del self._subscriptions[tx_id]
82 |
83 | # Low level zmq abstraction into obelisk frames
84 | def send(self, *args, **kwargs):
85 | self._socket.send(*args, **kwargs)
86 |
87 | def frame_received(self, frame, more):
88 | self._messages.append(frame)
89 | if not more:
90 | if not len(self._messages) == 3:
91 | print "Sequence with wrong messages", len(self._messages)
92 | print [m.encode("hex") for m in self._messages]
93 | self._messages = []
94 | return
95 | command, id, data = self._messages
96 | self._messages = []
97 | id = struct.unpack('I', id)[0]
98 | self.on_raw_message(id, command, data)
99 |
100 | def block_received(self, frame, more):
101 | self._block_messages.append(frame)
102 | if not more:
103 | nblocks = struct.unpack('Q', self._block_messages[3])[0]
104 | if not len(self._block_messages) == 4 + nblocks:
105 | print "Sequence with wrong messages",\
106 | len(self._block_messages),\
107 | 4 + nblocks
108 | self._block_messages = []
109 | return
110 | height, hash, header, tx_num = self._block_messages[:4]
111 | tx_hashes = self._block_messages[5:]
112 | if len(tx_num) >= 4:
113 | tx_num = struct.unpack_from('I', tx_num, 0)[0]
114 | else:
115 | print "wrong tx_num length", len(tx_num), tx_num
116 | tx_num = struct.unpack('I', tx_num.zfill(4))[0]
117 | self._block_messages = []
118 | height = struct.unpack('I', height)[0]
119 | self._block_cb(height, hash, header, tx_num, tx_hashes)
120 |
121 | def transaction_received(self, frame, more):
122 | self._tx_messages.append(frame)
123 | if not more:
124 | if not len(self._tx_messages) == 1:
125 | print "Sequence with wrong messages", len(self._tx_messages)
126 | self._tx_messages = []
127 | return
128 | tx_data = self._tx_messages[0]
129 | self._tx_messages = []
130 | self._tx_cb(tx_data)
131 |
132 | def setup(self, address):
133 | s = ZmqSocket(self.frame_received, self.zmq_version)
134 | s.connect(address)
135 | return s
136 |
137 | def setup_block_sub(self, address, cb):
138 | s = ZmqSocket(self.block_received, self.zmq_version, type='SUB')
139 | s.connect(address)
140 | self._block_cb = cb
141 | return s
142 |
143 | def setup_transaction_sub(self, address, cb):
144 | s = ZmqSocket(self.transaction_received, self.zmq_version, type='SUB')
145 | s.connect(address)
146 | self._tx_cb = cb
147 | return s
148 |
149 | # Low level packing
150 | def get_error(data):
151 | return struct.unpack_from('