├── .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 | chainside 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 | --------------------------------------------------------------------------------