├── .gitignore ├── .groc.json ├── .travis.yml ├── LICENSE ├── README.md ├── examples ├── blockreader.js ├── ecdsa.js └── stealthmessage.js ├── index.js ├── lib ├── address.js ├── base58.js ├── base58check.js ├── bip32.js ├── bip39.js ├── block.js ├── blockheader.js ├── bn.js ├── bsm.js ├── bufr.js ├── bufw.js ├── constants.js ├── ecdsa.js ├── expmt │ ├── aes.js │ ├── aescbc.js │ ├── cbc.js │ ├── ecies.js │ └── stealth │ │ ├── address.js │ │ ├── key.js │ │ ├── message.js │ │ └── tx.js ├── hash.js ├── interp.js ├── kdf.js ├── keypair.js ├── opcode.js ├── point.js ├── privkey.js ├── pubkey.js ├── random.js ├── script.js ├── sig.js ├── tx.js ├── txbuilder.js ├── txin.js ├── txout.js └── varint.js ├── npm-shrinkwrap.json ├── package.json └── test ├── address.js ├── base58.js ├── base58check.js ├── bip32.js ├── bip39.js ├── block.js ├── blockheader.js ├── bn.js ├── bsm.js ├── bufr.js ├── bufw.js ├── ecdsa.js ├── examples.js ├── expmt ├── aes.js ├── aescbc.js ├── cbc.js ├── ecies.js └── stealth │ ├── address.js │ ├── key.js │ ├── message.js │ └── tx.js ├── hash.js ├── index.html ├── interp.js ├── kdf.js ├── keypair.js ├── opcode.js ├── point.js ├── privkey.js ├── pubkey.js ├── random.js ├── script.js ├── sig.js ├── tx.js ├── txbuilder.js ├── txin.js ├── txout.js ├── varint.js └── vectors ├── bip39.json ├── bitcoind ├── script_invalid.json ├── script_valid.json ├── sighash.json ├── tx_invalid.json └── tx_valid.json ├── ecdsa.json └── kdf.json /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | coverage 3 | node_modules 4 | browser/fullnode.js 5 | browser/fullnode-min.js 6 | browser/tests.js 7 | npm-debug.log 8 | doc 9 | -------------------------------------------------------------------------------- /.groc.json: -------------------------------------------------------------------------------- 1 | { 2 | "glob": ["README.md", "lib/*.js", "lib/**/*.js", "lib/**/**/*.js", "examples/*.js"] 3 | } 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This software is licensed under the MIT License. 2 | 3 | Copyright (c) 2014 reddit, Inc. 4 | Copyright (c) 2014 Ryan X. Charles 5 | 6 | Parts of this software are based on bitcore 7 | Copyright (c) 2014 BitPay Inc. 8 | 9 | Parts of this software are based on BitcoinJS 10 | Copyright (c) 2011-2014 Bitcoinjs-lib contributors 11 | 12 | Parts of this software are based on Bitcoin Core 13 | Copyright (c) 2009-2014 Bitcoin Developers 14 | 15 | Parts of this software are based on BitcoinJ 16 | Copyright (c) 2011 Google Inc. 17 | 18 | Permission is hereby granted, free of charge, to any person obtaining a copy 19 | of this software and associated documentation files (the "Software"), to deal 20 | in the Software without restriction, including without limitation the rights 21 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 22 | copies of the Software, and to permit persons to whom the Software is 23 | furnished to do so, subject to the following conditions: 24 | 25 | The above copyright notice and this permission notice shall be included in 26 | all copies or substantial portions of the Software. 27 | 28 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 29 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 30 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 31 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 32 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 33 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 34 | THE SOFTWARE. 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | fullnode (alpha) 2 | ================ 3 | 4 | fullnode is a javascript implementation of bitcoin intended to satisfy certain 5 | goals: 6 | 7 | 1. Support ease-of-use by being internally consistent. It should not be 8 | necessary to read the source code of a class or function to know how to use it. 9 | 10 | 2. Have 100% test coverage, or nearly so, so that the library is known to be 11 | reliable. This should include running standard test vectors from bitcoin core. 12 | 13 | 3. Library objects have an interface suitable for use with a command-line 14 | interface and API, in particular having toString, fromString, toJSON, fromJSON, 15 | methods. Other common methods are toBuffer, fromBuffer relevant for binary 16 | formats such as transactions and blocks. 17 | 18 | 4. All standard features of the bitcoin protocol are implemented and saved in 19 | lib/. All BIPs are correctly implemented and, where appropriate, saved as 20 | bipxx.js in lib/ (since that is their standard name). Any non-standard features 21 | (such as colored coins or stealth addresses) are placed in the lib/expmt/ 22 | folder and are accessible at fullnode.expmt. Once they are standardized and 23 | given a BIP, they are renamed and placed in lib/. 24 | 25 | 5. Expose everything, including dependencies. This makes it possible to develop 26 | apps that require fine-grained control over the basics, such as big numbers and 27 | points. However, it also means that you can hurt yourself if you misuse these 28 | primitives. 29 | 30 | 6. It is always possible to create a new object without using "new". 31 | 32 | 7. Compatible with browserify (i.e., using require('fullnode/lib/message') 33 | should work both in node, and be automatically work in the browser with used in 34 | conjunction with browserify). 35 | 36 | 8. Minimize the use of dependencies so that all code can be easily audited. 37 | 38 | 9. All instance methods modify the state of the object and return the object, 39 | unless there is a good reason to do something different. To access the result 40 | of an instance method, you must access the object property(s) that it modifies. 41 | 42 | ## Alpha Caveat ## 43 | 44 | fullnode is still alpha, and has an unstable API. Once the code has been 45 | audited, the API will be settled, and version 1.0 will be released. It is 46 | recommended not to use fullnode for production software until that time. 47 | 48 | ## Notable Features ## 49 | 50 | * 90%+ test coverage 51 | * Browserifiable 52 | * Stealth keys, addresses, message 53 | * Bitcoin-style message signing and verification 54 | * Exposed big number and point classes 55 | * Deterministic k (deterministic signatures; RFC 6979) 56 | * Script interpreter, validated against bitcoind tests 57 | 58 | ## Documentation ## 59 | 60 | ``` 61 | npm install -g groc 62 | groc 63 | ``` 64 | 65 | ## Browser bundle ## 66 | 67 | ``` 68 | npm install -g browserify 69 | npm install -g uglifyify 70 | npm run build 71 | ``` 72 | ## Testing and Coverage ## 73 | 74 | ``` 75 | npm install -g mocha 76 | npm install -g istanbul 77 | npm test 78 | ``` 79 | 80 | You can see the coverage report at coverage/lcov-report/index.html. 81 | 82 | Or, to build tests for the browser: 83 | 84 | ``` 85 | npm run build 86 | ``` 87 | 88 | Then open test/index.html in your browser. 89 | -------------------------------------------------------------------------------- /examples/blockreader.js: -------------------------------------------------------------------------------- 1 | var Block = require('../lib/block'); 2 | var BufR = require('../lib/bufr'); 3 | var BufW = require('../lib/bufw'); 4 | 5 | //This example will parse the blocks in a block file. 6 | //To use, pipe in a blk*****.dat file. e.g.: 7 | //cat blk00000.dat | node blockreader.js 8 | 9 | var head = null; 10 | 11 | process.stdin.on('readable', function() { 12 | if (!head) { 13 | head = process.stdin.read(8); 14 | if (!head) 15 | return; 16 | } 17 | var body = process.stdin.read(head.slice(4).readUInt32LE(0)); 18 | if (!body) 19 | return; 20 | var blockbuf = BufW().write(head).write(body).concat(); 21 | var block = Block().fromBuffer(blockbuf); 22 | console.log(block.toJSON()); 23 | head = null; 24 | process.stdin.unshift(process.stdin.read()); 25 | }); 26 | -------------------------------------------------------------------------------- /examples/ecdsa.js: -------------------------------------------------------------------------------- 1 | var ECDSA = require('../lib/ecdsa'); 2 | var Keypair = require('../lib/keypair'); 3 | var Hash = require('../lib/hash'); 4 | 5 | //ECDSA is the signature algorithm used in bitcoin 6 | 7 | //start with a keypair that you will use for signing 8 | var keypair = Keypair().fromRandom(); 9 | 10 | //a message to be signed (normally you would have the hash of a transaction) 11 | var messagebuf = new Buffer('This is a message I would like to sign'); 12 | 13 | //calculate a 32 byte hash for use in ECDSA. one way to do that is sha256. 14 | var hashbuf = Hash.sha256(messagebuf); 15 | 16 | var sig = ECDSA.sign(hashbuf, keypair); 17 | 18 | //Anyone with the public key can verify 19 | var pubkey = keypair.pubkey; 20 | console.log('Valid signature? ' + ECDSA.verify(hashbuf, sig, pubkey)); 21 | 22 | -------------------------------------------------------------------------------- /examples/stealthmessage.js: -------------------------------------------------------------------------------- 1 | var Pubkey = require('../lib/pubkey'); 2 | var Address = require('../lib/address'); 3 | var SKey = require('../lib/expmt/stealth/key'); 4 | var SAddress = require('../lib/expmt/stealth/address'); 5 | var SMessage = require('../lib/expmt/stealth/message'); 6 | var Keypair = require('../lib/keypair') 7 | 8 | //First, the person receiving must make a stealth key. 9 | 10 | var sk = SKey().fromRandom(); 11 | 12 | //It has an associated stealth address. 13 | 14 | var sa = SAddress().fromSKey(sk); 15 | 16 | console.log('Stealth address: ' + sa); 17 | 18 | //Now make a message. 19 | 20 | var messagebuf = new Buffer('Hello there. Only you know this message is to you, and only you know what it says.'); 21 | 22 | //Encrypt the message with the stealth address. 23 | 24 | var encbuf = SMessage.encrypt(messagebuf, sa); 25 | 26 | console.log('Hex of the encrypted message: ' + encbuf.toString('hex')); 27 | 28 | //Note that the first 20 bytes are a pubkeyhash, which may be interpreted as a bitcoin address. 29 | //This address has never been seen before in public. 30 | 31 | var address = Address().set({hashbuf: encbuf.slice(0, 20)}); 32 | 33 | console.log('The randomly generated address the message is to: ' + address); 34 | 35 | //And the next 33 bytes are a nonce public key, which the message is "from". 36 | //It has never been seen before in public. 37 | 38 | var pubkey = Pubkey().fromDER(encbuf.slice(20, 20 + 33)); 39 | 40 | console.log('Nonce public key: ' + pubkey); 41 | 42 | //The owner of the stealth key can check to see if it is for them. 43 | 44 | console.log('Is the message for me? ' + (SMessage.isForMe(encbuf, sk) ? "yes" : "no")); 45 | 46 | //The owner can decrypt it. 47 | 48 | var messagebuf2 = SMessage.decrypt(encbuf, sk); 49 | 50 | console.log('Decrypted message: ' + messagebuf2.toString()); 51 | 52 | //If you do not have the payload privkey, you can still use isForMe. 53 | sk.payloadKeypair.privkey = undefined; 54 | 55 | console.log('Without payload privkey, is the message for me? ' + (SMessage.isForMe(encbuf, sk) ? "yes" : "no")); 56 | 57 | //...but not decrypt 58 | 59 | try { 60 | SMessage.decrypt(encbuf, sk); 61 | } catch (e) { 62 | console.log("...but without the payload privkey, I can't decrypt."); 63 | } 64 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * An example of how to build a bundle with fullnode. This bundle includes the 3 | * entire library. It is not typically recommended that you use fullnode this 4 | * way, since you probably do not use every component, and therefore do not 5 | * need to include every component into your project. You should simply 6 | * directly require the elements of the library you need, and browserify your 7 | * project. For instance, require('fullnode/lib/address'). 8 | */ 9 | var fullnode = module.exports; 10 | 11 | fullnode.version = require('./package').version; 12 | 13 | //main bitcoin library - bitcoin protocols and standards 14 | fullnode.Address = require('./lib/address'); 15 | fullnode.Base58 = require('./lib/base58'); 16 | fullnode.Base58Check = require('./lib/base58check'); 17 | fullnode.BIP32 = require('./lib/bip32'); 18 | fullnode.BIP39 = require('./lib/bip39'); 19 | fullnode.Block = require('./lib/block'); 20 | fullnode.Blockheader = require('./lib/blockheader'); 21 | fullnode.BN = require('./lib/bn'); 22 | fullnode.BSM = require('./lib/bsm'); 23 | fullnode.BufR = require('./lib/bufr'); 24 | fullnode.BufW = require('./lib/bufw'); 25 | fullnode.Constants = require('./lib/constants'); 26 | fullnode.ECDSA = require('./lib/ecdsa'); 27 | fullnode.Hash = require('./lib/hash'); 28 | fullnode.Interp = require('./lib/interp'); 29 | fullnode.KDF = require('./lib/kdf'); 30 | fullnode.Keypair = require('./lib/keypair'); 31 | fullnode.Opcode = require('./lib/opcode'); 32 | fullnode.Point = require('./lib/point'); 33 | fullnode.Privkey = require('./lib/privkey'); 34 | fullnode.Pubkey = require('./lib/pubkey'); 35 | fullnode.Random = require('./lib/random'); 36 | fullnode.Script = require('./lib/script'); 37 | fullnode.Sig = require('./lib/sig'); 38 | fullnode.Tx = require('./lib/tx'); 39 | fullnode.Txin = require('./lib/txin'); 40 | fullnode.Txout = require('./lib/txout'); 41 | fullnode.Varint = require('./lib/varint'); 42 | 43 | //experimental, nonstandard, or unstable features 44 | fullnode.expmt = {}; 45 | fullnode.expmt.AES = require('./lib/expmt/aes'); 46 | fullnode.expmt.AESCBC = require('./lib/expmt/aescbc'); 47 | fullnode.expmt.CBC = require('./lib/expmt/cbc'); 48 | fullnode.expmt.ECIES = require('./lib/expmt/ecies'); 49 | 50 | //experimental stealth address support 51 | fullnode.expmt.stealth = {}; 52 | fullnode.expmt.stealth.SAddress = require('./lib/expmt/stealth/address'); 53 | fullnode.expmt.stealth.SKey = require('./lib/expmt/stealth/key'); 54 | fullnode.expmt.stealth.SMessage = require('./lib/expmt/stealth/message'); 55 | fullnode.expmt.stealth.STx = require('./lib/expmt/stealth/tx'); 56 | 57 | //dependencies, subject to change 58 | fullnode.deps = {}; 59 | fullnode.deps.aes = require('aes'); 60 | fullnode.deps.bnjs = require('bn.js'); 61 | fullnode.deps.bs58 = require('bs58'); 62 | fullnode.deps.Buffer = Buffer; 63 | fullnode.deps.elliptic = require('elliptic'); 64 | fullnode.deps.hashjs = require('hash.js'); 65 | fullnode.deps.pbkdf2compat = require('pbkdf2-compat'); 66 | -------------------------------------------------------------------------------- /lib/address.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Bitcoin Address 3 | * =============== 4 | * 5 | * A bitcoin address. The primary way to use this class is either 6 | * Address().fromPubkey(pubkey) or Address.fromString(string). 7 | */ 8 | var Base58check = require('./base58check'); 9 | var constants = require('./constants'); 10 | var Hash = require('./hash'); 11 | var Pubkey = require('./pubkey'); 12 | var Script = require('./script'); 13 | 14 | function Address(buf) { 15 | if (!(this instanceof Address)) 16 | return new Address(buf); 17 | if (Buffer.isBuffer(buf)) { 18 | this.fromBuffer(buf); 19 | } else if (typeof buf === 'string') { 20 | var str = buf; 21 | this.fromString(str); 22 | } else if (buf) { 23 | var obj = buf; 24 | this.set(obj); 25 | } 26 | }; 27 | 28 | Address.prototype.set = function(obj) { 29 | this.hashbuf = obj.hashbuf || this.hashbuf || undefined; 30 | this.version = typeof obj.version !== undefined ? obj.version : this.version; 31 | return this; 32 | }; 33 | 34 | Address.prototype.fromBuffer = function(buf) { 35 | if (buf.length !== 1 + 20) 36 | throw new Error('Address buffers must be exactly 21 bytes'); 37 | this.version = buf[0]; 38 | this.hashbuf = buf.slice(1); 39 | return this; 40 | }; 41 | 42 | Address.prototype.fromHashbuf = function(hashbuf, networkstr, typestr) { 43 | if (hashbuf.length !== 20) 44 | throw new Error('hashbuf must be exactly 20 bytes'); 45 | this.hashbuf = hashbuf; 46 | if (networkstr) { 47 | if (!typestr) 48 | typestr = 'pubkeyhash'; 49 | this.version = constants[networkstr][typestr]; 50 | } else { 51 | this.version = 0; 52 | } 53 | return this; 54 | }; 55 | 56 | Address.prototype.fromPubkey = function(pubkey, networkstr) { 57 | this.hashbuf = Hash.sha256ripemd160(pubkey.toBuffer()); 58 | networkstr = networkstr || 'mainnet'; 59 | var typestr = 'pubkeyhash'; 60 | this.version = constants[networkstr][typestr]; 61 | return this; 62 | }; 63 | 64 | Address.prototype.fromScript = function(script, networkstr) { 65 | this.hashbuf = Hash.sha256ripemd160(script.toBuffer()); 66 | networkstr = networkstr || 'mainnet'; 67 | var typestr = 'scripthash'; 68 | this.version = constants[networkstr][typestr]; 69 | return this; 70 | }; 71 | 72 | Address.prototype.fromString = function(str) { 73 | var buf = Base58check.decode(str); 74 | return this.fromBuffer(buf); 75 | } 76 | 77 | Address.isValid = function(addrstr) { 78 | try { 79 | var address = new Address().fromString(addrstr); 80 | } catch (e) { 81 | return false; 82 | } 83 | return address.isValid(); 84 | }; 85 | 86 | Address.prototype.isValid = function() { 87 | try { 88 | this.validate(); 89 | return true; 90 | } catch (e) { 91 | return false; 92 | } 93 | }; 94 | 95 | Address.prototype.network = function() { 96 | if (this.version === constants['mainnet']['pubkeyhash']) { 97 | return 'mainnet'; 98 | } else if (this.version === constants['mainnet']['scripthash']) { 99 | return 'mainnet'; 100 | } else if (this.version === constants['testnet']['pubkeyhash']) { 101 | return 'testnet'; 102 | } else if (this.version === constants['testnet']['scripthash']) { 103 | return 'testnet'; 104 | } else { 105 | return 'unknown'; 106 | } 107 | }; 108 | 109 | Address.prototype.type = function() { 110 | if (this.version === constants['mainnet']['pubkeyhash']) { 111 | return 'pubkeyhash'; 112 | } else if (this.version === constants['mainnet']['scripthash']) { 113 | return 'scripthash'; 114 | } else if (this.version === constants['testnet']['pubkeyhash']) { 115 | return 'pubkeyhash'; 116 | } else if (this.version === constants['testnet']['scripthash']) { 117 | return 'scripthash'; 118 | } else { 119 | return 'unknown'; 120 | } 121 | }; 122 | 123 | Address.prototype.toBuffer = function() { 124 | versionbuf = new Buffer([this.version]); 125 | var buf = Buffer.concat([versionbuf, this.hashbuf]); 126 | return buf; 127 | }; 128 | 129 | Address.prototype.toString = function() { 130 | return Base58check.encode(this.toBuffer()); 131 | }; 132 | 133 | Address.prototype.validate = function() { 134 | if (!Buffer.isBuffer(this.hashbuf) || this.hashbuf.length !== 20) 135 | throw new Error('hash must be a buffer of 20 bytes'); 136 | if (this.version !== constants['mainnet']['pubkeyhash'] 137 | && this.version !== constants['mainnet']['scripthash'] 138 | && this.version !== constants['testnet']['pubkeyhash'] 139 | && this.version !== constants['testnet']['scripthash']) 140 | throw new Error('invalid version'); 141 | return this; 142 | }; 143 | 144 | module.exports = Address; 145 | -------------------------------------------------------------------------------- /lib/base58.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Base58 Encoding 3 | * =============== 4 | * 5 | * Base58 (no check) 6 | */ 7 | var bs58 = require('bs58'); 8 | 9 | var Base58 = function Base58(obj) { 10 | if (!(this instanceof Base58)) 11 | return new Base58(obj); 12 | if (Buffer.isBuffer(obj)) { 13 | var buf = obj; 14 | this.fromBuffer(buf); 15 | } else if (typeof obj === 'string') { 16 | var str = obj; 17 | this.fromString(str); 18 | } else if (obj) { 19 | this.set(obj); 20 | } 21 | }; 22 | 23 | Base58.prototype.set = function(obj) { 24 | this.buf = obj.buf || this.buf || undefined; 25 | return this; 26 | }; 27 | 28 | Base58.encode = function(buf) { 29 | if (!Buffer.isBuffer(buf)) 30 | throw new Error('Input should be a buffer'); 31 | return bs58.encode(buf); 32 | }; 33 | 34 | Base58.decode = function(str) { 35 | if (typeof str !== 'string') 36 | throw new Error('Input should be a string'); 37 | return bs58.decode(str); 38 | }; 39 | 40 | Base58.prototype.fromBuffer = function(buf) { 41 | this.buf = buf; 42 | return this; 43 | }; 44 | 45 | Base58.prototype.fromString = function(str) { 46 | var buf = Base58.decode(str); 47 | this.buf = buf; 48 | return this; 49 | }; 50 | 51 | Base58.prototype.toBuffer = function() { 52 | return this.buf; 53 | }; 54 | 55 | Base58.prototype.toString = function() { 56 | return Base58.encode(this.buf); 57 | }; 58 | 59 | module.exports = Base58; 60 | -------------------------------------------------------------------------------- /lib/base58check.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Base58 Check Encoding 3 | * ===================== 4 | * 5 | * Base58 check encoding. The usual way to use it is 6 | * Base58Check(buf).toString() or Base58Check(str).toBuffer(). This code was 7 | * originally written in BitcoinJS, then found its way into fullnode via 8 | * bitcore. 9 | */ 10 | var base58 = require('./base58'); 11 | var sha256sha256 = require('./hash').sha256sha256; 12 | 13 | var Base58Check = function Base58Check(obj) { 14 | if (!(this instanceof Base58Check)) 15 | return new Base58Check(obj); 16 | if (Buffer.isBuffer(obj)) { 17 | var buf = obj; 18 | this.fromBuffer(buf); 19 | } else if (typeof obj === 'string') { 20 | var str = obj; 21 | this.fromString(str); 22 | } else if (obj) { 23 | this.set(obj); 24 | } 25 | }; 26 | 27 | Base58Check.prototype.set = function(obj) { 28 | this.buf = obj.buf || this.buf || undefined; 29 | return this; 30 | }; 31 | 32 | Base58Check.decode = function(s) { 33 | if (typeof s !== 'string') 34 | throw new Error('Input must be a string'); 35 | 36 | var buf = base58.decode(s); 37 | 38 | if (buf.length < 4) 39 | throw new Error("Input string too short"); 40 | 41 | var data = buf.slice(0, -4); 42 | var csum = buf.slice(-4); 43 | 44 | var hash = sha256sha256(data); 45 | var hash4 = hash.slice(0, 4); 46 | 47 | if (csum.toString('hex') !== hash4.toString('hex')) 48 | throw new Error("Checksum mismatch"); 49 | 50 | return data; 51 | }; 52 | 53 | Base58Check.encode = function(buf) { 54 | if (!Buffer.isBuffer(buf)) 55 | throw new Error('Input must be a buffer'); 56 | var checkedBuf = new Buffer(buf.length + 4); 57 | var hash = sha256sha256(buf); 58 | buf.copy(checkedBuf); 59 | hash.copy(checkedBuf, buf.length); 60 | return base58.encode(checkedBuf); 61 | }; 62 | 63 | Base58Check.prototype.fromBuffer = function(buf) { 64 | this.buf = buf; 65 | return this; 66 | }; 67 | 68 | Base58Check.prototype.fromString = function(str) { 69 | var buf = Base58Check.decode(str); 70 | this.buf = buf; 71 | return this; 72 | }; 73 | 74 | Base58Check.prototype.toBuffer = function() { 75 | return this.buf; 76 | }; 77 | 78 | Base58Check.prototype.toString = function() { 79 | return Base58Check.encode(this.buf); 80 | }; 81 | 82 | module.exports = Base58Check; 83 | -------------------------------------------------------------------------------- /lib/block.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Block 3 | * ===== 4 | * 5 | * A block, of course, is a collection of transactions. This class is somewhat 6 | * incomplete at the moment. In the future, it should support the ability to 7 | * check to see if a transaction is in a block (thanks to the magic of merkle 8 | * trees). You will probably never use fullnode to create a block, since almost 9 | * everyone will use bitcoind for that. As such, the primary way to use this is 10 | * Block().fromBuffer(buf), which will parse the block and prepare its insides 11 | * for you to inspect. 12 | */ 13 | var Tx = require('./tx'); 14 | var BufR = require('./bufr'); 15 | var BufW = require('./bufw'); 16 | var Blockheader = require('./blockheader'); 17 | var Varint = require('./varint'); 18 | var Hash = require('./hash'); 19 | 20 | var Block = function Block(magicnum, blocksize, blockheader, txsvi, txs) { 21 | if (!(this instanceof Block)) 22 | return new Block(magicnum, blocksize, blockheader, txsvi, txs); 23 | if (typeof magicnum === 'number') { 24 | this.set({ 25 | magicnum: magicnum, 26 | blocksize: blocksize, 27 | blockheader: blockheader, 28 | txsvi: txsvi, 29 | txs: txs 30 | }); 31 | } else if (Buffer.isBuffer(magicnum)) { 32 | var blockbuf = magicnum; 33 | this.fromBuffer(blockbuf); 34 | } else if (magicnum) { 35 | var obj = magicnum; 36 | } 37 | }; 38 | 39 | Block.MAX_BLOCK_SIZE = 1000000; 40 | 41 | Block.prototype.set = function(obj) { 42 | this.magicnum = typeof obj.magicnum !== 'undefined' ? obj.magicnum : this.magicnum; 43 | this.blocksize = typeof obj.blocksize !== 'undefined' ? obj.blocksize : this.blocksize; 44 | this.blockheader = obj.blockheader || this.blockheader; 45 | this.txsvi = obj.txsvi || this.txsvi; 46 | this.txs = obj.txs || this.txs; 47 | return this; 48 | }; 49 | 50 | Block.prototype.fromJSON = function(json) { 51 | var txs = []; 52 | json.txs.forEach(function(tx) { 53 | txs.push(Tx().fromJSON(tx)); 54 | }); 55 | this.set({ 56 | magicnum: json.magicnum, 57 | blocksize: json.blocksize, 58 | blockheader: Blockheader().fromJSON(json.blockheader), 59 | txsvi: Varint().fromJSON(json.txsvi), 60 | txs: txs 61 | }); 62 | return this; 63 | }; 64 | 65 | Block.prototype.toJSON = function() { 66 | var txs = []; 67 | this.txs.forEach(function(tx) { 68 | txs.push(tx.toJSON()); 69 | }); 70 | return { 71 | magicnum: this.magicnum, 72 | blocksize: this.blocksize, 73 | blockheader: this.blockheader.toJSON(), 74 | txsvi: this.txsvi.toJSON(), 75 | txs: txs 76 | }; 77 | }; 78 | 79 | Block.prototype.fromBuffer = function(buf) { 80 | return this.fromBufR(BufR(buf)); 81 | }; 82 | 83 | Block.prototype.fromBufR = function(br) { 84 | this.magicnum = br.readUInt32LE(); 85 | this.blocksize = br.readUInt32LE(); 86 | this.blockheader = Blockheader().fromBufR(br); 87 | this.txsvi = Varint(br.readVarintBuf()); 88 | var txslen = this.txsvi.toNumber(); 89 | this.txs = []; 90 | for (var i = 0; i < txslen; i++) { 91 | this.txs.push(Tx().fromBufR(br)); 92 | } 93 | return this; 94 | }; 95 | 96 | Block.prototype.toBuffer = function() { 97 | return this.toBufW().concat(); 98 | }; 99 | 100 | Block.prototype.toBufW = function(bw) { 101 | if (!bw) 102 | bw = new BufW(); 103 | bw.writeUInt32LE(this.magicnum); 104 | bw.writeUInt32LE(this.blocksize); 105 | bw.write(this.blockheader.toBuffer()); 106 | bw.write(this.txsvi.buf); 107 | var txslen = this.txsvi.toNumber(); 108 | for (var i = 0; i < txslen; i++) { 109 | this.txs[i].toBufW(bw); 110 | } 111 | return bw; 112 | }; 113 | 114 | Block.prototype.hash = function() { 115 | return Hash.sha256sha256(this.blockheader.toBuffer()); 116 | }; 117 | 118 | Block.prototype.id = function() { 119 | return BufR(this.hash()).readReverse(); 120 | }; 121 | 122 | module.exports = Block; 123 | -------------------------------------------------------------------------------- /lib/blockheader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Block Header 3 | * ============ 4 | * 5 | * Every block contains a blockheader. This is probably not something you will 6 | * personally use, but it's here if you need it. 7 | */ 8 | var BufR = require('./bufr'); 9 | var BufW = require('./bufw'); 10 | 11 | var Blockheader = function Blockheader(version, prevblockidbuf, merklerootbuf, time, bits, nonce) { 12 | if (!(this instanceof Blockheader)) 13 | return new Blockheader(version, prevblockidbuf, merklerootbuf, time, bits, nonce); 14 | if (typeof version === 'number') { 15 | this.set({ 16 | version: version, 17 | prevblockidbuf: prevblockidbuf, 18 | merklerootbuf: merklerootbuf, 19 | time: time, 20 | bits: bits, 21 | nonce: nonce 22 | }); 23 | } else if (Buffer.isBuffer(version)) { 24 | var bhbuf = version; 25 | this.fromBuffer(bhbuf); 26 | } else if (version) { 27 | var obj = version; 28 | this.set(obj); 29 | } 30 | } 31 | 32 | Blockheader.prototype.set = function(obj) { 33 | this.version = typeof obj.version !== 'undefined' ? obj.version : this.version; 34 | this.prevblockidbuf = obj.prevblockidbuf || this.prevblockidbuf; 35 | this.merklerootbuf = obj.merklerootbuf || this.merklerootbuf; 36 | this.time = typeof obj.time !== 'undefined' ? obj.time : this.time; 37 | this.bits = typeof obj.bits !== 'undefined' ? obj.bits : this.bits; 38 | this.nonce = typeof obj.nonce !== 'undefined' ? obj.nonce : this.nonce; 39 | return this; 40 | }; 41 | 42 | Blockheader.prototype.fromJSON = function(json) { 43 | this.set({ 44 | version: json.version, 45 | prevblockidbuf: new Buffer(json.prevblockidbuf, 'hex'), 46 | merklerootbuf: new Buffer(json.merklerootbuf, 'hex'), 47 | time: json.time, 48 | bits: json.bits, 49 | nonce: json.nonce 50 | }); 51 | return this; 52 | }; 53 | 54 | Blockheader.prototype.toJSON = function() { 55 | return { 56 | version: this.version, 57 | prevblockidbuf: this.prevblockidbuf.toString('hex'), 58 | merklerootbuf: this.merklerootbuf.toString('hex'), 59 | time: this.time, 60 | bits: this.bits, 61 | nonce: this.nonce 62 | }; 63 | }; 64 | 65 | Blockheader.prototype.fromBuffer = function(buf) { 66 | return this.fromBufR(BufR(buf)); 67 | }; 68 | 69 | Blockheader.prototype.fromBufR = function(br) { 70 | this.version = br.readUInt32LE(); 71 | this.prevblockidbuf = br.read(32); 72 | this.merklerootbuf = br.read(32); 73 | this.time = br.readUInt32LE(); 74 | this.bits = br.readUInt32LE(); 75 | this.nonce = br.readUInt32LE(); 76 | return this; 77 | }; 78 | 79 | Blockheader.prototype.toBuffer = function() { 80 | return this.toBufW().concat(); 81 | }; 82 | 83 | Blockheader.prototype.toBufW = function(bw) { 84 | if (!bw) 85 | bw = new BufW(); 86 | bw.writeUInt32LE(this.version); 87 | bw.write(this.prevblockidbuf); 88 | bw.write(this.merklerootbuf); 89 | bw.writeUInt32LE(this.time); 90 | bw.writeUInt32LE(this.bits); 91 | bw.writeUInt32LE(this.nonce); 92 | return bw; 93 | }; 94 | 95 | module.exports = Blockheader; 96 | -------------------------------------------------------------------------------- /lib/bn.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Big Number 3 | * ========== 4 | * 5 | * Ah, big numbers. One of the elements of cryptography. Since javascript 6 | * numbers are only precise up to about 53 bits, and bitcoin is based on 7 | * cryptography that uses 256 bit numbers, we must use a big number library. 8 | * The library we use at the moment is Fedor Indutny's bn.js library. Since big 9 | * numbers are extremely useful, we provide some very basic wrappers for his 10 | * big number class and expose it. The wrappers merely allow you to do, say, 11 | * bn.cmp(num) instead of just bn.cmp(bn), which is nice. The primary way to 12 | * use this is bn(str) (where str is base 10) or bn(num) or 13 | * bn().fromBuffer(buf) or bn().fromSM(buf). You can also do little endian, 14 | * which bitcoin likes a lot, with bn().fromBuffer(buf, {endian: 'little'}) or 15 | * bn().fromSM(buf, {endian: 'little'}). 16 | */ 17 | var _BN = require('bn.js'); 18 | 19 | var BN = function BN(n, base) { 20 | if (!(this instanceof BN)) { 21 | return new BN(n, base); 22 | } 23 | _BN.apply(this, arguments); 24 | }; 25 | 26 | BN.prototype = _BN.prototype; 27 | 28 | var reversebuf = function(buf) { 29 | var buf2 = new Buffer(buf.length); 30 | for (var i = 0; i < buf.length; i++) { 31 | buf2[i] = buf[buf.length-1-i]; 32 | } 33 | return buf2; 34 | }; 35 | 36 | BN.prototype.toJSON = function() { 37 | return this.toString(); 38 | }; 39 | 40 | BN.prototype.fromJSON = function(str) { 41 | var bn = BN(str); 42 | bn.copy(this); 43 | return this; 44 | }; 45 | 46 | BN.prototype.fromNumber = function(n) { 47 | var bn = BN(n); 48 | bn.copy(this); 49 | return this; 50 | }; 51 | 52 | BN.prototype.toNumber = function() { 53 | return parseInt(this['toString'](10), 10); 54 | }; 55 | 56 | BN.prototype.fromString = function(str) { 57 | var bn = BN(str); 58 | bn.copy(this); 59 | return this; 60 | }; 61 | 62 | BN.fromBuffer = function(buf, opts) { 63 | if (typeof opts !== 'undefined' && opts.endian === 'little') { 64 | buf = reversebuf(buf); 65 | } 66 | var hex = buf.toString('hex'); 67 | var bn = new BN(hex, 16); 68 | return bn; 69 | }; 70 | 71 | BN.prototype.fromBuffer = function(buf, opts) { 72 | var bn = BN.fromBuffer(buf, opts); 73 | bn.copy(this); 74 | 75 | return this; 76 | }; 77 | 78 | BN.prototype.toBuffer = function(opts) { 79 | var buf; 80 | if (opts && opts.size) { 81 | var hex = this.toString(16, 2); 82 | var natlen = hex.length/2; 83 | buf = new Buffer(hex, 'hex'); 84 | 85 | if (natlen == opts.size) 86 | buf = buf; 87 | 88 | else if (natlen > opts.size) { 89 | buf = buf.slice(natlen - buf.length, buf.length); 90 | } 91 | 92 | else if (natlen < opts.size) { 93 | var rbuf = new Buffer(opts.size); 94 | for (var i = 0; i < buf.length; i++) 95 | rbuf[rbuf.length-1-i] = buf[buf.length-1-i]; 96 | for (var i = 0; i < opts.size - natlen; i++) 97 | rbuf[i] = 0; 98 | buf = rbuf; 99 | } 100 | } 101 | else { 102 | var hex = this.toString(16, 2); 103 | buf = new Buffer(hex, 'hex'); 104 | } 105 | 106 | if (typeof opts !== 'undefined' && opts.endian === 'little') { 107 | buf = reversebuf(buf); 108 | } 109 | 110 | return buf; 111 | }; 112 | 113 | /** 114 | * Signed magnitude buffer. Most significant bit represents sign (0 = positive, 115 | * 1 = negative). 116 | */ 117 | BN.prototype.fromSM = function(buf, opts) { 118 | if (buf.length === 0) 119 | this.fromBuffer(new Buffer([0])); 120 | 121 | var endian = 'big'; 122 | if (opts) 123 | endian = opts.endian; 124 | 125 | if (endian == 'little') 126 | buf = reversebuf(buf); 127 | 128 | if (buf[0] & 0x80) { 129 | buf[0] = buf[0] & 0x7f; 130 | this.fromBuffer(buf); 131 | this.neg().copy(this); 132 | } else { 133 | this.fromBuffer(buf); 134 | } 135 | return this; 136 | }; 137 | 138 | BN.prototype.toSM = function(opts) { 139 | var endian = 'big'; 140 | if (opts) 141 | endian = opts.endian; 142 | 143 | var buf; 144 | if (this.cmp(0) == -1) { 145 | buf = this.neg().toBuffer(); 146 | if (buf[0] & 0x80) 147 | buf = Buffer.concat([new Buffer([0x80]), buf]); 148 | else 149 | buf[0] = buf[0] | 0x80; 150 | } else { 151 | buf = this.toBuffer(); 152 | if (buf[0] & 0x80) 153 | buf = Buffer.concat([new Buffer([0x00]), buf]); 154 | } 155 | 156 | if (buf.length === 1 & buf[0] === 0) 157 | buf = new Buffer([]); 158 | 159 | if (endian == 'little') 160 | buf = reversebuf(buf); 161 | 162 | return buf; 163 | }; 164 | 165 | // This is analogous to the constructor for CScriptNum in bitcoind. Many ops in 166 | // bitcoind's script interpreter use CScriptNum, which is not really a proper 167 | // bignum. Instead, an error is thrown if trying to input a number bigger than 168 | // 4 bytes. We copy that behavior here. 169 | BN.prototype.fromScriptNumBuffer = function(buf, fRequireMinimal) { 170 | var nMaxNumSize = 4; 171 | if (buf.length > nMaxNumSize) 172 | throw new Error('script number overflow'); 173 | if (fRequireMinimal && buf.length > 0) { 174 | // Check that the number is encoded with the minimum possible 175 | // number of bytes. 176 | // 177 | // If the most-significant-byte - excluding the sign bit - is zero 178 | // then we're not minimal. Note how this test also rejects the 179 | // negative-zero encoding, 0x80. 180 | if ((buf[buf.length - 1] & 0x7f) === 0) { 181 | // One exception: if there's more than one byte and the most 182 | // significant bit of the second-most-significant-byte is set 183 | // it would conflict with the sign bit. An example of this case 184 | // is +-255, which encode to 0xff00 and 0xff80 respectively. 185 | // (big-endian). 186 | if (buf.length <= 1 || (buf[buf.length - 2] & 0x80) === 0) { 187 | throw new Error("non-minimally encoded script number"); 188 | } 189 | } 190 | } 191 | return this.fromSM(buf, {endian: 'little'}); 192 | }; 193 | 194 | // The corollary to the above, with the notable exception that we do not throw 195 | // an error if the output is larger than four bytes. (Which can happen if 196 | // performing a numerical operation that results in an overflow to more than 4 197 | // bytes). 198 | BN.prototype.toScriptNumBuffer = function(buf) { 199 | return this.toSM({endian: 'little'}); 200 | }; 201 | 202 | function decorate(name) { 203 | BN.prototype['_' + name] = BN.prototype[name]; 204 | var f = function(b) { 205 | if (typeof b === 'string') 206 | b = new BN(b); 207 | else if (typeof b === 'number') 208 | b = new BN(b.toString()); 209 | return this['_' + name](b); 210 | }; 211 | BN.prototype[name] = f; 212 | }; 213 | 214 | BN.prototype.eq = function(b) { 215 | return this.cmp(b) === 0; 216 | }; 217 | 218 | BN.prototype.neq = function(b) { 219 | return this.cmp(b) !== 0; 220 | }; 221 | 222 | BN.prototype.gt = function(b) { 223 | return this.cmp(b) > 0; 224 | }; 225 | 226 | BN.prototype.geq = function(b) { 227 | return this.cmp(b) >= 0; 228 | }; 229 | 230 | BN.prototype.lt = function(b) { 231 | return this.cmp(b) < 0; 232 | }; 233 | 234 | BN.prototype.leq = function(b) { 235 | return this.cmp(b) <= 0; 236 | }; 237 | 238 | decorate('add'); 239 | decorate('sub'); 240 | decorate('mul'); 241 | decorate('mod'); 242 | decorate('div'); 243 | decorate('cmp'); 244 | decorate('gt'); 245 | decorate('geq'); 246 | decorate('lt'); 247 | decorate('leq'); 248 | 249 | module.exports = BN; 250 | -------------------------------------------------------------------------------- /lib/bsm.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Bitcoin Signed Message 3 | * ====================== 4 | * 5 | * "Bitcoin Signed Message" just refers to a standard way of signing and 6 | * verifying an arbitrary message. The standard way to do this involves using a 7 | * "Bitcoin Signed Message:\n" prefix, which this code does. You are probably 8 | * interested in the static BSM.sign( ... ) and BSM.verify( ... ) functions, 9 | * which deal with a base64 string representing the compressed format of a 10 | * signature. 11 | */ 12 | var ECDSA = require('./ecdsa'); 13 | var Keypair = require('./keypair'); 14 | var Privkey = require('./privkey'); 15 | var Pubkey = require('./pubkey'); 16 | var BufW = require('./bufw'); 17 | var Hash = require('./hash'); 18 | var Address = require('./address'); 19 | var Sig = require('./sig'); 20 | 21 | var BSM = function BSM(obj) { 22 | if (!(this instanceof BSM)) 23 | return new BSM(obj); 24 | if (obj) 25 | this.set(obj); 26 | }; 27 | 28 | BSM.prototype.set = function(obj) { 29 | this.messagebuf = obj.messagebuf || this.messagebuf; 30 | this.keypair = obj.keypair || this.keypair; 31 | this.sig = obj.sig || this.sig; 32 | this.address = obj.address || this.address; 33 | this.verified = typeof obj.verified !== 'undefined' ? obj.verified : this.verified; 34 | return this; 35 | }; 36 | 37 | BSM.magicBytes = new Buffer('Bitcoin Signed Message:\n'); 38 | 39 | BSM.magicHash = function(messagebuf) { 40 | if (!Buffer.isBuffer(messagebuf)) 41 | throw new Error('messagebuf must be a buffer'); 42 | var bw = new BufW(); 43 | bw.writeVarintNum(BSM.magicBytes.length); 44 | bw.write(BSM.magicBytes); 45 | bw.writeVarintNum(messagebuf.length); 46 | bw.write(messagebuf); 47 | var buf = bw.concat(); 48 | 49 | var hashbuf = Hash.sha256sha256(buf); 50 | 51 | return hashbuf; 52 | }; 53 | 54 | BSM.sign = function(messagebuf, keypair) { 55 | var m = BSM({messagebuf: messagebuf, keypair: keypair}); 56 | m.sign(); 57 | var sigbuf = m.sig.toCompact(); 58 | var sigstr = sigbuf.toString('base64'); 59 | return sigstr; 60 | }; 61 | 62 | BSM.verify = function(messagebuf, sigstr, address) { 63 | var sigbuf = new Buffer(sigstr, 'base64'); 64 | var message = new BSM(); 65 | message.messagebuf = messagebuf; 66 | message.sig = Sig().fromCompact(sigbuf); 67 | message.address = address; 68 | 69 | return message.verify().verified; 70 | }; 71 | 72 | BSM.prototype.sign = function() { 73 | var hashbuf = BSM.magicHash(this.messagebuf); 74 | var ecdsa = ECDSA({hashbuf: hashbuf, keypair: this.keypair}); 75 | ecdsa.signRandomK(); 76 | ecdsa.calci(); 77 | this.sig = ecdsa.sig; 78 | return this; 79 | }; 80 | 81 | BSM.prototype.verify = function() { 82 | var hashbuf = BSM.magicHash(this.messagebuf); 83 | 84 | var ecdsa = new ECDSA(); 85 | ecdsa.hashbuf = hashbuf; 86 | ecdsa.sig = this.sig; 87 | ecdsa.keypair = new Keypair(); 88 | ecdsa.keypair.pubkey = ecdsa.sig2pubkey(); 89 | 90 | if (!ecdsa.verify()) { 91 | this.verified = false; 92 | return this; 93 | } 94 | 95 | var address = Address().fromPubkey(ecdsa.keypair.pubkey, undefined, this.sig.compressed); 96 | //TODO: what if livenet/testnet mismatch? 97 | if (address.hashbuf.toString('hex') === this.address.hashbuf.toString('hex')) 98 | this.verified = true; 99 | else 100 | this.verified = false; 101 | 102 | return this; 103 | }; 104 | 105 | module.exports = BSM; 106 | -------------------------------------------------------------------------------- /lib/bufr.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Buffer Reader 3 | * ============= 4 | * 5 | * This is a convenience class for reading Varints and other basic types from a 6 | * buffer. This class is most useful for reading Varints, and also for signed 7 | * or unsigned integers of various types. It can also read a buffer in reverse 8 | * order, which is useful in bitcoin which uses little endian numbers a lot so 9 | * you find that you must reverse things. You probably want to use it like: 10 | * varint = BufR(buf).readVarint() 11 | */ 12 | var BN = require('./bn'); 13 | 14 | var BufR = function BufR(buf) { 15 | if (!(this instanceof BufR)) 16 | return new BufR(buf); 17 | if (Buffer.isBuffer(buf)) { 18 | this.set({buf: buf}); 19 | } 20 | else if (buf) { 21 | var obj = buf; 22 | this.set(obj); 23 | } 24 | }; 25 | 26 | BufR.prototype.set = function(obj) { 27 | this.buf = obj.buf || this.buf || undefined; 28 | this.pos = obj.pos || this.pos || 0; 29 | return this; 30 | }; 31 | 32 | BufR.prototype.eof = function() { 33 | return this.pos >= this.buf.length; 34 | }; 35 | 36 | BufR.prototype.read = function(len) { 37 | if (typeof len === 'undefined') 38 | var len = this.buf.length; 39 | var buf = this.buf.slice(this.pos, this.pos + len); 40 | this.pos = this.pos + len; 41 | return buf; 42 | }; 43 | 44 | BufR.prototype.readReverse = function(len) { 45 | if (typeof len === 'undefined') 46 | var len = this.buf.length; 47 | var buf = this.buf.slice(this.pos, this.pos + len); 48 | this.pos = this.pos + len; 49 | var buf2 = new Buffer(buf.length); 50 | for (var i = 0; i < buf2.length; i++) 51 | buf2[i] = buf[buf.length - 1 - i]; 52 | return buf2; 53 | }; 54 | 55 | BufR.prototype.readUInt8 = function() { 56 | var val = this.buf.readUInt8(this.pos); 57 | this.pos = this.pos + 1; 58 | return val; 59 | }; 60 | 61 | BufR.prototype.readInt8 = function() { 62 | var val = this.buf.readInt8(this.pos); 63 | this.pos = this.pos + 1; 64 | return val; 65 | }; 66 | 67 | BufR.prototype.readUInt16BE = function() { 68 | var val = this.buf.readUInt16BE(this.pos); 69 | this.pos = this.pos + 2; 70 | return val; 71 | }; 72 | 73 | BufR.prototype.readInt16BE = function() { 74 | var val = this.buf.readInt16BE(this.pos); 75 | this.pos = this.pos + 2; 76 | return val; 77 | }; 78 | 79 | BufR.prototype.readUInt16LE = function() { 80 | var val = this.buf.readUInt16LE(this.pos); 81 | this.pos = this.pos + 2; 82 | return val; 83 | }; 84 | 85 | BufR.prototype.readInt16LE = function() { 86 | var val = this.buf.readInt16LE(this.pos); 87 | this.pos = this.pos + 2; 88 | return val; 89 | }; 90 | 91 | BufR.prototype.readUInt32BE = function() { 92 | var val = this.buf.readUInt32BE(this.pos); 93 | this.pos = this.pos + 4; 94 | return val; 95 | }; 96 | 97 | BufR.prototype.readInt32BE = function() { 98 | var val = this.buf.readInt32BE(this.pos); 99 | this.pos = this.pos + 4; 100 | return val; 101 | }; 102 | 103 | BufR.prototype.readUInt32LE = function() { 104 | var val = this.buf.readUInt32LE(this.pos); 105 | this.pos = this.pos + 4; 106 | return val; 107 | }; 108 | 109 | BufR.prototype.readInt32LE = function() { 110 | var val = this.buf.readInt32LE(this.pos); 111 | this.pos = this.pos + 4; 112 | return val; 113 | }; 114 | 115 | BufR.prototype.readUInt64BEBN = function() { 116 | var buf = this.buf.slice(this.pos, this.pos + 8); 117 | var bn = BN().fromBuffer(buf); 118 | this.pos = this.pos + 8; 119 | return bn; 120 | }; 121 | 122 | BufR.prototype.readUInt64LEBN = function() { 123 | var buf = this.readReverse(8); 124 | var bn = BN().fromBuffer(buf); 125 | return bn; 126 | }; 127 | 128 | BufR.prototype.readVarintNum = function() { 129 | var first = this.readUInt8(); 130 | switch (first) { 131 | case 0xFD: 132 | return this.readUInt16LE(); 133 | case 0xFE: 134 | return this.readUInt32LE(); 135 | case 0xFF: 136 | var bn = this.readUInt64LEBN(); 137 | var n = bn.toNumber(); 138 | if (n <= Math.pow(2, 53)) 139 | return n; 140 | else 141 | throw new Error('number too large to retain precision - use readVarintBN'); 142 | default: 143 | return first; 144 | } 145 | }; 146 | 147 | BufR.prototype.readVarintBuf = function() { 148 | var first = this.buf.readUInt8(this.pos); 149 | switch (first) { 150 | case 0xFD: 151 | return this.read(1 + 2); 152 | case 0xFE: 153 | return this.read(1 + 4); 154 | case 0xFF: 155 | return this.read(1 + 8); 156 | default: 157 | return this.read(1); 158 | } 159 | }; 160 | 161 | BufR.prototype.readVarintBN = function() { 162 | var first = this.readUInt8(); 163 | switch (first) { 164 | case 0xFD: 165 | return BN(this.readUInt16LE()); 166 | case 0xFE: 167 | return BN(this.readUInt32LE()); 168 | case 0xFF: 169 | return this.readUInt64LEBN(); 170 | default: 171 | return BN(first); 172 | } 173 | }; 174 | 175 | module.exports = BufR; 176 | -------------------------------------------------------------------------------- /lib/bufw.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Buffer Writer 3 | * ============= 4 | * 5 | * This is the writing complement of the BufR. You can easily write 6 | * Varints and other basic number types. The way to use it is: buf = 7 | * BufW().write(buf1).write(buf2).concat() 8 | */ 9 | var BN = require('./bn'); 10 | 11 | var BufW = function BufW(obj) { 12 | if (!(this instanceof BufW)) 13 | return new BufW(obj); 14 | if (obj) 15 | this.set(obj); 16 | else 17 | this.bufs = []; 18 | }; 19 | 20 | BufW.prototype.set = function(obj) { 21 | this.bufs = obj.bufs || this.bufs || []; 22 | return this; 23 | }; 24 | 25 | BufW.prototype.toBuffer = function() { 26 | return this.concat(); 27 | }; 28 | 29 | BufW.prototype.concat = function() { 30 | return Buffer.concat(this.bufs); 31 | }; 32 | 33 | BufW.prototype.write = function(buf) { 34 | this.bufs.push(buf); 35 | return this; 36 | }; 37 | 38 | BufW.prototype.writeReverse = function(buf) { 39 | var buf2 = new Buffer(buf.length); 40 | for (var i = 0; i < buf2.length; i++) 41 | buf2[i] = buf[buf.length - 1 - i]; 42 | this.bufs.push(buf2); 43 | return this; 44 | }; 45 | 46 | BufW.prototype.writeUInt8 = function(n) { 47 | var buf = new Buffer(1); 48 | buf.writeUInt8(n, 0); 49 | this.write(buf); 50 | return this; 51 | }; 52 | 53 | BufW.prototype.writeInt8 = function(n) { 54 | var buf = new Buffer(1); 55 | buf.writeInt8(n, 0); 56 | this.write(buf); 57 | return this; 58 | }; 59 | 60 | BufW.prototype.writeUInt16BE = function(n) { 61 | var buf = new Buffer(2); 62 | buf.writeUInt16BE(n, 0); 63 | this.write(buf); 64 | return this; 65 | }; 66 | 67 | BufW.prototype.writeInt16BE = function(n) { 68 | var buf = new Buffer(2); 69 | buf.writeInt16BE(n, 0); 70 | this.write(buf); 71 | return this; 72 | }; 73 | 74 | BufW.prototype.writeUInt16LE = function(n) { 75 | var buf = new Buffer(2); 76 | buf.writeUInt16LE(n, 0); 77 | this.write(buf); 78 | return this; 79 | }; 80 | 81 | BufW.prototype.writeInt16LE = function(n) { 82 | var buf = new Buffer(2); 83 | buf.writeInt16LE(n, 0); 84 | this.write(buf); 85 | return this; 86 | }; 87 | 88 | BufW.prototype.writeUInt32BE = function(n) { 89 | var buf = new Buffer(4); 90 | buf.writeUInt32BE(n, 0); 91 | this.write(buf); 92 | return this; 93 | }; 94 | 95 | BufW.prototype.writeInt32BE = function(n) { 96 | var buf = new Buffer(4); 97 | buf.writeInt32BE(n, 0); 98 | this.write(buf); 99 | return this; 100 | }; 101 | 102 | BufW.prototype.writeUInt32LE = function(n) { 103 | var buf = new Buffer(4); 104 | buf.writeUInt32LE(n, 0); 105 | this.write(buf); 106 | return this; 107 | }; 108 | 109 | BufW.prototype.writeInt32LE = function(n) { 110 | var buf = new Buffer(4); 111 | buf.writeInt32LE(n, 0); 112 | this.write(buf); 113 | return this; 114 | }; 115 | 116 | BufW.prototype.writeUInt64BEBN = function(bn) { 117 | var buf = bn.toBuffer({size: 8}); 118 | this.write(buf); 119 | return this; 120 | }; 121 | 122 | BufW.prototype.writeUInt64LEBN = function(bn) { 123 | var buf = bn.toBuffer({size: 8}); 124 | this.writeReverse(buf); 125 | return this; 126 | }; 127 | 128 | BufW.prototype.writeVarintNum = function(n) { 129 | var buf = BufW.varintBufNum(n); 130 | this.write(buf); 131 | return this; 132 | }; 133 | 134 | BufW.prototype.writeVarintBN = function(bn) { 135 | var buf = BufW.varintBufBN(bn); 136 | this.write(buf); 137 | return this; 138 | }; 139 | 140 | BufW.varintBufNum = function(n) { 141 | var buf = undefined; 142 | if (n < 253) { 143 | buf = new Buffer(1); 144 | buf.writeUInt8(n, 0); 145 | } else if (n < 0x10000) { 146 | buf = new Buffer(1 + 2); 147 | buf.writeUInt8(253, 0); 148 | buf.writeUInt16LE(n, 1); 149 | } else if (n < 0x100000000) { 150 | buf = new Buffer(1 + 4); 151 | buf.writeUInt8(254, 0); 152 | buf.writeUInt32LE(n, 1); 153 | } else { 154 | buf = new Buffer(1 + 8); 155 | buf.writeUInt8(255, 0); 156 | buf.writeInt32LE(n & -1, 1); 157 | buf.writeUInt32LE(Math.floor(n / 0x100000000), 5); 158 | } 159 | return buf; 160 | }; 161 | 162 | BufW.varintBufBN = function(bn) { 163 | var buf = undefined; 164 | var n = bn.toNumber(); 165 | if (n < 253) { 166 | buf = new Buffer(1); 167 | buf.writeUInt8(n, 0); 168 | } else if (n < 0x10000) { 169 | buf = new Buffer(1 + 2); 170 | buf.writeUInt8(253, 0); 171 | buf.writeUInt16LE(n, 1); 172 | } else if (n < 0x100000000) { 173 | buf = new Buffer(1 + 4); 174 | buf.writeUInt8(254, 0); 175 | buf.writeUInt32LE(n, 1); 176 | } else { 177 | var bw = new BufW(); 178 | bw.writeUInt8(255); 179 | bw.writeUInt64LEBN(bn); 180 | var buf = bw.concat(); 181 | } 182 | return buf; 183 | }; 184 | 185 | module.exports = BufW; 186 | -------------------------------------------------------------------------------- /lib/constants.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Constants 3 | * ========= 4 | * 5 | * Constants used to distinguish mainnet from testnet. 6 | */ 7 | exports.mainnet = { 8 | pubkeyhash: 0x00, 9 | privkey: 0x80, 10 | scripthash: 0x05, 11 | bip32pubkey: 0x0488b21e, 12 | bip32privkey: 0x0488ade4, 13 | }; 14 | 15 | exports.testnet = { 16 | pubkeyhash: 0x6f, 17 | privkey: 0xef, 18 | scripthash: 0xc4, 19 | bip32pubkey: 0x043587cf, 20 | bip32privkey: 0x04358394, 21 | }; 22 | -------------------------------------------------------------------------------- /lib/expmt/aes.js: -------------------------------------------------------------------------------- 1 | var aes = require('aes'); 2 | 3 | var AES = function AES() { 4 | }; 5 | 6 | AES.encrypt = function(messagebuf, keybuf) { 7 | var key = AES.buf2words(keybuf); 8 | var message = AES.buf2words(messagebuf); 9 | var a = new aes(key); 10 | var enc = a.encrypt(message); 11 | var encbuf = AES.words2buf(enc); 12 | return encbuf; 13 | }; 14 | 15 | AES.decrypt = function(encbuf, keybuf) { 16 | var enc = AES.buf2words(encbuf); 17 | var key = AES.buf2words(keybuf); 18 | var a = new aes(key); 19 | var message = a.decrypt(enc); 20 | var messagebuf = AES.words2buf(message); 21 | return messagebuf; 22 | }; 23 | 24 | AES.buf2words = function(buf) { 25 | if (buf.length % 4) 26 | throw new Error('buf length must be a multiple of 4'); 27 | 28 | var words = []; 29 | 30 | for (var i = 0; i < buf.length / 4; i++) { 31 | words.push(buf.readUInt32BE(i * 4)); 32 | }; 33 | 34 | return words; 35 | }; 36 | 37 | AES.words2buf = function(words) { 38 | var buf = new Buffer(words.length * 4); 39 | 40 | for (var i = 0; i < words.length; i++) { 41 | buf.writeUInt32BE(words[i], i * 4); 42 | }; 43 | 44 | return buf; 45 | }; 46 | 47 | module.exports = AES; 48 | -------------------------------------------------------------------------------- /lib/expmt/aescbc.js: -------------------------------------------------------------------------------- 1 | var AES = require('./aes'); 2 | var CBC = require('./cbc'); 3 | var Random = require('../random'); 4 | var Hash = require('../hash'); 5 | 6 | // Symmetric encryption with AES and CBC convenience class 7 | var AESCBC = function AESCBC() { 8 | }; 9 | 10 | AESCBC.encrypt = function(messagebuf, passwordstr) { 11 | var cipherkeybuf = Hash.sha256(new Buffer(passwordstr)); 12 | return AESCBC.encryptCipherkey(messagebuf, cipherkeybuf); 13 | }; 14 | 15 | AESCBC.decrypt = function(encbuf, passwordstr) { 16 | var cipherkeybuf = Hash.sha256(new Buffer(passwordstr)); 17 | return AESCBC.decryptCipherkey(encbuf, cipherkeybuf); 18 | }; 19 | 20 | AESCBC.encryptCipherkey = function(messagebuf, cipherkeybuf, ivbuf) { 21 | ivbuf = ivbuf || Random.getRandomBuffer(128 / 8); 22 | var ctbuf = CBC.encrypt(messagebuf, ivbuf, AES, cipherkeybuf); 23 | var encbuf = Buffer.concat([ivbuf, ctbuf]); 24 | return encbuf; 25 | }; 26 | 27 | AESCBC.decryptCipherkey = function(encbuf, cipherkeybuf) { 28 | var ivbuf = encbuf.slice(0, 128 / 8); 29 | var ctbuf = encbuf.slice(128 / 8); 30 | var messagebuf = CBC.decrypt(ctbuf, ivbuf, AES, cipherkeybuf); 31 | return messagebuf; 32 | }; 33 | 34 | module.exports = AESCBC; 35 | -------------------------------------------------------------------------------- /lib/expmt/cbc.js: -------------------------------------------------------------------------------- 1 | var Random = require('../random'); 2 | 3 | // Cipher Block Chaining 4 | // http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29 5 | var CBC = function CBC(blockcipher, cipherkeybuf, ivbuf) { 6 | if (!(this instanceof CBC)) 7 | return new CBC(blockcipher, cipherkeybuf, ivbuf); 8 | 9 | this.blockcipher = blockcipher; 10 | this.cipherkeybuf = cipherkeybuf; 11 | this.ivbuf = ivbuf; 12 | }; 13 | 14 | CBC.buf2blockbufs = function(buf, blocksize) { 15 | var bytesize = blocksize / 8; 16 | var blockbufs = []; 17 | 18 | for (var i = 0; i <= buf.length / bytesize; i++) { 19 | var blockbuf = buf.slice(i * bytesize, i * bytesize + bytesize); 20 | 21 | if (blockbuf.length < blocksize) 22 | blockbuf = CBC.pkcs7pad(blockbuf, blocksize); 23 | 24 | blockbufs.push(blockbuf); 25 | } 26 | 27 | return blockbufs; 28 | }; 29 | 30 | CBC.blockbufs2buf = function(blockbufs, blocksize) { 31 | var bytesize = blocksize / 8; 32 | 33 | var last = blockbufs[blockbufs.length - 1]; 34 | last = CBC.pkcs7unpad(last); 35 | blockbufs[blockbufs.length - 1] = last; 36 | 37 | var buf = Buffer.concat(blockbufs); 38 | 39 | return buf; 40 | }; 41 | 42 | CBC.encrypt = function(messagebuf, ivbuf, blockcipher, cipherkeybuf) { 43 | var blocksize = ivbuf.length * 8; 44 | var blockbufs = CBC.buf2blockbufs(messagebuf, blocksize); 45 | var encbufs = CBC.encryptblocks(blockbufs, ivbuf, blockcipher, cipherkeybuf); 46 | var encbuf = Buffer.concat(encbufs); 47 | return encbuf; 48 | }; 49 | 50 | CBC.decrypt = function(encbuf, ivbuf, blockcipher, cipherkeybuf) { 51 | var blocksize = ivbuf.length * 8; 52 | var bytesize = ivbuf.length; 53 | var encbufs = []; 54 | for (var i = 0; i < encbuf.length / bytesize; i++) { 55 | encbufs.push(encbuf.slice(i * bytesize, i * bytesize + bytesize)); 56 | } 57 | var blockbufs = CBC.decryptblocks(encbufs, ivbuf, blockcipher, cipherkeybuf); 58 | var buf = CBC.blockbufs2buf(blockbufs, blocksize); 59 | return buf; 60 | }; 61 | 62 | CBC.encryptblock = function(blockbuf, ivbuf, blockcipher, cipherkeybuf) { 63 | var xorbuf = CBC.xorbufs(blockbuf, ivbuf); 64 | var encbuf = blockcipher.encrypt(xorbuf, cipherkeybuf); 65 | return encbuf; 66 | }; 67 | 68 | CBC.decryptblock = function(encbuf, ivbuf, blockcipher, cipherkeybuf) { 69 | var xorbuf = blockcipher.decrypt(encbuf, cipherkeybuf); 70 | var blockbuf = CBC.xorbufs(xorbuf, ivbuf); 71 | return blockbuf; 72 | }; 73 | 74 | CBC.encryptblocks = function(blockbufs, ivbuf, blockcipher, cipherkeybuf) { 75 | var encbufs = []; 76 | 77 | for (var i = 0; i < blockbufs.length; i++) { 78 | var blockbuf = blockbufs[i]; 79 | var encbuf = CBC.encryptblock(blockbuf, ivbuf, blockcipher, cipherkeybuf); 80 | 81 | encbufs.push(encbuf); 82 | 83 | ivbuf = encbuf; 84 | } 85 | 86 | return encbufs; 87 | }; 88 | 89 | CBC.decryptblocks = function(encbufs, ivbuf, blockcipher, cipherkeybuf) { 90 | var blockbufs = []; 91 | 92 | for (var i = 0; i < encbufs.length; i++) { 93 | var encbuf = encbufs[i]; 94 | var blockbuf = CBC.decryptblock(encbuf, ivbuf, blockcipher, cipherkeybuf); 95 | 96 | blockbufs.push(blockbuf); 97 | 98 | ivbuf = encbuf; 99 | } 100 | 101 | return blockbufs; 102 | }; 103 | 104 | CBC.pkcs7pad = function(buf, blocksize) { 105 | var bytesize = blocksize / 8; 106 | var padbytesize = bytesize - buf.length; 107 | var pad = new Buffer(padbytesize); 108 | pad.fill(padbytesize); 109 | var paddedbuf = Buffer.concat([buf, pad]); 110 | return paddedbuf; 111 | }; 112 | 113 | CBC.pkcs7unpad = function(paddedbuf, blocksize) { 114 | var bytesize = blocksize / 8; 115 | var padbytesize = bytesize - paddedbuf.length; 116 | var padlength = paddedbuf[paddedbuf.length - 1]; 117 | var padbuf = paddedbuf.slice(paddedbuf.length - padlength, paddedbuf.length); 118 | var padbuf2 = new Buffer(padlength); 119 | padbuf2.fill(padlength); 120 | if (padbuf.toString('hex') !== padbuf2.toString('hex')) 121 | throw new Error('invalid padding'); 122 | return paddedbuf.slice(0, paddedbuf.length - padlength); 123 | }; 124 | 125 | CBC.xorbufs = function(buf1, buf2) { 126 | if (buf1.length !== buf2.length) 127 | throw new Error('bufs must have the same length'); 128 | 129 | var buf = new Buffer(buf1.length); 130 | 131 | for (var i = 0; i < buf1.length; i++) { 132 | buf[i] = buf1[i] ^ buf2[i]; 133 | } 134 | 135 | return buf; 136 | }; 137 | 138 | module.exports = CBC; 139 | -------------------------------------------------------------------------------- /lib/expmt/ecies.js: -------------------------------------------------------------------------------- 1 | var AESCBC = require('./aescbc'); 2 | var Keypair = require('../keypair'); 3 | var Point = require('../point'); 4 | var Hash = require('../hash'); 5 | var Pubkey = require('../pubkey'); 6 | var Privkey = require('../privkey'); 7 | 8 | // http://en.wikipedia.org/wiki/Integrated_Encryption_Scheme 9 | var ECIES = function ECIES() { 10 | if (!(this instanceof ECIES)) 11 | return new ECIES(); 12 | }; 13 | 14 | ECIES.encrypt = function(messagebuf, topubkey, fromkeypair, ivbuf) { 15 | if (!fromkeypair) 16 | fromkeypair = Keypair().fromRandom(); 17 | var r = fromkeypair.privkey.bn; 18 | var R = fromkeypair.pubkey.point; 19 | var Rpubkey = fromkeypair.pubkey; 20 | var Rbuf = Rpubkey.toDER(true); 21 | var KB = topubkey.point; 22 | var P = KB.mul(r); 23 | var S = P.getX(); 24 | var Sbuf = S.toBuffer({size: 32}); 25 | var kEkM = Hash.sha512(Sbuf); 26 | var kE = kEkM.slice(0, 32); 27 | var kM = kEkM.slice(32, 64); 28 | var c = AESCBC.encryptCipherkey(messagebuf, kE, ivbuf); 29 | var d = Hash.sha256hmac(c, kM); 30 | var encbuf = Buffer.concat([Rbuf, c, d]); 31 | return encbuf; 32 | }; 33 | 34 | ECIES.decrypt = function(encbuf, toprivkey) { 35 | var kB = toprivkey.bn; 36 | var frompubkey = Pubkey().fromDER(encbuf.slice(0, 33)); 37 | var R = frompubkey.point; 38 | var P = R.mul(kB); 39 | if (P.eq(new Point())) 40 | throw new Error('P equals 0'); 41 | var S = P.getX(); 42 | var Sbuf = S.toBuffer({size: 32}); 43 | var kEkM = Hash.sha512(Sbuf); 44 | var kE = kEkM.slice(0, 32); 45 | var kM = kEkM.slice(32, 64); 46 | var c = encbuf.slice(33, encbuf.length - 32); 47 | var d = encbuf.slice(encbuf.length - 32, encbuf.length); 48 | var d2 = Hash.sha256hmac(c, kM); 49 | if (d.toString('hex') !== d2.toString('hex')) 50 | throw new Error('Invalid checksum'); 51 | var messagebuf = AESCBC.decryptCipherkey(c, kE); 52 | return messagebuf; 53 | }; 54 | 55 | module.exports = ECIES; 56 | -------------------------------------------------------------------------------- /lib/expmt/stealth/address.js: -------------------------------------------------------------------------------- 1 | var SKey = require('./key'); 2 | var Base58check = require('../../base58check'); 3 | var Pubkey = require('../../pubkey'); 4 | var KDF = require('../../kdf'); 5 | var BufW = require('../../bufw'); 6 | var BufR = require('../../bufr'); 7 | 8 | var SAddress = function SAddress(addrstr) { 9 | if (!(this instanceof SAddress)) 10 | return new SAddress(addrstr); 11 | 12 | if (typeof addrstr === 'string') { 13 | this.fromString(addrstr) 14 | } 15 | else if (Buffer.isBuffer(addrstr)) { 16 | var buf = addrstr; 17 | this.fromBuffer(buf); 18 | } 19 | else if (addrstr) { 20 | var obj = addrstr; 21 | this.set(obj); 22 | } 23 | }; 24 | 25 | SAddress.mainver = 42; 26 | SAddress.testver = 43; 27 | 28 | SAddress.prototype.set = function(obj) { 29 | this.payloadPubkey = obj.payloadPubkey || this.payloadPubkey; 30 | this.scanPubkey = obj.scanPubkey || this.scanPubkey; 31 | return this; 32 | }; 33 | 34 | SAddress.prototype.fromJSON = function(json) { 35 | this.fromString(json); 36 | return this; 37 | }; 38 | 39 | SAddress.prototype.toJSON = function() { 40 | return this.toString(); 41 | }; 42 | 43 | SAddress.prototype.fromSKey = function(stealthkey) { 44 | this.set({ 45 | payloadPubkey: stealthkey.payloadKeypair.pubkey, 46 | scanPubkey: stealthkey.scanKeypair.pubkey 47 | }); 48 | return this; 49 | }; 50 | 51 | SAddress.prototype.fromBuffer = function(buf) { 52 | var parsed = SAddress.parseDWBuffer(buf); 53 | if ((parsed.version !== SAddress.mainver) && (parsed.version !== SAddress.testver)) 54 | throw new Error('Invalid version'); 55 | if (parsed.options !== 0) 56 | throw new Error('Invalid options'); 57 | if (!parsed.scanPubkey) 58 | throw new Error('Invalid scanPubkey'); 59 | if (parsed.payloadPubkeys.length !== 1) 60 | throw new Error('Must have exactly one payloadPubkey'); 61 | if (parsed.nSigs !== 1) 62 | throw new Error('Must require exactly one signature'); 63 | if (parsed.prefix.toString() !== "") 64 | throw new Error('Only blank prefixes supported'); 65 | this.scanPubkey = parsed.scanPubkey; 66 | this.payloadPubkey = parsed.payloadPubkeys[0]; 67 | return this; 68 | }; 69 | 70 | SAddress.prototype.fromString = function(str) { 71 | return this.fromBuffer(Base58check(str).toBuffer()); 72 | }; 73 | 74 | SAddress.prototype.getSharedKeypair = function(senderKeypair) { 75 | var sharedSecretPoint = this.scanPubkey.point.mul(senderKeypair.privkey.bn); 76 | var sharedSecretPubkey = Pubkey(sharedSecretPoint); 77 | var buf = sharedSecretPubkey.toDER(true); 78 | var sharedKeypair = KDF.sha256hmac2keypair(buf); 79 | 80 | return sharedKeypair; 81 | }; 82 | 83 | SAddress.prototype.getReceivePubkey = function(senderKeypair) { 84 | var sharedKeypair = this.getSharedKeypair(senderKeypair); 85 | var pubkey = Pubkey(this.payloadPubkey.point.add(sharedKeypair.pubkey.point)); 86 | 87 | return pubkey; 88 | }; 89 | 90 | SAddress.prototype.toBuffer = function(networkstr) { 91 | if (networkstr === 'testnet') 92 | var version = SAddress.testver; 93 | else 94 | var version = SAddress.mainver; 95 | var bw = new BufW(); 96 | bw.writeUInt8(version); 97 | bw.writeUInt8(0); //options 98 | bw.write(this.scanPubkey.toDER(true)); 99 | bw.writeUInt8(1); //number of payload keys - we only support 1 (not multisig) 100 | bw.write(this.payloadPubkey.toDER(true)); 101 | bw.writeUInt8(1); //number of signatures - we only support 1 (not multisig) 102 | bw.writeUInt8(0); //prefix length - we do not support prefix yet 103 | var buf = bw.concat(); 104 | return buf; 105 | }; 106 | 107 | SAddress.prototype.toString = function(networkstr) { 108 | return Base58check(this.toBuffer(networkstr)).toString(); 109 | }; 110 | 111 | SAddress.parseDWBuffer = function(buf) { 112 | var br = new BufR(buf); 113 | var parsed = {}; 114 | parsed.version = br.readUInt8(); 115 | parsed.options = br.readUInt8(); 116 | parsed.scanPubkey = Pubkey().fromBuffer(br.read(33)); 117 | parsed.nPayloadPubkeys = br.readUInt8(); 118 | parsed.payloadPubkeys = []; 119 | for (var i = 0; i < parsed.nPayloadPubkeys; i++) 120 | parsed.payloadPubkeys.push(Pubkey().fromBuffer(br.read(33))); 121 | parsed.nSigs = br.readUInt8(); 122 | parsed.nPrefix = br.readUInt8(); 123 | parsed.prefix = br.read(parsed.nPrefix / 8); 124 | return parsed; 125 | }; 126 | 127 | module.exports = SAddress; 128 | -------------------------------------------------------------------------------- /lib/expmt/stealth/key.js: -------------------------------------------------------------------------------- 1 | var Keypair = require('../../keypair'); 2 | var Privkey = require('../../privkey'); 3 | var Pubkey = require('../../pubkey'); 4 | var Point = require('../../point'); 5 | var Hash = require('../../hash'); 6 | var KDF = require('../../kdf'); 7 | 8 | var SKey = function SKey(payloadKeypair, scanKeypair) { 9 | if (!(this instanceof SKey)) 10 | return new SKey(payloadKeypair, scanKeypair); 11 | 12 | if (payloadKeypair instanceof Keypair) { 13 | this.set({ 14 | payloadKeypair: payloadKeypair, 15 | scanKeypair: scanKeypair 16 | }); 17 | } 18 | else if (payloadKeypair) { 19 | var obj = payloadKeypair; 20 | this.set(obj); 21 | } 22 | }; 23 | 24 | SKey.prototype.set = function(obj) { 25 | this.payloadKeypair = obj.payloadKeypair || this.payloadKeypair; 26 | this.scanKeypair = obj.scanKeypair || this.scanKeypair; 27 | return this; 28 | }; 29 | 30 | SKey.prototype.fromJSON = function(json) { 31 | this.set({ 32 | payloadKeypair: Keypair().fromJSON(json.payloadKeypair), 33 | scanKeypair: Keypair().fromJSON(json.scanKeypair) 34 | }); 35 | return this; 36 | }; 37 | 38 | SKey.prototype.toJSON = function() { 39 | return { 40 | payloadKeypair: this.payloadKeypair.toJSON(), 41 | scanKeypair: this.scanKeypair.toJSON() 42 | }; 43 | }; 44 | 45 | SKey.prototype.fromRandom = function() { 46 | this.payloadKeypair = Keypair().fromRandom(); 47 | this.scanKeypair = Keypair().fromRandom(); 48 | 49 | return this; 50 | }; 51 | 52 | SKey.prototype.getSharedKeypair = function(senderPubkey) { 53 | var sharedSecretPoint = senderPubkey.point.mul(this.scanKeypair.privkey.bn); 54 | var sharedSecretPubkey = Pubkey({point: sharedSecretPoint}); 55 | var buf = sharedSecretPubkey.toDER(true); 56 | var sharedKeypair = KDF.sha256hmac2keypair(buf); 57 | 58 | return sharedKeypair; 59 | }; 60 | 61 | SKey.prototype.getReceivePubkey = function(senderPubkey) { 62 | var sharedKeypair = this.getSharedKeypair(senderPubkey); 63 | var pubkey = Pubkey({point: this.payloadKeypair.pubkey.point.add(sharedKeypair.pubkey.point)}); 64 | 65 | return pubkey; 66 | }; 67 | 68 | SKey.prototype.getReceiveKeypair = function(senderPubkey) { 69 | var sharedKeypair = this.getSharedKeypair(senderPubkey); 70 | var privkey = Privkey({bn: this.payloadKeypair.privkey.bn.add(sharedKeypair.privkey.bn).mod(Point.getN())}); 71 | var key = Keypair({privkey: privkey}); 72 | key.privkey2pubkey(); 73 | 74 | return key; 75 | }; 76 | 77 | SKey.prototype.isForMe = function(senderPubkey, myPossiblePubkeyhashbuf) { 78 | var pubkey = this.getReceivePubkey(senderPubkey); 79 | var pubkeybuf = pubkey.toDER(true); 80 | var pubkeyhash = Hash.sha256ripemd160(pubkeybuf); 81 | 82 | if (pubkeyhash.toString('hex') === myPossiblePubkeyhashbuf.toString('hex')) 83 | return true; 84 | else 85 | return false; 86 | }; 87 | 88 | module.exports = SKey; 89 | -------------------------------------------------------------------------------- /lib/expmt/stealth/message.js: -------------------------------------------------------------------------------- 1 | var SKey = require('./key'); 2 | var SAddress = require('./address'); 3 | var ECIES = require('../ecies'); 4 | var Keypair = require('../../keypair'); 5 | var Address = require('../../address'); 6 | var Pubkey = require('../../pubkey'); 7 | 8 | var SMessage = function SMessage(obj) { 9 | if (!(this instanceof SMessage)) 10 | return new SMessage(obj); 11 | if (obj) 12 | this.set(obj); 13 | }; 14 | 15 | SMessage.prototype.set = function(obj) { 16 | this.messagebuf = obj.messagebuf || this.messagebuf; 17 | this.encbuf = obj.encbuf || this.encbuf; 18 | this.toSAddress = obj.toSAddress || this.toSAddress; 19 | this.toSKey = obj.toSKey || this.toSKey; 20 | this.fromKeypair = obj.fromKeypair || this.fromKeypair; 21 | this.receiveAddress = obj.receiveAddress || this.receiveAddress; 22 | return this; 23 | }; 24 | 25 | SMessage.encrypt = function(messagebuf, toSAddress, fromKeypair, ivbuf) { 26 | var sm = SMessage().set({ 27 | messagebuf: messagebuf, 28 | toSAddress: toSAddress, 29 | fromKeypair: fromKeypair 30 | }); 31 | sm.encrypt(ivbuf); 32 | var buf = Buffer.concat([ 33 | sm.receiveAddress.hashbuf, 34 | sm.fromKeypair.pubkey.toDER(true), 35 | sm.encbuf 36 | ]); 37 | return buf; 38 | }; 39 | 40 | SMessage.decrypt = function(buf, toSKey) { 41 | var sm = SMessage().set({ 42 | toSKey: toSKey, 43 | receiveAddress: Address().set({hashbuf: buf.slice(0, 20)}), 44 | fromKeypair: Keypair().set({pubkey: Pubkey().fromDER(buf.slice(20, 20 + 33))}), 45 | encbuf: buf.slice(20 + 33) 46 | }); 47 | return sm.decrypt().messagebuf; 48 | }; 49 | 50 | SMessage.isForMe = function(buf, toSKey) { 51 | var sm = SMessage().set({ 52 | toSKey: toSKey, 53 | receiveAddress: Address().set({hashbuf: buf.slice(0, 20)}), 54 | fromKeypair: Keypair().set({pubkey: Pubkey().fromDER(buf.slice(20, 20 + 33))}), 55 | encbuf: buf.slice(20 + 33) 56 | }); 57 | return sm.isForMe(); 58 | }; 59 | 60 | SMessage.prototype.encrypt = function(ivbuf) { 61 | if (!this.fromKeypair) 62 | this.fromKeypair = Keypair().fromRandom(); 63 | var receivePubkey = this.toSAddress.getReceivePubkey(this.fromKeypair); 64 | this.receiveAddress = Address().fromPubkey(receivePubkey); 65 | this.encbuf = ECIES.encrypt(this.messagebuf, receivePubkey, this.fromKeypair, ivbuf); 66 | return this; 67 | }; 68 | 69 | SMessage.prototype.decrypt = function() { 70 | var receiveKeypair = this.toSKey.getReceiveKeypair(this.fromKeypair.pubkey); 71 | this.messagebuf = ECIES.decrypt(this.encbuf, receiveKeypair.privkey); 72 | return this; 73 | }; 74 | 75 | SMessage.prototype.isForMe = function() { 76 | var receivePubkey = this.toSKey.getReceivePubkey(this.fromKeypair.pubkey); 77 | var receiveAddress = Address().fromPubkey(receivePubkey); 78 | if (receiveAddress.toString('hex') === this.receiveAddress.toString('hex')) 79 | return true; 80 | else 81 | return false; 82 | }; 83 | 84 | module.exports = SMessage; 85 | -------------------------------------------------------------------------------- /lib/expmt/stealth/tx.js: -------------------------------------------------------------------------------- 1 | var Tx = require('../../tx'); 2 | var Pubkey = require('../../pubkey'); 3 | 4 | var STx = function STx(tx, sa, sk) { 5 | if (!(this instanceof STx)) 6 | return new STx(tx, sa, sk); 7 | if (tx instanceof Tx) { 8 | this.tx = tx; 9 | this.sa = sa; 10 | this.sk = sk; 11 | } else if (tx) { 12 | var obj = tx; 13 | this.set(obj); 14 | } 15 | }; 16 | 17 | STx.prototype.set = function(obj) { 18 | this.sk = obj.sk || this.sk; 19 | this.sa = obj.sa || this.sa; 20 | this.tx = obj.tx || this.tx; 21 | return this; 22 | }; 23 | 24 | STx.prototype.isForMe = function() { 25 | if (!this.notMine()) 26 | return true; 27 | else 28 | return false; 29 | }; 30 | 31 | STx.prototype.notMine = function() { 32 | var err; 33 | if (err = this.notStealth()) 34 | return "Not stealth: " + err; 35 | var txopbuf = this.tx.txouts[0].script.chunks[1].buf; 36 | var parsed = STx.parseOpReturnData(txopbuf); 37 | var pubkey = parsed.pubkey; 38 | var pubkeyhashbuf = this.tx.txouts[1].script.chunks[2].buf; 39 | var sk = this.sk; 40 | if (sk.isForMe(pubkey, pubkeyhashbuf)) { 41 | return false; 42 | } else { 43 | return "STx not mine"; 44 | } 45 | }; 46 | 47 | //For now, we only support a very limited variety of stealth tx 48 | STx.prototype.notStealth = function() { 49 | var txouts = this.tx.txouts; 50 | if (!(txouts.length >= 2)) 51 | return "Not enough txouts"; 52 | if (!txouts[0].script.isOpReturn()) 53 | return "First txout is not OP_RETURN"; 54 | if (!txouts[1].script.isPubkeyhashOut()) 55 | return "Second txout is not pubkeyhash"; 56 | return false; 57 | }; 58 | 59 | STx.parseOpReturnData = function(buf) { 60 | var parsed = {}; 61 | parsed.version = buf[0]; 62 | parsed.noncebuf = buf.slice(1, 5); 63 | parsed.pubkey = Pubkey().fromBuffer(buf.slice(5, 5 + 33)); 64 | return parsed; 65 | }; 66 | 67 | module.exports = STx; 68 | -------------------------------------------------------------------------------- /lib/hash.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Hash 3 | * ==== 4 | * 5 | * Some hash functions are used through out bitcoin. We expose them here as a 6 | * convenience. 7 | */ 8 | var crypto = require('crypto'); 9 | var hashjs = require('hash.js'); 10 | 11 | var Hash = module.exports; 12 | 13 | Hash.sha1 = function(buf) { 14 | if (!Buffer.isBuffer(buf)) 15 | throw new Error('sha1 hash must be of a buffer'); 16 | var hashbuf = crypto.createHash('sha1').update(buf).digest(); 17 | return hashbuf; 18 | }; 19 | 20 | Hash.sha1.blocksize = 512; 21 | 22 | Hash.sha256 = function(buf) { 23 | if (!Buffer.isBuffer(buf)) 24 | throw new Error('sha256 hash must be of a buffer'); 25 | var hashbuf = crypto.createHash('sha256').update(buf).digest(); 26 | return hashbuf; 27 | }; 28 | 29 | Hash.sha256.blocksize = 512; 30 | 31 | Hash.sha256sha256 = function(buf) { 32 | try { 33 | return Hash.sha256(Hash.sha256(buf)); 34 | } catch (e) { 35 | throw new Error('sha256sha256 hash must be of a buffer: ' + e); 36 | } 37 | }; 38 | 39 | Hash.ripemd160 = function(buf) { 40 | if (!Buffer.isBuffer(buf)) 41 | throw new Error('ripemd160 hash must be of a buffer'); 42 | var hash = (new hashjs.ripemd160()).update(buf).digest(); 43 | return new Buffer(hash); 44 | }; 45 | 46 | Hash.sha256ripemd160 = function(buf) { 47 | try { 48 | return Hash.ripemd160(Hash.sha256(buf)); 49 | } catch (e) { 50 | throw new Error('sha256ripemd160 hash must be of a buffer: ' + e); 51 | } 52 | }; 53 | 54 | Hash.sha512 = function(buf) { 55 | if (!Buffer.isBuffer(buf)) 56 | throw new Error('sha512 hash must be of a buffer'); 57 | var hashbuf = crypto.createHash('sha512').update(buf).digest(); 58 | return hashbuf; 59 | }; 60 | 61 | Hash.sha512.blocksize = 1024; 62 | 63 | Hash.hmac = function(hashfstr, data, key) { 64 | if (hashfstr !== 'sha1' && hashfstr !== 'sha256' && hashfstr !== 'sha512' && hashfstr !== 'ripemd160') 65 | throw new Error('Invalid choice of hash function'); 66 | 67 | var hashf = Hash[hashfstr]; 68 | 69 | if (!Buffer.isBuffer(data) || !Buffer.isBuffer(key)) 70 | throw new Error('data and key must be buffers'); 71 | 72 | //http://en.wikipedia.org/wiki/Hash-based_message_authentication_code 73 | //http://tools.ietf.org/html/rfc4868#section-2 74 | if (!hashf.blocksize) 75 | throw new Error('Blocksize for hash function unknown'); 76 | 77 | var blocksize = hashf.blocksize / 8; 78 | 79 | if (key.length > blocksize) 80 | key = hashf(key); 81 | 82 | if (key.length < blocksize) { 83 | var fill = new Buffer(blocksize); 84 | fill.fill(0, key.length); 85 | key.copy(fill); 86 | key = fill; 87 | } 88 | 89 | var o_key_pad = new Buffer(blocksize); 90 | var i_key_pad = new Buffer(blocksize); 91 | for (var i = 0; i < blocksize; i++) { 92 | o_key_pad[i] = 0x5c ^ key[i]; 93 | i_key_pad[i] = 0x36 ^ key[i]; 94 | } 95 | 96 | return hashf(Buffer.concat([o_key_pad, hashf(Buffer.concat([i_key_pad, data]))])); 97 | }; 98 | 99 | Hash.sha1hmac = function(data, key) { 100 | return Hash.hmac('sha1', data, key); 101 | }; 102 | 103 | Hash.sha1hmac.bitsize = 160; 104 | 105 | Hash.sha256hmac = function(data, key) { 106 | return Hash.hmac('sha256', data, key); 107 | }; 108 | 109 | Hash.sha256hmac.bitsize = 256; 110 | 111 | Hash.sha512hmac = function(data, key) { 112 | return Hash.hmac('sha512', data, key); 113 | }; 114 | 115 | Hash.sha512hmac.bitsize = 512; 116 | -------------------------------------------------------------------------------- /lib/kdf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * KDF 3 | * === 4 | * 5 | * KDF stands for "key derivation function". A key derivation function is a 6 | * function that converts random data into a properly formatted key that can be 7 | * used by a cryptographic function. The one you probably want to use is 8 | * PBKDF2, although various other ones are provided as a convenience. 9 | */ 10 | var Bn = require('./bn'); 11 | var Privkey = require('./privkey'); 12 | var Point = require('./point'); 13 | var Pubkey = require('./pubkey'); 14 | var Keypair = require('./keypair'); 15 | var Hash = require('./hash'); 16 | var pbkdf2 = require('pbkdf2-compat'); 17 | 18 | function KDF() { 19 | }; 20 | 21 | // PBKDF2 22 | // http://tools.ietf.org/html/rfc2898#section-5.2 23 | // http://en.wikipedia.org/wiki/PBKDF2 24 | KDF.PBKDF2 = function(passbuf, saltbuf, niterations, keylenbits, hashfstr) { 25 | if (!niterations) 26 | niterations = 1; 27 | if (!keylenbits) 28 | keylenbits = 512; 29 | if (!hashfstr) 30 | hashfstr = 'sha512'; 31 | return pbkdf2.pbkdf2Sync(passbuf, saltbuf, niterations, keylenbits / 8, hashfstr); 32 | }; 33 | 34 | KDF.buf2keypair = function(buf) { 35 | return KDF.sha256hmac2keypair(buf); 36 | }; 37 | 38 | KDF.sha256hmac2keypair = function(buf) { 39 | var privkey = KDF.sha256hmac2privkey(buf); 40 | var keypair = Keypair().fromPrivkey(privkey); 41 | return keypair; 42 | }; 43 | 44 | KDF.sha256hmac2privkey = function(buf) { 45 | var bn; 46 | var concat = new Buffer([]); 47 | do { 48 | var hash = Hash.sha256hmac(buf, concat); 49 | var bn = Bn.fromBuffer(hash); 50 | concat = Buffer.concat([concat, new Buffer(0)]); 51 | } while(!bn.lt(Point.getN())); 52 | return new Privkey({bn: bn}); 53 | }; 54 | 55 | module.exports = KDF; 56 | -------------------------------------------------------------------------------- /lib/keypair.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Keypair 3 | * ======= 4 | * 5 | * A keypair is just a collection of a private key and a public key. Note that 6 | * in this implementation, both private key and public key are optional. If the 7 | * public key is not present, you can derive it from the private key using 8 | * privkey2pubkey(). If the private key is not present, there is no way to 9 | * derive it from the public key. 10 | */ 11 | var Privkey = require('./privkey'); 12 | var Pubkey = require('./pubkey'); 13 | var BN = require('./bn'); 14 | var point = require('./point'); 15 | 16 | var Keypair = function Keypair(obj) { 17 | if (!(this instanceof Keypair)) 18 | return new Keypair(obj); 19 | if (obj) 20 | this.set(obj); 21 | }; 22 | 23 | Keypair.prototype.set = function(obj) { 24 | this.privkey = obj.privkey || this.privkey || undefined; 25 | this.pubkey = obj.pubkey || this.pubkey || undefined; 26 | return this; 27 | }; 28 | 29 | Keypair.prototype.fromJSON = function(json) { 30 | if (json.privkey) 31 | this.set({privkey: Privkey().fromJSON(json.privkey)}); 32 | if (json.pubkey) 33 | this.set({pubkey: Pubkey().fromJSON(json.pubkey)}); 34 | return this; 35 | }; 36 | 37 | Keypair.prototype.toJSON = function() { 38 | var json = {}; 39 | if (this.privkey) 40 | json.privkey = this.privkey.toJSON(); 41 | if (this.pubkey) 42 | json.pubkey = this.pubkey.toJSON(); 43 | return json; 44 | }; 45 | 46 | Keypair.prototype.fromPrivkey = function(privkey) { 47 | this.privkey = privkey; 48 | this.privkey2pubkey(); 49 | return this; 50 | }; 51 | 52 | Keypair.prototype.fromRandom = function() { 53 | this.privkey = Privkey().fromRandom(); 54 | this.privkey2pubkey(); 55 | return this; 56 | }; 57 | 58 | Keypair.prototype.fromString = function(str) { 59 | var obj = JSON.parse(str); 60 | if (obj.privkey) { 61 | this.privkey = new Privkey(); 62 | this.privkey.fromString(obj.privkey); 63 | } 64 | if (obj.pubkey) { 65 | this.pubkey = new Pubkey(); 66 | this.pubkey.fromString(obj.pubkey); 67 | } 68 | }; 69 | 70 | Keypair.prototype.privkey2pubkey = function() { 71 | this.pubkey = Pubkey().fromPrivkey(this.privkey); 72 | }; 73 | 74 | Keypair.prototype.toString = function() { 75 | var obj = {}; 76 | if (this.privkey) 77 | obj.privkey = this.privkey.toString(); 78 | if (this.pubkey) 79 | obj.pubkey = this.pubkey.toString(); 80 | return JSON.stringify(obj); 81 | }; 82 | 83 | module.exports = Keypair; 84 | -------------------------------------------------------------------------------- /lib/opcode.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Opcode 3 | * ====== 4 | * 5 | * An opcode is one of the operations in the bitcoin scripting language. Each 6 | * operation is just a number from 0-255, and it has a corresponding string, 7 | * e.g. "OP_RETURN", which comes from the name of that constant in the bitcoind 8 | * source code. The way you probably want to use this is with 9 | * Opcode(str).toNumber() or Opcode(num).toString() 10 | */ 11 | function Opcode(num) { 12 | if (!(this instanceof Opcode)) 13 | return new Opcode(num); 14 | 15 | if (typeof num === 'number') { 16 | this.num = num; 17 | } else if (typeof num === 'string') { 18 | var str = num; 19 | this.num = map[str]; 20 | } else if (num) { 21 | var obj = num; 22 | this.set(obj); 23 | } 24 | } 25 | 26 | Opcode.prototype.set = function(obj) { 27 | this.num = typeof obj.num !== 'undefined' ? obj.num : this.num; 28 | return this; 29 | }; 30 | 31 | Opcode.prototype.fromNumber = function(num) { 32 | this.num = num; 33 | return this; 34 | }; 35 | 36 | Opcode.prototype.toNumber = function() { 37 | return this.num; 38 | }; 39 | 40 | Opcode.prototype.fromString = function(str) { 41 | var num = map[str]; 42 | if (typeof num === 'undefined') 43 | throw new Error('Invalid opcodestr'); 44 | this.num = num; 45 | return this; 46 | }; 47 | 48 | Opcode.prototype.toString = function() { 49 | var str = Opcode.str[this.num]; 50 | if (typeof str === 'undefined') { 51 | console.log('opcodenum: ' + this.num); 52 | throw new Error('Opcode does not have a string representation'); 53 | } 54 | return str; 55 | }; 56 | 57 | var map = { 58 | // push value 59 | OP_FALSE: 0x00, 60 | OP_0: 0x00, 61 | OP_PUSHDATA1: 0x4c, 62 | OP_PUSHDATA2: 0x4d, 63 | OP_PUSHDATA4: 0x4e, 64 | OP_1NEGATE: 0x4f, 65 | OP_RESERVED: 0x50, 66 | OP_TRUE: 0x51, 67 | OP_1: 0x51, 68 | OP_2: 0x52, 69 | OP_3: 0x53, 70 | OP_4: 0x54, 71 | OP_5: 0x55, 72 | OP_6: 0x56, 73 | OP_7: 0x57, 74 | OP_8: 0x58, 75 | OP_9: 0x59, 76 | OP_10: 0x5a, 77 | OP_11: 0x5b, 78 | OP_12: 0x5c, 79 | OP_13: 0x5d, 80 | OP_14: 0x5e, 81 | OP_15: 0x5f, 82 | OP_16: 0x60, 83 | 84 | // control 85 | OP_NOP: 0x61, 86 | OP_VER: 0x62, 87 | OP_IF: 0x63, 88 | OP_NOTIF: 0x64, 89 | OP_VERIF: 0x65, 90 | OP_VERNOTIF: 0x66, 91 | OP_ELSE: 0x67, 92 | OP_ENDIF: 0x68, 93 | OP_VERIFY: 0x69, 94 | OP_RETURN: 0x6a, 95 | 96 | // stack ops 97 | OP_TOALTSTACK: 0x6b, 98 | OP_FROMALTSTACK: 0x6c, 99 | OP_2DROP: 0x6d, 100 | OP_2DUP: 0x6e, 101 | OP_3DUP: 0x6f, 102 | OP_2OVER: 0x70, 103 | OP_2ROT: 0x71, 104 | OP_2SWAP: 0x72, 105 | OP_IFDUP: 0x73, 106 | OP_DEPTH: 0x74, 107 | OP_DROP: 0x75, 108 | OP_DUP: 0x76, 109 | OP_NIP: 0x77, 110 | OP_OVER: 0x78, 111 | OP_PICK: 0x79, 112 | OP_ROLL: 0x7a, 113 | OP_ROT: 0x7b, 114 | OP_SWAP: 0x7c, 115 | OP_TUCK: 0x7d, 116 | 117 | // splice ops 118 | OP_CAT: 0x7e, 119 | OP_SUBSTR: 0x7f, 120 | OP_LEFT: 0x80, 121 | OP_RIGHT: 0x81, 122 | OP_SIZE: 0x82, 123 | 124 | // bit logic 125 | OP_INVERT: 0x83, 126 | OP_AND: 0x84, 127 | OP_OR: 0x85, 128 | OP_XOR: 0x86, 129 | OP_EQUAL: 0x87, 130 | OP_EQUALVERIFY: 0x88, 131 | OP_RESERVED1: 0x89, 132 | OP_RESERVED2: 0x8a, 133 | 134 | // numeric 135 | OP_1ADD: 0x8b, 136 | OP_1SUB: 0x8c, 137 | OP_2MUL: 0x8d, 138 | OP_2DIV: 0x8e, 139 | OP_NEGATE: 0x8f, 140 | OP_ABS: 0x90, 141 | OP_NOT: 0x91, 142 | OP_0NOTEQUAL: 0x92, 143 | 144 | OP_ADD: 0x93, 145 | OP_SUB: 0x94, 146 | OP_MUL: 0x95, 147 | OP_DIV: 0x96, 148 | OP_MOD: 0x97, 149 | OP_LSHIFT: 0x98, 150 | OP_RSHIFT: 0x99, 151 | 152 | OP_BOOLAND: 0x9a, 153 | OP_BOOLOR: 0x9b, 154 | OP_NUMEQUAL: 0x9c, 155 | OP_NUMEQUALVERIFY: 0x9d, 156 | OP_NUMNOTEQUAL: 0x9e, 157 | OP_LESSTHAN: 0x9f, 158 | OP_GREATERTHAN: 0xa0, 159 | OP_LESSTHANOREQUAL: 0xa1, 160 | OP_GREATERTHANOREQUAL: 0xa2, 161 | OP_MIN: 0xa3, 162 | OP_MAX: 0xa4, 163 | 164 | OP_WITHIN: 0xa5, 165 | 166 | // crypto 167 | OP_RIPEMD160: 0xa6, 168 | OP_SHA1: 0xa7, 169 | OP_SHA256: 0xa8, 170 | OP_HASH160: 0xa9, 171 | OP_HASH256: 0xaa, 172 | OP_CODESEPARATOR: 0xab, 173 | OP_CHECKSIG: 0xac, 174 | OP_CHECKSIGVERIFY: 0xad, 175 | OP_CHECKMULTISIG: 0xae, 176 | OP_CHECKMULTISIGVERIFY: 0xaf, 177 | 178 | // expansion 179 | OP_NOP1: 0xb0, 180 | OP_NOP2: 0xb1, 181 | OP_NOP3: 0xb2, 182 | OP_NOP4: 0xb3, 183 | OP_NOP5: 0xb4, 184 | OP_NOP6: 0xb5, 185 | OP_NOP7: 0xb6, 186 | OP_NOP8: 0xb7, 187 | OP_NOP9: 0xb8, 188 | OP_NOP10: 0xb9, 189 | 190 | 191 | // template matching params 192 | OP_SMALLDATA: 0xf9, 193 | OP_SMALLINTEGER: 0xfa, 194 | OP_PUBKEYS: 0xfb, 195 | OP_PUBKEYHASH: 0xfd, 196 | OP_PUBKEY: 0xfe, 197 | 198 | OP_INVALIDOPCODE: 0xff 199 | }; 200 | 201 | Opcode.str = {} 202 | 203 | for (var k in map) { 204 | Opcode[k] = map[k]; 205 | if (map.hasOwnProperty(k)) { 206 | Opcode.str[map[k]] = k; 207 | } 208 | } 209 | 210 | module.exports = Opcode; 211 | -------------------------------------------------------------------------------- /lib/point.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Point (on secp256k1) 3 | * ==================== 4 | * 5 | * A point is a point on the secp256k1 curve which is the elliptic curve used 6 | * by bitcoin. This code is a wrapper for Fedor Indutny's Point class from his 7 | * elliptic library. This code adds a few minor conveniences, but is mostly the 8 | * same. 9 | */ 10 | var BN = require('./bn'); 11 | var elliptic = require('elliptic'); 12 | var ec = elliptic.curves.secp256k1; 13 | 14 | var Point = function Point(x, y, isRed) { 15 | return ec.curve.point(x, y, isRed); 16 | }; 17 | 18 | var _point = ec.curve.point(); 19 | var _Point = _point.constructor; 20 | Point.prototype = _Point.prototype; 21 | 22 | Point.fromX = ec.curve.pointFromX.bind(ec.curve); 23 | 24 | Point.prototype.copy = function(point) { 25 | for (var k in this) { 26 | if (this.hasOwnProperty(k)) { 27 | point[k] = this[k]; 28 | } 29 | } 30 | return this; 31 | }; 32 | 33 | Point.prototype.fromX = function(isOdd, x) { 34 | var point = Point.fromX(isOdd, x); 35 | point.copy(this); 36 | return this; 37 | }; 38 | 39 | // note that this overrides the elliptic point toJSON method 40 | Point.prototype.toJSON = function() { 41 | return { 42 | isOdd: this.x.isOdd(), 43 | x: this.x.toString() 44 | }; 45 | }; 46 | 47 | Point.prototype.fromJSON = function(json) { 48 | var point = Point().fromX(json.isOdd, BN().fromString(json.x)); 49 | point.copy(this); 50 | return this; 51 | }; 52 | 53 | Point.prototype.toString = function() { 54 | return JSON.stringify(this.toJSON()); 55 | }; 56 | 57 | Point.prototype.fromString = function(str) { 58 | var json = JSON.parse(str); 59 | var p = Point().fromJSON(json); 60 | p.copy(this); 61 | return this; 62 | }; 63 | 64 | Point.getG = function() { 65 | var g = ec.curve.g; 66 | return g; 67 | }; 68 | 69 | Point.getN = function() { 70 | return BN(ec.curve.n.toArray()); 71 | }; 72 | 73 | //https://www.iacr.org/archive/pkc2003/25670211/25670211.pdf 74 | Point.prototype.validate = function() { 75 | var p2 = Point.fromX(this.getY().isOdd(), this.getX()); 76 | if (!(p2.y.cmp(this.y) === 0)) 77 | throw new Error('Invalid y value of public key'); 78 | if (!(this.getX().gt(-1) && this.getX().lt(Point.getN())) 79 | ||!(this.getY().gt(-1) && this.getY().lt(Point.getN()))) 80 | throw new Error('Point does not lie on the curve'); 81 | if (!(this.mul(Point.getN()).isInfinity())) 82 | throw new Error('Point times N must be infinity'); 83 | return this; 84 | }; 85 | 86 | module.exports = Point; 87 | -------------------------------------------------------------------------------- /lib/privkey.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Private Key 3 | * =========== 4 | * 5 | * A private key is used for signing transactions (or messages). The primary 6 | * way to use this is Privkey().fromRandom(), or Privkey().fromBuffer(buf). 7 | */ 8 | var BN = require('./bn'); 9 | var Point = require('./point'); 10 | var constants = require('./constants'); 11 | var base58check = require('./base58check'); 12 | var Random = require('./random'); 13 | 14 | var Privkey = function Privkey(bn) { 15 | if (!(this instanceof Privkey)) 16 | return new Privkey(bn); 17 | if (bn instanceof BN) 18 | this.bn = bn; 19 | else if (bn) { 20 | var obj = bn; 21 | this.set(obj); 22 | } 23 | }; 24 | 25 | Privkey.prototype.set = function(obj) { 26 | this.bn = obj.bn || this.bn; 27 | this.networkstr = obj.networkstr || this.networkstr; 28 | this.compressed = typeof obj.compressed !== 'undefined' ? obj.compressed : this.compressed; 29 | return this; 30 | }; 31 | 32 | Privkey.prototype.fromJSON = function(json) { 33 | this.fromString(json); 34 | return this; 35 | }; 36 | 37 | Privkey.prototype.toJSON = function() { 38 | return this.toString(); 39 | }; 40 | 41 | Privkey.prototype.fromRandom = function() { 42 | do { 43 | var privbuf = Random.getRandomBuffer(32); 44 | var bn = BN().fromBuffer(privbuf); 45 | var condition = bn.lt(Point.getN()); 46 | } while (!condition); 47 | this.set({ 48 | bn: bn, 49 | networkstr: 'mainnet', 50 | compressed: true 51 | }); 52 | return this; 53 | }; 54 | 55 | Privkey.prototype.fromBN = function(bn) { 56 | this.bn = bn; 57 | return this; 58 | }; 59 | 60 | Privkey.prototype.validate = function() { 61 | if (!this.bn.lt(Point.getN())) 62 | throw new Error('Number must be less than N'); 63 | if (typeof constants[this.networkstr] === undefined) 64 | throw new Error('Must specify the networkstr ("mainnet" or "testnet")'); 65 | if (typeof this.compressed !== 'boolean') 66 | throw new Error('Must specify whether the corresponding public key is compressed or not (true or false)'); 67 | }; 68 | 69 | /** 70 | * Output the private key a Wallet Import Format (WIF) string. 71 | */ 72 | Privkey.prototype.toWIF = function() { 73 | var networkstr = this.networkstr; 74 | var compressed = this.compressed; 75 | 76 | if (typeof this.networkstr === 'undefined') 77 | networkstr = 'mainnet'; 78 | if (typeof this.compressed === 'undefined') 79 | compressed = true; 80 | 81 | var privbuf = this.bn.toBuffer({size: 32}); 82 | var buf; 83 | if (compressed) 84 | buf = Buffer.concat([new Buffer([constants[networkstr].privkey]), this.bn.toBuffer({size: 32}), new Buffer([0x01])]); 85 | else 86 | buf = Buffer.concat([new Buffer([constants[networkstr].privkey]), this.bn.toBuffer({size: 32})]); 87 | 88 | return base58check.encode(buf); 89 | }; 90 | 91 | /** 92 | * Input the private key from a Wallet Import Format (WIF) string. 93 | */ 94 | Privkey.prototype.fromWIF = function(str) { 95 | var buf = base58check.decode(str); 96 | 97 | if (buf.length === 1 + 32 + 1 && buf[1 + 32 + 1 - 1] == 1) 98 | this.compressed = true; 99 | else if (buf.length === 1 + 32) 100 | this.compressed = false; 101 | else 102 | throw new Error('Length of privkey buffer must be 33 (uncompressed pubkey) or 34 (compressed pubkey)'); 103 | 104 | if (buf[0] === constants.mainnet.privkey) 105 | this.networkstr = 'mainnet'; 106 | else if (buf[0] === constants.testnet.privkey) 107 | this.networkstr = 'testnet'; 108 | else 109 | throw new Error('Invalid networkstr'); 110 | 111 | this.bn = BN.fromBuffer(buf.slice(1, 32 + 1)); 112 | return this; 113 | }; 114 | 115 | Privkey.prototype.toString = function() { 116 | return this.toWIF(); 117 | }; 118 | 119 | Privkey.prototype.fromString = function(str) { 120 | return this.fromWIF(str); 121 | }; 122 | 123 | module.exports = Privkey; 124 | -------------------------------------------------------------------------------- /lib/pubkey.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Public Key 3 | * ========== 4 | * 5 | * A public key corresponds to a private key. If you have a private key, you 6 | * can find the corresponding public key with Pubkey().fromPrivkey(privkey). 7 | */ 8 | var Point = require('./point'); 9 | var bn = require('./bn'); 10 | var privkey = require('./privkey'); 11 | 12 | var Pubkey = function Pubkey(point) { 13 | if (!(this instanceof Pubkey)) 14 | return new Pubkey(point); 15 | if (point instanceof Point) 16 | this.point = point; 17 | else if (point) { 18 | var obj = point; 19 | this.set(obj); 20 | } 21 | }; 22 | 23 | Pubkey.prototype.set = function(obj) { 24 | if (obj.point && !obj.point.getX() && !obj.point.getY()) 25 | throw new Error('Invalid point'); 26 | this.point = obj.point || this.point; 27 | this.compressed = typeof obj.compressed !== 'undefined' ? obj.compressed : this.compressed; 28 | return this; 29 | }; 30 | 31 | Pubkey.prototype.fromJSON = function(json) { 32 | this.fromBuffer(new Buffer(json, 'hex')); 33 | return this; 34 | }; 35 | 36 | Pubkey.prototype.toJSON = function() { 37 | return this.toBuffer().toString('hex'); 38 | }; 39 | 40 | Pubkey.prototype.fromPrivkey = function(privkey) { 41 | this.set({ 42 | point: Point.getG().mul(privkey.bn), 43 | compressed: privkey.compressed} 44 | ); 45 | return this; 46 | }; 47 | 48 | Pubkey.prototype.fromBuffer = function(buf, strict) { 49 | return this.fromDER(buf, strict); 50 | }; 51 | 52 | /** 53 | * In order to mimic the non-strict style of OpenSSL, set strict = false. For 54 | * information and what prefixes 0x06 and 0x07 mean, in addition to the normal 55 | * compressed and uncompressed public keys, see the message by Peter Wuille 56 | * where he discovered these "hybrid pubkeys" on the mailing list: 57 | * http://sourceforge.net/p/bitcoin/mailman/message/29416133/ 58 | */ 59 | Pubkey.prototype.fromDER = function(buf, strict) { 60 | if (typeof strict === 'undefined') 61 | strict = true; 62 | else 63 | strict = false; 64 | if (buf[0] === 0x04 || (!strict && (buf[0] === 0x06 || buf[0] === 0x07))) { 65 | var xbuf = buf.slice(1, 33); 66 | var ybuf = buf.slice(33, 65); 67 | if (xbuf.length !== 32 || ybuf.length !== 32 || buf.length !== 65) 68 | throw new Error('Length of x and y must be 32 bytes'); 69 | var x = bn(xbuf); 70 | var y = bn(ybuf); 71 | this.point = Point(x, y); 72 | this.compressed = false; 73 | } else if (buf[0] === 0x03) { 74 | var xbuf = buf.slice(1); 75 | var x = bn(xbuf); 76 | this.fromX(true, x); 77 | this.compressed = true; 78 | } else if (buf[0] === 0x02) { 79 | var xbuf = buf.slice(1); 80 | var x = bn(xbuf); 81 | this.fromX(false, x); 82 | this.compressed = true; 83 | } else { 84 | throw new Error('Invalid DER format pubkey'); 85 | } 86 | return this; 87 | }; 88 | 89 | Pubkey.prototype.fromString = function(str) { 90 | this.fromDER(new Buffer(str, 'hex')); 91 | }; 92 | 93 | Pubkey.prototype.fromX = function(odd, x) { 94 | if (typeof odd !== 'boolean') 95 | throw new Error('Must specify whether y is odd or not (true or false)'); 96 | this.point = Point.fromX(odd, x); 97 | }; 98 | 99 | Pubkey.prototype.toBuffer = function() { 100 | var compressed = typeof this.compressed === 'undefined' ? true : this.compressed; 101 | return this.toDER(compressed); 102 | }; 103 | 104 | Pubkey.prototype.toDER = function(compressed) { 105 | compressed = typeof this.compressed === 'undefined' ? compressed : this.compressed; 106 | if (typeof compressed !== 'boolean') 107 | throw new Error('Must specify whether the public key is compressed or not (true or false)'); 108 | 109 | var x = this.point.getX(); 110 | var y = this.point.getY(); 111 | 112 | var xbuf = x.toBuffer({size: 32}); 113 | var ybuf = y.toBuffer({size: 32}); 114 | 115 | if (!compressed) { 116 | var prefix = new Buffer([0x04]); 117 | return Buffer.concat([prefix, xbuf, ybuf]); 118 | } else { 119 | var odd = ybuf[ybuf.length - 1] % 2; 120 | if (odd) 121 | var prefix = new Buffer([0x03]); 122 | else 123 | var prefix = new Buffer([0x02]); 124 | return Buffer.concat([prefix, xbuf]); 125 | } 126 | }; 127 | 128 | Pubkey.prototype.toString = function() { 129 | var compressed = typeof this.compressed === 'undefined' ? true : this.compressed; 130 | return this.toDER(compressed).toString('hex'); 131 | }; 132 | 133 | /** 134 | * Translated from bitcoind's IsCompressedOrUncompressedPubKey 135 | */ 136 | Pubkey.isCompressedOrUncompressed = function(buf) { 137 | if (buf.length < 33) { 138 | // Non-canonical public key: too short 139 | return false; 140 | } 141 | if (buf[0] === 0x04) { 142 | if (buf.length !== 65) 143 | // Non-canonical public key: invalid length for uncompressed key 144 | return false; 145 | } else if (buf[0] === 0x02 || buf[0] === 0x03) { 146 | if (buf.length !== 33) 147 | // Non-canonical public key: invalid length for compressed key 148 | return false; 149 | } else { 150 | // Non-canonical public key: neither compressed nor uncompressed 151 | return false; 152 | } 153 | return true; 154 | } 155 | 156 | //https://www.iacr.org/archive/pkc2003/25670211/25670211.pdf 157 | Pubkey.prototype.validate = function() { 158 | if (this.point.isInfinity()) 159 | throw new Error('point: Point cannot be equal to Infinity'); 160 | if (this.point.eq(Point(bn(0), bn(0)))) 161 | throw new Error('point: Point cannot be equal to 0, 0'); 162 | this.point.validate(); 163 | return this; 164 | }; 165 | 166 | module.exports = Pubkey; 167 | -------------------------------------------------------------------------------- /lib/random.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Random Number Generator 3 | * ======================= 4 | * 5 | * Random numbers are important in bitcoin primarily for generating private 6 | * keys. It is also important for creating signatures if you are using a random 7 | * value of k, but fullnode defaults to using deterministic k. That means 8 | * computing a random private key, or a random seed for use in BIP39 or BIP32, 9 | * is the primary use of the random number generator. Note that the simplicity 10 | * of this class is extremely carefully considered. It is easy to audit that 11 | * this code runs node's randomBytes function. It is also easy to audit that 12 | * the randomBytes method is correctly interpreted as 13 | * window.crypto.getRandomValues when this code is browserified by browserify, 14 | * and thus also works correctly in the browser. We deliberately do not do 15 | * anything else to this random number in order to minimize possible errors in 16 | * this absolutely critical code. 17 | */ 18 | var crypto = require('crypto'); 19 | 20 | var Random = {}; 21 | 22 | Random.getRandomBuffer = function(size) { 23 | return crypto.randomBytes(size); 24 | } 25 | 26 | module.exports = Random; 27 | -------------------------------------------------------------------------------- /lib/tx.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Transaction 3 | * =========== 4 | * 5 | * A bitcoin transaction. 6 | */ 7 | var Txin = require('./txin'); 8 | var Txout = require('./txout'); 9 | var BufW = require('./bufw'); 10 | var BufR = require('./bufr'); 11 | var Varint = require('./varint'); 12 | var Hash = require('./hash'); 13 | var Script = require('./script'); 14 | var Sig = require('./sig'); 15 | var BN = require('./bn'); 16 | var ECDSA = require('./ecdsa'); 17 | 18 | var Tx = function Tx(version, txinsvi, txins, txoutsvi, txouts, nlocktime) { 19 | if (!(this instanceof Tx)) 20 | return new Tx(version, txinsvi, txins, txoutsvi, txouts, nlocktime); 21 | if (typeof version === 'number') { 22 | this.initialize(); 23 | this.set({ 24 | version: version, 25 | txinsvi: txinsvi, 26 | txins: txins, 27 | txoutsvi: txoutsvi, 28 | txouts: txouts, 29 | nlocktime: nlocktime 30 | }); 31 | } else if (Buffer.isBuffer(version)) { 32 | //not necessary to initialize, since everything should be overwritten 33 | var txbuf = version; 34 | this.fromBuffer(txbuf); 35 | } else if (version) { 36 | this.initialize(); 37 | var obj = version; 38 | this.set(obj); 39 | } else { 40 | this.initialize(); 41 | } 42 | }; 43 | 44 | Tx.MAX_MONEY = 21000000 * 1e8; 45 | 46 | Tx.prototype.initialize = function() { 47 | this.version = 1; 48 | this.txinsvi = Varint(0); 49 | this.txins = []; 50 | this.txoutsvi = Varint(0); 51 | this.txouts = []; 52 | this.nlocktime = 0; 53 | return this; 54 | }; 55 | 56 | Tx.prototype.set = function(obj) { 57 | this.version = typeof obj.version !== 'undefined' ? obj.version : this.version; 58 | this.txinsvi = obj.txinsvi || this.txinsvi; 59 | this.txins = obj.txins || this.txins; 60 | this.txoutsvi = obj.txoutsvi || this.txoutsvi; 61 | this.txouts = obj.txouts || this.txouts; 62 | this.nlocktime = typeof obj.nlocktime !== 'undefined' ? obj.nlocktime : this.nlocktime; 63 | return this; 64 | }; 65 | 66 | Tx.prototype.fromJSON = function(json) { 67 | var txins = []; 68 | json.txins.forEach(function(txin) { 69 | txins.push(Txin().fromJSON(txin)); 70 | }); 71 | var txouts = []; 72 | json.txouts.forEach(function(txout) { 73 | txouts.push(Txout().fromJSON(txout)); 74 | }); 75 | this.set({ 76 | version: json.version, 77 | txinsvi: Varint().fromJSON(json.txinsvi), 78 | txins: txins, 79 | txoutsvi: Varint().fromJSON(json.txoutsvi), 80 | txouts: txouts, 81 | nlocktime: json.nlocktime 82 | }); 83 | return this; 84 | }; 85 | 86 | Tx.prototype.toJSON = function() { 87 | var txins = []; 88 | this.txins.forEach(function(txin) { 89 | txins.push(txin.toJSON()); 90 | }); 91 | var txouts = []; 92 | this.txouts.forEach(function(txout) { 93 | txouts.push(txout.toJSON()); 94 | }); 95 | return { 96 | version: this.version, 97 | txinsvi: this.txinsvi.toJSON(), 98 | txins: txins, 99 | txoutsvi: this.txoutsvi.toJSON(), 100 | txouts: txouts, 101 | nlocktime: this.nlocktime 102 | }; 103 | }; 104 | 105 | Tx.prototype.fromBuffer = function(buf) { 106 | return this.fromBufR(BufR(buf)); 107 | }; 108 | 109 | Tx.prototype.fromBufR = function(br) { 110 | this.version = br.readUInt32LE(); 111 | this.txinsvi = Varint(br.readVarintBuf()); 112 | var txinsnum = this.txinsvi.toNumber(); 113 | this.txins = []; 114 | for (var i = 0; i < txinsnum; i++) { 115 | this.txins.push(Txin().fromBufR(br)); 116 | } 117 | this.txoutsvi = Varint(br.readVarintBuf()); 118 | var txoutsnum = this.txoutsvi.toNumber(); 119 | this.txouts = []; 120 | for (var i = 0; i < txoutsnum; i++) { 121 | this.txouts.push(Txout().fromBufR(br)); 122 | } 123 | this.nlocktime = br.readUInt32LE(); 124 | return this; 125 | }; 126 | 127 | Tx.prototype.toBuffer = function() { 128 | return this.toBufW().concat(); 129 | }; 130 | 131 | Tx.prototype.toBufW = function(bw) { 132 | if (!bw) 133 | bw = new BufW(); 134 | bw.writeUInt32LE(this.version); 135 | bw.write(this.txinsvi.buf); 136 | for (var i = 0; i < this.txins.length; i++) { 137 | this.txins[i].toBufW(bw); 138 | } 139 | bw.write(this.txoutsvi.buf) 140 | for (var i = 0; i < this.txouts.length; i++) { 141 | this.txouts[i].toBufW(bw); 142 | } 143 | bw.writeUInt32LE(this.nlocktime); 144 | return bw; 145 | }; 146 | 147 | Tx.prototype.sighash = function(nhashtype, nin, subscript) { 148 | var txcopy = Tx(this.toBuffer()); 149 | 150 | subscript = Script(subscript.toBuffer()); 151 | subscript.removeCodeseparators(); 152 | 153 | for (var i = 0; i < txcopy.txins.length; i++) { 154 | txcopy.txins[i] = Txin(txcopy.txins[i].toBuffer()).setScript(Script('')); 155 | } 156 | 157 | txcopy.txins[nin] = Txin(txcopy.txins[nin].toBuffer()).setScript(subscript); 158 | 159 | if ((nhashtype & 31) === Sig.SIGHASH_NONE) { 160 | txcopy.txouts.length = 0; 161 | txcopy.txoutsvi = Varint(0); 162 | 163 | for (var i = 0; i < txcopy.txins.length; i++) { 164 | if (i !== nin) 165 | txcopy.txins[i].seqnum = 0; 166 | } 167 | } else if ((nhashtype & 31) === Sig.SIGHASH_SINGLE) { 168 | // The SIGHASH_SINGLE bug. 169 | // https://bitcointalk.org/index.php?topic=260595.0 170 | if (nin > txcopy.txouts.length - 1) 171 | return new Buffer('0000000000000000000000000000000000000000000000000000000000000001', 'hex'); 172 | 173 | txcopy.txouts.length = nin + 1; 174 | txcopy.txoutsvi = Varint(nin + 1); 175 | 176 | for (var i = 0; i < txcopy.txouts.length; i++) { 177 | if (i < nin) 178 | txcopy.txouts[i] = Txout(BN().fromBuffer(new Buffer('ffffffffffffffff', 'hex')), Script('')); 179 | } 180 | 181 | for (var i = 0; i < txcopy.txins.length; i++) { 182 | if (i !== nin) 183 | txcopy.txins[i].seqnum = 0; 184 | } 185 | } 186 | //else, SIGHASH_ALL 187 | 188 | if (nhashtype & Sig.SIGHASH_ANYONECANPAY) { 189 | txcopy.txins[0] = txcopy.txins[nin]; 190 | txcopy.txins.length = 1; 191 | txcopy.txinsvi = Varint(1); 192 | } 193 | 194 | var buf = BufW().write(txcopy.toBuffer()).writeInt32LE(nhashtype).concat(); 195 | return BufR(Hash.sha256sha256(buf)).readReverse(); 196 | }; 197 | 198 | // This function returns a signature but does not update any inputs 199 | Tx.prototype.sign = function(keypair, nhashtype, nin, subscript) { 200 | var hashbuf = this.sighash(nhashtype, nin, subscript); 201 | var sig = ECDSA.sign(hashbuf, keypair, 'little').set({nhashtype: nhashtype}); 202 | return sig; 203 | }; 204 | 205 | // This function takes a signature as input and does not parse any inputs 206 | Tx.prototype.verify = function(sig, pubkey, nin, subscript) { 207 | var hashbuf = this.sighash(sig.nhashtype, nin, subscript); 208 | return ECDSA.verify(hashbuf, sig, pubkey, 'little'); 209 | }; 210 | 211 | Tx.prototype.hash = function() { 212 | return Hash.sha256sha256(this.toBuffer()); 213 | }; 214 | 215 | Tx.prototype.id = function() { 216 | return BufR(this.hash()).readReverse(); 217 | }; 218 | 219 | Tx.prototype.addTxin = function(txhashbuf, txoutnum, script, seqnum) { 220 | var txin; 221 | if (txhashbuf instanceof Txin) 222 | txin = txhashbuf; 223 | else 224 | txin = Txin(txhashbuf, txoutnum, script, seqnum); 225 | this.txins.push(txin); 226 | this.txinsvi = Varint(this.txinsvi.toNumber() + 1); 227 | return this; 228 | }; 229 | 230 | Tx.prototype.addTxout = function(valuebn, script) { 231 | var txout; 232 | if (valuebn instanceof Txout) 233 | txout = valuebn; 234 | else 235 | txout = Txout(valuebn, script); 236 | this.txouts.push(txout); 237 | this.txoutsvi = Varint(this.txoutsvi.toNumber() + 1); 238 | return this; 239 | }; 240 | 241 | /** 242 | * Analagous to bitcoind's IsCoinBase function in transaction.h 243 | */ 244 | Tx.prototype.isCoinbase = function() { 245 | return (this.txins.length === 1 && this.txins[0].hasNullInput()); 246 | } 247 | 248 | module.exports = Tx; 249 | -------------------------------------------------------------------------------- /lib/txbuilder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Transaction Builder 3 | * =================== 4 | * 5 | * Transaction Builder. This is a, yet unfinished, convenience class for 6 | * building pubkeyhash and p2sh transactions, and also for verifying arbitrary 7 | * transactions (and their inputs). You can (or will be able to) pay to 8 | * pubkeyhash to p2sh and can spend pubkeyhash or p2sh-pubkeyhash or 9 | * p2sh-multisig. 10 | */ 11 | var Tx = require('./tx'); 12 | var Txout = require('./txout'); 13 | var Address = require('./address'); 14 | var Script = require('./script'); 15 | var BN = require('./bn'); 16 | var Block = require('./block'); 17 | var BufR = require('./bufr'); 18 | var Interp = require('./interp'); 19 | 20 | var Txbuilder = function Txbuilder(obj) { 21 | if (!(this instanceof Txbuilder)) 22 | return new Txbuilder(obj); 23 | if (obj) { 24 | this.initialize(); 25 | this.set(obj); 26 | } else { 27 | this.initialize(); 28 | } 29 | }; 30 | 31 | module.exports = Txbuilder; 32 | 33 | Txbuilder.prototype.initialize = function() { 34 | this.tx = Tx(); 35 | this.prevtxoutsmap = {}; 36 | return this; 37 | }; 38 | 39 | Txbuilder.prototype.set = function(obj) { 40 | this.tx = obj.tx || this.tx; 41 | this.prevtxoutsmap = obj.prevtxoutsmap || this.prevtxoutsmap; 42 | return this; 43 | }; 44 | 45 | Txbuilder.prototype.addToAddress = function(valuebn, addr) { 46 | if (!(addr instanceof Address) || !(valuebn instanceof BN)) 47 | throw new Error('addr must be an Address, and valuebn must be a BN'); 48 | if (addr.type() === 'scripthash') { 49 | var script = Script().fromScripthash(addr.hashbuf); 50 | } else if (addr.type() === 'pubkeyhash') { 51 | var script = Script().fromPubkeyhash(addr.hashbuf); 52 | } else { 53 | throw new Error('invalid address type'); 54 | } 55 | var txout = Txout(valuebn, script); 56 | this.tx.addTxout(txout); 57 | return this; 58 | }; 59 | 60 | /** 61 | * Verifies that the transaction is valid both by performing basic checks, such 62 | * as ensuring that no two inputs are the same, as well as by verifying every 63 | * script. The two checks are checktxstr, which is analagous to bitcoind's 64 | * CheckTransaction, and verifytxstr, which runs the script interpreter. 65 | * 66 | * This does NOT check that any possible claimed fees are accurate; checking 67 | * that the fees are accurate requires checking that the input transactions are 68 | * valid, which is not performed by this test. That check is done with the 69 | * normal verify function. 70 | */ 71 | Txbuilder.prototype.verifytx = function(flags) { 72 | return !this.checktxstr() && !this.verifytxstr(flags); 73 | }; 74 | 75 | /** 76 | * Check that a transaction passes basic sanity tests. If not, return a string 77 | * describing the error. This function contains the same logic as 78 | * CheckTransaction in bitcoin core. 79 | */ 80 | Txbuilder.prototype.checktxstr = function() { 81 | // Basic checks that don't depend on any context 82 | if (this.tx.txins.length === 0 || this.tx.txinsvi.toNumber() === 0) 83 | return "transaction txins empty"; 84 | if (this.tx.txouts.length === 0 || this.tx.txoutsvi.toNumber() === 0) 85 | return "transaction txouts empty"; 86 | 87 | // Size limits 88 | if (this.tx.toBuffer().length > Block.MAX_BLOCK_SIZE) 89 | return "transaction over the maximum block size"; 90 | 91 | // Check for negative or overflow output values 92 | var valueoutbn = BN(0); 93 | for (var i = 0; i < this.tx.txouts.length; i++) { 94 | var txout = this.tx.txouts[i]; 95 | if (txout.valuebn.lt(0)) 96 | return "transaction txout " + i + " negative"; 97 | if (txout.valuebn.gt(Tx.MAX_MONEY)) 98 | return "transaction txout " + i + " greater than MAX_MONEY"; 99 | valueoutbn = valueoutbn.add(txout.valuebn); 100 | if (valueoutbn.gt(Tx.MAX_MONEY)) 101 | return "transaction txout " + i + " total output greater than MAX_MONEY"; 102 | } 103 | 104 | // Check for duplicate inputs 105 | var txinmap = {}; 106 | for (var i = 0; i < this.tx.txins.length; i++) { 107 | var txin = this.tx.txins[i]; 108 | var inputid = txin.txhashbuf.toString('hex') + ':' + txin.txoutnum; 109 | if (typeof txinmap[inputid] !== 'undefined') 110 | return "transaction input " + i + " duplicate input"; 111 | txinmap[inputid] = true; 112 | } 113 | 114 | if (this.tx.isCoinbase()) { 115 | var buf = this.tx.txins[0].script.toBuffer(); 116 | if (buf.length < 2 || buf.length > 100) 117 | return "coinbase trasaction script size invalid"; 118 | } else { 119 | for (var i = 0; i < this.tx.txins.length; i++) { 120 | if (this.tx.txins[i].hasNullInput()) 121 | return "tranasction input " + i + " has null input"; 122 | } 123 | } 124 | return false; 125 | }; 126 | 127 | /** 128 | * verify the transaction inputs by running the script interpreter. Returns a 129 | * string of the script interpreter is invalid, otherwise returns false. 130 | */ 131 | Txbuilder.prototype.verifytxstr = function(flags) { 132 | for (var i = 0; i < this.tx.txins.length; i++) { 133 | if (!this.verifytxnin(i, flags)) 134 | return "input " + i + " failed script verify"; 135 | } 136 | return false; 137 | }; 138 | 139 | /** 140 | * Verify a particular input by running the script interpreter. Returns true if 141 | * the input is valid, false otherwise. 142 | */ 143 | Txbuilder.prototype.verifytxnin = function(nin, flags) { 144 | var txin = this.tx.txins[nin]; 145 | var scriptSig = txin.script; 146 | var txoutnum = txin.txoutnum; 147 | var txidbuf = BufR(txin.txhashbuf).readReverse(); 148 | var txidhex = txidbuf.toString('hex'); 149 | var mapid = txidhex + ":" + txoutnum; 150 | var scriptPubkey = this.prevtxoutsmap[mapid].script; 151 | var interp = Interp() 152 | var verified = interp.verify(scriptSig, scriptPubkey, this.tx, nin, flags); 153 | return verified; 154 | }; 155 | -------------------------------------------------------------------------------- /lib/txin.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Transaction Input 3 | * ================= 4 | * 5 | * An input to a transaction. The way you probably want to use this is through 6 | * the convenient method of Txin(txhashbuf, txoutnum, script, seqnum) (i.e., you 7 | * can leave out the scriptvi, which is computed automatically if you leave it 8 | * out.) 9 | */ 10 | var BufR = require('./bufr'); 11 | var BufW = require('./bufw'); 12 | var Varint = require('./varint'); 13 | var Script = require('./script'); 14 | 15 | var Txin = function Txin(txhashbuf, txoutnum, scriptvi, script, seqnum) { 16 | if (!(this instanceof Txin)) 17 | return new Txin(txhashbuf, txoutnum, scriptvi, script, seqnum); 18 | if (Buffer.isBuffer(txhashbuf) && typeof txoutnum !== 'undefined') { 19 | if (txhashbuf.length !== 32) 20 | throw new Error('txhashbuf must be 32 bytes'); 21 | if (scriptvi instanceof Script) { 22 | seqnum = script; 23 | script = scriptvi; 24 | this.set({ 25 | txhashbuf: txhashbuf, 26 | txoutnum: txoutnum, 27 | seqnum: seqnum 28 | }); 29 | this.setScript(script); 30 | } else { 31 | this.set({ 32 | txhashbuf: txhashbuf, 33 | txoutnum: txoutnum, 34 | scriptvi: scriptvi, 35 | script: script, 36 | seqnum: seqnum 37 | }); 38 | } 39 | } else if (Buffer.isBuffer(txhashbuf)) { 40 | var txinbuf = txhashbuf; 41 | this.fromBuffer(txinbuf); 42 | } else if (txhashbuf) { 43 | var obj = txhashbuf; 44 | this.set(obj); 45 | } 46 | }; 47 | 48 | Txin.prototype.set = function(obj) { 49 | this.txhashbuf = obj.txhashbuf || this.txhashbuf; 50 | this.txoutnum = typeof obj.txoutnum !== 'undefined' ? obj.txoutnum : this.txoutnum; 51 | this.scriptvi = typeof obj.scriptvi !== 'undefined' ? obj.scriptvi : this.scriptvi; 52 | this.script = obj.script || this.script; 53 | this.seqnum = typeof obj.seqnum !== 'undefined' ? obj.seqnum : this.seqnum; 54 | return this; 55 | }; 56 | 57 | Txin.prototype.setScript = function(script) { 58 | this.scriptvi = Varint(script.toBuffer().length); 59 | this.script = script; 60 | return this; 61 | }; 62 | 63 | Txin.prototype.fromJSON = function(json) { 64 | this.set({ 65 | txhashbuf: new Buffer(json.txhashbuf, 'hex'), 66 | txoutnum: json.txoutnum, 67 | scriptvi: Varint().fromJSON(json.scriptvi), 68 | script: Script().fromJSON(json.script), 69 | seqnum: json.seqnum 70 | }); 71 | return this; 72 | }; 73 | 74 | Txin.prototype.toJSON = function() { 75 | return { 76 | txhashbuf: this.txhashbuf.toString('hex'), 77 | txoutnum: this.txoutnum, 78 | scriptvi: this.scriptvi.toJSON(), 79 | script: this.script.toJSON(), 80 | seqnum: this.seqnum 81 | }; 82 | }; 83 | 84 | Txin.prototype.fromBuffer = function(buf) { 85 | return this.fromBufR(BufR(buf)); 86 | }; 87 | 88 | Txin.prototype.fromBufR = function(br) { 89 | this.txhashbuf = br.read(32); 90 | this.txoutnum = br.readUInt32LE(); 91 | this.scriptvi = Varint(br.readVarintBuf()); 92 | this.script = Script().fromBuffer(br.read(this.scriptvi.toNumber())); 93 | this.seqnum = br.readUInt32LE(); 94 | return this; 95 | }; 96 | 97 | Txin.prototype.toBuffer = function() { 98 | return this.toBufW().concat(); 99 | }; 100 | 101 | Txin.prototype.toBufW = function(bw) { 102 | if (!bw) 103 | bw = new BufW(); 104 | bw.write(this.txhashbuf); 105 | bw.writeUInt32LE(this.txoutnum); 106 | bw.write(this.scriptvi.buf); 107 | bw.write(this.script.toBuffer()); 108 | bw.writeUInt32LE(this.seqnum); 109 | return bw; 110 | }; 111 | 112 | Txin.prototype.hasNullInput = function() { 113 | var hex = this.txhashbuf.toString('hex'); 114 | if (hex === '0000000000000000000000000000000000000000000000000000000000000000' && this.txoutnum === 0xffffffff) 115 | return true; 116 | return false; 117 | }; 118 | 119 | /** 120 | * Analagous to bitcoind's SetNull in COutPoint 121 | */ 122 | Txin.prototype.setNullInput = function() { 123 | this.txhashbuf = new Buffer(32); 124 | this.txhashbuf.fill(0); 125 | this.txoutnum = 0xffffffff; // -1 cast to unsigned int 126 | }; 127 | 128 | module.exports = Txin; 129 | -------------------------------------------------------------------------------- /lib/txout.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Transaction Output 3 | * ================== 4 | * 5 | * An output to a transaction. The way you normally want to make one is with 6 | * Txout(valuebn, script) (i.e., just as with Txin, you can leave out the 7 | * scriptvi, since it can be computed automatically. 8 | */ 9 | var BN = require('./bn'); 10 | var BufR = require('./bufr'); 11 | var BufW = require('./bufw'); 12 | var Varint = require('./varint'); 13 | var Script = require('./script'); 14 | 15 | var Txout = function Txout(valuebn, scriptvi, script) { 16 | if (!(this instanceof Txout)) 17 | return new Txout(valuebn, scriptvi, script); 18 | if (valuebn instanceof BN) { 19 | if (scriptvi instanceof Script) { 20 | script = scriptvi; 21 | this.set({ 22 | valuebn: valuebn 23 | }); 24 | this.setScript(script); 25 | } else { 26 | this.set({ 27 | valuebn: valuebn, 28 | scriptvi: scriptvi, 29 | script: script 30 | }); 31 | } 32 | } else if (valuebn) { 33 | var obj = valuebn; 34 | this.set(obj); 35 | } 36 | }; 37 | 38 | Txout.prototype.set = function(obj) { 39 | this.valuebn = obj.valuebn || this.valuebn; 40 | this.scriptvi = obj.scriptvi || this.scriptvi; 41 | this.script = obj.script || this.script; 42 | return this; 43 | }; 44 | 45 | Txout.prototype.setScript = function(script) { 46 | this.scriptvi = Varint(script.toBuffer().length); 47 | this.script = script; 48 | return this; 49 | }; 50 | 51 | Txout.prototype.fromJSON = function(json) { 52 | this.set({ 53 | valuebn: BN().fromJSON(json.valuebn), 54 | scriptvi: Varint().fromJSON(json.scriptvi), 55 | script: Script().fromJSON(json.script) 56 | }); 57 | return this; 58 | }; 59 | 60 | Txout.prototype.toJSON = function() { 61 | return { 62 | valuebn: this.valuebn.toJSON(), 63 | scriptvi: this.scriptvi.toJSON(), 64 | script: this.script.toJSON() 65 | }; 66 | }; 67 | 68 | Txout.prototype.fromBuffer = function(buf) { 69 | return this.fromBufR(BufR(buf)); 70 | }; 71 | 72 | Txout.prototype.fromBufR = function(br) { 73 | this.valuebn = br.readUInt64LEBN(); 74 | this.scriptvi = Varint(br.readVarintNum()); 75 | this.script = Script().fromBuffer(br.read(this.scriptvi.toNumber())); 76 | return this; 77 | }; 78 | 79 | Txout.prototype.toBuffer = function() { 80 | var bw = new BufW(); 81 | return this.toBufW(bw).concat(); 82 | }; 83 | 84 | Txout.prototype.toBufW = function(bw) { 85 | if (!bw) 86 | bw = new BufW(); 87 | bw.writeUInt64LEBN(this.valuebn); 88 | bw.write(this.scriptvi.buf); 89 | bw.write(this.script.toBuffer()); 90 | return bw; 91 | }; 92 | 93 | module.exports = Txout; 94 | -------------------------------------------------------------------------------- /lib/varint.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Varint (a.k.a. Compact Size) 3 | * ============================ 4 | * 5 | * A varint is a varible sized integer, and it is a format that is unique to 6 | * bitcoin, and used throughout bitcoin to represent the length of binary data 7 | * in a compact format that can take up as little as 1 byte or as much as 9 8 | * bytes. 9 | */ 10 | var BufR = require('./bufr'); 11 | var BufW = require('./bufw'); 12 | var BN = require('./bn'); 13 | 14 | var Varint = function Varint(buf) { 15 | if (!(this instanceof Varint)) 16 | return new Varint(buf); 17 | if (Buffer.isBuffer(buf)) { 18 | this.buf = buf; 19 | } else if (typeof buf === 'number') { 20 | var num = buf; 21 | this.fromNumber(num); 22 | } else if (buf instanceof BN) { 23 | var bn = buf; 24 | this.fromBN(bn); 25 | } else if (buf) { 26 | var obj = buf; 27 | this.set(obj); 28 | } 29 | }; 30 | 31 | Varint.prototype.set = function(obj) { 32 | this.buf = obj.buf || this.buf; 33 | return this; 34 | }; 35 | 36 | Varint.prototype.fromJSON = function(json) { 37 | this.set({ 38 | buf: new Buffer(json, 'hex') 39 | }); 40 | return this; 41 | }; 42 | 43 | Varint.prototype.toJSON = function() { 44 | return this.buf.toString('hex'); 45 | }; 46 | 47 | Varint.prototype.fromBuffer = function(buf) { 48 | this.buf = buf; 49 | return this; 50 | }; 51 | 52 | Varint.prototype.fromBufR = function(br) { 53 | this.buf = br.readVarintBuf(); 54 | return this; 55 | }; 56 | 57 | Varint.prototype.fromBN = function(bn) { 58 | this.buf = BufW().writeVarintBN(bn).concat(); 59 | return this; 60 | }; 61 | 62 | Varint.prototype.fromNumber = function(num) { 63 | this.buf = BufW().writeVarintNum(num).concat(); 64 | return this; 65 | }; 66 | 67 | Varint.prototype.toBuffer = function() { 68 | return this.buf; 69 | }; 70 | 71 | Varint.prototype.toBN = function() { 72 | return BufR(this.buf).readVarintBN(); 73 | }; 74 | 75 | Varint.prototype.toNumber = function() { 76 | return BufR(this.buf).readVarintNum(); 77 | }; 78 | 79 | module.exports = Varint; 80 | -------------------------------------------------------------------------------- /npm-shrinkwrap.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fullnode", 3 | "version": "0.6.0", 4 | "dependencies": { 5 | "aes": { 6 | "version": "0.1.0", 7 | "from": "aes@=0.1.0", 8 | "resolved": "https://registry.npmjs.org/aes/-/aes-0.1.0.tgz" 9 | }, 10 | "bn.js": { 11 | "version": "0.16.1", 12 | "from": "bn.js@=0.16.1", 13 | "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-0.16.1.tgz" 14 | }, 15 | "bs58": { 16 | "version": "1.2.1", 17 | "from": "bs58@=1.2.1", 18 | "resolved": "https://registry.npmjs.org/bs58/-/bs58-1.2.1.tgz" 19 | }, 20 | "elliptic": { 21 | "version": "0.16.0", 22 | "from": "https://registry.npmjs.org/elliptic/-/elliptic-0.16.0.tgz", 23 | "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-0.16.0.tgz", 24 | "dependencies": { 25 | "brorand": { 26 | "version": "1.0.5", 27 | "from": "https://registry.npmjs.org/brorand/-/brorand-1.0.5.tgz", 28 | "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.0.5.tgz" 29 | }, 30 | "inherits": { 31 | "version": "2.0.1", 32 | "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", 33 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" 34 | } 35 | } 36 | }, 37 | "hash.js": { 38 | "version": "0.3.2", 39 | "from": "hash.js@=0.3.2", 40 | "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-0.3.2.tgz", 41 | "dependencies": { 42 | "inherits": { 43 | "version": "2.0.1", 44 | "from": "inherits@^2.0.1", 45 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" 46 | } 47 | } 48 | }, 49 | "pbkdf2-compat": { 50 | "version": "2.0.1", 51 | "from": "pbkdf2-compat@=2.0.1", 52 | "resolved": "https://registry.npmjs.org/pbkdf2-compat/-/pbkdf2-compat-2.0.1.tgz" 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fullnode", 3 | "version": "0.6.0", 4 | "description": "Javascript bitcoin library.", 5 | "author": "Ryan X. Charles ", 6 | "main": "index.js", 7 | "scripts": { 8 | "test": "istanbul cover _mocha -- --recursive", 9 | "build": "mkdir -p browser && browserify index.js -o browser/fullnode.js && browserify -g uglifyify index.js -o browser/fullnode-min.js && find test/ -type f -name \"*.js\" | xargs browserify -o browser/tests.js" 10 | }, 11 | "contributors": [ 12 | { 13 | "name": "Daniel Cousens", 14 | "email": "bitcoin@dcousens.com" 15 | }, 16 | { 17 | "name": "Gordon Hall", 18 | "email": "gordon@bitpay.com" 19 | }, 20 | { 21 | "name": "Jeff Garzik", 22 | "email": "jgarzik@bitpay.com" 23 | }, 24 | { 25 | "name": "Kyle Drake", 26 | "email": "kyle@kyledrake.net" 27 | }, 28 | { 29 | "name": "Manuel Araoz", 30 | "email": "manuelaraoz@gmail.com" 31 | }, 32 | { 33 | "name": "Matias Alejo Garcia", 34 | "email": "ematiu@gmail.com" 35 | }, 36 | { 37 | "name": "Ryan X. Charles", 38 | "email": "ryanxcharles@gmail.com" 39 | }, 40 | { 41 | "name": "Stefan Thomas", 42 | "email": "moon@justmoon.net" 43 | }, 44 | { 45 | "name": "Stephen Pair", 46 | "email": "stephen@bitpay.com" 47 | }, 48 | { 49 | "name": "Wei Lu", 50 | "email": "luwei.here@gmail.com" 51 | } 52 | ], 53 | "keywords": [ 54 | "bitcoin", 55 | "bip32", 56 | "bip39", 57 | "bip70", 58 | "stealth", 59 | "merge", 60 | "multisig" 61 | ], 62 | "repository": { 63 | "type": "git", 64 | "url": "https://github.com/ryanxcharles/fullnode.git" 65 | }, 66 | "dependencies": { 67 | "aes": "=0.1.0", 68 | "bn.js": "=0.16.1", 69 | "bs58": "=1.2.1", 70 | "elliptic": "=0.16.0", 71 | "hash.js": "=0.3.2", 72 | "pbkdf2-compat": "=2.0.1" 73 | }, 74 | "devDependencies": { 75 | "chai": "~1.9.0", 76 | "mocha": "~2.1.0", 77 | "browserify": "~8.0.3", 78 | "uglifyify": "~2.6.0", 79 | "groc": "~0.8.0" 80 | }, 81 | "license": "MIT" 82 | } 83 | -------------------------------------------------------------------------------- /test/address.js: -------------------------------------------------------------------------------- 1 | var should = require('chai').should(); 2 | var constants = require('../lib/constants'); 3 | var Pubkey = require('../lib/pubkey'); 4 | var Address = require('../lib/address'); 5 | var Script = require('../lib/script'); 6 | 7 | describe('Address', function() { 8 | var pubkeyhash = new Buffer('3c3fa3d4adcaf8f52d5b1843975e122548269937', 'hex'); 9 | var buf = Buffer.concat([new Buffer([0]), pubkeyhash]); 10 | var str = '16VZnHwRhwrExfeHFHGjwrgEMq8VcYPs9r'; 11 | 12 | it('should create a new address object', function() { 13 | var address = new Address(); 14 | should.exist(address); 15 | address = Address(buf); 16 | should.exist(address); 17 | address = Address(str); 18 | should.exist(address); 19 | }); 20 | 21 | describe('@isValid', function() { 22 | 23 | it('should validate this valid address string', function() { 24 | Address.isValid(str).should.equal(true); 25 | }); 26 | 27 | it('should invalidate this valid address string', function() { 28 | Address.isValid(str.substr(1)).should.equal(false); 29 | }); 30 | 31 | }); 32 | 33 | describe('#fromBuffer', function() { 34 | 35 | it('should make an address from a buffer', function() { 36 | Address().fromBuffer(buf).toBuffer().slice(1).toString('hex').should.equal(pubkeyhash.toString('hex')); 37 | Address().fromBuffer(buf).toString().should.equal(str); 38 | }); 39 | 40 | }); 41 | 42 | describe('#fromHashbuf', function() { 43 | 44 | it('should make an address from a hashbuf', function() { 45 | Address().fromHashbuf(pubkeyhash).toString().should.equal(str); 46 | var a = Address().fromHashbuf(pubkeyhash, 'testnet', 'scripthash'); 47 | a.network().should.equal('testnet'); 48 | a.type().should.equal('scripthash'); 49 | }); 50 | 51 | it('should throw an error for invalid length hashbuf', function() { 52 | (function() { 53 | Address().fromHashbuf(buf); 54 | }).should.throw('hashbuf must be exactly 20 bytes'); 55 | }); 56 | 57 | }); 58 | 59 | describe('#fromPubkey', function() { 60 | 61 | it('should make this address from a compressed pubkey', function() { 62 | var pubkey = new Pubkey(); 63 | pubkey.fromDER(new Buffer('0285e9737a74c30a873f74df05124f2aa6f53042c2fc0a130d6cbd7d16b944b004', 'hex')); 64 | var address = new Address(); 65 | address.fromPubkey(pubkey); 66 | address.toString().should.equal('19gH5uhqY6DKrtkU66PsZPUZdzTd11Y7ke'); 67 | }); 68 | 69 | it('should make this address from an uncompressed pubkey', function() { 70 | var pubkey = new Pubkey(); 71 | pubkey.fromDER(new Buffer('0285e9737a74c30a873f74df05124f2aa6f53042c2fc0a130d6cbd7d16b944b004', 'hex')); 72 | var address = new Address(); 73 | pubkey.compressed = false; 74 | address.fromPubkey(pubkey, 'mainnet'); 75 | address.toString().should.equal('16JXnhxjJUhxfyx4y6H4sFcxrgt8kQ8ewX'); 76 | }); 77 | 78 | }); 79 | 80 | describe('#fromScript', function() { 81 | 82 | it('should make this address from a script', function() { 83 | var script = Script().fromString("OP_CHECKMULTISIG"); 84 | var address = Address().fromScript(script); 85 | address.toString().should.equal('3BYmEwgV2vANrmfRymr1mFnHXgLjD6gAWm'); 86 | }); 87 | 88 | it('should make this address from other script', function() { 89 | var script = Script().fromString("OP_CHECKSIG OP_HASH160"); 90 | var address = Address().fromScript(script); 91 | address.toString().should.equal('347iRqVwks5r493N1rsLN4k9J7Ljg488W7'); 92 | }); 93 | 94 | }); 95 | 96 | describe('#fromString', function() { 97 | 98 | it('should derive from this known address string mainnet', function() { 99 | var address = new Address(); 100 | address.fromString(str); 101 | address.toBuffer().slice(1).toString('hex').should.equal(pubkeyhash.toString('hex')); 102 | }); 103 | 104 | it('should derive from this known address string testnet', function() { 105 | var address = new Address(); 106 | address.fromString(str); 107 | address.version = constants['testnet']['pubkeyhash']; 108 | address.fromString(address.toString()); 109 | address.toString().should.equal('mm1X5M2QWyHVjn7txrF7mmtZDpjCXzoa98'); 110 | }); 111 | 112 | it('should derive from this known address string mainnet scripthash', function() { 113 | var address = new Address(); 114 | address.fromString(str); 115 | address.version = constants['mainnet']['scripthash']; 116 | address.fromString(address.toString()); 117 | address.toString().should.equal('37BahqRsFrAd3qLiNNwLNV3AWMRD7itxTo'); 118 | }); 119 | 120 | it('should derive from this known address string testnet scripthash', function() { 121 | var address = new Address(); 122 | address.fromString(str); 123 | address.version = constants['testnet']['scripthash']; 124 | address.fromString(address.toString()); 125 | address.toString().should.equal('2MxjnmaMtsJfyFcyG3WZCzS2RihdNuWqeX4'); 126 | }); 127 | 128 | }); 129 | 130 | describe('#isValid', function() { 131 | 132 | it('should describe this valid address as valid', function() { 133 | var address = new Address(); 134 | address.fromString('37BahqRsFrAd3qLiNNwLNV3AWMRD7itxTo'); 135 | address.isValid().should.equal(true); 136 | }); 137 | 138 | it('should describe this address with unknown version as invalid', function() { 139 | var address = new Address(); 140 | address.fromString('37BahqRsFrAd3qLiNNwLNV3AWMRD7itxTo'); 141 | address.version = 1; 142 | address.isValid().should.equal(false); 143 | }); 144 | 145 | }); 146 | 147 | describe('#network', function() { 148 | 149 | it('should give mainnet for this mainnet address', function() { 150 | var addr = Address().fromString(str); 151 | addr.network().should.equal('mainnet'); 152 | addr.version = 1; 153 | addr.network().should.equal('unknown'); 154 | }); 155 | 156 | }); 157 | 158 | describe('#type', function() { 159 | 160 | it('should give pubkeyhash for this address', function() { 161 | var addr = Address().fromString(str); 162 | addr.type().should.equal('pubkeyhash'); 163 | addr.version = 1; 164 | addr.type().should.equal('unknown'); 165 | }); 166 | 167 | }); 168 | 169 | describe('#toBuffer', function() { 170 | 171 | it('should output this known hash', function() { 172 | var address = new Address(); 173 | address.fromString(str); 174 | address.toBuffer().slice(1).toString('hex').should.equal(pubkeyhash.toString('hex')); 175 | }); 176 | 177 | }); 178 | 179 | describe('#toString', function() { 180 | 181 | it('should output the same thing that was input', function() { 182 | var address = new Address(); 183 | address.fromString(str); 184 | address.toString().should.equal(str); 185 | }); 186 | 187 | }); 188 | 189 | describe('#validate', function() { 190 | 191 | it('should not throw an error on this valid address', function() { 192 | var address = new Address(); 193 | address.fromString(str); 194 | should.exist(address.validate()); 195 | }); 196 | 197 | it('should throw an error on this invalid version', function() { 198 | var address = new Address(); 199 | address.fromString(str); 200 | address.version = 1; 201 | (function() { 202 | address.validate(); 203 | }).should.throw('invalid version'); 204 | }); 205 | 206 | }); 207 | 208 | }); 209 | -------------------------------------------------------------------------------- /test/base58.js: -------------------------------------------------------------------------------- 1 | var Base58 = require('../lib/base58'); 2 | var should = require('chai').should(); 3 | 4 | describe('Base58', function() { 5 | var buf = new Buffer([0, 1, 2, 3, 253, 254, 255]); 6 | var enc = "1W7N4RuG"; 7 | 8 | it('should make an instance with "new"', function() { 9 | var b58 = new Base58(); 10 | should.exist(b58); 11 | }); 12 | 13 | it('should make an instance without "new"', function() { 14 | var b58 = Base58(); 15 | should.exist(b58); 16 | }); 17 | 18 | it('should allow this handy syntax', function() { 19 | Base58(buf).toString().should.equal(enc); 20 | Base58(enc).toBuffer().toString('hex').should.equal(buf.toString('hex')) 21 | }); 22 | 23 | describe('#set', function() { 24 | 25 | it('should set a blank buffer', function() { 26 | Base58().set({buf: new Buffer([])}); 27 | }); 28 | 29 | }); 30 | 31 | describe('@encode', function() { 32 | 33 | it('should encode the buffer accurately', function() { 34 | Base58.encode(buf).should.equal(enc); 35 | }); 36 | 37 | it('should throw an error when the Input is not a buffer', function() { 38 | (function() { 39 | Base58.encode("string") 40 | }).should.throw('Input should be a buffer'); 41 | }); 42 | 43 | }); 44 | 45 | describe('@decode', function() { 46 | 47 | it('should decode this encoded value correctly', function() { 48 | Base58.decode(enc).toString('hex').should.equal(buf.toString('hex')); 49 | Buffer.isBuffer(Base58.decode(enc)).should.equal(true); 50 | }); 51 | 52 | it('should throw an error when Input is not a string', function() { 53 | (function() { 54 | Base58.decode(5); 55 | }).should.throw('Input should be a string'); 56 | }); 57 | 58 | }); 59 | 60 | describe('#fromBuffer', function() { 61 | 62 | it('should not fail', function() { 63 | should.exist(Base58().fromBuffer(buf)); 64 | }); 65 | 66 | it('should set buffer', function() { 67 | var b58 = Base58().fromBuffer(buf); 68 | b58.buf.toString('hex').should.equal(buf.toString('hex')); 69 | }); 70 | 71 | }); 72 | 73 | describe('#fromString', function() { 74 | 75 | it('should convert this known string to a buffer', function() { 76 | Base58().fromString(enc).toBuffer().toString('hex').should.equal(buf.toString('hex')); 77 | }); 78 | 79 | }); 80 | 81 | describe('#toBuffer', function() { 82 | 83 | it('should return the buffer', function() { 84 | var b58 = Base58({buf: buf}); 85 | b58.buf.toString('hex').should.equal(buf.toString('hex')); 86 | }); 87 | 88 | }); 89 | 90 | describe('#toString', function() { 91 | 92 | it('should return the buffer', function() { 93 | var b58 = Base58({buf: buf}); 94 | b58.toString().should.equal(enc); 95 | }); 96 | 97 | }); 98 | 99 | }); 100 | -------------------------------------------------------------------------------- /test/base58check.js: -------------------------------------------------------------------------------- 1 | var should = require('chai').should(); 2 | var Base58Check = require('../lib/base58check'); 3 | var base58 = require('../lib/base58'); 4 | 5 | describe('Base58Check', function() { 6 | var buf = new Buffer([0, 1, 2, 3, 253, 254, 255]); 7 | var enc = "14HV44ipwoaqfg"; 8 | 9 | it('should make an instance with "new"', function() { 10 | var b58 = new Base58Check(); 11 | should.exist(b58); 12 | }); 13 | 14 | it('should make an instance without "new"', function() { 15 | var b58 = Base58Check(); 16 | should.exist(b58); 17 | }); 18 | 19 | it('should allow this handy syntax', function() { 20 | Base58Check(buf).toString().should.equal(enc); 21 | Base58Check(enc).toBuffer().toString('hex').should.equal(buf.toString('hex')); 22 | }); 23 | 24 | describe('#set', function() { 25 | 26 | it('should set a buf', function() { 27 | should.exist(Base58Check().set({buf: buf}).buf); 28 | }); 29 | 30 | }); 31 | 32 | describe('@encode', function() { 33 | 34 | it('should encode the buffer accurately', function() { 35 | Base58Check.encode(buf).should.equal(enc); 36 | }); 37 | 38 | it('should throw an error when the input is not a buffer', function() { 39 | (function() { 40 | Base58Check.encode("string") 41 | }).should.throw('Input must be a buffer'); 42 | }); 43 | 44 | }); 45 | 46 | describe('@decode', function() { 47 | 48 | it('should decode this encoded value correctly', function() { 49 | Base58Check.decode(enc).toString('hex').should.equal(buf.toString('hex')); 50 | }); 51 | 52 | it('should throw an error when input is not a string', function() { 53 | (function() { 54 | Base58Check.decode(5); 55 | }).should.throw('Input must be a string'); 56 | }); 57 | 58 | it('should throw an error when input is too short', function() { 59 | (function() { 60 | Base58Check.decode(enc.slice(0, 1)); 61 | }).should.throw('Input string too short'); 62 | }); 63 | 64 | it('should throw an error when there is a checksum mismatch', function() { 65 | var buf2 = base58.decode(enc); 66 | buf2[0] = buf2[0] + 1; 67 | var enc2 = base58.encode(buf2); 68 | (function() { 69 | Base58Check.decode(enc2); 70 | }).should.throw('Checksum mismatch'); 71 | }); 72 | 73 | }); 74 | 75 | describe('#fromBuffer', function() { 76 | 77 | it('should not fail', function() { 78 | should.exist(Base58Check().fromBuffer(buf)); 79 | }); 80 | 81 | it('should set buffer', function() { 82 | var b58 = Base58Check().fromBuffer(buf); 83 | b58.buf.toString('hex').should.equal(buf.toString('hex')); 84 | }); 85 | 86 | }); 87 | 88 | describe('#fromString', function() { 89 | 90 | it('should convert this known string to a buffer', function() { 91 | Base58Check().fromString(enc).toBuffer().toString('hex').should.equal(buf.toString('hex')); 92 | }); 93 | 94 | }); 95 | 96 | describe('#toBuffer', function() { 97 | 98 | it('should return the buffer', function() { 99 | var b58 = Base58Check({buf: buf}); 100 | b58.buf.toString('hex').should.equal(buf.toString('hex')); 101 | }); 102 | 103 | }); 104 | 105 | describe('#toString', function() { 106 | 107 | it('should return the buffer', function() { 108 | var b58 = Base58Check({buf: buf}); 109 | b58.toString().should.equal(enc); 110 | }); 111 | 112 | }); 113 | 114 | }); 115 | -------------------------------------------------------------------------------- /test/bip39.js: -------------------------------------------------------------------------------- 1 | var should = require('chai').should(); 2 | var BIP39 = require('../lib/bip39'); 3 | var vectors = require('./vectors/bip39'); 4 | 5 | describe('BIP39', function() { 6 | 7 | it('should initialize the class', function() { 8 | should.exist(BIP39); 9 | }); 10 | 11 | it('should have a wordlist of length 2048', function() { 12 | BIP39.wordlist_en.length.should.equal(2048); 13 | }); 14 | 15 | it('should generate a mnemonic phrase', function() { 16 | var phrase = BIP39.mnemonic(BIP39.wordlist_en, 128); 17 | }); 18 | 19 | describe('vectors', function() { 20 | 21 | vectors.english.forEach(function(vector, v) { 22 | it('should pass test vector ' + v, function() { 23 | var code = vector[0]; 24 | var mnemonic = vector[1]; 25 | var seed = vector[2]; 26 | var mnemonic1 = BIP39.entropy2mnemonic(BIP39.wordlist_en, new Buffer(code, 'hex')); 27 | var seed1 = BIP39.mnemonic2seed(mnemonic, 'TREZOR'); 28 | BIP39.check(BIP39.wordlist_en, mnemonic).should.be.true; 29 | mnemonic1.should.equal(mnemonic); 30 | seed1.toString('hex').should.equal(seed) 31 | }); 32 | }); 33 | 34 | }); 35 | 36 | }); 37 | -------------------------------------------------------------------------------- /test/block.js: -------------------------------------------------------------------------------- 1 | var Blockheader = require('../lib/blockheader'); 2 | var Block = require('../lib/block'); 3 | var BufR = require('../lib/bufr'); 4 | var BufW = require('../lib/bufw'); 5 | var Varint = require('../lib/varint'); 6 | var should = require('chai').should(); 7 | var Tx = require('../lib/tx'); 8 | 9 | describe('Block', function() { 10 | 11 | var txidhex = '8c9aa966d35bfeaf031409e0001b90ccdafd8d859799eb945a3c515b8260bcf2'; 12 | var txhex = '01000000029e8d016a7b0dc49a325922d05da1f916d1e4d4f0cb840c9727f3d22ce8d1363f000000008c493046022100e9318720bee5425378b4763b0427158b1051eec8b08442ce3fbfbf7b30202a44022100d4172239ebd701dae2fbaaccd9f038e7ca166707333427e3fb2a2865b19a7f27014104510c67f46d2cbb29476d1f0b794be4cb549ea59ab9cc1e731969a7bf5be95f7ad5e7f904e5ccf50a9dc1714df00fbeb794aa27aaff33260c1032d931a75c56f2ffffffffa3195e7a1ab665473ff717814f6881485dc8759bebe97e31c301ffe7933a656f020000008b48304502201c282f35f3e02a1f32d2089265ad4b561f07ea3c288169dedcf2f785e6065efa022100e8db18aadacb382eed13ee04708f00ba0a9c40e3b21cf91da8859d0f7d99e0c50141042b409e1ebbb43875be5edde9c452c82c01e3903d38fa4fd89f3887a52cb8aea9dc8aec7e2c9d5b3609c03eb16259a2537135a1bf0f9c5fbbcbdbaf83ba402442ffffffff02206b1000000000001976a91420bb5c3bfaef0231dc05190e7f1c8e22e098991e88acf0ca0100000000001976a9149e3e2d23973a04ec1b02be97c30ab9f2f27c3b2c88ac00000000'; 13 | var txbuf = new Buffer(txhex, 'hex'); 14 | var tx = Tx().fromBuffer(txbuf); 15 | var magicnum = 0xd9b4bef9; 16 | var blocksize = 50; 17 | bhhex = '0100000005050505050505050505050505050505050505050505050505050505050505050909090909090909090909090909090909090909090909090909090909090909020000000300000004000000'; 18 | bhbuf = new Buffer(bhhex, 'hex'); 19 | var bh = Blockheader().fromBuffer(bhbuf); 20 | var txsvi = Varint(1); 21 | var txs = [Tx().fromBuffer(txbuf)]; 22 | var block = Block().set({ 23 | magicnum: magicnum, 24 | blocksize: blocksize, 25 | blockheader: bh, 26 | txsvi: txsvi, 27 | txs: txs 28 | }); 29 | var blockhex = 'f9beb4d93200000001000000050505050505050505050505050505050505050505050505050505050505050509090909090909090909090909090909090909090909090909090909090909090200000003000000040000000101000000029e8d016a7b0dc49a325922d05da1f916d1e4d4f0cb840c9727f3d22ce8d1363f000000008c493046022100e9318720bee5425378b4763b0427158b1051eec8b08442ce3fbfbf7b30202a44022100d4172239ebd701dae2fbaaccd9f038e7ca166707333427e3fb2a2865b19a7f27014104510c67f46d2cbb29476d1f0b794be4cb549ea59ab9cc1e731969a7bf5be95f7ad5e7f904e5ccf50a9dc1714df00fbeb794aa27aaff33260c1032d931a75c56f2ffffffffa3195e7a1ab665473ff717814f6881485dc8759bebe97e31c301ffe7933a656f020000008b48304502201c282f35f3e02a1f32d2089265ad4b561f07ea3c288169dedcf2f785e6065efa022100e8db18aadacb382eed13ee04708f00ba0a9c40e3b21cf91da8859d0f7d99e0c50141042b409e1ebbb43875be5edde9c452c82c01e3903d38fa4fd89f3887a52cb8aea9dc8aec7e2c9d5b3609c03eb16259a2537135a1bf0f9c5fbbcbdbaf83ba402442ffffffff02206b1000000000001976a91420bb5c3bfaef0231dc05190e7f1c8e22e098991e88acf0ca0100000000001976a9149e3e2d23973a04ec1b02be97c30ab9f2f27c3b2c88ac00000000'; 30 | var blockbuf = new Buffer(blockhex, 'hex'); 31 | 32 | var genesishex = 'f9beb4d91d0100000100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000'; 33 | var genesisbuf = new Buffer(genesishex, 'hex'); 34 | var genesisidhex = '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f'; 35 | 36 | it('should make a new block', function() { 37 | var block = new Block(); 38 | should.exist(block); 39 | block = Block(); 40 | should.exist(block); 41 | block = Block(blockbuf); 42 | block.toBuffer().toString('hex').should.equal(blockhex); 43 | }); 44 | 45 | describe('#set', function() { 46 | 47 | it('should set these known values', function() { 48 | var block = Block().set({ 49 | magicnum: magicnum, 50 | blocksize: blocksize, 51 | blockheader: bh, 52 | txsvi: txsvi, 53 | txs: txs 54 | }); 55 | should.exist(block.magicnum); 56 | should.exist(block.blocksize); 57 | should.exist(block.blockheader); 58 | should.exist(block.txsvi); 59 | should.exist(block.txs); 60 | }); 61 | 62 | }); 63 | 64 | describe('#fromJSON', function() { 65 | 66 | it('should set these known values', function() { 67 | var block = Block().set({ 68 | magicnum: magicnum, 69 | blocksize: blocksize, 70 | blockheader: bh.toJSON(), 71 | txsvi: txsvi.toJSON(), 72 | txs: [txs[0].toJSON()] 73 | }); 74 | should.exist(block.magicnum); 75 | should.exist(block.blocksize); 76 | should.exist(block.blockheader); 77 | should.exist(block.txsvi); 78 | should.exist(block.txs); 79 | }); 80 | 81 | }); 82 | 83 | describe('#toJSON', function() { 84 | 85 | it('should recover these known values', function() { 86 | var json = block.toJSON(); 87 | should.exist(json.magicnum); 88 | should.exist(json.blocksize); 89 | should.exist(json.blockheader); 90 | should.exist(json.txsvi); 91 | should.exist(json.txs); 92 | }); 93 | 94 | }); 95 | 96 | describe('#fromBuffer', function() { 97 | 98 | it('should make a block from this known buffer', function() { 99 | var block = Block().fromBuffer(blockbuf); 100 | block.toBuffer().toString('hex').should.equal(blockhex); 101 | }); 102 | 103 | }); 104 | 105 | describe('#fromBufR', function() { 106 | 107 | it('should make a block from this known buffer', function() { 108 | var block = Block().fromBufR(BufR(blockbuf)); 109 | block.toBuffer().toString('hex').should.equal(blockhex); 110 | }); 111 | 112 | }); 113 | 114 | describe('#toBuffer', function() { 115 | 116 | it('should recover a block from this known buffer', function() { 117 | var block = Block().fromBuffer(blockbuf); 118 | block.toBuffer().toString('hex').should.equal(blockhex); 119 | }); 120 | 121 | }); 122 | 123 | describe('#toBufW', function() { 124 | 125 | it('should recover a block from this known buffer', function() { 126 | var block = Block().fromBuffer(blockbuf); 127 | block.toBufW().concat().toString('hex').should.equal(blockhex); 128 | }); 129 | 130 | }); 131 | 132 | describe('#hash', function() { 133 | 134 | it('should return the correct hash of the genesis block', function() { 135 | var block = Block().fromBuffer(genesisbuf); 136 | var blockhash = new Buffer(Array.apply([], new Buffer(genesisidhex, 'hex')).reverse()); 137 | block.hash().toString('hex').should.equal(blockhash.toString('hex')); 138 | }); 139 | 140 | }); 141 | 142 | describe('#id', function() { 143 | 144 | it('should return the correct id of the genesis block', function() { 145 | var block = Block().fromBuffer(genesisbuf); 146 | block.id().toString('hex').should.equal(genesisidhex); 147 | }); 148 | 149 | }); 150 | 151 | }); 152 | -------------------------------------------------------------------------------- /test/blockheader.js: -------------------------------------------------------------------------------- 1 | var Blockheader = require('../lib/blockheader'); 2 | var BufW = require('../lib/bufw'); 3 | var BufR = require('../lib/bufr'); 4 | var should = require('chai').should(); 5 | 6 | describe('Blockheader', function() { 7 | 8 | var bh = new Blockheader(); 9 | var version = 1; 10 | var prevblockidbuf = new Buffer(32); 11 | prevblockidbuf.fill(5); 12 | var merklerootbuf = new Buffer(32); 13 | merklerootbuf.fill(9); 14 | var time = 2; 15 | var bits = 3; 16 | var nonce = 4; 17 | bh.set({ 18 | version: version, 19 | prevblockidbuf: prevblockidbuf, 20 | merklerootbuf: merklerootbuf, 21 | time: time, 22 | bits: bits, 23 | nonce: nonce 24 | }); 25 | bhhex = '0100000005050505050505050505050505050505050505050505050505050505050505050909090909090909090909090909090909090909090909090909090909090909020000000300000004000000'; 26 | bhbuf = new Buffer(bhhex, 'hex'); 27 | 28 | it('should make a new blockheader', function() { 29 | var blockheader = new Blockheader(); 30 | should.exist(blockheader); 31 | blockheader = Blockheader(); 32 | should.exist(blockheader); 33 | Blockheader(bhbuf).toBuffer().toString('hex').should.equal(bhhex); 34 | }); 35 | 36 | describe('#set', function() { 37 | 38 | it('should set all the variables', function() { 39 | bh.set({ 40 | version: version, 41 | prevblockidbuf: prevblockidbuf, 42 | merklerootbuf: merklerootbuf, 43 | time: time, 44 | bits: bits, 45 | nonce: nonce 46 | }); 47 | should.exist(bh.version); 48 | should.exist(bh.prevblockidbuf); 49 | should.exist(bh.merklerootbuf); 50 | should.exist(bh.time); 51 | should.exist(bh.bits); 52 | should.exist(bh.nonce); 53 | }); 54 | 55 | }); 56 | 57 | describe('#fromJSON', function() { 58 | 59 | it('should set all the variables', function() { 60 | var bh = Blockheader().fromJSON({ 61 | version: version, 62 | prevblockidbuf: prevblockidbuf.toString('hex'), 63 | merklerootbuf: merklerootbuf.toString('hex'), 64 | time: time, 65 | bits: bits, 66 | nonce: nonce 67 | }); 68 | should.exist(bh.version); 69 | should.exist(bh.prevblockidbuf); 70 | should.exist(bh.merklerootbuf); 71 | should.exist(bh.time); 72 | should.exist(bh.bits); 73 | should.exist(bh.nonce); 74 | }); 75 | 76 | }); 77 | 78 | describe('#toJSON', function() { 79 | 80 | it('should set all the variables', function() { 81 | var json = bh.toJSON(); 82 | should.exist(json.version); 83 | should.exist(json.prevblockidbuf); 84 | should.exist(json.merklerootbuf); 85 | should.exist(json.time); 86 | should.exist(json.bits); 87 | should.exist(json.nonce); 88 | }); 89 | 90 | }); 91 | 92 | describe('#fromBuffer', function() { 93 | 94 | it('should parse this known buffer', function() { 95 | Blockheader().fromBuffer(bhbuf).toBuffer().toString('hex').should.equal(bhhex); 96 | }); 97 | 98 | }); 99 | 100 | describe('#fromBufR', function() { 101 | 102 | it('should parse this known buffer', function() { 103 | Blockheader().fromBufR(BufR(bhbuf)).toBuffer().toString('hex').should.equal(bhhex); 104 | }); 105 | 106 | }); 107 | 108 | describe('#toBuffer', function() { 109 | 110 | it('should output this known buffer', function() { 111 | Blockheader().fromBuffer(bhbuf).toBuffer().toString('hex').should.equal(bhhex); 112 | }); 113 | 114 | }); 115 | 116 | describe('#toBufW', function() { 117 | 118 | it('should output this known buffer', function() { 119 | Blockheader().fromBuffer(bhbuf).toBufW().concat().toString('hex').should.equal(bhhex); 120 | }); 121 | 122 | }); 123 | 124 | }); 125 | -------------------------------------------------------------------------------- /test/bsm.js: -------------------------------------------------------------------------------- 1 | var Address = require('../lib/address'); 2 | var BSM = require('../lib/bsm'); 3 | var Keypair = require('../lib/keypair'); 4 | var should = require('chai').should(); 5 | 6 | describe('BSM', function() { 7 | 8 | it('should make a new bsm', function() { 9 | var bsm = new BSM(); 10 | should.exist(bsm); 11 | }); 12 | 13 | it('should make a new bsm when called without "new"', function() { 14 | var bsm = BSM(); 15 | should.exist(bsm); 16 | }); 17 | 18 | describe('#set', function() { 19 | 20 | it('should set the messagebuf', function() { 21 | var messagebuf = new Buffer('message'); 22 | should.exist(BSM().set({messagebuf: messagebuf}).messagebuf); 23 | }); 24 | 25 | }); 26 | 27 | describe('@sign', function() { 28 | var messagebuf = new Buffer('this is my message'); 29 | var keypair = Keypair().fromRandom(); 30 | 31 | it('should return a base64 string', function() { 32 | var sigstr = BSM.sign(messagebuf, keypair); 33 | var sigbuf = new Buffer(sigstr, 'base64'); 34 | sigbuf.length.should.equal(1 + 32 + 32); 35 | }); 36 | 37 | it('should sign with a compressed pubkey', function() { 38 | var keypair = Keypair().fromRandom(); 39 | keypair.pubkey.compressed = true; 40 | var sigstr = BSM.sign(messagebuf, keypair); 41 | var sigbuf = new Buffer(sigstr, 'base64'); 42 | sigbuf[0].should.be.above(27 + 4 - 1); 43 | sigbuf[0].should.be.below(27 + 4 + 4 - 1); 44 | }); 45 | 46 | it('should sign with an uncompressed pubkey', function() { 47 | var keypair = Keypair().fromRandom(); 48 | keypair.pubkey.compressed = false; 49 | var sigstr = BSM.sign(messagebuf, keypair); 50 | var sigbuf = new Buffer(sigstr, 'base64'); 51 | sigbuf[0].should.be.above(27 - 1); 52 | sigbuf[0].should.be.below(27 + 4 - 1); 53 | }); 54 | 55 | }); 56 | 57 | describe('@verify', function() { 58 | var messagebuf = new Buffer('this is my message'); 59 | var keypair = Keypair().fromRandom(); 60 | 61 | it('should verify a signed message', function() { 62 | var sigstr = BSM.sign(messagebuf, keypair); 63 | var addr = Address().fromPubkey(keypair.pubkey); 64 | BSM.verify(messagebuf, sigstr, addr).should.equal(true); 65 | }); 66 | 67 | it('should verify this known good signature', function() { 68 | var addrstr = '1CKTmxj6DjGrGTfbZzVxnY4Besbv8oxSZb'; 69 | var address = Address().fromString(addrstr); 70 | var sigstr = 'IOrTlbNBI0QO990xOw4HAjnvRl/1zR+oBMS6HOjJgfJqXp/1EnFrcJly0UcNelqJNIAH4f0abxOZiSpYmenMH4M='; 71 | BSM.verify(messagebuf, sigstr, address); 72 | }); 73 | 74 | }); 75 | 76 | describe('#sign', function() { 77 | var messagebuf = new Buffer('this is my message'); 78 | var keypair = Keypair().fromRandom(); 79 | 80 | it('should sign a message', function() { 81 | var bsm = new BSM(); 82 | bsm.messagebuf = messagebuf; 83 | bsm.keypair = keypair; 84 | bsm.sign(); 85 | var sig = bsm.sig; 86 | should.exist(sig); 87 | }); 88 | 89 | }); 90 | 91 | describe('#verify', function() { 92 | var messagebuf = new Buffer('this is my message'); 93 | var keypair = Keypair().fromRandom(); 94 | 95 | it('should verify a message that was just signed', function() { 96 | var bsm = new BSM(); 97 | bsm.messagebuf = messagebuf; 98 | bsm.keypair = keypair; 99 | bsm.address = Address().fromPubkey(keypair.pubkey); 100 | bsm.sign(); 101 | bsm.verify(); 102 | bsm.verified.should.equal(true); 103 | }); 104 | 105 | }); 106 | 107 | }); 108 | -------------------------------------------------------------------------------- /test/bufw.js: -------------------------------------------------------------------------------- 1 | var BufW = require('../lib/bufw'); 2 | var BufR = require('../lib/bufr'); 3 | var BN = require('../lib/bn'); 4 | var should = require('chai').should(); 5 | 6 | describe('BufW', function() { 7 | 8 | it('should create a new buffer writer', function() { 9 | var bw = new BufW(); 10 | should.exist(bw); 11 | }); 12 | 13 | describe('#set', function() { 14 | 15 | it('set bufs', function() { 16 | var buf1 = new Buffer([0]); 17 | var buf2 = new Buffer([1]); 18 | var bufs = [buf1, buf2]; 19 | var bw = new BufW().set({bufs: [buf1, buf2]}); 20 | bw.concat().toString('hex').should.equal('0001'); 21 | }); 22 | 23 | }); 24 | 25 | describe('#toBuffer', function() { 26 | 27 | it('should concat these two bufs', function() { 28 | var buf1 = new Buffer([0]); 29 | var buf2 = new Buffer([1]); 30 | var bw = new BufW({bufs: [buf1, buf2]}); 31 | bw.toBuffer().toString('hex').should.equal('0001'); 32 | }); 33 | 34 | }); 35 | 36 | describe('#concat', function() { 37 | 38 | it('should concat these two bufs', function() { 39 | var buf1 = new Buffer([0]); 40 | var buf2 = new Buffer([1]); 41 | var bw = new BufW({bufs: [buf1, buf2]}); 42 | bw.concat().toString('hex').should.equal('0001'); 43 | }); 44 | 45 | }); 46 | 47 | describe('#write', function() { 48 | 49 | it('should write a buffer', function() { 50 | var buf = new Buffer([0]); 51 | var bw = new BufW(); 52 | bw.write(buf); 53 | bw.concat().toString('hex').should.equal('00'); 54 | }); 55 | 56 | }); 57 | 58 | describe('#writeReverse', function() { 59 | 60 | it('should write a buffer in reverse', function() { 61 | var buf = new Buffer([0, 1]); 62 | var bw = new BufW(); 63 | bw.writeReverse(buf); 64 | bw.concat().toString('hex').should.equal('0100'); 65 | }); 66 | 67 | }); 68 | 69 | describe('#writeUInt8', function() { 70 | 71 | it('should write 1', function() { 72 | var bw = new BufW(); 73 | bw.writeUInt8(1).concat().toString('hex').should.equal('01'); 74 | }); 75 | 76 | }); 77 | 78 | describe('#writeInt8', function() { 79 | 80 | it('should write 1', function() { 81 | var bw = new BufW(); 82 | bw.writeInt8(1).concat().toString('hex').should.equal('01'); 83 | BufW().writeInt8(-1).concat().toString('hex').should.equal('ff'); 84 | }); 85 | 86 | }); 87 | 88 | describe('#writeUInt16BE', function() { 89 | 90 | it('should write 1', function() { 91 | var bw = new BufW(); 92 | bw.writeUInt16BE(1).concat().toString('hex').should.equal('0001'); 93 | }); 94 | 95 | }); 96 | 97 | describe('#writeInt16BE', function() { 98 | 99 | it('should write 1', function() { 100 | var bw = new BufW(); 101 | bw.writeInt16BE(1).concat().toString('hex').should.equal('0001'); 102 | BufW().writeInt16BE(-1).concat().toString('hex').should.equal('ffff'); 103 | }); 104 | 105 | }); 106 | 107 | describe('#writeUInt16LE', function() { 108 | 109 | it('should write 1', function() { 110 | var bw = new BufW(); 111 | bw.writeUInt16LE(1).concat().toString('hex').should.equal('0100'); 112 | }); 113 | 114 | }); 115 | 116 | describe('#writeInt16LE', function() { 117 | 118 | it('should write 1', function() { 119 | var bw = new BufW(); 120 | bw.writeInt16LE(1).concat().toString('hex').should.equal('0100'); 121 | BufW().writeInt16LE(-1).concat().toString('hex').should.equal('ffff'); 122 | }); 123 | 124 | }); 125 | 126 | describe('#writeUInt32BE', function() { 127 | 128 | it('should write 1', function() { 129 | var bw = new BufW(); 130 | bw.writeUInt32BE(1).concat().toString('hex').should.equal('00000001'); 131 | }); 132 | 133 | }); 134 | 135 | describe('#writeInt32BE', function() { 136 | 137 | it('should write 1', function() { 138 | var bw = new BufW(); 139 | bw.writeInt32BE(1).concat().toString('hex').should.equal('00000001'); 140 | BufW().writeInt32BE(-1).concat().toString('hex').should.equal('ffffffff'); 141 | }); 142 | 143 | }); 144 | 145 | describe('#writeUInt32LE', function() { 146 | 147 | it('should write 1', function() { 148 | var bw = new BufW(); 149 | bw.writeUInt32LE(1).concat().toString('hex').should.equal('01000000'); 150 | }); 151 | 152 | }); 153 | 154 | describe('#writeInt32LE', function() { 155 | 156 | it('should write 1', function() { 157 | var bw = new BufW(); 158 | bw.writeInt32LE(1).concat().toString('hex').should.equal('01000000'); 159 | BufW().writeInt32LE(-1).concat().toString('hex').should.equal('ffffffff'); 160 | }); 161 | 162 | }); 163 | 164 | describe('#writeUInt64BEBN', function() { 165 | 166 | it('should write 1', function() { 167 | var bw = new BufW(); 168 | bw.writeUInt64BEBN(BN(1)).concat().toString('hex').should.equal('0000000000000001'); 169 | }); 170 | 171 | }); 172 | 173 | describe('#writeUInt64LEBN', function() { 174 | 175 | it('should write 1', function() { 176 | var bw = new BufW(); 177 | bw.writeUInt64LEBN(BN(1)).concat().toString('hex').should.equal('0100000000000000'); 178 | }); 179 | 180 | }); 181 | 182 | describe('#writeVarint', function() { 183 | 184 | it('should write a 1 byte varint', function() { 185 | var bw = new BufW(); 186 | bw.writeVarintNum(1); 187 | bw.concat().length.should.equal(1); 188 | }); 189 | 190 | it('should write a 3 byte varint', function() { 191 | var bw = new BufW(); 192 | bw.writeVarintNum(1000); 193 | bw.concat().length.should.equal(3); 194 | }); 195 | 196 | it('should write a 5 byte varint', function() { 197 | var bw = new BufW(); 198 | bw.writeVarintNum(Math.pow(2, 16 + 1)); 199 | bw.concat().length.should.equal(5); 200 | }); 201 | 202 | it('should write a 9 byte varint', function() { 203 | var bw = new BufW(); 204 | bw.writeVarintNum(Math.pow(2, 32 + 1)); 205 | bw.concat().length.should.equal(9); 206 | }); 207 | 208 | it('should read back the same value it wrote for a 9 byte varint', function() { 209 | var bw = new BufW(); 210 | var n = Math.pow(2, 53); 211 | n.should.equal(n + 1); //javascript number precision limit 212 | bw.writeVarintNum(n); 213 | var br = new BufR({buf: bw.concat()}); 214 | br.readVarintBN().toNumber().should.equal(n); 215 | }); 216 | 217 | }); 218 | 219 | describe('#writeVarintBN', function() { 220 | 221 | it('should write a 1 byte varint', function() { 222 | var bw = new BufW(); 223 | bw.writeVarintBN(BN(1)); 224 | bw.concat().length.should.equal(1); 225 | }); 226 | 227 | it('should write a 3 byte varint', function() { 228 | var bw = new BufW(); 229 | bw.writeVarintBN(BN(1000)); 230 | bw.concat().length.should.equal(3); 231 | }); 232 | 233 | it('should write a 5 byte varint', function() { 234 | var bw = new BufW(); 235 | bw.writeVarintBN(BN(Math.pow(2, 16 + 1))); 236 | bw.concat().length.should.equal(5); 237 | }); 238 | 239 | it('should write a 9 byte varint', function() { 240 | var bw = new BufW(); 241 | bw.writeVarintBN(BN(Math.pow(2, 32 + 1))); 242 | bw.concat().length.should.equal(9); 243 | }); 244 | 245 | }); 246 | 247 | }); 248 | -------------------------------------------------------------------------------- /test/examples.js: -------------------------------------------------------------------------------- 1 | if (process.browser) 2 | return; //examples are loaded from files, which doesn't work in the browser 3 | 4 | var should = require('chai').should(); 5 | var fs = require('fs'); 6 | 7 | describe('Examples', function() { 8 | 9 | var filenames = fs.readdirSync(__dirname + '/../examples/'); 10 | 11 | filenames.forEach(function(filename) { 12 | 13 | if (filename.slice(filename.length - 3) === '.js') { 14 | 15 | describe(filename, function() { 16 | 17 | it('should not throw any errors', function() { 18 | (function() { 19 | var save = console.log; 20 | console.log = function() {}; 21 | require('../examples/' + filename); 22 | console.log = save; 23 | }).should.not.throw(); 24 | }); 25 | 26 | }); 27 | 28 | } 29 | 30 | }); 31 | 32 | }); 33 | -------------------------------------------------------------------------------- /test/expmt/aes.js: -------------------------------------------------------------------------------- 1 | var should = require('chai').should(); 2 | var Hash = require('../../lib/hash'); 3 | var AES = require('../../lib/expmt/aes'); 4 | 5 | describe('AES', function() { 6 | var m128 = Hash.sha256(new Buffer('test1')).slice(0, 128 / 8); 7 | 8 | var k128 = Hash.sha256(new Buffer('test2')).slice(0, 128 / 8); 9 | var k192 = Hash.sha256(new Buffer('test2')).slice(0, 192 / 8); 10 | var k256 = Hash.sha256(new Buffer('test2')).slice(0, 256 / 8); 11 | 12 | var e128 = new Buffer('3477e13884125038f4dc24e9d2cfbbc7', 'hex'); 13 | var e192 = new Buffer('b670954c0e2da1aaa5f9063de04eb961', 'hex'); 14 | var e256 = new Buffer('dd2ce24581183a4a7c0b1068f8bc79f0', 'hex'); 15 | 16 | 17 | describe('@encrypt', function() { 18 | 19 | it('should encrypt with a 128 bit key', function() { 20 | var encbuf = AES.encrypt(m128, k128); 21 | encbuf.toString('hex').should.equal(e128.toString('hex')); 22 | }); 23 | 24 | it('should encrypt with a 192 bit key', function() { 25 | var encbuf = AES.encrypt(m128, k192); 26 | encbuf.toString('hex').should.equal(e192.toString('hex')); 27 | }); 28 | 29 | it('should encrypt with a 256 bit key', function() { 30 | var encbuf = AES.encrypt(m128, k256); 31 | encbuf.toString('hex').should.equal(e256.toString('hex')); 32 | }); 33 | 34 | }); 35 | 36 | describe('@decrypt', function() { 37 | 38 | it('should encrypt/decrypt with a 128 bit key', function() { 39 | var encbuf = AES.encrypt(m128, k128); 40 | var m = AES.decrypt(encbuf, k128); 41 | m.toString('hex').should.equal(m128.toString('hex')); 42 | }); 43 | 44 | it('should encrypt/decrypt with a 192 bit key', function() { 45 | var encbuf = AES.encrypt(m128, k192); 46 | var m = AES.decrypt(encbuf, k192); 47 | m.toString('hex').should.equal(m128.toString('hex')); 48 | }); 49 | 50 | it('should encrypt/decrypt with a 256 bit key', function() { 51 | var encbuf = AES.encrypt(m128, k256); 52 | var m = AES.decrypt(encbuf, k256); 53 | m.toString('hex').should.equal(m128.toString('hex')); 54 | }); 55 | 56 | }); 57 | 58 | describe('@buf2words', function() { 59 | 60 | it('should convert this 4 length buffer into an array', function() { 61 | var buf = new Buffer([0, 0, 0, 0]); 62 | var words = AES.buf2words(buf); 63 | words.length.should.equal(1); 64 | }); 65 | 66 | it('should throw an error on this 5 length buffer', function() { 67 | var buf = new Buffer([0, 0, 0, 0, 0]); 68 | (function() { 69 | var words = AES.buf2words(buf); 70 | }).should.throw(); 71 | }); 72 | 73 | }); 74 | 75 | describe('@words2buf', function() { 76 | 77 | it('should convert this array into a buffer', function() { 78 | var a = [100, 0]; 79 | var buf = AES.words2buf(a); 80 | buf.length.should.equal(8); 81 | }); 82 | 83 | }); 84 | 85 | }); 86 | -------------------------------------------------------------------------------- /test/expmt/aescbc.js: -------------------------------------------------------------------------------- 1 | var should = require('chai').should(); 2 | var AESCBC = require('../../lib/expmt/aescbc'); 3 | 4 | describe('AESCBC', function() { 5 | 6 | describe('@encrypt', function() { 7 | 8 | it('should return encrypt one block', function() { 9 | var password = "password"; 10 | var messagebuf = new Buffer(128 / 8 - 1); 11 | messagebuf.fill(0); 12 | var encbuf = AESCBC.encrypt(messagebuf, password); 13 | encbuf.length.should.equal(128 / 8 + 128 / 8); 14 | }); 15 | 16 | }); 17 | 18 | describe('@decrypt', function() { 19 | 20 | it('should decrypt that which was encrypted', function() { 21 | var password = "password"; 22 | var messagebuf = new Buffer(128 / 8 - 1); 23 | messagebuf.fill(0); 24 | var encbuf = AESCBC.encrypt(messagebuf, password); 25 | var messagebuf2 = AESCBC.decrypt(encbuf, password); 26 | messagebuf2.toString('hex').should.equal(messagebuf.toString('hex')); 27 | }); 28 | 29 | }); 30 | 31 | describe('@encryptCipherkey', function() { 32 | 33 | it('should return encrypt one block', function() { 34 | var cipherkeybuf = new Buffer(256 / 8); 35 | cipherkeybuf.fill(0x10); 36 | var ivbuf = new Buffer(128 / 8); 37 | ivbuf.fill(0); 38 | var messagebuf = new Buffer(128 / 8 - 1); 39 | messagebuf.fill(0); 40 | var encbuf = AESCBC.encryptCipherkey(messagebuf, cipherkeybuf, ivbuf); 41 | encbuf.length.should.equal(128 / 8 + 128 / 8); 42 | }); 43 | 44 | it('should return encrypt two blocks', function() { 45 | var cipherkeybuf = new Buffer(256 / 8); 46 | cipherkeybuf.fill(0x10); 47 | var ivbuf = new Buffer(128 / 8); 48 | ivbuf.fill(0); 49 | var messagebuf = new Buffer(128 / 8); 50 | messagebuf.fill(0); 51 | var encbuf = AESCBC.encryptCipherkey(messagebuf, cipherkeybuf, ivbuf); 52 | encbuf.length.should.equal(128 / 8 + 128 / 8 + 128 / 8); 53 | }); 54 | 55 | }); 56 | 57 | describe('@decryptCipherkey', function() { 58 | 59 | it('should decrypt that which was encrypted', function() { 60 | var cipherkeybuf = new Buffer(256 / 8); 61 | cipherkeybuf.fill(0x10); 62 | var ivbuf = new Buffer(128 / 8); 63 | ivbuf.fill(0); 64 | var messagebuf = new Buffer(128 / 8); 65 | messagebuf.fill(0); 66 | var encbuf = AESCBC.encryptCipherkey(messagebuf, cipherkeybuf, ivbuf); 67 | var messagebuf2 = AESCBC.decryptCipherkey(encbuf, cipherkeybuf); 68 | messagebuf2.toString('hex').should.equal(messagebuf.toString('hex')); 69 | }); 70 | 71 | }); 72 | 73 | }); 74 | -------------------------------------------------------------------------------- /test/expmt/ecies.js: -------------------------------------------------------------------------------- 1 | var ECIES = require('../../lib/expmt/ecies'); 2 | var should = require('chai').should(); 3 | var Keypair = require('../../lib/keypair'); 4 | var Hash = require('../../lib/hash'); 5 | 6 | describe('#ECIES', function() { 7 | 8 | it('should make a new ECIES object', function() { 9 | var ecies = new ECIES(); 10 | should.exist(ecies); 11 | }); 12 | 13 | it('should make a new ECIES object when called without "new"', function() { 14 | var ecies = ECIES(); 15 | should.exist(ecies); 16 | }); 17 | 18 | var fromkey = Keypair().fromRandom(); 19 | var tokey = Keypair().fromRandom(); 20 | var messagebuf = Hash.sha256(new Buffer('my message is the hash of this string')); 21 | 22 | describe('@encrypt', function() { 23 | 24 | it('should return a buffer', function() { 25 | var encbuf = ECIES.encrypt(messagebuf, tokey.pubkey, fromkey); 26 | Buffer.isBuffer(encbuf).should.equal(true); 27 | }); 28 | 29 | it('should return a buffer if fromkey is not present', function() { 30 | var encbuf = ECIES.encrypt(messagebuf, tokey.pubkey); 31 | Buffer.isBuffer(encbuf).should.equal(true); 32 | }); 33 | 34 | }); 35 | 36 | describe('@decrypt', function() { 37 | 38 | it('should decrypt that which was encrypted', function() { 39 | var encbuf = ECIES.encrypt(messagebuf, tokey.pubkey, fromkey); 40 | var messagebuf2 = ECIES.decrypt(encbuf, tokey.privkey); 41 | messagebuf2.toString('hex').should.equal(messagebuf.toString('hex')); 42 | }); 43 | 44 | it('should decrypt that which was encrypted if fromkeypair was randomly generated', function() { 45 | var encbuf = ECIES.encrypt(messagebuf, tokey.pubkey); 46 | var messagebuf2 = ECIES.decrypt(encbuf, tokey.privkey); 47 | messagebuf2.toString('hex').should.equal(messagebuf.toString('hex')); 48 | }); 49 | 50 | }); 51 | 52 | }); 53 | -------------------------------------------------------------------------------- /test/expmt/stealth/address.js: -------------------------------------------------------------------------------- 1 | var SAddress = require('../../../lib/expmt/stealth/address'); 2 | var should = require('chai').should(); 3 | var SKey = require('../../../lib/expmt/stealth/key'); 4 | var Keypair = require('../../../lib/keypair'); 5 | var Privkey = require('../../../lib/privkey'); 6 | var Pubkey = require('../../../lib/pubkey'); 7 | var BN = require('../../../lib/bn'); 8 | var Hash = require('../../../lib/hash'); 9 | var Base58check = require('../../../lib/base58check'); 10 | 11 | describe('SAddress', function() { 12 | 13 | var stealthkey = SKey(); 14 | stealthkey.payloadKeypair = Keypair(); 15 | stealthkey.payloadKeypair.privkey = Privkey(); 16 | stealthkey.payloadKeypair.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 1'))); 17 | stealthkey.payloadKeypair.privkey2pubkey(); 18 | stealthkey.scanKeypair = Keypair(); 19 | stealthkey.scanKeypair.privkey = Privkey(); 20 | stealthkey.scanKeypair.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 2'))); 21 | stealthkey.scanKeypair.privkey2pubkey(); 22 | 23 | var senderKeypair = Keypair(); 24 | senderKeypair.privkey = Privkey(); 25 | senderKeypair.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 3'))); 26 | senderKeypair.privkey2pubkey(); 27 | 28 | var addressString = 'vJmtuUb8ysKiM1HtHQF23FGfjGAKu5sM94UyyjknqhJHNdj5CZzwtpGzeyaATQ2HvuzomNVtiwsTJSWzzCBgCTtUZbRFpzKVq9MAUr'; 29 | var dwhex = '2a0002697763d7e9becb0c180083738c32c05b0e2fee26d6278020c06bbb04d5f66b32010362408459041e0473298af3824dbabe4d2b7f846825ed4d1c2e2c670c07cb275d0100'; 30 | var dwbuf = new Buffer(dwhex, 'hex'); 31 | 32 | it('should make a new stealth address', function() { 33 | var sa = new SAddress(); 34 | should.exist(sa); 35 | sa = SAddress(); 36 | should.exist(sa); 37 | sa = SAddress(addressString); 38 | should.exist(sa); 39 | sa = SAddress(Base58check.decode(addressString)); 40 | should.exist(sa); 41 | }); 42 | 43 | describe('#fromJSON', function() { 44 | 45 | it('should give a stealthkey address with the right pubkeys', function() { 46 | var sa = new SAddress(); 47 | sa.fromJSON(addressString); 48 | sa.payloadPubkey.toString().should.equal(stealthkey.payloadKeypair.pubkey.toString()); 49 | sa.scanPubkey.toString().should.equal(stealthkey.scanKeypair.pubkey.toString()); 50 | }); 51 | 52 | }); 53 | 54 | describe('#toJSON', function() { 55 | 56 | it('should return this known address string', function() { 57 | SAddress().fromJSON(addressString).toJSON().should.equal(addressString); 58 | }); 59 | 60 | }); 61 | 62 | describe('#fromBuffer', function() { 63 | 64 | it('should parse this DW buffer', function() { 65 | SAddress().fromBuffer(new Buffer(dwhex, 'hex')).toBuffer().toString('hex').should.equal(dwhex); 66 | }); 67 | 68 | }); 69 | 70 | describe('#fromString', function() { 71 | 72 | it('should parse this DW buffer', function() { 73 | SAddress().fromString(Base58check(new Buffer(dwhex, 'hex')).toString()).toBuffer().toString('hex').should.equal(dwhex); 74 | }); 75 | 76 | }); 77 | 78 | describe('#getSharedKeypair', function() { 79 | 80 | it('should return a key', function() { 81 | var sa = new SAddress(); 82 | sa.payloadPubkey = stealthkey.payloadKeypair.pubkey; 83 | sa.scanPubkey = stealthkey.scanKeypair.pubkey; 84 | var key = sa.getSharedKeypair(senderKeypair); 85 | (key instanceof Keypair).should.equal(true); 86 | }); 87 | 88 | it('should return the same key as SKey.prototype.getSharedKeypair', function() { 89 | var sa = new SAddress(); 90 | sa.payloadPubkey = stealthkey.payloadKeypair.pubkey; 91 | sa.scanPubkey = stealthkey.scanKeypair.pubkey; 92 | var key = sa.getSharedKeypair(senderKeypair); 93 | 94 | var key2 = stealthkey.getSharedKeypair(senderKeypair.pubkey); 95 | key.toString().should.equal(key2.toString()); 96 | }); 97 | 98 | }); 99 | 100 | describe('#getReceivePubkey', function() { 101 | 102 | it('should return a pubkey', function() { 103 | var pubkey = SAddress().fromSKey(stealthkey).getReceivePubkey(senderKeypair); 104 | (pubkey instanceof Pubkey).should.equal(true); 105 | }); 106 | 107 | it('should return the same pubkey as getReceivePubkey', function() { 108 | var pubkey = SAddress().fromSKey(stealthkey).getReceivePubkey(senderKeypair); 109 | var pubkey2 = stealthkey.getReceivePubkey(senderKeypair.pubkey); 110 | pubkey2.toString().should.equal(pubkey.toString()); 111 | }); 112 | 113 | }); 114 | 115 | describe('#toBuffer', function() { 116 | 117 | it('should return this known address buffer', function() { 118 | var buf = Base58check.decode(addressString); 119 | SAddress().fromBuffer(dwbuf).toBuffer().toString('hex').should.equal(dwhex); 120 | }); 121 | 122 | }); 123 | 124 | describe('#toString', function() { 125 | 126 | it('should return this known address buffer', function() { 127 | var buf = Base58check.decode(addressString); 128 | SAddress().fromBuffer(buf).toString().should.equal(Base58check(new Buffer(dwhex, 'hex')).toString()); 129 | }); 130 | 131 | }); 132 | 133 | describe('@parseDWBuffer', function() { 134 | 135 | it('should parse this known DW buffer', function() { 136 | var buf = new Buffer(dwhex, 'hex'); 137 | var parsed = SAddress.parseDWBuffer(buf); 138 | parsed.version.should.equal(42); 139 | parsed.options.should.equal(0); 140 | parsed.scanPubkey.toString().should.equal('02697763d7e9becb0c180083738c32c05b0e2fee26d6278020c06bbb04d5f66b32'); 141 | parsed.nPayloadPubkeys.should.equal(1); 142 | parsed.payloadPubkeys[0].toString().should.equal('0362408459041e0473298af3824dbabe4d2b7f846825ed4d1c2e2c670c07cb275d'); 143 | parsed.nSigs.should.equal(1); 144 | parsed.prefix.toString().should.equal(''); 145 | }); 146 | 147 | }); 148 | 149 | }); 150 | -------------------------------------------------------------------------------- /test/expmt/stealth/key.js: -------------------------------------------------------------------------------- 1 | var should = require('chai').should(); 2 | var SKey = require('../../../lib/expmt/stealth/key'); 3 | var Keypair = require('../../../lib/keypair'); 4 | var Privkey = require('../../../lib/privkey'); 5 | var Pubkey = require('../../../lib/pubkey'); 6 | var BN = require('../../../lib/bn'); 7 | var Hash = require('../../../lib/hash'); 8 | 9 | describe('SKey', function() { 10 | 11 | var stealthkey = SKey(); 12 | stealthkey.payloadKeypair = Keypair(); 13 | stealthkey.payloadKeypair.privkey = Privkey(); 14 | stealthkey.payloadKeypair.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 1'))); 15 | stealthkey.payloadKeypair.privkey2pubkey(); 16 | stealthkey.scanKeypair = Keypair(); 17 | stealthkey.scanKeypair.privkey = Privkey(); 18 | stealthkey.scanKeypair.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 2'))); 19 | stealthkey.scanKeypair.privkey2pubkey(); 20 | 21 | var senderKeypair = Keypair(); 22 | senderKeypair.privkey = Privkey(); 23 | senderKeypair.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 3'))); 24 | senderKeypair.privkey2pubkey(); 25 | 26 | it('should create a new stealthkey', function() { 27 | var stealthkey = new SKey(); 28 | should.exist(stealthkey); 29 | }); 30 | 31 | it('should create a new stealthkey without using "new"', function() { 32 | var stealthkey = SKey(); 33 | should.exist(stealthkey); 34 | }); 35 | 36 | it('should create a new stealthkey with both keypairs in the constructor', function() { 37 | var keypair1 = Keypair(); 38 | var keypair2 = Keypair(); 39 | var stealthkey = SKey(keypair1, keypair2); 40 | should.exist(stealthkey.payloadKeypair); 41 | should.exist(stealthkey.scanKeypair); 42 | }); 43 | 44 | describe('#set', function() { 45 | 46 | it('should set payload key', function() { 47 | should.exist(SKey().set({payloadKeypair: stealthkey.payloadKeypair}).payloadKeypair); 48 | }); 49 | 50 | }); 51 | 52 | describe('#fromJSON', function() { 53 | 54 | it('should make a stealthkey from this JSON', function() { 55 | var sk = SKey().fromJSON({ 56 | payloadKeypair: stealthkey.payloadKeypair.toJSON(), 57 | scanKeypair: stealthkey.scanKeypair.toJSON() 58 | }); 59 | sk.payloadKeypair.toString().should.equal(stealthkey.payloadKeypair.toString()); 60 | sk.scanKeypair.toString().should.equal(stealthkey.scanKeypair.toString()); 61 | }); 62 | 63 | }); 64 | 65 | describe('#toJSON', function() { 66 | 67 | it('should convert this stealthkey to json', function() { 68 | var json = stealthkey.toJSON() 69 | var json2 = SKey().fromJSON(json).toJSON(); 70 | json.payloadKeypair.privkey.should.equal(json2.payloadKeypair.privkey); 71 | json.scanKeypair.privkey.should.equal(json2.scanKeypair.privkey); 72 | }); 73 | 74 | }); 75 | 76 | describe('#fromRandom', function() { 77 | 78 | it('should create a new stealthkey from random', function() { 79 | var stealthkey = SKey().fromRandom(); 80 | should.exist(stealthkey.payloadKeypair.privkey.bn.gt(0)); 81 | should.exist(stealthkey.scanKeypair.privkey.bn.gt(0)); 82 | }); 83 | 84 | }); 85 | 86 | describe('#getSharedKeypair', function() { 87 | 88 | it('should return a key', function() { 89 | var key = stealthkey.getSharedKeypair(senderKeypair.pubkey); 90 | (key instanceof Keypair).should.equal(true); 91 | }); 92 | 93 | }); 94 | 95 | describe('#getReceivePubkey', function() { 96 | 97 | it('should return a pubkey', function() { 98 | var pubkey = stealthkey.getReceivePubkey(senderKeypair.pubkey); 99 | (pubkey instanceof Pubkey).should.equal(true); 100 | }); 101 | 102 | }); 103 | 104 | describe('#getReceiveKeypair', function() { 105 | 106 | it('should return a key', function() { 107 | var key = stealthkey.getReceiveKeypair(senderKeypair.pubkey); 108 | (key instanceof Keypair).should.equal(true); 109 | }); 110 | 111 | it('should return a key with the same pubkey as getReceivePubkey', function() { 112 | var key = stealthkey.getReceiveKeypair(senderKeypair.pubkey); 113 | var pubkey = stealthkey.getReceivePubkey(senderKeypair.pubkey); 114 | key.pubkey.toString().should.equal(pubkey.toString()); 115 | }); 116 | 117 | it('should return private key with length 32 or less', function() { 118 | var key = stealthkey.getReceiveKeypair(senderKeypair.pubkey); 119 | key.privkey.bn.toBuffer().length.should.be.below(33); 120 | }); 121 | 122 | }); 123 | 124 | describe('#isForMe', function() { 125 | 126 | it('should return true if it (the transaction or message) is for me', function() { 127 | var pubkeyhash = new Buffer('3cb64fa6ee9b3e8754e3e2bd033bf61048604a99', 'hex'); 128 | stealthkey.isForMe(senderKeypair.pubkey, pubkeyhash).should.equal(true); 129 | }); 130 | 131 | it('should return false if it (the transaction or message) is not for me', function() { 132 | var pubkeyhash = new Buffer('00b64fa6ee9b3e8754e3e2bd033bf61048604a99', 'hex'); 133 | stealthkey.isForMe(senderKeypair.pubkey, pubkeyhash).should.equal(false); 134 | }); 135 | 136 | }); 137 | 138 | }); 139 | -------------------------------------------------------------------------------- /test/expmt/stealth/message.js: -------------------------------------------------------------------------------- 1 | var Keypair = require('../../../lib/keypair'); 2 | var SMessage = require('../../../lib/expmt/stealth/message'); 3 | var SKey = require('../../../lib/expmt/stealth/key'); 4 | var SAddress = require('../../../lib/expmt/stealth/address'); 5 | var KDF = require('../../../lib/kdf'); 6 | var Hash = require('../../../lib/hash'); 7 | var should = require('chai').should(); 8 | var Address = require('../../../lib/address'); 9 | 10 | describe('SMessage', function() { 11 | 12 | var payloadKeypair = KDF.buf2keypair(new Buffer('key1')); 13 | var scanKeypair = KDF.buf2keypair(new Buffer('key2')); 14 | var fromKeypair = KDF.buf2keypair(new Buffer('key3')); 15 | var enchex = 'f557994f16d0d628fa4fdb4ab3d7e0bc5f2754f20381c7831a20c7c9ec88dcf092ea3683261798ccda991ed65a3a54a036d8125dec0381c7831a20c7c9ec88dcf092ea3683261798ccda991ed65a3a54a036d8125dec9f86d081884c7d659a2feaa0c55ad01560ba2904d3bc8395b6c4a6f87648edb33db6a22170e5e26f340c7ba943169210234cd6a753ad13919b0ab7d678b47b5e7d63e452382de2c2590fb57ef048f7b3'; 16 | var encbuf = new Buffer(enchex, 'hex'); 17 | var ivbuf = Hash.sha256(new Buffer('test')).slice(0, 128 / 8); 18 | var sk = SKey().set({payloadKeypair: payloadKeypair, scanKeypair: scanKeypair}); 19 | var sa = SAddress().fromSKey(sk); 20 | var messagebuf = new Buffer('this is my message'); 21 | 22 | it('should make a new stealthmessage', function() { 23 | var sm = new SMessage(); 24 | should.exist(sm); 25 | sm = SMessage() 26 | should.exist(sm); 27 | }); 28 | 29 | it('should allow "set" style syntax', function() { 30 | var encbuf = SMessage().set({ 31 | messagebuf: messagebuf, 32 | toSAddress: sa 33 | }).encrypt().encbuf; 34 | should.exist(encbuf); 35 | encbuf.length.should.equal(113); 36 | }); 37 | 38 | describe('#set', function() { 39 | 40 | it('should set the messagebuf', function() { 41 | var sm = SMessage().set({messagebuf: messagebuf}); 42 | should.exist(sm.messagebuf); 43 | }); 44 | 45 | }); 46 | 47 | describe('@encrypt', function() { 48 | 49 | it('should encrypt a message', function() { 50 | var encbuf = SMessage.encrypt(messagebuf, sa); 51 | encbuf.length.should.equal(166); 52 | }); 53 | 54 | it('should encrypt a message with this fromKeypair and ivbuf the same each time', function() { 55 | var encbuf = SMessage.encrypt(messagebuf, sa, fromKeypair, ivbuf); 56 | encbuf.length.should.equal(166); 57 | encbuf.toString('hex').should.equal(enchex); 58 | }); 59 | 60 | }); 61 | 62 | describe('@decrypt', function() { 63 | 64 | it('should decrypt this known message correctly', function() { 65 | var messagebuf2 = SMessage.decrypt(encbuf, sk); 66 | messagebuf2.toString('hex').should.equal(messagebuf.toString('hex')); 67 | }); 68 | 69 | }); 70 | 71 | describe('@isForMe', function() { 72 | 73 | it('should know that this message is for me', function() { 74 | SMessage.isForMe(encbuf, sk).should.equal(true); 75 | }); 76 | 77 | it('should know that this message is for me even if my payloadPrivkey is not present', function() { 78 | var sk2 = new SKey(); 79 | sk2.scanKeypair = sk.scanKeypair; 80 | sk2.payloadKeypair = Keypair().set({pubkey: sk.payloadKeypair.pubkey}); 81 | should.not.exist(sk2.payloadKeypair.privkey); 82 | SMessage.isForMe(encbuf, sk2).should.equal(true); 83 | }); 84 | 85 | }); 86 | 87 | describe('#encrypt', function() { 88 | 89 | it('should encrypt this message', function() { 90 | var sm = SMessage().set({ 91 | messagebuf: messagebuf, 92 | toSAddress: sa, 93 | fromKeypair: fromKeypair 94 | }); 95 | sm.encrypt().encbuf.length.should.equal(113); 96 | }); 97 | 98 | }); 99 | 100 | describe('#decrypt', function() { 101 | 102 | it('should decrypt that which was encrypted', function() { 103 | var sm = SMessage().set({ 104 | messagebuf: messagebuf, 105 | toSAddress: sa 106 | }).encrypt(); 107 | var messagebuf2 = SMessage().set({ 108 | encbuf: sm.encbuf, 109 | fromKeypair: sm.fromKeypair, 110 | toSKey: sk 111 | }).decrypt().messagebuf; 112 | messagebuf2.toString('hex').should.equal(messagebuf.toString('hex')); 113 | }); 114 | 115 | }); 116 | 117 | describe('#isForMe', function() { 118 | 119 | it('should know that this message is for me', function() { 120 | SMessage().set({ 121 | encbuf: encbuf, 122 | toSKey: sk, 123 | fromKeypair: fromKeypair, 124 | receiveAddress: Address().set({hashbuf: encbuf.slice(0, 20)}) 125 | }).isForMe().should.equal(true); 126 | }); 127 | 128 | it('should know that this message is not for me', function() { 129 | SMessage().set({ 130 | encbuf: encbuf, 131 | toSKey: sk, 132 | fromKeypair: fromKeypair, 133 | receiveAddress: Address().set({hashbuf: encbuf.slice(0, 20)}) 134 | }).isForMe().should.equal(true); 135 | }); 136 | 137 | }); 138 | 139 | }); 140 | -------------------------------------------------------------------------------- /test/expmt/stealth/tx.js: -------------------------------------------------------------------------------- 1 | var should = require('chai').should(); 2 | var Txout = require('../../../lib/txout'); 3 | var SKey = require('../../../lib/expmt/stealth/key'); 4 | var STx = require('../../../lib/expmt/stealth/tx'); 5 | var Tx = require('../../../lib/tx'); 6 | var Varint = require('../../../lib/varint'); 7 | 8 | describe('STx', function() { 9 | 10 | var txhex = '0100000001c828ccce36eca04f96321ad488528af86c7598e67157c4f8e2f462a9e0e3af5f010000006a47304402204525eef6a56cc57fb184e53efdfdc1086d5265da21480d55c2184536440a64f70220349cdc6c66a8507dde0d172fe64aeb57ae56e014b50315f615086a6b85c5424e012102c0633ddb6bf2a8686e2ba4ce8026c94e1e27ef12e73f8fed6d6d2b97199f9b74ffffffff020000000000000000286a2606deadbeef0365b5a5b0ba059666e907b0b5e07b37fdb162d1399ed829315491fe1f30c87b3f905f0100000000001976a9142042d5e7ef9e82346419fbfe7df5ae52fe4bea3c88ac00000000'; 11 | var txbuf = new Buffer(txhex, 'hex'); 12 | var txidhex = '66da969fff214c329e27062beaf3baf20ed035801559b31f3e868c2de4cdfc5b'; 13 | var tx = Tx(txbuf); 14 | 15 | it('should make a new STx', function() { 16 | var stx = new STx(); 17 | should.exist(stx); 18 | stx = STx(); 19 | should.exist(stx); 20 | }); 21 | 22 | describe('#isForMe', function() { 23 | 24 | it('should return false for this known tx and random stealthkey', function() { 25 | var sk = SKey().fromRandom(); 26 | var stx = STx().set({sk: sk, tx: tx}); 27 | stx.isForMe().should.equal(false); 28 | }); 29 | 30 | }); 31 | 32 | describe('#notMine', function() { 33 | 34 | it('should return true for this known tx and random stealthkey', function() { 35 | var sk = SKey().fromRandom(); 36 | var stx = STx().set({sk: sk, tx: tx}); 37 | stx.notMine().should.equal("STx not mine"); 38 | }); 39 | 40 | }); 41 | 42 | describe('#notStealth', function() { 43 | 44 | it('should know this is a stealth tx', function() { 45 | var stx = STx().set({tx: tx}); 46 | stx.notStealth().should.equal(false); 47 | }); 48 | 49 | it('should know this is not a stealth tx', function() { 50 | var tx2 = Tx(tx); 51 | tx2.txouts.pop(); 52 | tx2.txoutsvi = Varint(1); 53 | var stx = STx().set({tx: tx2}); 54 | stx.notStealth().should.equal("Not enough txouts"); 55 | }); 56 | 57 | }); 58 | 59 | describe('@parseOpReturnData', function() { 60 | var txout = tx.txouts[0]; 61 | var buf = txout.script.chunks[1].buf; 62 | var parsed = STx.parseOpReturnData(buf); 63 | (typeof parsed.version).should.equal('number'); 64 | parsed.noncebuf.length.should.be.above(0); 65 | parsed.pubkey.toBuffer().length.should.equal(33); 66 | }); 67 | 68 | }); 69 | -------------------------------------------------------------------------------- /test/hash.js: -------------------------------------------------------------------------------- 1 | var should = require('chai').should(); 2 | var Hash = require('../lib/hash'); 3 | 4 | describe('Hash', function() { 5 | var buf = new Buffer([0, 1, 2, 3, 253, 254, 255]); 6 | var str = "test string"; 7 | 8 | describe('@sha1', function() { 9 | 10 | it('should calculate the hash of this buffer correctly', function() { 11 | var hash = Hash.sha1(buf); 12 | hash.toString('hex').should.equal('de69b8a4a5604d0486e6420db81e39eb464a17b2'); 13 | hash = Hash.sha1(new Buffer(0)); 14 | hash.toString('hex').should.equal('da39a3ee5e6b4b0d3255bfef95601890afd80709'); 15 | }); 16 | 17 | it('should throw an error when the input is not a buffer', function() { 18 | (function() { 19 | Hash.sha1(str); 20 | }).should.throw('sha1 hash must be of a buffer'); 21 | }); 22 | 23 | }); 24 | 25 | describe("@sha1hmac", function() { 26 | 27 | // http://tools.ietf.org/html/rfc2202.html 28 | 29 | it('should calculate this known empty test vector correctly', function() { 30 | var hex = 'b617318655057264e28bc0b6fb378c8ef146be00'; 31 | Hash.sha1hmac(new Buffer("Hi There"), new Buffer('0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b', 'hex')).toString('hex').should.equal(hex); 32 | }); 33 | 34 | }); 35 | 36 | describe('@sha256', function() { 37 | 38 | it('should calculate the hash of this buffer correctly', function() { 39 | var hash = Hash.sha256(buf); 40 | hash.toString('hex').should.equal('6f2c7b22fd1626998287b3636089087961091de80311b9279c4033ec678a83e8'); 41 | }); 42 | 43 | it('should throw an error when the input is not a buffer', function() { 44 | (function() { 45 | Hash.sha256(str); 46 | }).should.throw('sha256 hash must be of a buffer'); 47 | }); 48 | 49 | }); 50 | 51 | describe('@sha256hmac', function() { 52 | 53 | it('should compute this known empty test vector correctly', function() { 54 | var key = new Buffer(''); 55 | var data = new Buffer(''); 56 | Hash.sha256hmac(data, key).toString('hex').should.equal('b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad'); 57 | }); 58 | 59 | it('should compute this known non-empty test vector correctly', function() { 60 | var key = new Buffer('key'); 61 | var data = new Buffer('The quick brown fox jumps over the lazy dog'); 62 | Hash.sha256hmac(data, key).toString('hex').should.equal('f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8'); 63 | }); 64 | 65 | }); 66 | 67 | describe('@sha256sha256', function() { 68 | 69 | it('should calculate the hash of this buffer correctly', function() { 70 | var hash = Hash.sha256sha256(buf); 71 | hash.toString('hex').should.equal('be586c8b20dee549bdd66018c7a79e2b67bb88b7c7d428fa4c970976d2bec5ba'); 72 | }); 73 | 74 | it('should throw an error when the input is not a buffer', function() { 75 | (function() { 76 | Hash.sha256sha256(str); 77 | }).should.throw('sha256sha256 hash must be of a buffer'); 78 | }); 79 | 80 | }); 81 | 82 | describe('@sha256ripemd160', function() { 83 | 84 | it('should calculate the hash of this buffer correctly', function() { 85 | var hash = Hash.sha256ripemd160(buf); 86 | hash.toString('hex').should.equal('7322e2bd8535e476c092934e16a6169ca9b707ec'); 87 | }); 88 | 89 | it('should throw an error when the input is not a buffer', function() { 90 | (function() { 91 | Hash.sha256ripemd160(str); 92 | }).should.throw('sha256ripemd160 hash must be of a buffer'); 93 | }); 94 | 95 | }); 96 | 97 | describe('@ripemd160', function() { 98 | 99 | it('should calculate the hash of this buffer correctly', function() { 100 | var hash = Hash.ripemd160(buf); 101 | hash.toString('hex').should.equal('fa0f4565ff776fee0034c713cbf48b5ec06b7f5c'); 102 | }); 103 | 104 | it('should throw an error when the input is not a buffer', function() { 105 | (function() { 106 | Hash.ripemd160(str); 107 | }).should.throw('ripemd160 hash must be of a buffer'); 108 | }); 109 | 110 | }); 111 | 112 | describe('@sha512', function() { 113 | 114 | it('should calculate the hash of this buffer correctly', function() { 115 | var hash = Hash.sha512(buf); 116 | hash.toString('hex').should.equal('c0530aa32048f4904ae162bc14b9eb535eab6c465e960130005feddb71613e7d62aea75f7d3333ba06e805fc8e45681454524e3f8050969fe5a5f7f2392e31d0'); 117 | }); 118 | 119 | it('should throw an error when the input is not a buffer', function() { 120 | (function() { 121 | Hash.sha512(str); 122 | }).should.throw('sha512 hash must be of a buffer'); 123 | }); 124 | 125 | }); 126 | 127 | describe("@sha512hmac", function() { 128 | 129 | it('should calculate this known empty test vector correctly', function() { 130 | var hex = 'b936cee86c9f87aa5d3c6f2e84cb5a4239a5fe50480a6ec66b70ab5b1f4ac6730c6c515421b327ec1d69402e53dfb49ad7381eb067b338fd7b0cb22247225d47'; 131 | Hash.sha512hmac(new Buffer([]), new Buffer([])).toString('hex').should.equal(hex); 132 | }); 133 | 134 | it('should calculate this known non-empty test vector correctly', function() { 135 | var hex = 'c40bd7c15aa493b309c940e08a73ffbd28b2e4cb729eb94480d727e4df577b13cc403a78e6150d83595f3b17c4cc331f12ca5952691de3735a63c1d4c69a2bac'; 136 | var data = new Buffer("test1"); 137 | var key = new Buffer("test2"); 138 | Hash.sha512hmac(data, key).toString('hex').should.equal(hex); 139 | }); 140 | 141 | }); 142 | 143 | }); 144 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Mocha 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /test/kdf.js: -------------------------------------------------------------------------------- 1 | var should = require('chai').should(); 2 | var KDF = require('../lib/kdf'); 3 | var Hash = require('../lib/hash'); 4 | var vectors = require('./vectors/kdf'); 5 | 6 | describe('KDF', function() { 7 | 8 | describe('@PBKDF2', function() { 9 | 10 | it('should return values of the right size', function() { 11 | var passbuf = new Buffer([0]); 12 | var saltbuf = new Buffer([0]); 13 | var key1 = KDF.PBKDF2(passbuf, saltbuf); 14 | key1.length.should.equal(512 / 8); 15 | var key2 = KDF.PBKDF2(passbuf, saltbuf, 2); 16 | key2.length.should.equal(512 / 8); 17 | key1.toString('hex').should.not.equal(key2.toString('hex')); 18 | var key3 = KDF.PBKDF2(passbuf, saltbuf, 2, 1024); 19 | key3.length.should.equal(1024 / 8); 20 | var key4 = KDF.PBKDF2(passbuf, saltbuf, 2, 256, 'sha256'); 21 | key4.length.should.equal(256 / 8); 22 | }); 23 | 24 | // Test vectors from: http://tools.ietf.org/html/rfc6070#section-2 25 | vectors.PBKDF2.valid.forEach(function(obj, i) { 26 | it('should work for PBKDF2 test vector ' + i, function() { 27 | var passbuf = new Buffer(obj.p, 'hex'); 28 | var saltbuf = new Buffer(obj.s, 'hex'); 29 | var niterations = obj.c; 30 | var keylenbits = obj.dkLen * 8; 31 | var hmacf = obj.hmacf; 32 | var key = KDF.PBKDF2(passbuf, saltbuf, niterations, keylenbits, hmacf); 33 | key.toString('hex').should.equal(obj.key); 34 | }); 35 | }); 36 | 37 | }); 38 | 39 | describe('@buf2keypair', function() { 40 | 41 | it('should compute these known values', function() { 42 | var buf = Hash.sha256(new Buffer('test')); 43 | var keypair = KDF.buf2keypair(buf); 44 | keypair.privkey.toString().should.equal('KxxVszVMFLGzmxpxR7sMSaWDmqMKLVhKebX5vZbGHyuR8spreQ7V'); 45 | keypair.pubkey.toString().should.equal('03774f761ae89a0d2fda0d532bad62286ae8fcda9bc38c060036296085592a97c1'); 46 | }); 47 | 48 | }); 49 | 50 | describe('@sha256hmac2keypair', function() { 51 | 52 | it('should compute these known values', function() { 53 | var buf = Hash.sha256(new Buffer('test')); 54 | var keypair = KDF.sha256hmac2keypair(buf); 55 | keypair.privkey.toString().should.equal('KxxVszVMFLGzmxpxR7sMSaWDmqMKLVhKebX5vZbGHyuR8spreQ7V'); 56 | keypair.pubkey.toString().should.equal('03774f761ae89a0d2fda0d532bad62286ae8fcda9bc38c060036296085592a97c1'); 57 | }); 58 | 59 | }); 60 | 61 | describe('@sha256hmac2privkey', function() { 62 | 63 | it('should compute this known privkey', function() { 64 | var buf = Hash.sha256(new Buffer('test')); 65 | var privkey = KDF.sha256hmac2privkey(buf); 66 | privkey.toString().should.equal('KxxVszVMFLGzmxpxR7sMSaWDmqMKLVhKebX5vZbGHyuR8spreQ7V'); 67 | }); 68 | 69 | }); 70 | 71 | }); 72 | -------------------------------------------------------------------------------- /test/keypair.js: -------------------------------------------------------------------------------- 1 | var should = require('chai').should(); 2 | var bn = require('../lib/bn'); 3 | var point = require('../lib/point'); 4 | var Privkey = require('../lib/privkey'); 5 | var Pubkey = require('../lib/pubkey'); 6 | var Keypair = require('../lib/keypair'); 7 | 8 | describe('Keypair', function() { 9 | 10 | it('should make a blank key', function() { 11 | var key = new Keypair(); 12 | should.exist(key); 13 | }); 14 | 15 | it('should make a key with a priv and pub', function() { 16 | var priv = new Privkey(); 17 | var pub = new Pubkey(); 18 | var key = new Keypair({privkey: priv, pubkey: pub}); 19 | should.exist(key); 20 | should.exist(key.privkey); 21 | should.exist(key.pubkey); 22 | }); 23 | 24 | describe("#set", function() { 25 | 26 | it('should make a new priv and pub', function() { 27 | should.exist(Keypair().set({privkey: Privkey()}).privkey); 28 | }); 29 | 30 | }); 31 | 32 | describe('#fromJSON', function() { 33 | 34 | it('should make a keypair from this json', function() { 35 | var privkey = Privkey().fromRandom(); 36 | var pubkey = Pubkey().fromPrivkey(privkey); 37 | var keypair = Keypair().fromJSON({ 38 | privkey: privkey.toJSON(), 39 | pubkey: pubkey.toJSON() 40 | }) 41 | keypair.privkey.toString().should.equal(privkey.toString()); 42 | keypair.pubkey.toString().should.equal(pubkey.toString()); 43 | }); 44 | 45 | }); 46 | 47 | describe('#toJSON', function() { 48 | 49 | it('should make json from this keypair', function() { 50 | var json = Keypair().fromRandom().toJSON(); 51 | should.exist(json.privkey); 52 | should.exist(json.pubkey); 53 | var keypair = Keypair().fromJSON(json); 54 | keypair.toJSON().privkey.toString().should.equal(json.privkey.toString()); 55 | keypair.toJSON().pubkey.toString().should.equal(json.pubkey.toString()); 56 | }); 57 | 58 | }); 59 | 60 | describe("#fromPrivkey", function() { 61 | 62 | it('should make a new key from a privkey', function() { 63 | should.exist(Keypair().fromPrivkey(Privkey().fromRandom()).pubkey); 64 | }); 65 | 66 | }); 67 | 68 | describe("#fromRandom", function() { 69 | 70 | it('should make a new priv and pub, should be compressed, mainnet', function() { 71 | var key = new Keypair(); 72 | key.fromRandom(); 73 | should.exist(key.privkey); 74 | should.exist(key.pubkey); 75 | key.privkey.bn.gt(bn(0)).should.equal(true); 76 | key.pubkey.point.getX().gt(bn(0)).should.equal(true); 77 | key.pubkey.point.getY().gt(bn(0)).should.equal(true); 78 | key.privkey.compressed.should.equal(true); 79 | key.privkey.networkstr.should.equal('mainnet'); 80 | key.pubkey.compressed.should.equal(true); 81 | }); 82 | 83 | }); 84 | 85 | describe("#fromString", function() { 86 | 87 | it('should recover a key creating with toString', function() { 88 | var key = new Keypair(); 89 | key.fromRandom(); 90 | var priv = key.privkey; 91 | var pub = key.pubkey; 92 | var str = key.toString(); 93 | key.fromString(str); 94 | should.exist(key.privkey); 95 | should.exist(key.pubkey); 96 | key.privkey.toString().should.equal(priv.toString()); 97 | key.pubkey.toString().should.equal(pub.toString()); 98 | }); 99 | 100 | it('should work with only Privkey set', function() { 101 | var key = new Keypair(); 102 | key.fromRandom(); 103 | key.pubkey = undefined; 104 | var priv = key.privkey; 105 | var str = key.toString(); 106 | key.fromString(str); 107 | should.exist(key.privkey); 108 | key.privkey.toString().should.equal(priv.toString()); 109 | }); 110 | 111 | it('should work with only Pubkey set', function() { 112 | var key = new Keypair(); 113 | key.fromRandom(); 114 | key.privkey = undefined; 115 | var pub = key.pubkey; 116 | var str = key.toString(); 117 | key.fromString(str); 118 | should.exist(key.pubkey); 119 | key.pubkey.toString().should.equal(pub.toString()); 120 | }); 121 | 122 | }); 123 | 124 | describe("#privkey2pubkey", function() { 125 | 126 | it('should convert this known Privkey to known Pubkey', function() { 127 | var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; 128 | var pubhex = '02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc'; 129 | var key = new Keypair(); 130 | key.privkey = new Privkey({bn: bn(new Buffer(privhex, 'hex'))}); 131 | key.privkey2pubkey(); 132 | key.pubkey.toString().should.equal(pubhex); 133 | }); 134 | 135 | it('should convert this known Privkey to known Pubkey and preserve compressed=true', function() { 136 | var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; 137 | var key = new Keypair(); 138 | key.privkey = new Privkey({bn: bn(new Buffer(privhex, 'hex'))}); 139 | key.privkey.compressed = true; 140 | key.privkey2pubkey(); 141 | key.pubkey.compressed.should.equal(true); 142 | }); 143 | 144 | it('should convert this known Privkey to known Pubkey and preserve compressed=true', function() { 145 | var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; 146 | var key = new Keypair(); 147 | key.privkey = new Privkey({bn: bn(new Buffer(privhex, 'hex'))}); 148 | key.privkey.compressed = false; 149 | key.privkey2pubkey(); 150 | key.pubkey.compressed.should.equal(false); 151 | }); 152 | 153 | }); 154 | 155 | describe("#toString", function() { 156 | 157 | it('should exist', function() { 158 | var key = new Keypair(); 159 | key.fromRandom(); 160 | should.exist(key.toString()); 161 | }); 162 | 163 | }); 164 | 165 | }); 166 | -------------------------------------------------------------------------------- /test/opcode.js: -------------------------------------------------------------------------------- 1 | var should = require('chai').should(); 2 | var Opcode = require('../lib/opcode'); 3 | 4 | describe('Opcode', function() { 5 | 6 | it('should create a new Opcode', function() { 7 | var opcode = new Opcode(5); 8 | }); 9 | 10 | it('should have 119 opcodes', function() { 11 | var i = 0; 12 | for (var key in Opcode) { 13 | if (key.indexOf('OP_') !== -1) 14 | i++; 15 | } 16 | i.should.equal(119); 17 | }); 18 | 19 | it('should convert to a string with this handy syntax', function() { 20 | Opcode(0).toString().should.equal('OP_0'); 21 | Opcode(96).toString().should.equal('OP_16'); 22 | Opcode(97).toString().should.equal('OP_NOP'); 23 | }); 24 | 25 | it('should convert to a number with this handy syntax', function() { 26 | Opcode('OP_0').toNumber().should.equal(0); 27 | Opcode('OP_16').toNumber().should.equal(96); 28 | Opcode('OP_NOP').toNumber().should.equal(97); 29 | }); 30 | 31 | describe('#fromNumber', function() { 32 | 33 | it('should work for 0', function() { 34 | Opcode().fromNumber(0).num.should.equal(0); 35 | }); 36 | 37 | }); 38 | 39 | describe('#toNumber', function() { 40 | 41 | it('should work for 0', function() { 42 | Opcode().fromNumber(0).toNumber().should.equal(0); 43 | }); 44 | 45 | }); 46 | 47 | describe('#fromString', function() { 48 | 49 | it('should work for OP_0', function() { 50 | Opcode().fromString('OP_0').num.should.equal(0); 51 | }); 52 | 53 | }); 54 | 55 | describe('#toString', function() { 56 | 57 | it('should work for OP_0', function() { 58 | Opcode().fromString('OP_0').toString().should.equal('OP_0'); 59 | }); 60 | 61 | }); 62 | 63 | describe('@str', function() { 64 | 65 | it('should exist and have op 185', function() { 66 | should.exist(Opcode.str); 67 | Opcode.str[185].should.equal('OP_NOP10'); 68 | }); 69 | 70 | }); 71 | 72 | }); 73 | -------------------------------------------------------------------------------- /test/privkey.js: -------------------------------------------------------------------------------- 1 | var Privkey = require('../lib/privkey'); 2 | var base58check = require('../lib/base58check'); 3 | var BN = require('../lib/bn'); 4 | var Point = require('../lib/point'); 5 | var should = require('chai').should(); 6 | 7 | describe('Privkey', function() { 8 | var hex = '96c132224121b509b7d0a16245e957d9192609c5637c6228311287b1be21627a'; 9 | var buf = new Buffer(hex, 'hex'); 10 | var enctestnet = 'cSdkPxkAjA4HDr5VHgsebAPDEh9Gyub4HK8UJr2DFGGqKKy4K5sG'; 11 | var enctu = '92jJzK4tbURm1C7udQXxeCBvXHoHJstDXRxAMouPG1k1XUaXdsu'; 12 | var encmainnet = 'L2Gkw3kKJ6N24QcDuH4XDqt9cTqsKTVNDGz1CRZhk9cq4auDUbJy'; 13 | var encmu = '5JxgQaFM1FMd38cd14e3mbdxsdSa9iM2BV6DHBYsvGzxkTNQ7Un'; 14 | 15 | it('should create an empty private key', function() { 16 | var privkey = new Privkey(); 17 | should.exist(privkey); 18 | }); 19 | 20 | it('should create a 0 private key with this convenience method', function() { 21 | var bn = BN(0); 22 | var privkey = new Privkey(bn); 23 | privkey.bn.toString().should.equal(bn.toString()); 24 | }); 25 | 26 | it('should create a mainnet private key', function() { 27 | var privkey = new Privkey({bn: BN.fromBuffer(buf), networkstr: 'mainnet', compressed: true}); 28 | privkey.toString().should.equal(encmainnet); 29 | }); 30 | 31 | it('should create an uncompressed testnet private key', function() { 32 | var privkey = new Privkey({bn: BN.fromBuffer(buf), networkstr: 'testnet', compressed: false}); 33 | privkey.toString().should.equal(enctu); 34 | }); 35 | 36 | it('should create an uncompressed mainnet private key', function() { 37 | var privkey = new Privkey({bn: BN.fromBuffer(buf), networkstr: 'mainnet', compressed: false}); 38 | privkey.toString().should.equal(encmu); 39 | }); 40 | 41 | describe('#set', function() { 42 | 43 | it('should set bn', function() { 44 | should.exist(Privkey().set({bn: BN.fromBuffer(buf)}).bn); 45 | }); 46 | 47 | }); 48 | 49 | describe('#fromJSON', function() { 50 | 51 | it('should input this address correctly', function() { 52 | var privkey = new Privkey(); 53 | privkey.fromJSON(encmu); 54 | privkey.toWIF().should.equal(encmu); 55 | }); 56 | 57 | }); 58 | 59 | describe('#toString', function() { 60 | 61 | it('should output this address correctly', function() { 62 | var privkey = new Privkey(); 63 | privkey.fromJSON(encmu); 64 | privkey.toJSON().should.equal(encmu); 65 | }); 66 | 67 | }); 68 | 69 | describe('#fromRandom', function() { 70 | 71 | it('should set bn gt 0 and lt n, and should be compressed', function() { 72 | var privkey = Privkey().fromRandom(); 73 | privkey.bn.gt(BN(0)).should.equal(true); 74 | privkey.bn.lt(Point.getN()).should.equal(true); 75 | privkey.compressed.should.equal(true); 76 | }); 77 | 78 | }); 79 | 80 | describe('#fromBN', function() { 81 | 82 | it('should create a privkey from a bignum', function() { 83 | var privkey = Privkey().fromBN(BN(5)); 84 | privkey.bn.toString().should.equal('5'); 85 | }); 86 | 87 | }); 88 | 89 | describe('#fromWIF', function() { 90 | 91 | it('should parse this compressed testnet address correctly', function() { 92 | var privkey = new Privkey(); 93 | privkey.fromWIF(encmainnet); 94 | privkey.toWIF().should.equal(encmainnet); 95 | }); 96 | 97 | }); 98 | 99 | describe('#toWIF', function() { 100 | 101 | it('should parse this compressed testnet address correctly', function() { 102 | var privkey = new Privkey(); 103 | privkey.fromWIF(enctestnet); 104 | privkey.toWIF().should.equal(enctestnet); 105 | }); 106 | 107 | }); 108 | 109 | describe('#fromString', function() { 110 | 111 | it('should parse this uncompressed testnet address correctly', function() { 112 | var privkey = new Privkey(); 113 | privkey.fromString(enctu); 114 | privkey.toWIF().should.equal(enctu); 115 | }); 116 | 117 | }); 118 | 119 | describe('#toString', function() { 120 | 121 | it('should parse this uncompressed mainnet address correctly', function() { 122 | var privkey = new Privkey(); 123 | privkey.fromString(encmu); 124 | privkey.toString().should.equal(encmu); 125 | }); 126 | 127 | }); 128 | 129 | }); 130 | -------------------------------------------------------------------------------- /test/random.js: -------------------------------------------------------------------------------- 1 | var should = require('chai').should(); 2 | var Random = require('../lib/random'); 3 | 4 | describe('Random', function() { 5 | 6 | describe('@getRandomBuffer', function() { 7 | 8 | it('should return a buffer', function() { 9 | var bytes = Random.getRandomBuffer(8); 10 | bytes.length.should.equal(8); 11 | Buffer.isBuffer(bytes).should.equal(true); 12 | }); 13 | 14 | it('should not equate two 256 bit random buffers', function() { 15 | var bytes1 = Random.getRandomBuffer(32); 16 | var bytes2 = Random.getRandomBuffer(32); 17 | bytes1.toString('hex').should.not.equal(bytes2.toString('hex')); 18 | }); 19 | 20 | it('should generate 100 8 byte buffers in a row that are not equal', function() { 21 | var hexs = []; 22 | for (var i = 0; i < 100; i++) 23 | hexs[i] = Random.getRandomBuffer(8).toString('hex'); 24 | for (var i = 0; i < 100; i++) 25 | for (var j = i + 1; j < 100; j++) 26 | hexs[i].should.not.equal(hexs[j]); 27 | }); 28 | 29 | }); 30 | 31 | }); 32 | -------------------------------------------------------------------------------- /test/txbuilder.js: -------------------------------------------------------------------------------- 1 | var should = require('chai').should(); 2 | var Txbuilder = require('../lib/txbuilder'); 3 | var Tx = require('../lib/tx'); 4 | var Txout = require('../lib/txout'); 5 | var Address = require('../lib/address'); 6 | var BN = require('../lib/bn'); 7 | var Interp = require('../lib/interp'); 8 | var BufR = require('../lib/bufr'); 9 | var Script = require('../lib/script'); 10 | var Pubkey = require('../lib/pubkey'); 11 | var Privkey = require('../lib/privkey'); 12 | var tx_valid = require('./vectors/bitcoind/tx_valid'); 13 | var tx_invalid = require('./vectors/bitcoind/tx_invalid'); 14 | 15 | describe('Txbuilder', function() { 16 | 17 | it('should make a new txbuilder', function() { 18 | var txb = new Txbuilder(); 19 | (txb instanceof Txbuilder).should.equal(true); 20 | should.exist(txb.tx); 21 | txb = Txbuilder(); 22 | (txb instanceof Txbuilder).should.equal(true); 23 | should.exist(txb.tx); 24 | txb = Txbuilder({ 25 | tx: Tx() 26 | }); 27 | should.exist(txb.tx); 28 | }); 29 | 30 | describe('#addToAddress', function() { 31 | 32 | it('should add a scripthash address', function() { 33 | var hashbuf = new Buffer(20); 34 | hashbuf.fill(0); 35 | var address = Address().fromScript(Script().fromScripthash(hashbuf)); 36 | var txb = Txbuilder(); 37 | txb.addToAddress(BN(0), address); 38 | txb.tx.txouts.length.should.equal(1); 39 | }); 40 | 41 | it('should add a pubkeyhash address', function() { 42 | var pubkey = Pubkey().fromPrivkey(Privkey().fromRandom()); 43 | var address = Address().fromPubkey(pubkey); 44 | var txb = Txbuilder(); 45 | txb.addToAddress(BN(0), address); 46 | txb.tx.txouts.length.should.equal(1); 47 | }); 48 | 49 | }); 50 | 51 | describe('vectors', function() { 52 | 53 | var c = 0; 54 | tx_valid.forEach(function(vector, i) { 55 | if (vector.length === 1) 56 | return; 57 | c++; 58 | it('should verify tx_valid vector ' + c, function() { 59 | var inputs = vector[0]; 60 | var txhex = vector[1]; 61 | var flags = Interp.getFlags(vector[2]); 62 | 63 | var map = {}; 64 | inputs.forEach(function(input) { 65 | var txoutnum = input[1]; 66 | if (txoutnum === -1) 67 | txoutnum = 0xffffffff; //bitcoind casts -1 to an unsigned int 68 | map[input[0] + ":" + txoutnum] = Txout().setScript(Script().fromBitcoindString(input[2])); 69 | }); 70 | 71 | var tx = Tx().fromBuffer(new Buffer(txhex, 'hex')); 72 | var txb = Txbuilder().set({ 73 | tx: tx, 74 | prevtxoutsmap: map 75 | }); 76 | 77 | txb.verifytx(flags).should.equal(true); 78 | }); 79 | }); 80 | 81 | var c = 0; 82 | tx_invalid.forEach(function(vector, i) { 83 | if (vector.length === 1) 84 | return; 85 | c++; 86 | it('should unverify tx_invalid vector ' + c, function() { 87 | var inputs = vector[0]; 88 | var txhex = vector[1]; 89 | var flags = Interp.getFlags(vector[2]); 90 | 91 | var map = {}; 92 | inputs.forEach(function(input) { 93 | var txoutnum = input[1]; 94 | if (txoutnum === -1) 95 | txoutnum = 0xffffffff; //bitcoind casts -1 to an unsigned int 96 | map[input[0] + ":" + txoutnum] = Txout().setScript(Script().fromBitcoindString(input[2])); 97 | }); 98 | 99 | var tx = Tx().fromBuffer(new Buffer(txhex, 'hex')); 100 | var txb = Txbuilder().set({ 101 | tx: tx, 102 | prevtxoutsmap: map 103 | }); 104 | 105 | txb.verifytx(flags).should.equal(false); 106 | }); 107 | }); 108 | 109 | }); 110 | 111 | }); 112 | -------------------------------------------------------------------------------- /test/txin.js: -------------------------------------------------------------------------------- 1 | var should = require('chai').should(); 2 | var Script = require('../lib/script'); 3 | var Txin = require('../lib/txin'); 4 | var Varint = require('../lib/varint'); 5 | var BufR = require('../lib/bufr'); 6 | 7 | describe('Txin', function() { 8 | 9 | var txhashbuf = new Buffer(32); 10 | txhashbuf.fill(0); 11 | var txoutnum = 0; 12 | var script = Script().fromString("OP_CHECKMULTISIG"); 13 | var scriptvi = Varint(script.toBuffer().length); 14 | var seqnum = 0; 15 | var txin = Txin().set({ 16 | txhashbuf: txhashbuf, 17 | txoutnum: txoutnum, 18 | scriptvi: scriptvi, 19 | script: script, 20 | seqnum: seqnum 21 | }); 22 | 23 | it('should make a new txin', function() { 24 | var txin = new Txin(); 25 | should.exist(txin); 26 | txin = Txin(); 27 | should.exist(txin); 28 | var txhashbuf = new Buffer(32); 29 | txhashbuf.fill(0); 30 | Txin(txhashbuf, 0).txhashbuf.length.should.equal(32); 31 | (function() { 32 | var txhashbuf2 = new Buffer(33); 33 | txhashbuf2.fill(0); 34 | Txin(txhashbuf2, 0); 35 | }).should.throw('txhashbuf must be 32 bytes'); 36 | }); 37 | 38 | it('should calculate scriptvi correctly when creating a new txin', function() { 39 | Txin(txin.txhashbuf, txin.txoutnum, txin.script, txin.seqnum).scriptvi.toNumber().should.equal(1); 40 | }); 41 | 42 | describe('#set', function() { 43 | 44 | it('should set these vars', function() { 45 | var txin = Txin().set({ 46 | txhashbuf: txhashbuf, 47 | txoutnum: txoutnum, 48 | scriptvi: scriptvi, 49 | script: script, 50 | seqnum: seqnum 51 | }); 52 | should.exist(txin.txhashbuf); 53 | should.exist(txin.txoutnum); 54 | should.exist(txin.scriptvi); 55 | should.exist(txin.script); 56 | should.exist(txin.seqnum); 57 | }); 58 | 59 | }); 60 | 61 | describe('#setScript', function() { 62 | 63 | it('should calculate the varint size correctly', function() { 64 | var txin2 = Txin(txin); 65 | txin2.setScript(Script('OP_RETURN OP_RETURN OP_RETURN')).scriptvi.toNumber().should.equal(3); 66 | }); 67 | 68 | }); 69 | 70 | describe('#fromJSON', function() { 71 | 72 | it('should set these vars', function() { 73 | var txin2 = Txin().fromJSON(txin.toJSON()); 74 | should.exist(txin2.txhashbuf); 75 | should.exist(txin2.txoutnum); 76 | should.exist(txin2.scriptvi); 77 | should.exist(txin2.script); 78 | should.exist(txin2.seqnum); 79 | }); 80 | 81 | }); 82 | 83 | describe('#toJSON', function() { 84 | 85 | it('should set these vars', function() { 86 | var json = txin.toJSON() 87 | should.exist(json.txhashbuf); 88 | should.exist(json.txoutnum); 89 | should.exist(json.scriptvi); 90 | should.exist(json.script); 91 | should.exist(json.seqnum); 92 | }); 93 | 94 | }); 95 | 96 | describe('#fromBuffer', function() { 97 | 98 | it('should convert this known buffer', function() { 99 | var hex = '00000000000000000000000000000000000000000000000000000000000000000000000001ae00000000'; 100 | var buf = new Buffer(hex, 'hex'); 101 | var txin = Txin().fromBuffer(buf); 102 | txin.scriptvi.toNumber().should.equal(1); 103 | txin.script.toString().should.equal('OP_CHECKMULTISIG'); 104 | }); 105 | 106 | }); 107 | 108 | describe('#fromBufR', function() { 109 | 110 | it('should convert this known buffer', function() { 111 | var hex = '00000000000000000000000000000000000000000000000000000000000000000000000001ae00000000'; 112 | var buf = new Buffer(hex, 'hex'); 113 | var br = BufR(buf); 114 | var txin = Txin().fromBufR(br); 115 | txin.scriptvi.toNumber().should.equal(1); 116 | txin.script.toString().should.equal('OP_CHECKMULTISIG'); 117 | }); 118 | 119 | }); 120 | 121 | describe('#toBuffer', function() { 122 | 123 | it('should convert this known buffer', function() { 124 | txin.toBuffer().toString('hex').should.equal('00000000000000000000000000000000000000000000000000000000000000000000000001ae00000000'); 125 | }); 126 | 127 | }); 128 | 129 | describe('#toBufW', function() { 130 | 131 | it('should convert this known buffer', function() { 132 | txin.toBufW().concat().toString('hex').should.equal('00000000000000000000000000000000000000000000000000000000000000000000000001ae00000000'); 133 | }); 134 | 135 | }); 136 | 137 | }); 138 | -------------------------------------------------------------------------------- /test/txout.js: -------------------------------------------------------------------------------- 1 | var should = require('chai').should(); 2 | var BN = require('../lib/bn'); 3 | var Txout = require('../lib/txout'); 4 | var Script = require('../lib/script'); 5 | var Varint = require('../lib/varint'); 6 | var BufR = require('../lib/bufr'); 7 | var BufW = require('../lib/bufw'); 8 | 9 | describe('Txout', function() { 10 | 11 | var valuebn = BN(5); 12 | var script = Script().fromString("OP_CHECKMULTISIG"); 13 | var scriptvi = Varint(script.toBuffer().length); 14 | var txout = new Txout().set({ 15 | valuebn: valuebn, 16 | scriptvi: scriptvi, 17 | script: script 18 | }); 19 | 20 | it('should make a new txout', function() { 21 | var txout = new Txout(); 22 | should.exist(txout); 23 | txout = Txout(); 24 | should.exist(txout); 25 | Txout(valuebn, scriptvi, script).valuebn.toString().should.equal('5'); 26 | }); 27 | 28 | it('should calculate scriptvi correctly when making a new txout', function() { 29 | Txout(valuebn, script).scriptvi.toNumber().should.equal(1); 30 | }); 31 | 32 | describe('#set', function() { 33 | 34 | it('should set this object', function() { 35 | var txout = new Txout().set({ 36 | valuebn: valuebn, 37 | scriptvi: scriptvi, 38 | script: script 39 | }); 40 | should.exist(txout.valuebn); 41 | should.exist(txout.scriptvi); 42 | should.exist(txout.script); 43 | }); 44 | 45 | }); 46 | 47 | describe('#setScript', function() { 48 | 49 | it('should set the script size correctly', function() { 50 | var txout2 = Txout(txout); 51 | txout2.setScript(Script('OP_RETURN OP_RETURN OP_RETURN')).scriptvi.toNumber().should.equal(3); 52 | }); 53 | 54 | }); 55 | 56 | describe('#fromJSON', function() { 57 | 58 | it('should set from this json', function() { 59 | var txout = Txout().fromJSON({ 60 | valuebn: valuebn.toJSON(), 61 | scriptvi: scriptvi.toJSON(), 62 | script: script.toJSON() 63 | }); 64 | should.exist(txout.valuebn); 65 | should.exist(txout.scriptvi); 66 | should.exist(txout.script); 67 | }); 68 | 69 | }); 70 | 71 | describe('#toJSON', function() { 72 | 73 | it('should return this json', function() { 74 | var txout = Txout().fromJSON({ 75 | valuebn: valuebn.toJSON(), 76 | scriptvi: scriptvi.toJSON(), 77 | script: script.toJSON() 78 | }); 79 | var json = txout.toJSON(); 80 | should.exist(json.valuebn); 81 | should.exist(json.scriptvi); 82 | should.exist(json.script); 83 | }); 84 | 85 | }); 86 | 87 | describe('#fromBuffer', function() { 88 | 89 | it('should make this txin from this known buffer', function() { 90 | var txout = Txout().fromBuffer(new Buffer('050000000000000001ae', 'hex')); 91 | txout.toBuffer().toString('hex').should.equal('050000000000000001ae'); 92 | }); 93 | 94 | }); 95 | 96 | describe('#fromBufR', function() { 97 | 98 | it('should make this txin from this known buffer', function() { 99 | var txout = Txout().fromBufR(BufR(new Buffer('050000000000000001ae', 'hex'))); 100 | txout.toBuffer().toString('hex').should.equal('050000000000000001ae'); 101 | }); 102 | 103 | }); 104 | 105 | describe('#toBuffer', function() { 106 | 107 | it('should output this known buffer', function() { 108 | var txout = Txout().fromBufR(BufR(new Buffer('050000000000000001ae', 'hex'))); 109 | txout.toBuffer().toString('hex').should.equal('050000000000000001ae'); 110 | }); 111 | 112 | }); 113 | 114 | describe('#toBufW', function() { 115 | 116 | it('should output this known buffer', function() { 117 | var txout = Txout().fromBufR(BufR(new Buffer('050000000000000001ae', 'hex'))); 118 | txout.toBufW().concat().toString('hex').should.equal('050000000000000001ae'); 119 | }); 120 | 121 | }); 122 | 123 | }); 124 | -------------------------------------------------------------------------------- /test/varint.js: -------------------------------------------------------------------------------- 1 | var BN = require('../lib/bn'); 2 | var should = require('chai').should(); 3 | var BufR = require('../lib/bufr'); 4 | var BufW = require('../lib/bufw'); 5 | var Varint = require('../lib/varint'); 6 | 7 | describe('Varint', function() { 8 | 9 | it('should make a new varint', function() { 10 | var buf = new Buffer('00', 'hex'); 11 | var varint = new Varint(buf); 12 | should.exist(varint); 13 | varint.buf.toString('hex').should.equal('00'); 14 | varint = Varint(buf); 15 | should.exist(varint); 16 | varint.buf.toString('hex').should.equal('00'); 17 | 18 | //various ways to use the constructor 19 | Varint(Varint(0).toBuffer()).toNumber().should.equal(0); 20 | Varint(0).toNumber().should.equal(0); 21 | Varint(BN(0)).toNumber().should.equal(0); 22 | 23 | //varints can have multiple buffer representations 24 | Varint(0).toNumber().should.equal(Varint(new Buffer([0xFD, 0, 0])).toNumber()) 25 | Varint(0).toBuffer().toString('hex').should.not.equal(Varint().fromBuffer(new Buffer([0xFD, 0, 0])).toBuffer().toString('hex')) 26 | }); 27 | 28 | describe('#set', function() { 29 | 30 | it('should set a buffer', function() { 31 | var buf = new Buffer('00', 'hex'); 32 | var varint = Varint().set({buf: buf}); 33 | varint.buf.toString('hex').should.equal('00'); 34 | varint.set({}); 35 | varint.buf.toString('hex').should.equal('00'); 36 | }); 37 | 38 | }); 39 | 40 | describe('#fromJSON', function() { 41 | 42 | it('should set a buffer', function() { 43 | var buf = BufW().writeVarintNum(5).concat(); 44 | var varint = Varint().fromJSON(buf.toString('hex')); 45 | varint.toNumber().should.equal(5); 46 | }); 47 | 48 | }); 49 | 50 | describe('#toJSON', function() { 51 | 52 | it('should return a buffer', function() { 53 | var buf = BufW().writeVarintNum(5).concat(); 54 | var varint = Varint().fromJSON(buf.toString('hex')); 55 | varint.toJSON().should.equal('05'); 56 | }); 57 | 58 | }); 59 | 60 | describe('#fromBuffer', function() { 61 | 62 | it('should set a buffer', function() { 63 | var buf = BufW().writeVarintNum(5).concat(); 64 | var varint = Varint().fromBuffer(buf); 65 | varint.toNumber().should.equal(5); 66 | }); 67 | 68 | }); 69 | 70 | describe('#fromBufR', function() { 71 | 72 | it('should set a buffer reader', function() { 73 | var buf = BufW().writeVarintNum(5).concat(); 74 | var br = BufR(buf); 75 | var varint = Varint().fromBufR(br); 76 | varint.toNumber().should.equal(5); 77 | }); 78 | 79 | }); 80 | 81 | describe('#fromBN', function() { 82 | 83 | it('should set a number', function() { 84 | var varint = Varint().fromBN(BN(5)); 85 | varint.toNumber().should.equal(5); 86 | }); 87 | 88 | }); 89 | 90 | describe('#fromNumber', function() { 91 | 92 | it('should set a number', function() { 93 | var varint = Varint().fromNumber(5); 94 | varint.toNumber().should.equal(5); 95 | }); 96 | 97 | }); 98 | 99 | describe('#toBuffer', function() { 100 | 101 | it('should return a buffer', function() { 102 | buf = BufW().writeVarintNum(5).concat(); 103 | var varint = Varint(buf); 104 | varint.toBuffer().toString('hex').should.equal(buf.toString('hex')); 105 | }); 106 | 107 | }); 108 | 109 | describe('#toBN', function() { 110 | 111 | it('should return a buffer', function() { 112 | var varint = Varint(5); 113 | varint.toBN().toString().should.equal(BN(5).toString()); 114 | }); 115 | 116 | }); 117 | 118 | describe('#toNumber', function() { 119 | 120 | it('should return a buffer', function() { 121 | var varint = Varint(5); 122 | varint.toNumber().should.equal(5); 123 | }); 124 | 125 | }); 126 | 127 | }); 128 | -------------------------------------------------------------------------------- /test/vectors/kdf.json: -------------------------------------------------------------------------------- 1 | { 2 | "PBKDF2": { 3 | "valid": [ 4 | { 5 | "p": "70617373776f7264", 6 | "s": "73616c74", 7 | "c": 1, 8 | "dkLen": 20, 9 | "hmacf": "sha1", 10 | "key": "0c60c80f961f0e71f3a9b524af6012062fe037a6" 11 | }, 12 | { 13 | "p": "70617373776f7264", 14 | "s": "73616c74", 15 | "c": 2, 16 | "dkLen": 20, 17 | "hmacf": "sha1", 18 | "key": "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957" 19 | }, 20 | { 21 | "p": "70617373776f7264", 22 | "s": "73616c74", 23 | "c": 4096, 24 | "dkLen": 20, 25 | "hmacf": "sha1", 26 | "key": "4b007901b765489abead49d926f721d065a429c1" 27 | }, 28 | { 29 | "p": "70617373776f726450415353574f524470617373776f7264", 30 | "s": "73616c7453414c5473616c7453414c5473616c7453414c5473616c7453414c5473616c74", 31 | "c": 4096, 32 | "dkLen": 25, 33 | "hmacf": "sha1", 34 | "key": "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038" 35 | }, 36 | { 37 | "p": "7061737300776f7264", 38 | "s": "7361006c74", 39 | "c": 4096, 40 | "dkLen": 16, 41 | "hmacf": "sha1", 42 | "key": "56fa6aa75548099dcc37d7f03425e0c3" 43 | } 44 | ] 45 | } 46 | } 47 | --------------------------------------------------------------------------------