├── test ├── mocha.opts ├── data │ ├── blk86756-testnet.dat │ └── ecdsa.json ├── transaction │ ├── sighash.js │ └── transaction.js ├── crypto │ ├── random.js │ ├── bn.js │ ├── hash.js │ ├── point.js │ └── signature.js ├── util │ ├── js.js │ ├── preconditions.js │ └── buffer.js ├── networks.js ├── encoding │ ├── base58.js │ ├── varint.js │ ├── base58check.js │ └── bufferwriter.js ├── block │ ├── block.js │ ├── blockheader.js │ └── miner.js └── blockchain.js ├── lib ├── block │ ├── index.js │ ├── miner.js │ ├── blockheader.js │ └── block.js ├── transaction │ ├── index.js │ └── sighash.js ├── store │ ├── block.js │ └── transaction.js ├── util │ ├── preconditions.js │ ├── js.js │ └── buffer.js ├── hdkeycache.js ├── crypto │ ├── random.js │ ├── hash.js │ ├── point.js │ ├── bn.js │ ├── ecdsa.js │ └── signature.js ├── encoding │ ├── varint.js │ ├── base58.js │ ├── base58check.js │ ├── bufferwriter.js │ └── bufferreader.js ├── errors │ ├── index.js │ └── spec.js ├── networks.js └── blockchain.js ├── Gulpfile.js ├── .travis.yml ├── README.md ├── .gitignore ├── bower.json ├── LICENSE ├── index.js └── package.json /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --recursive 2 | --timeout 5000 3 | -------------------------------------------------------------------------------- /lib/block/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./block'); 2 | 3 | module.exports.BlockHeader = require('./blockheader'); 4 | -------------------------------------------------------------------------------- /test/data/blk86756-testnet.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decentraland/stoneage-node/HEAD/test/data/blk86756-testnet.dat -------------------------------------------------------------------------------- /lib/transaction/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./transaction'); 2 | 3 | module.exports.Sighash = require('./sighash'); 4 | -------------------------------------------------------------------------------- /Gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp_bitcore = require('bitcore-build'); 4 | 5 | gulp_bitcore('decentraland-core', { 6 | fullname: true 7 | }); 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.12' 4 | before_install: 5 | - npm install -g bower 6 | - export DISPLAY=:99.0 7 | - sh -e /etc/init.d/xvfb start 8 | install: 9 | - bower install 10 | - npm install 11 | after_script: 12 | - gulp coveralls 13 | -------------------------------------------------------------------------------- /lib/store/block.js: -------------------------------------------------------------------------------- 1 | var Promise = require('bluebird'); 2 | 3 | function BlockStore() { 4 | this.block = {}; 5 | } 6 | 7 | BlockStore.prototype.get = function(hash) { 8 | return this.block[hash]; 9 | }; 10 | 11 | BlockStore.prototype.set = function(block) { 12 | return this.block[block.hash] = block; 13 | }; 14 | 15 | module.exports = BlockStore; 16 | -------------------------------------------------------------------------------- /test/transaction/sighash.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var buffer = require('buffer'); 4 | 5 | var chai = require('chai'); 6 | var should = chai.should(); 7 | var bitcore = require('../../'); 8 | var Script = bitcore.Script; 9 | var Transaction = bitcore.Transaction; 10 | var sighash = Transaction.sighash; 11 | 12 | describe('sighash', function() { 13 | 14 | }); 15 | -------------------------------------------------------------------------------- /lib/store/transaction.js: -------------------------------------------------------------------------------- 1 | var Promise = require('bluebird'); 2 | 3 | function TransactionStore() { 4 | this.txs = {}; 5 | } 6 | 7 | TransactionStore.prototype.get = function(hash) { 8 | return this.txs[hash]; 9 | }; 10 | 11 | TransactionStore.prototype.set = function(tx) { 12 | return this.txs[tx.hash] = tx; 13 | }; 14 | 15 | module.exports = TransactionStore; 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # decentraland-core 2 | Blockchain-based decentralized land implementation in JavaScript 3 | 4 | This is a clone of bitcore's codebase, with the following changes: 5 | - Removed Script-related classes 6 | - Removed URI, Unit, and other bitcoin-only utils 7 | - Changed PublicKey to support only compressed keys 8 | - Removed Transaction Input, Output and related classes 9 | - Changed Transaction signing logic (using simpler scheme) 10 | - Transaction now has owner, previous transaction, color, position, version, signature 11 | - Miner class added 12 | - Blockchain class added 13 | 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | 29 | *.swp 30 | *.swo 31 | 32 | decentraland-core.js 33 | tests.js 34 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "decentraland-core", 3 | "main": "./decentraland-core.min.js", 4 | "version": "0.0.3", 5 | "homepage": "http://decentraland.org", 6 | "authors": [ 7 | "Manuel Araoz ", 8 | "Esteban Ordano " 9 | ], 10 | "description": "Blockchain-based decentralized land implementation in JavaScript ", 11 | "moduleType": [ 12 | "globals" 13 | ], 14 | "keywords": [ 15 | "blockchain", 16 | "transaction", 17 | "p2p", 18 | "land", 19 | "decentralized" 20 | ], 21 | "license": "MIT", 22 | "ignore": [ 23 | "**/.*", 24 | "CONTRIBUTING.md", 25 | "gulpfile.js", 26 | "lib", 27 | "index.js", 28 | "karma.conf.js", 29 | "npm-shrinkwrap.json", 30 | "test" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /test/crypto/random.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var bitcore = require('../..'); 4 | var Random = bitcore.crypto.Random; 5 | 6 | describe('Random', function() { 7 | 8 | describe('@getRandomBuffer', function() { 9 | 10 | it('should return a buffer', function() { 11 | var bytes = Random.getRandomBuffer(8); 12 | bytes.length.should.equal(8); 13 | Buffer.isBuffer(bytes).should.equal(true); 14 | }); 15 | 16 | it('should not equate two 256 bit random buffers', function() { 17 | var bytes1 = Random.getRandomBuffer(32); 18 | var bytes2 = Random.getRandomBuffer(32); 19 | bytes1.toString('hex').should.not.equal(bytes2.toString('hex')); 20 | }); 21 | 22 | it('should generate 100 8 byte buffers in a row that are not equal', function() { 23 | var hexs = []; 24 | for (var i = 0; i < 100; i++) { 25 | hexs[i] = Random.getRandomBuffer(8).toString('hex'); 26 | } 27 | for (i = 0; i < 100; i++) { 28 | for (var j = i + 1; j < 100; j++) { 29 | hexs[i].should.not.equal(hexs[j]); 30 | } 31 | } 32 | }); 33 | 34 | }); 35 | 36 | }); 37 | -------------------------------------------------------------------------------- /lib/util/preconditions.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var errors = require('../errors'); 4 | var _ = require('lodash'); 5 | 6 | module.exports = { 7 | checkState: function(condition, message) { 8 | if (!condition) { 9 | throw new errors.InvalidState(message); 10 | } 11 | }, 12 | checkArgument: function(condition, argumentName, message, docsPath) { 13 | if (!condition) { 14 | throw new errors.InvalidArgument(argumentName, message, docsPath); 15 | } 16 | }, 17 | checkArgumentType: function(argument, type, argumentName) { 18 | argumentName = argumentName || '(unknown name)'; 19 | if (_.isString(type)) { 20 | if (type === 'Buffer') { 21 | var BufferUtil = require('./buffer'); 22 | if (!BufferUtil.isBuffer(argument)) { 23 | throw new errors.InvalidArgumentType(argument, type, argumentName); 24 | } 25 | } else if (typeof argument !== type) { 26 | throw new errors.InvalidArgumentType(argument, type, argumentName); 27 | } 28 | } else { 29 | if (!(argument instanceof type)) { 30 | throw new errors.InvalidArgumentType(argument, type.name, argumentName); 31 | } 32 | } 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /lib/hdkeycache.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | _cache: {}, 5 | _count: 0, 6 | _eraseIndex: 0, 7 | _usedList: {}, 8 | _usedIndex: {}, 9 | _CACHE_SIZE: 5000, 10 | 11 | get: function(xkey, number, hardened) { 12 | hardened = !!hardened; 13 | var key = xkey + '/' + number + '/' + hardened; 14 | if (this._cache[key]) { 15 | this._cacheHit(key); 16 | return this._cache[key]; 17 | } 18 | }, 19 | set: function(xkey, number, hardened, derived) { 20 | hardened = !!hardened; 21 | var key = xkey + '/' + number + '/' + hardened; 22 | this._cache[key] = derived; 23 | this._cacheHit(key); 24 | }, 25 | _cacheHit: function(key) { 26 | if (this._usedIndex[key]) { 27 | delete this._usedList[this._usedIndex[key]]; 28 | } 29 | this._usedList[this._count] = key; 30 | this._usedIndex[key] = this._count; 31 | this._count++; 32 | this._cacheRemove(); 33 | }, 34 | _cacheRemove: function() { 35 | while (this._eraseIndex < this._count - this._CACHE_SIZE) { 36 | if (this._usedList[this._eraseIndex]) { 37 | var removeKey = this._usedList[this._eraseIndex]; 38 | delete this._usedIndex[removeKey]; 39 | delete this._cache[removeKey]; 40 | } 41 | delete this._usedList[this._eraseIndex]; 42 | this._eraseIndex++; 43 | } 44 | } 45 | }; 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Manuel Aráoz 4 | 5 | Parts of this software are based on bitcore 6 | Copyright (c) 2013-2015 BitPay, Inc. 7 | 8 | Parts of this software are based on fullnode 9 | Copyright (c) 2014 Ryan X. Charles 10 | Copyright (c) 2014 reddit, Inc. 11 | 12 | Parts of this software are based on BitcoinJS 13 | Copyright (c) 2011 Stefan Thomas 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 all 26 | 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 THE 34 | SOFTWARE. 35 | 36 | -------------------------------------------------------------------------------- /lib/crypto/random.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function Random() { 4 | } 5 | 6 | /* secure random bytes that sometimes throws an error due to lack of entropy */ 7 | Random.getRandomBuffer = function(size) { 8 | if (process.browser) 9 | return Random.getRandomBufferBrowser(size); 10 | else 11 | return Random.getRandomBufferNode(size); 12 | }; 13 | 14 | Random.getRandomBufferNode = function(size) { 15 | var crypto = require('crypto'); 16 | return crypto.randomBytes(size); 17 | }; 18 | 19 | Random.getRandomBufferBrowser = function(size) { 20 | if (!window.crypto && !window.msCrypto) 21 | throw new Error('window.crypto not available'); 22 | 23 | if (window.crypto && window.crypto.getRandomValues) 24 | var crypto = window.crypto; 25 | else if (window.msCrypto && window.msCrypto.getRandomValues) //internet explorer 26 | var crypto = window.msCrypto; 27 | else 28 | throw new Error('window.crypto.getRandomValues not available'); 29 | 30 | var bbuf = new Uint8Array(size); 31 | crypto.getRandomValues(bbuf); 32 | var buf = new Buffer(bbuf); 33 | 34 | return buf; 35 | }; 36 | 37 | /* insecure random bytes, but it never fails */ 38 | Random.getPseudoRandomBuffer = function(size) { 39 | var b32 = 0x100000000; 40 | var b = new Buffer(size); 41 | var r; 42 | 43 | for (var i = 0; i <= size; i++) { 44 | var j = Math.floor(i / 4); 45 | var k = i - j * 4; 46 | if (k === 0) { 47 | r = Math.random() * b32; 48 | b[i] = r & 0xff; 49 | } else { 50 | b[i] = (r = r >>> 8) & 0xff; 51 | } 52 | } 53 | 54 | return b; 55 | }; 56 | 57 | module.exports = Random; 58 | -------------------------------------------------------------------------------- /lib/encoding/varint.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var BufferWriter = require('./bufferwriter'); 4 | var BufferReader = require('./bufferreader'); 5 | var BN = require('../crypto/bn'); 6 | 7 | var Varint = function Varint(buf) { 8 | if (!(this instanceof Varint)) 9 | return new Varint(buf); 10 | if (Buffer.isBuffer(buf)) { 11 | this.buf = buf; 12 | } else if (typeof buf === 'number') { 13 | var num = buf; 14 | this.fromNumber(num); 15 | } else if (buf instanceof BN) { 16 | var bn = buf; 17 | this.fromBN(bn); 18 | } else if (buf) { 19 | var obj = buf; 20 | this.set(obj); 21 | } 22 | }; 23 | 24 | Varint.prototype.set = function(obj) { 25 | this.buf = obj.buf || this.buf; 26 | return this; 27 | }; 28 | 29 | Varint.prototype.fromString = function(str) { 30 | this.set({ 31 | buf: new Buffer(str, 'hex') 32 | }); 33 | return this; 34 | }; 35 | 36 | Varint.prototype.toString = function() { 37 | return this.buf.toString('hex'); 38 | }; 39 | 40 | Varint.prototype.fromBuffer = function(buf) { 41 | this.buf = buf; 42 | return this; 43 | }; 44 | 45 | Varint.prototype.fromBufferReader = function(br) { 46 | this.buf = br.readVarintBuf(); 47 | return this; 48 | }; 49 | 50 | Varint.prototype.fromBN = function(bn) { 51 | this.buf = BufferWriter().writeVarintBN(bn).concat(); 52 | return this; 53 | }; 54 | 55 | Varint.prototype.fromNumber = function(num) { 56 | this.buf = BufferWriter().writeVarintNum(num).concat(); 57 | return this; 58 | }; 59 | 60 | Varint.prototype.toBuffer = function() { 61 | return this.buf; 62 | }; 63 | 64 | Varint.prototype.toBN = function() { 65 | return BufferReader(this.buf).readVarintBN(); 66 | }; 67 | 68 | Varint.prototype.toNumber = function() { 69 | return BufferReader(this.buf).readVarintNum(); 70 | }; 71 | 72 | module.exports = Varint; 73 | -------------------------------------------------------------------------------- /lib/encoding/base58.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash'); 4 | var bs58 = require('bs58'); 5 | var buffer = require('buffer'); 6 | 7 | var ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'.split(''); 8 | 9 | var Base58 = function Base58(obj) { 10 | /* jshint maxcomplexity: 8 */ 11 | if (!(this instanceof Base58)) { 12 | return new Base58(obj); 13 | } 14 | if (Buffer.isBuffer(obj)) { 15 | var buf = obj; 16 | this.fromBuffer(buf); 17 | } else if (typeof obj === 'string') { 18 | var str = obj; 19 | this.fromString(str); 20 | } else if (obj) { 21 | this.set(obj); 22 | } 23 | }; 24 | 25 | Base58.validCharacters = function validCharacters(chars) { 26 | if (buffer.Buffer.isBuffer(chars)) { 27 | chars = chars.toString(); 28 | } 29 | return _.all(_.map(chars, function(char) { return _.contains(ALPHABET, char); })); 30 | }; 31 | 32 | Base58.prototype.set = function(obj) { 33 | this.buf = obj.buf || this.buf || undefined; 34 | return this; 35 | }; 36 | 37 | Base58.encode = function(buf) { 38 | if (!buffer.Buffer.isBuffer(buf)) { 39 | throw new Error('Input should be a buffer'); 40 | } 41 | return bs58.encode(buf); 42 | }; 43 | 44 | Base58.decode = function(str) { 45 | if (typeof str !== 'string') { 46 | throw new Error('Input should be a string'); 47 | } 48 | return new Buffer(bs58.decode(str)); 49 | }; 50 | 51 | Base58.prototype.fromBuffer = function(buf) { 52 | this.buf = buf; 53 | return this; 54 | }; 55 | 56 | Base58.prototype.fromString = function(str) { 57 | var buf = Base58.decode(str); 58 | this.buf = buf; 59 | return this; 60 | }; 61 | 62 | Base58.prototype.toBuffer = function() { 63 | return this.buf; 64 | }; 65 | 66 | Base58.prototype.toString = function() { 67 | return Base58.encode(this.buf); 68 | }; 69 | 70 | module.exports = Base58; 71 | -------------------------------------------------------------------------------- /lib/errors/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash'); 4 | 5 | function format(message, args) { 6 | return message 7 | .replace('{0}', args[0]) 8 | .replace('{1}', args[1]) 9 | .replace('{2}', args[2]); 10 | } 11 | var traverseNode = function(parent, errorDefinition) { 12 | var NodeError = function() { 13 | if (_.isString(errorDefinition.message)) { 14 | this.message = format(errorDefinition.message, arguments); 15 | } else if (_.isFunction(errorDefinition.message)) { 16 | this.message = errorDefinition.message.apply(null, arguments); 17 | } else { 18 | throw new Error('Invalid error definition for ' + errorDefinition.name); 19 | } 20 | this.stack = this.message + '\n' + (new Error()).stack; 21 | }; 22 | NodeError.prototype = Object.create(parent.prototype); 23 | NodeError.prototype.name = parent.prototype.name + errorDefinition.name; 24 | parent[errorDefinition.name] = NodeError; 25 | if (errorDefinition.errors) { 26 | childDefinitions(NodeError, errorDefinition.errors); 27 | } 28 | return NodeError; 29 | }; 30 | 31 | /* jshint latedef: false */ 32 | var childDefinitions = function(parent, childDefinitions) { 33 | _.each(childDefinitions, function(childDefinition) { 34 | traverseNode(parent, childDefinition); 35 | }); 36 | }; 37 | /* jshint latedef: true */ 38 | 39 | var traverseRoot = function(parent, errorsDefinition) { 40 | childDefinitions(parent, errorsDefinition); 41 | return parent; 42 | }; 43 | 44 | 45 | var bitcore = {}; 46 | bitcore.Error = function() { 47 | this.message = 'Internal error'; 48 | this.stack = this.message + '\n' + (new Error()).stack; 49 | }; 50 | bitcore.Error.prototype = Object.create(Error.prototype); 51 | bitcore.Error.prototype.name = 'bitcore.Error'; 52 | 53 | 54 | var data = require('./spec'); 55 | traverseRoot(bitcore.Error, data); 56 | 57 | module.exports = bitcore.Error; 58 | 59 | module.exports.extend = function(spec) { 60 | return traverseNode(bitcore.Error, spec); 61 | }; 62 | -------------------------------------------------------------------------------- /lib/transaction/sighash.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var BufferReader = require('../encoding/bufferreader'); 4 | var Hash = require('../crypto/hash'); 5 | var ECDSA = require('../crypto/ecdsa'); 6 | var PublicKey = require('../publickey'); 7 | var $ = require('../util/preconditions'); 8 | 9 | /** 10 | * Returns a buffer of length 32 bytes with the hash that needs to be signed 11 | * for OP_CHECKSIG. 12 | * 13 | * @name Signing.sighash 14 | * @param {Transaction} transaction the transaction to sign 15 | */ 16 | var sighash = function sighash(tx) { 17 | var Transaction = require('./transaction'); 18 | var tx2 = new Transaction(tx.toString()); 19 | tx2.signature = null; 20 | var hash = Hash.sha256sha256(tx2.toBuffer()); 21 | return new BufferReader(hash).readReverse(); 22 | }; 23 | 24 | /** 25 | * Create a signature 26 | * 27 | * @name Signing.sign 28 | * @param {Transaction} transaction 29 | * @param {PrivateKey} privateKey 30 | * @param {number} sighash 31 | * @return {Signature} 32 | */ 33 | function sign(transaction, privateKey) { 34 | var hashbuf = sighash(transaction); 35 | var sig = ECDSA.sign(hashbuf, privateKey, 'little'); 36 | return sig; 37 | } 38 | 39 | /** 40 | * Verify a signature 41 | * 42 | * @name Signing.verify 43 | * @param {Transaction} transaction 44 | * @param {Signature} signature 45 | * @param {PublicKey} publicKey 46 | * @return {boolean} 47 | */ 48 | function verify(transaction, signature, publicKey) { 49 | var Transaction = require('./transaction'); 50 | var Signature = require('../crypto/signature'); 51 | $.checkArgument(transaction instanceof Transaction, 'transaction must be a Transaction'); 52 | $.checkArgument(signature instanceof Signature, 'signature must be a Signature'); 53 | $.checkArgument(publicKey instanceof PublicKey, 'publicKey must be a PublicKey'); 54 | var hashbuf = sighash(transaction); 55 | return ECDSA.verify(hashbuf, signature, publicKey, 'little'); 56 | } 57 | 58 | /** 59 | * @namespace Signing 60 | */ 61 | module.exports = { 62 | sighash: sighash, 63 | sign: sign, 64 | verify: verify 65 | }; 66 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var core = module.exports; 4 | 5 | // module information 6 | core.version = 'v' + require('./package.json').version; 7 | 8 | // crypto 9 | core.crypto = {}; 10 | core.crypto.BN = require('./lib/crypto/bn'); 11 | core.crypto.ECDSA = require('./lib/crypto/ecdsa'); 12 | core.crypto.Hash = require('./lib/crypto/hash'); 13 | core.crypto.Random = require('./lib/crypto/random'); 14 | core.crypto.Point = require('./lib/crypto/point'); 15 | core.crypto.Signature = require('./lib/crypto/signature'); 16 | 17 | // encoding 18 | core.encoding = {}; 19 | core.encoding.Base58 = require('./lib/encoding/base58'); 20 | core.encoding.Base58Check = require('./lib/encoding/base58check'); 21 | core.encoding.BufferReader = require('./lib/encoding/bufferreader'); 22 | core.encoding.BufferWriter = require('./lib/encoding/bufferwriter'); 23 | core.encoding.Varint = require('./lib/encoding/varint'); 24 | 25 | // utilities 26 | core.util = {}; 27 | core.util.buffer = require('./lib/util/buffer'); 28 | core.util.js = require('./lib/util/js'); 29 | core.util.preconditions = require('./lib/util/preconditions'); 30 | 31 | // errors thrown by the library 32 | core.errors = require('./lib/errors'); 33 | 34 | // main bitcoin library 35 | core.Block = require('./lib/block'); 36 | core.Blockchain = require('./lib/blockchain'); 37 | core.BlockHeader = require('./lib/block/blockheader'); 38 | core.Miner = require('./lib/block/miner'); 39 | core.HDPrivateKey = require('./lib/hdprivatekey.js'); 40 | core.HDPublicKey = require('./lib/hdpublickey.js'); 41 | core.Networks = require('./lib/networks'); 42 | core.PrivateKey = require('./lib/privatekey'); 43 | core.PublicKey = require('./lib/publickey'); 44 | core.Transaction = require('./lib/transaction'); 45 | 46 | // dependencies, subject to change 47 | core.deps = {}; 48 | core.deps.bnjs = require('bn.js'); 49 | core.deps.bs58 = require('bs58'); 50 | core.deps.Buffer = Buffer; 51 | core.deps.elliptic = require('elliptic'); 52 | core.deps._ = require('lodash'); 53 | 54 | // Internal usage, exposed for testing/advanced tweaking 55 | core.Transaction.sighash = require('./lib/transaction/sighash'); 56 | -------------------------------------------------------------------------------- /lib/util/js.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash'); 4 | 5 | /** 6 | * Determines whether a string contains only hexadecimal values 7 | * 8 | * @name JSUtil.isHexa 9 | * @param {string} value 10 | * @return {boolean} true if the string is the hexa representation of a number 11 | */ 12 | var isHexa = function isHexa(value) { 13 | if (!_.isString(value)) { 14 | return false; 15 | } 16 | return /^[0-9a-fA-F]+$/.test(value); 17 | }; 18 | 19 | /** 20 | * @namespace JSUtil 21 | */ 22 | module.exports = { 23 | /** 24 | * Test if an argument is a valid JSON object. If it is, returns a truthy 25 | * value (the json object decoded), so no double JSON.parse call is necessary 26 | * 27 | * @param {string} arg 28 | * @return {Object|boolean} false if the argument is not a JSON string. 29 | */ 30 | isValidJSON: function isValidJSON(arg) { 31 | var parsed; 32 | if (!_.isString(arg)) { 33 | return false; 34 | } 35 | try { 36 | parsed = JSON.parse(arg); 37 | } catch (e) { 38 | return false; 39 | } 40 | if (typeof(parsed) === 'object') { 41 | return true; 42 | } 43 | return false; 44 | }, 45 | isHexa: isHexa, 46 | isHexaString: isHexa, 47 | 48 | /** 49 | * Clone an array 50 | */ 51 | cloneArray: function(array) { 52 | return [].concat(array); 53 | }, 54 | 55 | /** 56 | * Define immutable properties on a target object 57 | * 58 | * @param {Object} target - An object to be extended 59 | * @param {Object} values - An object of properties 60 | * @return {Object} The target object 61 | */ 62 | defineImmutable: function defineImmutable(target, values) { 63 | Object.keys(values).forEach(function(key){ 64 | Object.defineProperty(target, key, { 65 | configurable: false, 66 | enumerable: true, 67 | value: values[key] 68 | }); 69 | }); 70 | return target; 71 | }, 72 | /** 73 | * Checks that a value is a natural number, a positive integer or zero. 74 | * 75 | * @param {*} value 76 | * @return {Boolean} 77 | */ 78 | isNaturalNumber: function isNaturalNumber(value) { 79 | return typeof value === 'number' && 80 | isFinite(value) && 81 | Math.floor(value) === value && 82 | value >= 0; 83 | } 84 | }; 85 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "decentraland-core", 3 | "version": "0.0.3", 4 | "description": "Blockchain-based decentralized land implementation in JavaScript", 5 | "main": "index.js", 6 | "author": "Manuel Araoz ", 7 | "scripts": { 8 | "lint": "gulp lint", 9 | "test": "gulp test", 10 | "coverage": "gulp coverage", 11 | "build": "gulp" 12 | }, 13 | "contributors": [ 14 | { 15 | "name": "Manuel Araoz", 16 | "email": "manuelaraoz@gmail.com" 17 | }, 18 | { 19 | "name": "Esteban Ordano", 20 | "email": "eordano@gmail.com" 21 | }, 22 | { 23 | "name": "Daniel Cousens", 24 | "email": "bitcoin@dcousens.com" 25 | }, 26 | { 27 | "name": "Gordon Hall", 28 | "email": "gordon@bitpay.com" 29 | }, 30 | { 31 | "name": "Jeff Garzik", 32 | "email": "jgarzik@bitpay.com" 33 | }, 34 | { 35 | "name": "Kyle Drake", 36 | "email": "kyle@kyledrake.net" 37 | }, 38 | { 39 | "name": "Matias Alejo Garcia", 40 | "email": "ematiu@gmail.com" 41 | }, 42 | { 43 | "name": "Ryan X. Charles", 44 | "email": "ryanxcharles@gmail.com" 45 | }, 46 | { 47 | "name": "Stefan Thomas", 48 | "email": "moon@justmoon.net" 49 | }, 50 | { 51 | "name": "Stephen Pair", 52 | "email": "stephen@bitpay.com" 53 | }, 54 | { 55 | "name": "Wei Lu", 56 | "email": "luwei.here@gmail.com" 57 | } 58 | ], 59 | "keywords": [ 60 | "blockchain", 61 | "transaction", 62 | "p2p", 63 | "land", 64 | "decentralized" 65 | ], 66 | "repository": { 67 | "type": "git", 68 | "url": "git://github.com/maraoz/decentraland-core.git" 69 | }, 70 | "browser": { 71 | "request": "browser-request" 72 | }, 73 | "dependencies": { 74 | "bluebird": "^2.9.26", 75 | "bn.js": "=2.0.4", 76 | "bs58": "=2.0.0", 77 | "elliptic": "=3.0.3", 78 | "hash.js": "=1.0.2", 79 | "inherits": "=2.0.1", 80 | "lodash": "=2.4.1", 81 | "sha512": "=0.0.1" 82 | }, 83 | "devDependencies": { 84 | "bitcore-build": "maraoz/bitcore-build#e848a8ba612366ad98abd20e229570dc061a4288", 85 | "brfs": "^1.2.0", 86 | "chai": "^1.10.0", 87 | "gulp": "^3.8.10", 88 | "sinon": "^1.13.0" 89 | }, 90 | "license": "MIT" 91 | } 92 | -------------------------------------------------------------------------------- /test/util/js.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* jshint unused: false */ 3 | 4 | var should = require('chai').should(); 5 | var expect = require('chai').expect; 6 | 7 | var bitcore = require('../..'); 8 | var JSUtil = bitcore.util.js; 9 | 10 | describe('js utils', function() { 11 | 12 | describe('isValidJSON', function() { 13 | 14 | var hexa = '8080808080808080808080808080808080808080808080808080808080808080'; 15 | var json = '{"key": ["value", "value2"]}'; 16 | var json2 = '["value", "value2", {"key": "value"}]'; 17 | 18 | it('does not mistake an integer as valid json object', function() { 19 | var valid = JSUtil.isValidJSON(hexa); 20 | valid.should.equal(false); 21 | }); 22 | 23 | it('correctly validates a json object', function() { 24 | var valid = JSUtil.isValidJSON(json); 25 | valid.should.equal(true); 26 | }); 27 | 28 | it('correctly validates an array json object', function() { 29 | var valid = JSUtil.isValidJSON(json); 30 | valid.should.equal(true); 31 | }); 32 | 33 | }); 34 | 35 | describe('isNaturalNumber', function() { 36 | it('false for float', function() { 37 | var a = JSUtil.isNaturalNumber(0.1); 38 | a.should.equal(false); 39 | }); 40 | 41 | it('false for string float', function() { 42 | var a = JSUtil.isNaturalNumber('0.1'); 43 | a.should.equal(false); 44 | }); 45 | 46 | it('false for string integer', function() { 47 | var a = JSUtil.isNaturalNumber('1'); 48 | a.should.equal(false); 49 | }); 50 | 51 | it('false for negative integer', function() { 52 | var a = JSUtil.isNaturalNumber(-1); 53 | a.should.equal(false); 54 | }); 55 | 56 | it('false for negative integer string', function() { 57 | var a = JSUtil.isNaturalNumber('-1'); 58 | a.should.equal(false); 59 | }); 60 | 61 | it('false for infinity', function() { 62 | var a = JSUtil.isNaturalNumber(Infinity); 63 | a.should.equal(false); 64 | }); 65 | 66 | it('false for NaN', function() { 67 | var a = JSUtil.isNaturalNumber(NaN); 68 | a.should.equal(false); 69 | }); 70 | 71 | it('true for zero', function() { 72 | var a = JSUtil.isNaturalNumber(0); 73 | a.should.equal(true); 74 | }); 75 | 76 | it('true for positive integer', function() { 77 | var a = JSUtil.isNaturalNumber(1000); 78 | a.should.equal(true); 79 | }); 80 | 81 | }); 82 | 83 | }); 84 | -------------------------------------------------------------------------------- /test/util/preconditions.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var should = require('chai').should(); 4 | 5 | var bitcore = require('../..'); 6 | var errors = bitcore.errors; 7 | var $ = bitcore.util.preconditions; 8 | var PrivateKey = bitcore.PrivateKey; 9 | 10 | describe('preconditions', function() { 11 | 12 | it('can be used to assert state', function() { 13 | (function() { 14 | $.checkState(false, 'testing'); 15 | }).should.throw(errors.InvalidState); 16 | }); 17 | it('throws no false negative', function() { 18 | (function() { 19 | $.checkState(true, 'testing'); 20 | }).should.not.throw(); 21 | }); 22 | 23 | it('can be used to check an argument', function() { 24 | (function() { 25 | $.checkArgument(false, 'testing'); 26 | }).should.throw(errors.InvalidArgument); 27 | 28 | (function() { 29 | $.checkArgument(true, 'testing'); 30 | }).should.not.throw(errors.InvalidArgument); 31 | }); 32 | 33 | it('can be used to check an argument type', function() { 34 | var error; 35 | try { 36 | $.checkArgumentType(1, 'string', 'argumentName'); 37 | } catch (e) { 38 | error = e; 39 | e.message.should.equal('Invalid Argument for argumentName, expected string but got number'); 40 | } 41 | should.exist(error); 42 | }); 43 | it('has no false negatives when used to check an argument type', function() { 44 | (function() { 45 | $.checkArgumentType('a String', 'string', 'argumentName'); 46 | }).should.not.throw(); 47 | }); 48 | 49 | it('can be used to check an argument type for a class', function() { 50 | var error; 51 | try { 52 | $.checkArgumentType(1, PrivateKey); 53 | } catch (e) { 54 | error = e; 55 | var fail = !(~e.message.indexOf('Invalid Argument for (unknown name)')); 56 | fail.should.equal(false); 57 | } 58 | should.exist(error); 59 | }); 60 | it('has no false negatives when checking a type for a class', function() { 61 | (function() { 62 | $.checkArgumentType(new PrivateKey(), PrivateKey); 63 | }).should.not.throw(); 64 | }); 65 | 66 | it('formats correctly a message on InvalidArgument()', function() { 67 | var error = new errors.InvalidArgument(); 68 | error.message.should.equal('Invalid Argument'); 69 | }); 70 | 71 | it('formats correctly a message on checkArgument', function() { 72 | var error; 73 | try { 74 | $.checkArgument(null, 'parameter must be provided'); 75 | } catch (e) { 76 | error = e; 77 | } 78 | error.message.should.equal('Invalid Argument: parameter must be provided'); 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /lib/crypto/hash.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var hashjs = require('hash.js'); 4 | var sha512 = require('sha512'); 5 | var crypto = require('crypto'); 6 | var BufferUtil = require('../util/buffer'); 7 | var $ = require('../util/preconditions'); 8 | 9 | var Hash = module.exports; 10 | 11 | Hash.sha1 = function(buf) { 12 | $.checkArgument(BufferUtil.isBuffer(buf)); 13 | return crypto.createHash('sha1').update(buf).digest(); 14 | }; 15 | 16 | Hash.sha1.blocksize = 512; 17 | 18 | Hash.sha256 = function(buf) { 19 | $.checkArgument(BufferUtil.isBuffer(buf)); 20 | return crypto.createHash('sha256').update(buf).digest(); 21 | }; 22 | 23 | Hash.sha256.blocksize = 512; 24 | 25 | Hash.sha256sha256 = function(buf) { 26 | $.checkArgument(BufferUtil.isBuffer(buf)); 27 | return Hash.sha256(Hash.sha256(buf)); 28 | }; 29 | 30 | Hash.ripemd160 = function(buf) { 31 | $.checkArgument(BufferUtil.isBuffer(buf)); 32 | var hash = (new hashjs.ripemd160()).update(buf).digest(); 33 | return new Buffer(hash); 34 | }; 35 | 36 | Hash.sha256ripemd160 = function(buf) { 37 | $.checkArgument(BufferUtil.isBuffer(buf)); 38 | return Hash.ripemd160(Hash.sha256(buf)); 39 | }; 40 | 41 | Hash.sha512 = function(buf) { 42 | $.checkArgument(BufferUtil.isBuffer(buf)); 43 | var hash = sha512(buf); 44 | return new Buffer(hash); 45 | }; 46 | 47 | Hash.sha512.blocksize = 1024; 48 | 49 | Hash.hmac = function(hashf, data, key) { 50 | //http://en.wikipedia.org/wiki/Hash-based_message_authentication_code 51 | //http://tools.ietf.org/html/rfc4868#section-2 52 | $.checkArgument(BufferUtil.isBuffer(data)); 53 | $.checkArgument(BufferUtil.isBuffer(key)); 54 | $.checkArgument(hashf.blocksize); 55 | 56 | var blocksize = hashf.blocksize / 8; 57 | 58 | if (key.length > blocksize) { 59 | key = hashf(key); 60 | } else if (key < blocksize) { 61 | var fill = new Buffer(blocksize); 62 | fill.fill(0); 63 | key.copy(fill); 64 | key = fill; 65 | } 66 | 67 | var o_key = new Buffer(blocksize); 68 | o_key.fill(0x5c); 69 | 70 | var i_key = new Buffer(blocksize); 71 | i_key.fill(0x36); 72 | 73 | var o_key_pad = new Buffer(blocksize); 74 | var i_key_pad = new Buffer(blocksize); 75 | for (var i = 0; i < blocksize; i++) { 76 | o_key_pad[i] = o_key[i] ^ key[i]; 77 | i_key_pad[i] = i_key[i] ^ key[i]; 78 | } 79 | 80 | return hashf(Buffer.concat([o_key_pad, hashf(Buffer.concat([i_key_pad, data]))])); 81 | }; 82 | 83 | Hash.sha256hmac = function(data, key) { 84 | return Hash.hmac(Hash.sha256, data, key); 85 | }; 86 | 87 | Hash.sha512hmac = function(data, key) { 88 | return Hash.hmac(Hash.sha512, data, key); 89 | }; 90 | -------------------------------------------------------------------------------- /lib/encoding/base58check.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash'); 4 | var Base58 = require('./base58'); 5 | var buffer = require('buffer'); 6 | var sha256sha256 = require('../crypto/hash').sha256sha256; 7 | 8 | var Base58Check = function Base58Check(obj) { 9 | if (!(this instanceof Base58Check)) 10 | return new Base58Check(obj); 11 | if (Buffer.isBuffer(obj)) { 12 | var buf = obj; 13 | this.fromBuffer(buf); 14 | } else if (typeof obj === 'string') { 15 | var str = obj; 16 | this.fromString(str); 17 | } else if (obj) { 18 | this.set(obj); 19 | } 20 | }; 21 | 22 | Base58Check.prototype.set = function(obj) { 23 | this.buf = obj.buf || this.buf || undefined; 24 | return this; 25 | }; 26 | 27 | Base58Check.validChecksum = function validChecksum(data, checksum) { 28 | if (_.isString(data)) { 29 | data = new buffer.Buffer(Base58.decode(data)); 30 | } 31 | if (_.isString(checksum)) { 32 | checksum = new buffer.Buffer(Base58.decode(checksum)); 33 | } 34 | if (!checksum) { 35 | checksum = data.slice(-4); 36 | data = data.slice(0, -4); 37 | } 38 | return Base58Check.checksum(data).toString('hex') === checksum.toString('hex'); 39 | }; 40 | 41 | Base58Check.decode = function(s) { 42 | if (typeof s !== 'string') 43 | throw new Error('Input must be a string'); 44 | 45 | var buf = new Buffer(Base58.decode(s)); 46 | 47 | if (buf.length < 4) 48 | throw new Error("Input string too short"); 49 | 50 | var data = buf.slice(0, -4); 51 | var csum = buf.slice(-4); 52 | 53 | var hash = sha256sha256(data); 54 | var hash4 = hash.slice(0, 4); 55 | 56 | if (csum.toString('hex') !== hash4.toString('hex')) 57 | throw new Error("Checksum mismatch"); 58 | 59 | return data; 60 | }; 61 | 62 | Base58Check.checksum = function(buffer) { 63 | return sha256sha256(buffer).slice(0, 4); 64 | }; 65 | 66 | Base58Check.encode = function(buf) { 67 | if (!Buffer.isBuffer(buf)) 68 | throw new Error('Input must be a buffer'); 69 | var checkedBuf = new Buffer(buf.length + 4); 70 | var hash = Base58Check.checksum(buf); 71 | buf.copy(checkedBuf); 72 | hash.copy(checkedBuf, buf.length); 73 | return Base58.encode(checkedBuf); 74 | }; 75 | 76 | Base58Check.prototype.fromBuffer = function(buf) { 77 | this.buf = buf; 78 | return this; 79 | }; 80 | 81 | Base58Check.prototype.fromString = function(str) { 82 | var buf = Base58Check.decode(str); 83 | this.buf = buf; 84 | return this; 85 | }; 86 | 87 | Base58Check.prototype.toBuffer = function() { 88 | return this.buf; 89 | }; 90 | 91 | Base58Check.prototype.toString = function() { 92 | return Base58Check.encode(this.buf); 93 | }; 94 | 95 | module.exports = Base58Check; 96 | -------------------------------------------------------------------------------- /lib/block/miner.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Transaction = require('../transaction'); 4 | var Block = require('./block'); 5 | var $ = require('../util/preconditions'); 6 | var EventEmitter = require('events').EventEmitter; 7 | var util = require('util'); 8 | var _ = require('lodash'); 9 | 10 | 11 | /** 12 | * block mining utility 13 | * 14 | * Events: 15 | * - block: new valid block was found! :) 16 | */ 17 | var Miner = function(opts) { 18 | $.checkArgument(opts, 'opts is required'); 19 | this.updateCoinbase(opts.coinbase); 20 | this.updatePrevious(opts.previous); 21 | 22 | // for testing 23 | this.time = opts.time; 24 | this.bits = opts.bits; 25 | this.nonce = opts.nonce; 26 | this._updateTemplate(); 27 | 28 | }; 29 | util.inherits(Miner, EventEmitter); 30 | 31 | Miner.prototype._updateTemplate = function() { 32 | $.checkState(this.previous, 'previous must be set'); 33 | $.checkState(this.coinbase, 'coinbase must be set'); 34 | var header = { 35 | height: this.previous.header.height + 1, 36 | prevHash: this.previous.id, 37 | nonce: this.nonce || 0, 38 | time: this.time, 39 | bits: this.bits 40 | }; 41 | this.template = Block.fromCoinbase(this.coinbase, header); 42 | }; 43 | 44 | Miner.prototype.updateCoinbase = function(coinbase) { 45 | $.checkArgument(coinbase, 'coinbase is required'); 46 | $.checkArgument(coinbase instanceof Transaction, 'coinbase needs to be a Transaction'); 47 | $.checkArgument(coinbase.isCoinbase(), 'coinbase needs to be a coinbase Transaction'); 48 | this.coinbase = coinbase; 49 | if (this.previous) { 50 | this._updateTemplate(); 51 | } 52 | }; 53 | 54 | Miner.prototype.updatePrevious = function(previous) { 55 | $.checkArgument(previous, 'previous block is required'); 56 | $.checkArgument(!_.isUndefined(previous.header.height), 'previous needs to be a Block'); 57 | $.checkArgument(!_.isUndefined(previous.id), 'previous needs to be a Block'); 58 | this.previous = previous; 59 | if (this.coinbase) { 60 | this._updateTemplate(); 61 | } 62 | }; 63 | 64 | 65 | Miner.prototype.run = function() { 66 | this.running = true; 67 | //console.log('running towards difficulty ', this.template.header.getTargetDifficulty()); 68 | while (this.running) { 69 | this.work(); 70 | } 71 | }; 72 | 73 | Miner.prototype.stop = function() { 74 | this.running = false; 75 | }; 76 | 77 | // do one unit of work 78 | Miner.prototype.work = function() { 79 | this.template.header.increaseNonce(); 80 | if (this.template.header.validProofOfWork()) { 81 | // console.log(this.template.header.nonce, this.template.header.id); 82 | this.emit('block', this.template); 83 | // we need to receive a new coinbase to continue 84 | this.stop(); 85 | return this.template; 86 | } 87 | }; 88 | 89 | 90 | // inform the miner of a new tip 91 | // block: new blockchain tip 92 | // coinbase (optional): new coinbase to try mining 93 | Miner.prototype.newTip = function(block, coinbase) { 94 | this.updatePrevious(block); 95 | if (coinbase) { 96 | this.updateCoinbase(coinbase); 97 | } 98 | }; 99 | 100 | // add a new transaction to the working block 101 | Miner.prototype.addTransaction = function(tx) { 102 | $.checkArgument(tx instanceof Transaction, 'tx is a required Transaction'); 103 | this.template.addTransaction(tx); 104 | }; 105 | 106 | module.exports = Miner; 107 | -------------------------------------------------------------------------------- /test/networks.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var expect = require('chai').expect; 4 | var should = require('chai').should(); 5 | var bitcore = require('..'); 6 | var networks = bitcore.Networks; 7 | 8 | describe('Networks', function() { 9 | 10 | var customnet; 11 | 12 | it('should contain all Networks', function() { 13 | should.exist(networks.livenet); 14 | should.exist(networks.testnet); 15 | should.exist(networks.defaultNetwork); 16 | }); 17 | 18 | it('should be able to define a custom Network', function() { 19 | var custom = { 20 | name: 'customnet', 21 | alias: 'mynet', 22 | pubkeyhash: 0x10, 23 | privatekey: 0x90, 24 | scripthash: 0x08, 25 | xpubkey: 0x0278b20e, 26 | xprivkey: 0x0278ade4, 27 | networkMagic: 0xe7beb4d4, 28 | port: 20001, 29 | dnsSeeds: [ 30 | 'localhost', 31 | 'mynet.localhost' 32 | ] 33 | }; 34 | networks.add(custom); 35 | customnet = networks.get('customnet'); 36 | for (var key in custom) { 37 | if (key !== 'networkMagic') { 38 | customnet[key].should.equal(custom[key]); 39 | } else { 40 | var expected = new Buffer('e7beb4d4', 'hex'); 41 | customnet[key].should.deep.equal(expected); 42 | } 43 | } 44 | }); 45 | 46 | it('can remove a custom network', function() { 47 | networks.remove(customnet); 48 | var net = networks.get('customnet'); 49 | should.equal(net, undefined); 50 | }); 51 | 52 | it('should not set a network map for an undefined value', function() { 53 | var custom = { 54 | name: 'somenet', 55 | pubkeyhash: 0x13, 56 | privatekey: 0x93, 57 | scripthash: 0x11, 58 | xpubkey: 0x0278b20f, 59 | xprivkey: 0x0278ade5, 60 | networkMagic: 0xe7beb4d5, 61 | port: 20008, 62 | dnsSeeds: [ 63 | 'somenet.localhost' 64 | ] 65 | }; 66 | networks.add(custom); 67 | var network = networks.get(undefined); 68 | should.not.exist(network); 69 | networks.remove(custom); 70 | }); 71 | 72 | var constants = ['name', 'alias', 'pubkeyhash', 'scripthash', 'xpubkey', 'xprivkey']; 73 | 74 | constants.forEach(function(key){ 75 | it('should have constant '+key+' for livenet and testnet', function(){ 76 | networks.testnet.hasOwnProperty(key).should.equal(true); 77 | networks.livenet.hasOwnProperty(key).should.equal(true); 78 | }); 79 | }); 80 | 81 | it('tests only for the specified key', function() { 82 | expect(networks.get(0x6f, 'pubkeyhash')).to.equal(networks.testnet); 83 | expect(networks.get(0x6f, 'privatekey')).to.equal(undefined); 84 | }); 85 | 86 | it('can test for multiple keys', function() { 87 | expect(networks.get(0x6f, ['pubkeyhash', 'scripthash'])).to.equal(networks.testnet); 88 | expect(networks.get(0xc4, ['pubkeyhash', 'scripthash'])).to.equal(networks.testnet); 89 | expect(networks.get(0x6f, ['privatekey', 'port'])).to.equal(undefined); 90 | }); 91 | 92 | it('converts to string using the "name" property', function() { 93 | networks.livenet.toString().should.equal('livenet'); 94 | }); 95 | 96 | it('network object should be immutable', function() { 97 | expect(networks.testnet.name).to.equal('testnet') 98 | var fn = function() { networks.testnet.name = 'livenet' } 99 | expect(fn).to.throw(TypeError) 100 | }); 101 | 102 | }); 103 | -------------------------------------------------------------------------------- /test/encoding/base58.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var should = require('chai').should(); 4 | var bitcore = require('../..'); 5 | var buffer = require('buffer'); 6 | var Base58 = bitcore.encoding.Base58; 7 | 8 | describe('Base58', function() { 9 | var buf = new buffer.Buffer([0, 1, 2, 3, 253, 254, 255]); 10 | var enc = '1W7N4RuG'; 11 | 12 | it('should make an instance with "new"', function() { 13 | var b58 = new Base58(); 14 | should.exist(b58); 15 | }); 16 | 17 | it('validates characters with no false negatives', function() { 18 | Base58.validCharacters( 19 | '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' 20 | ).should.equal(true); 21 | }); 22 | it('validates characters from buffer', function() { 23 | Base58.validCharacters( 24 | new buffer.Buffer('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz') 25 | ).should.equal(true); 26 | }); 27 | 28 | it('some characters are invalid (no false positives)', function() { 29 | Base58.validCharacters('!@#%^$&*()\\').should.equal(false); 30 | }); 31 | 32 | it('should make an instance without "new"', function() { 33 | var b58 = Base58(); 34 | should.exist(b58); 35 | }); 36 | 37 | it('should allow this handy syntax', function() { 38 | Base58(buf).toString().should.equal(enc); 39 | Base58(enc).toBuffer().toString('hex').should.equal(buf.toString('hex')); 40 | }); 41 | 42 | describe('#set', function() { 43 | 44 | it('should set a blank buffer', function() { 45 | Base58().set({ 46 | buf: new buffer.Buffer([]) 47 | }); 48 | }); 49 | 50 | }); 51 | 52 | describe('@encode', function() { 53 | 54 | it('should encode the buffer accurately', function() { 55 | Base58.encode(buf).should.equal(enc); 56 | }); 57 | 58 | it('should throw an error when the Input is not a buffer', function() { 59 | (function() { 60 | Base58.encode('string'); 61 | }).should.throw('Input should be a buffer'); 62 | }); 63 | 64 | }); 65 | 66 | describe('@decode', function() { 67 | 68 | it('should decode this encoded value correctly', function() { 69 | Base58.decode(enc).toString('hex').should.equal(buf.toString('hex')); 70 | }); 71 | 72 | it('should throw an error when Input is not a string', function() { 73 | (function() { 74 | Base58.decode(5); 75 | }).should.throw('Input should be a string'); 76 | }); 77 | 78 | }); 79 | 80 | describe('#fromBuffer', function() { 81 | 82 | it('should not fail', function() { 83 | should.exist(Base58().fromBuffer(buf)); 84 | }); 85 | 86 | it('should set buffer', function() { 87 | var b58 = Base58().fromBuffer(buf); 88 | b58.buf.toString('hex').should.equal(buf.toString('hex')); 89 | }); 90 | 91 | }); 92 | 93 | describe('#fromString', function() { 94 | 95 | it('should convert this known string to a buffer', function() { 96 | Base58().fromString(enc).toBuffer().toString('hex').should.equal(buf.toString('hex')); 97 | }); 98 | 99 | }); 100 | 101 | describe('#toBuffer', function() { 102 | 103 | it('should return the buffer', function() { 104 | var b58 = Base58({ 105 | buf: buf 106 | }); 107 | b58.buf.toString('hex').should.equal(buf.toString('hex')); 108 | }); 109 | 110 | }); 111 | 112 | describe('#toString', function() { 113 | 114 | it('should return the buffer', function() { 115 | var b58 = Base58({ 116 | buf: buf 117 | }); 118 | b58.toString().should.equal(enc); 119 | }); 120 | 121 | }); 122 | 123 | }); 124 | -------------------------------------------------------------------------------- /test/encoding/varint.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var should = require('chai').should(); 4 | var bitcore = require('../..'); 5 | var BN = bitcore.crypto.BN; 6 | var BufferReader = bitcore.encoding.BufferReader; 7 | var BufferWriter = bitcore.encoding.BufferWriter; 8 | var Varint = bitcore.encoding.Varint; 9 | 10 | describe('Varint', function() { 11 | 12 | it('should make a new varint', function() { 13 | var buf = new Buffer('00', 'hex'); 14 | var varint = new Varint(buf); 15 | should.exist(varint); 16 | varint.buf.toString('hex').should.equal('00'); 17 | varint = Varint(buf); 18 | should.exist(varint); 19 | varint.buf.toString('hex').should.equal('00'); 20 | 21 | //various ways to use the constructor 22 | Varint(Varint(0).toBuffer()).toNumber().should.equal(0); 23 | Varint(0).toNumber().should.equal(0); 24 | Varint(new BN(0)).toNumber().should.equal(0); 25 | }); 26 | 27 | describe('#set', function() { 28 | 29 | it('should set a buffer', function() { 30 | var buf = new Buffer('00', 'hex'); 31 | var varint = Varint().set({buf: buf}); 32 | varint.buf.toString('hex').should.equal('00'); 33 | varint.set({}); 34 | varint.buf.toString('hex').should.equal('00'); 35 | }); 36 | 37 | }); 38 | 39 | describe('#fromString', function() { 40 | 41 | it('should set a buffer', function() { 42 | var buf = BufferWriter().writeVarintNum(5).concat(); 43 | var varint = Varint().fromString(buf.toString('hex')); 44 | varint.toNumber().should.equal(5); 45 | }); 46 | 47 | }); 48 | 49 | describe('#toString', function() { 50 | 51 | it('should return a buffer', function() { 52 | var buf = BufferWriter().writeVarintNum(5).concat(); 53 | var varint = Varint().fromString(buf.toString('hex')); 54 | varint.toString().should.equal('05'); 55 | }); 56 | 57 | }); 58 | 59 | describe('#fromBuffer', function() { 60 | 61 | it('should set a buffer', function() { 62 | var buf = BufferWriter().writeVarintNum(5).concat(); 63 | var varint = Varint().fromBuffer(buf); 64 | varint.toNumber().should.equal(5); 65 | }); 66 | 67 | }); 68 | 69 | describe('#fromBufferReader', function() { 70 | 71 | it('should set a buffer reader', function() { 72 | var buf = BufferWriter().writeVarintNum(5).concat(); 73 | var br = BufferReader(buf); 74 | var varint = Varint().fromBufferReader(br); 75 | varint.toNumber().should.equal(5); 76 | }); 77 | 78 | }); 79 | 80 | describe('#fromBN', function() { 81 | 82 | it('should set a number', function() { 83 | var varint = Varint().fromBN(new BN(5)); 84 | varint.toNumber().should.equal(5); 85 | }); 86 | 87 | }); 88 | 89 | describe('#fromNumber', function() { 90 | 91 | it('should set a number', function() { 92 | var varint = Varint().fromNumber(5); 93 | varint.toNumber().should.equal(5); 94 | }); 95 | 96 | }); 97 | 98 | describe('#toBuffer', function() { 99 | 100 | it('should return a buffer', function() { 101 | var buf = BufferWriter().writeVarintNum(5).concat(); 102 | var varint = Varint(buf); 103 | varint.toBuffer().toString('hex').should.equal(buf.toString('hex')); 104 | }); 105 | 106 | }); 107 | 108 | describe('#toBN', function() { 109 | 110 | it('should return a buffer', function() { 111 | var varint = Varint(5); 112 | varint.toBN().toString().should.equal(new BN(5).toString()); 113 | }); 114 | 115 | }); 116 | 117 | describe('#toNumber', function() { 118 | 119 | it('should return a buffer', function() { 120 | var varint = Varint(5); 121 | varint.toNumber().should.equal(5); 122 | }); 123 | 124 | }); 125 | 126 | }); 127 | -------------------------------------------------------------------------------- /test/encoding/base58check.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var should = require('chai').should(); 4 | var bitcore = require('../..'); 5 | var Base58Check = bitcore.encoding.Base58Check; 6 | var Base58 = bitcore.encoding.Base58; 7 | 8 | describe('Base58Check', function() { 9 | var buf = new Buffer([0, 1, 2, 3, 253, 254, 255]); 10 | var enc = '14HV44ipwoaqfg'; 11 | 12 | it('should make an instance with "new"', function() { 13 | var b58 = new Base58Check(); 14 | should.exist(b58); 15 | }); 16 | 17 | it('can validate a serialized string', function() { 18 | var address = '3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy'; 19 | Base58Check.validChecksum(address).should.equal(true); 20 | address = address + 'a'; 21 | Base58Check.validChecksum(address).should.equal(false); 22 | }); 23 | 24 | it('should make an instance without "new"', function() { 25 | var b58 = Base58Check(); 26 | should.exist(b58); 27 | }); 28 | 29 | it('should allow this handy syntax', function() { 30 | Base58Check(buf).toString().should.equal(enc); 31 | Base58Check(enc).toBuffer().toString('hex').should.equal(buf.toString('hex')); 32 | }); 33 | 34 | describe('#set', function() { 35 | 36 | it('should set a buf', function() { 37 | should.exist(Base58Check().set({buf: buf}).buf); 38 | }); 39 | 40 | }); 41 | 42 | describe('@encode', function() { 43 | 44 | it('should encode the buffer accurately', function() { 45 | Base58Check.encode(buf).should.equal(enc); 46 | }); 47 | 48 | it('should throw an error when the input is not a buffer', function() { 49 | (function() { 50 | Base58Check.encode('string'); 51 | }).should.throw('Input must be a buffer'); 52 | }); 53 | 54 | }); 55 | 56 | describe('@decode', function() { 57 | 58 | it('should decode this encoded value correctly', function() { 59 | Base58Check.decode(enc).toString('hex').should.equal(buf.toString('hex')); 60 | }); 61 | 62 | it('should throw an error when input is not a string', function() { 63 | (function() { 64 | Base58Check.decode(5); 65 | }).should.throw('Input must be a string'); 66 | }); 67 | 68 | it('should throw an error when input is too short', function() { 69 | (function() { 70 | Base58Check.decode(enc.slice(0, 1)); 71 | }).should.throw('Input string too short'); 72 | }); 73 | 74 | it('should throw an error when there is a checksum mismatch', function() { 75 | var buf2 = Base58.decode(enc); 76 | buf2[0] = buf2[0] + 1; 77 | var enc2 = Base58.encode(buf2); 78 | (function() { 79 | Base58Check.decode(enc2); 80 | }).should.throw('Checksum mismatch'); 81 | }); 82 | 83 | }); 84 | 85 | describe('#fromBuffer', function() { 86 | 87 | it('should not fail', function() { 88 | should.exist(Base58Check().fromBuffer(buf)); 89 | }); 90 | 91 | it('should set buffer', function() { 92 | var b58 = Base58Check().fromBuffer(buf); 93 | b58.buf.toString('hex').should.equal(buf.toString('hex')); 94 | }); 95 | 96 | }); 97 | 98 | describe('#fromString', function() { 99 | 100 | it('should convert this known string to a buffer', function() { 101 | Base58Check().fromString(enc).toBuffer().toString('hex').should.equal(buf.toString('hex')); 102 | }); 103 | 104 | }); 105 | 106 | describe('#toBuffer', function() { 107 | 108 | it('should return the buffer', function() { 109 | var b58 = Base58Check({buf: buf}); 110 | b58.buf.toString('hex').should.equal(buf.toString('hex')); 111 | }); 112 | 113 | }); 114 | 115 | describe('#toString', function() { 116 | 117 | it('should return the buffer', function() { 118 | var b58 = Base58Check({buf: buf}); 119 | b58.toString().should.equal(enc); 120 | }); 121 | 122 | }); 123 | 124 | }); 125 | -------------------------------------------------------------------------------- /test/block/block.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var chai = require('chai'); 4 | 5 | var bitcore = require('../..'); 6 | var Block = bitcore.Block; 7 | var BlockHeader = bitcore.BlockHeader; 8 | var should = chai.should(); 9 | var Transaction = bitcore.Transaction; 10 | 11 | describe('Block', function() { 12 | 13 | var version = 23; 14 | var prevblockidbuf = '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f'; 15 | var merklerootbuf = '0000000000000000000000000000000000000000000000000000000000000000'; 16 | var time = 123456; 17 | var bits = 0x207fffff; // genesis bitcoin: 486604799 18 | var nonce = 1926; 19 | var height = 300200; 20 | var bh = new BlockHeader({ 21 | version: version, 22 | height: height, 23 | prevHash: prevblockidbuf, 24 | merkleRoot: merklerootbuf, 25 | time: time, 26 | bits: bits, 27 | nonce: nonce 28 | }); 29 | var txs = []; 30 | var testBlock = new Block({ 31 | header: bh, 32 | transactions: txs 33 | }); 34 | 35 | it('should not make an empty block', function() { 36 | (function() { 37 | return new Block(); 38 | }).should.throw('Unrecognized argument for Block'); 39 | }); 40 | 41 | describe('#constructor', function() { 42 | 43 | it('should set these known values', function() { 44 | var b = new Block(testBlock); 45 | should.exist(b.header); 46 | should.exist(b.transactions); 47 | }); 48 | 49 | }); 50 | 51 | 52 | describe('#toBuffer', function() { 53 | 54 | it('should recover a block from this known buffer', function() { 55 | var block = new Block(testBlock); 56 | block.toBuffer().toString('hex') 57 | .should.equal('17000000a894040040e20100ffff7f206fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000000000000000000000000000000000000000000000000000000000000000000000008607000000'); 58 | }); 59 | 60 | }); 61 | describe('#toObject', function() { 62 | 63 | it('roundtrips correctly', function() { 64 | var block = new Block(testBlock); 65 | var obj = block.toObject(); 66 | var block2 = Block.fromObject(obj); 67 | block2.toObject().should.deep.equal(block.toObject()); 68 | }); 69 | 70 | }); 71 | 72 | describe('#hash', function() { 73 | 74 | it('should return the correct hash of the genesis block', function() { 75 | var genesis = Block.genesis; 76 | var genesisTx = genesis.transactions[0]; 77 | 78 | genesisTx.id.should.equal('810ea4614b44d9c9e006393c9a1c42afaa1cd83055b04894d1367fc3b1cc29cd'); 79 | 80 | genesis.validMerkleRoot().should.equal(true); 81 | genesis.header.validProofOfWork().should.equal(true); 82 | genesis.id.should.equal('000006411a7d6bace415af53374feee3adae7ff05f8f899b5829a17c8ef782d9'); 83 | }); 84 | }); 85 | 86 | describe('#inspect', function() { 87 | 88 | it('should return the correct inspect of the genesis block', function() { 89 | var block = new Block(testBlock); 90 | console.log(block.hash); 91 | block.inspect().should.equal(''); 92 | }); 93 | 94 | }); 95 | 96 | describe('#merkleRoot', function() { 97 | 98 | it('should describe as valid merkle root', function() { 99 | var x = new Block(testBlock); 100 | var valid = x.validMerkleRoot(); 101 | valid.should.equal(true); 102 | }); 103 | 104 | it('should describe as invalid merkle root', function() { 105 | var x = new Block(testBlock); 106 | x.transactions.push(new Transaction()); 107 | var valid = x.validMerkleRoot(); 108 | valid.should.equal(false); 109 | }); 110 | 111 | it('should get a null hash merkle root', function() { 112 | var x = new Block(testBlock); 113 | x.transactions = []; // empty the txs 114 | var mr = x.getMerkleRoot(); 115 | mr.should.deep.equal(Block.Values.NULL_HASH); 116 | }); 117 | 118 | }); 119 | 120 | }); 121 | -------------------------------------------------------------------------------- /lib/encoding/bufferwriter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var bufferUtil = require('../util/buffer'); 4 | var assert = require('assert'); 5 | 6 | var BufferWriter = function BufferWriter(obj) { 7 | if (!(this instanceof BufferWriter)) { 8 | return new BufferWriter(obj); 9 | } 10 | if (obj) { 11 | this.set(obj); 12 | } else { 13 | this.bufs = []; 14 | } 15 | }; 16 | 17 | BufferWriter.prototype.set = function(obj) { 18 | this.bufs = obj.bufs || this.bufs || []; 19 | return this; 20 | }; 21 | 22 | BufferWriter.prototype.toBuffer = function() { 23 | return this.concat(); 24 | }; 25 | 26 | BufferWriter.prototype.concat = function() { 27 | return Buffer.concat(this.bufs); 28 | }; 29 | 30 | BufferWriter.prototype.write = function(buf) { 31 | assert(bufferUtil.isBuffer(buf)); 32 | this.bufs.push(buf); 33 | return this; 34 | }; 35 | 36 | BufferWriter.prototype.writeReverse = function(buf) { 37 | assert(bufferUtil.isBuffer(buf)); 38 | this.bufs.push(bufferUtil.reverse(buf)); 39 | return this; 40 | }; 41 | 42 | BufferWriter.prototype.writeUInt8 = function(n) { 43 | var buf = new Buffer(1); 44 | buf.writeUInt8(n, 0); 45 | this.write(buf); 46 | return this; 47 | }; 48 | 49 | BufferWriter.prototype.writeUInt16BE = function(n) { 50 | var buf = new Buffer(2); 51 | buf.writeUInt16BE(n, 0); 52 | this.write(buf); 53 | return this; 54 | }; 55 | 56 | BufferWriter.prototype.writeUInt16LE = function(n) { 57 | var buf = new Buffer(2); 58 | buf.writeUInt16LE(n, 0); 59 | this.write(buf); 60 | return this; 61 | }; 62 | 63 | BufferWriter.prototype.writeUInt32BE = function(n) { 64 | var buf = new Buffer(4); 65 | buf.writeUInt32BE(n, 0); 66 | this.write(buf); 67 | return this; 68 | }; 69 | 70 | BufferWriter.prototype.writeInt32LE = function(n) { 71 | var buf = new Buffer(4); 72 | buf.writeInt32LE(n, 0); 73 | this.write(buf); 74 | return this; 75 | }; 76 | 77 | BufferWriter.prototype.writeUInt32LE = function(n) { 78 | var buf = new Buffer(4); 79 | buf.writeUInt32LE(n, 0); 80 | this.write(buf); 81 | return this; 82 | }; 83 | 84 | BufferWriter.prototype.writeUInt64BEBN = function(bn) { 85 | var buf = bn.toBuffer({ 86 | size: 8 87 | }); 88 | this.write(buf); 89 | return this; 90 | }; 91 | 92 | BufferWriter.prototype.writeUInt64LEBN = function(bn) { 93 | var buf = bn.toBuffer({ 94 | size: 8 95 | }); 96 | var reversebuf = new Buffer(Array.apply(new Array(), buf).reverse()); 97 | this.write(reversebuf); 98 | return this; 99 | }; 100 | 101 | BufferWriter.prototype.writeVarintNum = function(n) { 102 | var buf = BufferWriter.varintBufNum(n); 103 | this.write(buf); 104 | return this; 105 | }; 106 | 107 | BufferWriter.prototype.writeVarintBN = function(bn) { 108 | var buf = BufferWriter.varintBufBN(bn); 109 | this.write(buf); 110 | return this; 111 | }; 112 | 113 | BufferWriter.varintBufNum = function(n) { 114 | var buf = undefined; 115 | if (n < 253) { 116 | buf = new Buffer(1); 117 | buf.writeUInt8(n, 0); 118 | } else if (n < 0x10000) { 119 | buf = new Buffer(1 + 2); 120 | buf.writeUInt8(253, 0); 121 | buf.writeUInt16LE(n, 1); 122 | } else if (n < 0x100000000) { 123 | buf = new Buffer(1 + 4); 124 | buf.writeUInt8(254, 0); 125 | buf.writeUInt32LE(n, 1); 126 | } else { 127 | buf = new Buffer(1 + 8); 128 | buf.writeUInt8(255, 0); 129 | buf.writeInt32LE(n & -1, 1); 130 | buf.writeUInt32LE(Math.floor(n / 0x100000000), 5); 131 | } 132 | return buf; 133 | }; 134 | 135 | BufferWriter.varintBufBN = function(bn) { 136 | var buf = undefined; 137 | var n = bn.toNumber(); 138 | if (n < 253) { 139 | buf = new Buffer(1); 140 | buf.writeUInt8(n, 0); 141 | } else if (n < 0x10000) { 142 | buf = new Buffer(1 + 2); 143 | buf.writeUInt8(253, 0); 144 | buf.writeUInt16LE(n, 1); 145 | } else if (n < 0x100000000) { 146 | buf = new Buffer(1 + 4); 147 | buf.writeUInt8(254, 0); 148 | buf.writeUInt32LE(n, 1); 149 | } else { 150 | var bw = new BufferWriter(); 151 | bw.writeUInt8(255); 152 | bw.writeUInt64LEBN(bn); 153 | var buf = bw.concat(); 154 | } 155 | return buf; 156 | }; 157 | 158 | module.exports = BufferWriter; 159 | -------------------------------------------------------------------------------- /lib/crypto/point.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var BN = require('./bn'); 4 | var BufferUtil = require('../util/buffer'); 5 | var ec = require('elliptic').curves.secp256k1; 6 | var ecPoint = ec.curve.point.bind(ec.curve); 7 | var ecPointFromX = ec.curve.pointFromX.bind(ec.curve); 8 | 9 | /** 10 | * 11 | * Instantiate a valid secp256k1 Point from the X and Y coordinates. 12 | * 13 | * @param {BN|String} x - The X coordinate 14 | * @param {BN|String} y - The Y coordinate 15 | * @link https://github.com/indutny/elliptic 16 | * @augments elliptic.curve.point 17 | * @throws {Error} A validation error if exists 18 | * @returns {Point} An instance of Point 19 | * @constructor 20 | */ 21 | var Point = function Point(x, y, isRed) { 22 | var point = ecPoint(x, y, isRed); 23 | point.validate(); 24 | return point; 25 | }; 26 | 27 | Point.prototype = Object.getPrototypeOf(ec.curve.point()); 28 | 29 | /** 30 | * 31 | * Instantiate a valid secp256k1 Point from only the X coordinate 32 | * 33 | * @param {boolean} odd - If the Y coordinate is odd 34 | * @param {BN|String} x - The X coordinate 35 | * @throws {Error} A validation error if exists 36 | * @returns {Point} An instance of Point 37 | */ 38 | Point.fromX = function fromX(odd, x){ 39 | var point = ecPointFromX(odd, x); 40 | point.validate(); 41 | return point; 42 | }; 43 | 44 | /** 45 | * 46 | * Will return a secp256k1 ECDSA base point. 47 | * 48 | * @link https://en.bitcoin.it/wiki/Secp256k1 49 | * @returns {Point} An instance of the base point. 50 | */ 51 | Point.getG = function getG() { 52 | return ec.curve.g; 53 | }; 54 | 55 | /** 56 | * 57 | * Will return the max of range of valid private keys as governed by the secp256k1 ECDSA standard. 58 | * 59 | * @link https://en.bitcoin.it/wiki/Private_key#Range_of_valid_ECDSA_private_keys 60 | * @returns {BN} A BN instance of the number of points on the curve 61 | */ 62 | Point.getN = function getN() { 63 | return new BN(ec.curve.n.toArray()); 64 | }; 65 | 66 | Point.prototype._getX = Point.prototype.getX; 67 | 68 | /** 69 | * 70 | * Will return the X coordinate of the Point 71 | * 72 | * @returns {BN} A BN instance of the X coordinate 73 | */ 74 | Point.prototype.getX = function getX() { 75 | return new BN(this._getX().toArray()); 76 | }; 77 | 78 | Point.prototype._getY = Point.prototype.getY; 79 | 80 | /** 81 | * 82 | * Will return the Y coordinate of the Point 83 | * 84 | * @returns {BN} A BN instance of the Y coordinate 85 | */ 86 | Point.prototype.getY = function getY() { 87 | return new BN(this._getY().toArray()); 88 | }; 89 | 90 | /** 91 | * 92 | * Will determine if the point is valid 93 | * 94 | * @link https://www.iacr.org/archive/pkc2003/25670211/25670211.pdf 95 | * @param {Point} An instance of Point 96 | * @throws {Error} A validation error if exists 97 | * @returns {Point} An instance of the same Point 98 | */ 99 | Point.prototype.validate = function validate() { 100 | 101 | if (this.isInfinity()){ 102 | throw new Error('Point cannot be equal to Infinity'); 103 | } 104 | 105 | if (this.getX().cmp(BN.Zero) === 0 || this.getY().cmp(BN.Zero) === 0){ 106 | throw new Error('Invalid x,y value for curve, cannot equal 0.'); 107 | } 108 | 109 | var p2 = ecPointFromX(this.getY().isOdd(), this.getX()); 110 | 111 | if (p2.y.cmp(this.y) !== 0) { 112 | throw new Error('Invalid y value for curve.'); 113 | } 114 | 115 | var xValidRange = (this.getX().gt(BN.Minus1) && this.getX().lt(Point.getN())); 116 | var yValidRange = (this.getY().gt(BN.Minus1) && this.getY().lt(Point.getN())); 117 | 118 | if ( !xValidRange || !yValidRange ) { 119 | throw new Error('Point does not lie on the curve'); 120 | } 121 | 122 | //todo: needs test case 123 | if (!(this.mul(Point.getN()).isInfinity())) { 124 | throw new Error('Point times N must be infinity'); 125 | } 126 | 127 | return this; 128 | 129 | }; 130 | 131 | Point.pointToCompressed = function pointToCompressed(point) { 132 | var xbuf = point.getX().toBuffer({size: 32}); 133 | var ybuf = point.getY().toBuffer({size: 32}); 134 | 135 | var prefix; 136 | var odd = ybuf[ybuf.length - 1] % 2; 137 | if (odd) { 138 | prefix = new Buffer([0x03]); 139 | } else { 140 | prefix = new Buffer([0x02]); 141 | } 142 | return BufferUtil.concat([prefix, xbuf]); 143 | }; 144 | 145 | module.exports = Point; 146 | -------------------------------------------------------------------------------- /test/crypto/bn.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var should = require('chai').should(); 4 | var bitcore = require('../..'); 5 | var BN = bitcore.crypto.BN; 6 | 7 | describe('BN', function() { 8 | it('should create a bn', function() { 9 | var bn = new BN(50); 10 | should.exist(bn); 11 | bn.toString().should.equal('50'); 12 | }); 13 | 14 | it('should parse this number', function() { 15 | var bn = new BN(999970000); 16 | bn.toString().should.equal('999970000'); 17 | }); 18 | 19 | it('should parse numbers below and at bn.js internal word size', function() { 20 | var bn = new BN(Math.pow(2, 26) - 1); 21 | bn.toString().should.equal((Math.pow(2, 26) - 1).toString()); 22 | bn = new BN(Math.pow(2, 26)); 23 | bn.toString().should.equal((Math.pow(2, 26)).toString()); 24 | }); 25 | 26 | describe('#add', function() { 27 | 28 | it('should add two small numbers together', function() { 29 | var bn1 = new BN(50); 30 | var bn2 = new BN(75); 31 | var bn3 = bn1.add(bn2); 32 | bn3.toString().should.equal('125'); 33 | }); 34 | 35 | }); 36 | 37 | describe('#sub', function() { 38 | 39 | it('should subtract a small number', function() { 40 | var bn1 = new BN(50); 41 | var bn2 = new BN(25); 42 | var bn3 = bn1.sub(bn2); 43 | bn3.toString().should.equal('25'); 44 | }); 45 | 46 | }); 47 | 48 | describe('#gt', function() { 49 | 50 | it('should say 1 is greater than 0', function() { 51 | var bn1 = new BN(1); 52 | var bn0 = new BN(0); 53 | bn1.gt(bn0).should.equal(true); 54 | }); 55 | 56 | it('should say a big number is greater than a small big number', function() { 57 | var bn1 = new BN('24023452345398529485723980457'); 58 | var bn0 = new BN('34098234283412341234049357'); 59 | bn1.gt(bn0).should.equal(true); 60 | }); 61 | 62 | it('should say a big number is great than a standard number', function() { 63 | var bn1 = new BN('24023452345398529485723980457'); 64 | var bn0 = new BN(5); 65 | bn1.gt(bn0).should.equal(true); 66 | }); 67 | 68 | }); 69 | 70 | describe('to/from ScriptNumBuffer', function() { 71 | [0, 1, 10, 256, 1000, 65536, 65537, -1, -1000, -65536, -65537].forEach(function(n) { 72 | it('rountrips correctly for ' + n, function() { 73 | BN.fromScriptNumBuffer(new BN(n).toScriptNumBuffer()).toNumber().should.equal(n); 74 | }); 75 | }); 76 | }); 77 | 78 | describe('#fromString', function() { 79 | it('should make BN from a string', function() { 80 | BN.fromString('5').toString().should.equal('5'); 81 | }); 82 | }); 83 | 84 | describe('#toString', function() { 85 | it('should make a string', function() { 86 | new BN(5).toString().should.equal('5'); 87 | }); 88 | }); 89 | 90 | describe('@fromBuffer', function() { 91 | 92 | it('should work with big endian', function() { 93 | var bn = BN.fromBuffer(new Buffer('0001', 'hex'), { 94 | endian: 'big' 95 | }); 96 | bn.toString().should.equal('1'); 97 | }); 98 | 99 | it('should work with big endian 256', function() { 100 | var bn = BN.fromBuffer(new Buffer('0100', 'hex'), { 101 | endian: 'big' 102 | }); 103 | bn.toString().should.equal('256'); 104 | }); 105 | 106 | it('should work with little endian if we specify the size', function() { 107 | var bn = BN.fromBuffer(new Buffer('0100', 'hex'), { 108 | size: 2, 109 | endian: 'little' 110 | }); 111 | bn.toString().should.equal('1'); 112 | }); 113 | 114 | }); 115 | 116 | describe('#toBuffer', function() { 117 | 118 | it('should create a 4 byte buffer', function() { 119 | var bn = new BN(1); 120 | bn.toBuffer({ 121 | size: 4 122 | }).toString('hex').should.equal('00000001'); 123 | }); 124 | 125 | it('should create a 4 byte buffer in little endian', function() { 126 | var bn = new BN(1); 127 | bn.toBuffer({ 128 | size: 4, 129 | endian: 'little' 130 | }).toString('hex').should.equal('01000000'); 131 | }); 132 | 133 | it('should create a 2 byte buffer even if you ask for a 1 byte', function() { 134 | var bn = new BN('ff00', 16); 135 | bn.toBuffer({ 136 | size: 1 137 | }).toString('hex').should.equal('ff00'); 138 | }); 139 | 140 | it('should create a 4 byte buffer even if you ask for a 1 byte', function() { 141 | var bn = new BN('ffffff00', 16); 142 | bn.toBuffer({ 143 | size: 4 144 | }).toString('hex').should.equal('ffffff00'); 145 | }); 146 | 147 | }); 148 | 149 | }); 150 | -------------------------------------------------------------------------------- /lib/networks.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var _ = require('lodash'); 3 | 4 | var BufferUtil = require('./util/buffer'); 5 | var JSUtil = require('./util/js'); 6 | var networks = []; 7 | var networkMaps = {}; 8 | 9 | /** 10 | * A network is merely a map containing values that correspond to version 11 | * numbers for each bitcoin network. Currently only supporting "livenet" 12 | * (a.k.a. "mainnet") and "testnet". 13 | * @constructor 14 | */ 15 | function Network() {} 16 | 17 | Network.prototype.toString = function toString() { 18 | return this.name; 19 | }; 20 | 21 | /** 22 | * @function 23 | * @member Networks#get 24 | * Retrieves the network associated with a magic number or string. 25 | * @param {string|number|Network} arg 26 | * @param {string|Array} keys - if set, only check if the magic number associated with this name matches 27 | * @return Network 28 | */ 29 | function get(arg, keys) { 30 | if (~networks.indexOf(arg)) { 31 | return arg; 32 | } 33 | if (keys) { 34 | if (!_.isArray(keys)) { 35 | keys = [keys]; 36 | } 37 | var containsArg = function(key) { 38 | return networks[index][key] === arg; 39 | }; 40 | for (var index in networks) { 41 | if (_.any(keys, containsArg)) { 42 | return networks[index]; 43 | } 44 | } 45 | return undefined; 46 | } 47 | return networkMaps[arg]; 48 | } 49 | 50 | /** 51 | * @function 52 | * @member Networks#add 53 | * Will add a custom Network 54 | * @param {Object} data 55 | * @param {string} data.name - The name of the network 56 | * @param {string} data.alias - The aliased name of the network 57 | * @param {Number} data.pubkeyhash - The publickey hash prefix 58 | * @param {Number} data.privatekey - The privatekey prefix 59 | * @param {Number} data.scripthash - The scripthash prefix 60 | * @param {Number} data.xpubkey - The extended public key magic 61 | * @param {Number} data.xprivkey - The extended private key magic 62 | * @param {Number} data.networkMagic - The network magic number 63 | * @param {Number} data.port - The network port 64 | * @param {Array} data.dnsSeeds - An array of dns seeds 65 | * @return Network 66 | */ 67 | function addNetwork(data) { 68 | 69 | var network = new Network(); 70 | 71 | JSUtil.defineImmutable(network, { 72 | name: data.name, 73 | alias: data.alias, 74 | pubkeyhash: data.pubkeyhash, 75 | privatekey: data.privatekey, 76 | scripthash: data.scripthash, 77 | xpubkey: data.xpubkey, 78 | xprivkey: data.xprivkey, 79 | networkMagic: BufferUtil.integerAsBuffer(data.networkMagic), 80 | port: data.port, 81 | dnsSeeds: data.dnsSeeds 82 | }); 83 | 84 | _.each(network, function(value) { 85 | if (!_.isUndefined(value) && !_.isObject(value)) { 86 | networkMaps[value] = network; 87 | } 88 | }); 89 | 90 | networks.push(network); 91 | 92 | return network; 93 | 94 | } 95 | 96 | /** 97 | * @function 98 | * @member Networks#remove 99 | * Will remove a custom network 100 | * @param {Network} network 101 | */ 102 | function removeNetwork(network) { 103 | for (var i = 0; i < networks.length; i++) { 104 | if (networks[i] === network) { 105 | networks.splice(i, 1); 106 | } 107 | } 108 | for (var key in networkMaps) { 109 | if (networkMaps[key] === network) { 110 | delete networkMaps[key]; 111 | } 112 | } 113 | } 114 | 115 | addNetwork({ 116 | name: 'livenet', 117 | alias: 'mainnet', 118 | pubkeyhash: 0x00, 119 | privatekey: 0x80, 120 | scripthash: 0x05, 121 | xpubkey: 0x0488b21e, 122 | xprivkey: 0x0488ade4, 123 | networkMagic: 0xf9beb4d9, 124 | port: 8333, 125 | dnsSeeds: [ 126 | 'seed.bitcoin.sipa.be', 127 | 'dnsseed.bluematt.me', 128 | 'dnsseed.bitcoin.dashjr.org', 129 | 'seed.bitcoinstats.com', 130 | 'seed.bitnodes.io', 131 | 'bitseed.xf2.org' 132 | ] 133 | }); 134 | 135 | addNetwork({ 136 | name: 'testnet', 137 | alias: 'testnet', 138 | pubkeyhash: 0x6f, 139 | privatekey: 0xef, 140 | scripthash: 0xc4, 141 | xpubkey: 0x043587cf, 142 | xprivkey: 0x04358394, 143 | networkMagic: 0x0b110907, 144 | port: 18333, 145 | dnsSeeds: [ 146 | 'testnet-seed.bitcoin.petertodd.org', 147 | 'testnet-seed.bluematt.me', 148 | 'testnet-seed.alexykot.me', 149 | 'testnet-seed.bitcoin.schildbach.de' 150 | ], 151 | }); 152 | 153 | /** 154 | * @instance 155 | * @member Networks#livenet 156 | */ 157 | var livenet = get('livenet'); 158 | 159 | /** 160 | * @instance 161 | * @member Networks#testnet 162 | */ 163 | var testnet = get('testnet'); 164 | 165 | /** 166 | * @namespace Networks 167 | */ 168 | module.exports = { 169 | add: addNetwork, 170 | remove: removeNetwork, 171 | defaultNetwork: livenet, 172 | livenet: livenet, 173 | mainnet: livenet, 174 | testnet: testnet, 175 | get: get 176 | }; 177 | -------------------------------------------------------------------------------- /test/blockchain.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var chai = require('chai'); 4 | var should = chai.should(); 5 | 6 | var bitcore = require('../'); 7 | var Block = bitcore.Block; 8 | var Transaction = bitcore.Transaction; 9 | var PrivateKey = bitcore.PrivateKey; 10 | var Blockchain = bitcore.Blockchain; 11 | var Miner = bitcore.Miner; 12 | 13 | describe('Blockchain', function() { 14 | 15 | it('Creates a blockchain with the genesis block', function() { 16 | var blockchain = new Blockchain(); 17 | blockchain.proposeNewBlock(Block.genesis); 18 | blockchain.tip.should.equal(Block.genesis.hash); 19 | }); 20 | 21 | var privKey = new PrivateKey('ecf4fd8e3c6b7cebeb028ceada16a24e266869e352e80971438bbb03db1c54e4'); 22 | var mineBlock = function(blockchain, transactions, color, callback) { 23 | var opts = {}; 24 | opts.coinbase = new Transaction() 25 | .at(0, blockchain.getTipBlock().height + 1) 26 | .to(privKey.publicKey) 27 | .colored(color || 0xff0000ff); 28 | opts.previous = blockchain.getTipBlock(); 29 | opts.time = 1432594281; 30 | var miner = new Miner(opts); 31 | transactions.map(function(tx) { 32 | miner.addTransaction(tx); 33 | }); 34 | miner.on('block', callback); 35 | miner.run(); 36 | }; 37 | 38 | it('Makes a simple reorg: Append to tip', function(cb) { 39 | var blockchain = new Blockchain(); 40 | blockchain.proposeNewBlock(Block.genesis); 41 | 42 | mineBlock(blockchain, [], null, function(block) { 43 | blockchain.proposeNewBlock(block); 44 | blockchain.tip.should.equal(block.hash); 45 | cb(); 46 | }); 47 | 48 | }); 49 | 50 | describe('transaction validation', function() { 51 | 52 | it('validates a transaction that spends a previous coinbase', function(cb) { 53 | var blockchain = new Blockchain(); 54 | blockchain.proposeNewBlock(Block.genesis); 55 | 56 | var block1, block2; 57 | mineBlock(blockchain, [], null, function(block) { 58 | block1 = block; 59 | blockchain.proposeNewBlock(block); 60 | 61 | var tx = new Transaction() 62 | .from(block1.transactions[0]) 63 | .to(privKey.publicKey) 64 | .colored(0x00fff0ff) 65 | .sign(privKey); 66 | 67 | mineBlock(blockchain, [tx], null, function(block) { 68 | block2 = block; 69 | blockchain.proposeNewBlock(block); 70 | blockchain.tip.should.equal(block2.hash); 71 | 72 | cb(); 73 | }); 74 | }) 75 | 76 | }); 77 | 78 | it('doesnt allow a transaction with invalid signature', function(cb) { 79 | var blockchain = new Blockchain(); 80 | blockchain.proposeNewBlock(Block.genesis); 81 | 82 | var block1, block2; 83 | mineBlock(blockchain, [], null, function(block) { 84 | block1 = block; 85 | blockchain.proposeNewBlock(block); 86 | 87 | var tx = new Transaction() 88 | .from(block1.transactions[0]) 89 | .to(privKey.publicKey) 90 | .colored(0x00fff0ff) 91 | .sign(privKey); 92 | 93 | // Corrupt signature 94 | tx.signature.r.words[1]++; 95 | 96 | mineBlock(blockchain, [tx], null, function(block) { 97 | block2 = block; 98 | (function() { 99 | blockchain.proposeNewBlock(block); 100 | }).should.throw('Invalid Argument'); 101 | 102 | cb(); 103 | }); 104 | }) 105 | 106 | }); 107 | }); 108 | 109 | describe('reorg', function() { 110 | it('Case: 1 block back; two forward; "move to niece"', function(callback) { 111 | // Genesis -> A 112 | // \ 113 | // `----> B --> C 114 | // First propose block A, then propose block B (no change), then propose C 115 | var blockchain = new Blockchain(); 116 | blockchain.proposeNewBlock(Block.genesis); 117 | 118 | var blockchain2 = new Blockchain(); 119 | blockchain2.proposeNewBlock(Block.genesis); 120 | 121 | mineBlock(blockchain, [], null, function(A) { 122 | blockchain.proposeNewBlock(A); 123 | 124 | var tx = new Transaction() 125 | .from(A.transactions[0]) 126 | .to(privKey.publicKey) 127 | .colored(0x00fff0ff) 128 | .sign(privKey); 129 | 130 | mineBlock(blockchain2, [], 0xFFFFFFFF, function(B) { 131 | blockchain2.proposeNewBlock(B); 132 | 133 | mineBlock(blockchain2, [], null, function(C) { 134 | 135 | blockchain.proposeNewBlock(B); 136 | blockchain.tip.should.equal(A.hash); 137 | blockchain.proposeNewBlock(C); 138 | blockchain.tip.should.equal(C.hash); 139 | 140 | blockchain.pixels['0_1'].should.equal(B.transactions[0]); 141 | blockchain.pixels['0_2'].should.equal(C.transactions[0]); 142 | 143 | callback(); 144 | }); 145 | }); 146 | }); 147 | }); 148 | }); 149 | }); 150 | -------------------------------------------------------------------------------- /lib/util/buffer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var buffer = require('buffer'); 4 | var assert = require('assert'); 5 | 6 | var js = require('./js'); 7 | var $ = require('./preconditions'); 8 | 9 | function equals(a, b) { 10 | if (a.length !== b.length) { 11 | return false; 12 | } 13 | var length = a.length; 14 | for (var i = 0; i < length; i++) { 15 | if (a[i] !== b[i]) { 16 | return false; 17 | } 18 | } 19 | return true; 20 | } 21 | 22 | module.exports = { 23 | /** 24 | * Fill a buffer with a value. 25 | * 26 | * @param {Buffer} buffer 27 | * @param {number} value 28 | * @return {Buffer} 29 | */ 30 | fill: function fill(buffer, value) { 31 | $.checkArgumentType(buffer, 'Buffer', 'buffer'); 32 | $.checkArgumentType(value, 'number', 'value'); 33 | var length = buffer.length; 34 | for (var i = 0; i < length; i++) { 35 | buffer[i] = value; 36 | } 37 | return buffer; 38 | }, 39 | 40 | /** 41 | * Return a copy of a buffer 42 | * 43 | * @param {Buffer} original 44 | * @return {Buffer} 45 | */ 46 | copy: function(original) { 47 | var buffer = new Buffer(original.length); 48 | original.copy(buffer); 49 | return buffer; 50 | }, 51 | 52 | /** 53 | * Returns true if the given argument is an instance of a buffer. Tests for 54 | * both node's Buffer and Uint8Array 55 | * 56 | * @param {*} arg 57 | * @return {boolean} 58 | */ 59 | isBuffer: function isBuffer(arg) { 60 | return buffer.Buffer.isBuffer(arg) || arg instanceof Uint8Array; 61 | }, 62 | 63 | /** 64 | * Returns a zero-filled byte array 65 | * 66 | * @param {number} bytes 67 | * @return {Buffer} 68 | */ 69 | emptyBuffer: function emptyBuffer(bytes) { 70 | $.checkArgumentType(bytes, 'number', 'bytes'); 71 | var result = new buffer.Buffer(bytes); 72 | for (var i = 0; i < bytes; i++) { 73 | result.write('\0', i); 74 | } 75 | return result; 76 | }, 77 | 78 | /** 79 | * Concatenates a buffer 80 | * 81 | * Shortcut for buffer.Buffer.concat 82 | */ 83 | concat: buffer.Buffer.concat, 84 | 85 | equals: equals, 86 | equal: equals, 87 | 88 | /** 89 | * Transforms a number from 0 to 255 into a Buffer of size 1 with that value 90 | * 91 | * @param {number} integer 92 | * @return {Buffer} 93 | */ 94 | integerAsSingleByteBuffer: function integerAsSingleByteBuffer(integer) { 95 | $.checkArgumentType(integer, 'number', 'integer'); 96 | return new buffer.Buffer([integer & 0xff]); 97 | }, 98 | 99 | /** 100 | * Transform a 4-byte integer into a Buffer of length 4. 101 | * 102 | * @param {number} integer 103 | * @return {Buffer} 104 | */ 105 | integerAsBuffer: function integerAsBuffer(integer) { 106 | $.checkArgumentType(integer, 'number', 'integer'); 107 | var bytes = []; 108 | bytes.push((integer >> 24) & 0xff); 109 | bytes.push((integer >> 16) & 0xff); 110 | bytes.push((integer >> 8) & 0xff); 111 | bytes.push(integer & 0xff); 112 | return new Buffer(bytes); 113 | }, 114 | 115 | /** 116 | * Transform the first 4 values of a Buffer into a number, in little endian encoding 117 | * 118 | * @param {Buffer} buffer 119 | * @return {number} 120 | */ 121 | integerFromBuffer: function integerFromBuffer(buffer) { 122 | $.checkArgumentType(buffer, 'Buffer', 'buffer'); 123 | return buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3]; 124 | }, 125 | 126 | /** 127 | * Transforms the first byte of an array into a number ranging from -128 to 127 128 | * @param {Buffer} buffer 129 | * @return {number} 130 | */ 131 | integerFromSingleByteBuffer: function integerFromBuffer(buffer) { 132 | $.checkArgumentType(buffer, 'Buffer', 'buffer'); 133 | return buffer[0]; 134 | }, 135 | 136 | /** 137 | * Transforms a buffer into a string with a number in hexa representation 138 | * 139 | * Shorthand for buffer.toString('hex') 140 | * 141 | * @param {Buffer} buffer 142 | * @return {string} 143 | */ 144 | bufferToHex: function bufferToHex(buffer) { 145 | $.checkArgumentType(buffer, 'Buffer', 'buffer'); 146 | return buffer.toString('hex'); 147 | }, 148 | 149 | /** 150 | * Reverse a buffer 151 | * @param {Buffer} param 152 | * @return {Buffer} 153 | */ 154 | reverse: function reverse(param) { 155 | $.checkArgumentType(param, 'Buffer', 'param'); 156 | var ret = new buffer.Buffer(param.length); 157 | for (var i = 0; i < param.length; i++) { 158 | ret[i] = param[param.length - i - 1]; 159 | } 160 | return ret; 161 | }, 162 | 163 | /** 164 | * Transforms an hexa encoded string into a Buffer with binary values 165 | * 166 | * Shorthand for Buffer(string, 'hex') 167 | * 168 | * @param {string} string 169 | * @return {Buffer} 170 | */ 171 | hexToBuffer: function hexToBuffer(string) { 172 | assert(js.isHexa(string)); 173 | return new buffer.Buffer(string, 'hex'); 174 | } 175 | }; 176 | 177 | module.exports.NULL_HASH = module.exports.fill(new Buffer(32), 0); 178 | module.exports.EMPTY_BUFFER = new Buffer(0); 179 | -------------------------------------------------------------------------------- /lib/encoding/bufferreader.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash'); 4 | var $ = require('../util/preconditions'); 5 | var BufferUtil = require('../util/buffer'); 6 | var BN = require('../crypto/bn'); 7 | 8 | var BufferReader = function BufferReader(buf) { 9 | if (!(this instanceof BufferReader)) { 10 | return new BufferReader(buf); 11 | } 12 | if (_.isUndefined(buf)) { 13 | return; 14 | } 15 | if (Buffer.isBuffer(buf)) { 16 | this.set({ 17 | buf: buf 18 | }); 19 | } else if (_.isString(buf)) { 20 | this.set({ 21 | buf: new Buffer(buf, 'hex'), 22 | }); 23 | } else if (_.isObject(buf)) { 24 | var obj = buf; 25 | this.set(obj); 26 | } else { 27 | throw new TypeError('Unrecognized argument for BufferReader'); 28 | } 29 | }; 30 | 31 | BufferReader.prototype.set = function(obj) { 32 | this.buf = obj.buf || this.buf || undefined; 33 | this.pos = obj.pos || this.pos || 0; 34 | return this; 35 | }; 36 | 37 | BufferReader.prototype.eof = function() { 38 | return this.pos >= this.buf.length; 39 | }; 40 | 41 | BufferReader.prototype.finished = BufferReader.prototype.eof; 42 | 43 | BufferReader.prototype.read = function(len) { 44 | $.checkArgument(!_.isUndefined(len), 'Must specify a length'); 45 | var buf = this.buf.slice(this.pos, this.pos + len); 46 | this.pos = this.pos + len; 47 | return buf; 48 | }; 49 | 50 | BufferReader.prototype.readAll = function() { 51 | var buf = this.buf.slice(this.pos, this.buf.length); 52 | this.pos = this.buf.length; 53 | return buf; 54 | }; 55 | 56 | BufferReader.prototype.readUInt8 = function() { 57 | var val = this.buf.readUInt8(this.pos); 58 | this.pos = this.pos + 1; 59 | return val; 60 | }; 61 | 62 | BufferReader.prototype.readUInt16BE = function() { 63 | var val = this.buf.readUInt16BE(this.pos); 64 | this.pos = this.pos + 2; 65 | return val; 66 | }; 67 | 68 | BufferReader.prototype.readUInt16LE = function() { 69 | var val = this.buf.readUInt16LE(this.pos); 70 | this.pos = this.pos + 2; 71 | return val; 72 | }; 73 | 74 | BufferReader.prototype.readUInt32BE = function() { 75 | var val = this.buf.readUInt32BE(this.pos); 76 | this.pos = this.pos + 4; 77 | return val; 78 | }; 79 | 80 | BufferReader.prototype.readInt32LE = function() { 81 | var val = this.buf.readInt32LE(this.pos); 82 | this.pos = this.pos + 4; 83 | return val; 84 | }; 85 | 86 | BufferReader.prototype.readUInt32LE = function() { 87 | var val = this.buf.readUInt32LE(this.pos); 88 | this.pos = this.pos + 4; 89 | return val; 90 | }; 91 | 92 | BufferReader.prototype.readUInt64BEBN = function() { 93 | var buf = this.buf.slice(this.pos, this.pos + 8); 94 | var bn = BN.fromBuffer(buf); 95 | this.pos = this.pos + 8; 96 | return bn; 97 | }; 98 | 99 | BufferReader.prototype.readUInt64LEBN = function() { 100 | var buf = this.buf.slice(this.pos, this.pos + 8); 101 | var reversebuf = BufferReader({ 102 | buf: buf 103 | }).readReverse(); 104 | var bn = BN.fromBuffer(reversebuf); 105 | this.pos = this.pos + 8; 106 | return bn; 107 | }; 108 | 109 | BufferReader.prototype.readVarintNum = function() { 110 | var first = this.readUInt8(); 111 | switch (first) { 112 | case 0xFD: 113 | return this.readUInt16LE(); 114 | case 0xFE: 115 | return this.readUInt32LE(); 116 | case 0xFF: 117 | var bn = this.readUInt64LEBN(); 118 | var n = bn.toNumber(); 119 | if (n <= Math.pow(2, 53)) { 120 | return n; 121 | } else { 122 | throw new Error('number too large to retain precision - use readVarintBN'); 123 | } 124 | break; 125 | default: 126 | return first; 127 | } 128 | }; 129 | 130 | /** 131 | * reads a length prepended buffer 132 | */ 133 | BufferReader.prototype.readVarLengthBuffer = function() { 134 | var len = this.readVarintNum(); 135 | var buf = this.read(len); 136 | $.checkState(buf.length === len, 'Invalid length while reading varlength buffer. ' + 137 | 'Expected to read: ' + len + ' and read ' + buf.length); 138 | return buf; 139 | }; 140 | 141 | BufferReader.prototype.readVarintBuf = function() { 142 | var first = this.buf.readUInt8(this.pos); 143 | switch (first) { 144 | case 0xFD: 145 | return this.read(1 + 2); 146 | case 0xFE: 147 | return this.read(1 + 4); 148 | case 0xFF: 149 | return this.read(1 + 8); 150 | default: 151 | return this.read(1); 152 | } 153 | }; 154 | 155 | BufferReader.prototype.readVarintBN = function() { 156 | var first = this.readUInt8(); 157 | switch (first) { 158 | case 0xFD: 159 | return new BN(this.readUInt16LE()); 160 | case 0xFE: 161 | return new BN(this.readUInt32LE()); 162 | case 0xFF: 163 | return this.readUInt64LEBN(); 164 | default: 165 | return new BN(first); 166 | } 167 | }; 168 | 169 | BufferReader.prototype.reverse = function() { 170 | var buf = new Buffer(this.buf.length); 171 | for (var i = 0; i < buf.length; i++) { 172 | buf[i] = this.buf[this.buf.length - 1 - i]; 173 | } 174 | this.buf = buf; 175 | return this; 176 | }; 177 | 178 | BufferReader.prototype.readReverse = function(len) { 179 | if (_.isUndefined(len)) { 180 | len = this.buf.length; 181 | } 182 | var buf = this.buf.slice(this.pos, this.pos + len); 183 | this.pos = this.pos + len; 184 | return BufferUtil.reverse(buf); 185 | }; 186 | 187 | module.exports = BufferReader; 188 | -------------------------------------------------------------------------------- /test/crypto/hash.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('chai').should(); 4 | var bitcore = require('../..'); 5 | var Hash = bitcore.crypto.Hash; 6 | 7 | describe('Hash', function() { 8 | var buf = new Buffer([0, 1, 2, 3, 253, 254, 255]); 9 | var str = 'test string'; 10 | 11 | describe('@sha1', function() { 12 | 13 | it('calculates the hash of this buffer correctly', function() { 14 | var hash = Hash.sha1(buf); 15 | hash.toString('hex').should.equal('de69b8a4a5604d0486e6420db81e39eb464a17b2'); 16 | hash = Hash.sha1(new Buffer(0)); 17 | hash.toString('hex').should.equal('da39a3ee5e6b4b0d3255bfef95601890afd80709'); 18 | }); 19 | 20 | it('throws an error when the input is not a buffer', function() { 21 | Hash.sha1.bind(Hash, str).should.throw('Invalid Argument'); 22 | }); 23 | 24 | }); 25 | 26 | describe('#sha256', function() { 27 | 28 | it('calculates the hash of this buffer correctly', function() { 29 | var hash = Hash.sha256(buf); 30 | hash.toString('hex').should.equal('6f2c7b22fd1626998287b3636089087961091de80311b9279c4033ec678a83e8'); 31 | }); 32 | 33 | it('fails when the input is not a buffer', function() { 34 | Hash.sha256.bind(Hash, str).should.throw('Invalid Argument'); 35 | }); 36 | 37 | }); 38 | 39 | describe('#sha256hmac', function() { 40 | 41 | it('computes this known big key correctly', function() { 42 | var key = new Buffer('b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad' + 43 | 'b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad' + 44 | 'b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad' + 45 | 'b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad'); 46 | var data = new Buffer(''); 47 | Hash.sha256hmac(data, key).toString('hex') 48 | .should.equal('fb1f87218671f1c0c4593a88498e02b6dfe8afd814c1729e89a1f1f6600faa23'); 49 | }); 50 | 51 | it('computes this known empty test vector correctly', function() { 52 | var key = new Buffer(''); 53 | var data = new Buffer(''); 54 | Hash.sha256hmac(data, key).toString('hex') 55 | .should.equal('b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad'); 56 | }); 57 | 58 | it('computes this known non-empty test vector correctly', function() { 59 | var key = new Buffer('key'); 60 | var data = new Buffer('The quick brown fox jumps over the lazy dog'); 61 | Hash.sha256hmac(data, key).toString('hex') 62 | .should.equal('f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8'); 63 | }); 64 | 65 | }); 66 | 67 | describe('#sha256sha256', function() { 68 | 69 | it('calculates the hash of this buffer correctly', function() { 70 | var hash = Hash.sha256sha256(buf); 71 | hash.toString('hex').should.equal('be586c8b20dee549bdd66018c7a79e2b67bb88b7c7d428fa4c970976d2bec5ba'); 72 | }); 73 | 74 | it('fails when the input is not a buffer', function() { 75 | Hash.sha256sha256.bind(Hash, str).should.throw('Invalid Argument'); 76 | }); 77 | 78 | }); 79 | 80 | describe('#sha256ripemd160', function() { 81 | 82 | it('calculates the hash of this buffer correctly', function() { 83 | var hash = Hash.sha256ripemd160(buf); 84 | hash.toString('hex').should.equal('7322e2bd8535e476c092934e16a6169ca9b707ec'); 85 | }); 86 | 87 | it('fails when the input is not a buffer', function() { 88 | Hash.sha256ripemd160.bind(Hash, str).should.throw('Invalid Argument'); 89 | }); 90 | 91 | }); 92 | 93 | describe('#ripemd160', function() { 94 | 95 | it('calculates the hash of this buffer correctly', function() { 96 | var hash = Hash.ripemd160(buf); 97 | hash.toString('hex').should.equal('fa0f4565ff776fee0034c713cbf48b5ec06b7f5c'); 98 | }); 99 | 100 | it('fails when the input is not a buffer', function() { 101 | Hash.ripemd160.bind(Hash, str).should.throw('Invalid Argument'); 102 | }); 103 | 104 | }); 105 | 106 | describe('#sha512', function() { 107 | 108 | it('calculates the hash of this buffer correctly', function() { 109 | var hash = Hash.sha512(buf); 110 | hash.toString('hex') 111 | .should.equal('c0530aa32048f4904ae162bc14b9eb535eab6c465e960130005fedd' + 112 | 'b71613e7d62aea75f7d3333ba06e805fc8e45681454524e3f8050969fe5a5f7f2392e31d0'); 113 | }); 114 | 115 | it('fails when the input is not a buffer', function() { 116 | Hash.sha512.bind(Hash, str).should.throw('Invalid Argument'); 117 | }); 118 | 119 | }); 120 | 121 | describe('#sha512hmac', function() { 122 | 123 | it('calculates this known empty test vector correctly', function() { 124 | var hex = 'b936cee86c9f87aa5d3c6f2e84cb5a4239a5fe50480a6ec66b70ab5b1f4a' + 125 | 'c6730c6c515421b327ec1d69402e53dfb49ad7381eb067b338fd7b0cb22247225d47'; 126 | Hash.sha512hmac(new Buffer([]), new Buffer([])).toString('hex').should.equal(hex); 127 | }); 128 | 129 | it('calculates this known non-empty test vector correctly', function() { 130 | var hex = 'c40bd7c15aa493b309c940e08a73ffbd28b2e4cb729eb94480d727e4df577' + 131 | 'b13cc403a78e6150d83595f3b17c4cc331f12ca5952691de3735a63c1d4c69a2bac'; 132 | var data = new Buffer('test1'); 133 | var key = new Buffer('test2'); 134 | Hash.sha512hmac(data, key).toString('hex').should.equal(hex); 135 | }); 136 | 137 | }); 138 | 139 | }); 140 | -------------------------------------------------------------------------------- /test/encoding/bufferwriter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var bitcore = require('../..'); 4 | var should = require('chai').should(); 5 | var BufferWriter = bitcore.encoding.BufferWriter; 6 | var BufferReader = bitcore.encoding.BufferReader; 7 | var BN = bitcore.crypto.BN; 8 | 9 | describe('BufferWriter', function() { 10 | 11 | it('should create a new buffer writer', function() { 12 | var bw = new BufferWriter(); 13 | should.exist(bw); 14 | }); 15 | 16 | describe('#set', function() { 17 | 18 | it('set bufs', function() { 19 | var buf1 = new Buffer([0]); 20 | var buf2 = new Buffer([1]); 21 | var bw = new BufferWriter().set({bufs: [buf1, buf2]}); 22 | bw.concat().toString('hex').should.equal('0001'); 23 | }); 24 | 25 | }); 26 | 27 | describe('#toBuffer', function() { 28 | 29 | it('should concat these two bufs', function() { 30 | var buf1 = new Buffer([0]); 31 | var buf2 = new Buffer([1]); 32 | var bw = new BufferWriter({bufs: [buf1, buf2]}); 33 | bw.toBuffer().toString('hex').should.equal('0001'); 34 | }); 35 | 36 | }); 37 | 38 | describe('#concat', function() { 39 | 40 | it('should concat these two bufs', function() { 41 | var buf1 = new Buffer([0]); 42 | var buf2 = new Buffer([1]); 43 | var bw = new BufferWriter({bufs: [buf1, buf2]}); 44 | bw.concat().toString('hex').should.equal('0001'); 45 | }); 46 | 47 | }); 48 | 49 | describe('#write', function() { 50 | 51 | it('should write a buffer', function() { 52 | var buf = new Buffer([0]); 53 | var bw = new BufferWriter(); 54 | bw.write(buf); 55 | bw.concat().toString('hex').should.equal('00'); 56 | }); 57 | 58 | }); 59 | 60 | describe('#writeUInt8', function() { 61 | 62 | it('should write 1', function() { 63 | var bw = new BufferWriter(); 64 | bw.writeUInt8(1).concat().toString('hex').should.equal('01'); 65 | }); 66 | 67 | }); 68 | 69 | describe('#writeUInt16BE', function() { 70 | 71 | it('should write 1', function() { 72 | var bw = new BufferWriter(); 73 | bw.writeUInt16BE(1).concat().toString('hex').should.equal('0001'); 74 | }); 75 | 76 | }); 77 | 78 | describe('#writeUInt16LE', function() { 79 | 80 | it('should write 1', function() { 81 | var bw = new BufferWriter(); 82 | bw.writeUInt16LE(1).concat().toString('hex').should.equal('0100'); 83 | }); 84 | 85 | }); 86 | 87 | describe('#writeUInt32BE', function() { 88 | 89 | it('should write 1', function() { 90 | var bw = new BufferWriter(); 91 | bw.writeUInt32BE(1).concat().toString('hex').should.equal('00000001'); 92 | }); 93 | 94 | }); 95 | 96 | describe('#writeUInt32LE', function() { 97 | 98 | it('should write 1', function() { 99 | var bw = new BufferWriter(); 100 | bw.writeUInt32LE(1).concat().toString('hex').should.equal('01000000'); 101 | }); 102 | 103 | }); 104 | 105 | describe('#writeUInt64BEBN', function() { 106 | 107 | it('should write 1', function() { 108 | var bw = new BufferWriter(); 109 | bw.writeUInt64BEBN(new BN(1)).concat().toString('hex').should.equal('0000000000000001'); 110 | }); 111 | 112 | }); 113 | 114 | describe('#writeUInt64LEBN', function() { 115 | 116 | it('should write 1', function() { 117 | var bw = new BufferWriter(); 118 | bw.writeUInt64LEBN(new BN(1)).concat().toString('hex').should.equal('0100000000000000'); 119 | }); 120 | 121 | }); 122 | 123 | describe('#writeVarint', function() { 124 | 125 | it('should write a 1 byte varint', function() { 126 | var bw = new BufferWriter(); 127 | bw.writeVarintNum(1); 128 | bw.concat().length.should.equal(1); 129 | }); 130 | 131 | it('should write a 3 byte varint', function() { 132 | var bw = new BufferWriter(); 133 | bw.writeVarintNum(1000); 134 | bw.concat().length.should.equal(3); 135 | }); 136 | 137 | it('should write a 5 byte varint', function() { 138 | var bw = new BufferWriter(); 139 | bw.writeVarintNum(Math.pow(2, 16 + 1)); 140 | bw.concat().length.should.equal(5); 141 | }); 142 | 143 | it('should write a 9 byte varint', function() { 144 | var bw = new BufferWriter(); 145 | bw.writeVarintNum(Math.pow(2, 32 + 1)); 146 | bw.concat().length.should.equal(9); 147 | }); 148 | 149 | it('should read back the same value it wrote for a 9 byte varint', function() { 150 | var bw = new BufferWriter(); 151 | var n = Math.pow(2, 53); 152 | n.should.equal(n + 1); //javascript number precision limit 153 | bw.writeVarintNum(n); 154 | var br = new BufferReader({buf: bw.concat()}); 155 | br.readVarintBN().toNumber().should.equal(n); 156 | }); 157 | 158 | }); 159 | 160 | describe('#writeVarintBN', function() { 161 | 162 | it('should write a 1 byte varint', function() { 163 | var bw = new BufferWriter(); 164 | bw.writeVarintBN(new BN(1)); 165 | bw.concat().length.should.equal(1); 166 | }); 167 | 168 | it('should write a 3 byte varint', function() { 169 | var bw = new BufferWriter(); 170 | bw.writeVarintBN(new BN(1000)); 171 | bw.concat().length.should.equal(3); 172 | }); 173 | 174 | it('should write a 5 byte varint', function() { 175 | var bw = new BufferWriter(); 176 | bw.writeVarintBN(new BN(Math.pow(2, 16 + 1))); 177 | bw.concat().length.should.equal(5); 178 | }); 179 | 180 | it('should write a 9 byte varint', function() { 181 | var bw = new BufferWriter(); 182 | bw.writeVarintBN(new BN(Math.pow(2, 32 + 1))); 183 | bw.concat().length.should.equal(9); 184 | }); 185 | 186 | }); 187 | 188 | }); 189 | -------------------------------------------------------------------------------- /lib/crypto/bn.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var BN = require('bn.js'); 4 | var $ = require('../util/preconditions'); 5 | var _ = require('lodash'); 6 | 7 | var reversebuf = function(buf) { 8 | var buf2 = new Buffer(buf.length); 9 | for (var i = 0; i < buf.length; i++) { 10 | buf2[i] = buf[buf.length - 1 - i]; 11 | } 12 | return buf2; 13 | }; 14 | 15 | BN.Zero = new BN(0); 16 | BN.One = new BN(1); 17 | BN.Minus1 = new BN(-1); 18 | 19 | BN.fromNumber = function(n) { 20 | $.checkArgument(_.isNumber(n)); 21 | return new BN(n); 22 | }; 23 | 24 | BN.fromString = function(str) { 25 | $.checkArgument(_.isString(str)); 26 | return new BN(str); 27 | }; 28 | 29 | BN.fromBuffer = function(buf, opts) { 30 | if (typeof opts !== 'undefined' && opts.endian === 'little') { 31 | buf = reversebuf(buf); 32 | } 33 | var hex = buf.toString('hex'); 34 | var bn = new BN(hex, 16); 35 | return bn; 36 | }; 37 | 38 | /** 39 | * Instantiate a BigNumber from a "signed magnitude buffer" 40 | * (a buffer where the most significant bit represents the sign (0 = positive, -1 = negative)) 41 | */ 42 | BN.fromSM = function(buf, opts) { 43 | var ret; 44 | if (buf.length === 0) { 45 | return BN.fromBuffer(new Buffer([0])); 46 | } 47 | 48 | var endian = 'big'; 49 | if (opts) { 50 | endian = opts.endian; 51 | } 52 | if (endian === 'little') { 53 | buf = reversebuf(buf); 54 | } 55 | 56 | if (buf[0] & 0x80) { 57 | buf[0] = buf[0] & 0x7f; 58 | ret = BN.fromBuffer(buf); 59 | ret.neg().copy(ret); 60 | } else { 61 | ret = BN.fromBuffer(buf); 62 | } 63 | return ret; 64 | }; 65 | 66 | 67 | BN.prototype.toNumber = function() { 68 | return parseInt(this.toString(10), 10); 69 | }; 70 | 71 | BN.prototype.toBuffer = function(opts) { 72 | var buf, hex; 73 | if (opts && opts.size) { 74 | hex = this.toString(16, 2); 75 | var natlen = hex.length / 2; 76 | buf = new Buffer(hex, 'hex'); 77 | 78 | if (natlen === opts.size) { 79 | buf = buf; 80 | } else if (natlen > opts.size) { 81 | buf = BN.trim(buf, natlen); 82 | } else if (natlen < opts.size) { 83 | buf = BN.pad(buf, natlen, opts.size); 84 | } 85 | } else { 86 | hex = this.toString(16, 2); 87 | buf = new Buffer(hex, 'hex'); 88 | } 89 | 90 | if (typeof opts !== 'undefined' && opts.endian === 'little') { 91 | buf = reversebuf(buf); 92 | } 93 | 94 | return buf; 95 | }; 96 | 97 | BN.prototype.toSMBigEndian = function() { 98 | var buf; 99 | if (this.cmp(BN.Zero) === -1) { 100 | buf = this.neg().toBuffer(); 101 | if (buf[0] & 0x80) { 102 | buf = Buffer.concat([new Buffer([0x80]), buf]); 103 | } else { 104 | buf[0] = buf[0] | 0x80; 105 | } 106 | } else { 107 | buf = this.toBuffer(); 108 | if (buf[0] & 0x80) { 109 | buf = Buffer.concat([new Buffer([0x00]), buf]); 110 | } 111 | } 112 | 113 | if (buf.length === 1 & buf[0] === 0) { 114 | buf = new Buffer([]); 115 | } 116 | return buf; 117 | }; 118 | 119 | BN.prototype.toSM = function(opts) { 120 | var endian = opts ? opts.endian : 'big'; 121 | var buf = this.toSMBigEndian(); 122 | 123 | if (endian === 'little') { 124 | buf = reversebuf(buf); 125 | } 126 | return buf; 127 | }; 128 | 129 | /** 130 | * Create a BN from a "ScriptNum": 131 | * This is analogous to the constructor for CScriptNum in bitcoind. Many ops in 132 | * bitcoind's script interpreter use CScriptNum, which is not really a proper 133 | * bignum. Instead, an error is thrown if trying to input a number bigger than 134 | * 4 bytes. We copy that behavior here. 135 | */ 136 | BN.fromScriptNumBuffer = function(buf, fRequireMinimal) { 137 | var nMaxNumSize = 4; 138 | $.checkArgument(buf.length <= nMaxNumSize, new Error('script number overflow')); 139 | if (fRequireMinimal && buf.length > 0) { 140 | // Check that the number is encoded with the minimum possible 141 | // number of bytes. 142 | // 143 | // If the most-significant-byte - excluding the sign bit - is zero 144 | // then we're not minimal. Note how this test also rejects the 145 | // negative-zero encoding, 0x80. 146 | if ((buf[buf.length - 1] & 0x7f) === 0) { 147 | // One exception: if there's more than one byte and the most 148 | // significant bit of the second-most-significant-byte is set 149 | // it would conflict with the sign bit. An example of this case 150 | // is +-255, which encode to 0xff00 and 0xff80 respectively. 151 | // (big-endian). 152 | if (buf.length <= 1 || (buf[buf.length - 2] & 0x80) === 0) { 153 | throw new Error('non-minimally encoded script number'); 154 | } 155 | } 156 | } 157 | return BN.fromSM(buf, { 158 | endian: 'little' 159 | }); 160 | }; 161 | 162 | /** 163 | * The corollary to the above, with the notable exception that we do not throw 164 | * an error if the output is larger than four bytes. (Which can happen if 165 | * performing a numerical operation that results in an overflow to more than 4 166 | * bytes). 167 | */ 168 | BN.prototype.toScriptNumBuffer = function() { 169 | return this.toSM({ 170 | endian: 'little' 171 | }); 172 | }; 173 | 174 | BN.prototype.gt = function(b) { 175 | return this.cmp(b) > 0; 176 | }; 177 | 178 | BN.prototype.lt = function(b) { 179 | return this.cmp(b) < 0; 180 | }; 181 | 182 | BN.trim = function(buf, natlen) { 183 | return buf.slice(natlen - buf.length, buf.length); 184 | }; 185 | 186 | BN.pad = function(buf, natlen, size) { 187 | var rbuf = new Buffer(size); 188 | for (var i = 0; i < buf.length; i++) { 189 | rbuf[rbuf.length - 1 - i] = buf[buf.length - 1 - i]; 190 | } 191 | for (i = 0; i < size - natlen; i++) { 192 | rbuf[i] = 0; 193 | } 194 | return rbuf; 195 | }; 196 | 197 | module.exports = BN; 198 | -------------------------------------------------------------------------------- /test/util/buffer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* jshint unused: false */ 3 | 4 | var should = require('chai').should(); 5 | var expect = require('chai').expect; 6 | 7 | var bitcore = require('../..'); 8 | var errors = bitcore.errors; 9 | var BufferUtil = bitcore.util.buffer; 10 | 11 | describe('buffer utils', function() { 12 | 13 | describe('equals', function() { 14 | it('recognizes these two equal buffers', function() { 15 | var bufferA = new Buffer([1, 2, 3]); 16 | var bufferB = new Buffer('010203', 'hex'); 17 | BufferUtil.equal(bufferA, bufferB).should.equal(true); 18 | }); 19 | it('no false positive: returns false with two different buffers', function() { 20 | var bufferA = new Buffer([1, 2, 3]); 21 | var bufferB = new Buffer('010204', 'hex'); 22 | BufferUtil.equal(bufferA, bufferB).should.equal(false); 23 | }); 24 | it('coverage: quickly realizes a difference in size and returns false', function() { 25 | var bufferA = new Buffer([1, 2, 3]); 26 | var bufferB = new Buffer([]); 27 | BufferUtil.equal(bufferA, bufferB).should.equal(false); 28 | }); 29 | it('"equals" is an an alias for "equal"', function() { 30 | var bufferA = new Buffer([1, 2, 3]); 31 | var bufferB = new Buffer([1, 2, 3]); 32 | BufferUtil.equal(bufferA, bufferB).should.equal(true); 33 | BufferUtil.equals(bufferA, bufferB).should.equal(true); 34 | }); 35 | }); 36 | 37 | describe('fill', function() { 38 | it('checks arguments', function() { 39 | expect(function() { 40 | BufferUtil.fill('something'); 41 | }).to.throw(errors.InvalidArgumentType); 42 | expect(function() { 43 | BufferUtil.fill(new Buffer([0, 0, 0]), 'invalid'); 44 | }).to.throw(errors.InvalidArgumentType); 45 | }); 46 | it('works correctly for a small buffer', function() { 47 | var buffer = BufferUtil.fill(new Buffer(10), 6); 48 | for (var i = 0; i < 10; i++) { 49 | buffer[i].should.equal(6); 50 | } 51 | }); 52 | }); 53 | 54 | describe('isBuffer', function() { 55 | it('has no false positive', function() { 56 | expect(BufferUtil.isBuffer(1)).to.equal(false); 57 | }); 58 | it('has no false negative', function() { 59 | expect(BufferUtil.isBuffer(new Buffer(0))).to.equal(true); 60 | }); 61 | }); 62 | 63 | describe('emptyBuffer', function() { 64 | it('creates a buffer filled with zeros', function() { 65 | var buffer = BufferUtil.emptyBuffer(10); 66 | expect(buffer.length).to.equal(10); 67 | for (var i = 0; i < 10; i++) { 68 | expect(buffer[i]).to.equal(0); 69 | } 70 | }); 71 | it('checks arguments', function() { 72 | expect(function() { 73 | BufferUtil.emptyBuffer('invalid'); 74 | }).to.throw(errors.InvalidArgumentType); 75 | }); 76 | }); 77 | 78 | describe('single byte buffer <=> integer', function() { 79 | it('integerAsSingleByteBuffer should return a buffer of length 1', function() { 80 | expect(BufferUtil.integerAsSingleByteBuffer(100)[0]).to.equal(100); 81 | }); 82 | it('should check the type', function() { 83 | expect(function() { 84 | BufferUtil.integerAsSingleByteBuffer('invalid'); 85 | }).to.throw(errors.InvalidArgumentType); 86 | expect(function() { 87 | BufferUtil.integerFromSingleByteBuffer('invalid'); 88 | }).to.throw(errors.InvalidArgumentType); 89 | }); 90 | it('works correctly for edge cases', function() { 91 | expect(BufferUtil.integerAsSingleByteBuffer(255)[0]).to.equal(255); 92 | expect(BufferUtil.integerAsSingleByteBuffer(-1)[0]).to.equal(255); 93 | }); 94 | it('does a round trip', function() { 95 | expect(BufferUtil.integerAsSingleByteBuffer( 96 | BufferUtil.integerFromSingleByteBuffer(new Buffer([255])) 97 | )[0]).to.equal(255); 98 | }); 99 | }); 100 | 101 | describe('4byte buffer integer <=> integer', function() { 102 | it('integerAsBuffer should return a buffer of length 4', function() { 103 | expect(BufferUtil.integerAsBuffer(100).length).to.equal(4); 104 | }); 105 | it('is little endian', function() { 106 | expect(BufferUtil.integerAsBuffer(100)[3]).to.equal(100); 107 | }); 108 | it('should check the type', function() { 109 | expect(function() { 110 | BufferUtil.integerAsBuffer('invalid'); 111 | }).to.throw(errors.InvalidArgumentType); 112 | expect(function() { 113 | BufferUtil.integerFromBuffer('invalid'); 114 | }).to.throw(errors.InvalidArgumentType); 115 | }); 116 | it('works correctly for edge cases', function() { 117 | expect(BufferUtil.integerAsBuffer(4294967295)[0]).to.equal(255); 118 | expect(BufferUtil.integerAsBuffer(4294967295)[3]).to.equal(255); 119 | expect(BufferUtil.integerAsBuffer(-1)[0]).to.equal(255); 120 | expect(BufferUtil.integerAsBuffer(-1)[3]).to.equal(255); 121 | }); 122 | it('does a round trip', function() { 123 | expect(BufferUtil.integerFromBuffer( 124 | BufferUtil.integerAsBuffer(10000) 125 | )).to.equal(10000); 126 | }); 127 | }); 128 | 129 | describe('buffer to hex', function() { 130 | it('returns an expected value in hexa', function() { 131 | expect(BufferUtil.bufferToHex(new Buffer([255, 0, 128]))).to.equal('ff0080'); 132 | }); 133 | it('checks the argument type', function() { 134 | expect(function() { 135 | BufferUtil.bufferToHex('invalid'); 136 | }).to.throw(errors.InvalidArgumentType); 137 | }); 138 | it('round trips', function() { 139 | var original = new Buffer([255, 0, 128]); 140 | var hexa = BufferUtil.bufferToHex(original); 141 | var back = BufferUtil.hexToBuffer(hexa); 142 | expect(BufferUtil.equal(original, back)).to.equal(true); 143 | }); 144 | }); 145 | 146 | describe('reverse', function() { 147 | it('reverses a buffer', function() { 148 | // http://bit.ly/1J2Ai4x 149 | var original = new Buffer([255, 0, 128]); 150 | var reversed = BufferUtil.reverse(original); 151 | original[0].should.equal(reversed[2]); 152 | original[1].should.equal(reversed[1]); 153 | original[2].should.equal(reversed[0]); 154 | }); 155 | it('checks the argument type', function() { 156 | expect(function() { 157 | BufferUtil.reverse('invalid'); 158 | }).to.throw(errors.InvalidArgumentType); 159 | }); 160 | }); 161 | }); 162 | -------------------------------------------------------------------------------- /test/crypto/point.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var should = require('chai').should(); 4 | var bitcore = require('../..'); 5 | var Point = bitcore.crypto.Point; 6 | var BN = bitcore.crypto.BN; 7 | 8 | describe('Point', function() { 9 | 10 | var valid = { 11 | x: 'ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2', 12 | y: '4836ab292c105a711ed10fcfd30999c31ff7c02456147747e03e739ad527c380', 13 | }; 14 | 15 | var invalidPair = { 16 | x: 'ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2', 17 | y: '0000000000000000000000000000000000000000000000000000000000000000', 18 | }; 19 | 20 | it('should create a point', function() { 21 | var p = Point(valid.x, valid.y); 22 | should.exist(p); 23 | }); 24 | 25 | it('should create a point when called with "new"', function() { 26 | var p = new Point(valid.x,valid.y); 27 | should.exist(p); 28 | }); 29 | 30 | describe('#getX', function() { 31 | 32 | it('should return x', function() { 33 | var p = Point(valid.x,valid.y); 34 | var x = p.getX(); 35 | x.toString('hex', 64).should.equal(valid.x); 36 | }); 37 | 38 | it('should be convertable to a buffer', function() { 39 | var p = Point(valid.x,valid.y); 40 | var a = p.getX().toBuffer({size: 32}); 41 | a.length.should.equal(32); 42 | a.should.deep.equal(new Buffer(valid.x, 'hex')); 43 | }); 44 | 45 | }); 46 | 47 | describe('#getY', function() { 48 | 49 | it('should return y', function() { 50 | var p = Point(valid.x,valid.y); 51 | p.getY().toString('hex', 64).should.equal(valid.y); 52 | }); 53 | 54 | it('should be convertable to a buffer', function() { 55 | var p = Point(valid.x,valid.y); 56 | var a = p.getY().toBuffer({size: 32}); 57 | a.length.should.equal(32); 58 | a.should.deep.equal(new Buffer(valid.y, 'hex')); 59 | }); 60 | 61 | }); 62 | 63 | describe('#add', function() { 64 | 65 | it('should accurately add g to itself', function() { 66 | var p1 = Point.getG(); 67 | var p2 = Point.getG(); 68 | var p3 = p1.add(p2); 69 | p3.getX().toString().should.equal('89565891926547004231252920425935692360644145829622209'+ 70 | '833684329913297188986597'); 71 | p3.getY().toString().should.equal('12158399299693830322967808612713398636155367887041628'+ 72 | '176798871954788371653930'); 73 | }); 74 | 75 | }); 76 | 77 | describe('#mul', function() { 78 | 79 | it('should accurately multiply g by 2', function() { 80 | var g = Point.getG(); 81 | var b = g.mul(new BN(2)); 82 | b.getX().toString().should.equal('8956589192654700423125292042593569236064414582962220983'+ 83 | '3684329913297188986597'); 84 | b.getY().toString().should.equal('1215839929969383032296780861271339863615536788704162817'+ 85 | '6798871954788371653930'); 86 | }); 87 | 88 | it('should accurately multiply g by n-1', function() { 89 | var g = Point.getG(); 90 | var n = Point.getN(); 91 | var b = g.mul(n.sub(new BN(1))); 92 | b.getX().toString().should.equal('55066263022277343669578718895168534326250603453777594175'+ 93 | '500187360389116729240'); 94 | b.getY().toString().should.equal('83121579216557378445487899878180864668798711284981320763'+ 95 | '518679672151497189239'); 96 | }); 97 | 98 | //not sure if this is technically accurate or not... 99 | //normally, you should always multiply g by something less than n 100 | //but it is the same result in OpenSSL 101 | it('should accurately multiply g by n+1', function() { 102 | var g = Point.getG(); 103 | var n = Point.getN(); 104 | var b = g.mul(n.add(new BN(1))); 105 | b.getX().toString().should.equal('550662630222773436695787188951685343262506034537775941755'+ 106 | '00187360389116729240'); 107 | b.getY().toString().should.equal('326705100207588169780830851305070431844712733806592432759'+ 108 | '38904335757337482424'); 109 | }); 110 | 111 | }); 112 | 113 | describe('@fromX', function() { 114 | 115 | it('should return g', function() { 116 | var g = Point.getG(); 117 | var p = Point.fromX(false, g.getX()); 118 | g.eq(p).should.equal(true); 119 | }); 120 | 121 | }); 122 | 123 | describe('#validate', function() { 124 | 125 | it('should describe this point as valid', function() { 126 | var p = Point(valid.x, valid.y); 127 | should.exist(p.validate()); 128 | }); 129 | 130 | it('should describe this point as invalid because of zero y', function() { 131 | var x = 'ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2'; 132 | var y = '0000000000000000000000000000000000000000000000000000000000000000'; 133 | (function() { 134 | var p = Point(x, y); 135 | }).should.throw('Invalid x,y value for curve, cannot equal 0.'); 136 | }); 137 | 138 | 139 | it('should describe this point as invalid because of invalid y', function() { 140 | var x = 'ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2'; 141 | var y = '00000000000000000000000000000000000000000000000000000000000000FF'; 142 | (function() { 143 | var p = Point(x, y); 144 | }).should.throw('Invalid y value for curve.'); 145 | }); 146 | 147 | 148 | it('should describe this point as invalid because out of curve bounds', function() { 149 | 150 | // point larger than max 151 | var x = 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEDCE6AF48A03BBFD25E8CD0364141'; 152 | // calculated y of x 153 | var y = 'ed3970f129bc2ca7c7c6cf92fa7da4de6a1dfc9c14da4bf056aa868d3dd74034'; 154 | 155 | (function() { 156 | // set the point 157 | var p = Point(x, y); 158 | }).should.throw('Point does not lie on the curve'); 159 | }); 160 | 161 | it('should describe this point as invalid because out of curve bounds', function() { 162 | 163 | // point larger than max 164 | var x = '0000000000000000000000000000000000000000000000000000000000000000'; 165 | 166 | (function() { 167 | // set the point 168 | var p = Point.fromX(false, x); 169 | }).should.throw('Invalid x,y value for curve, cannot equal 0.'); 170 | }); 171 | 172 | }); 173 | 174 | }); 175 | -------------------------------------------------------------------------------- /lib/errors/spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var docsURL = 'http://bitcore.io/'; 4 | 5 | module.exports = [{ 6 | name: 'InvalidB58Char', 7 | message: 'Invalid Base58 character: {0} in {1}' 8 | }, { 9 | name: 'InvalidB58Checksum', 10 | message: 'Invalid Base58 checksum for {0}' 11 | }, { 12 | name: 'InvalidNetwork', 13 | message: 'Invalid version for network: got {0}' 14 | }, { 15 | name: 'InvalidState', 16 | message: 'Invalid state: {0}' 17 | }, { 18 | name: 'NotImplemented', 19 | message: 'Function {0} was not implemented yet' 20 | }, { 21 | name: 'InvalidNetworkArgument', 22 | message: 'Invalid network: must be "livenet" or "testnet", got {0}' 23 | }, { 24 | name: 'InvalidArgument', 25 | message: function() { 26 | return 'Invalid Argument' + (arguments[0] ? (': ' + arguments[0]) : '') + 27 | (arguments[1] ? (' Documentation: ' + docsURL + arguments[1]) : ''); 28 | } 29 | }, { 30 | name: 'AbstractMethodInvoked', 31 | message: 'Abstract Method Invokation: {0}' 32 | }, { 33 | name: 'InvalidArgumentType', 34 | message: function() { 35 | return 'Invalid Argument for ' + arguments[2] + ', expected ' + arguments[1] + ' but got ' + typeof arguments[0]; 36 | } 37 | }, { 38 | name: 'Unit', 39 | message: 'Internal Error on Unit {0}', 40 | errors: [{ 41 | 'name': 'UnknownCode', 42 | 'message': 'Unrecognized unit code: {0}' 43 | }, { 44 | 'name': 'InvalidRate', 45 | 'message': 'Invalid exchange rate: {0}' 46 | }] 47 | }, { 48 | name: 'Transaction', 49 | message: 'Internal Error on Transaction {0}', 50 | errors: [{ 51 | name: 'Input', 52 | message: 'Internal Error on Input {0}', 53 | errors: [{ 54 | name: 'MissingScript', 55 | message: 'Need a script to create an input' 56 | }, { 57 | name: 'UnsupportedScript', 58 | message: 'Unsupported input script type: {0}' 59 | }, { 60 | name: 'MissingPreviousOutput', 61 | message: 'No previous output information.' 62 | }] 63 | }, { 64 | name: 'NeedMoreInfo', 65 | message: '{0}' 66 | }, { 67 | name: 'InvalidSorting', 68 | message: 'The sorting function provided did not return the change output as one of the array elements' 69 | }, { 70 | name: 'InvalidOutputAmountSum', 71 | message: '{0}' 72 | }, { 73 | name: 'MissingSignatures', 74 | message: 'Some inputs have not been fully signed' 75 | }, { 76 | name: 'InvalidIndex', 77 | message: 'Invalid index: {0} is not between 0, {1}' 78 | }, { 79 | name: 'UnableToVerifySignature', 80 | message: 'Unable to verify signature: {0}' 81 | }, { 82 | name: 'DustOutputs', 83 | message: 'Dust amount detected in one output' 84 | }, { 85 | name: 'InvalidSatoshis', 86 | message: 'Output satoshis are invalid', 87 | }, { 88 | name: 'FeeError', 89 | message: 'Internal Error on Fee {0}', 90 | errors: [{ 91 | name: 'TooSmall', 92 | message: 'Fee is too small: {0}', 93 | }, { 94 | name: 'TooLarge', 95 | message: 'Fee is too large: {0}', 96 | }, { 97 | name: 'Different', 98 | message: 'Unspent value is different from specified fee: {0}', 99 | }] 100 | }, { 101 | name: 'ChangeAddressMissing', 102 | message: 'Change address is missing' 103 | }, { 104 | name: 'BlockHeightTooHigh', 105 | message: 'Block Height can be at most 2^32 -1' 106 | }, { 107 | name: 'NLockTimeOutOfRange', 108 | message: 'Block Height can only be between 0 and 499 999 999' 109 | }, { 110 | name: 'LockTimeTooEarly', 111 | message: 'Lock Time can\'t be earlier than UNIX date 500 000 000' 112 | }] 113 | }, { 114 | name: 'Script', 115 | message: 'Internal Error on Script {0}', 116 | errors: [{ 117 | name: 'UnrecognizedAddress', 118 | message: 'Expected argument {0} to be an address' 119 | }, { 120 | name: 'CantDeriveAddress', 121 | message: 'Can\'t derive address associated with script {0}, needs to be p2pkh in, p2pkh out, p2sh in, or p2sh out.' 122 | }, { 123 | name: 'InvalidBuffer', 124 | message: 'Invalid script buffer: can\'t parse valid script from given buffer {0}' 125 | }] 126 | }, { 127 | name: 'HDPrivateKey', 128 | message: 'Internal Error on HDPrivateKey {0}', 129 | errors: [{ 130 | name: 'InvalidDerivationArgument', 131 | message: 'Invalid derivation argument {0}, expected string, or number and boolean' 132 | }, { 133 | name: 'InvalidEntropyArgument', 134 | message: 'Invalid entropy: must be an hexa string or binary buffer, got {0}', 135 | errors: [{ 136 | name: 'TooMuchEntropy', 137 | message: 'Invalid entropy: more than 512 bits is non standard, got "{0}"' 138 | }, { 139 | name: 'NotEnoughEntropy', 140 | message: 'Invalid entropy: at least 128 bits needed, got "{0}"' 141 | }] 142 | }, { 143 | name: 'InvalidLength', 144 | message: 'Invalid length for xprivkey string in {0}' 145 | }, { 146 | name: 'InvalidPath', 147 | message: 'Invalid derivation path: {0}' 148 | }, { 149 | name: 'UnrecognizedArgument', 150 | message: 'Invalid argument: creating a HDPrivateKey requires a string, buffer, json or object, got "{0}"' 151 | }] 152 | }, { 153 | name: 'HDPublicKey', 154 | message: 'Internal Error on HDPublicKey {0}', 155 | errors: [{ 156 | name: 'ArgumentIsPrivateExtended', 157 | message: 'Argument is an extended private key: {0}' 158 | }, { 159 | name: 'InvalidDerivationArgument', 160 | message: 'Invalid derivation argument: got {0}' 161 | }, { 162 | name: 'InvalidLength', 163 | message: 'Invalid length for xpubkey: got "{0}"' 164 | }, { 165 | name: 'InvalidPath', 166 | message: 'Invalid derivation path, it should look like: "m/1/100", got "{0}"' 167 | }, { 168 | name: 'MustSupplyArgument', 169 | message: 'Must supply an argument to create a HDPublicKey' 170 | }, { 171 | name: 'UnrecognizedArgument', 172 | message: 'Invalid argument for creation, must be string, json, buffer, or object' 173 | }] 174 | }, { 175 | name: 'Blockchain', 176 | message: 'Internal Error on Blockchain {0}', 177 | errors: [{ 178 | name: 'MissingParent', 179 | message: 'Could not found parent in memory for block {0}, parent is {1}' 180 | }, { 181 | name: 'PixelMined', 182 | message: 'Pixel at position {0} was already mined' 183 | }, { 184 | name: 'NotAdjacent', 185 | message: 'Pixel is not adjacent to a mined block' 186 | }, { 187 | name: 'SignatureMismatch', 188 | message: 'Block is invalid; signature mismatch for transaction {0} (block hash {1}, index {2})' 189 | }] 190 | }]; 191 | -------------------------------------------------------------------------------- /test/transaction/transaction.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint unused: false */ 4 | /* jshint latedef: false */ 5 | var should = require('chai').should(); 6 | var expect = require('chai').expect; 7 | var _ = require('lodash'); 8 | var sinon = require('sinon'); 9 | 10 | var bitcore = require('../..'); 11 | var BN = bitcore.crypto.BN; 12 | var Transaction = bitcore.Transaction; 13 | var PrivateKey = bitcore.PrivateKey; 14 | var Script = bitcore.Script; 15 | var Address = bitcore.Address; 16 | var Networks = bitcore.Networks; 17 | var errors = bitcore.errors; 18 | 19 | describe('Transaction', function() { 20 | 21 | it('can instantiate from constructor', function() { 22 | var tx = new Transaction(); 23 | should.exist(tx); 24 | }); 25 | 26 | var testPrevTxID = 'a477af6b2667c29670467e4e0728b685ee07b240235771862318e29ddbe58458'; 27 | var testOwner = '02dfe18e62ab4d1b5cef8a1e90cc010acfa15f08840efe3aca5dd8256a3a01f725'; 28 | var testTransaction = new Transaction() 29 | .from(testPrevTxID) 30 | .to(testOwner) 31 | .colored(0xaabbccff) 32 | .at(2, 3); 33 | 34 | it('can instantiate from constructor and builders', function() { 35 | var tx = new Transaction() 36 | .from(testPrevTxID) 37 | .to(testOwner) 38 | .colored(0xff0000ff) 39 | .at(20, -10); 40 | tx.version.should.equal(Transaction.CURRENT_VERSION); 41 | tx.input.toString('hex').should.equal(testPrevTxID); 42 | tx.owner.toString().should.equal(testOwner); 43 | tx.color.should.equal(0xff0000ff); 44 | tx.position.x.should.equal(20); 45 | tx.position.y.should.equal(-10); 46 | }); 47 | 48 | it('should serialize and deserialize correctly a built transaction', function() { 49 | var someone = new PrivateKey('690821300cad086e19dce9b7a6eb5278e8fcb33d658fb6663f191d12412239aa'); 50 | var coinbase = new Transaction() 51 | .at(2, 3) 52 | .to(someone.publicKey); 53 | 54 | var tx = new Transaction() 55 | .from(coinbase) 56 | .to(testOwner) 57 | .colored(0xaabbccff) 58 | .sign(someone); 59 | 60 | var hex = tx.toString(); 61 | hex.should.equal( 62 | '01' + // version 63 | coinbase.hash + // previous 64 | '02000000' + 65 | '03000000' + 66 | 'ffccbbaa' + 67 | testOwner + 68 | '46' + // signature size 69 | '30440220685f4dd87cd6c946e10d54f3255894c13d6bd73457e42c37b28a01a74fb96f75022019a6b' + 70 | '830f449bdffa35c00a5885836b0e42f26524f84a48b63ced5b0f723bdd5' 71 | ); 72 | (new Transaction(hex)).toString().should.equal(hex); 73 | }); 74 | 75 | describe('sign', function() { 76 | it('cant be signed without previous tx info', function() { 77 | var prevOwner = new PrivateKey('690821300cad086e19dce9b7a6eb5278e8fcb33d658fb6663f191d12412239aa'); 78 | 79 | var fails = function() { 80 | return new Transaction() 81 | .from(testPrevTxID) 82 | .to(testOwner) 83 | .colored(0xaabbccff) 84 | .at(2, 3) 85 | .sign(prevOwner); 86 | }; 87 | fails.should.throw('No previous transaction information'); 88 | 89 | }); 90 | 91 | it('first two transactions for a coordinate', function() { 92 | var x = 4; 93 | var y = 5; 94 | var firstOwner = new PrivateKey('690821300cad086e19dce9b7a6eb5278e8fcb33d658fb6663f191d12412239aa'); 95 | var secondOwner = new PrivateKey('b78ccb0e9dc8b623db160a1241e3a42461801938f0983a4461219834848dc80a'); 96 | 97 | var coinbase = new Transaction() 98 | .at(x, y) 99 | .to(firstOwner.toPublicKey()); 100 | var tx = new Transaction() 101 | .from(coinbase) 102 | .to(secondOwner.toPublicKey()) 103 | .sign(firstOwner); 104 | 105 | tx.isFullySigned().should.equal(true); 106 | var sig = tx.getSignature(); 107 | tx.isValidSignature(sig, firstOwner.publicKey); 108 | }); 109 | }); 110 | 111 | 112 | describe('isAdjacent', function() { 113 | it('recognizes all adjacent coordinates', function() { 114 | var x = -1; 115 | var y = 7; 116 | var tx = new Transaction() 117 | .at(x, y); 118 | var up = { 119 | x: x, 120 | y: y + 1 121 | }; 122 | var down = { 123 | x: x, 124 | y: y - 1 125 | }; 126 | var right = { 127 | x: x + 1, 128 | y: y 129 | }; 130 | var left = { 131 | x: x - 1, 132 | y: y 133 | }; 134 | tx.isAdjacent([up]).should.deep.equal(up); 135 | tx.isAdjacent([down]).should.deep.equal(down); 136 | tx.isAdjacent([left]).should.deep.equal(left); 137 | tx.isAdjacent([right]).should.deep.equal(right); 138 | tx.isAdjacent([up, down, left, right]).should.deep.equal(up); 139 | }); 140 | it('recognizes non adjacent coordinates', function() { 141 | var x = 4; 142 | var y = -2; 143 | var tx = new Transaction() 144 | .at(x, y); 145 | tx.isAdjacent([{ 146 | x: 5, 147 | y: -1 148 | }]).should.deep.equal(false); 149 | tx.isAdjacent([{ 150 | x: 4, 151 | y: 0 152 | }]).should.deep.equal(false); 153 | tx.isAdjacent([{ 154 | x: 6, 155 | y: -2 156 | }]).should.deep.equal(false); 157 | }); 158 | }); 159 | 160 | it('should serialize and deserialize correctly a coinbase transaction', function() { 161 | var hex = '010000000000000000000000000000000000000000000000000000000000000000040000000500000000000000028bf7ee49d293d9517e8e98c05d2eb4f2649abb1d97d089c1717b98631278116300'; 162 | var transaction = new Transaction(hex); 163 | transaction.uncheckedSerialize().should.equal(hex); 164 | }); 165 | it('should serialize and deserialize correctly a regular transaction', function() { 166 | var hex = '0138f7c7c01db041b613f492bb577030960bf520c3fdcb1204c07eaf90c7df98b304000000050000000000000002230a6509ec6649bc2b31fd197bac7e5f8eb12393e2676ec28ed3046cf43e72ab00'; 167 | var transaction = new Transaction(hex); 168 | transaction.uncheckedSerialize().should.equal(hex); 169 | }); 170 | 171 | it('fails if an invalid parameter is passed to constructor', function() { 172 | expect(function() { 173 | return new Transaction(1); 174 | }).to.throw(errors.InvalidArgument); 175 | }); 176 | 177 | 178 | it('serialize to Object roundtrip', function() { 179 | new Transaction(testTransaction.toObject()).uncheckedSerialize() 180 | .should.equal(testTransaction.uncheckedSerialize()); 181 | }); 182 | 183 | it('constructor returns a shallow copy of another transaction', function() { 184 | var transaction = new Transaction(testTransaction); 185 | var copy = new Transaction(transaction); 186 | copy.uncheckedSerialize().should.equal(transaction.uncheckedSerialize()); 187 | }); 188 | 189 | it('should display correctly in console', function() { 190 | var transaction = new Transaction(testTransaction); 191 | transaction.inspect().should.equal(''); 192 | }); 193 | 194 | describe('to and from JSON', function() { 195 | it('takes a string that is a valid JSON and deserializes from it', function() { 196 | var simple = new Transaction(testTransaction); 197 | expect(new Transaction(simple.toJSON()).uncheckedSerialize()).to.equal(simple.uncheckedSerialize()); 198 | }); 199 | }); 200 | 201 | }); 202 | -------------------------------------------------------------------------------- /test/block/blockheader.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var bitcore = require('../..'); 4 | var BufferUtil = bitcore.util.buffer; 5 | var BufferReader = bitcore.encoding.BufferReader; 6 | var BufferWriter = bitcore.encoding.BufferWriter; 7 | var _ = bitcore.deps._; 8 | var BN = bitcore.crypto.BN; 9 | 10 | var BlockHeader = bitcore.BlockHeader; 11 | var should = require('chai').should(); 12 | 13 | describe('BlockHeader', function() { 14 | 15 | var version = 23; 16 | var prevblockidbuf = '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f'; 17 | var merklerootbuf = '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b'; 18 | var time = 123456; 19 | var bits = 0x207fffff; // genesis bitcoin: 486604799 20 | var nonce = 1926; 21 | var height = 300200; 22 | var bh = new BlockHeader({ 23 | version: version, 24 | height: height, 25 | prevHash: prevblockidbuf, 26 | merkleRoot: merklerootbuf, 27 | time: time, 28 | bits: bits, 29 | nonce: nonce 30 | }); 31 | var bhbuf = bh.toBuffer(); 32 | var bhhex = bhbuf.toString('hex'); 33 | 34 | it('should make a new blockheader', function() { 35 | BlockHeader(bhbuf).toBuffer().toString('hex').should.equal(bhhex); 36 | }); 37 | 38 | it('should not make an empty block', function() { 39 | (function() { 40 | BlockHeader(); 41 | }).should.throw('Unrecognized argument for BlockHeader'); 42 | }); 43 | 44 | describe('#constructor', function() { 45 | 46 | it('should set all the variables', function() { 47 | var bh2 = new BlockHeader({ 48 | version: version, 49 | prevHash: prevblockidbuf, 50 | merkleRoot: merklerootbuf, 51 | time: time, 52 | bits: bits, 53 | nonce: nonce, 54 | height: height, 55 | }); 56 | should.exist(bh2.version); 57 | bh2.version.should.equal(version); 58 | should.exist(bh2.height); 59 | bh2.height.should.equal(height); 60 | should.exist(bh2.prevHash); 61 | BufferUtil.reverse(bh2.prevHash).toString('hex').should.equal(prevblockidbuf.toString('hex')); 62 | should.exist(bh2.merkleRoot); 63 | BufferUtil.reverse(bh2.merkleRoot).toString('hex').should.equal(merklerootbuf.toString('hex')); 64 | should.exist(bh2.time); 65 | bh2.time.should.equal(time); 66 | should.exist(bh2.bits); 67 | bh2.bits.should.equal(bits); 68 | should.exist(bh2.nonce); 69 | bh2.nonce.should.equal(nonce); 70 | }); 71 | 72 | }); 73 | 74 | describe('#fromJSON', function() { 75 | 76 | it('should set all the variables', function() { 77 | var bh = BlockHeader.fromJSON(JSON.stringify({ 78 | version: version, 79 | prevHash: prevblockidbuf.toString('hex'), 80 | merkleRoot: merklerootbuf.toString('hex'), 81 | time: time, 82 | bits: bits, 83 | nonce: nonce 84 | })); 85 | should.exist(bh.version); 86 | should.exist(bh.prevHash); 87 | should.exist(bh.merkleRoot); 88 | should.exist(bh.time); 89 | should.exist(bh.bits); 90 | should.exist(bh.nonce); 91 | }); 92 | 93 | }); 94 | 95 | describe('#toJSON', function() { 96 | 97 | it('should set all the variables', function() { 98 | var json = JSON.parse(bh.toJSON()); 99 | should.exist(json.version); 100 | should.exist(json.prevHash); 101 | should.exist(json.merkleRoot); 102 | should.exist(json.time); 103 | should.exist(json.bits); 104 | should.exist(json.nonce); 105 | }); 106 | 107 | }); 108 | 109 | describe('#fromJSON', function() { 110 | 111 | it('should parse this known json string', function() { 112 | 113 | var jsonString = JSON.stringify({ 114 | version: version, 115 | prevHash: prevblockidbuf, 116 | merkleRoot: merklerootbuf, 117 | time: time, 118 | bits: bits, 119 | nonce: nonce 120 | }); 121 | 122 | var json = new BlockHeader(jsonString); 123 | should.exist(json.version); 124 | should.exist(json.prevHash); 125 | should.exist(json.merkleRoot); 126 | should.exist(json.time); 127 | should.exist(json.bits); 128 | should.exist(json.nonce); 129 | }); 130 | 131 | }); 132 | 133 | describe('#fromString/#toString', function() { 134 | 135 | it('should output/input a block hex string', function() { 136 | var b = BlockHeader.fromString(bhhex); 137 | b.toString().should.equal(bhhex); 138 | }); 139 | 140 | }); 141 | 142 | describe('#fromBuffer', function() { 143 | 144 | it('should parse this known buffer', function() { 145 | BlockHeader.fromBuffer(bhbuf).toBuffer().toString('hex').should.equal(bhhex); 146 | }); 147 | 148 | }); 149 | 150 | describe('#fromBufferReader', function() { 151 | 152 | it('should parse this known buffer', function() { 153 | BlockHeader.fromBufferReader(BufferReader(bhbuf)).toBuffer().toString('hex').should.equal(bhhex); 154 | }); 155 | 156 | }); 157 | 158 | describe('#toBuffer', function() { 159 | 160 | it('should output this known buffer', function() { 161 | BlockHeader.fromBuffer(bhbuf).toBuffer().toString('hex').should.equal(bhhex); 162 | }); 163 | 164 | }); 165 | 166 | describe('#toBufferWriter', function() { 167 | 168 | it('should output this known buffer', function() { 169 | BlockHeader.fromBuffer(bhbuf).toBufferWriter().concat().toString('hex').should.equal(bhhex); 170 | }); 171 | 172 | it('doesn\'t create a bufferWriter if one provided', function() { 173 | var writer = new BufferWriter(); 174 | var blockHeader = BlockHeader.fromBuffer(bhbuf); 175 | blockHeader.toBufferWriter(writer).should.equal(writer); 176 | }); 177 | 178 | }); 179 | 180 | describe('#validTimestamp', function() { 181 | 182 | var x = BlockHeader(bh); 183 | 184 | it('should validate timpstamp as true', function() { 185 | var valid = x.validTimestamp(x); 186 | valid.should.equal(true); 187 | }); 188 | 189 | 190 | it('should validate timestamp as false', function() { 191 | x.time = Math.round(new Date().getTime() / 1000) + BlockHeader.Constants.MAX_TIME_OFFSET + 100; 192 | var valid = x.validTimestamp(x); 193 | valid.should.equal(false); 194 | }); 195 | 196 | }); 197 | 198 | describe('#validProofOfWork', function() { 199 | 200 | it('should validate proof-of-work as true', function() { 201 | var x = BlockHeader(bh); 202 | var valid = x.validProofOfWork(x); 203 | valid.should.equal(true); 204 | }); 205 | 206 | it('should validate proof of work as false because incorrect proof of work', function() { 207 | var x = new BlockHeader(bh); 208 | x.nonce -= 1; 209 | var valid = x.validProofOfWork(x); 210 | valid.should.equal(false); 211 | }); 212 | 213 | }); 214 | 215 | it('coverage: caches the "_id" property', function() { 216 | var blockHeader = new BlockHeader(bh); 217 | blockHeader.id.should.equal(blockHeader.id); 218 | }); 219 | 220 | it('create', function() { 221 | var data = { 222 | prevHash: bh.id, 223 | height: 2000, 224 | time: 123455678, 225 | merkleRoot: '0000000000000000000000000000000000000000000000000000000000000000' 226 | }; 227 | var header = BlockHeader.create(data); 228 | header.validProofOfWork().should.equal(true); 229 | header.version.should.equal(BlockHeader.Constants.CURRENT_VERSION); 230 | header.height.should.equal(2000); 231 | header.time.should.equal(123455678); 232 | header.merkleRoot.toString('hex') 233 | .should.equal('0000000000000000000000000000000000000000000000000000000000000000'); 234 | header.bits.should.equal(545259519); 235 | header.nonce.should.equal(0); 236 | }); 237 | 238 | 239 | describe('getBits and getTargetDifficulty', function() { 240 | 241 | var data = [ 242 | [0x1b0404cb, '00000000000404CB000000000000000000000000000000000000000000000000'], 243 | [0x207fffff, '7fffff0000000000000000000000000000000000000000000000000000000000'], 244 | [0x1e0fffff, '00000fffff000000000000000000000000000000000000000000000000000000'], 245 | ]; 246 | _.each(data, function(datum) { 247 | var bits = datum[0]; 248 | var diff = BN.fromBuffer(new Buffer(datum[1],'hex')); 249 | it('should work for ' + bits.toString(16), function() { 250 | BlockHeader.getTargetDifficulty(bits).toString('hex').should.equal(diff.toString('hex')); 251 | BlockHeader.getBits(diff).should.equal(bits); 252 | }); 253 | 254 | }); 255 | 256 | }); 257 | 258 | 259 | }); 260 | -------------------------------------------------------------------------------- /test/block/miner.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var chai = require('chai'); 4 | var should = chai.should(); 5 | 6 | var bitcore = require('../..'); 7 | var Block = bitcore.Block; 8 | var Transaction = bitcore.Transaction; 9 | var Miner = bitcore.Miner; 10 | var PrivateKey = bitcore.PrivateKey; 11 | 12 | describe('Miner', function() { 13 | 14 | 15 | this.timeout(10000); 16 | 17 | var id = new PrivateKey('ecf4fd8e3c6b7cebeb028ceada16a24e266869e352e80971438bbb03db1c54e4'); 18 | var opts = {}; 19 | var coinbases = []; 20 | for (var i = 0; i < 100; i++) { 21 | coinbases.push(new Transaction() 22 | .at(0, i + 1) 23 | .to(id.publicKey) 24 | .colored(0xff0000ff) 25 | ); 26 | } 27 | opts.coinbase = coinbases[0]; 28 | opts.previous = Block.genesis; 29 | opts.time = 1432594281; 30 | var blockchain = []; 31 | blockchain.push(Block.genesis); 32 | 33 | it('mines genesis block', function(cb) { 34 | var genesisTx = new Transaction() 35 | .at(0, 0) 36 | .to('03000000000000000006fd1af568353887e69e992938cbd5d40fb026467e2fef7c') 37 | .colored(0x13371337); 38 | 39 | var miner = new Miner({ 40 | coinbase: genesisTx, 41 | previous: { 42 | header: { 43 | height: -1 44 | }, 45 | id: '0000000000000000000000000000000000000000000000000000000000000000' 46 | }, 47 | time: 1433037823, 48 | bits: 0x1e0fffff, 49 | nonce: 586080 50 | }); 51 | miner.on('block', function(block) { 52 | block.id.should.equal(Block.genesis.id); 53 | cb(); 54 | }); 55 | miner.run(); 56 | 57 | }); 58 | 59 | it('initializes', function() { 60 | var miner = new Miner(opts); 61 | should.exist(miner); 62 | }); 63 | 64 | it('mines ' + coinbases.length + ' blocks in a row without txs', function(cb) { 65 | var miner = new Miner(opts); 66 | var n = 0; 67 | miner.on('block', function(block) { 68 | n += 1; 69 | //console.log('block', block.height, block.id, 'nonce', block.nonce, 'bits', block.bits.toString(16)); 70 | block.header.validProofOfWork().should.equal(true); 71 | blockchain.push(block); 72 | block.header.height.should.equal(n); 73 | block.transactions.length.should.equal(1); 74 | if (n === coinbases.length) { 75 | cb(); 76 | return; 77 | } 78 | 79 | miner.newTip(block, coinbases[n]); 80 | process.nextTick(miner.run.bind(miner)); 81 | }); 82 | miner.run(); 83 | }); 84 | 85 | it('mines first block without transactions', function(cb) { 86 | var miner = new Miner(opts); 87 | miner.on('block', function(block) { 88 | block.header.validProofOfWork().should.equal(true); 89 | cb(); 90 | }); 91 | miner.run(); 92 | }); 93 | 94 | it('mines second block without transactions', function(cb) { 95 | var opts1 = JSON.parse(JSON.stringify(opts)); 96 | opts1.previous = blockchain[0]; 97 | opts1.coinbase = coinbases[1]; 98 | var miner = new Miner(opts1); 99 | miner.on('block', function(block) { 100 | block.header.validProofOfWork().should.equal(true); 101 | cb(); 102 | }); 103 | miner.run(); 104 | }); 105 | 106 | it('mines second block with a transaction', function(cb) { 107 | var opts1 = JSON.parse(JSON.stringify(opts)); 108 | opts1.previous = blockchain[0]; 109 | opts1.coinbase = coinbases[1]; 110 | var miner = new Miner(opts1); 111 | miner.on('block', function(block) { 112 | block.header.validProofOfWork().should.equal(true); 113 | block.transactions.length.should.equal(2); 114 | cb(); 115 | }); 116 | var tx = new Transaction() 117 | .from(coinbases[1]) 118 | .to(id.publicKey) 119 | .colored(0x00ff00ff) 120 | .sign(id); 121 | miner.addTransaction(tx); 122 | miner.run(); 123 | }); 124 | 125 | it('serializes and deserializes mined block', function(cb) { 126 | var opts1 = JSON.parse(JSON.stringify(opts)); 127 | opts1.previous = blockchain[0]; 128 | opts1.coinbase = coinbases[1]; 129 | var miner = new Miner(opts1); 130 | miner.on('block', function(block) { 131 | block.header.validProofOfWork().should.equal(true); 132 | block.transactions.length.should.equal(2); 133 | 134 | var serialized = Block.fromString(block.toString()); 135 | 136 | block.header.validProofOfWork().should.equal(true); 137 | serialized.toString().should.equal(block.toString()); 138 | serialized.header.validProofOfWork().should.equal(true); 139 | cb(); 140 | }); 141 | var tx = new Transaction() 142 | .from(coinbases[1]) 143 | .to(id.publicKey) 144 | .colored(0x00ff00ff) 145 | .sign(id); 146 | miner.addTransaction(tx); 147 | miner.run(); 148 | }); 149 | 150 | it('mines ' + coinbases.length + ' blocks in a row with txs spending prev coinbase', function(cb) { 151 | var opts2 = JSON.parse(JSON.stringify(opts)); 152 | opts2.previous = blockchain[1]; 153 | opts2.coinbase = coinbases[2]; 154 | var miner = new Miner(opts2); 155 | var n = 1; 156 | miner.on('block', function(block) { 157 | n += 1; 158 | //console.log('block', block.header.height, block.id, block.header.nonce); 159 | block.header.validProofOfWork().should.equal(true); 160 | blockchain.push(block); 161 | block.header.height.should.equal(n); 162 | block.transactions.length.should.equal(2); 163 | if (n === coinbases.length) { 164 | cb(); 165 | return; 166 | } 167 | 168 | miner.newTip(block, coinbases[n]); 169 | 170 | var tx = new Transaction() 171 | .from(coinbases[n]) 172 | .to(id.publicKey) 173 | .colored(0x00ff00ff) 174 | .sign(id); 175 | miner.addTransaction(tx); 176 | process.nextTick(miner.run.bind(miner)); 177 | }); 178 | var tx = new Transaction() 179 | .from(coinbases[1]) 180 | .to(id.publicKey) 181 | .colored(0x00ff00ff) 182 | .sign(id); 183 | miner.addTransaction(tx); 184 | miner.run(); 185 | }); 186 | 187 | it('mines first block without transactions and higher difficulty', function(cb) { 188 | opts.bits = 0x1e0fffff; // 00000fffff000000000000000000000000000000000000000000000000000000 189 | opts.nonce = 2561158; 190 | var miner = new Miner(opts); 191 | miner.on('block', function(block) { 192 | block.header.validProofOfWork().should.equal(true); 193 | cb(); 194 | }); 195 | miner.run(); 196 | }); 197 | 198 | it('serializes and deserializes mined tx', function(cb) { 199 | opts.bits = 0x1e0fffff; // 00000fffff000000000000000000000000000000000000000000000000000000 200 | opts.nonce = 2561158; 201 | var miner = new Miner(opts); 202 | miner.on('block', function(block) { 203 | block.header.validProofOfWork().should.equal(true); 204 | cb(); 205 | }); 206 | miner.run(); 207 | }); 208 | 209 | it('mines first block with one transaction and higher difficulty', function(cb) { 210 | opts.bits = 0x1e0fffff; // 00000fffff000000000000000000000000000000000000000000000000000000 211 | opts.nonce = 3170113; 212 | var miner = new Miner(opts); 213 | miner.on('block', function(block) { 214 | block.header.validProofOfWork().should.equal(true); 215 | block.transactions.length.should.equal(2); 216 | cb(); 217 | }); 218 | var tx = new Transaction() 219 | .from(coinbases[1]) 220 | .to(id.publicKey) 221 | .colored(0x00ff00ff) 222 | .sign(id); 223 | miner.addTransaction(tx); 224 | miner.run(); 225 | }); 226 | 227 | it('mines first block with two transactions and moderate difficulty', function(cb) { 228 | opts.bits = 0x1f0fffff; // 00000fffff000000000000000000000000000000000000000000000000000000 229 | opts.nonce = 712; 230 | var miner = new Miner(opts); 231 | miner.on('block', function(block) { 232 | block.header.validProofOfWork().should.equal(true); 233 | block.transactions.length.should.equal(3); 234 | cb(); 235 | }); 236 | for (var i = 0; i < 100; i++) { 237 | miner.work(); 238 | } 239 | var tx = new Transaction() 240 | .from(coinbases[1]) 241 | .to(id.publicKey) 242 | .colored(0x00ff00ff) 243 | .sign(id); 244 | miner.addTransaction(tx); 245 | for (i = 0; i < 100; i++) { 246 | miner.work(); 247 | } 248 | var tx2 = new Transaction() 249 | .from(tx) 250 | .to(id.publicKey) 251 | .colored(0xffffffff) 252 | .sign(id); 253 | miner.addTransaction(tx2); 254 | miner.run(); 255 | }); 256 | 257 | 258 | }); 259 | -------------------------------------------------------------------------------- /lib/crypto/ecdsa.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var BN = require('./bn'); 4 | var Point = require('./point'); 5 | var Signature = require('./signature'); 6 | var PublicKey = require('../publickey'); 7 | var Random = require('./random'); 8 | var Hash = require('./hash'); 9 | var BufferUtil = require('../util/buffer'); 10 | var _ = require('lodash'); 11 | var $ = require('../util/preconditions'); 12 | 13 | var ECDSA = function ECDSA(obj) { 14 | if (!(this instanceof ECDSA)) { 15 | return new ECDSA(obj); 16 | } 17 | if (obj) { 18 | this.set(obj); 19 | } 20 | }; 21 | 22 | /* jshint maxcomplexity: 9 */ 23 | ECDSA.prototype.set = function(obj) { 24 | this.hashbuf = obj.hashbuf || this.hashbuf; 25 | this.endian = obj.endian || this.endian; //the endianness of hashbuf 26 | this.privkey = obj.privkey || this.privkey; 27 | this.pubkey = obj.pubkey || (this.privkey ? this.privkey.publicKey : this.pubkey); 28 | this.sig = obj.sig || this.sig; 29 | this.k = obj.k || this.k; 30 | this.verified = obj.verified || this.verified; 31 | return this; 32 | }; 33 | 34 | ECDSA.prototype.privkey2pubkey = function() { 35 | this.pubkey = this.privkey.toPublicKey(); 36 | }; 37 | 38 | ECDSA.prototype.calci = function() { 39 | for (var i = 0; i < 4; i++) { 40 | this.sig.i = i; 41 | var Qprime; 42 | try { 43 | Qprime = this.toPublicKey(); 44 | } catch (e) { 45 | console.error(e); 46 | continue; 47 | } 48 | 49 | if (Qprime.point.eq(this.pubkey.point)) { 50 | return this; 51 | } 52 | } 53 | 54 | this.sig.i = undefined; 55 | throw new Error('Unable to find valid recovery factor'); 56 | }; 57 | 58 | ECDSA.fromString = function(str) { 59 | var obj = JSON.parse(str); 60 | return new ECDSA(obj); 61 | }; 62 | 63 | ECDSA.prototype.randomK = function() { 64 | var N = Point.getN(); 65 | var k; 66 | do { 67 | k = BN.fromBuffer(Random.getRandomBuffer(32)); 68 | } while (!(k.lt(N) && k.gt(BN.Zero))); 69 | this.k = k; 70 | return this; 71 | }; 72 | 73 | 74 | // https://tools.ietf.org/html/rfc6979#section-3.2 75 | ECDSA.prototype.deterministicK = function(badrs) { 76 | /* jshint maxstatements: 25 */ 77 | // if r or s were invalid when this function was used in signing, 78 | // we do not want to actually compute r, s here for efficiency, so, 79 | // we can increment badrs. explained at end of RFC 6979 section 3.2 80 | if (_.isUndefined(badrs)) { 81 | badrs = 0; 82 | } 83 | var v = new Buffer(32); 84 | v.fill(0x01); 85 | var k = new Buffer(32); 86 | k.fill(0x00); 87 | var x = this.privkey.bn.toBuffer({ 88 | size: 32 89 | }); 90 | k = Hash.sha256hmac(Buffer.concat([v, new Buffer([0x00]), x, this.hashbuf]), k); 91 | v = Hash.sha256hmac(v, k); 92 | k = Hash.sha256hmac(Buffer.concat([v, new Buffer([0x01]), x, this.hashbuf]), k); 93 | v = Hash.sha256hmac(v, k); 94 | v = Hash.sha256hmac(v, k); 95 | var T = BN.fromBuffer(v); 96 | var N = Point.getN(); 97 | 98 | // also explained in 3.2, we must ensure T is in the proper range (0, N) 99 | for (var i = 0; i < badrs || !(T.lt(N) && T.gt(BN.Zero)); i++) { 100 | k = Hash.sha256hmac(Buffer.concat([v, new Buffer([0x00])]), k); 101 | v = Hash.sha256hmac(v, k); 102 | v = Hash.sha256hmac(v, k); 103 | T = BN.fromBuffer(v); 104 | } 105 | 106 | this.k = T; 107 | return this; 108 | }; 109 | 110 | // Information about public key recovery: 111 | // https://bitcointalk.org/index.php?topic=6430.0 112 | // http://stackoverflow.com/questions/19665491/how-do-i-get-an-ecdsa-public-key-from-just-a-bitcoin-signature-sec1-4-1-6-k 113 | ECDSA.prototype.toPublicKey = function() { 114 | /* jshint maxstatements: 25 */ 115 | var i = this.sig.i; 116 | $.checkArgument(i === 0 || i === 1 || i === 2 || i === 3, new Error('i must be equal to 0, 1, 2, or 3')); 117 | 118 | var e = BN.fromBuffer(this.hashbuf); 119 | var r = this.sig.r; 120 | var s = this.sig.s; 121 | 122 | // A set LSB signifies that the y-coordinate is odd 123 | var isYOdd = i & 1; 124 | 125 | // The more significant bit specifies whether we should use the 126 | // first or second candidate key. 127 | var isSecondKey = i >> 1; 128 | 129 | var n = Point.getN(); 130 | var G = Point.getG(); 131 | 132 | // 1.1 Let x = r + jn 133 | var x = isSecondKey ? r.add(n) : r; 134 | var R = Point.fromX(isYOdd, x); 135 | 136 | // 1.4 Check that nR is at infinity 137 | var nR = R.mul(n); 138 | 139 | if (!nR.isInfinity()) { 140 | throw new Error('nR is not a valid curve point'); 141 | } 142 | 143 | // Compute -e from e 144 | var eNeg = e.neg().mod(n); 145 | 146 | // 1.6.1 Compute Q = r^-1 (sR - eG) 147 | // Q = r^-1 (sR + -eG) 148 | var rInv = r.invm(n); 149 | 150 | //var Q = R.multiplyTwo(s, G, eNeg).mul(rInv); 151 | var Q = R.mul(s).add(G.mul(eNeg)).mul(rInv); 152 | 153 | var pubkey = PublicKey.fromPoint(Q); 154 | 155 | return pubkey; 156 | }; 157 | 158 | ECDSA.prototype.sigError = function() { 159 | /* jshint maxstatements: 25 */ 160 | if (!BufferUtil.isBuffer(this.hashbuf) || this.hashbuf.length !== 32) { 161 | return 'hashbuf must be a 32 byte buffer'; 162 | } 163 | 164 | var r = this.sig.r; 165 | var s = this.sig.s; 166 | if (!(r.gt(BN.Zero) && r.lt(Point.getN())) || !(s.gt(BN.Zero) && s.lt(Point.getN()))) { 167 | return 'r and s not in range'; 168 | } 169 | 170 | var e = BN.fromBuffer(this.hashbuf, this.endian ? { 171 | endian: this.endian 172 | } : undefined); 173 | var n = Point.getN(); 174 | var sinv = s.invm(n); 175 | var u1 = sinv.mul(e).mod(n); 176 | var u2 = sinv.mul(r).mod(n); 177 | 178 | var pubkeyPoint = this.pubkey.point; 179 | var p = Point 180 | .getG() 181 | .mulAdd(u1, pubkeyPoint, u2); 182 | if (p.isInfinity()) { 183 | return 'p is infinity'; 184 | } 185 | 186 | if (p.getX().mod(n).cmp(r) !== 0) { 187 | return 'Invalid signature'; 188 | } else { 189 | return false; 190 | } 191 | }; 192 | 193 | ECDSA.toLowS = function(s) { 194 | //enforce low s 195 | //see BIP 62, "low S values in signatures" 196 | if (s.gt(BN.fromBuffer(new Buffer('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0', 'hex')))) { 197 | s = Point.getN().sub(s); 198 | } 199 | return s; 200 | }; 201 | 202 | ECDSA.prototype._findSignature = function(d, e) { 203 | var N = Point.getN(); 204 | var G = Point.getG(); 205 | // try different values of k until r, s are valid 206 | var badrs = 0; 207 | var k, Q, r, s; 208 | do { 209 | if (!this.k || badrs > 0) { 210 | this.deterministicK(badrs); 211 | } 212 | badrs++; 213 | k = this.k; 214 | Q = G.mul(k); 215 | r = Q.x.mod(N); 216 | s = k.invm(N).mul(e.add(d.mul(r))).mod(N); 217 | } while (r.cmp(BN.Zero) <= 0 || s.cmp(BN.Zero) <= 0); 218 | 219 | s = ECDSA.toLowS(s); 220 | return { 221 | s: s, 222 | r: r 223 | }; 224 | 225 | }; 226 | 227 | ECDSA.prototype.sign = function() { 228 | var hashbuf = this.hashbuf; 229 | var privkey = this.privkey; 230 | var d = privkey.bn; 231 | 232 | $.checkState(hashbuf && privkey && d, new Error('invalid parameters')); 233 | $.checkState(BufferUtil.isBuffer(hashbuf) && hashbuf.length === 32, new Error('hashbuf must be a 32 byte buffer')); 234 | 235 | var e = BN.fromBuffer(hashbuf, this.endian ? { 236 | endian: this.endian 237 | } : undefined); 238 | 239 | var obj = this._findSignature(d, e); 240 | 241 | this.sig = new Signature(obj); 242 | return this; 243 | }; 244 | 245 | ECDSA.prototype.toString = function() { 246 | var obj = {}; 247 | if (this.hashbuf) { 248 | obj.hashbuf = this.hashbuf.toString('hex'); 249 | } 250 | if (this.privkey) { 251 | obj.privkey = this.privkey.toString(); 252 | } 253 | if (this.pubkey) { 254 | obj.pubkey = this.pubkey.toString(); 255 | } 256 | if (this.sig) { 257 | obj.sig = this.sig.toString(); 258 | } 259 | if (this.k) { 260 | obj.k = this.k.toString(); 261 | } 262 | return JSON.stringify(obj); 263 | }; 264 | 265 | ECDSA.prototype.verify = function() { 266 | if (!this.sigError()) { 267 | this.verified = true; 268 | } else { 269 | this.verified = false; 270 | } 271 | return this; 272 | }; 273 | 274 | ECDSA.sign = function(hashbuf, privkey, endian) { 275 | return ECDSA().set({ 276 | hashbuf: hashbuf, 277 | endian: endian, 278 | privkey: privkey 279 | }).sign().sig; 280 | }; 281 | 282 | ECDSA.verify = function(hashbuf, sig, pubkey, endian) { 283 | return ECDSA() 284 | .set({ 285 | hashbuf: hashbuf, 286 | endian: endian, 287 | sig: sig, 288 | pubkey: pubkey 289 | }) 290 | .verify() 291 | .verified; 292 | }; 293 | 294 | module.exports = ECDSA; 295 | -------------------------------------------------------------------------------- /lib/blockchain.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var events = require('events'); 4 | var util = require('util'); 5 | var _ = require('lodash'); 6 | 7 | var $ = require('./util/preconditions'); 8 | 9 | var Sighash = require('./transaction/sighash'); 10 | var BlockStore = require('./store/block'); 11 | var TransactionStore = require('./store/transaction'); 12 | 13 | var errors = require('./errors'); 14 | 15 | var NULL = '0000000000000000000000000000000000000000000000000000000000000000'; 16 | 17 | var MAX_REWIND = 100; 18 | 19 | var neighbors = function(pos) { 20 | return [ 21 | {x: pos.x -1, y: pos.y}, 22 | {x: pos.x +1, y: pos.y}, 23 | {x: pos.x, y: pos.y - 1}, 24 | {x: pos.x, y: pos.y + 1} 25 | ]; 26 | }; 27 | 28 | var posToString = function(pos) { 29 | return pos.x + '_' + pos.y; 30 | }; 31 | 32 | function Blockchain() { 33 | events.EventEmitter.call(this); 34 | this.tip = NULL; 35 | this.work = {}; 36 | this.work[NULL] = 0; 37 | this.height = {}; 38 | this.height[NULL] = -1; 39 | this.hashByHeight = { 40 | '-1': NULL 41 | }; 42 | this.next = {}; 43 | this.prev = {}; 44 | 45 | this.pixels = {}; 46 | 47 | this.blockStore = new BlockStore(); 48 | this.txStore = new TransactionStore(); 49 | } 50 | util.inherits(Blockchain, events.EventEmitter); 51 | 52 | Blockchain.NULL = NULL; 53 | 54 | Blockchain.fromObject = function(obj) { 55 | var blockchain = new Blockchain(); 56 | blockchain.tip = obj.tip; 57 | blockchain.work = obj.work; 58 | blockchain.hashByHeight = obj.hashByHeight; 59 | blockchain.height = obj.height; 60 | blockchain.next = obj.next; 61 | blockchain.prev = obj.prev; 62 | return blockchain; 63 | }; 64 | 65 | var getWork = function(hash) { 66 | // TODO: Calculate work 67 | return 1; 68 | }; 69 | 70 | Blockchain.prototype.addHashReferences = function(block) { 71 | 72 | var self = this; 73 | var prevHash = block.prevHash; 74 | var hash = block.hash; 75 | 76 | this.work[hash] = this.work[prevHash] + getWork(hash); 77 | this.prev[hash] = prevHash; 78 | }; 79 | 80 | Blockchain.prototype.saveBlockToStore = function(block) { 81 | this.blockStore.set(block); 82 | this.saveTxToStore(block); 83 | }; 84 | 85 | Blockchain.prototype.saveTxToStore = function(block) { 86 | var self = this; 87 | block.transactions.map(function(tx) { 88 | self.txStore.set(tx); 89 | }); 90 | }; 91 | 92 | Blockchain.prototype.isValidBlock = function(block) { 93 | try { 94 | this.checkValidBlock(block); 95 | } catch (e) { 96 | return false; 97 | } 98 | return true; 99 | }; 100 | 101 | Blockchain.prototype.checkValidBlock = function(block) { 102 | if (_.isUndefined(this.work[block.prevHash])) { 103 | throw new errors.Blockchain.MissingParent(block.hash, block.prevHash); 104 | } 105 | var coinbase = block.transactions[0]; 106 | var coinbasePos = posToString(coinbase.position); 107 | if (this.pixels[coinbasePos]) { 108 | throw new errors.Blockchain.PixelMined(coinbasePos); 109 | } 110 | var neighborList = neighbors(coinbase.position); 111 | var adjacent = block.height === 0; 112 | for (var i = 0; i < neighborList.length; i++) { 113 | if (this.pixels[posToString(neighborList[i])]) { 114 | adjacent = true; 115 | } 116 | } 117 | if (!adjacent) { 118 | throw new errors.Blockchain.NotAdjacent(); 119 | } 120 | var prevTx = {}; 121 | prevTx[coinbasePos] = coinbase; 122 | for (var i = 1; i < block.transactions.length; i++) { 123 | var tx = block.transactions[i]; 124 | var pos = posToString(tx.position); 125 | if (!prevTx[pos]) { 126 | prevTx[pos] = this.pixels[pos]; 127 | } 128 | if (!Sighash.verify(tx, tx.signature, prevTx[pos].owner)) { 129 | throw new errors.Blockchain.SignatureMismatch(tx, block.hash, i); 130 | } 131 | prevTx[pos] = tx; 132 | } 133 | return true; 134 | }; 135 | 136 | Blockchain.prototype._appendNewBlock = function(hash) { 137 | var toUnconfirm = []; 138 | var toConfirm = []; 139 | var self = this; 140 | 141 | // console.log('Appending new block' + hash); 142 | 143 | var pointer = hash; 144 | while (_.isUndefined(this.height[pointer])) { 145 | toConfirm.push(pointer); 146 | pointer = this.prev[pointer]; 147 | } 148 | var commonAncestor = pointer; 149 | 150 | pointer = this.tip; 151 | while (pointer !== commonAncestor) { 152 | toUnconfirm.push(pointer); 153 | pointer = this.prev[pointer]; 154 | } 155 | 156 | toConfirm.reverse(); 157 | toUnconfirm.map(function(hash) { 158 | self.unconfirm(this.blockStore.get(hash)); 159 | }, this); 160 | try { 161 | toConfirm.map(function(hash) { 162 | var block = this.blockStore.get(hash); 163 | $.checkArgument(this.isValidBlock(block)); 164 | self.confirm(block); 165 | }, this); 166 | } catch (e) { 167 | // console.log('Rollback: ' + e.message); 168 | toUnconfirm.reverse(); 169 | toUnconfirm.map(function(hash) { 170 | self.confirm(this.blockStore.get(hash)); 171 | }, this); 172 | 173 | throw e; 174 | } 175 | return { 176 | unconfirmed: toUnconfirm, 177 | confirmed: toConfirm 178 | }; 179 | }; 180 | 181 | Blockchain.prototype.proposeNewBlock = function(block) { 182 | var prevHash = block.prevHash; 183 | var hash = block.hash; 184 | 185 | $.checkState(this.hasData(prevHash), 'No previous data to estimate work'); 186 | this.saveBlockToStore(block); 187 | this.addHashReferences(block); 188 | 189 | var work = this.work[hash]; 190 | var tipWork = this.work[this.tip]; 191 | $.checkState(!_.isUndefined(work), 'No work found for ' + hash); 192 | $.checkState(!_.isUndefined(tipWork), 'No work found for tip ' + this.tip); 193 | 194 | // console.log('Tip has ' + tipWork + '(hash ' + this.tip + '); new block has '+work+' (hash '+hash+')'); 195 | if (work > tipWork) { 196 | return this._appendNewBlock(hash); 197 | } 198 | return { 199 | unconfirmed: [], 200 | confirmed: [] 201 | }; 202 | }; 203 | 204 | Blockchain.prototype.confirm = function(block) { 205 | var hash = block.hash; 206 | var prevHash = this.prev[hash]; 207 | $.checkState( 208 | prevHash !== NULL || prevHash === this.tip, 209 | 'Attempting to confirm a non-contiguous block.' 210 | ); 211 | 212 | this.tip = hash; 213 | var height = this.height[prevHash] + 1; 214 | this.next[prevHash] = hash; 215 | this.hashByHeight[height] = hash; 216 | this.height[hash] = height; 217 | 218 | for (var i = 0; i < block.transactions.length; i++) { 219 | var tx = block.transactions[i]; 220 | var pos = posToString(tx.position); 221 | // console.log('Update: pixel pos' + pos + ' set to ' + block.hash + ':' + i); 222 | this.pixels[pos] = tx; 223 | } 224 | }; 225 | 226 | Blockchain.prototype.unconfirm = function(block) { 227 | var hash = block.hash; 228 | var prevHash = this.prev[hash]; 229 | $.checkState(hash === this.tip, 'Attempting to unconfirm a non-tip block'); 230 | 231 | this.tip = prevHash; 232 | var height = this.height[hash]; 233 | delete this.next[prevHash]; 234 | delete this.hashByHeight[height]; 235 | delete this.height[hash]; 236 | 237 | for (var i = block.transactions.length - 1; i > 0; i--) { 238 | var tx = block.transactions[i]; 239 | var prevTx = this.txStore.get(tx.input); 240 | this.pixels[posToString(prevTx.position)] = prevTx; 241 | } 242 | delete this.pixels[posToString(block.transactions[0].position)]; 243 | }; 244 | 245 | Blockchain.prototype.hasData = function(hash) { 246 | if (hash === NULL) { 247 | return true; 248 | } 249 | return !_.isUndefined(this.work[hash]); 250 | }; 251 | 252 | Blockchain.prototype.prune = function() { 253 | var self = this; 254 | _.each(this.prev, function(key) { 255 | if (!self.height[key]) { 256 | delete self.prev[key]; 257 | delete self.work[key]; 258 | } 259 | }); 260 | }; 261 | 262 | Blockchain.prototype.toObject = function() { 263 | return { 264 | tip: this.tip, 265 | work: this.work, 266 | next: this.next, 267 | hashByHeight: this.hashByHeight, 268 | height: this.height, 269 | prev: this.prev 270 | }; 271 | }; 272 | 273 | Blockchain.prototype.toJSON = function() { 274 | return JSON.stringify(this.toObject()); 275 | }; 276 | 277 | Blockchain.prototype.getBlockLocator = function() { 278 | $.checkState(this.tip); 279 | $.checkState(!_.isUndefined(this.height[this.tip])); 280 | 281 | var result = []; 282 | var currentHeight = this.getCurrentHeight(); 283 | var exponentialBackOff = 1; 284 | for (var i = 0; i < 10; i++) { 285 | if (currentHeight >= 0) { 286 | result.push(this.hashByHeight[currentHeight--]); 287 | } 288 | } 289 | while (currentHeight > 0) { 290 | result.push(this.hashByHeight[currentHeight]); 291 | currentHeight -= exponentialBackOff; 292 | exponentialBackOff *= 2; 293 | } 294 | return result; 295 | }; 296 | 297 | Blockchain.prototype.getCurrentHeight = function() { 298 | return this.height[this.tip]; 299 | }; 300 | 301 | Blockchain.prototype.getBlock = function(hash) { 302 | return this.blockStore.get(hash); 303 | }; 304 | 305 | Blockchain.prototype.getTransaction = function(hash) { 306 | return this.txStore.get(hash); 307 | }; 308 | 309 | Blockchain.prototype.getTipBlock = function() { 310 | return this.blockStore.get(this.tip); 311 | }; 312 | 313 | module.exports = Blockchain; 314 | -------------------------------------------------------------------------------- /test/crypto/signature.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var should = require('chai').should(); 4 | var bitcore = require('../..'); 5 | var BN = bitcore.crypto.BN; 6 | var Signature = bitcore.crypto.Signature; 7 | 8 | describe('Signature', function() { 9 | 10 | it('should make a blank signature', function() { 11 | var sig = new Signature(); 12 | should.exist(sig); 13 | }); 14 | 15 | it('should work with conveniently setting r, s', function() { 16 | var r = new BN(); 17 | var s = new BN(); 18 | var sig = new Signature(r, s); 19 | should.exist(sig); 20 | sig.r.toString().should.equal(r.toString()); 21 | sig.s.toString().should.equal(s.toString()); 22 | }); 23 | 24 | describe('#set', function() { 25 | 26 | it('should set compressed', function() { 27 | should.exist(Signature().set({ 28 | compressed: true 29 | })); 30 | }); 31 | 32 | }); 33 | 34 | describe('#fromCompact', function() { 35 | 36 | it('should create a signature from a compressed signature', function() { 37 | var blank = new Buffer(32); 38 | blank.fill(0); 39 | var compressed = Buffer.concat([ 40 | new Buffer([0 + 27 + 4]), 41 | blank, 42 | blank 43 | ]); 44 | var sig = Signature.fromCompact(compressed); 45 | sig.r.cmp(BN.Zero).should.equal(0); 46 | sig.s.cmp(BN.Zero).should.equal(0); 47 | }); 48 | 49 | }); 50 | 51 | describe('#fromDER', function() { 52 | 53 | var buf = new Buffer('3044022075fc517e541bd54769c080b64397e32161c850f6c1b2b67a5c433affbb3e62770220729e85cc46ffab881065ec07694220e71d4df9b2b8c8fd12c3122cf3a5efbcf2', 'hex'); 54 | 55 | it('should parse this DER format signature', function() { 56 | var sig = Signature.fromDER(buf); 57 | sig.r.toBuffer({ 58 | size: 32 59 | }).toString('hex').should.equal('75fc517e541bd54769c080b64397e32161c850f6c1b2b67a5c433affbb3e6277'); 60 | sig.s.toBuffer({ 61 | size: 32 62 | }).toString('hex').should.equal('729e85cc46ffab881065ec07694220e71d4df9b2b8c8fd12c3122cf3a5efbcf2'); 63 | }); 64 | 65 | }); 66 | 67 | describe('#fromString', function() { 68 | 69 | var buf = new Buffer('3044022075fc517e541bd54769c080b64397e32161c850f6c1b2b67a5c433affbb3e62770220729e85cc46ffab881065ec07694220e71d4df9b2b8c8fd12c3122cf3a5efbcf2', 'hex'); 70 | 71 | it('should parse this DER format signature in hex', function() { 72 | var sig = Signature.fromString(buf.toString('hex')); 73 | sig.r.toBuffer({ 74 | size: 32 75 | }).toString('hex').should.equal('75fc517e541bd54769c080b64397e32161c850f6c1b2b67a5c433affbb3e6277'); 76 | sig.s.toBuffer({ 77 | size: 32 78 | }).toString('hex').should.equal('729e85cc46ffab881065ec07694220e71d4df9b2b8c8fd12c3122cf3a5efbcf2'); 79 | }); 80 | 81 | }); 82 | 83 | describe('#parseDER', function() { 84 | 85 | it('should parse this signature generated in node', function() { 86 | var sighex = '30450221008bab1f0a2ff2f9cb8992173d8ad73c229d31ea8e10b0f4d4ae1a0d8ed76021fa02200993a6ec81755b9111762fc2cf8e3ede73047515622792110867d12654275e72'; 87 | var sig = new Buffer(sighex, 'hex'); 88 | var parsed = Signature.parseDER(sig); 89 | parsed.header.should.equal(0x30); 90 | parsed.length.should.equal(69); 91 | parsed.rlength.should.equal(33); 92 | parsed.rneg.should.equal(true); 93 | parsed.rbuf.toString('hex').should.equal('008bab1f0a2ff2f9cb8992173d8ad73c229d31ea8e10b0f4d4ae1a0d8ed76021fa'); 94 | parsed.r.toString().should.equal('63173831029936981022572627018246571655303050627048489594159321588908385378810'); 95 | parsed.slength.should.equal(32); 96 | parsed.sneg.should.equal(false); 97 | parsed.sbuf.toString('hex').should.equal('0993a6ec81755b9111762fc2cf8e3ede73047515622792110867d12654275e72'); 98 | parsed.s.toString().should.equal('4331694221846364448463828256391194279133231453999942381442030409253074198130'); 99 | }); 100 | 101 | it('should parse this 69 byte signature', function() { 102 | var sighex = '3043021f59e4705959cc78acbfcf8bd0114e9cc1b389a4287fb33152b73a38c319b50302202f7428a27284c757e409bf41506183e9e49dfb54d5063796dfa0d403a4deccfa'; 103 | var sig = new Buffer(sighex, 'hex'); 104 | var parsed = Signature.parseDER(sig); 105 | parsed.header.should.equal(0x30); 106 | parsed.length.should.equal(67); 107 | parsed.rlength.should.equal(31); 108 | parsed.rneg.should.equal(false); 109 | parsed.rbuf.toString('hex').should.equal('59e4705959cc78acbfcf8bd0114e9cc1b389a4287fb33152b73a38c319b503'); 110 | parsed.r.toString().should.equal('158826015856106182499128681792325160381907915189052224498209222621383996675'); 111 | parsed.slength.should.equal(32); 112 | parsed.sneg.should.equal(false); 113 | parsed.sbuf.toString('hex').should.equal('2f7428a27284c757e409bf41506183e9e49dfb54d5063796dfa0d403a4deccfa'); 114 | parsed.s.toString().should.equal('21463938592353267769710297084836796652964571266930856168996063301532842380538'); 115 | }); 116 | 117 | it('should parse this 68 byte signature', function() { 118 | var sighex = '3042021e17cfe77536c3fb0526bd1a72d7a8e0973f463add210be14063c8a9c37632022061bfa677f825ded82ba0863fb0c46ca1388dd3e647f6a93c038168b59d131a51'; 119 | var sig = new Buffer(sighex, 'hex'); 120 | var parsed = Signature.parseDER(sig); 121 | parsed.header.should.equal(0x30); 122 | parsed.length.should.equal(66); 123 | parsed.rlength.should.equal(30); 124 | parsed.rneg.should.equal(false); 125 | parsed.rbuf.toString('hex').should.equal('17cfe77536c3fb0526bd1a72d7a8e0973f463add210be14063c8a9c37632'); 126 | parsed.r.toString().should.equal('164345250294671732127776123343329699648286106708464198588053542748255794'); 127 | parsed.slength.should.equal(32); 128 | parsed.sneg.should.equal(false); 129 | parsed.sbuf.toString('hex').should.equal('61bfa677f825ded82ba0863fb0c46ca1388dd3e647f6a93c038168b59d131a51'); 130 | parsed.s.toString().should.equal('44212963026209759051804639008236126356702363229859210154760104982946304432721'); 131 | }); 132 | 133 | it('should parse this signature from script_valid.json', function() { 134 | var sighex = '304502203e4516da7253cf068effec6b95c41221c0cf3a8e6ccb8cbf1725b562e9afde2c022100ab1e3da73d67e32045a20e0b999e049978ea8d6ee5480d485fcf2ce0d03b2ef051'; 135 | var sig = Buffer(sighex, 'hex'); 136 | var parsed = Signature.parseDER(sig, false); 137 | should.exist(parsed); 138 | }); 139 | 140 | }); 141 | 142 | describe('#toDER', function() { 143 | 144 | it('should convert these known r and s values into a known signature', function() { 145 | var r = new BN('63173831029936981022572627018246571655303050627048489594159321588908385378810'); 146 | var s = new BN('4331694221846364448463828256391194279133231453999942381442030409253074198130'); 147 | var sig = new Signature({ 148 | r: r, 149 | s: s 150 | }); 151 | var der = sig.toDER(r, s); 152 | der.toString('hex').should.equal('30450221008bab1f0a2ff2f9cb8992173d8ad73c229d31ea8e10b0f4d4ae1a0d8ed76021fa02200993a6ec81755b9111762fc2cf8e3ede73047515622792110867d12654275e72'); 153 | }); 154 | 155 | }); 156 | 157 | describe('#toString', function() { 158 | it('should convert this signature in to hex DER', function() { 159 | var r = new BN('63173831029936981022572627018246571655303050627048489594159321588908385378810'); 160 | var s = new BN('4331694221846364448463828256391194279133231453999942381442030409253074198130'); 161 | var sig = new Signature({ 162 | r: r, 163 | s: s 164 | }); 165 | var hex = sig.toString(); 166 | hex.should.equal('30450221008bab1f0a2ff2f9cb8992173d8ad73c229d31ea8e10b0f4d4ae1a0d8ed76021fa02200993a6ec81755b9111762fc2cf8e3ede73047515622792110867d12654275e72'); 167 | }); 168 | }); 169 | 170 | 171 | describe('@isTxDER', function() { 172 | it('should know this is a DER signature', function() { 173 | var sighex = '3042021e17cfe77536c3fb0526bd1a72d7a8e0973f463add210be14063c8a9c37632022061bfa677f825ded82ba0863fb0c46ca1388dd3e647f6a93c038168b59d131a5101'; 174 | var sigbuf = new Buffer(sighex, 'hex'); 175 | Signature.isTxDER(sigbuf).should.equal(true); 176 | }); 177 | 178 | it('should know this is not a DER signature', function() { 179 | //for more extensive tests, see the script interpreter 180 | var sighex = '3042021e17cfe77536c3fb0526bd1a72d7a8e0973f463add210be14063c8a9c37632022061bfa677f825ded82ba0863fb0c46ca1388dd3e647f6a93c038168b59d131a5101'; 181 | var sigbuf = new Buffer(sighex, 'hex'); 182 | sigbuf[0] = 0x31; 183 | Signature.isTxDER(sigbuf).should.equal(false); 184 | }); 185 | 186 | 187 | }); 188 | describe('#hasLowS', function() { 189 | it('should detect high and low S', function() { 190 | var r = new BN('63173831029936981022572627018246571655303050627048489594159321588908385378810'); 191 | var s = new BN('4331694221846364448463828256391194279133231453999942381442030409253074198130'); 192 | var s2 = new BN('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B2000'); 193 | var sig = new Signature({ 194 | r: r, 195 | s: s 196 | }); 197 | var sig2 = new Signature({ 198 | r: r, 199 | s: s2 200 | }); 201 | sig2.hasLowS().should.equal(true); 202 | sig.hasLowS().should.equal(false); 203 | 204 | }); 205 | }); 206 | 207 | }); 208 | -------------------------------------------------------------------------------- /lib/crypto/signature.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var BN = require('./bn'); 4 | var _ = require('lodash'); 5 | var $ = require('../util/preconditions'); 6 | var BufferUtil = require('../util/buffer'); 7 | 8 | var Signature = function Signature(r, s) { 9 | if (!(this instanceof Signature)) { 10 | return new Signature(r, s); 11 | } 12 | if (r instanceof BN) { 13 | this.set({ 14 | r: r, 15 | s: s 16 | }); 17 | } else if (r) { 18 | var obj = r; 19 | this.set(obj); 20 | } 21 | }; 22 | 23 | /* jshint maxcomplexity: 7 */ 24 | Signature.prototype.set = function(obj) { 25 | this.r = obj.r || this.r || undefined; 26 | this.s = obj.s || this.s || undefined; 27 | this.i = typeof obj.i !== 'undefined' ? obj.i : this.i; //public key recovery parameter in range [0, 3] 28 | this.compressed = typeof obj.compressed !== 'undefined' ? 29 | obj.compressed : this.compressed; //whether the recovered pubkey is compressed 30 | return this; 31 | }; 32 | 33 | Signature.fromCompact = function(buf) { 34 | var sig = new Signature(); 35 | var compressed = true; 36 | var i = buf.slice(0, 1)[0] - 27 - 4; 37 | var b2 = buf.slice(1, 33); 38 | var b3 = buf.slice(33, 65); 39 | 40 | $.checkArgument(i === 0 || i === 1 || i === 2 || i === 3, new Error('i must be 0, 1, 2, or 3')); 41 | $.checkArgument(b2.length === 32, new Error('r must be 32 bytes')); 42 | $.checkArgument(b3.length === 32, new Error('s must be 32 bytes')); 43 | 44 | sig.compressed = compressed; 45 | sig.i = i; 46 | sig.r = BN.fromBuffer(b2); 47 | sig.s = BN.fromBuffer(b3); 48 | 49 | return sig; 50 | }; 51 | 52 | Signature.fromDER = Signature.fromBuffer = function(buf, strict) { 53 | var obj = Signature.parseDER(buf, strict); 54 | var sig = new Signature(); 55 | 56 | sig.r = obj.r; 57 | sig.s = obj.s; 58 | 59 | return sig; 60 | }; 61 | 62 | Signature.fromString = function(str) { 63 | var buf = new Buffer(str, 'hex'); 64 | return Signature.fromDER(buf); 65 | }; 66 | 67 | 68 | /** 69 | * In order to mimic the non-strict DER encoding of OpenSSL, set strict = false. 70 | */ 71 | Signature.parseDER = function(buf, strict) { 72 | $.checkArgument(BufferUtil.isBuffer(buf), new Error('DER formatted signature should be a buffer')); 73 | if (_.isUndefined(strict)) { 74 | strict = true; 75 | } 76 | 77 | var header = buf[0]; 78 | $.checkArgument(header === 0x30, new Error('Header byte should be 0x30')); 79 | 80 | var length = buf[1]; 81 | var buflength = buf.slice(2).length; 82 | $.checkArgument(!strict || length === buflength, new Error('Length byte should length of what follows')); 83 | 84 | length = length < buflength ? length : buflength; 85 | 86 | var rheader = buf[2 + 0]; 87 | $.checkArgument(rheader === 0x02, new Error('Integer byte for r should be 0x02')); 88 | 89 | var rlength = buf[2 + 1]; 90 | var rbuf = buf.slice(2 + 2, 2 + 2 + rlength); 91 | var r = BN.fromBuffer(rbuf); 92 | var rneg = buf[2 + 1 + 1] === 0x00 ? true : false; 93 | $.checkArgument(rlength === rbuf.length, new Error('Length of r incorrect')); 94 | 95 | var sheader = buf[2 + 2 + rlength + 0]; 96 | $.checkArgument(sheader === 0x02, new Error('Integer byte for s should be 0x02')); 97 | 98 | var slength = buf[2 + 2 + rlength + 1]; 99 | var sbuf = buf.slice(2 + 2 + rlength + 2, 2 + 2 + rlength + 2 + slength); 100 | var s = BN.fromBuffer(sbuf); 101 | var sneg = buf[2 + 2 + rlength + 2 + 2] === 0x00 ? true : false; 102 | $.checkArgument(slength === sbuf.length, new Error('Length of s incorrect')); 103 | 104 | var sumlength = 2 + 2 + rlength + 2 + slength; 105 | $.checkArgument(length === sumlength - 2, new Error('Length of signature incorrect')); 106 | 107 | var obj = { 108 | header: header, 109 | length: length, 110 | rheader: rheader, 111 | rlength: rlength, 112 | rneg: rneg, 113 | rbuf: rbuf, 114 | r: r, 115 | sheader: sheader, 116 | slength: slength, 117 | sneg: sneg, 118 | sbuf: sbuf, 119 | s: s 120 | }; 121 | 122 | return obj; 123 | }; 124 | 125 | 126 | Signature.prototype.toCompact = function(i, compressed) { 127 | i = typeof i === 'number' ? i : this.i; 128 | compressed = typeof compressed === 'boolean' ? compressed : this.compressed; 129 | 130 | if (!(i === 0 || i === 1 || i === 2 || i === 3)) { 131 | throw new Error('i must be equal to 0, 1, 2, or 3'); 132 | } 133 | 134 | var val = i + 27 + 4; 135 | if (compressed === false) 136 | val = val - 4; 137 | var b1 = new Buffer([val]); 138 | var b2 = this.r.toBuffer({ 139 | size: 32 140 | }); 141 | var b3 = this.s.toBuffer({ 142 | size: 32 143 | }); 144 | return Buffer.concat([b1, b2, b3]); 145 | }; 146 | 147 | Signature.prototype.toBuffer = Signature.prototype.toDER = function() { 148 | var rnbuf = this.r.toBuffer(); 149 | var snbuf = this.s.toBuffer(); 150 | 151 | var rneg = rnbuf[0] & 0x80 ? true : false; 152 | var sneg = snbuf[0] & 0x80 ? true : false; 153 | 154 | var rbuf = rneg ? Buffer.concat([new Buffer([0x00]), rnbuf]) : rnbuf; 155 | var sbuf = sneg ? Buffer.concat([new Buffer([0x00]), snbuf]) : snbuf; 156 | 157 | var rlength = rbuf.length; 158 | var slength = sbuf.length; 159 | var length = 2 + rlength + 2 + slength; 160 | var rheader = 0x02; 161 | var sheader = 0x02; 162 | var header = 0x30; 163 | 164 | var der = Buffer.concat([new Buffer([header, length, rheader, rlength]), rbuf, new Buffer([sheader, slength]), sbuf]); 165 | return der; 166 | }; 167 | 168 | Signature.prototype.toString = function() { 169 | var buf = this.toDER(); 170 | return buf.toString('hex'); 171 | }; 172 | 173 | /** 174 | * This function is translated from bitcoind's IsDERSignature and is used in 175 | * the script interpreter. This "DER" format actually includes an extra byte, 176 | * the nhashtype, at the end. It is really the tx format, not DER format. 177 | * 178 | * A canonical signature exists of: [30] [total len] [02] [len R] [R] [02] [len S] [S] [hashtype] 179 | * Where R and S are not negative (their first byte has its highest bit not set), and not 180 | * excessively padded (do not start with a 0 byte, unless an otherwise negative number follows, 181 | * in which case a single 0 byte is necessary and even required). 182 | * 183 | * See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623 184 | */ 185 | Signature.isTxDER = function(buf) { 186 | if (buf.length < 9) { 187 | // Non-canonical signature: too short 188 | return false; 189 | } 190 | if (buf.length > 73) { 191 | // Non-canonical signature: too long 192 | return false; 193 | } 194 | if (buf[0] !== 0x30) { 195 | // Non-canonical signature: wrong type 196 | return false; 197 | } 198 | if (buf[1] !== buf.length - 3) { 199 | // Non-canonical signature: wrong length marker 200 | return false; 201 | } 202 | var nLenR = buf[3]; 203 | if (5 + nLenR >= buf.length) { 204 | // Non-canonical signature: S length misplaced 205 | return false; 206 | } 207 | var nLenS = buf[5 + nLenR]; 208 | if ((nLenR + nLenS + 7) !== buf.length) { 209 | // Non-canonical signature: R+S length mismatch 210 | return false; 211 | } 212 | 213 | var R = buf.slice(4); 214 | if (buf[4 - 2] !== 0x02) { 215 | // Non-canonical signature: R value type mismatch 216 | return false; 217 | } 218 | if (nLenR === 0) { 219 | // Non-canonical signature: R length is zero 220 | return false; 221 | } 222 | if (R[0] & 0x80) { 223 | // Non-canonical signature: R value negative 224 | return false; 225 | } 226 | if (nLenR > 1 && (R[0] === 0x00) && !(R[1] & 0x80)) { 227 | // Non-canonical signature: R value excessively padded 228 | return false; 229 | } 230 | 231 | var S = buf.slice(6 + nLenR); 232 | if (buf[6 + nLenR - 2] !== 0x02) { 233 | // Non-canonical signature: S value type mismatch 234 | return false; 235 | } 236 | if (nLenS === 0) { 237 | // Non-canonical signature: S length is zero 238 | return false; 239 | } 240 | if (S[0] & 0x80) { 241 | // Non-canonical signature: S value negative 242 | return false; 243 | } 244 | if (nLenS > 1 && (S[0] === 0x00) && !(S[1] & 0x80)) { 245 | // Non-canonical signature: S value excessively padded 246 | return false; 247 | } 248 | return true; 249 | }; 250 | 251 | /** 252 | * Compares to bitcoind's IsLowDERSignature 253 | * See also ECDSA signature algorithm which enforces this. 254 | * See also BIP 62, "low S values in signatures" 255 | */ 256 | Signature.prototype.hasLowS = function() { 257 | if (this.s.lt(new BN(1)) || 258 | this.s.gt(new BN('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0'))) { 259 | return false; 260 | } 261 | return true; 262 | }; 263 | 264 | /** 265 | * @returns true if the nhashtype is exactly equal to one of the standard options or combinations thereof. 266 | * Translated from bitcoind's IsDefinedHashtypeSignature 267 | */ 268 | Signature.prototype.hasDefinedHashtype = function() { 269 | if (this.nhashtype < Signature.SIGHASH_ALL || this.nhashtype > Signature.SIGHASH_SINGLE) { 270 | return false; 271 | } 272 | return true; 273 | }; 274 | 275 | Signature.prototype.toTxFormat = function() { 276 | var derbuf = this.toDER(); 277 | var buf = new Buffer(1); 278 | buf.writeUInt8(this.nhashtype, 0); 279 | return Buffer.concat([derbuf, buf]); 280 | }; 281 | 282 | Signature.SIGHASH_ALL = 0x01; 283 | Signature.SIGHASH_NONE = 0x02; 284 | Signature.SIGHASH_SINGLE = 0x03; 285 | Signature.SIGHASH_ANYONECANPAY = 0x80; 286 | 287 | module.exports = Signature; 288 | -------------------------------------------------------------------------------- /lib/block/blockheader.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash'); 4 | var BN = require('../crypto/bn'); 5 | var BufferUtil = require('../util/buffer'); 6 | var BufferReader = require('../encoding/bufferreader'); 7 | var BufferWriter = require('../encoding/bufferwriter'); 8 | var Hash = require('../crypto/hash'); 9 | var JSUtil = require('../util/js'); 10 | var $ = require('../util/preconditions'); 11 | 12 | /** 13 | * Instantiate a BlockHeader from a Buffer, JSON object, or Object with 14 | * the properties of the BlockHeader 15 | * 16 | * @param {*} - A Buffer, JSON string, or Object 17 | * @returns {BlockHeader} - An instance of block header 18 | * @constructor 19 | */ 20 | var BlockHeader = function BlockHeader(arg) { 21 | if (!(this instanceof BlockHeader)) { 22 | return new BlockHeader(arg); 23 | } 24 | _.extend(this, BlockHeader._from(arg)); 25 | return this; 26 | }; 27 | 28 | /** 29 | * @param {*} - A Buffer, JSON string or Object 30 | * @returns {Object} - An object representing block header data 31 | * @throws {TypeError} - If the argument was not recognized 32 | * @private 33 | */ 34 | BlockHeader._from = function _from(arg) { 35 | var info = {}; 36 | if (BufferUtil.isBuffer(arg)) { 37 | info = BlockHeader._fromBufferReader(BufferReader(arg)); 38 | } else if (JSUtil.isValidJSON(arg)) { 39 | info = BlockHeader._fromJSON(arg); 40 | } else if (_.isObject(arg)) { 41 | info = BlockHeader._fromObject(arg); 42 | } else { 43 | throw new TypeError('Unrecognized argument for BlockHeader'); 44 | } 45 | return info; 46 | }; 47 | 48 | /** 49 | * @param {String} - A JSON string 50 | * @returns {Object} - An object representing block header data 51 | * @private 52 | */ 53 | BlockHeader._fromJSON = function _fromJSON(data) { 54 | $.checkArgument(JSUtil.isValidJSON(data), 'data must be a valid JSON string'); 55 | data = JSON.parse(data); 56 | return BlockHeader._fromObject(data); 57 | }; 58 | 59 | /** 60 | * @param {Object} - A JSON string 61 | * @returns {Object} - An object representing block header data 62 | * @private 63 | */ 64 | BlockHeader._fromObject = function _fromObject(data) { 65 | $.checkArgument(data, 'data is required'); 66 | var prevHash = data.prevHash; 67 | var merkleRoot = data.merkleRoot; 68 | if (_.isString(data.prevHash)) { 69 | prevHash = BufferUtil.reverse(new Buffer(data.prevHash, 'hex')); 70 | } 71 | if (_.isString(data.merkleRoot)) { 72 | merkleRoot = BufferUtil.reverse(new Buffer(data.merkleRoot, 'hex')); 73 | } 74 | var info = { 75 | version: data.version, 76 | height: data.height, 77 | time: data.time, 78 | timestamp: data.time, 79 | bits: data.bits, 80 | prevHash: prevHash, 81 | merkleRoot: merkleRoot, 82 | nonce: data.nonce 83 | }; 84 | return info; 85 | }; 86 | 87 | 88 | BlockHeader.prototype.increaseNonce = function() { 89 | this._id = null; 90 | this.nonce += 1; 91 | }; 92 | 93 | /** 94 | * @param {String} - A JSON string or object 95 | * @returns {BlockHeader} - An instance of block header 96 | */ 97 | BlockHeader.fromJSON = function fromJSON(json) { 98 | var info = BlockHeader._fromJSON(json); 99 | return new BlockHeader(info); 100 | }; 101 | 102 | /** 103 | * @param {Object} - A plain javascript object 104 | * @returns {BlockHeader} - An instance of block header 105 | */ 106 | BlockHeader.fromObject = function fromObject(obj) { 107 | var info = BlockHeader._fromObject(obj); 108 | return new BlockHeader(info); 109 | }; 110 | 111 | /** 112 | * @param {Buffer} - A buffer of the block header 113 | * @returns {BlockHeader} - An instance of block header 114 | */ 115 | BlockHeader.fromBuffer = function fromBuffer(buf) { 116 | var info = BlockHeader._fromBufferReader(BufferReader(buf)); 117 | return new BlockHeader(info); 118 | }; 119 | 120 | /** 121 | * @param {string} - A hex encoded buffer of the block header 122 | * @returns {BlockHeader} - An instance of block header 123 | */ 124 | BlockHeader.fromString = function fromString(str) { 125 | var buf = new Buffer(str, 'hex'); 126 | return BlockHeader.fromBuffer(buf); 127 | }; 128 | 129 | 130 | BlockHeader.create = function(data) { 131 | $.checkArgument(!_.isUndefined(data), 'data is required'); 132 | $.checkArgument(!_.isUndefined(data.height), 'data.height is required'); 133 | $.checkArgument(!_.isUndefined(data.prevHash), 'data.prevHash is required'); 134 | data.time = data.time || Math.floor(new Date().getTime() / 1000); 135 | data.nonce = data.nonce || 0; 136 | data.bits = data.bits || BlockHeader.Constants.DEFAULT_BITS; 137 | return new BlockHeader({ 138 | version: BlockHeader.Constants.CURRENT_VERSION, 139 | height: data.height, 140 | time: data.time, 141 | timestamp: data.time, 142 | bits: data.bits, 143 | prevHash: data.prevHash, 144 | merkleRoot: data.merkleRoot, 145 | nonce: data.nonce 146 | }); 147 | 148 | }; 149 | 150 | /** 151 | * @param {BufferReader} - A BufferReader of the block header 152 | * @returns {Object} - An object representing block header data 153 | * @private 154 | */ 155 | BlockHeader._fromBufferReader = function _fromBufferReader(br) { 156 | var info = {}; 157 | info.version = br.readUInt32LE(); 158 | info.height = br.readUInt32LE(); 159 | info.time = br.readUInt32LE(); 160 | info.bits = br.readUInt32LE(); 161 | info.prevHash = br.read(32); 162 | info.merkleRoot = br.read(32); 163 | info.nonce = br.readUInt32LE(); 164 | return info; 165 | }; 166 | 167 | /** 168 | * @param {BufferReader} - A BufferReader of the block header 169 | * @returns {BlockHeader} - An instance of block header 170 | */ 171 | BlockHeader.fromBufferReader = function fromBufferReader(br) { 172 | var info = BlockHeader._fromBufferReader(br); 173 | return new BlockHeader(info); 174 | }; 175 | 176 | /** 177 | * @returns {Object} - A plain object of the BlockHeader 178 | */ 179 | BlockHeader.prototype.toObject = function toObject() { 180 | return { 181 | version: this.version, 182 | height: this.height, 183 | time: this.time, 184 | bits: this.bits, 185 | prevHash: BufferUtil.reverse(this.prevHash).toString('hex'), 186 | merkleRoot: BufferUtil.reverse(this.merkleRoot).toString('hex'), 187 | nonce: this.nonce 188 | }; 189 | }; 190 | 191 | /** 192 | * @returns {string} - A JSON string 193 | */ 194 | BlockHeader.prototype.toJSON = function toJSON() { 195 | return JSON.stringify(this.toObject()); 196 | }; 197 | 198 | /** 199 | * @returns {Buffer} - A Buffer of the BlockHeader 200 | */ 201 | BlockHeader.prototype.toBuffer = function toBuffer() { 202 | return this.toBufferWriter().concat(); 203 | }; 204 | 205 | /** 206 | * @returns {string} - A hex encoded string of the BlockHeader 207 | */ 208 | BlockHeader.prototype.toString = function toString() { 209 | return this.toBuffer().toString('hex'); 210 | }; 211 | 212 | /** 213 | * @param {BufferWriter} - An existing instance BufferWriter 214 | * @returns {BufferWriter} - An instance of BufferWriter representation of the BlockHeader 215 | */ 216 | BlockHeader.prototype.toBufferWriter = function toBufferWriter(bw) { 217 | if (!bw) { 218 | bw = new BufferWriter(); 219 | } 220 | bw.writeUInt32LE(this.version); 221 | bw.writeUInt32LE(this.height); 222 | bw.writeUInt32LE(this.time); 223 | bw.writeUInt32LE(this.bits); 224 | bw.write(this.prevHash); 225 | bw.write(this.merkleRoot); 226 | bw.writeUInt32LE(this.nonce); 227 | return bw; 228 | }; 229 | 230 | /** 231 | * @link https://en.bitcoin.it/wiki/Difficulty 232 | * @returns {BN} - An instance of BN with the decoded difficulty bits 233 | */ 234 | BlockHeader.prototype.getTargetDifficulty = function getTargetDifficulty() { 235 | return BlockHeader.getTargetDifficulty(this.bits); 236 | }; 237 | 238 | BlockHeader.getTargetDifficulty = function(bits) { 239 | $.checkArgument(_.isNumber(bits), 'bits must be a number'); 240 | var target = new BN(bits & 0xffffff); 241 | var mov = 8 * ((bits >>> 24) - 3); 242 | while (mov-- > 0) { 243 | target = target.mul(new BN(2)); 244 | } 245 | return target; 246 | 247 | }; 248 | 249 | BlockHeader.getBits = function(target) { 250 | $.checkArgument(target instanceof BN, 'target must be a BN'); 251 | 252 | var s = target.toString(16); 253 | if (s.length % 2 !== 0) { 254 | s = '0' + s; 255 | } 256 | var exp = (s.length / 2).toString(16); 257 | var mantissa = s.substring(0, 6); 258 | var bits = parseInt(exp + mantissa, 16); 259 | return bits; 260 | }; 261 | 262 | /** 263 | * @returns {Buffer} - The little endian hash buffer of the header 264 | */ 265 | BlockHeader.prototype.getHash = function hash() { 266 | var buf = this.toBuffer(); 267 | return Hash.sha256sha256(buf); 268 | }; 269 | 270 | var idProperty = { 271 | configurable: false, 272 | enumerable: true, 273 | /** 274 | * @returns {string} - The big endian hash buffer of the header 275 | */ 276 | get: function() { 277 | if (!this._id) { 278 | this._id = BufferReader(this.getHash()).readReverse().toString('hex'); 279 | } 280 | return this._id; 281 | }, 282 | set: _.noop 283 | }; 284 | Object.defineProperty(BlockHeader.prototype, 'id', idProperty); 285 | Object.defineProperty(BlockHeader.prototype, 'hash', idProperty); 286 | 287 | /** 288 | * @returns {Boolean} - If timestamp is not too far in the future 289 | */ 290 | BlockHeader.prototype.validTimestamp = function validTimestamp() { 291 | var currentTime = Math.round(new Date().getTime() / 1000); 292 | if (this.time > currentTime + BlockHeader.Constants.MAX_TIME_OFFSET) { 293 | return false; 294 | } 295 | return true; 296 | }; 297 | 298 | /** 299 | * @returns {Boolean} - If the proof-of-work hash satisfies the target difficulty 300 | */ 301 | BlockHeader.prototype.validProofOfWork = function validProofOfWork() { 302 | var pow = new BN(this.id, 'hex'); 303 | var target = this.getTargetDifficulty(); 304 | 305 | if (pow.cmp(target) > 0) { 306 | return false; 307 | } 308 | return true; 309 | }; 310 | 311 | /** 312 | * @returns {string} - A string formated for the console 313 | */ 314 | BlockHeader.prototype.inspect = function inspect() { 315 | return ''; 316 | }; 317 | 318 | BlockHeader.Constants = { 319 | CURRENT_VERSION: 1, 320 | DEFAULT_BITS: 0x207fffff, // target 7fffff0000000000000000000000000000000000000000000000000000000000 321 | START_OF_HEADER: 8, // Start buffer position in raw block data 322 | MAX_TIME_OFFSET: 2 * 60 * 60, // The max a timestamp can be in the future 323 | LARGEST_HASH: new BN('10000000000000000000000000000000000000000000000000000000000000000', 'hex') 324 | }; 325 | 326 | module.exports = BlockHeader; 327 | -------------------------------------------------------------------------------- /test/data/ecdsa.json: -------------------------------------------------------------------------------- 1 | { 2 | "valid": [ 3 | { 4 | "d": "01", 5 | "k": "ec633bd56a5774a0940cb97e27a9e4e51dc94af737596a0c5cbb3d30332d92a5", 6 | "message": "Everything should be made as simple as possible, but not simpler.", 7 | "i": 0, 8 | "signature": { 9 | "r": "23362334225185207751494092901091441011938859014081160902781146257181456271561", 10 | "s": "50433721247292933944369538617440297985091596895097604618403996029256432099938" 11 | } 12 | }, 13 | { 14 | "d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", 15 | "k": "9dc74cbfd383980fb4ae5d2680acddac9dac956dca65a28c80ac9c847c2374e4", 16 | "message": "Equations are more important to me, because politics is for the present, but an equation is something for eternity.", 17 | "i": 0, 18 | "signature": { 19 | "r": "38341707918488238920692284707283974715538935465589664377561695343399725051885", 20 | "s": "3180566392414476763164587487324397066658063772201694230600609996154610926757" 21 | } 22 | }, 23 | { 24 | "d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", 25 | "k": "fd27071f01648ebbdd3e1cfbae48facc9fa97edc43bbbc9a7fdc28eae13296f5", 26 | "message": "Not only is the Universe stranger than we think, it is stranger than we can think.", 27 | "i": 0, 28 | "signature": { 29 | "r": "115464191557905790016094131873849783294273568009648050793030031933291767741904", 30 | "s": "50562520307781850052192542766631199590053690478900449960232079510155113443971" 31 | } 32 | }, 33 | { 34 | "d": "0000000000000000000000000000000000000000000000000000000000000001", 35 | "k": "f0cd2ba5fc7c183de589f6416220a36775a146740798756d8d949f7166dcc87f", 36 | "message": "How wonderful that we have met with a paradox. Now we have some hope of making progress.", 37 | "i": 1, 38 | "signature": { 39 | "r": "87230998027579607140680851455601772643840468630989315269459846730712163783123", 40 | "s": "53231320085894623106179381504478252331065330583563809963303318469380290929875" 41 | } 42 | }, 43 | { 44 | "d": "69ec59eaa1f4f2e36b639716b7c30ca86d9a5375c7b38d8918bd9c0ebc80ba64", 45 | "k": "6bb4a594ad57c1aa22dbe991a9d8501daf4688bf50a4892ef21bd7c711afda97", 46 | "message": "Computer science is no more about computers than astronomy is about telescopes.", 47 | "i": 0, 48 | "signature": { 49 | "r": "51348483531757779992459563033975330355971795607481991320287437101831125115997", 50 | "s": "6277080015686056199074771961940657638578000617958603212944619747099038735862" 51 | } 52 | }, 53 | { 54 | "d": "00000000000000000000000000007246174ab1e92e9149c6e446fe194d072637", 55 | "k": "097b5c8ee22c3ea78a4d3635e0ff6fe85a1eb92ce317ded90b9e71aab2b861cb", 56 | "message": "...if you aren't, at any given time, scandalized by code you wrote five or even three years ago, you're not learning anywhere near enough", 57 | "i": 1, 58 | "signature": { 59 | "r": "113979859486826658566290715281614250298918272782414232881639314569529560769671", 60 | "s": "6517071009538626957379450615706485096874328019806177698938278220732027419959" 61 | } 62 | }, 63 | { 64 | "d": "000000000000000000000000000000000000000000056916d0f9b31dc9b637f3", 65 | "k": "19355c36c8cbcdfb2382e23b194b79f8c97bf650040fc7728dfbf6b39a97c25b", 66 | "message": "The question of whether computers can think is like the question of whether submarines can swim.", 67 | "i": 1, 68 | "signature": { 69 | "r": "93122007060065279508564838030979550535085999589142852106617159184757394422777", 70 | "s": "3078539468410661027472930027406594684630312677495124015420811882501887769839" 71 | } 72 | } 73 | ], 74 | "invalid": { 75 | "sigError": [ 76 | { 77 | "description": "The wrong signature", 78 | "exception": "Invalid signature", 79 | "d": "01", 80 | "message": "foo", 81 | "signature": { 82 | "r": "38341707918488238920692284707283974715538935465589664377561695343399725051885", 83 | "s": "3180566392414476763164587487324397066658063772201694230600609996154610926757" 84 | } 85 | }, 86 | { 87 | "description": "Invalid r value (< 0)", 88 | "exception": "r and s not in range", 89 | "d": "01", 90 | "message": "foo", 91 | "signature": { 92 | "r": "-1", 93 | "s": "2" 94 | } 95 | }, 96 | { 97 | "description": "Invalid r value (== 0)", 98 | "exception": "r and s not in range", 99 | "d": "01", 100 | "message": "foo", 101 | "signature": { 102 | "r": "0", 103 | "s": "2" 104 | } 105 | }, 106 | { 107 | "description": "Invalid r value (>= n)", 108 | "exception": "r and s not in range", 109 | "d": "01", 110 | "message": "foo", 111 | "signature": { 112 | "r": "115792089237316195423570985008687907852837564279074904382605163141518161494337", 113 | "s": "2" 114 | } 115 | }, 116 | { 117 | "description": "Invalid s value (< 0)", 118 | "exception": "r and s not in range", 119 | "d": "01", 120 | "message": "foo", 121 | "signature": { 122 | "r": "2", 123 | "s": "-1" 124 | } 125 | }, 126 | { 127 | "description": "Invalid s value (== 0)", 128 | "exception": "r and s not in range", 129 | "d": "01", 130 | "message": "foo", 131 | "signature": { 132 | "r": "2", 133 | "s": "0" 134 | } 135 | }, 136 | { 137 | "description": "Invalid s value (>= n)", 138 | "exception": "r and s not in range", 139 | "d": "01", 140 | "message": "foo", 141 | "signature": { 142 | "r": "2", 143 | "s": "115792089237316195423570985008687907852837564279074904382605163141518161494337" 144 | } 145 | }, 146 | { 147 | "description": "Invalid r, s values (r = s = -n)", 148 | "exception": "r and s not in range", 149 | "d": "01", 150 | "message": "foo", 151 | "signature": { 152 | "r": "-115792089237316195423570985008687907852837564279074904382605163141518161494337", 153 | "s": "-115792089237316195423570985008687907852837564279074904382605163141518161494337" 154 | } 155 | } 156 | ] 157 | }, 158 | "deterministicK": [ 159 | { 160 | "message": "test data", 161 | "privkey": "fee0a1f7afebf9d2a5a80c0c98a31c709681cce195cbcd06342b517970c0be1e", 162 | "k_bad00": "fcce1de7a9bcd6b2d3defade6afa1913fb9229e3b7ddf4749b55c4848b2a196e", 163 | "k_bad01": "727fbcb59eb48b1d7d46f95a04991fc512eb9dbf9105628e3aec87428df28fd8", 164 | "k_bad15": "398f0e2c9f79728f7b3d84d447ac3a86d8b2083c8f234a0ffa9c4043d68bd258" 165 | }, 166 | { 167 | "message": "Everything should be made as simple as possible, but not simpler.", 168 | "privkey": "0000000000000000000000000000000000000000000000000000000000000001", 169 | "k_bad00": "ec633bd56a5774a0940cb97e27a9e4e51dc94af737596a0c5cbb3d30332d92a5", 170 | "k_bad01": "df55b6d1b5c48184622b0ead41a0e02bfa5ac3ebdb4c34701454e80aabf36f56", 171 | "k_bad15": "def007a9a3c2f7c769c75da9d47f2af84075af95cadd1407393dc1e26086ef87" 172 | }, 173 | { 174 | "message": "Satoshi Nakamoto", 175 | "privkey": "0000000000000000000000000000000000000000000000000000000000000002", 176 | "k_bad00": "d3edc1b8224e953f6ee05c8bbf7ae228f461030e47caf97cde91430b4607405e", 177 | "k_bad01": "f86d8e43c09a6a83953f0ab6d0af59fb7446b4660119902e9967067596b58374", 178 | "k_bad15": "241d1f57d6cfd2f73b1ada7907b199951f95ef5ad362b13aed84009656e0254a" 179 | }, 180 | { 181 | "message": "Diffie Hellman", 182 | "privkey": "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", 183 | "k_bad00": "c378a41cb17dce12340788dd3503635f54f894c306d52f6e9bc4b8f18d27afcc", 184 | "k_bad01": "90756c96fef41152ac9abe08819c4e95f16da2af472880192c69a2b7bac29114", 185 | "k_bad15": "7b3f53300ab0ccd0f698f4d67db87c44cf3e9e513d9df61137256652b2e94e7c" 186 | }, 187 | { 188 | "message": "Japan", 189 | "privkey": "8080808080808080808080808080808080808080808080808080808080808080", 190 | "k_bad00": "f471e61b51d2d8db78f3dae19d973616f57cdc54caaa81c269394b8c34edcf59", 191 | "k_bad01": "6819d85b9730acc876fdf59e162bf309e9f63dd35550edf20869d23c2f3e6d17", 192 | "k_bad15": "d8e8bae3ee330a198d1f5e00ad7c5f9ed7c24c357c0a004322abca5d9cd17847" 193 | }, 194 | { 195 | "message": "Bitcoin", 196 | "privkey": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", 197 | "k_bad00": "36c848ffb2cbecc5422c33a994955b807665317c1ce2a0f59c689321aaa631cc", 198 | "k_bad01": "4ed8de1ec952a4f5b3bd79d1ff96446bcd45cabb00fc6ca127183e14671bcb85", 199 | "k_bad15": "56b6f47babc1662c011d3b1f93aa51a6e9b5f6512e9f2e16821a238d450a31f8" 200 | }, 201 | { 202 | "message": "i2FLPP8WEus5WPjpoHwheXOMSobUJVaZM1JPMQZq", 203 | "privkey": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", 204 | "k_bad00": "6e9b434fcc6bbb081a0463c094356b47d62d7efae7da9c518ed7bac23f4e2ed6", 205 | "k_bad01": "ae5323ae338d6117ce8520a43b92eacd2ea1312ae514d53d8e34010154c593bb", 206 | "k_bad15": "3eaa1b61d1b8ab2f1ca71219c399f2b8b3defa624719f1e96fe3957628c2c4ea" 207 | }, 208 | { 209 | "message": "lEE55EJNP7aLrMtjkeJKKux4Yg0E8E1SAJnWTCEh", 210 | "privkey": "3881e5286abc580bb6139fe8e83d7c8271c6fe5e5c2d640c1f0ed0e1ee37edc9", 211 | "k_bad00": "5b606665a16da29cc1c5411d744ab554640479dd8abd3c04ff23bd6b302e7034", 212 | "k_bad01": "f8b25263152c042807c992eacd2ac2cc5790d1e9957c394f77ea368e3d9923bd", 213 | "k_bad15": "ea624578f7e7964ac1d84adb5b5087dd14f0ee78b49072aa19051cc15dab6f33" 214 | }, 215 | { 216 | "message": "2SaVPvhxkAPrayIVKcsoQO5DKA8Uv5X/esZFlf+y", 217 | "privkey": "7259dff07922de7f9c4c5720d68c9745e230b32508c497dd24cb95ef18856631", 218 | "k_bad00": "3ab6c19ab5d3aea6aa0c6da37516b1d6e28e3985019b3adb388714e8f536686b", 219 | "k_bad01": "19af21b05004b0ce9cdca82458a371a9d2cf0dc35a813108c557b551c08eb52e", 220 | "k_bad15": "117a32665fca1b7137a91c4739ac5719fec0cf2e146f40f8e7c21b45a07ebc6a" 221 | }, 222 | { 223 | "message": "00A0OwO2THi7j5Z/jp0FmN6nn7N/DQd6eBnCS+/b", 224 | "privkey": "0d6ea45d62b334777d6995052965c795a4f8506044b4fd7dc59c15656a28f7aa", 225 | "k_bad00": "79487de0c8799158294d94c0eb92ee4b567e4dc7ca18addc86e49d31ce1d2db6", 226 | "k_bad01": "9561d2401164a48a8f600882753b3105ebdd35e2358f4f808c4f549c91490009", 227 | "k_bad15": "b0d273634129ff4dbdf0df317d4062a1dbc58818f88878ffdb4ec511c77976c0" 228 | } 229 | ] 230 | } 231 | -------------------------------------------------------------------------------- /lib/block/block.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash'); 4 | var BlockHeader = require('./blockheader'); 5 | var BN = require('../crypto/bn'); 6 | var BufferUtil = require('../util/buffer'); 7 | var BufferReader = require('../encoding/bufferreader'); 8 | var BufferWriter = require('../encoding/bufferwriter'); 9 | var Hash = require('../crypto/hash'); 10 | var JSUtil = require('../util/js'); 11 | var Transaction = require('../transaction'); 12 | var $ = require('../util/preconditions'); 13 | 14 | /** 15 | * Instantiate a Block from a Buffer, JSON object, or Object with 16 | * the properties of the Block 17 | * 18 | * @param {*} - A Buffer, JSON string, or Object 19 | * @returns {Block} 20 | * @constructor 21 | */ 22 | function Block(arg) { 23 | if (!(this instanceof Block)) { 24 | return new Block(arg); 25 | } 26 | _.extend(this, Block._from(arg)); 27 | if (!this.header.merkleRoot) { 28 | this.header.merkleRoot = this.getMerkleRoot(); 29 | } 30 | $.checkArgument(this.validMerkleRoot(), 'invalid merkle root found in header.'); 31 | return this; 32 | } 33 | 34 | // https://github.com/bitcoin/bitcoin/blob/b5fa132329f0377d787a4a21c1686609c2bfaece/src/primitives/block.h#L14 35 | Block.MAX_BLOCK_SIZE = 1000000; 36 | 37 | /** 38 | * @param {*} - A Buffer, JSON string or Object 39 | * @returns {Object} - An object representing block data 40 | * @throws {TypeError} - If the argument was not recognized 41 | * @private 42 | */ 43 | Block._from = function _from(arg) { 44 | var info = {}; 45 | if (BufferUtil.isBuffer(arg)) { 46 | info = Block._fromBufferReader(BufferReader(arg)); 47 | } else if (JSUtil.isValidJSON(arg)) { 48 | info = Block._fromJSON(arg); 49 | } else if (_.isObject(arg)) { 50 | info = Block._fromObject(arg); 51 | } else { 52 | throw new TypeError('Unrecognized argument for Block'); 53 | } 54 | return info; 55 | }; 56 | 57 | /** 58 | * @param {String} - A JSON string 59 | * @returns {Object} - An object representing block data 60 | * @private 61 | */ 62 | Block._fromJSON = function _fromJSON(data) { 63 | $.checkArgument(JSUtil.isValidJSON(data), 'data must be valid JSON'); 64 | data = JSON.parse(data); 65 | return Block._fromObject(data); 66 | }; 67 | 68 | /** 69 | * @param {Object} - A plain javascript object 70 | * @returns {Object} - An object representing block data 71 | * @private 72 | */ 73 | Block._fromObject = function _fromObject(data) { 74 | var transactions = []; 75 | data.transactions.forEach(function(tx) { 76 | transactions.push(Transaction().fromJSON(tx)); 77 | }); 78 | var info = { 79 | header: BlockHeader.fromObject(data.header), 80 | transactions: transactions 81 | }; 82 | return info; 83 | }; 84 | 85 | /** 86 | * @param {String} - A JSON string 87 | * @returns {Block} - An instance of block 88 | */ 89 | Block.fromJSON = function fromJSON(json) { 90 | var info = Block._fromJSON(json); 91 | return new Block(info); 92 | }; 93 | 94 | /** 95 | * @param {Object} - A plain javascript object 96 | * @returns {Block} - An instance of block 97 | */ 98 | Block.fromObject = function fromObject(obj) { 99 | var info = Block._fromObject(obj); 100 | return new Block(info); 101 | }; 102 | 103 | /** 104 | * @param {BufferReader} - Block data 105 | * @returns {Object} - An object representing the block data 106 | * @private 107 | */ 108 | Block._fromBufferReader = function _fromBufferReader(br) { 109 | var info = {}; 110 | $.checkState(!br.finished(), 'No block data received'); 111 | info.header = BlockHeader.fromBufferReader(br); 112 | var transactions = br.readVarintNum(); 113 | info.transactions = []; 114 | for (var i = 0; i < transactions; i++) { 115 | var tx = new Transaction(); 116 | tx.fromBufferReader(br); 117 | info.transactions.push(tx); 118 | } 119 | return info; 120 | }; 121 | 122 | /** 123 | * @param {BufferReader} - A buffer reader of the block 124 | * @returns {Block} - An instance of block 125 | */ 126 | Block.fromBufferReader = function fromBufferReader(br) { 127 | $.checkArgument(br, 'br is required'); 128 | var info = Block._fromBufferReader(br); 129 | return new Block(info); 130 | }; 131 | 132 | /** 133 | * @param {Buffer} - A buffer of the block 134 | * @returns {Block} - An instance of block 135 | */ 136 | Block.fromBuffer = function fromBuffer(buf) { 137 | return Block.fromBufferReader(new BufferReader(buf)); 138 | }; 139 | 140 | /** 141 | * @param {string} - str - A hex encoded string of the block 142 | * @returns {Block} - A hex encoded string of the block 143 | */ 144 | Block.fromString = function fromString(str) { 145 | var buf = new Buffer(str, 'hex'); 146 | return Block.fromBuffer(buf); 147 | }; 148 | 149 | Block.fromCoinbase = function(coinbase, header) { 150 | $.checkArgument(coinbase instanceof Transaction, 'coinbase must be a Transaction'); 151 | $.checkArgument(coinbase.isCoinbase(), 'coinbase must be a coinbase Transaction'); 152 | $.checkArgument(_.isNumber(header.height), 'header.height is a required number'); 153 | $.checkArgument(!_.isUndefined(header.prevHash), 'header.prevHash is required'); 154 | var o = {}; 155 | o.transactions = [coinbase.toObject()]; 156 | o.header = BlockHeader.create(header); 157 | var ret = new Block(o); 158 | return ret; 159 | }; 160 | 161 | /** 162 | * @returns {Object} - A plain object with the block properties 163 | */ 164 | Block.prototype.toObject = function toObject() { 165 | var transactions = []; 166 | this.transactions.forEach(function(tx) { 167 | transactions.push(tx.toObject()); 168 | }); 169 | return { 170 | header: this.header.toObject(), 171 | transactions: transactions 172 | }; 173 | }; 174 | 175 | /** 176 | * @returns {string} - A JSON string 177 | */ 178 | Block.prototype.toJSON = function toJSON() { 179 | return JSON.stringify(this.toObject()); 180 | }; 181 | 182 | /** 183 | * @returns {Buffer} - A buffer of the block 184 | */ 185 | Block.prototype.toBuffer = function toBuffer() { 186 | return this.toBufferWriter().concat(); 187 | }; 188 | 189 | /** 190 | * @returns {string} - A hex encoded string of the block 191 | */ 192 | Block.prototype.toString = function toString() { 193 | return this.toBuffer().toString('hex'); 194 | }; 195 | 196 | /** 197 | * @param {BufferWriter} - An existing instance of BufferWriter 198 | * @returns {BufferWriter} - An instance of BufferWriter representation of the Block 199 | */ 200 | Block.prototype.toBufferWriter = function toBufferWriter(bw) { 201 | if (!bw) { 202 | bw = new BufferWriter(); 203 | } 204 | bw.write(this.header.toBuffer()); 205 | bw.writeVarintNum(this.transactions.length); 206 | for (var i = 0; i < this.transactions.length; i++) { 207 | this.transactions[i].toBufferWriter(bw); 208 | } 209 | return bw; 210 | }; 211 | 212 | /** 213 | * Will iterate through each transaction and return an array of hashes 214 | * @returns {Array} - An array with transaction hashes 215 | */ 216 | Block.prototype.getTransactionHashes = function getTransactionHashes() { 217 | var hashes = []; 218 | if (this.transactions.length === 0) { 219 | return [Block.Values.NULL_HASH]; 220 | } 221 | for (var t = 0; t < this.transactions.length; t++) { 222 | hashes.push(this.transactions[t].getHash()); 223 | } 224 | return hashes; 225 | }; 226 | 227 | /** 228 | * Will build a merkle tree of all the transactions, ultimately arriving at 229 | * a single point, the merkle root. 230 | * @link https://en.bitcoin.it/wiki/Protocol_specification#Merkle_Trees 231 | * @returns {Array} - An array with each level of the tree after the other. 232 | */ 233 | Block.prototype.getMerkleTree = function getMerkleTree() { 234 | 235 | var tree = this.getTransactionHashes(); 236 | 237 | var j = 0; 238 | for (var size = this.transactions.length; size > 1; size = Math.floor((size + 1) / 2)) { 239 | for (var i = 0; i < size; i += 2) { 240 | var i2 = Math.min(i + 1, size - 1); 241 | var buf = Buffer.concat([tree[j + i], tree[j + i2]]); 242 | tree.push(Hash.sha256sha256(buf)); 243 | } 244 | j += size; 245 | } 246 | return tree; 247 | }; 248 | 249 | /** 250 | * Calculates the merkleRoot from the transactions. 251 | * @returns {Buffer} - A buffer of the merkle root hash 252 | */ 253 | Block.prototype.getMerkleRoot = function getMerkleRoot() { 254 | var tree = this.getMerkleTree(); 255 | var root = tree[tree.length - 1]; 256 | $.checkState(BufferUtil.isBuffer(root), 'root should be a Buffer'); 257 | return root; 258 | }; 259 | 260 | /** 261 | * Verifies that the transactions in the block match the header merkle root 262 | * @returns {Boolean} - If the merkle roots match 263 | */ 264 | Block.prototype.validMerkleRoot = function validMerkleRoot() { 265 | 266 | var h = new BN(this.header.merkleRoot.toString('hex'), 'hex'); 267 | var c = new BN(this.getMerkleRoot().toString('hex'), 'hex'); 268 | 269 | if (h.cmp(c) !== 0) { 270 | return false; 271 | } 272 | 273 | return true; 274 | }; 275 | 276 | Block.prototype.addTransaction = function(tx) { 277 | $.checkArgument(tx instanceof Transaction, 'tx is a required Transaction'); 278 | this.transactions.push(tx); 279 | this.header.merkleRoot = this.getMerkleRoot(); 280 | }; 281 | 282 | 283 | /** 284 | * @returns {Buffer} - The little endian hash buffer of the header 285 | */ 286 | Block.prototype.getHash = function() { 287 | return this.header.getHash(); 288 | }; 289 | 290 | var defineChildProperty = function(name) { 291 | Object.defineProperty(Block.prototype, name, { 292 | configurable: false, 293 | enumerable: true, 294 | writeable: false, 295 | get: function() { 296 | if (BufferUtil.isBuffer(this.header[name])) { 297 | return BufferUtil.reverse(this.header[name]).toString('hex'); 298 | } 299 | return this.header[name]; 300 | } 301 | }); 302 | }; 303 | defineChildProperty('id'); 304 | defineChildProperty('hash'); 305 | defineChildProperty('prevHash'); 306 | defineChildProperty('version'); 307 | defineChildProperty('height'); 308 | defineChildProperty('timestamp'); 309 | defineChildProperty('bits'); 310 | defineChildProperty('merkleRoot'); 311 | defineChildProperty('nonce'); 312 | 313 | /** 314 | * @returns {string} - A string formatted for the console 315 | */ 316 | Block.prototype.inspect = function inspect() { 317 | return ''; 318 | }; 319 | 320 | Block.Values = { 321 | START_OF_BLOCK: 8, // Start of block in raw block data 322 | NULL_HASH: new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex'), 323 | }; 324 | 325 | var genesisTx = new Transaction() 326 | .at(0, 0) 327 | .to('03000000000000000006fd1af568353887e69e992938cbd5d40fb026467e2fef7c') 328 | .colored(0x13371337); 329 | var genesis = Block.fromCoinbase(genesisTx, { 330 | height: 0, 331 | nonce: 586081, 332 | prevHash: '0000000000000000000000000000000000000000000000000000000000000000', 333 | bits: 0x1e0fffff, 334 | time: 1433037823 335 | }); 336 | 337 | Block.genesis = genesis; 338 | 339 | module.exports = Block; 340 | --------------------------------------------------------------------------------