├── .gitignore
├── LICENSE.md
├── README.md
├── lib
├── address.js
├── bip39.js
├── bip39wordlists
│ └── en_wordlist.js
├── block.js
├── crypto.js
├── hd.js
├── index.js
├── network.js
├── opcodes.js
├── scripts.js
├── solvers.js
└── transaction.js
├── package-lock.json
├── package.json
├── test
├── addresses.js
├── bip32.js
├── bip39.js
├── blocks.js
├── hd.js
├── integration
│ ├── client.js
│ ├── config.js
│ ├── test.js
│ └── test_node
│ │ └── bitcoin.conf
├── scripts.js
├── segwit.js
├── signatures.js
├── solversdata
├── test.js
├── transactions.js
└── wif.js
└── tools
├── bytebuffer.js
└── conversions.js
/.gitignore:
--------------------------------------------------------------------------------
1 | npm-freeze-manifest.json
2 | node_modules/
3 | .idea/
4 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | developed with :heart: by chainside
5 |
6 |
7 | # btcnodejs
8 |
9 | `btcnodejs` is a Segwit-compliant bitcoin library which provides tools for managing bitcoin data structures. It is the NodeJS version of [btcpy](https://github.com/chainside/btcpy).
10 |
11 | **This library is a work in progress and its usage in a production environment is highly discouraged. Also, as long as the version is 0.\*, API breaking changes might occur**
12 |
13 | Some of the functionalities are a wrapping around [bitcoinjs-lib](https://github.com/bitcoinjs/bitcoinjs-lib), and their development is still in progress.
14 |
15 | It makes usage of [bytebuffer.js](https://github.com/dcodeIO/bytebuffer.js) for representing most of the data structures
16 |
17 | Table of Contents
18 | =================
19 | * [Installation](#installation)
20 | * [Usage](#usage)
21 | * [Browserify](#browserify)
22 | * [API](#api)
23 | * [Transactions](#transactions)
24 | * [Scripts](#scripts)
25 | * [Solvers](#solvers)
26 | * [Crypto](#crypto)
27 | * [HD](#hd)
28 | * [Address](#address)
29 | * [Block](#block)
30 |
31 | # Installation
32 |
33 | To install the library, run `npm install btcnodejs`
34 |
35 | # Browserify
36 |
37 | This library can be used in the browser with [browserify](https://github.com/browserify/browserify). If you are familiar with browserify, you can skip this.
38 |
39 | Assuming the entry point of the project is a file main.js like:
40 |
41 | ```javascript
42 | const btcnodejs = require("btcnodejs");
43 | const net = btcnodejs.network;
44 | net.setup("testnet");
45 | var k = new btcnodejs.HDPrivateKey();
46 | console.log(k.privkey.toWIF());
47 | ```
48 |
49 | Go into the main.js folder and run:
50 |
51 | ```
52 | browserify main.js > browser_main.js
53 |
54 | ```
55 |
56 | Now you can load the 'browserified' main.js into your html:
57 |
58 | ```html
59 |
60 |
61 |
62 | Example Browserify
63 |
64 |
65 |
66 |
67 |
68 |
69 | ```
70 | # What it does
71 |
72 | This library aims to manage bitcoin data structures. It offers functionalities for
73 |
74 | * Transactions and block headers deserialization
75 |
76 | * Scripts creation
77 |
78 | * Privatekeys, Publickeys and HDkeys management
79 |
80 | * Transactions signing
81 |
82 | # What it does not do
83 |
84 | This library does not implement the following functionalities:
85 |
86 | * Validation : when transactions and scripts are parsed, only
87 | format errors are reported. No proof-of-work validation, script execution,
88 | transaction validation and signature verification is performed
89 |
90 | * Networking : this library does not provide functionalities to communicate with bitcoin nodes. Separates networking modules will be released soon.
91 |
92 | # Tests
93 |
94 | In order to run tests, cd in the package directory and run
95 |
96 | `npm test`
97 |
98 | To run tests in the browser, you first need [brfs](https://github.com/browserify/brfs) installed. Then you can run browserify on the tests file, by doing (within the package directory):
99 |
100 | `browserify -t brfs test/test.js > test/browser_tests.js`
101 |
102 | Now you can create the .html file with the test script:
103 |
104 | ```html
105 |
106 |
107 |
108 | Mocha Tests
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
120 |
121 |
122 | ```
123 | # Usage
124 | On the first import, network setup must be executed. This is achieved by doing:
125 |
126 | ```javascript
127 | const network = require('btcnodejs').network
128 | network.setup('testnet') //network can be either 'testnet' or 'mainnet'
129 | ```
130 | Once the network setup is executed, every subsequent setup will throw an exception.
131 |
132 | The network module also exposes functionalities to get the current setup network
133 |
134 | ```javascript
135 | const network = require('btcnodejs').network
136 | network.setup('testnet')
137 | network.net_name() //outputs 'testnet'
138 | network.is_mainnet() //returns 'false'
139 |
140 | ```
141 |
142 |
143 | # API
144 |
145 | ## Transactions
146 | ## `btcnodejs.Transaction`
147 |
148 | Transactions are immutable objects representing bitcoin transactions.
149 |
150 | #### Attributes
151 |
152 | * **version : Integer**
153 |
154 | * **inputs : list of Input objects**
155 |
156 | * **outputs : list of Output objects**
157 |
158 | * **locktime : Locktime object**
159 |
160 | * **segwit : Boolean**
161 |
162 | * **txid : String**
163 |
164 | #### new Transaction(version, inputs, outputs, locktime, segwit=false)
165 |
166 | #### toJSON()
167 | Returns the JSON representation of the transaction
168 | #### hash()
169 | Computes the double sha256 on the serialized transaction.
170 | Returns the hex string representing the hash
171 | #### segwitId()
172 | Computes the txid of a segwit transaction
173 | Returns the hex string representing the id of the transaction.
174 | #### static fromHex(hex)
175 |
176 | * **hex : Hexadecimal string**
177 |
178 | Returns a Transaction object
179 |
180 | ```javascript
181 | const btcnodejs = require('btcnodejs')
182 | const Transaction = btcnodejs.Transaction
183 | btcnodejs.network.setup('testnet')
184 |
185 | const tx = Transaction.fromHex("0100000001e4da173fbefe5e60ff63dfd38566ade407532294db655463b77a783f379ce605000000006b4" +
186 | "83045022100af246c27890c2bc07a0b7450d3d82509702a44a4defdff766355240b114ee2ac02207bb67b" +
187 | "468452fa1b325dd5583879f5c1412e0bb4dae1c2c96c7a408796ab76f1012102ab9e8575536a1e99604a1" +
188 | "58fc60fe2ebd1cb1839e919b4ca42b8d050cfad71b2ffffffff0100c2eb0b000000001976a914df76c017" +
189 | "354ac39bde796abe4294d31de8b5788a88ac00000000")
190 |
191 | console.log(tx.txid) //'e977c07090c2a1dcaefd3f3c4ebf4e231f4116cb272f805b0b22a85e7eece09c'
192 |
193 |
194 | ```
195 |
196 | #### toHex()
197 |
198 | Returns the hexadecimal representation of the transactions
199 |
200 | #### serialize(segwit = this.segwit)
201 |
202 | * **segwit : Boolean**
203 |
204 | Returns a ByteBuffer containing the serialized transaction. If called with no parameter, the serialization is performed based on the transaction type. To perform a non-segwit serialization of a segwit transaction(i.e. to compute the segwit txid), false can be passed to the function.
205 |
206 | #### static deserialize(bytebuffer)
207 |
208 | * **bytebuffer : ByteBuffer object**
209 |
210 | Returns a Transaction object from a ByteBuffer object representing the serialized transaction
211 |
212 | #### toMutable()
213 |
214 | Returns a MutableTransaction object
215 |
216 | ## `btcnodejs.MutableTransaction`
217 |
218 | Mutable Transaction objects
219 |
220 | #### Attributes
221 |
222 | * **version : Integer**
223 |
224 | * **inputs : list of Input objects**
225 |
226 | * **outputs : list of Output objects**
227 |
228 | * **locktime : Locktime object**
229 |
230 | * **segwit : Boolean**
231 |
232 | * **txid : String**
233 |
234 | #### new MutableTransaction(version, inputs, outputs, locktime, segwit=false)
235 |
236 | Returns a MutableTransaction object
237 |
238 | #### toImmutable()
239 |
240 | Returns an immutable Transaction object
241 |
242 | #### spend(txouts, solvers)
243 |
244 | * **txouts : List of Output objects**
245 |
246 | * **solvers : List of Solver objects**
247 |
248 | Returns a Transaction object where the scriptSigs of its inputs are computed.
249 |
250 | ```javascript
251 | const btcnodejs = require('./lib/index')
252 | btcnodejs.network.setup(network)
253 | var t = new btcnodejs.Transaction(...)
254 | var tospend = btcnodejs.Transaction.fromHex('...')
255 | var key = btcnodejs.Privatekey.fromWIF(wif_key)
256 | var solver = new btcnodejs.P2pkhSolver(key)
257 | var unsigned = t.toMutable()
258 | var spent = unsigned.spend([tospend.outputs[1]], [solver])
259 | ```
260 |
261 | ## `btcnodejs.Sighash`
262 |
263 | Sighash object
264 |
265 | #### Attributes
266 | * **sighash : String**
267 | * **values : 'ALL' | 'NONE' | 'SINGLE'
268 |
269 | * **anyonecanpay : Boolean**
270 |
271 | #### new Sighash(sighash, anyonecanpay = false)
272 |
273 | Returns a Sighash object
274 |
275 | ## `btcnodejs.Input`
276 |
277 | Transaction Input object
278 |
279 | #### Attributes
280 |
281 | * **txid : String**
282 |
283 | * **out : Integer**
284 |
285 | * **scriptSig : ScriptSig object**
286 |
287 | * **sequence : Sequence object**
288 |
289 | * **witness : Witness object**
290 |
291 | #### new Input(txid, out, scriptSig, sequence, witness = undefined)
292 |
293 | Returns an Input object
294 |
295 | #### toJSON()
296 |
297 | Returns the JSON representation of the Input
298 |
299 | ## `btcnodejs.Output`
300 |
301 | Transaction Output object
302 |
303 | #### Attributes
304 |
305 | * **amount : Integer**
306 |
307 | * **scriptPubKey : ScriptPubKey object**
308 |
309 | #### new Output(amount, scriptPubKey)
310 |
311 | Returns an Output object
312 |
313 | #### toJSON()
314 |
315 | Returns the JSON representation of the Output
316 |
317 | ## `btcnodejs.Witness`
318 |
319 | Input Witness Object
320 |
321 | #### Attributes
322 |
323 | * **data : List of ByteBuffers**
324 |
325 | #### new Witness(data)
326 |
327 | Returns a Witness Object where data represents the required data to sign a transaction Input
328 |
329 | #### serialize()
330 |
331 | Returns a ByteBuffer representing the Witness serialization as it appears in a bitcoin transaction
332 |
333 | #### toScriptSig()
334 |
335 | Returns a ScriptSig object
336 |
337 | #### toHex()
338 |
339 | Returns the hexadecimal representation of the Witness object
340 |
341 | #### static fromHexArray([hex_wit_sig, hex_wit_pk])
342 |
343 | Returns a Witness object from an array of hexadecimal strings representing the siganture and the public key
344 |
345 | ## `btcnodejs.Sequence`
346 |
347 | Sequence object representing the sequence number of a transaction Input
348 |
349 | #### Attributes
350 |
351 | * **n : Integer**
352 |
353 | #### new Sequence(n)
354 |
355 | Returns a Sequence object
356 |
357 | #### isTime()
358 |
359 | Returns a Boolean which tells if the Sequence is measured in time
360 |
361 | #### isBlocks()
362 |
363 | Returns a Boolean which tells if the Sequence is measured in blocks
364 |
365 | #### isActive()
366 |
367 | Returns a Boolean which tells if a Sequence restriction is active
368 |
369 | ## `btcnodejs.Locktime`
370 |
371 | Locktime object representing the locktime on a transaction
372 |
373 | #### Attributes
374 |
375 | * **n : Integer**
376 |
377 | #### new Locktime(n)
378 |
379 | Returns a Locktime object
380 |
381 | #### isTime()
382 |
383 | Returns a Boolean which tells if the Locktime is measured in time
384 |
385 | #### isBlocks()
386 |
387 | Returns a Boolean which tells if the Locktime is measured in blocks
388 |
389 | #### isActive()
390 |
391 | Returns a Boolean which tells if a Locktime restriction is active
392 |
393 | ## Scripts
394 |
395 |
396 |
397 | ## `btcnodejs.Script`
398 |
399 | Base Script object representing a general script as a ByteBuffer. Every Script class extends Script.
400 |
401 | #### Attributes
402 |
403 | * **body : ByteBuffer**
404 |
405 | #### new Script(body)
406 |
407 | Returns a Script object initialized from a bytebuffer representing the script
408 |
409 | #### serialize()
410 |
411 | Returns the body of the script_code
412 |
413 | ## `btcnodejs.ScriptSig`
414 |
415 | ScriptSig object
416 |
417 | #### toAsm()
418 |
419 | Returns a string representing the ASM of the script
420 |
421 | #### static fromAsm(asm)
422 |
423 | Returns a ScriptSig from an ASM string
424 |
425 | #### toHex()
426 |
427 | Returns the hexadecimal representation of the ScriptSig
428 |
429 | #### static fromHex(hex)
430 |
431 | Returns a ScriptSig object from an hexadecimal string representing the body of the script
432 |
433 |
434 | #### toWitness()
435 |
436 | Returns a Witness object where its data is retrieved from the ScriptSig body removing the push operations
437 |
438 | ```javascript
439 | const btcnodejs = require('btcnodejs')
440 | const ScriptSig = btcnodejs.ScriptSig
441 | btcnodejs.network.setup('testnet')
442 |
443 | const sig = ScriptSig.fromHex("483045022100b7bf286e5f6ac6fa308e8876a8a59b289094851a26cf62c20abd174917eb7762022069b5269e584e4c7" +
444 | "6f207d1b789bff7171a663d795e49751c12cf07dceb2a94c70121024a0dcb0527c2751ea4dda3aa98f6eface16e978d" +
445 | "ba8062bcbed623f158c07691")
446 |
447 | sig.toWitness().toHex()
448 | // "02483045022100b7bf286e5f6ac6fa308e8876a8a59b289094851a26cf62c20abd174917eb7762022069b5269e584e4c76f207d1b789bff7171a663d795e49751c12c f07dceb2a94c70121024a0dcb0527c2751ea4dda3aa98f6eface16e978dba8062bcbed623f158c07691"
449 | ```
450 |
451 | ## `btcnodejs.ScriptPubKey`
452 |
453 | ScriptPubkey object representing a general script pubkey. It extends Script and is extended by specific ScriptPubkey types.
454 |
455 | #### toHex()
456 |
457 | Returns the hexadecimal representation of the ScriptPubKey
458 |
459 | #### static fromHex(hex)
460 |
461 | Returns a ScriptPubKey object from an hexadecimal string representing the body of the script. If the hex is representing an identifiable script, the fromHex() will return an instance of the specific ScriptPubKey. At the moment, identifiables scripts are P2pkh, P2sh, P2wpkhV0, P2wshV0, MultiSig
462 |
463 | ```javascript
464 | const btcnodejs = require('btcnodejs')
465 | const ScriptPubKey = btcnodejs.ScriptPubKey
466 | btcnodejs.network.setup('testnet')
467 |
468 | const spk = ScriptPubKey.fromHex('76a9148b4912ec0496b5f759f3af5ab24d6f4779a52f9e88ac')
469 | spk instanceof btcnodejs.P2pkhScript //true
470 |
471 |
472 | ```
473 |
474 | #### toAddress(network = undefined, segwitV = undefined)
475 |
476 | * **network : String**
477 |
478 | * **segwitV : Integer**
479 |
480 | Returns the p2sh/p2wsh address of the Script.
481 | * If network is not specified, the initial setup network will be considered as the address network.
482 |
483 | * If segwitV is specified, the address will be of type 'p2wsh', with segwitV value as the segwit version
484 |
485 | ```javascript
486 | const btcnodejs = require('btcnodejs')
487 | const ScriptPubKey = btcnodejs.ScriptPubKey
488 | btcnodejs.network.setup('testnet')
489 |
490 | const spk = ScriptPubKey.fromHex('76a9148b4912ec0496b5f759f3af5ab24d6f4779a52f9e88ac')
491 | const p2pkh_address = spk.toAddress()
492 | p2pkh_address.hash // "029c09b86e1e4c3822bc71859af3300520d577c2"
493 | p2pkh_address.toBase58() // "2MsV2GNkfjxPjsp9ux2vwxW5HYaZh1HDtXJ
494 |
495 |
496 | ```
497 |
498 | ## `btcnodejs.P2pkhScript`
499 |
500 | P2pkhScript object
501 |
502 | #### new P2pkhScript(source)
503 |
504 | * **source : Address object | Publickey object | ByteBuffer**
505 |
506 | Returns a P2pkhScript object. This can be obtained from an Address, a Publickey or a ByteBuffer representing a pubkeyhash
507 |
508 | #### Attributes
509 |
510 | * **type : String**
511 |
512 | * **pubkeyhash : ByteBuffer**
513 |
514 | #### getAddress()
515 |
516 | Returns an Address object representing the script Address
517 |
518 | ## `btcnodejs.P2wpkhV0Script`
519 |
520 | Segwit version of P2pkhScript. It has the same interface of P2pkhScript but the source Address must be a Segwit Address object
521 |
522 | #### getScriptCode()
523 |
524 | Returns the ScriptCode of the P2wpkhV0Script
525 |
526 | ## `btcnodejs.P2shScript`
527 |
528 | P2shScript object
529 |
530 | #### Attributes
531 |
532 | * **type : String**
533 |
534 | * **scripthash : ByteBuffer**
535 |
536 | #### new P2shScript(source)
537 |
538 | * **source : Address object | ScriptPubKey object | ByteBuffer**
539 |
540 | Returns a P2pkhScript object. This can be obtained from an Address, a ScriptPubKey or a ByteBuffer representing a scripthash
541 |
542 | #### getAddress()
543 |
544 | Returns an Address object representing the script Address
545 |
546 | ## `btcnodejs.P2wshV0Script`
547 |
548 | Segwit version of P2shScript. It has the same interface of P2shScript but the source Address must be a Segwit Address object
549 |
550 | ## `btcnodejs.IfElseScript`
551 |
552 | IfElseScript object
553 |
554 |
555 | #### new IfElseScript(source)
556 |
557 | * **source : Array of ScriptPubKey objects**
558 |
559 |
560 | #### Attributes
561 |
562 | * **type : String**
563 |
564 | * **if_script : ScriptPubKey object**
565 |
566 | * **else_script : ScriptPubKey object**
567 |
568 |
569 | ```javascript
570 | const btcnodejs = require('btcnodejs')
571 | btcnodejs.network.setup('testnet')
572 |
573 | const p2pkh = new btcnodejs.P2pkhScript(btcnodejs.Publickey.fromHex("026263992eda6538202047f1514e0f6155a229c3d61b066807664e9ef73d406d95"))
574 | const multisig = new btcnodejs.MultiSigScript([
575 | 2,
576 | btcnodejs.Publickey.fromHex(
577 | "02c08786d63f78bd0a6777ffe9c978cf5899756cfc32bfad09a89e211aeb926242"
578 | ),
579 | btcnodejs.Publickey.fromHex(
580 | "033e81519ecf373ea3a5c7e1c051b71a898fb3438c9550e274d980f147eb4d069d"
581 | ),
582 | btcnodejs.Publickey.fromHex(
583 | "036d568125a969dc78b963b494fa7ed5f20ee9c2f2fc2c57f86c5df63089f2ed3a"
584 | ),
585 | 3
586 | ])
587 | const ie_script = new btcnodejs.IfElseScript([p2pkh, multisig])
588 |
589 | ```
590 |
591 | ## `btcnodejs.RelativeTimelockScript`
592 |
593 | RelativeTimelockScript object
594 |
595 | #### new RelativeTimelockScript(source)
596 |
597 | * **source : Array[ScriptPubKey object, Sequence object]**
598 |
599 | Returns a RelativeTimelockScript object
600 |
601 | #### Attributes
602 |
603 | * **sequence : Sequence object**
604 |
605 | * **locked_script : ScriptPubKey object**
606 |
607 | * **type : String**
608 |
609 | ## `btcnodejs.MultiSigScript`
610 |
611 | MultisigScript object
612 |
613 | #### new MultiSigScript(source)
614 |
615 | * **source : [m, Publickey_1, Publickey_2, ... , Publickey_n, n]**
616 |
617 | Returns a MultiSigScript object
618 |
619 | #### Attributes
620 |
621 | * **type : String**
622 |
623 | * **m : Integer**
624 |
625 | * **n : Integer**
626 |
627 | * **pubkeys : Array of Publickey objects**
628 |
629 | ```javascript
630 | const btcnodejs = require('btcnodejs')
631 | btcnodejs.network.setup('testnet')
632 |
633 | //Creating a 2-of-3 Multisig Script
634 | const multisig = new btcnodejs.MultiSigScript([
635 | 2,
636 | btcnodejs.Publickey.fromHex(
637 | "02c08786d63f78bd0a6777ffe9c978cf5899756cfc32bfad09a89e211aeb926242"
638 | ),
639 | btcnodejs.Publickey.fromHex(
640 | "033e81519ecf373ea3a5c7e1c051b71a898fb3438c9550e274d980f147eb4d069d"
641 | ),
642 | btcnodejs.Publickey.fromHex(
643 | "036d568125a969dc78b963b494fa7ed5f20ee9c2f2fc2c57f86c5df63089f2ed3a"
644 | ),
645 | 3
646 | ])
647 |
648 |
649 | ```
650 |
651 | ## Solvers
652 |
653 | Solvers are objects which are able to compute the scriptSig and Witness from a given array of digests. They are an easy way to compute transaction input's signatures.
654 |
655 | They all provide the method:
656 | #### solve(digests)
657 |
658 | * **digests : Array of digests**
659 |
660 | which returns an object :
661 |
662 | **{scriptSig : ScriptSig object , witness: Witness object}**
663 |
664 | ```javascript
665 | const btcnodejs = require('btcnodejs')
666 | btcnodejs.network.setup('testnet')
667 |
668 | const private_key = btcnodejs.Privatekey.fromHex('9b1b400e3b1211c6a56695cf1742f0a94ea38b995c1e1fb910458baa8a0874c4')
669 | const p2pkh_solver = new btcnodejs.P2pkhSolver(private_key)
670 |
671 | p2pkh_solver.solve(["0e12bda8a692aefa29651e87af9f47127ab098be1c189284e41d8e17a0516add"]).scriptSig.toHex()
672 | //
673 | '47304402202409f1f966c382f02e023ac828d7653e9268777bd1030e7101338f36a383fde302207ced2d14ff131d3b349bb00d3010a16f08831548e68750223cb8117cab553cab01210330e8ca46b7e5aa07d975ee152214431e419fac34e50becaf7e46db9a9c97d244'
674 | ```
675 |
676 | ## `btcnodejs.P2pkhSolver`
677 |
678 | Solver for P2pkh Scripts
679 |
680 | #### Attributes
681 |
682 | * **privkey : Privatekey object**
683 |
684 | * **sighash : Sighash object**
685 |
686 | #### new P2pkhSolver(privkey, sighash = new Sighash('ALL'))
687 |
688 | Returns a P2pkhSolver object
689 |
690 | ```javascript
691 | const btcnodejs = require('btcnodejs')
692 | btcnodejs.network.setup('testnet')
693 |
694 | const private_key = btcnodejs.Privatekey.fromHex('9b1b400e3b1211c6a56695cf1742f0a94ea38b995c1e1fb910458baa8a0874c4')
695 | const p2pkh_solver = new btcnodejs.P2pkhSolver(private_key)
696 |
697 | ```
698 |
699 | ## `btcnodejs.P2wpkhV0Solver`
700 |
701 | Solver for P2wpkh version 0 scripts. It extends P2pkhSolver
702 |
703 | ## `btcnodejs.P2shSolver`
704 |
705 | Solver for P2sh scripts
706 |
707 | #### Attributes
708 |
709 | * **redeemScript : ScriptPubKey object**
710 |
711 | * **redeemScriptSolver : Solver object**
712 |
713 | #### new P2shSolver(redeemScript, redeemScriptSolver)
714 |
715 |
716 | ## `btcnodejs.P2wshV0Solver`
717 |
718 | Solver for P2wsh version 0 Scripts
719 |
720 | #### Attributes
721 |
722 | * **witnessScript : ScriptPubKey object**
723 |
724 | * **witnessScriptSolver : Solver object**
725 |
726 | #### new P2wshV0Solver(witnessScript, witnessScriptSolver)
727 |
728 | ## `btcnodejs.MultiSigSolver`
729 |
730 | Solver for MultiSig Scripts
731 |
732 | #### Attributes
733 |
734 | * **privkeys : Array of Privatekey objects**
735 |
736 | * **sighashes : Array of Sighash objects**
737 |
738 | #### new MultiSigSolver(privkeys, sighashes = [new Sighash('ALL')])
739 |
740 | The number of sigashes must be equal to the number of privatekeys.
741 |
742 | ## `btcnodejs.IfElseSolver`
743 |
744 | Solver for If Else Scripts
745 |
746 | #### Attributes
747 |
748 | * **branch : Integer**
749 |
750 | * **innerSolver : Solver object**
751 |
752 | #### new IfElseSolver(branch, innerSolver)
753 |
754 | ## `btcnodejs.RelativeTimelockSolver`
755 |
756 | #### Attributes
757 |
758 | * **innerSolver : Solver object**
759 |
760 | #### new RelativeTimelockSolver(innerSolver)
761 |
762 | ## Crypto
763 |
764 | The library provides structures and methods to handle Private and Public key objects
765 |
766 | ## `btcnodejs.Privatekey`
767 |
768 | #### Attributes
769 |
770 | * **body : ByteBuffer**
771 |
772 | #### new Privatekey(bytebuffer)
773 |
774 | Returns a Privatekey object where the body is a bytebuffer representing the private key
775 |
776 | #### static fromHex(hex)
777 |
778 | * **hex : Hexadecimal String**
779 |
780 | Returns a Privatekey object from an hexadecimal string representing a private key
781 |
782 | #### toHex()
783 |
784 | Returns the Hexadecimal representation of the Privatekey
785 |
786 | #### serialize()
787 |
788 | Returns the body of the Privatekey
789 |
790 | #### getPublic(compressed = true)
791 |
792 | * **compressed : Boolean**
793 |
794 | Returns a Publickey object representing the public key associated with this Privatekey. By passing `compressed = false`, the public key will be of uncompressed type.
795 |
796 | #### sign(message)
797 |
798 | * **message : String**
799 |
800 | Computes the signature of a message, using the [elliptic](https://github.com/indutny/elliptic) nodejs library using the `secp256k1` curve.
801 |
802 | #### signDER(message)
803 |
804 | Returns the signature of the message in `DER` encoding
805 |
806 | #### toWIF(compressed = false)
807 |
808 | Returns the [Wallet import format](https://en.bitcoin.it/wiki/Wallet_import_format) string representing the private key. If `true` is passed in input, the WIF string will represent a private key associated with a compressed Publickey.
809 |
810 | #### fromWIF(wif_string)
811 |
812 | * **wif_string : String**
813 |
814 | Return a Privatekey object from its Wallet import format string.
815 |
816 | #### fromBip32(bip32_string)
817 |
818 | * **bip32_string : String**
819 |
820 | Returns a Privatekey object from its Bip32 format string.
821 |
822 | ## `btcnodejs.Publickey`
823 |
824 | Publickey object
825 |
826 | #### Attributes
827 |
828 | * **type: String**
829 |
830 | * **uncompressed : ByteBuffer**
831 |
832 | * **compressed : ByteBuffer**
833 |
834 | #### new Publickey(bytebuffer)
835 |
836 | Returns a Publickey object. Its type will be `odd`, `even`, `uncompressed` based on the the input bytebuffer data. It will keep both the uncompressed and compressed versions as its body, but its type will decide which version to use for any operation.
837 |
838 | #### hash()
839 |
840 | Returns the hash of the Publickey body.
841 |
842 | #### static fromHex(hex)
843 |
844 | Returns a Publickey object from its hexadecimal representation
845 |
846 | #### toHex(compressed = true)
847 |
848 | Returns the hexadecimal representation of its body. If `false` is passed as input it will return the hexadecimal representing its uncompressed version.
849 |
850 | #### toAddress(network = undefined, segwit = false)
851 |
852 | * **network : String**
853 |
854 | * **segwit : Boolean**
855 |
856 | Returns an Address object created from the Publickey hash.
857 |
858 | * If network is undefined, the first network name setup will be used.
859 |
860 | * If segwit is true, the Address type will be p2wpkh, otherwise it will be p2pkh
861 |
862 | #### serialize()
863 |
864 | Returns the Publickey body
865 |
866 | ## HD
867 |
868 | This library exposes functionalities to manage Hierarchical deterministic keys. It makes usage of [bitcoinjs-lib](https://github.com/bitcoinjs/bitcoinjs-lib) for some functionalities
869 |
870 | ## `btcnodejs.HDPrivateKey`
871 |
872 | Hierarchical deterministic PrivateKey object
873 |
874 | #### Attributes
875 |
876 | * **privkey : Privatekey object**
877 |
878 | * **depth : Integer**
879 |
880 | * **fingerPrint : Integer**
881 |
882 | * **parentFingerPrint : Integer**
883 |
884 | * **childIndex : Integer**
885 |
886 | * **chainCode : String**
887 |
888 | * **checksum : Integer**
889 |
890 | * **xprivkey : String**
891 |
892 | #### new HDPrivateKey(source)
893 |
894 | * **source : String | undefined**
895 |
896 | Returns an HDPrivateKey object. If no parameter is given as input, a random HDPrivateKey is returned. Otherwise, a bip32 representation of an hd key can be passed
897 |
898 | #### derive(path)
899 |
900 | * **path : String**
901 |
902 | Returns a child HDPrivateKey derived as specified in BIP32. The path must be a string starting with 'm/'. To derive an hardened child, its index in the path is followed by `'` i.e derive('m/0'')
903 |
904 | #### getPublic()
905 |
906 | Returns the corresponding HDPublicKey
907 |
908 | #### static fromSeed(seed)
909 |
910 | * **seed : String**
911 |
912 | Returns a master HDPrivateKey generated from the hexadecimal string representing a seed
913 |
914 | ## `btcnodejs.HDPublicKey`
915 |
916 | Hierarchical deterministic Public key object
917 |
918 | #### Attributes
919 |
920 | * **pubkey : Privatekey object**
921 |
922 | * **depth : Integer**
923 |
924 | * **fingerPrint : Integer**
925 |
926 | * **parentFingerPrint : Integer**
927 |
928 | * **childIndex : Integer**
929 |
930 | * **chainCode : String**
931 |
932 | * **checksum : Integer**
933 |
934 | * **xpubkey : String**
935 |
936 | #### new HDPublicKey(source)
937 |
938 | * **source : String | undefined**
939 |
940 | Returns an HDPublicKey object. If no parameter is given as input, a random HDPublicKey is returned. Otherwise, a bip32 representation of an hd key can be passed.
941 |
942 | #### derive(path)
943 |
944 | * **path : String**
945 |
946 | Returns a child HDPublicKey derived as specified in BIP32.
947 |
948 | ## Address
949 |
950 | This library exposes functionalities to manage bitcoin addresses. It wraps [bitcoinjs-lib](https://github.com/bitcoinjs/bitcoinjs-lib) for addresses encodings.
951 |
952 | ## `btcnodejs.Address`
953 |
954 | #### Attributes
955 |
956 | * **network : String**
957 |
958 | * **type : String**
959 |
960 | * **hash : ByteBuffer**
961 |
962 | #### new Address(type, hash, network = undefined)
963 |
964 | Returns an Address object. If network is not specified, the first setup network name will be used.
965 |
966 | #### static fromBase58(base58string)
967 |
968 | Returns an Address object from its base58 encoding
969 |
970 | #### toBase58()
971 |
972 | Returns a Base58 encoded string representing the bitcoin address
973 |
974 | ## `btcnodejs.SegwitAddress`
975 |
976 | SegwitAddress object extending Address. It has an extra `version` attribute specifying the segwit version.
977 |
978 | #### Attributes
979 |
980 | * **version : Integer**
981 |
982 | #### static fromBech32(bech32string)
983 |
984 | Returns a Segwit Address object from its bech32 encoding
985 |
986 | #### toBech32()
987 |
988 | Returns a Bech32 encoded string representing the bitcoin Segwit address
989 |
990 | ## Block
991 |
992 | ## `btcnodejs.BlockHeader`
993 |
994 | BlockHeader object
995 |
996 | #### Attributes
997 |
998 | * **version : Integer**
999 |
1000 | * **prev_block : String**
1001 |
1002 | * **merkle_root : String**
1003 |
1004 | * **timestamp : Integer**
1005 |
1006 | * **bits : Integer**
1007 |
1008 | * **nonce : Integer**
1009 |
1010 | #### new BlockHeader(version, prev_block, merkle_root, timestamp, bits, nonce)
1011 |
1012 | Returns a BlockHeader objects
1013 |
1014 | #### static fromHex(hex)
1015 |
1016 | Returns a BlockHeader object from an hexadecimal string representing the associated BlockHeader
1017 |
1018 | #### serialize()
1019 |
1020 | Returns a ByteBuffer representing the serialized BlockHeader
1021 |
1022 | #### blockHash()
1023 |
1024 | Returns an hexadecimal string representing the hash of the associated Block
1025 |
1026 | ## TODO
1027 |
1028 | * Expand the test vectors
1029 |
1030 | * Add docstrings to code
1031 |
1032 | * Manage Block and MerkleBlock structures
1033 |
1034 | * Add caching in segwit digests computation
1035 |
1036 | * Add further helpers for creating transactions
1037 |
1038 | * Implement the functionalities which are now wrappings around external libraries
1039 |
1040 | * Manage `OP_CODESEPARATOR` in transaction signatures
1041 |
--------------------------------------------------------------------------------
/lib/address.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2017 chainside srl
3 |
4 | This file is part of the btcnodejs package.
5 |
6 | It is subject to the license terms in the LICENSE.md file found in the top-level
7 | directory of this distribution.
8 |
9 | No part of btcnodejs, including this file, may be copied, modified,
10 | propagated, or distributed except according to the terms contained in the
11 | LICENSE.md file.
12 | */
13 | class Address {
14 | constructor(type, hash, network = undefined) {
15 | this.network = network ? network : net.net_name();
16 | this.type = type;
17 | this.hash = hash;
18 | }
19 |
20 | static fromBase58(base58addr) {
21 | const address_data = addr.fromBase58Check(base58addr);
22 | const hash = new ByteBuffer.fromHex(address_data.hash.toString("hex"));
23 | const type = Address.versions[address_data.version]["type"];
24 | const network = Address.versions[address_data.version]["network"];
25 | return new Address(type, hash, network);
26 | }
27 |
28 | toBase58() {
29 | const version = Address.types[this.network][this.type]["version"];
30 | const payload =
31 | this.hash.buffer instanceof ArrayBuffer ? abtb(this.hash.buffer) : this.hash.buffer;
32 | return addr.toBase58Check(payload, version);
33 | }
34 |
35 | toScript() {
36 | return this.type === "p2pkh" ? new scripts.P2pkhScript(this.hash) : new scripts.P2shScript(this.hash);
37 | }
38 | }
39 |
40 | class SegwitAddress extends Address {
41 | constructor(type, hash, version, network = undefined) {
42 | super(type, hash, network);
43 | this.version = version;
44 | }
45 |
46 | static fromBech32(bech32addr) {
47 | const address_data = addr.fromBech32(bech32addr);
48 | const hash = new ByteBuffer.fromHex(address_data.data.toString("hex"));
49 | let type;
50 | if (bech32addr.length === 42) type = "p2wpkh";
51 | else if (bech32addr.length === 62) type = "p2wsh";
52 | else throw new TypeError("Unknown Bech32 address string length");
53 | const version = address_data.version;
54 | const network = SegwitAddress.prefixes[address_data.prefix];
55 | return new SegwitAddress(type, hash, version, network);
56 | }
57 |
58 | toBech32() {
59 | return addr.toBech32(
60 | $.hexToBytes(this.hash.toHex(0, this.hash.capacity())),
61 | this.version,
62 | SegwitAddress.networks[this.network]["prefix"]
63 | );
64 | }
65 |
66 | toScript() {
67 | return this.type === "p2wsh" ? new scripts.P2wshV0Script(this.hash) : new scripts.P2wpkhV0Script(this.hash);
68 | }
69 | }
70 |
71 | Address.types = {
72 | mainnet: {
73 | p2pkh: {
74 | version: 0,
75 | prefix: "1"
76 | },
77 | p2sh: {
78 | version: 5,
79 | prefix: "3"
80 | }
81 | },
82 | testnet: {
83 | p2pkh: {
84 | version: 111,
85 | prefix: ["m", "n"]
86 | },
87 | p2sh: {
88 | version: 196,
89 | prefix: "2"
90 | }
91 | }
92 | };
93 | Address.versions = {
94 | 0: {
95 | network: "mainnet",
96 | type: "p2pkh",
97 | prefix: "1"
98 | },
99 | 5: {
100 | network: "mainnet",
101 | type: "p2sh",
102 | prefix: "3"
103 | },
104 | 111: {
105 | network: "testnet",
106 | type: "p2pkh",
107 | prefix: ["m", "n"]
108 | },
109 | 196: {
110 | network: "testnet",
111 | type: "p2sh",
112 | prefix: "2"
113 | }
114 | };
115 | SegwitAddress.prefixes = {
116 | bc: "mainnet",
117 | tb: "testnet"
118 | };
119 | SegwitAddress.networks = {
120 | mainnet: {
121 | prefix: "bc"
122 | },
123 | testnet: {
124 | prefix: "tb"
125 | }
126 | };
127 | module.exports = {
128 | Address,
129 | SegwitAddress
130 | };
131 | const addr = require("bitcoinjs-lib").address;
132 | const net = require("./network");
133 | const ByteBuffer = require("bytebuffer");
134 | const $ = require("../tools/conversions");
135 | const abtb = require("arraybuffer-to-buffer");
136 | const scripts = require("./scripts");
137 |
--------------------------------------------------------------------------------
/lib/bip39.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 |
4 | const ENTROPY_SIZE_ERROR = "Entropy Size must be between 16 and 32 bytes and a multiple of 4";
5 | const MAX_ENTROPY_SIZE = 32;
6 | const MIN_ENTROPY_SIZE = 16;
7 |
8 |
9 | function zeroPadding(string, n) {
10 | while (string.length < n) {
11 | string = "0" + string;
12 | }
13 | return string;
14 | }
15 |
16 | function calculateChecksumBits(entropy) {
17 | var sha_256 = shajs("sha256").update(entropy, "hex").digest("hex");
18 | var entropy_sha256 = $.hexToBinary(sha_256);
19 | return entropy_sha256.substring(0, entropy.length * 4 / 32);
20 | }
21 |
22 | /**
23 | *
24 | * @param entropySize Int, size of the initial entropy (in bytes)
25 | * @returns {Array} Array of strings representing the mnemonic sentence
26 | */
27 |
28 |
29 | function generateMnemonic(entropySize) {
30 |
31 | if (entropySize < MIN_ENTROPY_SIZE || entropySize > MAX_ENTROPY_SIZE) {
32 | throw(ENTROPY_SIZE_ERROR);
33 | }
34 | if (entropySize % 4 !== 0) {
35 | throw ENTROPY_SIZE_ERROR;
36 | }
37 | var entropy = crypto.randomBytes(entropySize).toString("hex");
38 |
39 | var entropy_bits = $.hexToBinary(entropy);
40 | var checksum_bits = calculateChecksumBits(entropy);
41 |
42 | var entropy_cs = entropy_bits + checksum_bits;
43 | var chunks = entropy_cs.match(/(.{1,11})/g);
44 | return _.map(chunks, chunk => words[parseInt(chunk, 2)]);
45 | }
46 |
47 | /**
48 | *
49 | * @param mnemonic Array of strings representing a bip39 mnemonic sentence
50 | * @param passphrase String passphrase (defaults to "")
51 | */
52 | function generateSeed(mnemonic, passphrase = "") {
53 | var salt = Buffer.from("mnemonic" + passphrase, "utf-8");
54 | var password = Buffer.from(mnemonic.join(" "), "utf-8");
55 | var seed = crypto.pbkdf2Sync(password, salt, 2048, 64, "sha512");
56 | return seed.toString("hex");
57 | }
58 |
59 | function validateMnemonic(mnemonic) {
60 | var indexes = _.map(mnemonic, word => words.indexOf(word));
61 | var chunks = _.map(indexes, index => zeroPadding(index.toString(2), 11));
62 |
63 | var entropy_cs = chunks.join("");
64 | var checksum_length = mnemonic.length / 3;
65 |
66 | var entropy_bits = entropy_cs.substring(0, entropy_cs.length - checksum_length);
67 | var checksum_bits = entropy_cs.substring(entropy_bits.length, entropy_cs.length);
68 |
69 | var entropy = Buffer.from(_.map(entropy_bits.match(/(.{1,8})/g), bin => parseInt(bin, 2))).toString("hex");
70 | var entropy_checksum = calculateChecksumBits(entropy);
71 |
72 | return entropy_checksum === checksum_bits;
73 |
74 | }
75 |
76 | const crypto = require("crypto");
77 | const shajs = require("sha.js");
78 | const $ = require("../tools/conversions");
79 | const words = require("./bip39wordlists/en_wordlist").words;
80 | const _ = require("lodash");
81 |
82 | module.exports = {
83 | generateSeed,
84 | generateMnemonic,
85 | validateMnemonic
86 | };
87 |
--------------------------------------------------------------------------------
/lib/block.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2017 chainside srl
3 |
4 | This file is part of the btcnodejs package.
5 |
6 | It is subject to the license terms in the LICENSE.md file found in the top-level
7 | directory of this distribution.
8 |
9 | No part of btcnodejs, including this file, may be copied, modified,
10 | propagated, or distributed except according to the terms contained in the
11 | LICENSE.md file.
12 | */
13 | class BlockHeader {
14 | constructor(version, prev_block, merkle_root, timestamp, bits, nonce) {
15 | this.version = version;
16 | this.prev_block = prev_block;
17 | this.merkle_root = merkle_root;
18 | this.timestamp = timestamp;
19 | this.bits = bits;
20 | this.nonce = nonce;
21 | }
22 |
23 | static fromHex(hex) {
24 | const buffer = new ByteBuffer.fromHex(hex);
25 | return BlockHeader.deserialize(buffer);
26 | }
27 |
28 | serialize() {
29 | let buffer = new ByteBuffer(80);
30 | buffer.append($.numToBytes(this.version, 4));
31 | buffer.append($.hexToBytes(this.prev_block).reverse());
32 | buffer.append($.hexToBytes(this.merkle_root).reverse());
33 | buffer.append($.numToBytes(this.timestamp, 4));
34 | buffer.append($.numToBytes(this.bits, 4));
35 | buffer.append($.numToBytes(this.nonce, 4));
36 | return buffer;
37 | }
38 |
39 | static deserialize(bytebuffer) {
40 | const buffer = bytebuffer.LE();
41 | let offset = 0;
42 | const version = buffer.readInt32(offset);
43 | offset += 4;
44 | const prev_block = $.swapHex(buffer.toHex(offset, offset + 32));
45 | offset += 32;
46 | const merkle_root = $.swapHex(buffer.toHex(offset, offset + 32));
47 | offset += 32;
48 | const timestamp = buffer.readInt32(offset);
49 | offset += 4;
50 | const bits = buffer.readInt32(offset);
51 | offset += 4;
52 | const nonce = buffer.readInt32(offset);
53 | offset += 4;
54 |
55 | return new BlockHeader(version, prev_block, merkle_root, timestamp, bits, nonce);
56 | }
57 |
58 | blockHash() {
59 | const serialized = this.serialize();
60 | const hex = serialized.toHex(0, serialized.capacity());
61 | return $.swapHex(
62 | shajs("sha256")
63 | .update(
64 | shajs("sha256")
65 | .update(hex, "hex")
66 | .digest("hex"),
67 | "hex"
68 | )
69 | .digest("hex")
70 | );
71 | }
72 | }
73 |
74 | module.exports = {
75 | BlockHeader
76 | };
77 | const $ = require("../tools/conversions");
78 | const _ = require("lodash");
79 | const ByteBuffer = require("bytebuffer");
80 | const shajs = require("sha.js");
81 |
--------------------------------------------------------------------------------
/lib/crypto.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2017 chainside srl
3 |
4 | This file is part of the btcnodejs package.
5 |
6 | It is subject to the license terms in the LICENSE.md file found in the top-level
7 | directory of this distribution.
8 |
9 | No part of btcnodejs, including this file, may be copied, modified,
10 | propagated, or distributed except according to the terms contained in the
11 | LICENSE.md file.
12 | */
13 | "use strict";
14 |
15 | class Privatekey {
16 | constructor(bytebuffer) {
17 | this.body = bytebuffer;
18 | }
19 |
20 | static fromHex(hex) {
21 | const buffer = new ByteBuffer.fromHex(hex);
22 | return new Privatekey(buffer);
23 | }
24 |
25 | toHex() {
26 | return this.body.toHex(0, this.body.capacity());
27 | }
28 |
29 | serialize() {
30 | return this.body;
31 | }
32 |
33 | getPublic(compressed = true) {
34 | const ec = new EC("secp256k1");
35 | const key = ec.keyFromPrivate(this.body.toHex(0, this.body.capacity()), "hex");
36 | const pub = key.getPublic();
37 | const uncompressed_hex = pub.x.toString("hex", 64) + pub.y.toString("hex", 64);
38 | const buffer = new ByteBuffer.fromHex(uncompressed_hex).prepend("04", "hex", 0);
39 | const uncompressed_pub = new Publickey(buffer);
40 | if (compressed) return new Publickey(uncompressed_pub.compressed);
41 | else return uncompressed_pub;
42 | }
43 |
44 | sign(message) {
45 | let ec = new EC("secp256k1");
46 | let key = ec.keyFromPrivate(this.body.toHex(0, this.body.capacity()), "hex");
47 | let sig = key.sign(message);
48 |
49 | let highest_order = new BN(
50 | "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0",
51 | 16
52 | );
53 | if (sig.s.gt(highest_order)) {
54 | let order = new BN(
55 | "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141",
56 | 16
57 | );
58 | sig.s = order.sub(sig.s);
59 | }
60 | return sig;
61 | }
62 |
63 | signDER(message) {
64 | return $.bytesToHex(this.sign(message).toDER());
65 | }
66 |
67 | toWIF(compressed = false) {
68 | let key = this.body.clone();
69 | if (compressed)
70 | key = key.append("01", "hex", this.body.capacity()).copy(0, this.body.capacity() + 1);
71 | let v_key =
72 | net.net_name() === "mainnet" ? key.prepend("80", "hex", 0) : key.prepend("ef", "hex", 0);
73 | let checksum = shajs("sha256")
74 | .update(
75 | shajs("sha256")
76 | .update(v_key.toHex(0, v_key.limit), "hex")
77 | .digest("hex"),
78 | "hex"
79 | )
80 | .digest("hex")
81 | .substring(0, 8);
82 | let extended_key = v_key.append(checksum, "hex", v_key.limit).copy(0, v_key.limit + 4);
83 | let payload =
84 | extended_key.buffer instanceof ArrayBuffer
85 | ? abtb(extended_key.buffer)
86 | : extended_key.buffer;
87 | let wif = bs58.encode(payload);
88 | return wif;
89 | }
90 |
91 | static fromWIF(wif_string) {
92 | if (!(wif_string.length >= 51 && wif_string.length <= 52))
93 | throw new Error("Invalid WIF string");
94 | const decoded = bs58check.decode(wif_string);
95 | const wif_key = new ByteBuffer(decoded.length).append(decoded);
96 | return new Privatekey(wif_key.copy(1, 33));
97 | }
98 |
99 | static fromBip32(bip_string) {
100 | if (bip_string.substring(0, 4) !== "tprv" && bip_string.substring(0, 4) !== "xprv")
101 | throw new TypeError("Key prefix is invalid");
102 | const decoded = new ByteBuffer.fromHex(bs58check.decode(bip_string).toString("hex"));
103 | if (decoded.readByte(decoded.capacity() - 33) !== 0)
104 | throw new TypeError("Byte -33 expected to be 0");
105 |
106 | return new Privatekey(decoded.copy(decoded.capacity() - 32, decoded.capacity()));
107 | }
108 | }
109 |
110 | class Publickey {
111 | constructor(bytebuffer) {
112 | this.type = Publickey.types[bytebuffer.toHex(0, 1)];
113 | if (this.type === "uncompressed") {
114 | this.uncompressed = bytebuffer;
115 | const header =
116 | parseInt(
117 | this.uncompressed.toHex(
118 | this.uncompressed.capacity() - 1,
119 | this.uncompressed.capacity()
120 | ),
121 | 16
122 | ) % 2
123 | ? "03"
124 | : "02";
125 | this.compressed = new ByteBuffer.fromHex(header + this.uncompressed.toHex(1, 33));
126 | } else {
127 | this.compressed = bytebuffer;
128 | this.uncompressed = Publickey.uncompress(bytebuffer);
129 | }
130 | }
131 |
132 | verify(msg, signature) {
133 | const ec = new EC("secp256k1");
134 | return ec.verify(msg, signature, this.toHex(), "hex");
135 | }
136 |
137 | hash() {
138 | const to_hash = this.type === "uncompressed" ? this.uncompressed : this.compressed;
139 | const sha256 = shajs("sha256")
140 | .update(to_hash.toHex(), "hex")
141 | .digest("hex");
142 | const ripe = new ripemd160().update(sha256, "hex").digest("hex");
143 | return new ByteBuffer.fromHex(ripe);
144 | }
145 |
146 | static fromBip32(bip_string) {
147 | if (bip_string.substring(0, 4) !== "tpub" && bip_string.substring(0, 4) !== "xpub")
148 | throw new TypeError("Key prefix is invalid");
149 | const decoded = new ByteBuffer.fromHex(bs58check.decode(bip_string).toString("hex"));
150 | return new Publickey(decoded.copy(decoded.capacity() - 33, decoded.capacity()));
151 | }
152 |
153 | static fromHex(hex) {
154 | let buffer = new ByteBuffer.fromHex(hex);
155 | return new Publickey(buffer);
156 | }
157 |
158 | toHex(compressed = true) {
159 | if (compressed) return this.compressed.toHex(0, this.compressed.capacity());
160 | else return this.uncompressed.toHex(0, this.compressed.capacity());
161 | }
162 |
163 | static uncompress(pubkey) {
164 | const p = new BN("fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", 16);
165 | const header = pubkey.readByte(0);
166 | const body = pubkey.copy(1, pubkey.capacity());
167 | const parity = header - 2;
168 | const alpha = $.bnmodexp(new BN(body.toHex(), 16), 3, p)
169 | .add(new BN(7))
170 | .mod(p);
171 | let y = $.bnmodexp(alpha, p.add(new BN(1)).div(new BN(4)), p);
172 | if (!y.mod(new BN(2)).eq(new BN(parity))) y = y.neg().umod(p);
173 | let buf = y.toArrayLike(Buffer, "big", 32);
174 | const uncompressed = new ByteBuffer(body.capacity() + buf.length + 1);
175 | return uncompressed
176 | .append("04", "hex")
177 | .append(body)
178 | .append(buf);
179 | }
180 |
181 | toAddress(network = undefined, segwit = false) {
182 | if (network === undefined) network = net.net_name();
183 | const type = segwit ? "p2wpkh" : "p2pkh";
184 | return new Address(type, this.hash(), network);
185 | }
186 |
187 | serialize() {
188 | return this.type === "uncompressed" ? this.uncompressed : this.compressed;
189 | }
190 | }
191 |
192 | Privatekey.bip32 = {
193 | testnet: "tprv",
194 | mainnet: "xprv"
195 | };
196 | Publickey.types = {
197 | "02": "even",
198 | "03": "odd",
199 | "04": "uncompressed"
200 | };
201 | Publickey.wif = {
202 | K: "compressed",
203 | L: "compressed",
204 | "5": "uncompressed",
205 |
206 | c: "compressed",
207 | "9": "uncompressed"
208 | };
209 | module.exports = {
210 | Privatekey,
211 | Publickey
212 | };
213 | const ByteBuffer = require("bytebuffer");
214 | const EC = require("elliptic").ec;
215 | const $ = require("../tools/conversions");
216 | const net = require("./network");
217 | const shajs = require("sha.js");
218 | const bs58 = require("bs58");
219 | const bs58check = require("bs58check");
220 | const BN = require("bn.js");
221 | const ripemd160 = require("ripemd160");
222 | const Address = require("./address").Address;
223 | const abtb = require("arraybuffer-to-buffer");
224 |
--------------------------------------------------------------------------------
/lib/hd.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2017-2018 chainside srl
3 |
4 | This file is part of the btcnodejs package.
5 |
6 | It is subject to the license terms in the LICENSE.md file found in the top-level
7 | directory of this distribution.
8 |
9 | No part of btcnodejs, including this file, may be copied, modified,
10 | propagated, or distributed except according to the terms contained in the
11 | LICENSE.md file.
12 | */
13 | class HDPrivateKey {
14 | constructor(obj) {
15 | if (!obj) {
16 | throw new TypeError("contructor argument must be either an HDNode or a base58 string");
17 | } else if (typeof obj === "string") {
18 | let nw = net.is_mainnet() ? bitcoinjs.networks.bitcoin : bitcoinjs.networks.testnet;
19 |
20 | this._bckey = bitcoinjs.HDNode.fromBase58(obj, nw);
21 | } else {
22 | this._bckey = obj;
23 | }
24 | this._setParams();
25 | this.network = net.net_name();
26 | this.privkey = crypto.Privatekey.fromBip32(this._bckey.toBase58());
27 | }
28 |
29 | derive(path) {
30 | if (path.substring(0, 1) === "m" && this._bckey.depth !== 0)
31 | throw new TypeError("trying to derive a master path from a non master key");
32 | return new HDPrivateKey(this._bckey.derivePath(path));
33 | }
34 |
35 | getPublic() {
36 | return new HDPublicKey(this._bckey.neutered());
37 | }
38 |
39 | toString() {
40 | return this._bckey.toBase58();
41 | }
42 |
43 | static fromSeed(seed) {
44 | const nw = net.is_mainnet() ? bitcoinjs.networks.bitcoin : bitcoinjs.networks.testnet;
45 | const key = bitcoinjs.HDNode.fromSeedHex(seed, nw);
46 | return new HDPrivateKey(key, nw);
47 | }
48 |
49 | _setParams() {
50 |
51 | this.depth = this._bckey.depth;
52 | this.chainCode = this._bckey.chainCode;
53 | this.parentFingerPrint = ByteBuffer.fromHex($.numToHex(this._bckey.parentFingerprint, 16));
54 | this.fingerPrint = ByteBuffer.fromBinary(this._bckey.getFingerprint());
55 | this.childIndex = this._bckey.index;
56 | }
57 | }
58 |
59 | class HDPublicKey {
60 | constructor(obj) {
61 | if (!obj) {
62 | throw new TypeError("contructor argument must be either an HDNode or a base58 string");
63 |
64 | } else if (typeof obj === "string") {
65 | let nw = net.is_mainnet() ? bitcoinjs.networks.bitcoin : bitcoinjs.networks.testnet;
66 | this._bckey = bitcoinjs.HDNode.fromBase58(obj, nw).neutered();
67 | } else {
68 | this._bckey = obj;
69 | }
70 | this._setParams();
71 | this.network = net.net_name();
72 | this.pubkey = crypto.Publickey.fromBip32(this._bckey.toBase58());
73 | }
74 |
75 | derive(path) {
76 | return new HDPublicKey(this._bckey.derivePath(path));
77 | }
78 |
79 | toString() {
80 | return this._bckey.toBase58();
81 | }
82 |
83 | _setParams() {
84 | this.depth = this._bckey.depth;
85 | this.chainCode = this._bckey.chainCode;
86 | this.parentFingerPrint = ByteBuffer.fromHex($.numToHex(this._bckey.parentFingerprint, 16));
87 | this.fingerPrint = ByteBuffer.fromBinary(this._bckey.getFingerprint());
88 | this.childIndex = this._bckey.index;
89 | }
90 | }
91 |
92 | module.exports = {
93 | HDPublicKey,
94 | HDPrivateKey
95 | };
96 | const bitcoinjs = require("bitcoinjs-lib");
97 | const ByteBuffer = require("bytebuffer");
98 | const _ = require("lodash");
99 | const net = require("./network");
100 | const crypto = require("./crypto");
101 | const $ = require("../tools/conversions");
102 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2017 chainside srl
3 |
4 | This file is part of the btcnodejs package.
5 |
6 | It is subject to the license terms in the LICENSE.md file found in the top-level
7 | directory of this distribution.
8 |
9 | No part of btcnodejs, including this file, may be copied, modified,
10 | propagated, or distributed except according to the terms contained in the
11 | LICENSE.md file.
12 | */
13 | const transaction = require("./transaction");
14 | const crypto = require("./crypto");
15 | const address = require("./address");
16 | const hd = require("./hd");
17 | const scripts = require("./scripts");
18 | const solvers = require("./solvers");
19 | const network = require("./network");
20 | const block = require("./block");
21 |
22 |
23 | module.exports = {
24 | Transaction: transaction.Transaction,
25 | MutableTransaction: transaction.MutableTransaction,
26 | Sighash: transaction.Sighash,
27 | Input: transaction.Input,
28 | Output: transaction.Output,
29 | Witness: transaction.Witness,
30 | Sequence: transaction.Sequence,
31 | Locktime: transaction.Locktime,
32 | Script: scripts.Script,
33 | ScriptSig: scripts.ScriptSig,
34 | ScriptPubKey: scripts.ScriptPubKey,
35 | StackData: scripts.StackData,
36 | P2pkhScript: scripts.P2pkhScript,
37 | P2pkScript: scripts.P2pkScript,
38 | P2wpkhV0Script: scripts.P2wpkhV0Script,
39 | P2shScript: scripts.P2shScript,
40 | P2wshV0Script: scripts.P2wshV0Script,
41 | IfElseScript: scripts.IfElseScript,
42 | RelativeTimelockScript: scripts.RelativeTimelockScript,
43 | MultiSigScript: scripts.MultiSigScript,
44 | P2pkhSolver: solvers.P2pkhSolver,
45 | P2wpkhV0Solver: solvers.P2wpkhV0Solver,
46 | P2shSolver: solvers.P2shSolver,
47 | P2wshV0Solver: solvers.P2wshV0Solver,
48 | IfElseSolver: solvers.IfElseSolver,
49 | MultiSigSolver: solvers.MultiSigSolver,
50 | RelativeTimelockSolver: solvers.RelativeTimelockSolver,
51 | Privatekey: crypto.Privatekey,
52 | Publickey: crypto.Publickey,
53 | HDPrivateKey: hd.HDPrivateKey,
54 | HDPublicKey: hd.HDPublicKey,
55 | Address: address.Address,
56 | SegwitAddress: address.SegwitAddress,
57 | BlockHeader: block.BlockHeader,
58 | network: network
59 | };
60 |
--------------------------------------------------------------------------------
/lib/network.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2017 chainside srl
3 |
4 | This file is part of the btcnodejs package.
5 |
6 | It is subject to the license terms in the LICENSE.md file found in the top-level
7 | directory of this distribution.
8 |
9 | No part of btcnodejs, including this file, may be copied, modified,
10 | propagated, or distributed except according to the terms contained in the
11 | LICENSE.md file.
12 | */
13 | var MAINNET = undefined;
14 | var NETNAME = undefined;
15 |
16 | function setup(network = "testnet", testing = false) {
17 | if (MAINNET !== undefined && !testing) throw new Error("Network setup already executed");
18 | if (network !== "mainnet" && network !== "testnet")
19 | throw "Invalid network type: valid types are 'mainnet' or 'testnet'";
20 | MAINNET = network == "mainnet";
21 | NETNAME = network;
22 | }
23 |
24 | function net_name() {
25 | if (!NETNAME) throw new Error("Netwok setup not executed");
26 | return NETNAME;
27 | }
28 |
29 | function is_mainnet() {
30 | if (MAINNET === undefined) throw new Error("Netwok setup not executed");
31 | return MAINNET;
32 | }
33 |
34 | module.exports = {
35 | setup,
36 | net_name,
37 | is_mainnet
38 | };
39 |
--------------------------------------------------------------------------------
/lib/opcodes.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2017 chainside srl
3 |
4 | This file is part of the btcnodejs package.
5 |
6 | It is subject to the license terms in the LICENSE.md file found in the top-level
7 | directory of this distribution.
8 |
9 | No part of btcnodejs, including this file, may be copied, modified,
10 | propagated, or distributed except according to the terms contained in the
11 | LICENSE.md file.
12 | */
13 | "use strict";
14 |
15 | class OpCodeConverter {
16 |
17 | static exists(op) {
18 | return Object.keys(this._opcode_to_int_dict).includes(op);
19 | }
20 |
21 | static toInt(op) {
22 | if (this.exists(op)) {
23 | return this._opcode_to_int_dict[op];
24 | } else {
25 | throw new Error(`Unknown op: ${op}`);
26 | }
27 | }
28 |
29 | static fromInt(num) {
30 | if ((0 > num) || (255 < num)) {
31 | throw new Error(`Integer ${num} cannot be converted to opcode`);
32 | } else if (Object.keys(this._int_to_opcode_dict).includes(num)) {
33 | return this._int_to_opcode_dict[num];
34 | } else {
35 | return 'OP_UNKNOWN';
36 | }
37 | }
38 |
39 | static toHex(op) {
40 | return this.toInt(op).toString(16);
41 | }
42 |
43 | static fromHex(hexnum) {
44 | return this.fromInt(parseInt(hexnum, 16));
45 | }
46 |
47 | }
48 |
49 | OpCodeConverter.opcodes = [['OP_0', 0],
50 | ['OP_PUSHDATA1', 76],
51 | ['OP_PUSHDATA2', 77],
52 | ['OP_PUSHDATA4', 78],
53 | ['OP_1NEGATE', 79],
54 | ['OP_RESERVED', 80],
55 | ['OP_1', 81],
56 | ['OP_2', 82],
57 | ['OP_3', 83],
58 | ['OP_4', 84],
59 | ['OP_5', 85],
60 | ['OP_6', 86],
61 | ['OP_7', 87],
62 | ['OP_8', 88],
63 | ['OP_9', 89],
64 | ['OP_10', 90],
65 | ['OP_11', 91],
66 | ['OP_12', 92],
67 | ['OP_13', 93],
68 | ['OP_14', 94],
69 | ['OP_15', 95],
70 | ['OP_16', 96],
71 | ['OP_NOP', 97],
72 | ['OP_VER', 98],
73 | ['OP_IF', 99],
74 | ['OP_NOTIF', 100],
75 | ['OP_VERIF', 101],
76 | ['OP_VERNOTIF', 102],
77 | ['OP_ELSE', 103],
78 | ['OP_ENDIF', 104],
79 | ['OP_VERIFY', 105],
80 | ['OP_RETURN', 106],
81 | ['OP_TOALTSTACK', 107],
82 | ['OP_FROMALTSTACK', 108],
83 | ['OP_2DROP', 109],
84 | ['OP_2DUP', 110],
85 | ['OP_3DUP', 111],
86 | ['OP_2OVER', 112],
87 | ['OP_2ROT', 113],
88 | ['OP_2SWAP', 114],
89 | ['OP_IFDUP', 115],
90 | ['OP_DEPTH', 116],
91 | ['OP_DROP', 117],
92 | ['OP_DUP', 118],
93 | ['OP_NIP', 119],
94 | ['OP_OVER', 120],
95 | ['OP_PICK', 121],
96 | ['OP_ROLL', 122],
97 | ['OP_ROT', 123],
98 | ['OP_SWAP', 124],
99 | ['OP_TUCK', 125],
100 | ['OP_CAT', 126],
101 | ['OP_SUBSTR', 127],
102 | ['OP_LEFT', 128],
103 | ['OP_RIGHT', 129],
104 | ['OP_SIZE', 130],
105 | ['OP_INVERT', 131],
106 | ['OP_AND', 132],
107 | ['OP_OR', 133],
108 | ['OP_XOR', 134],
109 | ['OP_EQUAL', 135],
110 | ['OP_EQUALVERIFY', 136],
111 | ['OP_RESERVED1', 137],
112 | ['OP_RESERVED2', 138],
113 | ['OP_1ADD', 139],
114 | ['OP_1SUB', 140],
115 | ['OP_2MUL', 141],
116 | ['OP_2DIV', 142],
117 | ['OP_NEGATE', 143],
118 | ['OP_ABS', 144],
119 | ['OP_NOT', 145],
120 | ['OP_0NOTEQUAL', 146],
121 | ['OP_ADD', 147],
122 | ['OP_SUB', 148],
123 | ['OP_MUL', 149],
124 | ['OP_DIV', 150],
125 | ['OP_MOD', 151],
126 | ['OP_LSHIFT', 152],
127 | ['OP_RSHIFT', 153],
128 | ['OP_BOOLAND', 154],
129 | ['OP_BOOLOR', 155],
130 | ['OP_NUMEQUAL', 156],
131 | ['OP_NUMEQUALVERIFY', 157],
132 | ['OP_NUMNOTEQUAL', 158],
133 | ['OP_LESSTHAN', 159],
134 | ['OP_GREATERTHAN', 160],
135 | ['OP_LESSTHANOREQUAL', 161],
136 | ['OP_GREATERTHANOREQUAL', 162],
137 | ['OP_MIN', 163],
138 | ['OP_MAX', 164],
139 | ['OP_WITHIN', 165],
140 | ['OP_RIPEMD160', 166],
141 | ['OP_SHA1', 167],
142 | ['OP_SHA256', 168],
143 | ['OP_HASH160', 169],
144 | ['OP_HASH256', 170],
145 | ['OP_CODESEPARATOR', 171],
146 | ['OP_CHECKSIG', 172],
147 | ['OP_CHECKSIGVERIFY', 173],
148 | ['OP_CHECKMULTISIG', 174],
149 | ['OP_CHECKMULTISIGVERIFY', 175],
150 | ['OP_NOP1', 176],
151 | ['OP_NOP2', 177],
152 | ['OP_CHECKLOCKTIMEVERIFY', 177],
153 | ['OP_NOP3', 178],
154 | ['OP_CHECKSEQUENCEVERIFY', 178],
155 | ['OP_NOP4', 179],
156 | ['OP_NOP5', 180],
157 | ['OP_NOP6', 181],
158 | ['OP_NOP7', 182],
159 | ['OP_NOP8', 183],
160 | ['OP_NOP9', 184],
161 | ['OP_NOP10', 185],
162 | ['OP_NULLDATA', 252],
163 | ['OP_PUBKEYHASH', 253],
164 | ['OP_PUBKEY', 254],
165 | ['OP_INVALIDOPCODE', 255], ];
166 |
167 | OpCodeConverter._opcode_to_int_dict = OpCodeConverter.opcodes.reduce((dict, opcode) => {
168 | dict[opcode[0]] = opcode[1];
169 | return dict;
170 | },
171 | {});
172 |
173 | OpCodeConverter._int_to_opcode_dict = OpCodeConverter.opcodes.reduce((dict, opcode) => {
174 | dict[opcode[1]] = opcode[0];
175 | return dict;
176 | },
177 | {});
178 |
179 | module.exports = {
180 | OpCodeConverter,
181 | };
182 |
--------------------------------------------------------------------------------
/lib/scripts.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2017 chainside srl
3 |
4 | This file is part of the btcnodejs package.
5 |
6 | It is subject to the license terms in the LICENSE.md file found in the top-level
7 | directory of this distribution.
8 |
9 | No part of btcnodejs, including this file, may be copied, modified,
10 | propagated, or distributed except according to the terms contained in the
11 | LICENSE.md file.
12 | */
13 | "use strict";
14 |
15 | class Script {
16 | constructor(bytebuffer) {
17 | this.body = bytebuffer;
18 | }
19 |
20 | serialize() {
21 | return this.body;
22 | }
23 |
24 | static compileToBuffer(str) {
25 | const arr = str.split(" ").reduce((arr, opcode) => {
26 | let res;
27 | try {
28 | res = opcodes.OpCodeConverter.toInt(opcode);
29 | } catch (err) {
30 | res = StackData.getPushOp($.hexToBytes(opcode));
31 | }
32 | return arr.concat(res);
33 | }, []);
34 | return new ByteBuffer(arr.length).append(arr).reset();
35 | }
36 |
37 | static compileToHex(str) {
38 | return this.compileToBuffer(str).toString('hex');
39 | }
40 | }
41 |
42 | class ScriptSig extends Script {
43 | constructor(bytebuffer) {
44 | super(bytebuffer);
45 | }
46 |
47 | static empty() {
48 | return new ScriptSig(new ByteBuffer(0));
49 | }
50 |
51 | static fromAsm(asm) {
52 | const script = bitcoinjs.script;
53 | const payload = script.fromASM(asm);
54 | const buffer = new ByteBuffer(payload.length).append(payload);
55 | return new ScriptSig(buffer);
56 | }
57 |
58 | toAsm() {
59 | let script = bitcoinjs.script;
60 | let payload =
61 | this.body.buffer instanceof ArrayBuffer ? abtb(this.body.buffer) : this.body.buffer;
62 | return script.toASM(payload);
63 | }
64 |
65 | static fromHex(hex) {
66 | let buffer = new ByteBuffer.fromHex(hex);
67 | return new ScriptSig(buffer);
68 | }
69 |
70 | toHex() {
71 | return this.body.toHex(0, this.body.capacity());
72 | }
73 |
74 | toWitness() {
75 | let offset = 0;
76 | let witnessData = [];
77 | while (offset < this.body.capacity()) {
78 | const op = this.body.readByte(offset);
79 | if (op === 0) {
80 | witnessData.push(new ByteBuffer(0));
81 | offset += 1;
82 | }
83 | if (1 <= op && op <= 75) {
84 | witnessData.push(new ByteBuffer.fromHex(this.body.toHex(offset + 1, offset + 1 + op)));
85 | offset += 1 + op;
86 | }
87 | //the next byte gives the size
88 | if (op === 76) {
89 | const next_op = this.body.readByte(offset + 1);
90 | witnessData.push(
91 | new ByteBuffer.fromHex(this.body.toHex(offset + 2, offset + 2 + next_op))
92 | );
93 | offset += 2 + next_op;
94 | }
95 | //the next two bytes give the size
96 | if (op === 77) {
97 | const next_op = parseInt(this.body.toHex(offset + 1, offset + 2), 16);
98 | witnessData.push(
99 | new ByteBuffer.fromHex(this.body.toHex(offset + 3, offset + 3 + next_op))
100 | );
101 | offset += 3 + next_op;
102 | }
103 | //the next four bytes give the size
104 | if (op === 78) {
105 | const next_op = parseInt(this.body.toHex(offset + 1, offset + 4), 16);
106 | witnessData.push(
107 | new ByteBuffer.fromHex(this.body.toHex(offset + 5, offset + 5 + next_op))
108 | );
109 | offset += 5 + next_op;
110 | }
111 | if (op === 79) {
112 | witnessData.push(new ByteBuffer(1).append([-1]));
113 | offset += 2;
114 | }
115 | if (op === 81) {
116 | witnessData.push(new ByteBuffer(1).append([1]));
117 | offset += 2;
118 | }
119 | if (82 <= op && op <= 96) {
120 | witnessData.push(new ByteBuffer(1).append([op - 80]));
121 | offset += 2;
122 | }
123 | }
124 | return new transaction.Witness(witnessData);
125 | }
126 | }
127 |
128 | class ScriptPubKey extends Script {
129 | constructor(bytebuffer) {
130 | super(bytebuffer);
131 | }
132 |
133 | toHex() {
134 | return this.body.toHex(0, this.body.capacity());
135 | }
136 |
137 | static empty() {
138 | return new ScriptPubKey(new ByteBuffer(0));
139 | }
140 |
141 | static fromHex(hex) {
142 | let buffer = new ByteBuffer.fromHex(hex);
143 | let script = ScriptPubKey.identifyFromBuffer(buffer);
144 | if (!script) return new ScriptPubKey(buffer);
145 | return script;
146 | }
147 |
148 | static identifyFromBuffer(buffer) {
149 | let script = false;
150 | _.forEach(Script.identifiables, type => {
151 | let data = type.identify(buffer);
152 | if (data) script = new type(data.data);
153 | });
154 | return script;
155 | }
156 |
157 | static identifyFromHex(hex) {
158 | let buffer = ByteBuffer.fromHex(hex);
159 | return ScriptPubKey.identifyFromBuffer(buffer);
160 | }
161 |
162 | toAddress(network = undefined, segwitV = undefined) {
163 | if (network === undefined) network = net.net_name();
164 | const type = segwitV !== undefined ? "p2wsh" : "p2sh";
165 | const hash = segwitV !== undefined ? this.p2wshHash() : this.p2shHash();
166 | return segwitV !== undefined
167 | ? new address.SegwitAddress(type, hash, segwitV, network)
168 | : new address.Address(type, hash, network);
169 |
170 | }
171 |
172 | p2shHash() {
173 | const sha256 = shajs("sha256")
174 | .update(this.body.toHex(0, this.body.capacity()), "hex")
175 | .digest("hex");
176 | const ripe = new ripemd160().update(sha256, "hex").digest("hex");
177 | return new ByteBuffer.fromHex(ripe);
178 | }
179 |
180 | p2wshHash() {
181 | const sha256 = shajs("sha256")
182 | .update(this.body.toHex(0, this.body.capacity()), "hex")
183 | .digest("hex");
184 | return new ByteBuffer.fromHex(sha256);
185 | }
186 |
187 | static requireOp(op, byte) {
188 | if (typeof op === "number") {
189 | if (byte !== op) {
190 | return false;
191 | }
192 | } else if (Script.op_codes[op] !== byte) {
193 | return false;
194 | }
195 | return true;
196 | }
197 | }
198 |
199 | class P2pkScript extends ScriptPubKey {
200 | constructor(source) {
201 | let type = "p2pk";
202 | let pubkey;
203 | let compressed;
204 | if (source instanceof crypto.Publickey) {
205 | compressed = source.type !== "uncompressed";
206 | pubkey = compressed ? source.compressed : source.uncompressed;
207 | }
208 |
209 | if (source instanceof ByteBuffer) {
210 | compressed = source.capacity() === 33;
211 | pubkey = source;
212 | }
213 | const keydim = compressed ? 33 : 65;
214 | const buffer = new ByteBuffer(keydim + 2);
215 | buffer
216 | .append($.numToBytes(keydim, 1))
217 | .append(pubkey.reset())
218 | .append($.numToBytes(Script.op_codes["OP_CHECKSIG"], 1));
219 | super(buffer);
220 | this.pubkey = pubkey;
221 | this.type = type;
222 | }
223 |
224 | static identify(script) {
225 | const buf = script.copy(0, script.capacity());
226 | const compressed = ScriptPubKey.requireOp(33, buf.readUint8(0));
227 | const uncompressed = ScriptPubKey.requireOp(65, buf.readUint8(0));
228 | if (
229 | (compressed || uncompressed) &&
230 | ScriptPubKey.requireOp("OP_CHECKSIG", buf.readUint8(compressed ? 33 : 65)) &&
231 | script.capacity() === (compressed ? 34 : 66)
232 | )
233 | return {data: buf.copy(1, (compressed ? 33 : 65))};
234 |
235 |
236 | return false;
237 | }
238 | }
239 |
240 | class P2pkhScript extends ScriptPubKey {
241 | constructor(source) {
242 | let type = "p2pkh";
243 | let pubkeyhash;
244 | if (source instanceof crypto.Publickey) pubkeyhash = source.hash();
245 | if (source instanceof address.Address) {
246 | if (source.type !== type)
247 | throw new TypeError("Invalid address type provided for p2pkh: " + source.type);
248 | pubkeyhash = source.hash;
249 | }
250 | if (source instanceof ByteBuffer) pubkeyhash = source;
251 |
252 | const buffer = new ByteBuffer(25);
253 | buffer
254 | .append($.numToBytes(Script.op_codes["OP_DUP"], 1))
255 | .append($.numToBytes(Script.op_codes["OP_HASH160"], 1))
256 | .append($.numToBytes(20, 1))
257 | .append(pubkeyhash.reset())
258 | .append($.numToBytes(Script.op_codes["OP_EQUALVERIFY"], 1))
259 | .append($.numToBytes(Script.op_codes["OP_CHECKSIG"], 1));
260 | super(buffer);
261 | this.type = type;
262 | this.pubkeyhash = pubkeyhash;
263 | }
264 |
265 | getAddress() {
266 | return new address.Address(this.type, this.pubkeyhash);
267 | }
268 |
269 | static identify(script) {
270 | const buf = script.copy(0, script.capacity());
271 | if (
272 | ScriptPubKey.requireOp("OP_DUP", buf.readUint8(0)) &&
273 | ScriptPubKey.requireOp("OP_HASH160", buf.readUint8(1)) &&
274 | ScriptPubKey.requireOp(20, buf.readUint8(2)) &&
275 | ScriptPubKey.requireOp("OP_EQUALVERIFY", buf.readUint8(23)) &&
276 | ScriptPubKey.requireOp("OP_CHECKSIG", buf.readUint8(24)) &&
277 | script.capacity() === 25
278 | )
279 | return {data: buf.copy(3, 23)};
280 |
281 | return false;
282 | }
283 | }
284 |
285 | class P2wpkhV0Script extends ScriptPubKey {
286 | constructor(source) {
287 | let type = "p2wpkh";
288 | let pubkeyhash;
289 | if (source instanceof crypto.Publickey) pubkeyhash = source.hash();
290 | if (source instanceof ByteBuffer) pubkeyhash = source;
291 | if (source instanceof address.SegwitAddress) {
292 | if (source.type !== type)
293 | throw new TypeError("Invalid address type provided for p2wpkh: " + source.type);
294 | pubkeyhash = source.hash;
295 | }
296 | const buffer = new ByteBuffer(22);
297 | buffer
298 | .append($.numToBytes(Script.op_codes["OP_0"], 1))
299 | .append($.numToBytes(20, 1))
300 | .append($.hexToBytes(pubkeyhash.toHex(0, pubkeyhash.capacity())));
301 | super(buffer);
302 | this.type = type;
303 | this.pubkeyhash = pubkeyhash;
304 | }
305 |
306 | static fromHex(hex) {
307 | let buffer = new ByteBuffer.fromHex(hex);
308 | return new P2wpkhV0Script(buffer.copy(2, buffer.capacity()));
309 |
310 | }
311 |
312 | getAddress() {
313 | return new address.SegwitAddress("p2wpkh", this.pubkeyhash, 0);
314 | }
315 |
316 | getScriptCode() {
317 | const sc = new P2pkhScript(this.pubkeyhash);
318 | var script_len = $.numToVarInt(sc.body.capacity());
319 | return [script_len].concat($.hexToBytes(sc.toHex()));
320 |
321 | }
322 |
323 | static identify(script) {
324 | const buf = script.copy(0, script.capacity());
325 | if (
326 | ScriptPubKey.requireOp("OP_0", buf.readUint8(0)) &&
327 | ScriptPubKey.requireOp(20, buf.readUint8(1)) &&
328 | script.capacity() === 22
329 | )
330 | return {data: buf.copy(2, 22)};
331 |
332 | return false;
333 | }
334 | }
335 |
336 | class P2shScript extends ScriptPubKey {
337 | constructor(source) {
338 | let type = "p2sh";
339 | let scripthash;
340 | if (source instanceof ScriptPubKey) scripthash = source.p2shHash();
341 | if (source instanceof address.Address) {
342 | if (source.type !== type)
343 | throw new TypeError("Invalid address type provided for p2sh: " + source.type);
344 | scripthash = source.hash;
345 | }
346 | if (source instanceof ByteBuffer) scripthash = source;
347 | const buffer = new ByteBuffer(23);
348 | buffer
349 | .append($.numToBytes(Script.op_codes["OP_HASH160"], 1))
350 | .append($.numToBytes(20, 1))
351 | .append(scripthash.reset())
352 | .append($.numToBytes(Script.op_codes["OP_EQUAL"], 1));
353 |
354 | super(buffer);
355 | this.type = type;
356 | this.scripthash = scripthash;
357 | }
358 |
359 | getAddress() {
360 | return new address.Address(this.type, this.scripthash);
361 | }
362 |
363 | static identify(script) {
364 | const buf = script.copy(0, script.capacity());
365 | if (
366 | ScriptPubKey.requireOp("OP_HASH160", buf.readUint8(0)) &&
367 | ScriptPubKey.requireOp(20, buf.readUint8(1)) &&
368 | ScriptPubKey.requireOp("OP_EQUAL", buf.readUint8(22)) &&
369 | script.capacity() === 23
370 | )
371 | return {data: buf.copy(2, 22)};
372 |
373 | return false;
374 | }
375 | }
376 |
377 | class P2wshV0Script extends ScriptPubKey {
378 | constructor(source) {
379 | let type = "p2wsh";
380 | let scripthash;
381 | if (source instanceof ScriptPubKey) scripthash = source.p2wshHash();
382 | if (source instanceof address.SegwitAddress) {
383 | if (source.type !== type)
384 | throw new TypeError("Invalid address type provided for p2wsh: " + source.type);
385 |
386 | scripthash = source.hash;
387 | }
388 | if (source instanceof ByteBuffer) scripthash = source;
389 | const buffer = new ByteBuffer(34);
390 | buffer
391 | .append($.numToBytes(Script.op_codes["OP_0"], 1))
392 | .append($.numToBytes(32, 1))
393 | .append(scripthash);
394 |
395 | super(buffer);
396 | this.type = type;
397 | this.scripthash = scripthash;
398 | }
399 |
400 | getAddress() {
401 | return new address.SegwitAddress("p2wsh", this.scripthash, 0);
402 | }
403 |
404 | static identify(script) {
405 | const buf = script.copy(0, script.capacity());
406 | if (
407 | ScriptPubKey.requireOp("OP_0", buf.readUint8(0)) &&
408 | ScriptPubKey.requireOp(32, buf.readUint8(1)) &&
409 | script.capacity() === 34
410 | )
411 | return {data: buf.copy(2, 34)};
412 |
413 | return false;
414 | }
415 | }
416 |
417 | class IfElseScript extends ScriptPubKey {
418 | constructor(source) {
419 | let if_script, else_script;
420 | if (source instanceof Array) {
421 | if (!(source[0] instanceof ScriptPubKey && source[1] instanceof ScriptPubKey))
422 | throw new TypeError("Invalid objects to build an If-Else script");
423 | if_script = source[0];
424 | else_script = source[1];
425 | }
426 |
427 | const buffer = new ByteBuffer(if_script.body.capacity() + else_script.body.capacity() + 3);
428 | buffer
429 | .append($.numToBytes(Script.op_codes["OP_IF"], 1))
430 | .append(if_script.serialize().flip())
431 | .append($.numToBytes(Script.op_codes["OP_ELSE"], 1))
432 | .append(else_script.serialize().flip())
433 | .append($.numToBytes(Script.op_codes["OP_ENDIF"], 1));
434 |
435 | super(buffer);
436 | this.if_script = if_script;
437 | this.else_script = else_script;
438 | this.type = "if{" + if_script.type + "}else{" + else_script.type + "}";
439 | }
440 | }
441 |
442 | class RelativeTimelockScript extends ScriptPubKey {
443 | constructor(source) {
444 | let sequence, locked_script;
445 | if (source instanceof Array) {
446 | if (!(source[1] instanceof transaction.Sequence && source[0] instanceof ScriptPubKey))
447 | throw new TypeError("Invalid objects provided to build a RelativeTimelockScript");
448 | sequence = source[1];
449 | locked_script = source[0];
450 | }
451 |
452 | const push_seq = StackData.opFromInt(sequence.n);
453 | const buffer = new ByteBuffer(push_seq.length + locked_script.body.capacity() + 2);
454 |
455 | buffer
456 | .append(push_seq)
457 | .append($.numToBytes(Script.op_codes["OP_CHECKSEQUENCEVERIFY"], 1))
458 | .append($.numToBytes(Script.op_codes["OP_DROP"], 1))
459 | .append(locked_script.serialize().flip());
460 | super(buffer);
461 | this.sequence = sequence;
462 | this.locked_script = locked_script;
463 | this.type = "RelativeTimelock " + this.locked_script.type;
464 | }
465 | }
466 |
467 | class TimelockScript extends ScriptPubKey {
468 | constructor(source) {
469 | let locktime, locked_script;
470 | if (source instanceof Array) {
471 | if (!(source[1] instanceof transaction.Locktime && source[0] instanceof ScriptPubKey))
472 | throw new TypeError("Invalid objects provided to build a TimeLockScript");
473 | locktime = source[1];
474 | locked_script = source[0];
475 | }
476 | const push_locktime = StackData.opFromInt(locktime.n);
477 | const buffer = new ByteBuffer(push_locktime.n + locked_script.body.capacity() + 2);
478 |
479 | buffer
480 | .append(push_locktime)
481 | .append($.numToBytes(Script.op_codes["OP_CHECKLOCKTIMEVERIFY"], 1))
482 | .append($.numToBytes(Script.op_codes["OP_DROP"], 1))
483 | .append(locked_script.serialize().flip());
484 | super(buffer);
485 | this.locktime = locktime;
486 | this.locked_script = locked_script;
487 | this.type = "Timelock " + this.locked_script.type;
488 | }
489 | }
490 |
491 | class MultiSigScript extends ScriptPubKey {
492 | constructor(source) {
493 | let m, n, pubkeys;
494 | if (source instanceof Array) {
495 | m = source[0];
496 | pubkeys = _.slice(source, 1, source.length - 1);
497 | n = source[source.length - 1];
498 | if (n !== pubkeys.length)
499 | throw new TypeError(
500 | "The number of pubkeys must be equal to n, " +
501 | pubkeys.length +
502 | "pubkeys provided, while n is " +
503 | n
504 | );
505 | }
506 | const push_m = StackData.opFromInt(m);
507 | const push_n = StackData.opFromInt(n);
508 |
509 | const serializedKeys = [];
510 | _.forEach(pubkeys, key => serializedKeys.push(key.serialize()));
511 | let serializedDim = 0;
512 | _.forEach(serializedKeys, key => (serializedDim += key.capacity()));
513 | const buffer = new ByteBuffer(
514 | push_m.length + push_n.length + serializedDim + pubkeys.length + 1
515 | );
516 | buffer.append(push_m);
517 | _.forEach(serializedKeys, function (key) {
518 | buffer.append($.numToBytes(key.capacity(), 1));
519 | buffer.append(key);
520 | });
521 | buffer.append(push_n).append($.numToBytes(Script.op_codes["OP_CHECKMULTISIG"], 1));
522 | super(buffer);
523 | this.type = "multisig";
524 | this.m = m;
525 | this.n = n;
526 | this.pubkeys = pubkeys;
527 | }
528 |
529 | static identify(script) {
530 | const buf = script.copy(0, script.capacity());
531 | const req = buf.readUint8(0);
532 | const keys = buf.readUint8(script.capacity() - 2);
533 | let read = 0;
534 | for (var i = 1; i < keys; i++) {
535 | let keysize = buf.readUint8(i);
536 | if (keysize !== 33 && keysize !== 65) return false;
537 | i += keysize;
538 | read += 1 + keysize;
539 | }
540 | if (!ScriptPubKey.requireOp("OP_CHECKMULTISIG", buf.readUint8(script.capacity() - 1)))
541 | return false;
542 | if (read + 3 !== script.capacity()) return false;
543 | return true;
544 | }
545 | }
546 |
547 | class StackData {
548 | static getPushOp(bytes) {
549 | const len = bytes.length;
550 | if (len === 0) return [0];
551 | if (len === 1) {
552 | if (bytes[0] === 0)
553 | throw new TypeError("Trying to push 0x00 as a literal instead of empty array");
554 | if (1 <= bytes[0] && bytes[0] <= 16)
555 | return $.numToBytes(80 + bytes[0], $.bytesLen(80 + bytes[0]));
556 | }
557 |
558 | if (len <= 75) return _.concat($.numToBytes(len, $.bytesLen(len)), bytes);
559 | else {
560 | let size;
561 | if (len <= 0xff) size = 1;
562 | else if (len <= 0xffff) size = 2;
563 | else if (len <= 0xffffffff) size = 4;
564 | else if (len > 0xffffffff) throw new RangeError("Data length too big to push");
565 | return _.concat(
566 | $.numToBytes(Script.op_codes["OP_PUSHDATA" + size], 1),
567 | $.numToBytes(len, size),
568 | bytes
569 | );
570 | }
571 | }
572 |
573 | static opFromInt(int) {
574 | if (int === 0) return [0];
575 | const sign = int < 0;
576 | const absolute = $.numToBytes(Math.abs(int), Math.ceil($.bytesLen(Math.abs(int))));
577 | if (absolute[absolute.length - 1] & (1 << 7)) absolute.append(sign ? 1 << 7 : 0);
578 | else absolute[absolute.length - 1] |= sign ? 1 << 7 : 0;
579 | return StackData.getPushOp(absolute);
580 | }
581 | }
582 |
583 | Script.op_codes = {
584 | OP_0: 0,
585 | OP_PUSHDATA1: 76,
586 | OP_PUSHDATA2: 77,
587 | OP_PUSHDATA4: 78,
588 | OP_1: 81,
589 | OP_IF: 99,
590 | OP_ELSE: 103,
591 | OP_DROP: 117,
592 | OP_DUP: 118,
593 | OP_ENDIF: 104,
594 | OP_EQUAL: 135,
595 | OP_EQUALVERIFY: 136,
596 | OP_HASH160: 169,
597 | OP_CHECKSIG: 172,
598 | OP_CHECKMULTISIG: 174,
599 | OP_HASH256: 170,
600 | OP_CHECKLOCKTIMEVERIFY: 177,
601 | OP_CHECKSEQUENCEVERIFY: 178
602 | };
603 | Script.identifiables = [P2pkhScript, P2pkScript, P2shScript, P2wshV0Script, P2wpkhV0Script];
604 | module.exports = {
605 | ScriptSig,
606 | ScriptPubKey,
607 | P2pkhScript,
608 | P2shScript,
609 | P2pkScript,
610 | P2wpkhV0Script,
611 | P2wshV0Script,
612 | RelativeTimelockScript,
613 | MultiSigScript,
614 | IfElseScript,
615 | StackData,
616 | Script,
617 | TimelockScript
618 | };
619 | const ByteBuffer = require("bytebuffer");
620 | const bitcoinjs = require("bitcoinjs-lib");
621 | const $ = require("../tools/conversions");
622 | const net = require("./network");
623 | const shajs = require("sha.js");
624 | const ripemd160 = require("ripemd160");
625 | const address = require("./address");
626 | const crypto = require("./crypto");
627 | const transaction = require("./transaction");
628 | const _ = require("lodash");
629 | const abtb = require("arraybuffer-to-buffer");
630 | const opcodes = require("./opcodes");
631 |
--------------------------------------------------------------------------------
/lib/solvers.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2017 chainside srl
3 |
4 | This file is part of the btcnodejs package.
5 |
6 | It is subject to the license terms in the LICENSE.md file found in the top-level
7 | directory of this distribution.
8 |
9 | No part of btcnodejs, including this file, may be copied, modified,
10 | propagated, or distributed except according to the terms contained in the
11 | LICENSE.md file.
12 | */
13 | "use strict";
14 |
15 | class Solver {
16 | constructor(sighashes = new transaction.Sighash("ALL")) {
17 | this.sighashes = sighashes;
18 | }
19 |
20 | hasPrevScript() {
21 | return false;
22 | }
23 | }
24 |
25 | class SingleSigSolver extends Solver {
26 | constructor(sighash = new transaction.Sighash("ALL")) {
27 | super(sighash);
28 | this.sighash = sighash;
29 | }
30 |
31 | getSighashes() {
32 | return [this.sighash];
33 | }
34 |
35 | solvesSegwit() {
36 | return false;
37 | }
38 | }
39 |
40 | class SegwitSolver extends Solver {
41 | solvesSegwit() {
42 | return true;
43 | }
44 | }
45 |
46 | class P2pkhSolver extends SingleSigSolver {
47 | constructor(privkey, sighash = new transaction.Sighash("ALL")) {
48 | super(sighash);
49 | this.privkey = privkey;
50 | }
51 |
52 | solve(digests) {
53 | const pubkey = this.privkey.getPublic();
54 |
55 | const signature = _.concat(
56 | $.hexToBytes(this.privkey.signDER(digests[0])),
57 | this.sighash.toByte()
58 | );
59 |
60 | const pkpushop = scripts.StackData.getPushOp($.hexToBytes(pubkey.toHex()));
61 | const sigpushop = scripts.StackData.getPushOp(signature);
62 | const scriptSigbody = new ByteBuffer.fromHex($.bytesToHex(_.concat(sigpushop, pkpushop)));
63 | return {
64 | scriptSig: new scripts.ScriptSig(scriptSigbody),
65 | witness: new transaction.Witness([])
66 | };
67 | }
68 | }
69 |
70 | class P2pkSolver extends SingleSigSolver {
71 | constructor(privkey, sighash = new transaction.Sighash("ALL")) {
72 | super(sighash);
73 | this.privkey = privkey;
74 | }
75 |
76 | solve(digests) {
77 | const signature = _.concat(
78 | $.hexToBytes(this.privkey.signDER(digests[0])),
79 | this.sighash.toByte()
80 | );
81 | const sigpushop = scripts.StackData.getPushOp(signature);
82 | const scriptSigbody = new ByteBuffer.fromHex($.bytesToHex(sigpushop));
83 | return {
84 | scriptSig: new scripts.ScriptSig(scriptSigbody),
85 | witness: new transaction.Witness([])
86 | };
87 | }
88 | }
89 |
90 | class P2wpkhV0Solver extends P2pkhSolver {
91 | constructor(privkey, sighash = new transaction.Sighash("ALL")) {
92 | super(privkey, sighash);
93 | }
94 |
95 | solve(digests) {
96 | const solveData = super.solve(digests);
97 | const scriptSig = solveData.scriptSig;
98 | const witness = solveData.witness;
99 |
100 | return {
101 | scriptSig: witness.toScriptSig(),
102 | witness: scriptSig.toWitness()
103 | };
104 | }
105 |
106 | solvesSegwit() {
107 | return true;
108 | }
109 | }
110 |
111 | class P2shSolver extends Solver {
112 | constructor(redeemScript, redeemScriptSolver) {
113 | super();
114 | this.redeemScript = redeemScript;
115 | this.redeemScriptSolver = redeemScriptSolver;
116 | }
117 |
118 | solve(digests) {
119 | const solveData = this.redeemScriptSolver.solve(digests);
120 | const redeemSig = solveData.scriptSig;
121 | const redeemWitness = solveData.witness;
122 | const redeempushop = scripts.StackData.getPushOp($.hexToBytes(this.redeemScript.toHex()));
123 | const redeemSigpushop = $.hexToBytes(redeemSig.toHex());
124 | const scriptSigbody = new ByteBuffer.fromHex(
125 | $.bytesToHex(_.concat(redeemSigpushop, redeempushop))
126 | );
127 | return {
128 | scriptSig: new scripts.ScriptSig(scriptSigbody),
129 | witness: redeemWitness
130 | };
131 | }
132 |
133 | getSighashes() {
134 | return this.redeemScriptSolver.getSighashes();
135 | }
136 |
137 | solvesSegwit() {
138 | return this.redeemScriptSolver.solvesSegwit();
139 | }
140 |
141 | getPrevScript() {
142 | if (this.redeemScriptSolver.hasPrevScript())
143 | return this.redeemScriptSolver.getPrevScript();
144 | return this.redeemScript;
145 | }
146 |
147 | hasPrevScript() {
148 | return true;
149 | }
150 | }
151 |
152 | class P2wshV0Solver extends SegwitSolver {
153 | constructor(witnessScript, witnessScriptSolver) {
154 | super();
155 | this.witnessScript = witnessScript;
156 | this.witnessScriptSolver = witnessScriptSolver;
157 | }
158 |
159 | solve(digests) {
160 | const solveData = this.witnessScriptSolver.solve(digests);
161 | const witnessSig = solveData.scriptSig;
162 | const witnessWit = solveData.witness;
163 |
164 | return {
165 | scriptSig: new scripts.ScriptSig(new ByteBuffer(0)),
166 | witness: new transaction.Witness(
167 | _.concat(witnessSig.toWitness().data, witnessWit.data, this.getPrevScript().body)
168 | )
169 | };
170 | }
171 |
172 | getPrevScript() {
173 | return this.witnessScript;
174 | }
175 |
176 | getSighashes() {
177 | return this.witnessScriptSolver.getSighashes();
178 | }
179 |
180 | hasPrevScript() {
181 | return true;
182 | }
183 | }
184 |
185 | class MultiSigSolver extends Solver {
186 | constructor(privkeys, sighashes = null) {
187 | let selfSighashes = [];
188 | if (!sighashes)
189 | _.forEach(privkeys, () => selfSighashes.push(new transaction.Sighash("ALL")));
190 | else selfSighashes = sighashes;
191 | super(selfSighashes);
192 | this.sighashes = selfSighashes;
193 | this.privkeys = privkeys;
194 | }
195 |
196 | solve(digests) {
197 | if (digests.length !== this.privkeys.length) {
198 | throw new RangeError(
199 | "The number of digests must be equal to the number of private keys"
200 | );
201 | }
202 | let signatures = [];
203 | let privkeys = this.privkeys;
204 | let sighashes = this.sighashes;
205 |
206 | _.forEach(digests, function (digest, index) {
207 | signatures.push(
208 | scripts.StackData.getPushOp(
209 | _.concat($.hexToBytes(privkeys[index].signDER(digest)), sighashes[index].toByte())
210 | )
211 | );
212 | });
213 |
214 | const scriptSigbody = new ByteBuffer.fromHex(
215 | $.bytesToHex(_.concat($.numToBytes(0, 1), _.flatten(signatures)))
216 | );
217 | return {
218 | scriptSig: new scripts.ScriptSig(scriptSigbody),
219 | witness: new transaction.Witness([])
220 | };
221 | }
222 |
223 | getSighashes() {
224 | return this.sighashes;
225 | }
226 |
227 | solvesSegwit() {
228 | return false;
229 | }
230 | }
231 |
232 | class IfElseSolver extends Solver {
233 | constructor(branch, innerSolver) {
234 | super();
235 | this.branch = branch;
236 | this.innerSolver = innerSolver;
237 | }
238 |
239 | solve(digests) {
240 | const solveData = this.innerSolver.solve(digests);
241 | const innerSig = solveData.scriptSig;
242 | const innerWit = solveData.witness;
243 | const innerSigpushop = $.hexToBytes(innerSig.toHex());
244 | const branchPushop = scripts.StackData.opFromInt(this.branch);
245 | const scriptSigbody = new ByteBuffer.fromHex(
246 | $.bytesToHex(_.concat(innerSigpushop, branchPushop))
247 | );
248 | return {
249 | scriptSig: new scripts.ScriptSig(scriptSigbody),
250 | witness: innerWit
251 | };
252 | }
253 |
254 | getSighashes() {
255 | return this.innerSolver.getSighashes();
256 | }
257 |
258 | solvesSegwit() {
259 | return this.innerSolver.solvesSegwit();
260 | }
261 |
262 | }
263 |
264 | class TimlockSolver extends Solver {
265 | constructor(innerSolver) {
266 | super();
267 | this.innerSolver = innerSolver;
268 | }
269 |
270 | solve(digests) {
271 | return this.innerSolver.solve(digests);
272 | }
273 |
274 | getSighashes() {
275 | return this.innerSolver.getSighashes();
276 | }
277 |
278 | solvesSegwit() {
279 | return this.innerSolver.solvesSegwit();
280 | }
281 | }
282 |
283 | class RelativeTimelockSolver extends TimlockSolver {
284 | }
285 |
286 | module.exports = {
287 | SingleSigSolver,
288 | P2pkhSolver,
289 | P2shSolver,
290 | MultiSigSolver,
291 | IfElseSolver,
292 | RelativeTimelockSolver,
293 | P2wpkhV0Solver,
294 | P2wshV0Solver,
295 | P2pkSolver
296 | };
297 | const $ = require("../tools/conversions");
298 | const _ = require("lodash");
299 | const scripts = require("./scripts");
300 | const ByteBuffer = require("bytebuffer");
301 | const transaction = require("./transaction");
302 |
--------------------------------------------------------------------------------
/lib/transaction.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2017 chainside srl
3 |
4 | This file is part of the btcnodejs package.
5 |
6 | It is subject to the license terms in the LICENSE.md file found in the top-level
7 | directory of this distribution.
8 |
9 | No part of btcnodejs, including this file, may be copied, modified,
10 | propagated, or distributed except according to the terms contained in the
11 | LICENSE.md file.
12 | */
13 | "use strict";
14 |
15 | class Transaction {
16 | constructor(version, inputs, outputs, locktime, segwit = false) {
17 | this.version = version;
18 | this.inputs = inputs;
19 | this.outputs = outputs;
20 | this.locktime = locktime;
21 | this.segwit = segwit;
22 | this.txid = this.segwit ? this.segwitId() : this.hash();
23 | if (new.target === Transaction) {
24 | Object.freeze(this);
25 | }
26 | }
27 |
28 | toJSON() {
29 | return JSON.stringify({
30 | txid: this.txid,
31 | outputs: _.forEach(this.outputs, out => out.toJSON()),
32 | inputs: _.forEach(this.inputs, input => input.toJSON()),
33 | locktime: this.locktime.n,
34 | version: this.version
35 | });
36 | }
37 |
38 | hash() {
39 | const serialized = this.serialize();
40 | const hex = serialized.toHex(0, serialized.capacity());
41 | return $.swapHex(
42 | shajs("sha256")
43 | .update(
44 | shajs("sha256")
45 | .update(hex, "hex")
46 | .digest("hex"),
47 | "hex"
48 | )
49 | .digest("hex")
50 | );
51 | }
52 |
53 | segwitId() {
54 | const serialized = this.serialize(false);
55 | const hex = serialized.toHex(0, serialized.capacity());
56 | return $.swapHex(
57 | shajs("sha256")
58 | .update(
59 | shajs("sha256")
60 | .update(hex, "hex")
61 | .digest("hex"),
62 | "hex"
63 | )
64 | .digest("hex")
65 | );
66 | }
67 |
68 | static fromHex(hex) {
69 | const buffer = new ByteBuffer.fromHex(hex);
70 | return Transaction.deserialize(buffer);
71 | }
72 |
73 | toHex() {
74 | const serialized = this.serialize();
75 | return serialized.toHex(0, serialized.capacity());
76 | }
77 |
78 | serialize(segwit = this.segwit) {
79 | const outputSegwit = segwit && this.segwit;
80 | let ser_ins = [],
81 | ser_outs = [];
82 | let ser_ins_dim = 0,
83 | ser_outs_dim = 0;
84 | let ser_witnesses = [];
85 | let ser_witnesses_dim = 0;
86 | _.forEach(this.inputs, input => {
87 | const ser_in = input.serialize();
88 | ser_ins_dim += ser_in.capacity();
89 | ser_ins.push(ser_in);
90 | if (outputSegwit) {
91 | const ser_wit = input.witness.serialize();
92 | ser_witnesses_dim += ser_wit.capacity();
93 | ser_witnesses.push(ser_wit);
94 | }
95 | });
96 | _.forEach(this.outputs, output => {
97 | const ser_out = output.serialize();
98 | ser_outs_dim += ser_out.capacity();
99 | ser_outs.push(ser_out);
100 | });
101 |
102 | var dim =
103 | ser_ins_dim +
104 | ser_outs_dim +
105 | 8 +
106 | $.numToVarInt(this.inputs.length).length +
107 | $.numToVarInt(this.outputs.length).length;
108 |
109 | if (outputSegwit) dim += ser_witnesses_dim + 2;
110 | let buffer = new ByteBuffer(dim);
111 |
112 | buffer.append($.numToBytes(this.version, 4));
113 |
114 | if (outputSegwit) buffer.append($.numToBytes(0, 1)).append($.numToBytes(1, 1));
115 | buffer.append($.numToVarInt(this.inputs.length));
116 | _.forEach(ser_ins, ser_in => buffer.append(ser_in.flip()));
117 | buffer.append($.numToVarInt(this.outputs.length));
118 | _.forEach(ser_outs, ser_out => buffer.append(ser_out.flip()));
119 | if (outputSegwit)
120 | _.forEach(ser_witnesses, ser_wit => buffer.append(ser_wit.flip()));
121 | buffer.append($.numToBytes(this.locktime.n, 4));
122 |
123 | return buffer;
124 | }
125 |
126 | toMutable() {
127 | var inputs = [];
128 | _.forEach(this.inputs, input => {
129 | var mutable_input = Object.assign({}, input);
130 | Object.setPrototypeOf(mutable_input, Input.prototype);
131 | inputs.push(mutable_input);
132 | });
133 | return new MutableTransaction(
134 | this.version,
135 | inputs,
136 | this.outputs,
137 | this.locktime,
138 | this.segwit
139 | );
140 | }
141 |
142 | static deserialize(bytebuffer) {
143 | let segwit = false;
144 | let buffer = bytebuffer.LE();
145 |
146 | let offset = 0;
147 | let ins = [],
148 | outs = [];
149 | const version = buffer.readInt32(offset);
150 |
151 | offset += 4;
152 | var n_ins = buffer.parseVarint(offset);
153 | offset += n_ins.length;
154 | if (n_ins.value === 0) {
155 | const flag = buffer.readByte(offset);
156 |
157 | offset += 1;
158 | if (flag !== 1) throw "Transaction is marked as segwit but flag is invalid";
159 | segwit = true;
160 |
161 | n_ins = buffer.parseVarint(offset);
162 | offset += n_ins.length;
163 | }
164 |
165 | for (var i = 0; i < n_ins.value; i++) {
166 | const d_in = Transaction.deserialize_input(buffer, offset);
167 | ins.push(d_in.input);
168 | offset += d_in.read;
169 | }
170 | const n_outs = buffer.parseVarint(offset);
171 | offset += n_outs.length;
172 | for (var i = 0; i < n_outs.value; i++) {
173 | const d_out = Transaction.deserialize_output(buffer, offset);
174 | outs.push(d_out.output);
175 | offset += d_out.read;
176 | }
177 |
178 | if (segwit) {
179 | const witnesses = [];
180 | for (var i = 0; i < n_ins.value; i++) {
181 | var n_data = buffer.parseVarint(offset);
182 | offset += n_data.length;
183 | var wit_data = [];
184 | for (var i = 0; i < n_data.value; i++) {
185 | let data_length = buffer.parseVarint(offset);
186 | offset += data_length.length;
187 | let data = new ByteBuffer.fromHex(buffer.toHex(offset, offset + data_length.value));
188 | offset += data_length.value;
189 | wit_data.push(data);
190 | }
191 | witnesses.push(new Witness(wit_data));
192 | }
193 | _.forEach(ins, (input, index) => (input.witness = witnesses[index]));
194 | }
195 | let locktime = new Locktime(buffer.readInt32(offset));
196 | offset += 4;
197 | if (buffer.capacity() !== offset) throw new Error("Incomplete tx deserialization");
198 | return new Transaction(version, ins, outs, locktime, segwit);
199 | }
200 |
201 | static deserialize_input(buffer, offset) {
202 | let i_offset = offset;
203 | const txid = $.swapHex(buffer.toHex(offset, offset + 32));
204 | offset += 32;
205 | const vout = buffer.readInt32(offset);
206 | offset += 4;
207 | const script_len = buffer.parseVarint(offset);
208 | offset += script_len.length;
209 | const script_sig = scripts.ScriptSig.fromHex(
210 | buffer.toHex(offset, offset + script_len.value)
211 | );
212 | offset += script_len.value;
213 | const sequence = new Sequence(buffer.readUint32(offset));
214 | offset += 4;
215 | return {
216 | input: new Input(txid, vout, script_sig, sequence),
217 | read: offset - i_offset
218 | };
219 | }
220 |
221 | static deserialize_output(buffer, offset) {
222 | let i_offset = offset;
223 | const amount = buffer.readInt64(offset).toNumber();
224 | offset += 8;
225 | const script_len = buffer.parseVarint(offset);
226 | offset += script_len.length;
227 | const script = scripts.ScriptPubKey.fromHex(
228 | buffer.toHex(offset, offset + script_len.value)
229 | );
230 | offset += script_len.value;
231 | return {
232 | output: new Output(amount, script),
233 | read: offset - i_offset
234 | };
235 | }
236 |
237 | getDigestPreImage(index, prev_script, sighash = new Sighash("ALL")) {
238 | let throwaway = this.toMutable();
239 | for (var i = 0; i < throwaway.inputs.length; i++) {
240 | if (i === index) throwaway.inputs[i].scriptSig = prev_script;
241 | else throwaway.inputs[i].scriptSig = scripts.ScriptSig.empty();
242 | }
243 | if (sighash.sighash in {NONE: 0x02, SINGLE: 0x03}) {
244 | if (sighash.sighash === "NONE") throwaway.outputs = [];
245 | else if (sighash.sighash === "SINGLE") {
246 | if (index >= throwaway.outputs.length)
247 | throw new RangeError(
248 | "Index greater than outputs number while SIGHASH SINGLE was chosen"
249 | );
250 | let matching_out = throwaway.outputs[index];
251 | throwaway.outputs = [];
252 | for (var j = 0; j < index; j++) {
253 | throwaway.outputs.append(
254 | new Output(0xffffffffffffffff, scripts.ScriptPubKey.empty())
255 | );
256 | }
257 | throwaway.outputs.append(matching_out);
258 | for (var j = 0; j < index; j++) {
259 | if (j !== index) throwaway.inputs[i].sequence = 0;
260 | }
261 | }
262 | }
263 |
264 | if (sighash.anyonecanpay) throwaway.inputs = [throwaway.inputs[index]];
265 | const serialized = throwaway.serialize();
266 | const buffer = new ByteBuffer(serialized.capacity() + 4);
267 | buffer.append(serialized.reset()).append($.numToBytes(Sighash.types[sighash.sighash], 4));
268 | return buffer;
269 | }
270 |
271 | getDigest(index, prev_script, sighash = new Sighash("ALL")) {
272 | const d_preimage = this.getDigestPreImage(index, prev_script, sighash);
273 | const preimage = d_preimage.toHex(0, d_preimage.capacity());
274 | return shajs("sha256")
275 | .update(
276 | shajs("sha256")
277 | .update(preimage, "hex")
278 | .digest("hex"),
279 | "hex"
280 | )
281 | .digest("hex");
282 | }
283 |
284 | getSegwitDigestPreImage(index, prev_script, prev_amount, sighash = new Sighash("ALL")) {
285 | let hash_prevouts = new ByteBuffer(32);
286 | let hash_sequence = new ByteBuffer(32);
287 | let hash_outputs = new ByteBuffer(32);
288 | if (!sighash.anyonecanpay) hash_prevouts = this.hashPrevouts();
289 | if (!sighash.anyonecanpay && !(sighash.sighash in {NONE: 0x02, SINGLE: 0x03}))
290 | hash_sequence = this.hashSequence();
291 | if (!(sighash.sighash in {NONE: 0x02, SINGLE: 0x03})) hash_outputs = this.hashOutputs();
292 | else if (sighash.sighash === "SINGLE" && index < this.outputs.length) {
293 | let tohash_buf = this.outputs[index].serialize();
294 | let tohash = tohash_buf.toHex(0, tohash_buf.capacity());
295 | hash_outputs = shajs("sha256")
296 | .update(
297 | shajs("sha256")
298 | .update(tohash, "hex")
299 | .digest("hex"),
300 | "hex"
301 | )
302 | .digest("hex");
303 | }
304 | let script_code;
305 |
306 | if (prev_script instanceof scripts.P2wpkhV0Script)
307 | script_code = prev_script.getScriptCode();
308 | else {
309 | var script_len = $.numToVarInt(prev_script.body.capacity());
310 | script_code = [script_len].concat($.hexToBytes(prev_script.toHex(0, prev_script.body.capacity())));
311 | }
312 | const curr_in = this.inputs[index];
313 | let dim =
314 | 28 +
315 | $.hexToBytes(hash_prevouts.toHex(0, hash_prevouts.capacity())).length +
316 | $.hexToBytes(hash_sequence.toHex(0, hash_sequence.capacity())).length +
317 | $.hexToBytes(hash_outputs.toHex(0, hash_outputs.capacity())).length +
318 | $.hexToBytes(curr_in.txid).length +
319 | script_code.length;
320 |
321 | const buffer = new ByteBuffer(dim);
322 | buffer
323 | .append($.numToBytes(this.version, 4))
324 | .append($.hexToBytes(hash_prevouts.toHex(0, hash_prevouts.capacity())))
325 | .append($.hexToBytes(hash_sequence.toHex(0, hash_sequence.capacity())))
326 | .append($.hexToBytes(curr_in.txid).reverse())
327 | .append($.numToBytes(curr_in.out, 4))
328 | .append(script_code)
329 | .append($.numToBytes(prev_amount, 8))
330 | .append($.numToBytes(curr_in.sequence.n, 4))
331 | .append($.hexToBytes(hash_outputs.toHex(0, hash_outputs.capacity())))
332 | .append($.numToBytes(this.locktime.n, 4))
333 | .append($.numToBytes(Sighash.types[sighash.sighash], 4));
334 | /*
335 | console.log("Preimage");
336 | console.log(buffer.toHex(0, buffer.capacity()));
337 | console.log("Version: " + this.version);
338 | console.log("Hash prevouts: " + hash_prevouts.toHex(0, hash_prevouts.capacity()));
339 | console.log("Hash sequence: " + hash_sequence.toHex(0, hash_sequence.capacity()));
340 | console.log("Outpoint txid: " + curr_in.txid);
341 | console.log("Outpoint vout: " + curr_in.out);
342 | console.log("Script code: " + script_code);
343 | console.log("Prev amount: " + prev_amount);
344 | console.log("Sequence: " + curr_in.sequence.n);
345 | console.log("Hash outputs: " + hash_outputs.toHex(0, hash_outputs.capacity()));
346 | console.log("Locktime: " + this.locktime.n);
347 | console.log("Sighash: " + Sighash.types[sighash.sighash]);
348 | console.log("Digest: " + buffer.toHex(0, buffer.capacity()));
349 | */
350 | return buffer;
351 | }
352 |
353 | getSegwitDigest(index, prev_script, prev_amount, sighash = new Sighash("ALL")) {
354 |
355 | const preimage = this.getSegwitDigestPreImage(index, prev_script, prev_amount, sighash);
356 | const hex = preimage.toHex(0, preimage.capacity());
357 | const digest = shajs("sha256")
358 | .update(
359 | shajs("sha256")
360 | .update(hex, "hex")
361 | .digest("hex"),
362 | "hex"
363 | )
364 | .digest("hex");
365 |
366 | return digest;
367 | }
368 |
369 | hashPrevouts() {
370 | let data = [];
371 | _.forEach(this.inputs, input => {
372 | let txid = $.hexToBytes(input.txid).reverse();
373 | let vout = $.numToBytes(input.out, 4);
374 | data.push(txid);
375 | data.push(vout);
376 | });
377 | const hex = $.bytesToHex(_.flatten(data));
378 | const hash = shajs("sha256")
379 | .update(
380 | shajs("sha256")
381 | .update(hex, "hex")
382 | .digest("hex"),
383 | "hex"
384 | )
385 | .digest("hex");
386 | const buffer = new ByteBuffer.fromHex(hash);
387 | return buffer;
388 | }
389 |
390 | hashSequence() {
391 | let data = [];
392 | _.forEach(this.inputs, input => {
393 | let sequence = $.numToBytes(input.sequence.n, 4);
394 |
395 | data.push(sequence);
396 | });
397 | const hex = $.bytesToHex(_.flatten(data));
398 | const hash = shajs("sha256")
399 | .update(
400 | shajs("sha256")
401 | .update(hex, "hex")
402 | .digest("hex"),
403 | "hex"
404 | )
405 | .digest("hex");
406 | const buffer = new ByteBuffer.fromHex(hash);
407 | return buffer;
408 | }
409 |
410 | hashOutputs() {
411 | let data = [];
412 | _.forEach(this.outputs, output => {
413 | let ser_buf = output.serialize();
414 | let ser_out = $.hexToBytes(ser_buf.toHex(0, ser_buf.capacity()));
415 |
416 | data.push(ser_out);
417 | });
418 | const hex = $.bytesToHex(_.flatten(data));
419 | const hash = shajs("sha256")
420 | .update(
421 | shajs("sha256")
422 | .update(hex, "hex")
423 | .digest("hex"),
424 | "hex"
425 | )
426 | .digest("hex");
427 | const buffer = new ByteBuffer.fromHex(hash);
428 | return buffer;
429 | }
430 | }
431 |
432 | class MutableTransaction extends Transaction {
433 | constructor(version, inputs, outputs, locktime, segwit = false) {
434 | super(version, inputs, outputs, locktime, segwit);
435 | }
436 |
437 | toImmutable() {
438 | return new Transaction(
439 | this.version,
440 | this.inputs,
441 | this.outputs,
442 | this.locktime,
443 | this.segwit
444 | );
445 | }
446 |
447 | spend(txouts, solvers) {
448 |
449 | if (this.segwit) return this.spendSegwit(txouts, solvers);
450 | else {
451 | var spent = this.spendClassic(txouts, solvers);
452 | return spent;
453 | }
454 | }
455 |
456 | spendSingle(index, txout, solver) {
457 | const sighashes = solver.getSighashes();
458 | const prev_script = solver.hasPrevScript() ? solver.getPrevScript() : txout.scriptPubKey;
459 | let solveData;
460 | if (sighashes.length === 0) {
461 | solveData = solver.solve();
462 | } else if (sighashes.length === 1) {
463 |
464 | let digest = this.getDigest(index, prev_script, sighashes[0]);
465 |
466 | solveData = solver.solve([digest]);
467 |
468 | } else {
469 | let digests = [];
470 | _.forEach(sighashes, (sighash) => {
471 | digests.push(this.getDigest(index, prev_script, sighash));
472 | });
473 | solveData = solver.solve(digests);
474 | }
475 | const scriptSig = solveData.scriptSig;
476 | const witness = solveData.witness;
477 | this.inputs[index].scriptSig = scriptSig;
478 |
479 |
480 | }
481 |
482 | spendClassic(txouts, solvers) {
483 | if (_.some(solvers, solver => solver.solvesSegwit()))
484 | return this.spendSegwit(txouts, solvers);
485 | if (solvers.length !== this.inputs.length || txouts.length !== solvers.length)
486 | throw new Error("The number of solvers must be equal to the number of utxos");
487 | _.forEach(_.zip(_.range(solvers.length), txouts, solvers), list =>
488 | this.spendSingle(list[0], list[1], list[2])
489 | );
490 | return this.toImmutable();
491 | }
492 |
493 | spendSegwitSingle(index, txout, solver) {
494 | const sighashes = solver.getSighashes();
495 | const prev_script = solver.hasPrevScript() ? solver.getPrevScript() : txout.scriptPubKey;
496 | let solveData;
497 | if (sighashes.length === 0) {
498 | solveData = solver.solve();
499 | } else if (sighashes.length === 1) {
500 | if (solver.solvesSegwit()) {
501 |
502 | let digest = this.getSegwitDigest(index, prev_script, txout.amount, sighashes[0]);
503 | solveData = solver.solve([digest]);
504 | } else {
505 |
506 | let digest = this.getDigest(index, prev_script, sighashes[0]);
507 | solveData = solver.solve([digest]);
508 | }
509 | } else {
510 | let digests = [];
511 | if (solver.solvesSegwit()) {
512 | _.forEach(sighashes, sighash => {
513 | digests.push(this.getSegwitDigest(index, prev_script, txout.amount, sighash));
514 | });
515 | } else {
516 | _.forEach(sighashes, sighash => {
517 | digests.push(this.getDigest(index, prev_script, sighash));
518 | });
519 | }
520 | solveData = solver.solve(digests);
521 | }
522 |
523 | this.inputs[index].scriptSig = solveData.scriptSig;
524 | this.inputs[index].witness = solveData.witness;
525 |
526 | }
527 |
528 | spendSegwit(txouts, solvers) {
529 | if (solvers.length !== this.inputs.length || txouts.length !== solvers.length)
530 | throw new Error("The number of solvers must be equal to the number of utxos");
531 | _.forEach(_.zip(_.range(solvers.length), txouts, solvers), list =>
532 | this.spendSegwitSingle(list[0], list[1], list[2])
533 | );
534 | return this.toImmutable();
535 | }
536 | }
537 |
538 | class Sighash {
539 | constructor(sighash, anyonecanpay = false) {
540 | if (!(sighash in Sighash.types)) {
541 | throw new TypeError("Invalid sighash provided");
542 | }
543 | this.sighash = sighash;
544 | this.anyonecanpay = anyonecanpay;
545 | if (new.target === Sighash) {
546 | Object.freeze(this);
547 | }
548 | }
549 |
550 | toInt() {
551 | if (this.anyonecanpay) return Sighash.types[this.sighash] | 0x80;
552 | return Sighash.types[this.sighash];
553 | }
554 |
555 | static fromByte(byte) {
556 | if (byte & 0x80) {
557 | return new Sighash(Sighash.bytes[byte & ~0x80], true);
558 | }
559 | return new Sighash(Sighash.bytes[byte], false);
560 | }
561 |
562 | toByte() {
563 | return $.numToBytes(this.toInt(), 1);
564 | }
565 |
566 | serialize() {
567 | return $.numToBytes(this.toInt(), 4);
568 | }
569 | }
570 |
571 | Sighash.types = {
572 | ALL: 0x01,
573 | NONE: 0x02,
574 | SINGLE: 0x03
575 | };
576 | Sighash.bytes = {
577 | 1: "ALL",
578 | 2: "NONE",
579 | 3: "SINGLE"
580 | };
581 |
582 | class Output {
583 | constructor(amount, scriptPubKey) {
584 | this.amount = amount;
585 | this.scriptPubKey = scriptPubKey;
586 | }
587 |
588 | toJSON() {
589 | return JSON.stringify({
590 | amount: this.amount.toString(10),
591 | script: this.scriptPubKey.body.toString("hex")
592 | });
593 | }
594 |
595 | serialize() {
596 | let dim =
597 | 8 +
598 | $.numToVarInt(this.scriptPubKey.body.capacity()).length +
599 | $.numToVarInt(this.scriptPubKey.body.capacity())[0];
600 | let buffer = new ByteBuffer(dim);
601 | let ser_spk = this.scriptPubKey.serialize();
602 | buffer.append($.numToBytes(this.amount, 8));
603 | buffer.append($.numToVarInt(this.scriptPubKey.body.capacity()));
604 | buffer.append(ser_spk.toHex(0, ser_spk.capacity()), "hex");
605 | return buffer;
606 | }
607 | }
608 |
609 | class Input {
610 | constructor(txid, out, scriptSig, sequence, witness = undefined) {
611 | this.txid = txid;
612 | this.out = out;
613 | this.scriptSig = scriptSig;
614 | this.sequence = sequence;
615 | this.witness = witness || new Witness([]);
616 | }
617 |
618 | toJSON() {
619 | return JSON.stringify({
620 | txid: this.txid,
621 | out: this.out,
622 | scriptSig: this.scriptSig.body.toHex(0, this.scriptSig.body.capacity()),
623 | sequence: this.sequence.n
624 | });
625 | }
626 |
627 | serialize() {
628 |
629 | let dim =
630 | $.hexToBytes(this.txid).reverse().length +
631 | $.numToVarInt(this.scriptSig.body.capacity()).length +
632 | this.scriptSig.body.capacity() +
633 | 8;
634 |
635 |
636 | const buffer = new ByteBuffer(dim);
637 | const ser_sig = this.scriptSig.serialize();
638 |
639 | buffer.append($.hexToBytes(this.txid).reverse());
640 | buffer.append($.numToBytes(this.out, 4));
641 | buffer.append($.numToVarInt(this.scriptSig.body.capacity()));
642 | buffer.append(ser_sig.toHex(0, ser_sig.capacity()), "hex");
643 | buffer.append($.numToBytes(this.sequence.n, 4));
644 | return buffer;
645 | }
646 | }
647 |
648 | class Witness {
649 | constructor(data) {
650 | this.data = data;
651 | }
652 |
653 | static fromHexArray(hexs) {
654 | let data = [];
655 | _.forEach(hexs, hex => {
656 | data.push(new ByteBuffer.fromHex(hex));
657 | });
658 | return new Witness(data);
659 | }
660 |
661 | serialize() {
662 | let dim = 0;
663 | _.forEach(this.data, data => {
664 | dim += data.capacity() + $.numToVarInt(data.capacity()).length;
665 | });
666 | const buffer = new ByteBuffer(dim);
667 | _.forEach(this.data, data => {
668 | buffer.append($.numToVarInt(data.capacity()));
669 | buffer.append(data.toHex(0, data.capacity()), "hex");
670 | });
671 | buffer.prepend($.numToVarInt(this.data.length), 0);
672 | return buffer;
673 | }
674 |
675 | toScriptSig() {
676 | let scriptSigdata = [];
677 | _.forEach(this.data, data => {
678 | let pushop = scripts.StackData.getPushOp($.hexToBytes(data.toHex(0, data.capacity())));
679 | scriptSigdata.push(pushop);
680 | });
681 | scriptSigdata = _.flatten(scriptSigdata);
682 | return new scripts.ScriptSig(
683 | new ByteBuffer(scriptSigdata.length).append(scriptSigdata, 0)
684 | );
685 | }
686 |
687 | toHex() {
688 | let hex = "";
689 | _.forEach(this.data, data => {
690 | hex += data.toHex(0, data.capacity());
691 | });
692 | return hex;
693 | }
694 | }
695 |
696 | class Sequence {
697 | constructor(n) {
698 | this.n = n;
699 | }
700 |
701 | isTime() {
702 | return Boolean(this.n & ((1 << Sequence.disableFlag) >>> 0));
703 | }
704 |
705 | isBlocks() {
706 | return !this.isTime();
707 | }
708 |
709 | isActive() {
710 | return !(this.n & ((1 << Sequence.typeFlag) >>> 0));
711 | }
712 |
713 | forScript() {
714 | return scripts.StackData.opFromInt(this.n);
715 | }
716 | }
717 |
718 | Sequence.disableFlag = 31;
719 | Sequence.typeFlag = 22;
720 | Sequence.max = 0xffffffff;
721 |
722 | class Locktime {
723 | constructor(n) {
724 | this.n = n;
725 | }
726 |
727 | isBlocks() {
728 | return 0 < this.n && this.n < Locktime.threshold;
729 | }
730 |
731 | isTime() {
732 | return this.n >= Locktime.threshold;
733 | }
734 |
735 | isActive() {
736 | return this.n > 0;
737 | }
738 |
739 | forScript() {
740 | return scripts.StackData.opFromInt(this.n);
741 | }
742 | }
743 |
744 | Locktime.threshold = 500000000;
745 |
746 | module.exports = {
747 | Transaction,
748 | MutableTransaction,
749 | Locktime,
750 | Input,
751 | Witness,
752 | Sequence,
753 | Output,
754 | Sighash
755 | };
756 | const P2shSolver = require("./solvers").P2shSolver;
757 | const bitcoin = require("bitcoinjs-lib");
758 | const $ = require("../tools/conversions");
759 | const scripts = require("./scripts");
760 | const ByteBuffer = require("bytebuffer");
761 | const bufferUtils = require("../tools/bytebuffer");
762 | const shajs = require("sha.js");
763 | const _ = require("lodash");
764 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "btcnodejs",
3 | "version": "0.1.3",
4 | "description": "Nodejs bitcoin library which provides tools to handle Bitcoin data structures in a simple fashion",
5 | "main": "./lib/index.js",
6 | "directories": {
7 | "lib": "lib",
8 | "test": "test"
9 | },
10 | "dependencies": {
11 | "arraybuffer-to-buffer": "^0.0.4",
12 | "assert": "^1.4.1",
13 | "bitcoinjs-lib": "^3.2.0",
14 | "bn.js": "^4.11.8",
15 | "brfs": "^2.0.2",
16 | "bs58": "^4.0.1",
17 | "bs58check": "^2.0.2",
18 | "bytebuffer": "^5.0.1",
19 | "elliptic": "^6.4.0",
20 | "line-reader": "^0.4.0",
21 | "lodash": "^4.17.15",
22 | "long": "^3.2.0",
23 | "ripemd160": "^2.0.1",
24 | "sha.js": "^2.4.8"
25 | },
26 | "devDependencies": {
27 | "bitcoind-rpc": "^0.7.1",
28 | "debug": ">=2.6.9",
29 | "growl": ">=1.10.0",
30 | "mocha": "5.2.0"
31 | },
32 | "scripts": {
33 | "test": "./node_modules/mocha/bin/mocha test/test.js",
34 | "integration": "./node_modules/mocha/bin/mocha test/integration/test.js"
35 | },
36 | "repository": {
37 | "type": "git",
38 | "url": "git+https://github.com/chainside/btcjs.git"
39 | },
40 | "keywords": [
41 | "bitcoin",
42 | "btc"
43 | ],
44 | "author": "Francesco Andreozzi",
45 | "license": "LGPLv3",
46 | "bugs": {
47 | "url": "https://github.com/chainside/btcjs/issues"
48 | },
49 | "homepage": "https://github.com/chainside/btcjs#readme"
50 | }
51 |
--------------------------------------------------------------------------------
/test/bip32.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | keys: [
3 | {
4 | pub:
5 | "tpubDHVQPtNuLdRLj7FU348D5PcrkkPj5ibhN52cfjthEH9KTfwTaVmodTn1Ekpge6PhUjW1noZ452xesirHgBKbzmY6hz4eoVXDwHcjczDT7zb",
6 | priv:
7 | "tprv8koNFULfCFjfqeDg9QTcfyxkBisnvPQnnmRqPDrPp1LvdBggx6xDSyA94cjpAXS7ccGhsQ7w6q7Y9Ku31e1eTDztU49LVBz9B1sCDoeE6Jc",
8 | u_hexpub:
9 | "0430e8ca46b7e5aa07d975ee152214431e419fac34e50becaf7e46db9a9c97d244426184e0d6ca627ac5f2837ba887c9faa6c5b100bb92ac7f41ae7890cff9fe2d",
10 | hexpub: "0330e8ca46b7e5aa07d975ee152214431e419fac34e50becaf7e46db9a9c97d244",
11 | hexpriv: "9b1b400e3b1211c6a56695cf1742f0a94ea38b995c1e1fb910458baa8a0874c4"
12 | },
13 | {
14 | pub:
15 | "tpubDEhdzhXujo86G6PXroPKQJSCJi8qbdQvrALhTNiExsGKfFHXtVbTE9tnLBCAP7nqQrqfUSVTCDuqv6RMHu8PDL5a8G43b5N2zKsF89nmLd6",
16 | priv:
17 | "tprv8i1brHVfbRSRNdMjy9iiztn5jgcuSJE2GrjvArfwYbTvpm2mG6ms3fGvA5kdZ3qZ7KB26VehAudSCUURKT56Hej2pBgj26ZkNbdD1YMdTiD",
18 | u_hexpub:
19 | "041c703de670b3b0df446e948f76acecd6e539a6a395b408bbcd711e2744b74a7bb488d3b387f0fcd19bc2bd8e13d11821011d7036ab4018a04b44810ca8e3ffe6",
20 | hexpub: "021c703de670b3b0df446e948f76acecd6e539a6a395b408bbcd711e2744b74a7b",
21 | hexpriv: "e2cf56175f5cd5f19e9d1599b99463d769c6e16f1753dfa18aab64cbabeb7b7d"
22 | },
23 | {
24 | pub:
25 | "tpubDDqNYkcvEKbQJKtda5miuWCBWqX2Bd8qJWgSNbsiqfSHRzNpsjpXHAMiNYNHZw9FCnkuJpVAJjZkTeujhT4h293w6YMexGyAgNGRYWVtJ1D",
26 | priv:
27 | "tprv8h9LQLag5wujQrrqgS78W6Y4wp162HwvjD5f65qRRPdtbW84FLzw6fjrCNeqEvsKqiDxLtzJ9oHUGVTL17KptjbDVqgJ2XvAs2LcvSWrTUh",
28 | u_hexpub:
29 | "04ce81eed1b5b05b538c62eddeef310a31958929f67b58183fde1a6e14314207b7eae85ecfca3fec3fc4d437f4cf9726262faf065fc0c6741045c641a95c9199a5",
30 | hexpub: "03ce81eed1b5b05b538c62eddeef310a31958929f67b58183fde1a6e14314207b7",
31 | hexpriv: "61f0ddee86bca16f89dbbfef3fc4059758d8279e77dd816490f2c621bb6f67b7"
32 | },
33 | {
34 | pub:
35 | "tpubDATAg7GX3FHknHSDzfVEgwo8U1aEWPNdkgRjs4dBF244x9xC6tRqUkM8ZMk8JNHnmQMqNG1evQBNKwt97G348FXaWjhT88UWbwqrBTpmwe3",
36 | priv:
37 | "tprv8dm8XhEGtsc5tpQS71peHY91tz4JM4BjBNpxaYaspkFg7fhRUVcFJFjGPFHYGQLo21nmCrkjryoUFJKSKMUjKqpuRXAmMMhvMaP27UtqeLA",
38 | u_hexpub:
39 | "043804dcac6b7dfb41ff1b4731d690bbb3b480d505dab7ef83c237c18d98eb7c219189e56822e59668b0efe3a5723734ea6e88b9cf8371fa8effc3538e518e0012",
40 | hexpub: "023804dcac6b7dfb41ff1b4731d690bbb3b480d505dab7ef83c237c18d98eb7c21",
41 | hexpriv: "784c5adf8cb196ae4a8c3e4b9708974d699fe0ec41f07ada6e342a0e65300c14"
42 | },
43 | {
44 | pub:
45 | "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8",
46 | priv:
47 | "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi",
48 | u_hexpub:
49 | "0439a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c23cbe7ded0e7ce6a594896b8f62888fdbc5c8821305e2ea42bf01e37300116281",
50 | hexpub: "0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2",
51 | hexpriv: "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35"
52 | },
53 | {
54 | pub:
55 | "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw",
56 | priv:
57 | "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7",
58 | u_hexpub:
59 | "045a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc567f717885be239daadce76b568958305183ad616ff74ed4dc219a74c26d35f839",
60 | hexpub: "035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56",
61 | hexpriv: "edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea"
62 | },
63 | {
64 | pub:
65 | "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ",
66 | priv:
67 | "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs",
68 | u_hexpub:
69 | "04501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c008794c1df8131b9ad1e1359965b3f3ee2feef0866be693729772be14be881ab",
70 | hexpub: "03501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c",
71 | hexpriv: "3c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368"
72 | },
73 | {
74 | pub:
75 | "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5",
76 | priv:
77 | "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM",
78 | u_hexpub:
79 | "0457bfe1e341d01c69fe5654309956cbea516822fba8a601743a012a7896ee8dc24310ef3676384179e713be3115e93f34ac9a3933f6367aeb3081527ea74027b7",
80 | hexpub: "0357bfe1e341d01c69fe5654309956cbea516822fba8a601743a012a7896ee8dc2",
81 | hexpriv: "cbce0d719ecf7431d88e6a89fa1483e02e35092af60c042b1df2ff59fa424dca"
82 | }
83 | ]
84 | };
85 |
--------------------------------------------------------------------------------
/test/bip39.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | seeds: [
3 | {
4 | mnemonic: ["pyramid", "zone", "flower", "dawn", "second", "blue", "invite", "dice", "window", "damage", "hollow",
5 | "electric", "treat", "pulse", "giggle", "cushion", "flock", "impact", "demise", "require", "good", "corn",
6 | "yellow", "genius"],
7 | seed: "27e79ba3e766919d2efca0bf86f874e2343072990406e0c2be97b5cb2adb6048ca0be82cf9c79e1ac2b601e385bb8" +
8 | "3e7fb924982f93acbc702c8c7285419a595",
9 | key: "xprv9s21ZrQH143K2Az7YE73Ke1yJrKFzNp271h8gACB1i9xoTBUYM7kHPj3FXFK5w7rU48neUGWS4cvXpbWXr3M3bdkSu" +
10 | "RC77wdJx6v6dfCDAb",
11 | passphrase: ""
12 | },
13 | {
14 | mnemonic: ["clip", "spot", "rate", "ball", "thrive", "salad", "jaguar", "school", "sheriff", "inch", "acquire",
15 | "loop", "innocent", "depth", "road", "west", "oppose", "release", "ability", "guilt", "mix", "unit", "frost", "suggest"],
16 | seed: "6e1e2973e055abaf66b3b0387bf9a5db754483e199dbac3cb28c18789bde710ca3ab5865c2c9bb35d3f4bf2df15f89" +
17 | "d345db2f48880c238d22c8f64173f3a543",
18 | key: "xprv9s21ZrQH143K3Radgu9RzQnwCFYGvvjWYJpmTENdd8Mcoa3pmd4wM3gv6yquKRVpSkGZndpvJSpxVgxaZKvkgGYvtk5" +
19 | "eRPsKhkc3tmtnmnZ",
20 | passphrase: ""
21 | },
22 | {
23 | mnemonic: ["gift", "fish", "either", "lemon", "comic", "hollow", "harvest", "option", "scatter", "guard", "enroll",
24 | "history", "leader", "swap", "humor", "gorilla", "tip", "biology"],
25 | seed: "125c2cee33e482b55edc94888e4a9f5e4bc573d604ffd8fc731d4ffed46667873572f329d9a7948bc7e2a0200f4c88e" +
26 | "bf537dc80ddab6c0c97fff212cb7b7c2c",
27 | key: "xprv9s21ZrQH143K35qMC7RgVTfb1spBVeNTaWTsRaxJL2MDmYUjtqgfkNoBR1wr2WDQofae7p4iFu3Jwdnh1SchdxuoCv7t" +
28 | "RwP5JNasRq7fnAV",
29 | passphrase: ""
30 | },
31 | {
32 | mnemonic: ["chapter", "depth", "spray", "teach", "jacket", "bleak", "future", "pattern", "student", "achieve",
33 | "neglect", "night", "neglect", "duck", "marble", "waste", "huge", "proof"],
34 | seed: "62e6625994e05c7b82c2c27a41f51f2b82a24cac1ce1c20cf97357033939ca513218fe212976f7c134d643b19791e44" +
35 | "6edc80ef3045f07e0a1e89968067b9601",
36 | key: "xprv9s21ZrQH143K4CpzuCj3oKFMZpyt52Qzk7dhjUxuBgeVt4FToGXtJma25z84jRJBhzmU66w4qr1sYQABGJhomfVrQeSf" +
37 | "QmpKe6p9RqapbsA",
38 | passphrase: ""
39 | },
40 | {
41 | mnemonic: ["board", "provide", "tribe", "orchard", "gallery", "quiz", "sheriff", "limb", "kite", "secret", "ring",
42 | "siege", "trust", "note", "bubble", "silk", "chicken", "apart"],
43 | seed: "a7daac38c408824f093686ec6b9ed8b65451846eaf96841ac198ecd3aa53a3593c96a00e3adc5ef555cbe699e6c7709d" +
44 | "08d7ff4ae842706860754f934c53bc4e",
45 | key: "xprv9s21ZrQH143K3tjJe59mfKYwJVwF18fkTw9Hb5S66nrL4zUiwGvLBEkp2Ay4EuYcKrwXg3huTNAzG1qKYnvRpNNGkHYxP" +
46 | "Lp1Z3J8qwooh8A",
47 | passphrase: ""
48 | },
49 | {
50 | mnemonic: ["chicken", "wall", "antique", "theme", "legend", "toe", "menu", "cushion", "fish", "blossom", "across",
51 | "report", "again", "crime", "head", "vivid", "tide", "grace", "around", "effort", "fork", "muffin", "team",
52 | "piano"],
53 | seed: "8795d6f2a1d90ac1179753ab64b424caa9ea153dca7381269e6bf6c22232f6c27e7ccca9b02b326265f32252e4e132db4" +
54 | "ae7145f7bc2b9c81b26079acf1428de",
55 | key: "xprv9s21ZrQH143K2LSkQqv6uroDZHTifi6W5Sn8s8bjHuqjQd3uGso33ehuoy6WPBQtGjpfy4aDMkJeQhxndB7DgnQ4eWCeJo" +
56 | "JtWAityr51M6f",
57 | passphrase: ""
58 | },
59 | {
60 | mnemonic: ["doctor", "autumn", "tail", "mixed", "travel", "planet", "bench", "derive", "grab", "they", "brick",
61 | "noise"],
62 | seed: "27d5d9ea88d104975fa7b270a3beee8110758b94a84cefaef6ec76313c061fa84514db970a7115c03bfcda2199d9d4228c" +
63 | "a181fcfe39a69074c1daa59d323b15",
64 | key: "xprv9s21ZrQH143K2wTvDLCMwivyWYrnoVj8FCC4P4aXxcDkBnbKfdCkqXf6PkTr1LUHuCNcivKKDR6dnNbzoKy1eppLd2pnRGw" +
65 | "x9RC9Bu3n1JU",
66 | passphrase: ""
67 | },
68 | {
69 | mnemonic: ["wolf", "bonus", "lonely", "hip", "dance", "bus", "race", "tomato", "cave", "melt", "basic", "summer",
70 | "syrup", "shrimp", "salad", "snake", "anchor", "orient", "burden", "come", "uniform", "negative", "pepper",
71 | "matter"],
72 | seed: "9a8b45e7eaa3431cd6479fae3b2ebfd8d8074d88fbbced6e1dffc5d8b4559460c38cd17aef88e9e8c734f08c00b2e999c4f" +
73 | "ef964648e28f560b576d7f0c6abea",
74 | key: "xprv9s21ZrQH143K3WTn1mnauFjWEBZsjr74Aj1hiKkmLy4T4hm8QHYLYB966HpuwZE6Y9EwPcKLLtze1DW7TgsWBH2EK" +
75 | "WnEB3b9dStXajxGwtW",
76 | passphrase: ""
77 | },
78 | {
79 | mnemonic: ["wolf", "bonus", "lonely", "hip", "dance", "bus", "race", "tomato", "cave", "melt", "basic", "summer",
80 | "syrup", "shrimp", "salad", "snake", "anchor", "orient", "burden", "come", "uniform", "negative", "pepper",
81 | "matter"],
82 | seed: "2cb5ad6267b4522fcbaa0588a44e3dad67c372e4e4d5e1a21e692758cf826ccf3e813f4b9a05a6b1801771e0b19625dcde6" +
83 | "6ecd263c0d9aa6ea3a3b2713bbf27",
84 | key: "xprv9s21ZrQH143K33BVXfzXwMge7VxNxMhJFptZQX338Ke3QzMM8pGLZMxb3gYLcX9pXSpiws8JWUjobPRb35FfFsQgTy2mUwNWm" +
85 | "m3fA2fZ6Co",
86 | passphrase: "pass1"
87 | },
88 | {
89 | mnemonic: ["sight", "magic", "usual", "viable", "truth", "tomato", "gravity", "left", "ice", "orbit", "hard", "sugar",
90 | "upset", "humble", "unveil", "cherry", "surge", "drop", "offer", "nice", "start", "idle", "west", "arch"],
91 | seed: "44ed68881768bcb3a999f2371858d908bf96d6f382979206762bac028179e5bf0cf111c851a785fd0627c13d6c6027c8dc66" +
92 | "807b9ec9948cbe2c0cb0b979514e",
93 | key: "xprv9s21ZrQH143K31JtB2YMD5NeKidvPmqWoWV7qCB3xgY45WGE6oPLZemMcq65uKWCcgpT99q51d5eSqhHwbc4XAdQyd14oQJuA" +
94 | "ajcuaXNhed",
95 | passphrase: "pass2"
96 | }
97 |
98 | ]
99 | };
100 |
--------------------------------------------------------------------------------
/test/blocks.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | hash: "0000000000000178a6f26995c9937e99d42c1d4d4131ad162e283d34a9633780",
4 | raw:
5 | "00000020751406511dd24f6972987b00049e8ddcaa53e9a5402a9c3e320000000000000061acb5579267e3ebec145871380c3e4f9759fbe1476e76149fed1647980d84522bf92" +
6 | "359a1ca041a55a1eee30c01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2a03452c11042bf923592f244d696e65642062" +
7 | "792037706f6f6c2e636f6d2f0100000048db000000000000ffffffff0126e65409000000001976a914255c8fce64bc2c994f6f1d2cc85579983601bb5488ac000000000100000" +
8 | "0017363302165009d464cad2867df7f3d97979258ff9cf2c94fe5329ec149992a0a01000000d900473044022043781eaef3d91c3dec2ecc7092dda492d785aad19769e227ba99" +
9 | "343656b32bb1022006ea05e1c7578774e3cd6b77e23ae62c9335bf4108dd17a16ab7857be7fd58700147304402203a7b1e1426099fc37789df566646886a105a2062b5170f916" +
10 | "23fbf03005d6a9502207cce979d47a1ded7a1db101a204048e635c6f1eb7f89bbc08bc7a0ac3c7ddb2d0147522102632178d046673c9729d828cfee388e121f497707f810c131" +
11 | "e0d3fc0fe0bd66d62103a0951ec7d3a9da9de171617026442fcd30f34d66100fab539853b43f508787d452aeffffffff0240420f000000000017a9140c3b53daff6dea5651309" +
12 | "8d72aa18138fdda503287f2dd647e0000000017a9148ce5408cfeaddb7ccb2545ded41ef4781094548487000000000100000001b1caab76a5198100d2daaeb22f5a73786c1be1" +
13 | "dd4979def0844b4af6a2b5759701000000da004730440220679c47ac4cc8135b322483dd4e53979ec650225d8acb3da4f5d8cf7746988d4a02207e96e67bc865632ffd29bf1f9" +
14 | "bd5f51d2a69de94fa8e93ecbfd9dc0fc58fd2ee01483045022100810052ac4d4731433bb8800e767d52933b201223651a2aea5019818c83b47d8d022023cd498fe594cdfc9d5b" +
15 | "0861421731d7a67f0253ab413cae3c2b518eba9176d00147522102632178d046673c9729d828cfee388e121f497707f810c131e0d3fc0fe0bd66d62103a0951ec7d3a9da9de17" +
16 | "1617026442fcd30f34d66100fab539853b43f508787d452aeffffffff0240420f000000000017a9149177db5b7b36c858e779e875398874b44b4291278724f7537e0000000017" +
17 | "a9148ce5408cfeaddb7ccb2545ded41ef4781094548487000000000100000001c79a11e2d9e715cf74509a4fd892065cc5a29e5d47ea955bd473388ae15702fe01000000db004" +
18 | "83045022100d5356c3cbccac67a82d8ae2a9164158c89a9d697bf8d1ab3365f721180a361cf022046dc8e00e755d9600bce5273ab429daf64d94387809708576a4938e0b8121b" +
19 | "df01483045022100d6cb1f5f720a01f05526316e6a52c4b26a936fe09b7ec8f2c449ac1bf4e87c9202205d8cc290a6e7758615ea2df2d8736f49fbe54fe538a8ea169803beed4" +
20 | "5d53c130147522102632178d046673c9729d828cfee388e121f497707f810c131e0d3fc0fe0bd66d62103a0951ec7d3a9da9de171617026442fcd30f34d66100fab539853b43f" +
21 | "508787d452aeffffffff0240420f000000000017a91407f8e7ae1e3a611818b11c571fc1f37e7c9485f3870206387e0000000017a9148ce5408cfeaddb7ccb2545ded41ef4781" +
22 | "094548487000000000100000001b9f2d7513c823afcc6dc0e0afb64d61da468ba0e26da9bd909a4eaa45813fe0a010000006b483045022100c31aaa1fb2cbd5fef776baffa9e2" +
23 | "171903b5826846c2d8decf6d9bd1403a5c2a022074f88e660647006b4f1557198c0f4e3b502cb1e91a2388081280aa6eee81af590121036576eed4dd5de137031c22b1375cd0a" +
24 | "67b9e17a4efd8cab8b07b1fa7e1679791feffffff02459e62060000000017a914c69533b4df0e4afe850863ad19d4775057023d5e87d2ebf89e6f0000001976a914c5b1f3478d" +
25 | "a9559680472a48d8eda4f42d0a3bee88ac3b2c11000100000001c4f627209c6f4bf1851986b543924fde50b40d1a12a93022534fb4d842e51975010000006b483045022100c0b" +
26 | "aa3c2d3eb4cc7ecc7636a2c4ce273bc362a287f9d09f3a4353018c5d4832b02204268cae2e2921a5164663a550c9f965d05e81fc0d77d9ea549913c1cfc778a730121038516ab" +
27 | "e640be18deeab8cbb129c2265a58a5102b2c187c3b00041c4b64bafa38ffffffff0323020000000000001976a9146e9fb388545eeac583779c09e212298249e4a91388acac120" +
28 | "100000000001976a9141e7034e686d3df27a484a8e06e99bc9b2a82a63a88ac0000000000000000026a000000000001000000012ddfbd28c7137c773ca8e88bb9887dfe503c3f" +
29 | "a530c909186803c913631d4f6b010000006b483045022100abaa2211961cf9dd4238e764286638e9d1f8b8e323f9f45729ddd8c4aa505449022041d90fccbb619d87d8edb7fe1" +
30 | "5f5a1117573a186ca7bf2e421087e42cd1a4f7a0121038516abe640be18deeab8cbb129c2265a58a5102b2c187c3b00041c4b64bafa38ffffffff0323020000000000001976a9" +
31 | "146e9fb388545eeac583779c09e212298249e4a91388acac120100000000001976a9141e7034e686d3df27a484a8e06e99bc9b2a82a63a88ac0000000000000000026a0000000" +
32 | "00001000000015e29111ced61eb17fedfbcecce965cccf252a22c40667707c909e9d91f1ae826030000006a473044022034b5aa260e3a8de2da3a4daa99d0deb556d68a6dd627" +
33 | "551a8a768748df9a2b2c022042a12fce0105bd76742457e0a03c2f56397cecb4d0080605952f009788ed549c012103b64e32e5f62e03701428fb1e3151e9a57f149c67708f6164a23" +
34 | "5c8199fe17cc2ffffffff0510270000000000001976a91413b29e222f7e96f7fc4e51778215d2071c25fe2a88ac10270000000000001976a91413b29e222f7e96f7fc4e51778215d2" +
35 | "071c25fe2a88aca0860100000000001976a91413d35ad337dd80a055757e5ea0a45b59fee3060c88ac5cd9d30c000000001976a91413d35ad337dd80a055757e5ea0a45b59fee3060" +
36 | "c88ac0000000000000000026a000000000001000000029d796553688d8222bb83fe004c1afdc378c94ddb9cd9048b6310162ad341f7da020000006a4730440220095b1cd23f1dc234" +
37 | "18062ef38376d6582fc04cda323a8b05ea82dd12cd937f3002200153083fd8da6cb810c04e4444361aaf8318feaa006c198babd82609c397f48e012103b64e32e5f62e03701428fb1" +
38 | "e3151e9a57f149c67708f6164a235c8199fe17cc2ffffffff4d195edfbfb89c1e06eef5fd0ac799c0618917864e9db9f5babff9994694c275030000006b483045022100b8d223a5d4" +
39 | "cb5ad084f398d32264f76252b27aa964b75a78c62dc994697e313502201938f8a83262a47a46d29b5e3d34ca10d99eb2a0b4e823cb20aa0cedd1455367012103b64e32e5f62e03701" +
40 | "428fb1e3151e9a57f149c67708f6164a235c8199fe17cc2ffffffff0510270000000000001976a914464d545207666eae5cf656eb2e3ef3683c3845d088ac10270000000000001976" +
41 | "a914464d545207666eae5cf656eb2e3ef3683c3845d088aca0860100000000001976a91413d35ad337dd80a055757e5ea0a45b59fee3060c88ac46931604000000001976a91413d35" +
42 | "ad337dd80a055757e5ea0a45b59fee3060c88ac0000000000000000026a0000000000010000000140255d7edf9a8e9896b49883b984450d98b85b685f6480ea64ef3110d1e24ea101" +
43 | "0000006b483045022100dc91cc36ab937a96fca53b60d2e3d7d3e0fe1df1780301de69a90769c51fc311022001546d83d88a951bbefb4dfa4755fead9a1f92a1a89622fa56cf7e466" +
44 | "5a553890121024e68ae959c2e5ea5930d7fb0b0b657237345002a2f796f3cb5c88ef169f7ae1fffffffff026887eb0b000000001976a91483733adf835c38ff1f38f9c94f20304611" +
45 | "80233f88ac0000000000000000516a4c4e424c466f7220676f6f64206c75636b2c2049206c696b65206d79207268796d6573206174726f63696f75730a537570657263616c6166726" +
46 | "167696c697374696365787069616c69646f63696f7573000000000200000001749b7bb91129f6cfdf0e718bb93b69913aa042b5b95d0a75b53ddaf620f47b6b010000006a47304402" +
47 | "20457a6bba7567339990a908aa8e01581bafef6fed897ec38665db6289b08e43c902203daeff344efb8c0ba115e6a438352fbbb18fb8faff908ef537989c0582d78df20121022492b" +
48 | "726a9f9145a55de46c30788e464078e177f6f06517441a0a2e0a983cea6feffffff02d0070000000000001976a9142d7cd96c8c3c5abed84b00f14925fcd850b0996488ac6cd9d508" +
49 | "000000001976a91400e11e0f2b95cf19618a469b04e5ce4c2771269088ac432c11000200000001a10a003534b680f823eb6327f18f7d25b62099c68514cc2a9c21e3e022b89f80010" +
50 | "000006b483045022100a2147a82ffb809ae8c9ca1cb19e0ae47ea59820a96c4f08d1a6a18bd122df45e02205cf7aa73155ef4ea151268a01c4782d1abb067257d4d6eb648fb103cb6" +
51 | "aed2f0012103b3291cab50639641beef486d0ee263ef670528407fb2ba9eb4c313babf3f93d1feffffff02ad9e1708000000001976a914f11e2d2a4d92ce294c587eb8c334cb95c29" +
52 | "0126388acd0070000000000001976a914cf0fe6b32ab653dfab2ecc97b1ab07133ceef3b688ac162c1100"
53 | }
54 | ];
55 |
--------------------------------------------------------------------------------
/test/hd.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | keys: [
3 | {
4 | path: "m",
5 | seed: "000102030405060708090a0b0c0d0e0f",
6 | pub:
7 | "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8",
8 | prv:
9 | "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"
10 | },
11 | {
12 | path: "m/0'",
13 | pub:
14 | "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw",
15 | prv:
16 | "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7"
17 | },
18 | {
19 | path: "m/0'/1",
20 | pub:
21 | "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ",
22 | prv:
23 | "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs"
24 | },
25 | {
26 | path: "m/0'/1/2'",
27 | pub:
28 | "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5",
29 | prv:
30 | "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM"
31 | },
32 | {
33 | path: "m/0'/1/2'/2",
34 | pub:
35 | "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV",
36 | prv:
37 | "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334"
38 | },
39 | {
40 | path: "m/0'/1/2'/2/1000000000",
41 | pub:
42 | "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy",
43 | prv:
44 | "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76"
45 | },
46 | {
47 | path: "m",
48 | seed:
49 | "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542",
50 | pub:
51 | "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB",
52 | prv:
53 | "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U"
54 | },
55 | {
56 | path: "m/0",
57 | pub:
58 | "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH",
59 | prv:
60 | "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt"
61 | },
62 | {
63 | path: "m/0/2147483647'",
64 | pub:
65 | "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a",
66 | prv:
67 | "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9"
68 | },
69 | {
70 | path: "m/0/2147483647'/1",
71 | pub:
72 | "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon",
73 | prv:
74 | "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef"
75 | },
76 | {
77 | path: "m/0/2147483647'/1/2147483646'",
78 | pub:
79 | "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL",
80 | prv:
81 | "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc"
82 | },
83 | {
84 | path: "m/0/2147483647'/1/2147483646'/2",
85 | pub:
86 | "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt",
87 | prv:
88 | "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j"
89 | },
90 | {
91 | path: "m",
92 | seed:
93 | "4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be",
94 | pub:
95 | "xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13",
96 | prv:
97 | "xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6"
98 | },
99 | {
100 | path: "m/0'",
101 | pub:
102 | "xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y",
103 | prv:
104 | "xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L"
105 | }
106 | ]
107 | };
108 |
--------------------------------------------------------------------------------
/test/integration/client.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const RpcClient = require("bitcoind-rpc");
3 | const config = require("./config");
4 | const client = new RpcClient(config);
5 | const exec = require("child_process").exec;
6 |
7 | const TEST_PATH = "";
8 |
9 | function runNodes() {
10 | return new Promise((resolve, reject) => {
11 | exec("bitcoind --conf=" + TEST_PATH + "/test_node/bitcoin.conf " +
12 | "--datadir=" + TEST_PATH + "/test_node", (err, stdout, stderr) => {
13 | if (err !== null) reject(err);
14 | else setTimeout(resolve, 2000);
15 | });
16 | });
17 | }
18 |
19 | function stopNodes() {
20 | return new Promise((resolve, reject) => {
21 | exec("bitcoin-cli --conf=" + TEST_PATH + "/test_node/bitcoin.conf stop", (err, stdout, stderr) => {
22 | if (err !== null) reject(err);
23 | else setTimeout(resolve, 2000);
24 | });
25 | });
26 | }
27 |
28 | function clearNodes() {
29 | return new Promise((resolve, reject) => {
30 | exec("rm -r " + TEST_PATH + "/test_node/regtest", (err, stdout, stderr) => {
31 | if (err !== null) reject(err);
32 | else setTimeout(resolve, 2000);
33 | });
34 | });
35 | }
36 |
37 | function sendRawTransaction(tx_hex) {
38 | return new Promise((resolve, reject) => {
39 | client.sendRawTransaction(tx_hex, (err, res) => {
40 | if (err) reject(err);
41 | resolve(res.result);
42 | });
43 | });
44 |
45 | }
46 |
47 | function getRawTransaction(txid) {
48 | return new Promise((resolve, reject) => {
49 | client.getRawTransaction(txid, 0, (err, res) => {
50 | if (err) reject(err);
51 | resolve(res.result);
52 | });
53 | });
54 | }
55 |
56 | function generateBlocks(number) {
57 | return new Promise((resolve, reject) => {
58 | client.generate(number, (err, res) => {
59 | if (err) reject(err);
60 | resolve(res.result);
61 | });
62 | });
63 | }
64 |
65 | function sendToAddress(address, amount) {
66 | return new Promise((resolve, reject) => {
67 | client.sendToAddress(address, amount, (err, res) => {
68 | if (err) reject(err);
69 | resolve(res.result);
70 | });
71 | });
72 | }
73 |
74 | module.exports = {
75 | generateBlocks,
76 | sendRawTransaction,
77 | sendToAddress,
78 | getRawTransaction,
79 | runNodes,
80 | stopNodes,
81 | clearNodes
82 | };
83 |
84 |
--------------------------------------------------------------------------------
/test/integration/config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | user: "btcnodejs",
3 | protocol: "http",
4 | pass: "btcnodejs",
5 | host: "127.0.0.1",
6 | port: "18342"
7 | };
8 |
--------------------------------------------------------------------------------
/test/integration/test.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const _ = require("lodash");
3 | const assert = require("assert");
4 | const before = require("mocha").before;
5 | const beforeEach = require("mocha").beforeEach;
6 | const after = require("mocha").after;
7 | const afterEach = require("mocha").afterEach;
8 | const net = require("../../lib/network");
9 | const hd = require("../../lib/hd");
10 | const rpcclient = require("./client");
11 | const scripts = require("../../lib/scripts");
12 | const transactions = require("../../lib/transaction");
13 | const solvers = require("../../lib/solvers");
14 | const Address = require("../../lib/address").Address;
15 |
16 | const Sighash = transactions.Sighash;
17 | const Locktime = transactions.Locktime;
18 | const Output = transactions.Output;
19 | const Input = transactions.Input;
20 | const Transaction = transactions.Transaction;
21 | const MutableTransaction = transactions.MutableTransaction;
22 | const Sequence = transactions.Sequence;
23 |
24 | const P2pkhSolver = solvers.P2pkhSolver;
25 | const MultiSigSolver = solvers.MultiSigSolver;
26 | const IfElseSolver = solvers.IfElseSolver;
27 | const RtlSolver = solvers.RelativeTimelockSolver;
28 | const P2shSolver = solvers.P2shSolver;
29 | const P2wpkhV0Solver = solvers.P2wpkhV0Solver;
30 | const P2wshV0Solver = solvers.P2wshV0Solver;
31 | const P2pkSolver = solvers.P2pkSolver;
32 |
33 | net.setup("testnet");
34 |
35 | var receivingKeys = [
36 | new hd.HDPrivateKey("tprv8koNFULfCFjfqeDg9QTcfyxkBisnvPQnnmRqPDrPp1LvdBggx6xDSyA94cjpAXS7ccGhsQ7w6q7Y9Ku31e1eTDztU49LVBz9B1sCDoeE6Jc"),
37 | new hd.HDPrivateKey("tprv8i1brHVfbRSRNdMjy9iiztn5jgcuSJE2GrjvArfwYbTvpm2mG6ms3fGvA5kdZ3qZ7KB26VehAudSCUURKT56Hej2pBgj26ZkNbdD1YMdTiD"),
38 | new hd.HDPrivateKey("tprv8h9LQLag5wujQrrqgS78W6Y4wp162HwvjD5f65qRRPdtbW84FLzw6fjrCNeqEvsKqiDxLtzJ9oHUGVTL17KptjbDVqgJ2XvAs2LcvSWrTUh"),
39 | new hd.HDPrivateKey("tprv8dm8XhEGtsc5tpQS71peHY91tz4JM4BjBNpxaYaspkFg7fhRUVcFJFjGPFHYGQLo21nmCrkjryoUFJKSKMUjKqpuRXAmMMhvMaP27UtqeLA")];
40 | var receivingAddresses = _.map(receivingKeys, key => key.privkey.getPublic().toAddress().toBase58());
41 |
42 | var initUtxo = [];
43 | var initSolvers = _.map(receivingKeys, key => new P2pkhSolver(key.privkey));
44 |
45 | var sighashes = [
46 | new Sighash("ALL", false),
47 | new Sighash("ALL", true),
48 | new Sighash("NONE", false),
49 | new Sighash("NONE", true),
50 | new Sighash("SINGLE", false),
51 | new Sighash("SINGLE", true)];
52 |
53 | const SATOSHIS = function (val) {
54 | return val * 100000000;
55 | };
56 | const masterKey = new hd.HDPrivateKey("tprv8koNFULfCFjfqeDg9QTcfyxkBisnvPQnnmRqPDrPp1LvdBggx6xD" +
57 | "SyA94cjpAXS7ccGhsQ7w6q7Y9Ku31e1eTDztU49LVBz9B1sCDoeE6Jc");
58 |
59 | const formatOutputs = function (tx) {
60 | var formatted = [];
61 | _.forEach(tx.outputs, (output, index) => {
62 | formatted.push({
63 | n: index,
64 | out: output,
65 | txid: tx.txid
66 | });
67 | });
68 | return formatted;
69 | };
70 | const formatInitOutputs = function (tx) {
71 | var formatted = [];
72 | _.forEach(tx.outputs, (output, index) => {
73 | var out_address = new Address("p2pkh", output.scriptPubKey.pubkeyhash, "testnet");
74 | if (receivingAddresses.includes(out_address.toBase58())) {
75 | formatted.push({
76 | n: index,
77 | out: output,
78 | txid: tx.txid
79 | });
80 | }
81 | });
82 | return formatted;
83 | };
84 | const checkSegwit = function (solvers) {
85 | var segwit = false;
86 | if (_.some(solvers, solver => solver.solvesSegwit())) {
87 | segwit = true;
88 | }
89 | return segwit;
90 | };
91 | const spendToP2pk = function (outputs, solvers, amount, sequence = new Sequence(0)) {
92 | return new Promise((resolve, reject) => {
93 | if (solvers.length !== outputs.length) throw "A solver for each output must be provided";
94 | var txouts = [];
95 | var txins = [];
96 |
97 | var segwit = checkSegwit(solvers);
98 | _.forEach(outputs, out => {
99 | var script = new scripts.P2pkScript(masterKey.privkey.getPublic());
100 | txouts.push(new Output(amount, script));
101 | txins.push(new Input(out.txid, out.n, scripts.ScriptSig.empty(), sequence));
102 | });
103 |
104 | var tx = new MutableTransaction(2, txins, txouts, new Locktime(0), segwit);
105 | var spent = tx.spend(_.map(outputs, output => output.out), solvers);
106 | rpcclient.sendRawTransaction(spent.toHex())
107 | .then(() => resolve([formatOutputs(spent), _.map(spent.outputs, out => new P2pkSolver(masterKey.privkey))]))
108 | .catch(err => reject(err));
109 | });
110 | };
111 | const spendToP2pkh = function (outputs, solvers, amount, sequence = new Sequence(0)) {
112 | return new Promise((resolve, reject) => {
113 | if (solvers.length !== outputs.length) throw "A solver for each output must be provided";
114 | var txouts = [];
115 | var txins = [];
116 |
117 | var segwit = checkSegwit(solvers);
118 | _.forEach(outputs, out => {
119 | var script = new scripts.P2pkhScript(masterKey.privkey.getPublic());
120 |
121 | txouts.push(new Output(amount, script));
122 | txins.push(new Input(out.txid, out.n, scripts.ScriptSig.empty(), sequence));
123 | });
124 |
125 | var tx = new MutableTransaction(2, txins, txouts, new Locktime(0), segwit);
126 | var spent = tx.spend(_.map(outputs, output => output.out), solvers);
127 | rpcclient.sendRawTransaction(spent.toHex())
128 | .then(() => resolve([formatOutputs(spent), _.map(spent.outputs, out => new P2pkhSolver(masterKey.privkey))]))
129 | .catch(err => reject(err));
130 | });
131 | };
132 |
133 | const spendToMultisig = function (outputs, solvers, amount, sequence = new Sequence(0)) {
134 | return new Promise((resolve, reject) => {
135 | if (solvers.length !== outputs.length) throw "A solver for each output must be provided";
136 |
137 | var txouts = [];
138 | var txins = [];
139 | var segwit = checkSegwit(solvers);
140 |
141 | let [privkey1, privkey2, privkey3] = [receivingKeys[0].privkey, receivingKeys[1].privkey, receivingKeys[2].privkey];
142 | _.forEach(outputs, (out, index) => {
143 | var script = new scripts.MultiSigScript([
144 | 2,
145 | privkey1.getPublic(),
146 | privkey2.getPublic(),
147 | privkey3.getPublic(),
148 | 3]);
149 | txouts.push(new Output(amount, script));
150 | txins.push(new Input(out.txid, out.n, scripts.ScriptSig.empty(), sequence));
151 | });
152 |
153 | var tx = new MutableTransaction(2, txins, txouts, new Locktime(0), segwit);
154 | var spent = tx.spend(_.map(outputs, output => output.out), solvers);
155 |
156 | rpcclient.sendRawTransaction(spent.toHex())
157 | .then(() => resolve([formatOutputs(spent), _.map(spent.outputs, out => new MultiSigSolver([privkey1, privkey2]))]))
158 | .catch(err => reject(err));
159 | });
160 | };
161 |
162 | const spendToIfElse = function (outputs, solvers, amount, branch, sequence = new Sequence(0)) {
163 | return new Promise((resolve, reject) => {
164 | if (solvers.length !== outputs.length) throw "A solver for each output must be provided";
165 | if (outputs.length !== 2) throw "Invalid data length to build if-else outputs";
166 | var txouts = [];
167 | var txins = [];
168 | var segwit = checkSegwit(solvers);
169 | var if_branch = outputs[0].out.scriptPubKey;
170 | var else_branch = outputs[1].out.scriptPubKey;
171 | var script = new scripts.IfElseScript([if_branch, else_branch]);
172 | _.forEach(outputs, out => {
173 | txins.push(new Input(out.txid, out.n, scripts.ScriptSig.empty(), sequence));
174 | });
175 | txouts.push(new Output(amount, script));
176 | var tx = new MutableTransaction(2, txins, txouts, new Locktime(0), segwit);
177 | var spent = tx.spend(_.map(outputs, output => output.out), solvers);
178 |
179 | var solver = branch === 0 ? solvers[1] : solvers[0];
180 | rpcclient.sendRawTransaction(spent.toHex())
181 | .then(() => resolve([formatOutputs(spent), _.map(spent.outputs, out => new IfElseSolver(branch, solver))]))
182 | .catch(err => reject(err));
183 | });
184 | };
185 |
186 | const spendToRtl = function (outputs, solvers, amount, script_sequence, sequence = new Sequence(0)) {
187 | return new Promise((resolve, reject) => {
188 | if (solvers.length !== outputs.length) throw "A solver for each output must be provided";
189 | var txouts = [];
190 | var txins = [];
191 | var segwit = checkSegwit(solvers);
192 | _.forEach(outputs, out => {
193 | var script = new scripts.RelativeTimelockScript([out.out.scriptPubKey, script_sequence]);
194 | txouts.push(new Output(amount, script));
195 | txins.push(new Input(out.txid, out.n, scripts.ScriptSig.empty(), sequence));
196 | });
197 | var tx = new MutableTransaction(2, txins, txouts, new Locktime(0), segwit);
198 | var spent = tx.spend(_.map(outputs, output => output.out), solvers);
199 | rpcclient.sendRawTransaction(spent.toHex())
200 | .then(() => resolve([formatOutputs(spent), _.map(spent.outputs, (out, index) =>
201 | new RtlSolver(solvers[index])), script_sequence]))
202 | .catch(err => reject(err));
203 | });
204 | };
205 | const spendToTl = function (outputs, solvers, amount, script_timelock, sequence = new Sequence(0)) {
206 | return new Promise((resolve, reject) => {
207 | if (solvers.length !== outputs.length) throw "A solver for each output must be provided";
208 | var txouts = [];
209 | var txins = [];
210 | var segwit = checkSegwit(solvers);
211 | _.forEach(outputs, out => {
212 | var script = new scripts.TimelockScript(([out.out.scriptPubKey, script_timelock]));
213 |
214 | });
215 | });
216 | };
217 | const spendToP2sh = function (outputs, solvers, amount, sequence = new Sequence(0)) {
218 | return new Promise((resolve, reject) => {
219 | if (solvers.length !== outputs.length) throw "A solver for each output must be provided";
220 | var txouts = [];
221 | var txins = [];
222 | var segwit = checkSegwit(solvers);
223 |
224 | _.forEach(outputs, out => {
225 | var script = new scripts.P2shScript(out.out.scriptPubKey);
226 | txouts.push(new Output(amount, script));
227 | txins.push(new Input(out.txid, out.n, scripts.ScriptSig.empty(), sequence));
228 | });
229 |
230 | var tx = new MutableTransaction(2, txins, txouts, new Locktime(0), segwit);
231 |
232 | var spent = tx.spend(_.map(outputs, output => output.out), solvers);
233 | rpcclient.sendRawTransaction(spent.toHex())
234 | .then(() => resolve([formatOutputs(spent), _.map(outputs, (out, index) =>
235 | new P2shSolver(out.out.scriptPubKey, solvers[index]))]))
236 | .catch(err => reject(err));
237 | });
238 | };
239 | const spendToP2wpkhV0 = function (outputs, solvers, amount, sequence = new Sequence(0)) {
240 | return new Promise((resolve, reject) => {
241 | if (solvers.length !== outputs.length) throw "A solver for each output must be provided";
242 | var txouts = [];
243 | var txins = [];
244 | var segwit = checkSegwit(solvers);
245 | _.forEach(outputs, out => {
246 | var script = new scripts.P2wpkhV0Script(masterKey.privkey.getPublic());
247 | txouts.push(new Output(amount, script));
248 | txins.push(new Input(out.txid, out.n, scripts.ScriptSig.empty(), sequence));
249 | });
250 | var tx = new MutableTransaction(2, txins, txouts, new Locktime(0), segwit);
251 | var spent = tx.spend(_.map(outputs, output => output.out), solvers);
252 | rpcclient.sendRawTransaction(spent.toHex())
253 | .then(() => resolve([formatOutputs(spent), _.map(spent.outputs, (out, index) =>
254 | new P2wpkhV0Solver(masterKey.privkey))]))
255 | .catch(err => reject(err));
256 | });
257 | };
258 | const spendToP2wshV0 = function (outputs, solvers, amount, sequence = new Sequence(0)) {
259 | return new Promise((resolve, reject) => {
260 | if (solvers.length !== outputs.length) throw "A solver for each output must be provided";
261 | var txouts = [];
262 | var txins = [];
263 | var segwit = checkSegwit(solvers);
264 | _.forEach(outputs, out => {
265 | var script = new scripts.P2wshV0Script(out.out.scriptPubKey);
266 | txouts.push(new Output(amount, script));
267 | txins.push(new Input(out.txid, out.n, scripts.ScriptSig.empty(), sequence));
268 | });
269 | var tx = new MutableTransaction(2, txins, txouts, new Locktime(0), segwit);
270 | var spent = tx.spend(_.map(outputs, output => output.out), solvers);
271 | rpcclient.sendRawTransaction(spent.toHex())
272 | .then(() => resolve([formatOutputs(spent), _.map(outputs, (out, index) =>
273 | new P2wshV0Solver(out.out.scriptPubKey, solvers[index]))]))
274 | .catch(err => reject(err));
275 | });
276 | };
277 | // generate initial regtest coins
278 | const fundAddresses = function () {
279 | return new Promise(async (resolve, reject) => {
280 | rpcclient.generateBlocks(200).then(async () => {
281 | for (var addr of receivingAddresses) {
282 | await rpcclient.sendToAddress(addr, 10.0).then(txid => {
283 | rpcclient.getRawTransaction(txid).then(tx_raw => {
284 | var tx = Transaction.fromHex(tx_raw);
285 | initUtxo.push(formatInitOutputs(tx));
286 | if (initUtxo.length === receivingAddresses.length) {
287 | initUtxo = _.flatten(initUtxo);
288 | resolve();
289 | }
290 | }).catch(err => reject(err));
291 | }).catch(err => reject(err));
292 | }
293 | }).catch(err => reject(err));
294 | });
295 | };
296 |
297 | const setup = function () {
298 | beforeEach((done) => {
299 | rpcclient.runNodes()
300 | .then(() => fundAddresses())
301 | .then(() => done())
302 | .catch(err => done(err));
303 | });
304 | afterEach((done) => {
305 | rpcclient.stopNodes()
306 | .then(rpcclient.clearNodes())
307 | .then(() => {
308 | initUtxo = [];
309 | done();
310 | })
311 | .catch(err => done(err));
312 | });
313 | };
314 |
315 | describe("Integration Test", function () {
316 | this.timeout(10000);
317 | setup();
318 | it("can generate starting utxo", () => {
319 | assert.equal(initUtxo.length, receivingAddresses.length);
320 | });
321 | });
322 | describe("btcnodejs P2pk signature algorithm", function () {
323 | this.timeout(10000);
324 | setup();
325 | it("can spend p2pk transactions", (done) => {
326 | spendToP2pk(initUtxo, initSolvers, SATOSHIS(9))
327 | .then(([outs, solvers]) => spendToP2pkh(outs, solvers, SATOSHIS(8)))
328 | .then(([outs, solvers]) => assert.equal(outs.length, solvers.length))
329 | .then(() => done())
330 | .catch(err => done(err));
331 | });
332 | });
333 |
334 | describe("btcnodejs If-Else signature algorithm", function () {
335 | this.timeout(10000);
336 | setup();
337 | it("can spend if {multisig} else {p2pkh} and if {p2pkh} else {multisig} outputs", (done) => {
338 | spendToMultisig(initUtxo.slice(0, 2), initSolvers.slice(0, 2), SATOSHIS(9))
339 | .then(([multisig_outs, multisig_solvers]) => {
340 | spendToP2pkh(initUtxo.slice(2, 4), initSolvers.slice(2, 4), SATOSHIS(9))
341 | .then(([p2pkh_outs, p2pkh_solvers]) => {
342 | spendToIfElse([p2pkh_outs[0], multisig_outs[1]], [p2pkh_solvers[0], multisig_solvers[1]], SATOSHIS(8), 0)
343 | .then(([if_else_out, if_else_solver]) => {
344 | spendToIfElse([p2pkh_outs[1], multisig_outs[0]], [p2pkh_solvers[1], multisig_solvers[0]], SATOSHIS(7), 1)
345 | .then(([if_else_out2, if_else_solver2]) => {
346 | spendToP2pkh(_.flatten([if_else_out, if_else_out2]), _.flatten([if_else_solver, if_else_solver2]), SATOSHIS(7))
347 | .then(([outs, solvers]) => assert(outs.length === solvers.length))
348 | .then(() => done())
349 | .catch(err => done(err));
350 | });
351 | });
352 | });
353 | });
354 | });
355 | it("can spend if {p2pkh} else {rtl} (on both branches) transactions", (done) => {
356 | spendToRtl(initUtxo.slice(0, 2), initSolvers.slice(0, 2), SATOSHIS(9), new Sequence(0))
357 | .then(([rtl_outs, rtl_solvers, rtl_sequence]) => {
358 | spendToP2pkh(initUtxo.slice(2, 4), initSolvers.slice(2, 4), SATOSHIS(9))
359 | .then(([p2pkh_outs, p2pkh_solvers]) => {
360 | spendToIfElse([rtl_outs[0], p2pkh_outs[0]], [rtl_solvers[0], p2pkh_solvers[0]], SATOSHIS(8), 0)
361 | .then(([if_else_out, if_else_solver]) => {
362 | spendToIfElse([rtl_outs[1], p2pkh_outs[1]], [rtl_solvers[1], p2pkh_solvers[1]], SATOSHIS(8), 1)
363 | .then(([if_else_out2, if_else_solver2]) => {
364 | spendToP2pkh(_.flatten([if_else_out, if_else_out2]), _.flatten([if_else_solver, if_else_solver2]), SATOSHIS(7))
365 | .then(([outs, solvers]) => assert.equal(outs.length, solvers.length))
366 | .then(() => done())
367 | .catch(err => done(err));
368 | });
369 | });
370 | });
371 | });
372 | });
373 | it("can spend if {p2pk} else {multisig} (on both branches) transactions", (done) => {
374 | spendToMultisig(initUtxo.slice(0, 2), initSolvers.slice(0, 2), SATOSHIS(9))
375 | .then(([multisig_outs, multisig_solvers]) => {
376 | spendToP2pk(initUtxo.slice(2, 4), initSolvers.slice(2, 4), SATOSHIS(9))
377 | .then(([p2pk_outs, p2pk_solvers]) => {
378 | spendToIfElse([p2pk_outs[0], multisig_outs[1]], [p2pk_solvers[0], multisig_solvers[1]], SATOSHIS(8), 0)
379 | .then(([if_else_out, if_else_solver]) => {
380 | spendToIfElse([p2pk_outs[1], multisig_outs[0]], [p2pk_solvers[1], multisig_solvers[0]], SATOSHIS(7), 1)
381 | .then(([if_else_out2, if_else_solver2]) => {
382 | spendToP2pkh(_.flatten([if_else_out, if_else_out2]), _.flatten([if_else_solver, if_else_solver2]), SATOSHIS(7))
383 | .then(([outs, solvers]) => assert(outs.length === solvers.length))
384 | .then(() => done())
385 | .catch(err => done(err));
386 | });
387 | });
388 | });
389 | });
390 | });
391 |
392 | });
393 | describe("btcnodejs P2pkh signature algorithm", function () {
394 | this.timeout(10000);
395 | setup();
396 | it("can spend p2pkh transactions", (done) => {
397 | spendToP2pkh(initUtxo, initSolvers, SATOSHIS(9))
398 | .then(([outs, solvers]) => spendToP2pkh(outs, solvers))
399 | .then(([outs, solvers]) => assert.equal(outs.length, solvers.length))
400 | .then(() => done())
401 | .catch(err => done(err));
402 | });
403 | });
404 |
405 | describe("btcnodejs Multisig signature algorithm", function () {
406 | this.timeout(10000);
407 | setup();
408 | it("can spend multisig transactions", (done) => {
409 | spendToMultisig(initUtxo, initSolvers, SATOSHIS(9))
410 | .then(([outs, solvers]) => spendToP2pkh(outs, solvers, SATOSHIS(8)))
411 | .then(([outs, solvers]) => assert.equal(outs.length, solvers.length))
412 | .then(() => done())
413 | .catch(err => done(err));
414 | });
415 | });
416 |
417 | describe("btcnodejs Rtl signature algorithm", function () {
418 | this.timeout(10000);
419 | setup();
420 |
421 | it("can spend rtl over p2pkh transactions", (done) => {
422 | spendToRtl(initUtxo, initSolvers, SATOSHIS(9), new Sequence(0))
423 | .then(([outs, solvers]) => spendToP2pkh(outs, solvers, SATOSHIS(8)))
424 | .then(([outs, solvers]) => assert.equal(outs.length, solvers.length))
425 | .then(() => done())
426 | .catch(err => done(err));
427 | });
428 |
429 | it("can spend rtl (2) over p2pkh transactions", (done) => {
430 | spendToRtl(initUtxo, initSolvers, SATOSHIS(9), new Sequence(2))
431 | .then(([outs, solvers, sequence]) => {
432 | rpcclient.generateBlocks(3)
433 | .then(() => spendToP2pkh(outs, solvers, SATOSHIS(8), sequence))
434 | .then(([outs, solvers]) => assert.equal(outs.length, solvers.length))
435 | .then(() => done())
436 | .catch(err => done(err));
437 | });
438 | });
439 | it("can spend rtl over multisig transactions", (done) => {
440 | spendToMultisig(initUtxo, initSolvers, SATOSHIS(9))
441 | .then(([outs, solvers]) => spendToRtl(outs, solvers, SATOSHIS(8), new Sequence(0)))
442 | .then(([outs, solvers]) => spendToP2pkh(outs, solvers), SATOSHIS(7))
443 | .then(([outs, solvers]) => assert.equal(outs.length, solvers.length))
444 | .then(() => done())
445 | .catch(err => done(err));
446 | });
447 | it("can spend rtl over p2pk transactions", (done) => {
448 | spendToP2pk(initUtxo, initSolvers, SATOSHIS(9))
449 | .then(([outs, solvers]) => spendToRtl(outs, solvers, SATOSHIS(8), new Sequence(0)))
450 | .then(([outs, solvers]) => spendToP2pkh(outs, solvers, SATOSHIS(7)))
451 | .then(([outs, solvers]) => assert.equal(outs.length, solvers.length))
452 | .then(() => done())
453 | .catch(err => done(err));
454 | });
455 |
456 | });
457 |
458 |
459 | describe("btcnodejs P2sh signature algorithm", function () {
460 | this.timeout(10000);
461 | setup();
462 |
463 | it("can spend p2sh over p2pk transactions", (done) => {
464 | spendToP2pk(initUtxo, initSolvers, SATOSHIS(9))
465 | .then(([outs, solvers]) => spendToP2sh(outs, solvers, SATOSHIS(8)))
466 | .then(([outs, solvers]) => spendToP2pkh(outs, solvers, SATOSHIS(7)))
467 | .then(([outs, solvers]) => assert.equal(outs.length, solvers.length))
468 | .then(() => done())
469 | .catch(err => done(err));
470 | });
471 | it("can spend p2sh over p2pkh transactions", (done) => {
472 | spendToP2sh(initUtxo, initSolvers, SATOSHIS(9))
473 | .then(([outs, solvers]) => spendToP2pkh(outs, solvers, SATOSHIS(8)))
474 | .then(([outs, solvers]) => assert.equal(outs.length, solvers.length))
475 | .then(() => done())
476 | .catch(err => done(err));
477 | });
478 | it("can spend p2sh over if-else transactions", (done) => {
479 | spendToIfElse(initUtxo.slice(0, 2), initSolvers.slice(0, 2), SATOSHIS(9), 0)
480 | .then(([if_else_out, if_else_solver]) => spendToP2sh(if_else_out, if_else_solver, SATOSHIS(8)))
481 | .then(([outs, solvers]) => spendToP2pkh(outs, solvers, SATOSHIS(7)))
482 | .then(([outs, solvers]) => assert.equal(outs.length, solvers.length))
483 | .then(() => done())
484 | .catch(err => done(err));
485 | });
486 | it("can spend p2sh over rtl transactions", (done) => {
487 | spendToRtl(initUtxo, initSolvers, SATOSHIS(9), new Sequence(0))
488 | .then(([outs, solvers]) => spendToP2sh(outs, solvers, SATOSHIS(8)))
489 | .then(([outs, solvers]) => spendToP2pkh(outs, solvers, SATOSHIS(7)))
490 | .then(([outs, solvers]) => assert.equal(outs.length, solvers.length))
491 | .then(() => done())
492 | .catch(err => done(err));
493 | });
494 |
495 | it("can spend p2sh over multisig transactions", (done) => {
496 | spendToMultisig(initUtxo, initSolvers, SATOSHIS(9))
497 | .then(([outs, solvers]) => spendToP2sh(outs, solvers, SATOSHIS(8)))
498 | .then(([outs, solvers]) => spendToP2pkh(outs, solvers, SATOSHIS(7)))
499 | .then(([outs, solvers]) => assert.equal(outs.length, solvers.length))
500 | .then(() => done())
501 | .catch(err => done(err));
502 | });
503 |
504 | it("can spend p2sh over p2wpkh transactions", (done) => {
505 | spendToP2wpkhV0(initUtxo, initSolvers, SATOSHIS(8))
506 | .then(([outs, solvers]) => spendToP2sh(outs, solvers, SATOSHIS(7)))
507 | .then(([outs, solvers]) => spendToP2pkh(outs, solvers, SATOSHIS(6)))
508 | .then(([outs, solvers]) => assert.equal(outs.length, solvers.length))
509 | .then(() => done())
510 | .catch(err => done(err));
511 | });
512 | it("can spend p2sh over p2wsh transactions", (done) => {
513 | spendToP2wshV0(initUtxo, initSolvers, SATOSHIS(9))
514 | .then(([outs, solvers]) => spendToP2sh(outs, solvers, SATOSHIS(8)))
515 | .then(([outs, solvers]) => spendToP2pkh(outs, solvers, SATOSHIS(7)))
516 | .then(([outs, solvers]) => assert(outs.length, solvers.length))
517 | .then(() => done())
518 | .catch(err => done(err));
519 | });
520 |
521 | });
522 |
523 |
524 | describe("btcnodejs P2wpkhV0 signature algorithm", function () {
525 | this.timeout(10000);
526 | setup();
527 | it("can spend p2wpkh transactions", (done) => {
528 | spendToP2wpkhV0(initUtxo, initSolvers, SATOSHIS(9))
529 | .then(([outs, solvers]) => spendToP2pkh(outs, solvers, SATOSHIS(8)))
530 | .then(([outs, solvers]) => assert.equal(outs.length, solvers.length))
531 | .then(() => done())
532 | .catch(err => done(err));
533 | });
534 | });
535 |
536 | describe("btcnodejs P2wshV0 signature algorithm", function () {
537 | this.timeout(10000);
538 | setup();
539 |
540 | it("can spend p2wshv0 over p2pk transactions", (done) => {
541 | spendToP2pk(initUtxo, initSolvers, SATOSHIS(9))
542 | .then(([outs, solvers]) => spendToP2wshV0(outs, solvers, SATOSHIS(8)))
543 | .then(([outs, solvers]) => spendToP2pkh(outs, solvers, SATOSHIS(7)))
544 | .then(([outs, solvers]) => assert.equal(outs.length, solvers.length))
545 | .then(() => done())
546 | .catch(err => done(err));
547 | });
548 | it("can spend p2wshv0 over p2pkh transactions", (done) => {
549 | spendToP2wshV0(initUtxo, initSolvers, SATOSHIS(9))
550 | .then(([outs, solvers]) => spendToP2pkh(outs, solvers, SATOSHIS(8)))
551 | .then(([outs, solvers]) => assert.equal(outs.length, solvers.length))
552 | .then(() => done())
553 | .catch(err => done(err));
554 | });
555 |
556 |
557 | it("can spend p2wshv0 over multisig transactions", (done) => {
558 | spendToMultisig(initUtxo, initSolvers, SATOSHIS(9))
559 | .then(([outs, solvers]) => spendToP2wshV0(outs, solvers, SATOSHIS(8)))
560 | .then(([outs, solvers]) => spendToP2pkh(outs, solvers, SATOSHIS(7)))
561 | .then(([outs, solvers]) => assert.equal(outs.length, solvers.length))
562 | .then(() => done())
563 | .catch(err => done(err));
564 | });
565 |
566 | it("can spend p2wshv0 over IfElse transactions", (done) => {
567 | spendToIfElse(initUtxo.slice(0, 2), initSolvers.slice(0, 2), SATOSHIS(9), 0)
568 | .then(([if_else_out, if_else_solver]) => spendToP2wshV0(if_else_out, if_else_solver, SATOSHIS(8)))
569 | .then(([outs, solvers]) => spendToP2pkh(outs, solvers, SATOSHIS(7)))
570 | .then(([outs, solvers]) => assert.equal(outs.length, solvers.length))
571 | .then(() => done())
572 | .catch(err => done(err));
573 | });
574 | it("can spend p2wshv0 over Rtl transactions", (done) => {
575 | spendToRtl(initUtxo, initSolvers, SATOSHIS(9), new Sequence(0))
576 | .then(([outs, solvers]) => spendToP2wshV0(outs, solvers, SATOSHIS(8)))
577 | .then(([outs, solvers]) => spendToP2pkh(outs, solvers, SATOSHIS(7)))
578 | .then(([outs, solvers]) => assert.equal(outs.length, solvers.length))
579 | .then(() => done())
580 | .catch(err => done(err));
581 | });
582 |
583 | });
584 |
585 |
--------------------------------------------------------------------------------
/test/integration/test_node/bitcoin.conf:
--------------------------------------------------------------------------------
1 | rpcuser=btcnodejs
2 | rpcpassword=btcnodejs
3 |
4 | daemon=1
5 | server=1
6 |
7 | mainnet=0
8 | testnet=0
9 | regtest=1
10 |
11 | rpcport=18342
12 |
13 | #externalip=93.63.247.0
14 |
15 | rpcallowip=0.0.0.0/0
16 | prematurewitness=1
17 | unpnp=0
18 | rpckeepalive=0
19 | rpcthreads=10
20 | relaypriority=0
21 | debug=1
22 | txindex=1
23 | reindex=1
24 | maxtxfee=10000
25 |
--------------------------------------------------------------------------------
/test/scripts.js:
--------------------------------------------------------------------------------
1 | const transaction = require("../lib/transaction");
2 | const scripts = require("../lib/scripts");
3 | const crypto = require("../lib/crypto");
4 | module.exports = {
5 | spk: {
6 | p2pk: [
7 | {
8 | hex: "4104ea0d6650c8305f1213a89c65fc8f4343a5dac8e985c869e51d3aa02879b57c60cff49fcb99314d02dfc612d654e4333150ef61fa569c1c66415602cae387baf7ac",
9 | pubkey: "04ea0d6650c8305f1213a89c65fc8f4343a5dac8e985c869e51d3aa02879b57c60cff49fcb99314d02dfc612d654e4333150ef61fa569c1c66415602cae387baf7",
10 | p2shHash: "45f76843096c76fa9f80ad4f33bf6eae2772a8c0",
11 | keytype: "uncompressed"
12 | },
13 | {
14 | hex: "2103ea0d6650c8305f1213a89c65fc8f4343a5dac8e985c869e51d3aa02879b57c60ac",
15 | pubkey: "03ea0d6650c8305f1213a89c65fc8f4343a5dac8e985c869e51d3aa02879b57c60",
16 | p2shHash: "3fb94b381c0409f4f54e27b7b160cb8a8f951122",
17 | keytype: "compressed"
18 | },
19 |
20 |
21 | ],
22 | p2pkh: [
23 | {
24 | hex: "76a9148b4912ec0496b5f759f3af5ab24d6f4779a52f9e88ac",
25 | pubkey: "026263992eda6538202047f1514e0f6155a229c3d61b066807664e9ef73d406d95",
26 | pubkeyhash: "8b4912ec0496b5f759f3af5ab24d6f4779a52f9e",
27 | address: "mtDRkyy3a65oNhATimSQFhRqU511buvVAT",
28 | p2shHash: "029c09b86e1e4c3822bc71859af3300520d577c2"
29 | }
30 | ],
31 | p2sh: [
32 | {
33 | hex: "a914ed4a0e1af5316666499ec6f8a5a99bf4abaf754987",
34 | scripthash: "ed4a0e1af5316666499ec6f8a5a99bf4abaf7549",
35 | address: "2NEstrBkkLcEmXCrUxStQnY9dP9PRoHNmNk"
36 | }
37 | ],
38 | multisig: [
39 | {
40 | hex:
41 | "522102c08786d63f78bd0a6777ffe9c978cf5899756cfc32bfad09a89e211aeb92624221033e81519ecf373ea3a5c7e1c" +
42 | "051b71a898fb3438c9550e274d980f147eb4d069d21036d568125a969dc78b963b494fa7ed5f20ee9c2f2fc2c57f86c5d" +
43 | "f63089f2ed3a53ae",
44 | data: [
45 | 2,
46 | "02c08786d63f78bd0a6777ffe9c978cf5899756cfc32bfad09a89e211aeb926242",
47 | "033e81519ecf373ea3a5c7e1c051b71a898fb3438c9550e274d980f147eb4d069d",
48 | "036d568125a969dc78b963b494fa7ed5f20ee9c2f2fc2c57f86c5df63089f2ed3a",
49 | 3
50 | ],
51 | p2shHash: "45cbbfbc9d78d3d26e464972e1ed0640e57baabc"
52 | }
53 | ],
54 | if_else: [
55 | {
56 | hex:
57 | "63522102c08786d63f78bd0a6777ffe9c978cf5899756cfc32bfad09a89e211aeb92624221033e81519ecf373ea3a5c7e1c" +
58 | "051b71a898fb3438c9550e274d980f147eb4d069d21036d568125a969dc78b963b494fa7ed5f20ee9c2f2fc2c57f86c5df6" +
59 | "3089f2ed3a53ae6755b27576a914f89873b36ea31cfbf4d2081db73147078460c61188ac68",
60 | if_script: new scripts.MultiSigScript([
61 | 2,
62 | crypto.Publickey.fromHex(
63 | "02c08786d63f78bd0a6777ffe9c978cf5899756cfc32bfad09a89e211aeb926242"
64 | ),
65 | crypto.Publickey.fromHex(
66 | "033e81519ecf373ea3a5c7e1c051b71a898fb3438c9550e274d980f147eb4d069d"
67 | ),
68 | crypto.Publickey.fromHex(
69 | "036d568125a969dc78b963b494fa7ed5f20ee9c2f2fc2c57f86c5df63089f2ed3a"
70 | ),
71 | 3
72 | ]),
73 | else_script: new scripts.RelativeTimelockScript([
74 | new scripts.P2pkhScript(
75 | crypto.Publickey.fromHex(
76 | "02c08786d63f78bd0a6777ffe9c978cf5899756cfc32bfad09a89e211aeb926242"
77 | )
78 | ),
79 | new transaction.Sequence(5)
80 | ]),
81 | p2shHash: "b6ec775b130018247aa62797865071c354cc86eb"
82 | }
83 | ],
84 | relative_timelock: [
85 | {
86 | hex: "55b27576a914f89873b36ea31cfbf4d2081db73147078460c61188ac",
87 | data: [
88 | new scripts.P2pkhScript(
89 | crypto.Publickey.fromHex(
90 | "02c08786d63f78bd0a6777ffe9c978cf5899756cfc32bfad09a89e211aeb926242"
91 | )
92 | ),
93 | new transaction.Sequence(5)
94 | ],
95 | p2shHash: "a7553c4129f920da43f9621a62c98c1833d54be2"
96 | }
97 | ],
98 | timelock: [
99 | {
100 | hex: "55b17576a914f89873b36ea31cfbf4d2081db73147078460c61188ac",
101 | data: [
102 | new scripts.P2pkhScript(crypto.Publickey.fromHex(
103 | "02c08786d63f78bd0a6777ffe9c978cf5899756cfc32bfad09a89e211aeb926242"
104 | )),
105 | new transaction.Locktime(5)
106 | ],
107 | p2shHash: "e26f9ff6cc394c24689a2a6b8f9501ff4f80bfc6"
108 | }
109 | ],
110 | p2wpkh: [
111 | {
112 | hex: "0014f81b6a6cfaaf19dbd9e56b9cab2d8a457608ad8e",
113 | hash: "4d09a2ea331cf46887c00a82122d4cc288a77c4480acf474073c178428efd3d4",
114 | type: "p2wpkhv0",
115 | address: "tb1qlqdk5m864uvahk09dww2ktv2g4mq3tvwl8wkcz"
116 | }
117 | ],
118 | p2wsh: [
119 | {
120 | hex: "0020cdbf909e935c855d3e8d1b61aeb9c5e3c03ae8021b286839b1a72f2e48fdba70",
121 | hash: "bec02ca04c990083b099f9e9d2a90e9498025f5061cfb3e3b3077aa4b60a484f",
122 | type: "p2wshv0",
123 | address: "tb1qeklep85ntjz4605drds6aww9u0qr46qzrv5xswd35uhjuj8ahfcqlpvc8e"
124 | }
125 | ]
126 | },
127 | sigs: [
128 | {
129 | asm:
130 | "3045022100b7bf286e5f6ac6fa308e8876a8a59b289094851a26cf62c20abd174917eb7762022069b5269e584e4c76" +
131 | "f207d1b789bff7171a663d795e49751c12cf07dceb2a94c701 024a0dcb0527c2751ea4dda3aa98f6eface16e978db" +
132 | "a8062bcbed623f158c07691",
133 | hex:
134 | "483045022100b7bf286e5f6ac6fa308e8876a8a59b289094851a26cf62c20abd174917eb7762022069b5269e584e4c7" +
135 | "6f207d1b789bff7171a663d795e49751c12cf07dceb2a94c70121024a0dcb0527c2751ea4dda3aa98f6eface16e978d" +
136 | "ba8062bcbed623f158c07691",
137 | witness_hex:
138 | "02483045022100b7bf286e5f6ac6fa308e8876a8a59b289094851a26cf62c20abd174917eb7762022069b5269e584e4" +
139 | "c76f207d1b789bff7171a663d795e49751c12cf07dceb2a94c70121024a0dcb0527c2751ea4dda3aa98f6eface16e97" +
140 | "8dba8062bcbed623f158c07691"
141 | },
142 | {
143 | asm:
144 | "3045022100af246c27890c2bc07a0b7450d3d82509702a44a4defdff766355240b114ee2ac02207bb67b468452fa1b3" +
145 | "25dd5583879f5c1412e0bb4dae1c2c96c7a408796ab76f101 02ab9e8575536a1e99604a158fc60fe2ebd1cb1839e91" +
146 | "9b4ca42b8d050cfad71b2",
147 | hex:
148 | "483045022100af246c27890c2bc07a0b7450d3d82509702a44a4defdff766355240b114ee2ac02207bb67b468452fa1" +
149 | "b325dd5583879f5c1412e0bb4dae1c2c96c7a408796ab76f1012102ab9e8575536a1e99604a158fc60fe2ebd1cb1839" +
150 | "e919b4ca42b8d050cfad71b2",
151 | witness_hex:
152 | "02483045022100af246c27890c2bc07a0b7450d3d82509702a44a4defdff766355240b114ee2ac02207bb67b468452f" +
153 | "a1b325dd5583879f5c1412e0bb4dae1c2c96c7a408796ab76f1012102ab9e8575536a1e99604a158fc60fe2ebd1cb18" +
154 | "39e919b4ca42b8d050cfad71b2"
155 | },
156 | {
157 | asm:
158 | "304502210092a204c35e27ded55012b28b8f88192058d29fd8c455442eca025f74cb6a51a3022016b1b98397b6c1387" +
159 | "126732c66300e75680d0896b64dcf4a6835f72435b0035001 02a32cf30511795881f432b38883a5793d00828430226" +
160 | "d379d43ae2dbb603a8c9b",
161 | hex:
162 | "48304502210092a204c35e27ded55012b28b8f88192058d29fd8c455442eca025f74cb6a51a3022016b1b98397b6c13" +
163 | "87126732c66300e75680d0896b64dcf4a6835f72435b00350012102a32cf30511795881f432b38883a5793d00828430" +
164 | "226d379d43ae2dbb603a8c9b",
165 | witness_hex:
166 | "0248304502210092a204c35e27ded55012b28b8f88192058d29fd8c455442eca025f74cb6a51a3022016b1b98397b6c" +
167 | "1387126732c66300e75680d0896b64dcf4a6835f72435b00350012102a32cf30511795881f432b38883a5793d008284" +
168 | "30226d379d43ae2dbb603a8c9b"
169 | },
170 | {
171 | asm:
172 | "3045022100f3ecd6482fc71de2f84f876eea8ac8be4f9cf92f885f14e97283c46d97c7566302206ec0f71137c8ff101b" +
173 | "2437441924726af39ff1db68f17238b4835c5214d5ad0d01 036451829b5a49f4909500ce18c4500bf16f9c4dd49c1be" +
174 | "2e9c74d210a134514d7",
175 | hex:
176 | "483045022100f3ecd6482fc71de2f84f876eea8ac8be4f9cf92f885f14e97283c46d97c7566302206ec0f71137c8ff10" +
177 | "1b2437441924726af39ff1db68f17238b4835c5214d5ad0d0121036451829b5a49f4909500ce18c4500bf16f9c4dd49c" +
178 | "1be2e9c74d210a134514d7",
179 | witness_hex:
180 | "02483045022100f3ecd6482fc71de2f84f876eea8ac8be4f9cf92f885f14e97283c46d97c7566302206ec0f71137c8ff" +
181 | "101b2437441924726af39ff1db68f17238b4835c5214d5ad0d0121036451829b5a49f4909500ce18c4500bf16f9c4dd4" +
182 | "9c1be2e9c74d210a134514d7"
183 | }
184 | ],
185 | compile: {
186 | p2sh: {
187 | hex: "a914ed4a0e1af5316666499ec6f8a5a99bf4abaf754987",
188 | asm: "OP_HASH160 ed4a0e1af5316666499ec6f8a5a99bf4abaf7549 OP_EQUAL",
189 | code: "P2shScript(bytearray(unhexlify(\"ed4a0e1af5316666499ec6f8a5a99bf4abaf7549\")))",
190 | type: "p2sh"
191 | },
192 | p2pkh: {
193 | hex: "76a914df76c017354ac39bde796abe4294d31de8b5788a88ac",
194 | asm: "OP_DUP OP_HASH160 df76c017354ac39bde796abe4294d31de8b5788a OP_EQUALVERIFY OP_CHECKSIG",
195 | code: "P2pkhScript(bytearray(unhexlify(\"df76c017354ac39bde796abe4294d31de8b5788a\")))",
196 | type: "p2pkh"
197 | },
198 | p2pk: {
199 | hex: "4104ea0d6650c8305f1213a89c65fc8f4343a5dac8e985c869e51d3aa02879b57c60cff49fcb99314d02dfc612d654e4333150ef61fa569c1c66415602cae387baf7ac",
200 | asm: "04ea0d6650c8305f1213a89c65fc8f4343a5dac8e985c869e51d3aa02879b57c60cff49fcb99314d02dfc612d654e4333150ef61fa569c1c66415602cae387baf7 OP_CHECKSIG",
201 | code: "P2pkScript(PublicKey.unhexlify(\"04ea0d6650c8305f1213a89c65fc8f4343a5dac8e985c869e51d3aa02879b57c60cff49fcb99314d02dfc612d654e4333150ef61fa569c1c66415602cae387baf7\"))",
202 | type: "p2pk"
203 | },
204 | if_else_timelock: {
205 | hex: "6352210384478d41e71dc6c3f9edde0f928a47d1b724c05984ebfb4e7d0422e80abe95ff2103eb27fa93667e4f48e36071eb21c7229e5416ff0abd2886d59c8f314fb3cbee4052ae67037b9710b175210384478d41e71dc6c3f9edde0f928a47d1b724c05984ebfb4e7d0422e80abe95ffac68",
206 | asm: "OP_IF OP_2 0384478d41e71dc6c3f9edde0f928a47d1b724c05984ebfb4e7d0422e80abe95ff 03eb27fa93667e4f48e36071eb21c7229e5416ff0abd2886d59c8f314fb3cbee40 OP_2 OP_CHECKMULTISIG OP_ELSE 7b9710 OP_CHECKLOCKTIMEVERIFY OP_DROP 0384478d41e71dc6c3f9edde0f928a47d1b724c05984ebfb4e7d0422e80abe95ff OP_CHECKSIG OP_ENDIF",
207 | code: "IfElseScript(MultisigScript(2, PublicKey.unhexlify(\"0384478d41e71dc6c3f9edde0f928a47d1b724c05984ebfb4e7d0422e80abe95ff\"), PublicKey.unhexlify(\"03eb27fa93667e4f48e36071eb21c7229e5416ff0abd2886d59c8f314fb3cbee40\"), 2), AbsoluteTimelockScript(Locktime(1087355), P2pkScript(PublicKey.unhexlify(\"0384478d41e71dc6c3f9edde0f928a47d1b724c05984ebfb4e7d0422e80abe95ff\"))))",
208 | type: "if{ multisig }else{ [timelock] p2pk }"
209 | },
210 | relativetimelock: {
211 | hex: "635221021b98b2e4ba9dae9f869bcf948c45df6b6f8e6bb623915cf144237f5e6ab98cf4210376d53363bbeefed905fc685e4d4e1fe0cbf9959e8f59e9f5f209f489b3a6285752ae6755b275210301bf316386b5b09abe8f71cc68bf7ab62bc9f511b7c13fe0febd75e3ac5ce855ac68",
212 | asm: "OP_IF OP_2 021b98b2e4ba9dae9f869bcf948c45df6b6f8e6bb623915cf144237f5e6ab98cf4 0376d53363bbeefed905fc685e4d4e1fe0cbf9959e8f59e9f5f209f489b3a62857 OP_2 OP_CHECKMULTISIG OP_ELSE OP_5 OP_CHECKSEQUENCEVERIFY OP_DROP 0301bf316386b5b09abe8f71cc68bf7ab62bc9f511b7c13fe0febd75e3ac5ce855 OP_CHECKSIG OP_ENDIF",
213 | code: "IfElseScript(MultisigScript(2, PublicKey.unhexlify(\"021b98b2e4ba9dae9f869bcf948c45df6b6f8e6bb623915cf144237f5e6ab98cf4\"), PublicKey.unhexlify(\"0376d53363bbeefed905fc685e4d4e1fe0cbf9959e8f59e9f5f209f489b3a62857\"), 2),RelativeTimelockScript(Sequence(5), P2pkScript(PublicKey.unhexlify(\"0301bf316386b5b09abe8f71cc68bf7ab62bc9f511b7c13fe0febd75e3ac5ce855\"))))",
214 | type: "if{ multisig }else{ [relativetimelock] p2pk }"
215 | },
216 | multisig: {
217 | hex: "522102c08786d63f78bd0a6777ffe9c978cf5899756cfc32bfad09a89e211aeb92624221033e81519ecf373ea3a5c7e1c051b71a898fb3438c9550e274d980f147eb4d069d21036d568125a969dc78b963b494fa7ed5f20ee9c2f2fc2c57f86c5df63089f2ed3a53ae",
218 | asm: "OP_2 02c08786d63f78bd0a6777ffe9c978cf5899756cfc32bfad09a89e211aeb926242 033e81519ecf373ea3a5c7e1c051b71a898fb3438c9550e274d980f147eb4d069d 036d568125a969dc78b963b494fa7ed5f20ee9c2f2fc2c57f86c5df63089f2ed3a OP_3 OP_CHECKMULTISIG",
219 | code: "MultisigScript(2, PublicKey.unhexlify(\"02c08786d63f78bd0a6777ffe9c978cf5899756cfc32bfad09a89e211aeb926242\"), PublicKey.unhexlify(\"033e81519ecf373ea3a5c7e1c051b71a898fb3438c9550e274d980f147eb4d069d\"), PublicKey.unhexlify(\"036d568125a969dc78b963b494fa7ed5f20ee9c2f2fc2c57f86c5df63089f2ed3a\"), 3)",
220 | type: "multisig"
221 | },
222 | nulldata: {
223 | hex: "6a28444f4350524f4f463832bd18ceb0a7861f2a8198013047a3fb861261523c0fc4164abc044e517702",
224 | asm: "OP_RETURN 444f4350524f4f463832bd18ceb0a7861f2a8198013047a3fb861261523c0fc4164abc044e517702",
225 | code: "NulldataScript(StackData.unhexlify(\"444f4350524f4f463832bd18ceb0a7861f2a8198013047a3fb861261523c0fc4164abc044e517702\"))",
226 | type: "nulldata"
227 | },
228 | p2wpkh: {
229 | hex: "0014f81b6a6cfaaf19dbd9e56b9cab2d8a457608ad8e",
230 | asm: "OP_0 f81b6a6cfaaf19dbd9e56b9cab2d8a457608ad8e",
231 | code: "P2wpkhV0Script(bytearray(unhexlify(\"f81b6a6cfaaf19dbd9e56b9cab2d8a457608ad8e\")))",
232 | type: "p2wpkhv0"
233 | },
234 | p2wsh: {
235 | hex: "0020cdbf909e935c855d3e8d1b61aeb9c5e3c03ae8021b286839b1a72f2e48fdba70",
236 | asm: "OP_0 cdbf909e935c855d3e8d1b61aeb9c5e3c03ae8021b286839b1a72f2e48fdba70",
237 | code: "P2wshV0Script(bytearray(unhexlify(\"cdbf909e935c855d3e8d1b61aeb9c5e3c03ae8021b286839b1a72f2e48fdba70\")))",
238 | type: "p2wshv0"
239 | }
240 | }
241 | };
242 |
--------------------------------------------------------------------------------
/test/segwit.js:
--------------------------------------------------------------------------------
1 | const scripts = require("../lib/scripts");
2 | const crypto = require("../lib/crypto");
3 | module.exports = {
4 | txs_data: [
5 | {
6 | unsigned_tx:
7 | "0100000002fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f" +
8 | "0000000000eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57" +
9 | "b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85" +
10 | "c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2" +
11 | "f0167faa815988ac11000000",
12 | hash_prevouts: "96b827c8483d4e9b96712b6713a7b68d6e8003a781feba36c31143470b4efd37",
13 | hash_sequence: "52b0a642eea2fb7ae638c36f6252b6750293dbe574a806984b8e4d8548339a3b",
14 | hash_outputs: "863ef3e1a92afbfdb97f31ad0fc7683ee943e9abcf2501590ff8f6551f47e5e5",
15 | hash_preimage:
16 | "0100000096b827c8483d4e9b96712b6713a7b68d6e8003a781feba36c31143470b4efd37" +
17 | "52b0a642eea2fb7ae638c36f6252b6750293dbe574a806984b8e4d8548339a3bef51e1b8" +
18 | "04cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a010000001976a914" +
19 | "1d0f172a0ecb48aee1be1f2687d2963ae33f71a188ac0046c32300000000ffffffff863e" +
20 | "f3e1a92afbfdb97f31ad0fc7683ee943e9abcf2501590ff8f6551f47e5e5110000000100" +
21 | "0000",
22 | sighash: "c37af31116d1b27caf68aae9e3ac82f1477929014d5b917657d0eb49478cb670",
23 | signature:
24 | "304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a02205" +
25 | "73a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee",
26 | txins: [
27 | {
28 | prev_script: scripts.P2pkhScript.fromHex(
29 | "2103c9f4836b9a4f77fc0d81f7bcb01b7f1b35916864b9476c241ce9fc198bd25432ac"
30 | ),
31 | prev_amount: 625000000
32 | },
33 | {
34 | prev_script: scripts.P2wpkhV0Script.fromHex(
35 | "00141d0f172a0ecb48aee1be1f2687d2963ae33f71a1"
36 | ),
37 | prev_amount: 600000000,
38 | digest_preimage:
39 | "0100000096b827c8483d4e9b96712b6713a7b68d6e8003a781feba36c31" +
40 | "143470b4efd3752b0a642eea2fb7ae638c36f6252b6750293dbe574a806" +
41 | "984b8e4d8548339a3bef51e1b804cc89d182d279655c3aa89e815b1b309" +
42 | "fe287d9b2b55d57b90ec68a010000001976a9141d0f172a0ecb48aee1be" +
43 | "1f2687d2963ae33f71a188ac0046c32300000000ffffffff863ef3e1a92" +
44 | "afbfdb97f31ad0fc7683ee943e9abcf2501590ff8f6551f47e5e5110000" +
45 | "0001000000",
46 | digest: "c37af31116d1b27caf68aae9e3ac82f1477929014d5b917657d0eb49478cb670",
47 | privk: crypto.Privatekey.fromHex(
48 | "619c335025c7f4012e556c2a58b2506e30b8511b53ade95e" + "a316fd8c3286feb9"
49 | )
50 | }
51 | ],
52 | txid: "3335ffae0df20c5407e8de12b49405c8e912371f00fe4132bfaf95ad49c40243"
53 | },
54 | {
55 | unsigned_tx:
56 | "0100000001db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a5477" +
57 | "0100000000feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bbc043f3" +
58 | "5b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fea7ad0402e8bd8ad6" +
59 | "d77c88ac92040000",
60 | hash_prevouts: "b0287b4a252ac05af83d2dcef00ba313af78a3e9c329afa216eb3aa2a7b4613a",
61 | hash_sequence: "18606b350cd8bf565266bc352f0caddcf01e8fa789dd8a15386327cf8cabe198",
62 | hash_outputs: "de984f44532e2173ca0d64314fcefe6d30da6f8cf27bafa706da61df8a226c83",
63 | hash_preimage:
64 | "01000000b0287b4a252ac05af83d2dcef00ba313af78a3e9c329afa216eb3aa2a7b4613a" +
65 | "18606b350cd8bf565266bc352f0caddcf01e8fa789dd8a15386327cf8cabe198db6b1b20" +
66 | "aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a5477010000001976a914" +
67 | "79091972186c449eb1ded22b78e40d009bdf008988ac00ca9a3b00000000feffffffde98" +
68 | "4f44532e2173ca0d64314fcefe6d30da6f8cf27bafa706da61df8a226c83920400000100" +
69 | "0000",
70 | sighash: "64f3b0f4dd2bb3aa1ce8566d220cc74dda9df97d8490cc81d89d735c92e59fb6",
71 | signature:
72 | "3044022047ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f0220" +
73 | "217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb",
74 | txins: [
75 | {
76 | prev_script: scripts.P2wpkhV0Script.fromHex(
77 | "001479091972186c449eb1ded22b78e40d009bdf0089"
78 | ),
79 | prev_amount: 1000000000,
80 | digest_preimage:
81 | "01000000b0287b4a252ac05af83d2dcef00ba313af78a3e9c329afa216e" +
82 | "b3aa2a7b4613a18606b350cd8bf565266bc352f0caddcf01e8fa789dd8a" +
83 | "15386327cf8cabe198db6b1b20aa0fd7b23880be2ecbd4a98130974cf47" +
84 | "48fb66092ac4d3ceb1a5477010000001976a91479091972186c449eb1de" +
85 | "d22b78e40d009bdf008988ac00ca9a3b00000000feffffffde984f44532" +
86 | "e2173ca0d64314fcefe6d30da6f8cf27bafa706da61df8a226c83920400" +
87 | "0001000000",
88 | digest: "64f3b0f4dd2bb3aa1ce8566d220cc74dda9df97d8490cc81d89d735c92e59fb6",
89 | privk: crypto.Privatekey.fromHex(
90 | "eb696a065ef48a2192da5b28b694f87544b30fae8327c451" + "0137a922f32c6dcf"
91 | )
92 | }
93 | ],
94 | txid: "321a59707939041eeb0d524f34432c0c46ca3920f0964e6c23697581f176b6c0"
95 | }
96 | ],
97 | addresses: {
98 | p2wpkh: [
99 | {
100 | bech32: "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4",
101 | hash: "751e76e8199196d454941c45d1b3a323f1433bd6"
102 | }
103 | ],
104 | p2wsh: [
105 | {
106 | bech32: "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy",
107 | hash: "000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433"
108 | },
109 | {
110 | bech32: "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7",
111 | hash: "1863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262"
112 | }
113 | ]
114 | }
115 | };
116 |
--------------------------------------------------------------------------------
/test/signatures.js:
--------------------------------------------------------------------------------
1 | const scripts = require("../lib/scripts");
2 |
3 | module.exports = {
4 | digests: [
5 | {
6 | unsigned_tx:
7 | "02000000019379fdc32c4c9d4f14cf43bf89a4e64365e4e713e19d7154989f84b" +
8 | "8d42bbb650100000000ffffffff0180f0fa02000000001976a914fec8ecfbb43b" +
9 | "e3b0ed143feafe8a79860ac512b288ac00000000",
10 | txins: [
11 | {
12 | prev_script: scripts.P2pkhScript.fromHex(
13 | "76a914534f5b2c28ac08363dab4d4dfd937e36810b05dd88ac"
14 | ),
15 | digest_preimage:
16 | "02000000019379fdc32c4c9d4f14cf43bf89a4e64365e4e713e19d7154989" +
17 | "f84b8d42bbb65010000001976a914534f5b2c28ac08363dab4d4dfd937e36" +
18 | "810b05dd88acffffffff0180f0fa02000000001976a914fec8ecfbb43be3b" +
19 | "0ed143feafe8a79860ac512b288ac0000000001000000",
20 | digest: "666493e1386741fa42db54a3968a51b6243b1edd1a980b617d8fbce296132cd0",
21 | priv_wif: "cPXc7uzzsS5GKmRx7c6AMkwBWmj3LTPNAEeyrARLbVBaqXrrRLBK"
22 | }
23 | ]
24 | },
25 | {
26 | unsigned_tx:
27 | "02000000018eb50f296c02578b3908584faefc739da78579c3667e43a80233abd4" +
28 | "d3454e4f0000000000ffffffff01002d3101000000001976a914534f5b2c28ac08" +
29 | "363dab4d4dfd937e36810b05dd88ac00000000",
30 | txins: [
31 | {
32 | prev_script: scripts.P2pkhScript.fromHex(
33 | "76a914fec8ecfbb43be3b0ed143feafe8a79860ac512b288ac"
34 | ),
35 | digest_preimage:
36 | "02000000018eb50f296c02578b3908584faefc739da78579c3667e43a8023" +
37 | "3abd4d3454e4f000000001976a914fec8ecfbb43be3b0ed143feafe8a7986" +
38 | "0ac512b288acffffffff01002d3101000000001976a914534f5b2c28ac083" +
39 | "63dab4d4dfd937e36810b05dd88ac0000000001000000",
40 | digest: "9e8b4eb4adad1c8ff31679150a092cfdb9ddec068a2e1c616cacea8f5944170e",
41 | priv_wif: "cW3YGYL49CpzxjrnZz1jUuEnaAuG4hEgU7oHQTKBNsqcZ584jVmF"
42 | }
43 | ]
44 | }
45 | ],
46 | signatures: [
47 | {
48 | key: "b1060bed3ce69fbc7f15c129c70c98fdc19885d042883b2601a53d6b90786a56",
49 | message: "aabbcc",
50 | signature:
51 | "30440220057dc4d850be7815d672b02b138de2c9e1ed71e165f2063400f3aa67ed50" +
52 | "ad5102206d8b4bf06ba9a4282f960443a1c041950579dc9f279560f2c566c479be6f" +
53 | "9a9a"
54 | },
55 | {
56 | key: "b1060bed3ce69fbc7f15c129c70c98fdc19885d042883b2601a53d6b90786a56",
57 | message: "0ab1ce148c65faff0019",
58 | signature:
59 | "3045022100c2337de29df19f69f5707c87916aa473467aa67645265ee62f8fb04e8c" +
60 | "ac60ad02205f2fbb1104bf604f250893b62543416b5d9d261aadd1869a38029c6ccd" +
61 | "806377"
62 | },
63 | {
64 | key: "b1060bed3ce69fbc7f15c129c70c98fdc19885d042883b2601a53d6b90786a56",
65 | message: "c37af31116d1b27caf68aae9e3ac82f1477929014d5b917657d0eb49478cb670",
66 | signature:
67 | "3044022022313b172c5249a3b054712e1edcc41d094779982ef406d66685a5fbebe3c" +
68 | "5e802202e94281fcd73f121c98a908b53e5515417949afcb23f1d48b2a28263a1ee53" +
69 | "14"
70 | },
71 | {
72 | key: "566bc51059b577de9c32bba4d5b3cba715f155ce870e01b5409f89ef5a5f70ff",
73 | message: "64f3b0f4dd2bb3aa1ce8566d220cc74dda9df97d8490cc81d89d735c92e59fb6",
74 | signature:
75 | "3044022002828d6eca86405888f00434af1958eb3883a7048c47dac78977ea666c651" +
76 | "b27022015cf5d763c8dea65e68ddc4a2c82076ca8be19ff4ce6874c9cfa927ec87585" +
77 | "99"
78 | },
79 | {
80 | key: "566bc51059b577de9c32bba4d5b3cba715f155ce870e01b5409f89ef5a5f70ff",
81 | message: "a4f3b0f4652bb32d1cefa56d320cc74d7a9dff7d8490ccd1d896c35c92e59fb6",
82 | signature:
83 | "3045022100e159f775573c8d02d2acc3449bf64b504b81b8d06a94f6d342ec1085627a" +
84 | "63e70220235395623f203132e2ce36e8ea91afd7ee283c5d7d38efb8be03558f51aaf3" +
85 | "60"
86 | }
87 | ]
88 | };
89 |
--------------------------------------------------------------------------------
/test/wif.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | keys: [
3 | {
4 | wif: "5KAFQsA5qtk3RNXAfph4KXWkqAXDx6f91sSyqyjuq6KUfWsbky3",
5 | hex: "b1060bed3ce69fbc7f15c129c70c98fdc19885d042883b2601a53d6b90786a56"
6 | },
7 | {
8 | wif: "5HyrgcD3RBX5iKLDGFbiBFN899rkYSi6obLKL2yn1n1nvqQWdV4",
9 | hex: "15bb440ffce8cf660f2f9f4130bb5a5847e1a1a0e3f2f15c64bc91ea7d5783de"
10 | },
11 | {
12 | wif: "5JUM68oq1SEoYWXauRXjPzFykxbWWBxQ2w3YdKH3zmn2gbeNCwY",
13 | hex: "566bc51059b577de9c32bba4d5b3cba715f155ce870e01b5409f89ef5a5f70ff"
14 | },
15 | {
16 | wif: "5J1CkkKSsYqyrhZesUzChnjDnkhRRkDcWgh2H1jKeaMqTVYdt6V",
17 | hex: "18c9b3a095d99cd10899f1d7ec6a4f52cd11ef1c2ec29a806c7267c67589b60f"
18 | },
19 | {
20 | wif: "5K1pukTRmcgvQT2e8KY5b27LDYhcdUTfqdXTFwexHoa4Mh6TMwN",
21 | hex: "9de6406e7afc24f51bb2c0a4d690732790b12563e2cb58be29dbcc58ea3a7be6"
22 | },
23 | {
24 | wif: "5KXLxiahr9aFv3wCfePY2r4KJdAmqgXHLEWRG8B7b5cngWumk7N",
25 | hex: "e0ec86c2226068d46426159058ada533cca9e4983d559afbee3f91927e63fc3f"
26 | },
27 | {
28 | wif: "5JwPxhgszsVCHoKvVKaukbaccEJa6qyiyjeLwnTdbptydA7UgVX",
29 | hex: "93d717d6e0ee8526bad83dcdea1284b3c1ae632b6cb87aade1879d3526b4eb07"
30 | },
31 | {
32 | wif: "5JuMEY3YpMN4rJey3cwBq6pgXeZ4KjLz6Qm1Wm57mWGY2HmVvNG",
33 | hex: "8f31344c72474f4a6ea78bc218d53764547306728a09ca7f38c8f5cd5ecc5e3a"
34 | },
35 | {
36 | wif: "5KQeMRyaveNTjj5iqj3f4eecUL1FqUEBUiVYpL1daz8v1F5VoXY",
37 | hex: "d1b5def6305709e5c783546f459ffedef3350e16c3a1916b5cdda376243bc31e"
38 | },
39 | {
40 | wif: "5KaFQHYWgiuN84F6EfHByvUkjrgVqG5k1Ahv8YM7W5LCTKPrUNm",
41 | hex: "e784a66a744e0fe38784091315b4729fd6b3cdb63d44d9d8c6df18d0ab4cf63f"
42 | }
43 | ]
44 | };
45 |
--------------------------------------------------------------------------------
/tools/bytebuffer.js:
--------------------------------------------------------------------------------
1 | const ByteBuffer = require("bytebuffer");
2 | const $ = require("./conversions");
3 |
4 | ByteBuffer.prototype.writeVarint = function (int, offset = undefined) {
5 | if (0 < int && int < 0xfd) {
6 | return {value: int, length: 1};
7 | } else if (0xfd < int && int <= 0xffff) {
8 | return {
9 | value: parseInt("0xFD" + $.bytesToHex($.numToBytes(int, 2)), 16),
10 | length: 2
11 | };
12 | } else if (0xffff < int && int <= 0xffffffff) {
13 | return {
14 | value: parseInt("0xFE" + $.bytesToHex($.numToBytes(int, 4)), 16),
15 | length: 4
16 | };
17 | } else if (0xffffffff < int && int <= 0xffffffffffffffff) {
18 | return {
19 | value: parseInt("0xFF" + $.bytesToHex($.numToBytes(int, 8)), 16),
20 | length: 8
21 | };
22 | } else {
23 | throw ("Wrong value for varint: ", int.toString("hex"));
24 | }
25 | };
26 |
27 | ByteBuffer.prototype.parseVarint = function (offset = undefined) {
28 | const header = parseInt(this.toHex(offset, offset + 1), 16);
29 |
30 | if (0 <= header && header < 0xfd) {
31 | return {
32 | value: header,
33 | length: 1
34 | };
35 | } else if (header === 0xfd) {
36 |
37 | return {
38 | value: parseInt(this.toHex(offset + 1, offset + 3)),
39 | length: 2
40 | };
41 | } else if (header === 0xfe) {
42 |
43 | return {
44 | value: parseInt(this.toHex(offset + 1, offset + 5)),
45 | length: 4
46 | };
47 | } else if (header === 0xff) {
48 |
49 | return {
50 | value: parseInt(this.toHex(offset + 1, offset + 9)),
51 | length: 8
52 | };
53 | } else throw ("Wrong header for varint: " + header);
54 | };
55 |
--------------------------------------------------------------------------------
/tools/conversions.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const BN = require("bn.js");
3 | const _ = require("lodash");
4 |
5 | function swapHex(value) {
6 | let s = value.toString(16);
7 | s = s.replace(/^(.(..)*)$/, "0$1");
8 | var a = s.match(/../g);
9 | a.reverse();
10 | var s2 = a.join("");
11 | return s2;
12 | }
13 |
14 | function numToBytes(num, bytes) {
15 | if (bytes === undefined) bytes = 8;
16 | if (bytes === 0) return [];
17 | else return [num % 256].concat(numToBytes(Math.floor(num / 256), bytes - 1));
18 | }
19 |
20 | function numToVarInt(num) {
21 | if (num < 253) return [num];
22 | else if (num < 65536) return [253].concat(numToBytes(num, 2));
23 | else if (num < 4294967296) return [254].concat(numToBytes(num, 4));
24 | else return [253].concat(numToBytes(num, 8));
25 | }
26 |
27 | function numToHex(num) {
28 | return bytesToHex(numToBytes(num));
29 | }
30 |
31 | function hexToBytes(hex) {
32 | for (var bytes = [], c = 0; c < hex.length; c += 2)
33 | bytes.push(parseInt(hex.substr(c, 2), 16));
34 | return bytes;
35 | }
36 |
37 | function bytesToHex(bytes) {
38 | for (var hex = [], i = 0; i < bytes.length; i++) {
39 | hex.push((bytes[i] >>> 4).toString(16));
40 | hex.push((bytes[i] & 0xf).toString(16));
41 | }
42 | return hex.join("");
43 | }
44 |
45 | function bytesLen(num) {
46 | return Math.ceil(num.toString(2).length / 8);
47 | }
48 |
49 |
50 | function hexToBinary(hex) {
51 | return _.map(hex.match(/.{2}/g), chunk => hexByteToBinary(chunk)).join("");
52 |
53 | }
54 |
55 | function hexByteToBinary(hexByte) {
56 | return ("00000000" + (parseInt(hexByte, 16)).toString(2)).substr(-8);
57 | }
58 |
59 | function bnmodexp(a, b, n) {
60 | if (!BN.isBN(a)) a = new BN(a);
61 | if (!BN.isBN(b)) b = new BN(b);
62 | if (!BN.isBN(n)) n = new BN(n);
63 | a = a.mod(n);
64 | let result = new BN(1);
65 | let x = a;
66 | while (!b.isZero()) {
67 | let leastSignificantBit = b.mod(new BN(2));
68 | b = b.div(new BN(2));
69 | if (leastSignificantBit.eq(new BN(1))) {
70 | result = result.mul(x).mod(n);
71 | }
72 | x = x.mul(x).mod(n);
73 | }
74 | return result;
75 | }
76 |
77 | module.exports = {
78 | swapHex,
79 | numToBytes,
80 | numToVarInt,
81 | hexToBytes,
82 | bytesToHex,
83 | bnmodexp,
84 | bytesLen,
85 | hexToBinary,
86 | numToHex
87 | };
88 |
--------------------------------------------------------------------------------