Socket
Socket
Sign inDemoInstall

bitcoinjs-lib

Package Overview
Dependencies
11
Maintainers
3
Versions
87
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.2.0 to 1.0.0

jshint.json

62

package.json
{
"name": "bitcoinjs-lib",
"version": "0.2.0",
"version": "1.0.0",
"description": "Client-side Bitcoin JavaScript library",

@@ -12,3 +12,24 @@ "main": "./src/index.js",

],
"author": "Stefan Thomas <justmoon@members.fsf.org> (http://www.justmoon.net)",
"contributors": [
{
"name": "Daniel Cousens",
"email": "bitcoin@dcousens.com",
"url": "http://dcousens.com"
},
{
"name": "Kyle Drake",
"email": "kyle@kyledrake.net",
"url": "http://kyledrake.net/"
},
{
"name": "Wei Lu",
"email": "luwei.here@gmail.com",
"url": "http://weilu.github.io/"
},
{
"name": "Stefan Thomas",
"email": "justmoon@members.fsf.org",
"url": "http://www.justmoon.net"
}
],
"repository": {

@@ -19,17 +40,21 @@ "type": "git",

"devDependencies": {
"browserify": "4.1.11",
"coveralls": "~2.10.0",
"helloblock-js": "^0.2.1",
"istanbul": "0.1.30",
"jshint": "2.5.1",
"mocha": "1.18.2",
"istanbul": "0.1.30",
"uglify-js": "2.4.13",
"node-browserify": "https://github.com/substack/node-browserify/tarball/master",
"sinon": "1.9.0"
"mocha-lcov-reporter": "0.0.1",
"sinon": "1.9.0",
"uglify-js": "2.4.13"
},
"testling": {
"browsers": [
"android-browser/4.2..latest",
"chrome/20..latest",
"firefox/21..latest",
"safari/latest",
"ipad/6..latest",
"iphone/6..latest",
"opera/15..latest",
"iphone/6..latest",
"ipad/6..latest",
"android-browser/4.2..latest"
"safari/latest"
],

@@ -40,10 +65,21 @@ "harness": "mocha-bdd",

"scripts": {
"test": "./node_modules/.bin/istanbul test ./node_modules/.bin/_mocha -- --reporter list test/*.js",
"compile": "./node_modules/.bin/browserify ./src/index.js -s Bitcoin | ./node_modules/.bin/uglifyjs > bitcoinjs-min.js",
"coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- --reporter list test/*.js",
"compile": "./node_modules/.bin/browserify ./src/index.js -s Bitcoin | ./node_modules/.bin/uglifyjs > bitcoinjs-min.js"
"coveralls": "npm run-script coverage && node ./node_modules/.bin/coveralls < coverage/lcov.info",
"integration": "./node_modules/.bin/_mocha --reporter list test/integration/*.js",
"jshint": "./node_modules/.bin/jshint --config jshint.json src/*.js ; true",
"test": "npm run-script unit",
"unit": "./node_modules/.bin/istanbul test ./node_modules/.bin/_mocha -- --reporter list `find test -maxdepth 1 -not -type d`"
},
"browser": {
"crypto": "crypto-browserify"
},
"dependencies": {
"bigi": "1.1.0",
"bs58": "1.1.0",
"crypto-js": "3.1.2-3",
"secure-random": "0.2.1"
"crypto-browserify": "2.1.8",
"ecurve": "0.10.0",
"secure-random": "1.1.1"
}
}

@@ -1,20 +0,35 @@

# bitcoinjs-lib
# BitcoinJS (bitcoinjs-lib)
[![Build Status](https://travis-ci.org/bitcoinjs/bitcoinjs-lib.png?branch=master)](https://travis-ci.org/bitcoinjs/bitcoinjs-lib)
[![Build Status](https://travis-ci.org/bitcoinjs/bitcoinjs-lib.png?branch=master)](https://travis-ci.org/bitcoinjs/bitcoinjs-lib) [![Coverage Status](https://coveralls.io/repos/bitcoinjs/bitcoinjs-lib/badge.png)](https://coveralls.io/r/bitcoinjs/bitcoinjs-lib)
[![NPM](https://nodei.co/npm/bitcoinjs-lib.png)](https://nodei.co/npm/bitcoinjs-lib/)
[![Browser Support](https://ci.testling.com/bitcoinjs/bitcoinjs-lib.png)](https://ci.testling.com/bitcoinjs/bitcoinjs-lib)
A pure JavaScript Bitcoin library for node.js and browsers. Backed by (slowly improving) testing, proven by over a million wallet users. The backbone for almost all Bitcoin web wallets in production today.
The pure JavaScript Bitcoin library for node.js and browsers.
A continued implementation of the original `0.1.3` version used by over a million wallet users; the backbone for almost all Bitcoin web wallets in production today.
**Warning**: Master is not stable. Expect the interface to change rapidly, including some of the examples below. This is not the original bitcoinjs-lib that was not updated for a while. The current bitcoinjs-lib has been refactored to clean things up, add new functionality and merge improvements from the community. If you are looking for the original, it will be tagged as `0.1.3`. We will use `0.2.x` for releases based on these changes, so be sure to use the `0.1.3` tag if you need the original version.
## Features
- Bitcoin Testnet and Mainnet (production) support
- [HD Wallets](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)
- Highly secure random private key / address generation using [window.crypto.getRandomValues](https://developer.mozilla.org/en-US/docs/Web/API/Window.crypto)
- ECDSA signing and verification
- Transaction creation (pay-to-pubkey-hash), support for multisignature transactions
- A (somewhat incomplete) wallet implementation, improvements ongoing
- Clean: Pure JavaScript, concise code, easy to read.
- Tested: Coverage > 90%, third-party integration tests.
- Careful: Two person approval process for small, focused pull requests.
- Compatible: Works on Node.js and all modern browsers.
- Powerful: Support for advanced features, such as multi-sig, HD Wallets.
- Secure: Strong random number generation, PGP signed releases, trusted developers.
- Principled: No support for browsers with crap RNG (IE < 11)
- Standardized: Node community coding style, Browserify, Node's stdlib and Buffers.
- Fast: Optimized code, uses typed arrays instead of byte arrays for performance.
- Experiment-friendly: Bitcoin Mainnet and Testnet support.
- Altcoin-ready: Capable of working with bitcoin-derived cryptocurrencies (such as Dogecoin).
## Should I use this in production?
If you are thinking of using the master branch of this library in production, *stop*.
Master is not stable; it is our development branch, and only tagged releases may be classified as stable.
If you are looking for the original, it is tagged as `0.1.3`. Unless you need it for dependency reasons, it is strongly recommended that you use (or upgrade to) the newest version, which adds major functionality, cleans up the interface, fixes many bugs, and adds over 1,300 more tests.
## Installation

@@ -24,3 +39,2 @@

Note: The npm version is currently out of date, are working to resolve this. The best way to use the latest code is to clone the repository.

@@ -37,10 +51,17 @@ ## Setup

### Browser
Compile `bitcoinjs-min.js` with the following command:
From the repository: Compile `bitcoinjs-min.js` with the following command:
$ npm run-script compile
After loading this file in your browser, you will be able to use the global `Bitcoin` object.
From NPM:
$ npm -g install bitcoinjs-lib browserify uglify-js
$ browserify -r bitcoinjs-lib -s Bitcoin | uglifyjs > bitcoinjs.min.js
After loading this file in your browser, you will be able to use the global `bitcoin` object.
## Usage

@@ -50,2 +71,3 @@

### Generating a Bitcoin address

@@ -55,10 +77,10 @@

key = new Bitcoin.ECKey()
key = bitcoin.ECKey.makeRandom()
// Print your private key (a hex string)
console.log(key.toString())
// Print your private key (in WIF format)
console.log(key.toWIF())
// => 8c112cf628362ecf4d482f68af2dbb50c8a2cb90d226215de925417aa9336a48
// Print your public key (defaults to a Bitcoin address)
console.log(key.getPub().getAddress())
// Print your public key (toString defaults to a Bitcoin address)
console.log(key.pub.getAddress().toString())
// => 14bZ7YWde4KdRb5YN7GYkToz3EHVCvRxkF

@@ -70,3 +92,3 @@ ```

```javascript
tx = new Bitcoin.Transaction()
tx = new bitcoin.Transaction()

@@ -79,4 +101,4 @@ // Add the input (who is paying) of the form [previous transaction hash, index of the output to use]

// Initialize a private key using hex
key = new Bitcoin.ECKey("8c112cf628362ecf4d482f68af2dbb50c8a2cb90d226215de925417aa9336a48")
// Initialize a private key using WIF
key = bitcoin.ECKey.fromWIF("L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy")

@@ -87,3 +109,3 @@ // Sign the first input with the new key

// Print transaction serialized as hex
console.log(tx.serializeHex())
console.log(tx.toHex())
// => 0100000001313eb630b128102b60241ca895f1d0ffca2170d5a0990e094f2182c102ab94aa000000008a47304402200169f1f844936dc60df54e812345f5dd3e6681fea52e33c25154ad9cc23a330402204381ed8e73d74a95b15f312f33d5a0072c7a12dd6c3294df6e8efbe4aff27426014104e75628573696aed32d7656fb35e9c71ea08eb6492837e13d2662b9a36821d0fff992692fd14d74fdec20fae29128ba12653249cbeef521fc5eba84dde0689f27ffffffff01983a0000000000001976a914ad618cf4333b3b248f9744e8e81db2964d0ae39788ac00000000

@@ -95,12 +117,23 @@

## Projects utilizing bitcoinjs-lib
## Projects utilizing BitcoinJS
- [Blockchain.info Wallet](http://blockchain.info/wallet)
- [Bitaddress.org](https://www.bitaddress.org)
- [Coinpunk](https://coinpunk.com)
- [DarkWallet](https://darkwallet.unsystem.net)
- [Hive Wallet](https://www.hivewallet.com)
- [Justchain Exchange](https://justcoin.com)
- [Skyhook ATM](http://projectskyhook.com)
- [BitAddress](https://www.bitaddress.org)
- [Blockchain.info](https://blockchain.info/wallet)
- [Brainwallet](https://brainwallet.github.io)
- [Dark Wallet](https://darkwallet.unsystem.net)
- [Dogechain Wallet](https://dogechain.info)
- [GreenAddress](https://greenaddress.it)
Feel free to send pull requests to have your project/startup listed here.
## Contributors
Stefan Thomas is the inventor and creator of this project. His pioneering work made Bitcoin web wallets possible.
Since then, many people have contributed. [Click here](https://github.com/bitcoinjs/bitcoinjs-lib/graphs/contributors) to see the comprehensive list.
Daniel Cousens, Wei Lu, JP Richardson and Kyle Drake lead the major refactor of the library from 0.1.3 to 1.0.0.
## Contributing

@@ -114,6 +147,17 @@

### Running the test suite
$ npm test
$ npm run-script coverage
## Complementing Libraries
- [bip39](https://github.com/weilu/bip39) - Wei Lu's Mnemonic code generator
- [BCoin](https://github.com/indutny/bcoin) - BIP37 / Bloom Filters / SPV client
- [scryptsy](https://github.com/cryptocoinjs/scryptsy) - Private key encryption (BIP38)
- [insight](https://github.com/bitpay/insight) - A bitcoin blockchain API for web wallets.
## Alternatives

@@ -128,2 +172,3 @@

## Copyright

@@ -133,11 +178,1 @@

Released under MIT license
http://bitcoinjs.org/
JSBN (c) 2003-2005 Tom Wu
Released under BSD license
http://www-cs-students.stanford.edu/~tjw/jsbn/
CryptoJS (c) 2009–2012 by Jeff Mott
Released under New BSD license
http://code.google.com/p/crypto-js/

@@ -1,65 +0,64 @@

var base58 = require('./base58')
var assert = require('assert')
var base58check = require('./base58check')
var convert = require('./convert')
var error = require('./util').error
var mainnet = require('./network').mainnet.addressVersion
var networks = require('./networks')
var scripts = require('./scripts')
function Address(bytes, version) {
if (!(this instanceof Address)) {
return new Address(bytes, version)
}
function findScriptTypeByVersion(version) {
for (var networkName in networks) {
var network = networks[networkName]
if (bytes instanceof Address) {
this.hash = bytes.hash
this.version = bytes.version
if (version === network.pubKeyHash) return 'pubkeyhash'
if (version === network.scriptHash) return 'scripthash'
}
else if (typeof bytes === 'string') {
if (bytes.length <= 35) {
var decode = base58check.decode(bytes)
}
this.hash = decode.payload
this.version = decode.version
}
else if (bytes.length <= 40) {
this.hash = convert.hexToBytes(bytes)
this.version = version || mainnet
}
else {
error('invalid or unrecognized input')
}
}
else {
this.hash = bytes
this.version = version || mainnet
}
function Address(hash, version) {
assert(Buffer.isBuffer(hash), 'Expected Buffer, got ' + hash)
assert.strictEqual(hash.length, 20, 'Invalid hash length')
assert.strictEqual(version & 0xff, version, 'Invalid version byte')
this.hash = hash
this.version = version
}
/**
* Serialize this object as a standard Bitcoin address.
* Returns the address as a base58-encoded string in the standardized format.
*/
Address.prototype.toString = function () {
return base58check.encode(this.hash.slice(0), this.version)
// Import functions
Address.fromBase58Check = function(string) {
var payload = base58check.decode(string)
var version = payload.readUInt8(0)
var hash = payload.slice(1)
return new Address(hash, version)
}
/**
* Returns the version of an address, e.g. if the address belongs to the main
* net or the test net.
*/
Address.getVersion = function (address) {
return base58.decode(address)[0]
Address.fromOutputScript = function(script, network) {
network = network || networks.bitcoin
var type = scripts.classifyOutput(script)
if (type === 'pubkeyhash') return new Address(script.chunks[2], network.pubKeyHash)
if (type === 'scripthash') return new Address(script.chunks[1], network.scriptHash)
assert(false, type + ' has no matching Address')
}
/**
* Returns true if a bitcoin address is a valid address, otherwise false.
*/
Address.validate = function (address) {
try {
base58check.decode(address)
return true
} catch (e) {
return false
}
// Export functions
Address.prototype.toBase58Check = function () {
var payload = new Buffer(21)
payload.writeUInt8(this.version, 0)
this.hash.copy(payload, 1)
return base58check.encode(payload)
}
Address.prototype.toOutputScript = function() {
var scriptType = findScriptTypeByVersion(this.version)
if (scriptType === 'pubkeyhash') return scripts.pubKeyHashOutput(this.hash)
if (scriptType === 'scripthash') return scripts.scriptHashOutput(this.hash)
assert(false, this.toString() + ' has no matching Script')
}
Address.prototype.toString = Address.prototype.toBase58Check
module.exports = Address
// https://en.bitcoin.it/wiki/Base58Check_encoding
var assert = require('assert')
var base58 = require('./base58')
var crypto = require('crypto')
var base58 = require('bs58')
var crypto = require('./crypto')
function sha256(buf) {
var hash = crypto.createHash('sha256')
hash.update(buf)
return hash.digest()
}
// Encode a buffer as a base58-check-encoded string
function encode(buffer, version) {
version = version || 0
function encode(payload) {
var checksum = crypto.hash256(payload).slice(0, 4)
// FIXME: `new Buffer(buffer)` is unnecessary if input is a Buffer
var version = new Buffer([version])
var payload = new Buffer(buffer)
var message = Buffer.concat([version, payload])
var checksum = sha256(sha256(message)).slice(0, 4)
return base58.encode(Buffer.concat([
message,
payload,
checksum

@@ -34,16 +20,9 @@ ]))

var message = buffer.slice(0, -4)
var payload = buffer.slice(0, -4)
var checksum = buffer.slice(-4)
var newChecksum = sha256(sha256(message)).slice(0, 4)
var newChecksum = crypto.hash256(payload).slice(0, 4)
assert.deepEqual(newChecksum, checksum)
assert.deepEqual(newChecksum, checksum, 'Invalid checksum')
var version = message.readUInt8(0)
var payload = message.slice(1)
return {
version: version,
payload: payload,
checksum: checksum
}
return payload
}

@@ -50,0 +29,0 @@

@@ -0,200 +1,32 @@

var assert = require('assert')
var Crypto = require('crypto-js')
var WordArray = Crypto.lib.WordArray
var base64map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
function lpad(str, padString, length) {
while (str.length < length) str = padString + str
return str
}
function bufferToWordArray(buffer) {
assert(Buffer.isBuffer(buffer), 'Expected Buffer, got', buffer)
function bytesToHex(bytes) {
// FIXME: transitionary fix
if (Buffer.isBuffer(bytes)) {
return bytes.toString('hex')
var words = []
for (var i = 0, b = 0; i < buffer.length; i++, b += 8) {
words[b >>> 5] |= buffer[i] << (24 - b % 32)
}
return bytes.map(function(x) {
return lpad(x.toString(16), '0', 2)
}).join('')
return new WordArray.init(words, buffer.length)
}
function hexToBytes(hex) {
return hex.match(/../g).map(function(x) {
return parseInt(x,16)
})
}
function wordArrayToBuffer(wordArray) {
assert(Array.isArray(wordArray.words), 'Expected WordArray, got' + wordArray)
function bytesToBase64(bytes) {
var base64 = []
var words = wordArray.words
var buffer = new Buffer(words.length * 4)
for (var i = 0; i < bytes.length; i += 3) {
var triplet = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]
for (var j = 0; j < 4; j++) {
if (i * 8 + j * 6 <= bytes.length * 8) {
base64.push(base64map.charAt((triplet >>> 6 * (3 - j)) & 0x3F))
} else {
base64.push('=')
}
}
}
return base64.join('')
}
function base64ToBytes(base64) {
// Remove non-base-64 characters
base64 = base64.replace(/[^A-Z0-9+\/]/ig, '')
var bytes = []
var imod4 = 0
for (var i = 0; i < base64.length; imod4 = ++i % 4) {
if (!imod4) continue
bytes.push(
(
(base64map.indexOf(base64.charAt(i - 1)) & (Math.pow(2, -2 * imod4 + 8) - 1)) <<
(imod4 * 2)
) |
(base64map.indexOf(base64.charAt(i)) >>> (6 - imod4 * 2))
)
}
return bytes
}
/**
* Hex only (allowing bin would be potentially risky, as 01010101 = \x01 * 4 or 85)
*/
function coerceToBytes(input) {
if (typeof input != 'string') return input
return hexToBytes(input)
}
function binToBytes(bin) {
return bin.match(/......../g).map(function(x) {
return parseInt(x,2)
words.forEach(function(value, i) {
buffer.writeInt32BE(value & -1, i * 4)
})
}
function bytesToBin(bytes) {
return bytes.map(function(x) {
return lpad(x.toString(2), '0', 8)
}).join('')
return buffer
}
function bytesToString(bytes) {
return bytes.map(function(x){
return String.fromCharCode(x)
}).join('')
}
function stringToBytes(string) {
return string.split('').map(function(x) {
return x.charCodeAt(0)
})
}
/**
* Create a byte array representing a number with the given length
*/
function numToBytes(num, bytes) {
if (bytes === undefined) bytes = 8
if (bytes === 0) return []
return [num % 256].concat(numToBytes(Math.floor(num / 256), bytes - 1))
}
/**
* Convert a byte array to the number that it represents
*/
function bytesToNum(bytes) {
if (bytes.length === 0) return 0
return bytes[0] + 256 * bytesToNum(bytes.slice(1))
}
/**
* Turn an integer into a "var_int".
*
* "var_int" is a variable length integer used by Bitcoin's binary format.
*
* Returns a byte array.
*/
function numToVarInt(num) {
if (num < 253) return [num]
if (num < 65536) return [253].concat(numToBytes(num, 2))
if (num < 4294967296) return [254].concat(numToBytes(num, 4))
return [255].concat(numToBytes(num, 8))
}
/**
* Turn an VarInt into an integer
*
* "var_int" is a variable length integer used by Bitcoin's binary format.
*
* Returns { bytes: bytesUsed, number: theNumber }
*/
function varIntToNum(bytes) {
var prefix = bytes[0]
var viBytes =
prefix < 253 ? bytes.slice(0, 1)
: prefix === 253 ? bytes.slice(1, 3)
: prefix === 254 ? bytes.slice(1, 5)
: bytes.slice(1, 9)
return {
bytes: prefix < 253 ? viBytes : bytes.slice(0, viBytes.length + 1),
number: bytesToNum(viBytes)
}
}
function bytesToWords(bytes) {
var words = []
for (var i = 0, b = 0; i < bytes.length; i++, b += 8) {
words[b >>> 5] |= bytes[i] << (24 - b % 32)
}
return words
}
function wordsToBytes(words) {
var bytes = []
for (var b = 0; b < words.length * 32; b += 8) {
bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF)
}
return bytes
}
function bytesToWordArray(bytes) {
return new WordArray.init(bytesToWords(bytes), bytes.length)
}
function wordArrayToBytes(wordArray) {
return wordsToBytes(wordArray.words)
}
function reverseEndian (hex) {
return bytesToHex(hexToBytes(hex).reverse())
}
module.exports = {
lpad: lpad,
bytesToHex: bytesToHex,
hexToBytes: hexToBytes,
bytesToBase64: bytesToBase64,
base64ToBytes: base64ToBytes,
coerceToBytes: coerceToBytes,
binToBytes: binToBytes,
bytesToBin: bytesToBin,
bytesToString: bytesToString,
stringToBytes: stringToBytes,
numToBytes: numToBytes,
bytesToNum: bytesToNum,
numToVarInt: numToVarInt,
varIntToNum: varIntToNum,
bytesToWords: bytesToWords,
wordsToBytes: wordsToBytes,
bytesToWordArray: bytesToWordArray,
wordArrayToBytes: wordArrayToBytes,
reverseEndian: reverseEndian
bufferToWordArray: bufferToWordArray,
wordArrayToBuffer: wordArrayToBuffer
}

@@ -1,299 +0,180 @@

var sec = require('./jsbn/sec')
var rng = require('secure-random')
var BigInteger = require('./jsbn/jsbn')
var convert = require('./convert')
var HmacSHA256 = require('crypto-js/hmac-sha256')
var ECPointFp = require('./jsbn/ec').ECPointFp
var ecparams = sec("secp256k1")
var P_OVER_FOUR = null
var assert = require('assert')
var crypto = require('./crypto')
function implShamirsTrick(P, k, Q, l) {
var m = Math.max(k.bitLength(), l.bitLength())
var Z = P.add2D(Q)
var R = P.curve.getInfinity()
var BigInteger = require('bigi')
var ECSignature = require('./ecsignature')
var Point = require('ecurve').Point
for (var i = m - 1; i >= 0; --i) {
R = R.twice2D()
// https://tools.ietf.org/html/rfc6979#section-3.2
function deterministicGenerateK(curve, hash, d) {
assert(Buffer.isBuffer(hash), 'Hash must be a Buffer, not ' + hash)
assert.equal(hash.length, 32, 'Hash must be 256 bit')
assert(d instanceof BigInteger, 'Private key must be a BigInteger')
R.z = BigInteger.ONE
var x = d.toBuffer(32)
var k = new Buffer(32)
var v = new Buffer(32)
if (k.testBit(i)) {
if (l.testBit(i)) {
R = R.add2D(Z)
} else {
R = R.add2D(P)
}
} else {
if (l.testBit(i)) {
R = R.add2D(Q)
}
}
}
// Step B
v.fill(1)
return R
}
// Step C
k.fill(0)
function deterministicGenerateK(hash,key) {
var vArr = []
var kArr = []
for (var i = 0;i < 32;i++) vArr.push(1)
for (var i = 0;i < 32;i++) kArr.push(0)
var v = convert.bytesToWordArray(vArr)
var k = convert.bytesToWordArray(kArr)
// Step D
k = crypto.HmacSHA256(Buffer.concat([v, new Buffer([0]), x, hash]), k)
k = HmacSHA256(convert.bytesToWordArray(vArr.concat([0]).concat(key).concat(hash)), k)
v = HmacSHA256(v, k)
vArr = convert.wordArrayToBytes(v)
k = HmacSHA256(convert.bytesToWordArray(vArr.concat([1]).concat(key).concat(hash)), k)
v = HmacSHA256(v,k)
v = HmacSHA256(v,k)
vArr = convert.wordArrayToBytes(v)
return BigInteger.fromByteArrayUnsigned(vArr)
}
// Step E
v = crypto.HmacSHA256(v, k)
var ECDSA = {
getBigRandom: function (limit) {
return new BigInteger(limit.bitLength(), rng).
mod(limit.subtract(BigInteger.ONE)).
add(BigInteger.ONE)
},
sign: function (hash, priv) {
var d = priv
var n = ecparams.getN()
var e = BigInteger.fromByteArrayUnsigned(hash)
// Step F
k = crypto.HmacSHA256(Buffer.concat([v, new Buffer([1]), x, hash]), k)
var k = deterministicGenerateK(hash,priv.toByteArrayUnsigned())
var G = ecparams.getG()
var Q = G.multiply(k)
var r = Q.getX().toBigInteger().mod(n)
// Step G
v = crypto.HmacSHA256(v, k)
var s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n)
// Step H1/H2a, ignored as tlen === qlen (256 bit)
// Step H2b
v = crypto.HmacSHA256(v, k)
return ECDSA.serializeSig(r, s)
},
var T = BigInteger.fromBuffer(v)
verify: function (hash, sig, pubkey) {
var r,s
if (Array.isArray(sig)) {
var obj = ECDSA.parseSig(sig)
r = obj.r
s = obj.s
} else if ("object" === typeof sig && sig.r && sig.s) {
r = sig.r
s = sig.s
} else {
throw new Error("Invalid value for signature")
}
// Step H3, repeat until T is within the interval [1, n - 1]
while ((T.signum() <= 0) || (T.compareTo(curve.n) >= 0)) {
k = crypto.HmacSHA256(Buffer.concat([v, new Buffer([0])]), k)
v = crypto.HmacSHA256(v, k)
var Q
if (pubkey instanceof ECPointFp) {
Q = pubkey
} else if (Array.isArray(pubkey)) {
Q = ECPointFp.decodeFrom(ecparams.getCurve(), pubkey)
} else {
throw new Error("Invalid format for pubkey value, must be byte array or ECPointFp")
}
var e = BigInteger.fromByteArrayUnsigned(hash)
T = BigInteger.fromBuffer(v)
}
return ECDSA.verifyRaw(e, r, s, Q)
},
return T
}
verifyRaw: function (e, r, s, Q) {
var n = ecparams.getN()
var G = ecparams.getG()
function sign(curve, hash, d) {
var k = deterministicGenerateK(curve, hash, d)
if (r.compareTo(BigInteger.ONE) < 0 || r.compareTo(n) >= 0) {
return false
}
var n = curve.n
var G = curve.G
var Q = G.multiply(k)
var e = BigInteger.fromBuffer(hash)
if (s.compareTo(BigInteger.ONE) < 0 || s.compareTo(n) >= 0) {
return false
}
var r = Q.affineX.mod(n)
assert.notEqual(r.signum(), 0, 'Invalid R value')
var c = s.modInverse(n)
var s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n)
assert.notEqual(s.signum(), 0, 'Invalid S value')
var u1 = e.multiply(c).mod(n)
var u2 = r.multiply(c).mod(n)
var N_OVER_TWO = n.shiftRight(1)
// TODO(!!!): For some reason Shamir's trick isn't working with
// signed message verification!? Probably an implementation
// error!
//var point = implShamirsTrick(G, u1, Q, u2)
var point = G.multiply(u1).add(Q.multiply(u2))
// enforce low S values, see bip62: 'low s values in signatures'
if (s.compareTo(N_OVER_TWO) > 0) {
s = n.subtract(s)
}
var v = point.getX().toBigInteger().mod(n)
return new ECSignature(r, s)
}
return v.equals(r)
},
function verify(curve, hash, signature, Q) {
var e = BigInteger.fromBuffer(hash)
/**
* Serialize a signature into DER format.
*
* Takes two BigIntegers representing r and s and returns a byte array.
*/
serializeSig: function (r, s) {
var rBa = r.toByteArraySigned()
var sBa = s.toByteArraySigned()
return verifyRaw(curve, e, signature, Q)
}
var sequence = []
sequence.push(0x02); // INTEGER
sequence.push(rBa.length)
sequence = sequence.concat(rBa)
function verifyRaw(curve, e, signature, Q) {
var n = curve.n
var G = curve.G
sequence.push(0x02); // INTEGER
sequence.push(sBa.length)
sequence = sequence.concat(sBa)
var r = signature.r
var s = signature.s
sequence.unshift(sequence.length)
sequence.unshift(0x30); // SEQUENCE
if (r.signum() === 0 || r.compareTo(n) >= 0) return false
if (s.signum() === 0 || s.compareTo(n) >= 0) return false
return sequence
},
var c = s.modInverse(n)
/**
* Parses a byte array containing a DER-encoded signature.
*
* This function will return an object of the form:
*
* {
* r: BigInteger,
* s: BigInteger
* }
*/
parseSig: function (sig) {
var cursor
if (sig[0] != 0x30) {
throw new Error("Signature not a valid DERSequence")
}
var u1 = e.multiply(c).mod(n)
var u2 = r.multiply(c).mod(n)
cursor = 2
if (sig[cursor] != 0x02) {
throw new Error("First element in signature must be a DERInteger")
}
var rBa = sig.slice(cursor+2, cursor+2+sig[cursor+1])
var point = G.multiplyTwo(u1, Q, u2)
var v = point.affineX.mod(n)
cursor += 2+sig[cursor+1]
if (sig[cursor] != 0x02) {
throw new Error("Second element in signature must be a DERInteger")
}
var sBa = sig.slice(cursor+2, cursor+2+sig[cursor+1])
return v.equals(r)
}
cursor += 2+sig[cursor+1]
/**
* Recover a public key from a signature.
*
* See SEC 1: Elliptic Curve Cryptography, section 4.1.6, "Public
* Key Recovery Operation".
*
* http://www.secg.org/download/aid-780/sec1-v2.pdf
*/
function recoverPubKey(curve, e, signature, i) {
assert.strictEqual(i & 3, i, 'Recovery param is more than two bits')
//if (cursor != sig.length)
// throw new Error("Extra bytes in signature")
var r = signature.r
var s = signature.s
var r = BigInteger.fromByteArrayUnsigned(rBa)
var s = BigInteger.fromByteArrayUnsigned(sBa)
// A set LSB signifies that the y-coordinate is odd
var isYOdd = i & 1
return {r: r, s: s}
},
// The more significant bit specifies whether we should use the
// first or second candidate key.
var isSecondKey = i >> 1
parseSigCompact: function (sig) {
if (sig.length !== 65) {
throw new Error("Signature has the wrong length")
}
var n = curve.n
var G = curve.G
// Signature is prefixed with a type byte storing three bits of
// information.
var i = sig[0] - 27
if (i < 0 || i > 7) {
throw new Error("Invalid signature type")
}
// 1.1 Let x = r + jn
var x = isSecondKey ? r.add(n) : r
var R = curve.pointFromX(isYOdd, x)
var n = ecparams.getN()
var r = BigInteger.fromByteArrayUnsigned(sig.slice(1, 33)).mod(n)
var s = BigInteger.fromByteArrayUnsigned(sig.slice(33, 65)).mod(n)
// 1.4 Check that nR is at infinity
var nR = R.multiply(n)
assert(curve.isInfinity(nR), 'nR is not a valid curve point')
return {r: r, s: s, i: i}
},
// Compute -e from e
var eNeg = e.negate().mod(n)
/**
* Recover a public key from a signature.
*
* See SEC 1: Elliptic Curve Cryptography, section 4.1.6, "Public
* Key Recovery Operation".
*
* http://www.secg.org/download/aid-780/sec1-v2.pdf
*/
recoverPubKey: function (r, s, hash, i) {
// The recovery parameter i has two bits.
i = i & 3
// 1.6.1 Compute Q = r^-1 (sR - eG)
// Q = r^-1 (sR + -eG)
var rInv = r.modInverse(n)
// The less significant bit specifies whether the y coordinate
// of the compressed point is even or not.
var isYEven = i & 1
var Q = R.multiplyTwo(s, G, eNeg).multiply(rInv)
curve.validate(Q)
// The more significant bit specifies whether we should use the
// first or second candidate key.
var isSecondKey = i >> 1
return Q
}
var n = ecparams.getN()
var G = ecparams.getG()
var curve = ecparams.getCurve()
var p = curve.getQ()
var a = curve.getA().toBigInteger()
var b = curve.getB().toBigInteger()
/**
* Calculate pubkey extraction parameter.
*
* When extracting a pubkey from a signature, we have to
* distinguish four different cases. Rather than putting this
* burden on the verifier, Bitcoin includes a 2-bit value with the
* signature.
*
* This function simply tries all four cases and returns the value
* that resulted in a successful pubkey recovery.
*/
function calcPubKeyRecoveryParam(curve, e, signature, Q) {
for (var i = 0; i < 4; i++) {
var Qprime = recoverPubKey(curve, e, signature, i)
// We precalculate (p + 1) / 4 where p is if the field order
if (!P_OVER_FOUR) {
P_OVER_FOUR = p.add(BigInteger.ONE).divide(BigInteger.valueOf(4))
// 1.6.2 Verify Q
if (Qprime.equals(Q)) {
return i
}
}
// 1.1 Compute x
var x = isSecondKey ? r.add(n) : r
throw new Error('Unable to find valid recovery factor')
}
// 1.3 Convert x to point
var alpha = x.multiply(x).multiply(x).add(a.multiply(x)).add(b).mod(p)
var beta = alpha.modPow(P_OVER_FOUR, p)
// var xorOdd = beta.isEven() ? (i % 2) : ((i+1) % 2)
// If beta is even, but y isn't or vice versa, then convert it,
// otherwise we're done and y == beta.
var y = (beta.isEven() ? !isYEven : isYEven) ? beta : p.subtract(beta)
// 1.4 Check that nR is at infinity
var R = new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y))
R.validate()
// 1.5 Compute e from M
var e = BigInteger.fromByteArrayUnsigned(hash)
var eNeg = BigInteger.ZERO.subtract(e).mod(n)
// 1.6 Compute Q = r^-1 (sR - eG)
var rInv = r.modInverse(n)
var Q = implShamirsTrick(R, s, G, eNeg).multiply(rInv)
Q.validate()
if (!ECDSA.verifyRaw(e, r, s, Q)) {
throw new Error("Pubkey recovery unsuccessful")
}
return Q
},
/**
* Calculate pubkey extraction parameter.
*
* When extracting a pubkey from a signature, we have to
* distinguish four different cases. Rather than putting this
* burden on the verifier, Bitcoin includes a 2-bit value with the
* signature.
*
* This function simply tries all four cases and returns the value
* that resulted in a successful pubkey recovery.
*/
calcPubKeyRecoveryParam: function (origPubKey, r, s, hash) {
for (var i = 0; i < 4; i++) {
var pubKey = ECDSA.recoverPubKey(r, s, hash, i)
if (pubKey.equals(origPubKey)) {
return i
}
}
throw new Error("Unable to find valid recovery factor")
}
module.exports = {
calcPubKeyRecoveryParam: calcPubKeyRecoveryParam,
deterministicGenerateK: deterministicGenerateK,
recoverPubKey: recoverPubKey,
sign: sign,
verify: verify,
verifyRaw: verifyRaw
}
module.exports = ECDSA

@@ -1,188 +0,79 @@

var Address = require('./address')
var assert = require('assert')
var convert = require('./convert')
var base58check = require('./base58check')
var BigInteger = require('./jsbn/jsbn')
var ecdsa = require('./ecdsa')
var ECPointFp = require('./jsbn/ec').ECPointFp
var sec = require('./jsbn/sec')
var Network = require('./network')
var util = require('./util')
var ecparams = sec("secp256k1")
var networks = require('./networks')
var secureRandom = require('secure-random')
// input can be nothing, array of bytes, hex string, or base58 string
var ECKey = function (input, compressed) {
if (!(this instanceof ECKey)) { return new ECKey(input, compressed) }
if (!input) {
// Generate new key
var n = ecparams.getN()
this.priv = ecdsa.getBigRandom(n)
this.compressed = compressed || false
}
else this.import(input,compressed)
}
var BigInteger = require('bigi')
var ECPubKey = require('./ecpubkey')
ECKey.prototype.import = function (input, compressed) {
function has(li, v) { return li.indexOf(v) >= 0 }
function fromBin(x) { return BigInteger.fromByteArrayUnsigned(x) }
var ecurve = require('ecurve')
var curve = ecurve.getCurveByName('secp256k1')
this.priv =
input instanceof ECKey ? input.priv
: input instanceof BigInteger ? input.mod(ecparams.getN())
: Array.isArray(input) ? fromBin(input.slice(0, 32))
: typeof input != "string" ? null
: input.length == 44 ? fromBin(convert.base64ToBytes(input))
: input.length == 51 && input[0] == '5' ? fromBin(base58check.decode(input).payload)
: input.length == 51 && input[0] == '9' ? fromBin(base58check.decode(input).payload)
: input.length == 52 && has('LK', input[0]) ? fromBin(base58check.decode(input).payload.slice(0, 32))
: input.length == 52 && input[0] == 'c' ? fromBin(base58check.decode(input).payload.slice(0, 32))
: has([64,65],input.length) ? fromBin(convert.hexToBytes(input.slice(0, 64)))
: null
function ECKey(d, compressed) {
assert(d.signum() > 0, 'Private key must be greater than 0')
assert(d.compareTo(curve.n) < 0, 'Private key must be less than the curve order')
assert(this.priv !== null)
var Q = curve.G.multiply(d)
this.compressed =
compressed !== undefined ? compressed
: input instanceof ECKey ? input.compressed
: input instanceof BigInteger ? false
: Array.isArray(input) ? false
: typeof input != "string" ? null
: input.length == 44 ? false
: input.length == 51 && input[0] == '5' ? false
: input.length == 51 && input[0] == '9' ? false
: input.length == 52 && has('LK', input[0]) ? true
: input.length == 52 && input[0] == 'c' ? true
: input.length == 64 ? false
: input.length == 65 ? true
: null
assert(this.compressed !== null)
this.d = d
this.pub = new ECPubKey(Q, compressed)
}
ECKey.prototype.getPub = function(compressed) {
if (compressed === undefined) compressed = this.compressed
return ECPubKey(ecparams.getG().multiply(this.priv), compressed)
}
// Static constructors
ECKey.fromWIF = function(string) {
var payload = base58check.decode(string)
var compressed = false
ECKey.prototype.toBin = function() {
return convert.bytesToString(this.toBytes())
}
// Ignore the version byte
payload = payload.slice(1)
ECKey.version_bytes = {
0: 128,
111: 239
}
if (payload.length === 33) {
assert.strictEqual(payload[32], 0x01, 'Invalid compression flag')
ECKey.prototype.toWif = function(version) {
version = version || Network.mainnet.addressVersion
// Truncate the compression flag
payload = payload.slice(0, -1)
compressed = true
}
return base58check.encode(this.toBytes(), ECKey.version_bytes[version])
}
assert.equal(payload.length, 32, 'Invalid WIF payload length')
ECKey.prototype.toHex = function() {
return convert.bytesToHex(this.toBytes())
var d = BigInteger.fromBuffer(payload)
return new ECKey(d, compressed)
}
ECKey.prototype.toBytes = function() {
var bytes = this.priv.toByteArrayUnsigned()
if (this.compressed) bytes.push(1)
return bytes
}
ECKey.makeRandom = function(compressed, rng) {
rng = rng || secureRandom.randomBuffer
ECKey.prototype.toBase64 = function() {
return convert.bytesToBase64(this.toBytes())
}
var buffer = rng(32)
assert(Buffer.isBuffer(buffer), 'Expected Buffer, got ' + buffer)
ECKey.prototype.toString = ECKey.prototype.toHex
var d = BigInteger.fromBuffer(buffer)
d = d.mod(curve.n)
ECKey.prototype.getAddress = function(version) {
return this.getPub().getAddress(version)
return new ECKey(d, compressed)
}
ECKey.prototype.add = function(key) {
return ECKey(this.priv.add(ECKey(key).priv), this.compressed)
}
// Export functions
ECKey.prototype.toWIF = function(network) {
network = network || networks.bitcoin
ECKey.prototype.multiply = function(key) {
return ECKey(this.priv.multiply(ECKey(key).priv), this.compressed)
}
var bufferLen = this.pub.compressed ? 34 : 33
var buffer = new Buffer(bufferLen)
ECKey.prototype.sign = function(hash) {
return ecdsa.sign(hash, this.priv)
}
buffer.writeUInt8(network.wif, 0)
this.d.toBuffer(32).copy(buffer, 1)
ECKey.prototype.verify = function(hash, sig) {
return this.getPub().verify(hash, sig)
}
var ECPubKey = function(input, compressed) {
if (!(this instanceof ECPubKey)) {
return new ECPubKey(input, compressed)
if (this.pub.compressed) {
buffer.writeUInt8(0x01, 33)
}
this.import(input, compressed)
return base58check.encode(buffer)
}
ECPubKey.prototype.import = function(input, compressed) {
var decode = function(x) { return ECPointFp.decodeFrom(ecparams.getCurve(), x) }
this.pub =
input instanceof ECPointFp ? input
: input instanceof ECKey ? ecparams.getG().multiply(input.priv)
: input instanceof ECPubKey ? input.pub
: typeof input == "string" ? decode(convert.hexToBytes(input))
: Array.isArray(input) ? decode(input)
: null
assert(this.pub !== null)
this.compressed =
compressed ? compressed
: input instanceof ECPointFp ? input.compressed
: input instanceof ECPubKey ? input.compressed
: (this.pub[0] < 4)
// Operations
ECKey.prototype.sign = function(hash) {
return ecdsa.sign(curve, hash, this.d)
}
ECPubKey.prototype.add = function(key) {
return ECPubKey(this.pub.add(ECPubKey(key).pub), this.compressed)
}
ECPubKey.prototype.multiply = function(key) {
return ECPubKey(this.pub.multiply(ECKey(key).priv), this.compressed)
}
ECPubKey.prototype.toBytes = function(compressed) {
if (compressed === undefined) compressed = this.compressed
return this.pub.getEncoded(compressed)
}
ECPubKey.prototype.toHex = function(compressed) {
return convert.bytesToHex(this.toBytes(compressed))
}
ECPubKey.prototype.toBin = function(compressed) {
return convert.bytesToString(this.toBytes(compressed))
}
ECPubKey.prototype.toWif = function(version) {
version = version || Network.mainnet.addressVersion
return base58check.encode(this.toBytes(), version)
}
ECPubKey.prototype.toString = ECPubKey.prototype.toHex
ECPubKey.prototype.getAddress = function(version) {
version = version || Network.mainnet.addressVersion
return new Address(util.sha256ripe160(this.toBytes()), version)
}
ECPubKey.prototype.verify = function(hash, sig) {
return ecdsa.verify(hash, sig, this.toBytes())
}
module.exports = {
ECKey: ECKey,
ECPubKey: ECPubKey
}
module.exports = ECKey

@@ -1,26 +0,19 @@

var Key = require('./eckey')
var T = require('./transaction')
module.exports = {
Address: require('./address'),
Key: Key.ECKey,
ECKey: Key.ECKey,
ECPubKey: Key.ECPubKey,
base58check: require('./base58check'),
bufferutils: require('./bufferutils'),
convert: require('./convert'),
crypto: require('./crypto'),
ecdsa: require('./ecdsa'),
ECKey: require('./eckey'),
ECPubKey: require('./ecpubkey'),
ECSignature: require('./ecsignature'),
Message: require('./message'),
BigInteger: require('./jsbn/jsbn'),
Crypto: require('crypto-js'), //should we expose this at all?
opcodes: require('./opcodes'),
HDNode: require('./hdnode'),
Script: require('./script'),
Opcode: require('./opcode'),
Transaction: T.Transaction,
Util: require('./util'),
TransactionIn: T.TransactionIn,
TransactionOut: T.TransactionOut,
ECPointFp: require('./jsbn/ec').ECPointFp,
Wallet: require('./wallet'),
network: require('./network'),
ecdsa: require('./ecdsa'),
HDWallet: require('./hdwallet.js'),
base58: require('./base58'),
base58check: require('./base58check'),
convert: require('./convert')
scripts: require('./scripts'),
Transaction: require('./transaction'),
networks: require('./networks'),
Wallet: require('./wallet')
}
/// Implements Bitcoin's feature for signing arbitrary messages.
var Address = require('./address')
var convert = require('./convert')
var BigInteger = require('bigi')
var bufferutils = require('./bufferutils')
var crypto = require('./crypto')
var ecdsa = require('./ecdsa')
var ECPubKey = require('./eckey').ECPubKey
var SHA256 = require('crypto-js/sha256')
var networks = require('./networks')
// FIXME: magicHash is incompatible with other magic messages
var magicBytes = convert.stringToBytes('Bitcoin Signed Message:\n')
var Address = require('./address')
var ECPubKey = require('./ecpubkey')
var ECSignature = require('./ecsignature')
function magicHash(message) {
var messageBytes = convert.stringToBytes(message)
var ecurve = require('ecurve')
var ecparams = ecurve.getCurveByName('secp256k1')
var buffer = [].concat(
convert.numToVarInt(magicBytes.length),
magicBytes,
convert.numToVarInt(messageBytes.length),
messageBytes
)
function magicHash(message, network) {
var magicPrefix = new Buffer(network.magicPrefix)
var messageBuffer = new Buffer(message)
var lengthBuffer = new Buffer(bufferutils.varIntSize(messageBuffer.length))
bufferutils.writeVarInt(lengthBuffer, messageBuffer.length, 0)
return convert.wordArrayToBytes(SHA256(SHA256(convert.bytesToWordArray(buffer))))
var buffer = Buffer.concat([magicPrefix, lengthBuffer, messageBuffer])
return crypto.hash256(buffer)
}
// TODO: parameterize compression instead of using ECKey.compressed
function sign(key, message) {
var hash = magicHash(message)
var sig = key.sign(hash)
var obj = ecdsa.parseSig(sig)
var i = ecdsa.calcPubKeyRecoveryParam(key.getPub().pub, obj.r, obj.s, hash)
function sign(privKey, message, network) {
network = network || networks.bitcoin
i += 27
if (key.compressed) {
i += 4
}
var hash = magicHash(message, network)
var signature = privKey.sign(hash)
var e = BigInteger.fromBuffer(hash)
var i = ecdsa.calcPubKeyRecoveryParam(ecparams, e, signature, privKey.pub.Q)
var rBa = obj.r.toByteArrayUnsigned()
var sBa = obj.s.toByteArrayUnsigned()
// Pad to 32 bytes per value
while (rBa.length < 32) rBa.unshift(0);
while (sBa.length < 32) sBa.unshift(0);
sig = [i].concat(rBa, sBa)
return sig
return signature.toCompact(i, privKey.pub.compressed)
}
function verify(address, sig, message) {
sig = ecdsa.parseSigCompact(sig)
// TODO: network could be implied from address
function verify(address, signatureBuffer, message, network) {
if (address instanceof Address) {
address = address.toString()
}
network = network || networks.bitcoin
var pubKey = new ECPubKey(ecdsa.recoverPubKey(sig.r, sig.s, magicHash(message), sig.i))
var isCompressed = !!(sig.i & 4)
pubKey.compressed = isCompressed
var hash = magicHash(message, network)
var parsed = ECSignature.parseCompact(signatureBuffer)
var e = BigInteger.fromBuffer(hash)
var Q = ecdsa.recoverPubKey(ecparams, e, parsed.signature, parsed.i)
address = new Address(address)
return pubKey.getAddress(address.version).toString() === address.toString()
var pubKey = new ECPubKey(Q, parsed.compressed)
return pubKey.getAddress(network).toString() === address
}

@@ -59,0 +52,0 @@

@@ -1,391 +0,133 @@

var Opcode = require('./opcode')
var util = require('./util')
var convert = require('./convert')
var Address = require('./address')
var network = require('./network')
var assert = require('assert')
var bufferutils = require('./bufferutils')
var crypto = require('./crypto')
var opcodes = require('./opcodes')
var Script = function(data) {
this.buffer = data || []
if(!Array.isArray(this.buffer)) {
throw new Error('expect Script to be initialized with Array, but got ' + data)
}
this.parse()
}
function Script(buffer, chunks) {
assert(Buffer.isBuffer(buffer), 'Expected Buffer, got ' + buffer)
assert(Array.isArray(chunks), 'Expected Array, got ' + chunks)
Script.fromHex = function(data) {
return new Script(convert.hexToBytes(data))
this.buffer = buffer
this.chunks = chunks
}
Script.fromPubKey = function(str) {
var script = new Script()
var s = str.split(' ')
for (var i in s) {
if (Opcode.map.hasOwnProperty(s[i])) {
script.writeOp(Opcode.map[s[i]])
// Import operations
Script.fromASM = function(asm) {
var strChunks = asm.split(' ')
var chunks = strChunks.map(function(strChunk) {
if (strChunk in opcodes) {
return opcodes[strChunk]
} else {
script.writeBytes(convert.hexToBytes(s[i]))
return new Buffer(strChunk, 'hex')
}
}
return script
}
})
Script.fromScriptSig = function(str) {
var script = new Script()
var s = str.split(' ')
for (var i in s) {
if (Opcode.map.hasOwnProperty(s[i])) {
script.writeOp(Opcode.map[s[i]])
} else {
script.writeBytes(convert.hexToBytes(s[i]))
}
}
return script
return Script.fromChunks(chunks)
}
/**
* Update the parsed script representation.
*
* Each Script object stores the script in two formats. First as a raw byte
* array and second as an array of 'chunks', such as opcodes and pieces of
* data.
*
* This method updates the chunks cache. Normally this is called by the
* constructor and you don't need to worry about it. However, if you change
* the script buffer manually, you should update the chunks using this method.
*/
Script.prototype.parse = function() {
var self = this
Script.fromBuffer = function(buffer) {
var chunks = []
this.chunks = []
// Cursor
var i = 0
// Read n bytes and store result as a chunk
function readChunk(n) {
self.chunks.push(self.buffer.slice(i, i + n))
i += n
}
while (i < buffer.length) {
var opcode = buffer.readUInt8(i)
while (i < this.buffer.length) {
var opcode = this.buffer[i++]
if (opcode >= 0xF0) {
// Two byte opcode
opcode = (opcode << 8) | this.buffer[i++]
}
if ((opcode > opcodes.OP_0) && (opcode <= opcodes.OP_PUSHDATA4)) {
var d = bufferutils.readPushDataInt(buffer, i)
i += d.size
var len
if (opcode > 0 && opcode < Opcode.map.OP_PUSHDATA1) {
// Read some bytes of data, opcode value is the length of data
readChunk(opcode)
} else if (opcode == Opcode.map.OP_PUSHDATA1) {
len = this.buffer[i++]
readChunk(len)
} else if (opcode == Opcode.map.OP_PUSHDATA2) {
len = (this.buffer[i++] << 8) | this.buffer[i++]
readChunk(len)
} else if (opcode == Opcode.map.OP_PUSHDATA4) {
len = (this.buffer[i++] << 24) |
(this.buffer[i++] << 16) |
(this.buffer[i++] << 8) |
this.buffer[i++]
readChunk(len)
} else {
this.chunks.push(opcode)
}
}
}
var data = buffer.slice(i, i + d.number)
i += d.number
/**
* Compare the script to known templates of scriptPubKey.
*
* This method will compare the script to a small number of standard script
* templates and return a string naming the detected type.
*
* Currently supported are:
* Address:
* Paying to a Bitcoin address which is the hash of a pubkey.
* OP_DUP OP_HASH160 [pubKeyHash] OP_EQUALVERIFY OP_CHECKSIG
*
* Pubkey:
* Paying to a public key directly.
* [pubKey] OP_CHECKSIG
*
* Strange:
* Any other script (no template matched).
*/
Script.prototype.getOutType = function() {
if (this.chunks[this.chunks.length - 1] == Opcode.map.OP_EQUAL &&
this.chunks[0] == Opcode.map.OP_HASH160 &&
this.chunks.length == 3) {
// Transfer to M-OF-N
return 'P2SH'
} else if (this.chunks.length == 5 &&
this.chunks[0] == Opcode.map.OP_DUP &&
this.chunks[1] == Opcode.map.OP_HASH160 &&
this.chunks[3] == Opcode.map.OP_EQUALVERIFY &&
this.chunks[4] == Opcode.map.OP_CHECKSIG) {
// Transfer to Bitcoin address
return 'Pubkey'
} else {
return 'Strange'
}
}
chunks.push(data)
/**
* Returns the address corresponding to this output in hash160 form.
* Assumes strange scripts are P2SH
*/
Script.prototype.toScriptHash = function() {
var outType = this.getOutType()
} else {
chunks.push(opcode)
if (outType == 'Pubkey') {
return this.chunks[2]
i += 1
}
}
if (outType == 'P2SH') {
return util.sha256ripe160(this.buffer)
}
return util.sha256ripe160(this.buffer)
return new Script(buffer, chunks)
}
//TODO: support testnet
Script.prototype.getToAddress = function() {
var outType = this.getOutType()
Script.fromChunks = function(chunks) {
assert(Array.isArray(chunks), 'Expected Array, got ' + chunks)
if (outType == 'Pubkey') {
return new Address(this.chunks[2])
}
var bufferSize = chunks.reduce(function(accum, chunk) {
if (Buffer.isBuffer(chunk)) {
return accum + bufferutils.pushDataSize(chunk.length) + chunk.length
}
if (outType == 'P2SH') {
return new Address(this.chunks[1], 5)
}
return accum + 1
}, 0.0)
return new Address(this.chunks[1], 5)
}
var buffer = new Buffer(bufferSize)
var offset = 0
//TODO: support testnet
Script.prototype.getFromAddress = function(){
return new Address(this.simpleInHash())
}
chunks.forEach(function(chunk) {
if (Buffer.isBuffer(chunk)) {
offset += bufferutils.writePushDataInt(buffer, chunk.length, offset)
/**
* Compare the script to known templates of scriptSig.
*
* This method will compare the script to a small number of standard script
* templates and return a string naming the detected type.
*
* WARNING: Use this method with caution. It merely represents a heuristic
* based on common transaction formats. A non-standard transaction could
* very easily match one of these templates by accident.
*
* Currently supported are:
* Address:
* Paying to a Bitcoin address which is the hash of a pubkey.
* [sig] [pubKey]
*
* Pubkey:
* Paying to a public key directly.
* [sig]
*
* Multisig:
* Paying to M-of-N public keys.
*
* Strange:
* Any other script (no template matched).
*/
Script.prototype.getInType = function() {
if (this.chunks.length == 1 &&
Array.isArray(this.chunks[0])) {
// Direct IP to IP transactions only have the signature in their scriptSig.
// TODO: We could also check that the length of the data is correct.
return 'Pubkey'
} else if (this.chunks.length == 2 &&
Array.isArray(this.chunks[0]) &&
Array.isArray(this.chunks[1])) {
return 'Address'
} else if (this.chunks[0] == Opcode.map.OP_0 &&
this.chunks.slice(1).reduce(function(t, chunk, i) {
return t && Array.isArray(chunk) && (chunk[0] == 48 || i == this.chunks.length - 1)
}, true)) {
return 'Multisig'
} else {
return 'Strange'
}
}
chunk.copy(buffer, offset)
offset += chunk.length
/**
* Returns the affected public key for this input.
*
* This currently only works with payToPubKeyHash transactions. It will also
* work in the future for standard payToScriptHash transactions that use a
* single public key.
*
* However for multi-key and other complex transactions, this will only return
* one of the keys or raise an error. Therefore, it is recommended for indexing
* purposes to use Script#simpleInHash or Script#simpleOutHash instead.
*
* @deprecated
*/
Script.prototype.simpleInPubKey = function() {
switch (this.getInType()) {
case 'Address':
return this.chunks[1]
case 'Pubkey':
// TODO: Theoretically, we could recover the pubkey from the sig here.
// See https://bitcointalk.org/?topic=6430.0
throw new Error('Script does not contain pubkey')
default:
throw new Error('Encountered non-standard scriptSig')
}
}
} else {
buffer.writeUInt8(chunk, offset)
offset += 1
}
})
/**
* Returns the affected address hash for this input.
*
* For standard transactions, this will return the hash of the pubKey that
* can spend this output.
*
* In the future, for standard payToScriptHash inputs, this will return the
* scriptHash.
*
* Note: This function provided for convenience. If you have the corresponding
* scriptPubKey available, you are urged to use Script#simpleOutHash instead
* as it is more reliable for non-standard payToScriptHash transactions.
*
* This method is useful for indexing transactions.
*/
Script.prototype.simpleInHash = function() {
return util.sha256ripe160(this.simpleInPubKey())
assert.equal(offset, buffer.length, 'Could not decode chunks')
return new Script(buffer, chunks)
}
/**
* Old name for Script#simpleInHash.
*
* @deprecated
*/
Script.prototype.simpleInPubKeyHash = Script.prototype.simpleInHash
/**
* Add an op code to the script.
*/
Script.prototype.writeOp = function(opcode) {
this.buffer.push(opcode)
this.chunks.push(opcode)
Script.fromHex = function(hex) {
return Script.fromBuffer(new Buffer(hex, 'hex'))
}
/**
* Add a data chunk to the script.
*/
Script.prototype.writeBytes = function(data) {
// FIXME: Script module doesn't support buffers yet
if (Buffer.isBuffer(data)) {
data = Array.prototype.map.bind(data, function(x) { return x })()
}
// Constants
Script.EMPTY = Script.fromChunks([])
if (data.length < Opcode.map.OP_PUSHDATA1) {
this.buffer.push(data.length)
} else if (data.length <= 0xff) {
this.buffer.push(Opcode.map.OP_PUSHDATA1)
this.buffer.push(data.length)
} else if (data.length <= 0xffff) {
this.buffer.push(Opcode.map.OP_PUSHDATA2)
this.buffer.push(data.length & 0xff)
this.buffer.push((data.length >>> 8) & 0xff)
} else {
this.buffer.push(Opcode.map.OP_PUSHDATA4)
this.buffer.push(data.length & 0xff)
this.buffer.push((data.length >>> 8) & 0xff)
this.buffer.push((data.length >>> 16) & 0xff)
this.buffer.push((data.length >>> 24) & 0xff)
}
this.buffer = this.buffer.concat(data)
this.chunks.push(data)
// Operations
Script.prototype.getHash = function() {
return crypto.hash160(this.buffer)
}
/**
* Create an output for an address
*/
Script.createOutputScript = function(address) {
var script = new Script()
address = new Address(address)
if (address.version == network.mainnet.p2shVersion ||
address.version == network.testnet.p2shVersion) {
// Standard pay-to-script-hash
script.writeOp(Opcode.map.OP_HASH160)
script.writeBytes(address.hash)
script.writeOp(Opcode.map.OP_EQUAL)
}
else {
// Standard pay-to-pubkey-hash
script.writeOp(Opcode.map.OP_DUP)
script.writeOp(Opcode.map.OP_HASH160)
script.writeBytes(address.hash)
script.writeOp(Opcode.map.OP_EQUALVERIFY)
script.writeOp(Opcode.map.OP_CHECKSIG)
}
return script
// FIXME: doesn't work for data chunks, maybe time to use buffertools.compare...
Script.prototype.without = function(needle) {
return Script.fromChunks(this.chunks.filter(function(op) {
return op !== needle
}))
}
/**
* Extract pubkeys from a multisig script
*/
Script.prototype.extractPubkeys = function() {
return this.chunks.filter(function(chunk) {
return(chunk[0] == 4 && chunk.length == 65 || chunk[0] < 4 && chunk.length == 33)
})
// Export operations
var reverseOps = []
for (var op in opcodes) {
var code = opcodes[op]
reverseOps[code] = op
}
/**
* Create an m-of-n output script
*/
Script.createMultiSigOutputScript = function(m, pubkeys) {
var script = new Script()
pubkeys = pubkeys.sort()
Script.prototype.toASM = function() {
return this.chunks.map(function(chunk) {
if (Buffer.isBuffer(chunk)) {
return chunk.toString('hex')
script.writeOp(Opcode.map.OP_1 + m - 1)
for (var i = 0; i < pubkeys.length; ++i) {
script.writeBytes(pubkeys[i])
}
script.writeOp(Opcode.map.OP_1 + pubkeys.length - 1)
script.writeOp(Opcode.map.OP_CHECKMULTISIG)
return script
} else {
return reverseOps[chunk]
}
}).join(' ')
}
/**
* Create a standard payToPubKeyHash input.
*/
Script.createInputScript = function(signature, pubKey) {
var script = new Script()
script.writeBytes(signature)
script.writeBytes(pubKey)
return script
Script.prototype.toBuffer = function() {
return this.buffer
}
/**
* Create a multisig input
*/
Script.createMultiSigInputScript = function(signatures, script) {
script = new Script(script)
var k = script.chunks[0][0]
//Not enough sigs
if (signatures.length < k) return false;
var inScript = new Script()
inScript.writeOp(Opcode.map.OP_0)
signatures.map(function(sig) {
inScript.writeBytes(sig)
})
inScript.writeBytes(script.buffer)
return inScript
Script.prototype.toHex = function() {
return this.toBuffer().toString('hex')
}
Script.prototype.clone = function() {
return new Script(this.buffer)
}
module.exports = Script

@@ -1,12 +0,19 @@

var BigInteger = require('./jsbn/jsbn')
var assert = require('assert')
var bufferutils = require('./bufferutils')
var crypto = require('./crypto')
var opcodes = require('./opcodes')
var scripts = require('./scripts')
var Address = require('./address')
var ECKey = require('./eckey')
var ECSignature = require('./ecsignature')
var Script = require('./script')
var util = require('./util')
var convert = require('./convert')
var ECKey = require('./eckey').ECKey
var ECDSA = require('./ecdsa')
var Address = require('./address')
var SHA256 = require('crypto-js/sha256')
var Transaction = function (doc) {
if (!(this instanceof Transaction)) { return new Transaction(doc) }
Transaction.DEFAULT_SEQUENCE = 0xffffffff
Transaction.SIGHASH_ALL = 0x01
Transaction.SIGHASH_NONE = 0x02
Transaction.SIGHASH_SINGLE = 0x03
Transaction.SIGHASH_ANYONECANPAY = 0x80
function Transaction() {
this.version = 1

@@ -16,26 +23,2 @@ this.locktime = 0

this.outs = []
this.defaultSequence = [255, 255, 255, 255] // 0xFFFFFFFF
if (doc) {
if (typeof doc == "string" || Array.isArray(doc)) {
doc = Transaction.deserialize(doc)
}
if (doc.hash) this.hash = doc.hash;
if (doc.version) this.version = doc.version;
if (doc.locktime) this.locktime = doc.locktime;
if (doc.ins && doc.ins.length) {
doc.ins.forEach(function(input) {
this.addInput(new TransactionIn(input))
}, this)
}
if (doc.outs && doc.outs.length) {
doc.outs.forEach(function(output) {
this.addOutput(new TransactionOut(output))
}, this)
}
this.hash = this.hash || this.getHash()
}
}

@@ -48,30 +31,35 @@

*
* - An existing TransactionIn object
* - A transaction and an index
* - A transaction hash and an index
* - A single string argument of the form txhash:index
*
* Note that this method does not sign the created input.
*/
Transaction.prototype.addInput = function (tx, outIndex) {
if (arguments[0] instanceof TransactionIn) {
this.ins.push(arguments[0])
Transaction.prototype.addInput = function(tx, index, sequence) {
if (sequence == undefined) sequence = Transaction.DEFAULT_SEQUENCE
var hash
if (typeof tx === 'string') {
hash = new Buffer(tx, 'hex')
// TxId hex is big-endian, we need little-endian
Array.prototype.reverse.call(hash)
} else if (tx instanceof Transaction) {
hash = tx.getHash()
} else {
hash = tx
}
else if (arguments[0].length > 65) {
var args = arguments[0].split(':')
return this.addInput(args[0], args[1])
}
else {
var hash = typeof tx === "string" ? tx : tx.hash
hash = Array.isArray(hash) ? convert.bytesToHex(hash) : hash
this.ins.push(new TransactionIn({
outpoint: {
hash: hash,
index: outIndex
},
script: new Script(),
sequence: this.defaultSequence
}))
}
assert(Buffer.isBuffer(hash), 'Expected Transaction, txId or txHash, got ' + tx)
assert.equal(hash.length, 32, 'Expected hash length of 32, got ' + hash.length)
assert.equal(typeof index, 'number', 'Expected number index, got ' + index)
return (this.ins.push({
hash: hash,
index: index,
script: Script.EMPTY,
sequence: sequence
}) - 1)
}

@@ -84,61 +72,79 @@

*
* i) An existing TransactionOut object
* ii) An address object or an address and a value
* iii) An address:value string
*
* - A base58 address string and a value
* - An Address object and a value
* - A scriptPubKey Script and a value
*/
Transaction.prototype.addOutput = function (address, value) {
if (arguments[0] instanceof TransactionOut) {
this.outs.push(arguments[0])
return
Transaction.prototype.addOutput = function(scriptPubKey, value) {
// Attempt to get a valid address if it's a base58 address string
if (typeof scriptPubKey === 'string') {
scriptPubKey = Address.fromBase58Check(scriptPubKey)
}
if (arguments[0].indexOf(':') >= 0) {
var args = arguments[0].split(':')
address = args[0]
value = parseInt(args[1])
// Attempt to get a valid script if it's an Address object
if (scriptPubKey instanceof Address) {
var address = scriptPubKey
scriptPubKey = address.toOutputScript()
}
this.outs.push(new TransactionOut({
return (this.outs.push({
script: scriptPubKey,
value: value,
script: Script.createOutputScript(address)
}))
}) - 1)
}
/**
* Serialize this transaction.
*
* Returns the transaction as a byte array in the standard Bitcoin binary
* format. This method is byte-perfect, i.e. the resulting byte array can
* be hashed to get the transaction's standard Bitcoin hash.
*/
Transaction.prototype.serialize = function () {
var buffer = []
buffer = buffer.concat(convert.numToBytes(parseInt(this.version), 4))
buffer = buffer.concat(convert.numToVarInt(this.ins.length))
Transaction.prototype.toBuffer = function () {
var txInSize = this.ins.reduce(function(a, x) {
return a + (40 + bufferutils.varIntSize(x.script.buffer.length) + x.script.buffer.length)
}, 0)
this.ins.forEach(function(txin) {
// Why do blockchain.info, blockexplorer.com, sx and just about everybody
// else use little-endian hashes? No idea...
buffer = buffer.concat(convert.hexToBytes(txin.outpoint.hash).reverse())
var txOutSize = this.outs.reduce(function(a, x) {
return a + (8 + bufferutils.varIntSize(x.script.buffer.length) + x.script.buffer.length)
}, 0)
buffer = buffer.concat(convert.numToBytes(parseInt(txin.outpoint.index), 4))
var buffer = new Buffer(
8 +
bufferutils.varIntSize(this.ins.length) +
bufferutils.varIntSize(this.outs.length) +
txInSize +
txOutSize
)
var scriptBytes = txin.script.buffer
buffer = buffer.concat(convert.numToVarInt(scriptBytes.length))
buffer = buffer.concat(scriptBytes)
buffer = buffer.concat(txin.sequence)
var offset = 0
function writeSlice(slice) {
slice.copy(buffer, offset)
offset += slice.length
}
function writeUInt32(i) {
buffer.writeUInt32LE(i, offset)
offset += 4
}
function writeUInt64(i) {
bufferutils.writeUInt64LE(buffer, i, offset)
offset += 8
}
function writeVarInt(i) {
var n = bufferutils.writeVarInt(buffer, i, offset)
offset += n
}
writeUInt32(this.version)
writeVarInt(this.ins.length)
this.ins.forEach(function(txin) {
writeSlice(txin.hash)
writeUInt32(txin.index)
writeVarInt(txin.script.buffer.length)
writeSlice(txin.script.buffer)
writeUInt32(txin.sequence)
})
buffer = buffer.concat(convert.numToVarInt(this.outs.length))
writeVarInt(this.outs.length)
this.outs.forEach(function(txout) {
buffer = buffer.concat(convert.numToBytes(txout.value,8))
var scriptBytes = txout.script.buffer
buffer = buffer.concat(convert.numToVarInt(scriptBytes.length))
buffer = buffer.concat(scriptBytes)
writeUInt64(txout.value)
writeVarInt(txout.script.buffer.length)
writeSlice(txout.script.buffer)
})
buffer = buffer.concat(convert.numToBytes(parseInt(this.locktime), 4))
writeUInt32(this.locktime)

@@ -148,13 +154,6 @@ return buffer

Transaction.prototype.serializeHex = function() {
return convert.bytesToHex(this.serialize())
Transaction.prototype.toHex = function() {
return this.toBuffer().toString('hex')
}
//var OP_CODESEPARATOR = 171
var SIGHASH_ALL = 1
var SIGHASH_NONE = 2
var SIGHASH_SINGLE = 3
var SIGHASH_ANYONECANPAY = 80
/**

@@ -168,62 +167,50 @@ * Hash transaction for signing a specific input.

*/
Transaction.prototype.hashTransactionForSignature =
function (connectedScript, inIndex, hashType)
{
Transaction.prototype.hashForSignature = function(prevOutScript, inIndex, hashType) {
assert(inIndex >= 0, 'Invalid vin index')
assert(inIndex < this.ins.length, 'Invalid vin index')
assert(prevOutScript instanceof Script, 'Invalid Script object')
var txTmp = this.clone()
var hashScript = prevOutScript.without(opcodes.OP_CODESEPARATOR)
// In case concatenating two scripts ends up with two codeseparators,
// or an extra one at the end, this prevents all those possible
// incompatibilities.
/*scriptCode = scriptCode.filter(function (val) {
return val !== OP_CODESEPARATOR
});*/
// Blank out other inputs' signatures
txTmp.ins.forEach(function(txin) {
txin.script = new Script()
txin.script = Script.EMPTY
})
txTmp.ins[inIndex].script = hashScript
txTmp.ins[inIndex].script = connectedScript
var hashTypeModifier = hashType & 0x1f
if (hashTypeModifier === Transaction.SIGHASH_NONE) {
assert(false, 'SIGHASH_NONE not yet supported')
// Blank out some of the outputs
if ((hashType & 0x1f) == SIGHASH_NONE) {
txTmp.outs = []
} else if (hashTypeModifier === Transaction.SIGHASH_SINGLE) {
assert(false, 'SIGHASH_SINGLE not yet supported')
// Let the others update at will
txTmp.ins.forEach(function(txin, i) {
if (i != inIndex) {
txTmp.ins[i].sequence = 0
}
})
} else if ((hashType & 0x1f) == SIGHASH_SINGLE) {
// TODO: Implement
}
// Blank out other inputs completely, not recommended for open transactions
if (hashType & SIGHASH_ANYONECANPAY) {
txTmp.ins = [txTmp.ins[inIndex]]
if (hashType & Transaction.SIGHASH_ANYONECANPAY) {
assert(false, 'SIGHASH_ANYONECANPAY not yet supported')
}
var buffer = txTmp.serialize()
var hashTypeBuffer = new Buffer(4)
hashTypeBuffer.writeInt32LE(hashType, 0)
buffer = buffer.concat(convert.numToBytes(parseInt(hashType), 4))
buffer = convert.bytesToWordArray(buffer)
var buffer = Buffer.concat([txTmp.toBuffer(), hashTypeBuffer])
return crypto.hash256(buffer)
}
return convert.wordArrayToBytes(SHA256(SHA256(buffer)))
Transaction.prototype.getHash = function () {
return crypto.hash256(this.toBuffer())
}
/**
* Calculate and return the transaction's hash.
* Reverses hash since blockchain.info, blockexplorer.com and others
* use little-endian hashes for some stupid reason
*/
Transaction.prototype.getHash = function ()
{
var buffer = convert.bytesToWordArray(this.serialize())
return convert.wordArrayToBytes(SHA256(SHA256(buffer))).reverse()
Transaction.prototype.getId = function () {
var buffer = this.getHash()
// Big-endian is used for TxHash
Array.prototype.reverse.call(buffer)
return buffer.toString('hex')
}
Transaction.prototype.clone = function ()
{
Transaction.prototype.clone = function () {
var newTx = new Transaction()

@@ -233,8 +220,16 @@ newTx.version = this.version

this.ins.forEach(function(txin) {
newTx.addInput(txin.clone())
newTx.ins = this.ins.map(function(txin) {
return {
hash: txin.hash,
index: txin.index,
script: txin.script,
sequence: txin.sequence
}
})
this.outs.forEach(function(txout) {
newTx.addOutput(txout.clone())
newTx.outs = this.outs.map(function(txout) {
return {
script: txout.script,
value: txout.value
}
})

@@ -245,211 +240,98 @@

Transaction.deserialize = function(buffer) {
if (typeof buffer == "string") {
buffer = convert.hexToBytes(buffer)
Transaction.fromBuffer = function(buffer) {
var offset = 0
function readSlice(n) {
offset += n
return buffer.slice(offset - n, offset)
}
var pos = 0
var readAsInt = function(bytes) {
if (bytes === 0) return 0;
pos++;
return buffer[pos-1] + readAsInt(bytes-1) * 256
function readUInt32() {
var i = buffer.readUInt32LE(offset)
offset += 4
return i
}
var readVarInt = function() {
var bytes = buffer.slice(pos, pos + 9) // maximum possible number of bytes to read
var result = convert.varIntToNum(bytes)
pos += result.bytes.length
return result.number
function readUInt64() {
var i = bufferutils.readUInt64LE(buffer, offset)
offset += 8
return i
}
var readBytes = function(bytes) {
pos += bytes
return buffer.slice(pos - bytes, pos)
function readVarInt() {
var vi = bufferutils.readVarInt(buffer, offset)
offset += vi.size
return vi.number
}
var readVarString = function() {
var size = readVarInt()
return readBytes(size)
}
var obj = {
ins: [],
outs: []
}
obj.version = readAsInt(4)
var ins = readVarInt()
var i
for (i = 0; i < ins; i++) {
obj.ins.push({
outpoint: {
hash: convert.bytesToHex(readBytes(32).reverse()),
index: readAsInt(4)
},
script: new Script(readVarString()),
sequence: readBytes(4)
var tx = new Transaction()
tx.version = readUInt32()
var vinLen = readVarInt()
for (var i = 0; i < vinLen; ++i) {
var hash = readSlice(32)
var vout = readUInt32()
var scriptLen = readVarInt()
var script = readSlice(scriptLen)
var sequence = readUInt32()
tx.ins.push({
hash: hash,
index: vout,
script: Script.fromBuffer(script),
sequence: sequence
})
}
var outs = readVarInt()
for (i = 0; i < outs; i++) {
obj.outs.push({
value: convert.bytesToNum(readBytes(8)),
script: new Script(readVarString())
var voutLen = readVarInt()
for (i = 0; i < voutLen; ++i) {
var value = readUInt64()
var scriptLen = readVarInt()
var script = readSlice(scriptLen)
tx.outs.push({
value: value,
script: Script.fromBuffer(script)
})
}
obj.locktime = readAsInt(4)
tx.locktime = readUInt32()
assert.equal(offset, buffer.length, 'Transaction has unexpected data')
return new Transaction(obj)
return tx
}
/**
* Signs a standard output at some index with the given key
*/
Transaction.prototype.sign = function(index, key, type) {
type = type || SIGHASH_ALL
key = new ECKey(key)
// TODO: getPub is slow, sha256ripe160 probably is too.
// This could be sped up a lot by providing these as inputs.
var pub = key.getPub().toBytes(),
hash160 = util.sha256ripe160(pub),
script = Script.createOutputScript(new Address(hash160)),
hash = this.hashTransactionForSignature(script, index, type),
sig = key.sign(hash).concat([type])
this.ins[index].script = Script.createInputScript(sig, pub)
Transaction.fromHex = function(hex) {
return Transaction.fromBuffer(new Buffer(hex, 'hex'))
}
// Takes outputs of the form [{ output: 'txhash:index', address: 'address' },...]
Transaction.prototype.signWithKeys = function(keys, outputs, type) {
type = type || SIGHASH_ALL
var addrdata = keys.map(function(key) {
key = new ECKey(key)
return {
key: key,
address: key.getAddress().toString()
}
})
var hmap = {}
outputs.forEach(function(o) {
hmap[o.output] = o
})
for (var i = 0; i < this.ins.length; i++) {
var outpoint = this.ins[i].outpoint.hash + ':' + this.ins[i].outpoint.index
var histItem = hmap[outpoint]
if (!histItem) continue;
var thisInputAddrdata = addrdata.filter(function(a) {
return a.address == histItem.address
})
if (thisInputAddrdata.length === 0) continue;
this.sign(i,thisInputAddrdata[0].key)
}
}
/**
* Signs a P2SH output at some index with the given key
* Signs a pubKeyHash output at some index with the given key
*/
Transaction.prototype.p2shsign = function(index, script, key, type) {
script = new Script(script)
key = new ECKey(key)
type = type || SIGHASH_ALL
var hash = this.hashTransactionForSignature(script, index, type),
sig = key.sign(hash).concat([type])
return sig
}
Transaction.prototype.sign = function(index, privKey, hashType) {
var prevOutScript = privKey.pub.getAddress().toOutputScript()
var signature = this.signInput(index, prevOutScript, privKey, hashType)
Transaction.prototype.multisign = Transaction.prototype.p2shsign
Transaction.prototype.applyMultisigs = function(index, script, sigs/*, type*/) {
this.ins[index].script = Script.createMultiSigInputScript(sigs, script)
// FIXME: Assumed prior TX was pay-to-pubkey-hash
var scriptSig = scripts.pubKeyHashInput(signature, privKey.pub)
this.setInputScript(index, scriptSig)
}
Transaction.prototype.validateSig = function(index, script, sig, pub) {
script = new Script(script)
var hash = this.hashTransactionForSignature(script,index,1)
return ECDSA.verify(hash, convert.coerceToBytes(sig),
convert.coerceToBytes(pub))
}
Transaction.prototype.signInput = function(index, prevOutScript, privKey, hashType) {
hashType = hashType || Transaction.SIGHASH_ALL
Transaction.feePerKb = 20000
Transaction.prototype.estimateFee = function(feePerKb){
var uncompressedInSize = 180
var outSize = 34
var fixedPadding = 34
var hash = this.hashForSignature(prevOutScript, index, hashType)
var signature = privKey.sign(hash)
if(feePerKb == undefined) feePerKb = Transaction.feePerKb;
var size = this.ins.length * uncompressedInSize + this.outs.length * outSize + fixedPadding
return feePerKb * Math.ceil(size / 1000)
return signature.toScriptSignature(hashType)
}
var TransactionIn = function (data) {
if (typeof data == "string") {
this.outpoint = { hash: data.split(':')[0], index: data.split(':')[1] }
} else if (data.outpoint) {
this.outpoint = data.outpoint
} else {
this.outpoint = { hash: data.hash, index: data.index }
}
if (data.scriptSig) {
this.script = Script.fromScriptSig(data.scriptSig)
} else if (data.script) {
this.script = data.script
} else {
this.script = new Script(data.script)
}
this.sequence = data.sequence || this.defaultSequence
Transaction.prototype.setInputScript = function(index, script) {
this.ins[index].script = script
}
TransactionIn.prototype.clone = function () {
return new TransactionIn({
outpoint: {
hash: this.outpoint.hash,
index: this.outpoint.index
},
script: this.script.clone(),
sequence: this.sequence
})
}
// FIXME: could be validateInput(index, prevTxOut, pub)
Transaction.prototype.validateInput = function(index, prevOutScript, pubKey, buffer) {
var parsed = ECSignature.parseScriptSignature(buffer)
var hash = this.hashForSignature(prevOutScript, index, parsed.hashType)
var TransactionOut = function (data) {
this.script =
data.script instanceof Script ? data.script.clone()
: Array.isArray(data.script) ? new Script(data.script)
: typeof data.script == "string" ? new Script(convert.hexToBytes(data.script))
: data.scriptPubKey ? Script.fromScriptSig(data.scriptPubKey)
: data.address ? Script.createOutputScript(data.address)
: new Script()
if (this.script.buffer.length > 0) this.address = this.script.getToAddress();
this.value =
Array.isArray(data.value) ? convert.bytesToNum(data.value)
: "string" == typeof data.value ? parseInt(data.value)
: data.value instanceof BigInteger ? parseInt(data.value.toString())
: data.value
return pubKey.verify(hash, parsed.signature)
}
TransactionOut.prototype.clone = function() {
var newTxout = new TransactionOut({
script: this.script.clone(),
value: this.value
})
return newTxout
}
TransactionOut.prototype.scriptPubKey = function() {
return convert.bytesToHex(this.script.buffer)
}
module.exports = {
Transaction: Transaction,
TransactionIn: TransactionIn,
TransactionOut: TransactionOut
}
module.exports = Transaction

@@ -1,11 +0,11 @@

var convert = require('./convert')
var Transaction = require('./transaction').Transaction
var HDNode = require('./hdwallet.js')
var assert = require('assert')
var networks = require('./networks')
var rng = require('secure-random')
function Wallet(seed, options) {
if (!(this instanceof Wallet)) { return new Wallet(seed, options); }
var Address = require('./address')
var HDNode = require('./hdnode')
var Transaction = require('./transaction')
var options = options || {}
var network = options.network || 'mainnet'
function Wallet(seed, network) {
network = network || networks.bitcoin

@@ -27,9 +27,9 @@ // Stored in a closure to make accidental serialization less likely

// Make a new master key
this.newMasterKey = function(seed, network) {
if (!seed) seed = rng(32, { array: true });
masterkey = new HDNode(seed, network)
this.newMasterKey = function(seed) {
seed = seed || new Buffer(rng(32))
masterkey = HDNode.fromSeedBuffer(seed, network)
// HD first-level child derivation method should be private
// HD first-level child derivation method should be hardened
// See https://bitcointalk.org/index.php?topic=405179.msg4415254#msg4415254
accountZero = masterkey.derivePrivate(0)
accountZero = masterkey.deriveHardened(0)
externalAccount = accountZero.derive(0)

@@ -43,4 +43,4 @@ internalAccount = accountZero.derive(1)

}
this.newMasterKey(seed, network)
this.newMasterKey(seed)

@@ -70,3 +70,3 @@ this.generateAddress = function() {

var output = this.outputs[key]
if(!output.spend) utxo.push(outputToUnspentOutput(output))
if(!output.to) utxo.push(outputToUnspentOutput(output))
}

@@ -83,3 +83,3 @@

var o = unspentOutputToOutput(uo)
outputs[o.receive] = o
outputs[o.from] = o
})

@@ -90,22 +90,11 @@

this.setUnspentOutputsAsync = function(utxo, callback) {
var error = null
try {
this.setUnspentOutputs(utxo)
} catch(err) {
error = err
} finally {
process.nextTick(function(){ callback(error) })
}
}
function outputToUnspentOutput(output){
var hashAndIndex = output.receive.split(":")
var hashAndIndex = output.from.split(":")
return {
hash: hashAndIndex[0],
hashLittleEndian: convert.reverseEndian(hashAndIndex[0]),
outputIndex: parseInt(hashAndIndex[1]),
address: output.address,
value: output.value
value: output.value,
pending: output.pending
}

@@ -115,8 +104,9 @@ }

function unspentOutputToOutput(o) {
var hash = o.hash || convert.reverseEndian(o.hashLittleEndian)
var hash = o.hash
var key = hash + ":" + o.outputIndex
return {
receive: key,
from: key,
address: o.address,
value: o.value
value: o.value,
pending: o.pending
}

@@ -128,4 +118,4 @@ }

if (isNullOrUndefined(uo.hash) && isNullOrUndefined(uo.hashLittleEndian)) {
missingField = "hash(or hashLittleEndian)"
if (isNullOrUndefined(uo.hash)) {
missingField = "hash"
}

@@ -146,3 +136,3 @@

message.push(requiredKeys.join(', '))
message.push("and hash(or hashLittleEndian)")
message.push("and hash")
throw new Error(message.join(' '))

@@ -156,13 +146,30 @@ }

this.processTx = function(tx) {
var txhash = convert.bytesToHex(tx.getHash())
this.processPendingTx = function(tx){
processTx(tx, true)
}
tx.outs.forEach(function(txOut, i){
var address = txOut.address.toString()
this.processConfirmedTx = function(tx){
processTx(tx, false)
}
function processTx(tx, isPending) {
var txid = tx.getId()
tx.outs.forEach(function(txOut, i) {
var address
try {
address = Address.fromOutputScript(txOut.script, network).toString()
} catch(e) {
if (!(e.message.match(/has no matching Address/))) throw e
}
if (isMyAddress(address)) {
var output = txhash+':'+i
var output = txid + ':' + i
me.outputs[output] = {
receive: output,
from: output,
value: txOut.value,
address: address,
pending: isPending
}

@@ -172,7 +179,17 @@ }

tx.ins.forEach(function(txIn, i){
var op = txIn.outpoint
var o = me.outputs[op.hash+':'+op.index]
if (o) {
o.spend = txhash+':'+i
tx.ins.forEach(function(txIn, i) {
// copy and convert to big-endian hex
var txinId = new Buffer(txIn.hash)
Array.prototype.reverse.call(txinId)
txinId = txinId.toString('hex')
var output = txinId + ':' + txIn.index
if (!(output in me.outputs)) return
if (isPending) {
me.outputs[output].to = txid + ':' + i
me.outputs[output].pending = true
} else {
delete me.outputs[output]
}

@@ -182,67 +199,47 @@ })

this.createTx = function(to, value, fixedFee) {
checkDust(value)
this.createTx = function(to, value, fixedFee, changeAddress) {
assert(value > network.dustThreshold, value + ' must be above dust threshold (' + network.dustThreshold + ' Satoshis)')
var utxos = getCandidateOutputs(value)
var accum = 0
var subTotal = value
var addresses = []
var tx = new Transaction()
tx.addOutput(to, value)
var utxo = getCandidateOutputs(value)
var totalInValue = 0
for(var i=0; i<utxo.length; i++){
var output = utxo[i]
tx.addInput(output.receive)
for (var i = 0; i < utxos.length; ++i) {
var utxo = utxos[i]
addresses.push(utxo.address)
totalInValue += output.value
if(totalInValue < value) continue
var outpoint = utxo.from.split(':')
tx.addInput(outpoint[0], parseInt(outpoint[1]))
var fee = fixedFee == undefined ? estimateFeePadChangeOutput(tx) : fixedFee
if(totalInValue < value + fee) continue
var change = totalInValue - value - fee
if(change > 0 && !isDust(change)) {
tx.addOutput(getChangeAddress(), change)
accum += utxo.value
subTotal = value + fee
if (accum >= subTotal) {
var change = accum - subTotal
if (change > network.dustThreshold) {
tx.addOutput(changeAddress || getChangeAddress(), change)
}
break
}
break
}
checkInsufficientFund(totalInValue, value, fee)
assert(accum >= subTotal, 'Not enough funds (incl. fee): ' + accum + ' < ' + subTotal)
this.sign(tx)
this.signWith(tx, addresses)
return tx
}
this.createTxAsync = function(to, value, fixedFee, callback){
if(fixedFee instanceof Function) {
callback = fixedFee
fixedFee = undefined
}
var tx = null
var error = null
function getCandidateOutputs() {
var unspent = []
try {
tx = this.createTx(to, value, fixedFee)
} catch(err) {
error = err
} finally {
process.nextTick(function(){ callback(error, tx) })
}
}
this.dustThreshold = 5430
function isDust(amount) {
return amount <= me.dustThreshold
}
function checkDust(value){
if (isNullOrUndefined(value) || isDust(value)) {
throw new Error("Value must be above dust threshold")
}
}
function getCandidateOutputs(value){
var unspent = []
for (var key in me.outputs){
for (var key in me.outputs) {
var output = me.outputs[key]
if(!output.spend) unspent.push(output)
if (!output.pending) unspent.push(output)
}

@@ -257,6 +254,7 @@

function estimateFeePadChangeOutput(tx){
function estimateFeePadChangeOutput(tx) {
var tmpTx = tx.clone()
tmpTx.addOutput(getChangeAddress(), 0)
return tmpTx.estimateFee()
tmpTx.addOutput(getChangeAddress(), network.dustSoftThreshold || 0)
return network.estimateFee(tmpTx)
}

@@ -269,16 +267,11 @@

function checkInsufficientFund(totalInValue, value, fee) {
if(totalInValue < value + fee) {
throw new Error('Not enough money to send funds including transaction fee. Have: ' +
totalInValue + ', needed: ' + (value + fee))
}
}
this.signWith = function(tx, addresses) {
assert.equal(tx.ins.length, addresses.length, 'Number of addresses must match number of transaction inputs')
this.sign = function(tx) {
tx.ins.forEach(function(inp,i) {
var output = me.outputs[inp.outpoint.hash + ':' + inp.outpoint.index]
if (output) {
tx.sign(i, me.getPrivateKeyForAddress(output.address))
}
addresses.forEach(function(address, i) {
var key = me.getPrivateKeyForAddress(address)
tx.sign(i, key)
})
return tx

@@ -293,7 +286,7 @@ }

this.getPrivateKey = function(index) {
return externalAccount.derive(index).priv
return externalAccount.derive(index).privKey
}
this.getInternalPrivateKey = function(index) {
return internalAccount.derive(index).priv
return internalAccount.derive(index).privKey
}

@@ -300,0 +293,0 @@

var assert = require('assert')
var networks = require('../src/networks')
var Address = require('../src/address')
var network = require('../src/network')
var base58 = require('../src/base58')
var base58check = require('../src/base58check')
var mainnet = network.mainnet.addressVersion
var testnet = network.testnet.addressVersion
var Script = require('../src/script')
var fixtures = require('./fixtures/address.json')
describe('Address', function() {
var testnetAddress, mainnetAddress
var testnetP2shAddress, mainnetP2shAddress
describe('Constructor', function() {
it('does not mutate the input', function() {
fixtures.valid.forEach(function(f) {
var hash = new Buffer(f.hex, 'hex')
var addr = new Address(hash, f.version)
beforeEach(function(){
mainnetAddress = '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa'
testnetAddress = 'mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhef'
mainnetP2shAddress = '3NJZLcZEEYBpxYEUGewU4knsQRn1WM5Fkt'
testnetP2shAddress = '2MxKEf2su6FGAUfCEAHreGFQvEYrfYNHvL7'
assert.equal(addr.version, f.version)
assert.equal(addr.hash.toString('hex'), f.hex)
})
})
})
describe('parsing', function() {
it('works with Address object', function() {
var addr = new Address(new Address('mwrB4fgT1KSBCqELaWv7o7tsExuQzW3NY3', network.testnet.addressVersion))
describe('fromBase58Check', function() {
fixtures.valid.forEach(function(f) {
it('imports ' + f.description + '(' + f.network + ') correctly', function() {
var addr = Address.fromBase58Check(f.base58check)
assert.equal(addr.toString(), 'mwrB4fgT1KSBCqELaWv7o7tsExuQzW3NY3')
assert.equal(addr.version, network.testnet.addressVersion)
assert.equal(addr.version, f.version)
assert.equal(addr.hash.toString('hex'), f.hex)
})
})
it('works with hex', function() {
var addr = new Address('13483382d3c3d43fc9d7b52e652b6bbb70e8b667')
assert.equal(addr.toString(), '12kxLGqrnnchwN9bHHNV2fWDtJGwxKTcJS')
fixtures.invalid.fromBase58Check.forEach(function(f) {
it('throws on ' + f.description, function() {
assert.throws(function() {
Address.fromBase58Check(f.base58check)
}, new RegExp(f.exception))
})
})
})
it('throws error for invalid or unrecognized input', function() {
assert.throws(function() {
new Address('beepboopbeepboopbeepboopbeepboopbeepboopbeep')
}, Error)
})
describe('fromOutputScript', function() {
fixtures.valid.forEach(function(f) {
it('imports ' + f.description + '(' + f.network + ') correctly', function() {
var script = Script.fromHex(f.script)
var addr = Address.fromOutputScript(script, networks[f.network])
it('works for byte input', function() {
var hash = base58check.decode(mainnetAddress)
var addr = new Address(hash.payload)
assert.equal(addr.hash, hash.payload)
assert.equal(network.mainnet.addressVersion, hash.version)
var hash = base58check.decode(testnetAddress)
var addr = new Address(hash.payload)
assert.equal(addr.hash, hash.payload)
assert.equal(network.testnet.addressVersion, hash.version)
assert.equal(addr.version, f.version)
assert.equal(addr.hash.toString('hex'), f.hex)
})
})
it('fails for bad input', function() {
assert.throws(function() {
new Address('foo')
}, Error)
})
})
fixtures.invalid.fromOutputScript.forEach(function(f) {
it('throws when ' + f.description, function() {
var script = Script.fromHex(f.hex)
describe('getVersion', function() {
it('returns the proper address version', function() {
assert.equal(Address.getVersion(mainnetAddress), network.mainnet.addressVersion)
assert.equal(Address.getVersion(testnetAddress), network.testnet.addressVersion)
assert.throws(function() {
Address.fromOutputScript(script)
}, new RegExp(f.description))
})
})
})
describe('toString', function() {
it('defaults to base58', function() {
var addr = '18fN1QTGWmHWCA9r2dyDH6FbMEyc7XHmQQ'
assert.equal((new Address(addr)).toString(), addr)
})
})
describe('toBase58Check', function() {
fixtures.valid.forEach(function(f) {
it('exports ' + f.description + '(' + f.network + ') correctly', function() {
var addr = Address.fromBase58Check(f.base58check)
var result = addr.toBase58Check()
describe('Constructor', function(){
it('resolves version correctly', function(){
assert.equal((new Address(testnetAddress)).version, testnet)
assert.equal((new Address(mainnetAddress)).version, mainnet)
assert.equal((new Address(testnetP2shAddress)).version, network.testnet.p2shVersion)
assert.equal((new Address(mainnetP2shAddress)).version, network.mainnet.p2shVersion)
assert.equal(result, f.base58check)
})
})
})
describe('validate', function() {
it('validates known good addresses', function() {
function validate(addr, expectedVersion) {
assert.ok(Address.validate(addr))
}
describe('toOutputScript', function() {
fixtures.valid.forEach(function(f) {
it('imports ' + f.description + '(' + f.network + ') correctly', function() {
var addr = Address.fromBase58Check(f.base58check)
var script = addr.toOutputScript()
validate(testnetAddress)
validate(mainnetAddress)
validate('12KYrjTdVGjFMtaxERSk3gphreJ5US8aUP')
validate('12QeMLzSrB8XH8FvEzPMVoRxVAzTr5XM2y')
validate('1oNLrsHnBcR6dpaBpwz3LSwutbUNkNSjs')
validate('1SQHtwR5oJRKLfiWQ2APsAd9miUc4k2ez')
validate('116CGDLddrZhMrTwhCVJXtXQpxygTT1kHd')
// p2sh addresses
validate(testnetP2shAddress)
validate(mainnetP2shAddress)
assert.equal(script.toHex(), f.script)
})
})
it('does not validate illegal examples', function() {
function invalid(addr) {
assert.ok(!Address.validate(addr))
}
fixtures.invalid.toOutputScript.forEach(function(f) {
it('throws when ' + f.description, function() {
var addr = new Address(new Buffer(f.hex, 'hex'), f.version)
invalid(''); //empty should be invalid
invalid('%%@'); // invalid base58 string
invalid('1A1zP1eP5QGefi2DzPTf2L5SLmv7DivfNz'); // bad address (doesn't checksum)
invalid('mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhe'); // bad address (doesn't checksum)
assert.throws(function() {
addr.toOutputScript()
}, new RegExp(f.description))
})
})
})
})
var assert = require('assert')
var base58check = require('../').base58check
var base58check = require('../src/base58check')
var fixtures = require('./fixtures/base58check.json')
describe('base58check', function() {
var evec, dvec
describe('decode', function() {
fixtures.valid.forEach(function(f) {
it('can decode ' + f.string, function() {
var actual = base58check.decode(f.string).toString('hex')
beforeEach(function() {
function fromHex(h) { return new Buffer(h, 'hex') }
// base58check encoded strings
evec = [
'5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAbuatmU', // 0x00 WIF
'5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf', // 0x01 WIF
'5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreQyNNN1W', // 0x7f WIF
'1EHNa6Q4Jz2uvNExL497mE43ikXhwF6kZm', // uncompressed 0x01 address
'1FB8cZijTpRQp3HX8AEkNuQJBqApqfTcX7' // uncompressed 0x7f address
]
// decoded equivalent of above
dvec = [
{
version: 0x80,
payload: '0000000000000000000000000000000000000000000000000000000000000000',
checksum: '0565fba7'
},
{
version: 0x80,
payload: '0000000000000000000000000000000000000000000000000000000000000001',
checksum: 'a85aa87e',
},
{
version: 0x80,
payload: '000000000000000000000000000000000000000000000000000000000000007f',
checksum: '64046be9',
},
{
version: 0x00,
payload: '91b24bf9f5288532960ac687abb035127b1d28a5',
checksum: '0074ffe0',
},
{
version: 0x00,
payload: '9b7c46977b68474e12066a370b169ec6b9b02644',
checksum: '4d210d6e'
}
].map(function(x) {
return {
version: x.version,
payload: fromHex(x.payload),
checksum: fromHex(x.checksum)
}
assert.equal(actual, f.payload)
})
})
})
describe('decode', function() {
it('decodes the test vectors', function() {
evec.forEach(function(x, i) {
var actual = base58check.decode(x)
var expected = dvec[i]
assert.deepEqual(expected, actual)
fixtures.invalid.forEach(function(f) {
it('throws on ' + f, function() {
assert.throws(function() {
base58check.decode(f)
}, /Invalid checksum/)
})

@@ -67,8 +26,7 @@ })

describe('encode', function() {
it('encodes the test vectors', function() {
dvec.forEach(function(x, i) {
var actual = base58check.encode(x.payload, x.version)
var expected = evec[i]
fixtures.valid.forEach(function(f) {
it('can encode ' + f.string, function() {
var actual = base58check.encode(new Buffer(f.payload, 'hex'))
assert.deepEqual(expected, actual)
assert.strictEqual(actual, f.string)
})

@@ -75,0 +33,0 @@ })

var assert = require('assert')
var convert = require('../src/convert.js')
var convert = require('../src/convert')
var fixtures = require('./fixtures/convert')
describe('convert', function() {
describe('bytesToHex', function() {
it('handles example 1', function() {
assert.equal(convert.bytesToHex([0, 1, 2, 255]), '000102ff')
})
})
describe('bufferToWordArray', function() {
fixtures.valid.forEach(function(f) {
it('converts ' + f.hex + ' correctly', function() {
var buffer = new Buffer(f.hex, 'hex')
var result = convert.bufferToWordArray(buffer)
describe('hexToBytes', function() {
it('handles example 1', function() {
assert.deepEqual(convert.hexToBytes('000102ff'), [0, 1, 2, 255])
assert.deepEqual(result, f.wordArray)
})
})
})
it('converts from bytes to hex and back', function() {
var bytes = []
for (var i=0 ; i<256 ; ++i) {
bytes.push(i)
}
describe('wordArrayToBuffer', function() {
fixtures.valid.forEach(function(f) {
it('converts to ' + f.hex + ' correctly', function() {
var resultHex = convert.wordArrayToBuffer(f.wordArray).toString('hex')
var hex = convert.bytesToHex(bytes)
assert.equal(hex.length, 512)
assert.deepEqual(convert.hexToBytes(hex), bytes)
})
describe('bytesToBase64', function() {
it('passes RFC4648 test vectors', function() {
// Test vectors from:
// http://tools.ietf.org/html/rfc4648#page-12
var b64 = function(s) {
return convert.bytesToBase64(convert.stringToBytes(s))
}
assert.equal(b64(''), '')
assert.equal(b64('f'), 'Zg==')
assert.equal(b64('fo'), 'Zm8=')
assert.equal(b64('foo'), 'Zm9v')
assert.equal(b64('foob'), 'Zm9vYg==')
assert.equal(b64('fooba'), 'Zm9vYmE=')
assert.equal(b64('foobar'), 'Zm9vYmFy')
})
})
describe('byte array and word array conversions', function(){
var bytes, wordArray
beforeEach(function(){
bytes = [
98, 233, 7, 177, 92, 191, 39, 213, 66, 83,
153, 235, 246, 240, 251, 80, 235, 184, 143, 24
]
wordArray = {
words: [1659439025, 1556031445, 1112775147, -151979184, -340226280],
sigBytes: 20
}
})
describe('bytesToWords', function() {
it('works', function() {
assert.deepEqual(convert.bytesToWordArray(bytes), wordArray)
assert.deepEqual(resultHex, f.hex)
})
})
describe('bytesToWords', function() {
it('works', function() {
assert.deepEqual(convert.wordArrayToBytes(wordArray), bytes)
})
})
})
describe('numToVarInt', function() {
describe('works', function() {
var data = [
0, 128, 252, // 8-bit
256, 512, 1024, // 16-bit
65541, // 32-bit
4294967299, // 64-bit
]
var expected = [
[0], [128], [252], // 8-bit
[253, 0, 1], [253, 0, 2], [253, 0, 4], // 16-bit
[254, 5, 0, 1, 0], // 32-bit
[255, 3, 0, 0, 0, 1, 0, 0, 0] // 64-bit
]
for (var i = 0; i < data.length; ++i) {
var actual = convert.numToVarInt(data[i])
assert.deepEqual(actual, expected[i])
}
})
})
describe('varIntToNum', function() {
it('works on valid input', function() {
var data = [
[0], [128], [252], // 8-bit
[253, 0, 1], [253, 0, 2], [253, 0, 4], // 16-bit
[254, 5, 0, 1, 0], // 32-bit
[255, 3, 0, 0, 0, 1, 0, 0, 0] // 64-bit
]
var expected = [
0, 128, 252, // 8-bit
256, 512, 1024, // 16-bit
65541, // 32-bit
4294967299, // 64-bit
]
for (var i = 0; i < data.length; ++i) {
var actual = convert.varIntToNum(data[i])
assert.equal(actual.number, expected[i])
assert.deepEqual(actual.bytes, data[i])
}
})
it('uses only what is necessary', function() {
var data = [
[0, 99],
[253, 0, 1, 99],
[254, 5, 0, 1, 0, 99],
[255, 3, 0, 0, 0, 1, 0, 0, 0, 99]
]
var expected = [0, 256, 65541, 4294967299]
for (var i = 0; i < data.length; ++i) {
var actual = convert.varIntToNum(data[i])
assert.equal(actual.number, expected[i])
assert.deepEqual(actual.bytes, data[i].slice(0, -1))
}
})
})
describe('reverseEndian', function() {
it('works', function() {
var bigEndian = "6a4062273ac4f9ea4ffca52d9fd102b08f6c32faa0a4d1318e3a7b2e437bb9c7"
var littleEdian = "c7b97b432e7b3a8e31d1a4a0fa326c8fb002d19f2da5fc4feaf9c43a2762406a"
assert.deepEqual(convert.reverseEndian(bigEndian), littleEdian)
assert.deepEqual(convert.reverseEndian(littleEdian), bigEndian)
})
})
})
var assert = require('assert')
var convert = require('../').convert
var ecdsa = require('../').ecdsa
var ECPubKey = require('../').ECPubKey
var Message = require('../').Message
var crypto = require('../src/crypto')
var ecdsa = require('../src/ecdsa')
var message = require('../src/message')
var networks = require('../src/networks')
var sinon = require('sinon')
var BigInteger = require('bigi')
var ECSignature = require('../src/ecsignature')
var ecurve = require('ecurve')
var curve = ecurve.getCurveByName('secp256k1')
var fixtures = require('./fixtures/ecdsa.json')
describe('ecdsa', function() {
describe('deterministicGenerateK', function() {
fixtures.valid.forEach(function(f) {
it('for \"' + f.message + '\"', function() {
var d = BigInteger.fromHex(f.d)
var h1 = crypto.sha256(f.message)
var k = ecdsa.deterministicGenerateK(curve, h1, d)
assert.equal(k.toHex(), f.k)
})
})
it('loops until an appropriate k value is found', sinon.test(function() {
this.mock(BigInteger).expects('fromBuffer')
.exactly(3)
.onCall(0).returns(new BigInteger('0'))
.onCall(1).returns(curve.n)
.onCall(2).returns(new BigInteger('42'))
var d = new BigInteger('1')
var h1 = new Buffer(32)
var k = ecdsa.deterministicGenerateK(curve, h1, d)
assert.equal(k.toString(), '42')
}))
})
describe('recoverPubKey', function() {
it('succesfully recovers a public key', function() {
var addr = 'mgQK8S6CfSXKjPmnujArSmVxafeJfrZsa3'
var signature = convert.base64ToBytes('H0PG6+PUo96UPTJ/DVj8aBU5it+Nuli4YdsLuTMvfJxoHH9Jb7jYTQXCCOX2jrTChD5S1ic3vCrUQHdmB5/sEQY=')
var obj = ecdsa.parseSigCompact(signature)
var pubKey = new ECPubKey(ecdsa.recoverPubKey(obj.r, obj.s, Message.magicHash('1111'), obj.i))
fixtures.valid.forEach(function(f) {
it('recovers the pubKey for ' + f.d, function() {
var d = BigInteger.fromHex(f.d)
var Q = curve.G.multiply(d)
var signature = {
r: new BigInteger(f.signature.r),
s: new BigInteger(f.signature.s)
}
var h1 = crypto.sha256(f.message)
var e = BigInteger.fromBuffer(h1)
var Qprime = ecdsa.recoverPubKey(curve, e, signature, f.i)
assert.equal(pubKey.toHex(true), '02e8fcf4d749b35879bc1f3b14b49e67ab7301da3558c5a9b74a54f1e6339c334c')
assert(Qprime.equals(Q))
})
})
describe('with i ∈ {0,1,2,3}', function() {
var hash = message.magicHash('1111', networks.bitcoin)
var e = BigInteger.fromBuffer(hash)
var signatureBuffer = new Buffer('INcvXVVEFyIfHLbDX+xoxlKFn3Wzj9g0UbhObXdMq+YMKC252o5RHFr0/cKdQe1WsBLUBi4morhgZ77obDJVuV0=', 'base64')
var signature = ECSignature.parseCompact(signatureBuffer).signature
var points = [
'03e3a8c44a8bf712f1fbacee274fb19c0239b1a9e877eff0075ea335f2be8ff380',
'0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798',
'03d49e765f0bc27525c51a1b98fb1c99dacd59abe85a203af90f758260550b56c5',
'027eea09d46ac7fb6aa2e96f9c576677214ffdc238eb167734a9b39d1eb4c3d30d'
]
points.forEach(function(expectedHex, i) {
it('recovers an expected point for i of ' + i, function() {
var Qprime = ecdsa.recoverPubKey(curve, e, signature, i)
var QprimeHex = Qprime.getEncoded().toString('hex')
assert.equal(QprimeHex, expectedHex)
})
})
})
fixtures.invalid.recoverPubKey.forEach(function(f) {
it('throws on ' + f.description, function() {
var e = BigInteger.fromHex(f.e)
var signature = new ECSignature(new BigInteger(f.signature.r), new BigInteger(f.signature.s))
assert.throws(function() {
ecdsa.recoverPubKey(curve, e, signature, f.i)
}, new RegExp(f.exception))
})
})
})
describe('sign', function() {
fixtures.valid.forEach(function(f) {
it('produces a deterministic signature for \"' + f.message + '\"', function() {
var d = BigInteger.fromHex(f.d)
var hash = crypto.sha256(f.message)
var signature = ecdsa.sign(curve, hash, d)
assert.equal(signature.r.toString(), f.signature.r)
assert.equal(signature.s.toString(), f.signature.s)
})
})
it('should sign with low S value', function() {
var hash = crypto.sha256('Vires in numeris')
var sig = ecdsa.sign(curve, hash, BigInteger.ONE)
// See BIP62 for more information
var N_OVER_TWO = curve.n.shiftRight(1)
assert(sig.s.compareTo(N_OVER_TWO) <= 0)
})
})
describe('verifyRaw', function() {
fixtures.valid.forEach(function(f) {
it('verifies a valid signature for \"' + f.message + '\"', function() {
var d = BigInteger.fromHex(f.d)
var e = BigInteger.fromBuffer(crypto.sha256(f.message))
var signature = new ECSignature(
new BigInteger(f.signature.r),
new BigInteger(f.signature.s)
)
var Q = curve.G.multiply(d)
assert(ecdsa.verifyRaw(curve, e, signature, Q))
})
})
fixtures.invalid.verifyRaw.forEach(function(f) {
it('fails to verify with ' + f.description, function() {
var d = BigInteger.fromHex(f.d)
var e = BigInteger.fromHex(f.e)
var signature = new ECSignature(
new BigInteger(f.signature.r),
new BigInteger(f.signature.s)
)
var Q = curve.G.multiply(d)
assert.equal(ecdsa.verifyRaw(curve, e, signature, Q), false)
})
})
})
})
var assert = require('assert')
var ECKey = require('../src/eckey.js').ECKey
var ECPubKey = require('../src/eckey.js').ECPubKey
var convert = require('../src/convert.js')
var bytesToHex = convert.bytesToHex
var hexToBytes = convert.hexToBytes
var Address = require('../src/address')
var Network = require('../src/network')
var testnet = Network.testnet.addressVersion
var crypto = require('../src/crypto')
var networks = require('../src/networks')
var secureRandom = require('secure-random')
var sinon = require('sinon')
var BigInteger = require('bigi')
var ECKey = require('../src/eckey')
var fixtures = require('./fixtures/eckey.json')
describe('ECKey', function() {
describe('constructor', function() {
it('parses hex', function() {
var priv = '18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725'
var pub = '0450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b235' +
'22cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6'
var key = new ECKey(priv)
it('defaults to compressed', function() {
var privKey = new ECKey(BigInteger.ONE)
assert.equal(key.getPub().toHex(), pub)
assert.equal(key.compressed, false)
assert.equal(privKey.pub.compressed, true)
})
it('parses base64', function() {
var priv = 'VYdB+iv47y5FaUVIPdQInkgATrABeuD1lACUoM4x7tU='
var pub = '042f43c16c08849fed20a35bb7b1947bbf0923c52d613ee13b5c665a1e10d24b2' +
'8be909a70f5f87c1adb79fbcd1b3f17d20aa91c04fc355112dba2ce9b1cbf013b'
var key = new ECKey(priv)
it('supports the uncompressed flag', function() {
var privKey = new ECKey(BigInteger.ONE, false)
assert.equal(key.getPub().toHex(), pub)
assert.equal(key.compressed, false)
assert.equal(privKey.pub.compressed, false)
})
it('parses WIF', function() {
var priv = '5HwoXVkHoRM8sL2KmNRS217n1g8mPPBomrY7yehCuXC1115WWsh'
var pub = '044f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0' +
'f0b704075871aa385b6b1b8ead809ca67454d9683fcf2ba03456d6fe2c4abe2b07f0fbdbb2f1c1'
var addr = '1MsHWS1BnwMc3tLE8G35UXsS58fKipzB7a'
var key = new ECKey(priv)
fixtures.valid.forEach(function(f) {
it('calculates the matching pubKey for ' + f.d, function() {
var d = new BigInteger(f.d)
var privKey = new ECKey(d)
assert.equal(key.compressed, false)
assert.equal(key.getPub().toHex(), pub)
assert.equal(key.getAddress().toString(), addr)
assert.equal(privKey.pub.Q.toString(), f.Q)
})
})
it('parses compressed WIF', function() {
var priv = 'KwntMbt59tTsj8xqpqYqRRWufyjGunvhSyeMo3NTYpFYzZbXJ5Hp'
var pub = '034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa'
var addr = '1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9'
var key = new ECKey(priv)
fixtures.invalid.constructor.forEach(function(f) {
it('throws on ' + f.d, function() {
var d = new BigInteger(f.d)
assert.equal(key.compressed, true)
assert.equal(key.getPub().toHex(), pub)
assert.equal(key.getAddress().toString(), addr)
assert.throws(function() {
new ECKey(d)
}, new RegExp(f.exception))
})
})
it('alternative constructor syntax', function() {
var priv = 'ca48ec9783cf3ad0dfeff1fc254395a2e403cbbc666477b61b45e31d3b8ab458'
var pub = '044b12d9d7c77db68388b6ff7c89046174c871546436806bcd80d07c28ea81199' +
'283fbec990dad6fb98f93f712d50cb874dd717de6a184158d63886dda3090f566'
var key = ECKey(priv, false)
assert.equal(key.getPub().toHex(), pub)
assert.equal(key.compressed, false)
assert.equal(key.toHex(), priv)
})
})
describe('toAddress', function() {
var privkeys = [
'ca48ec9783cf3ad0dfeff1fc254395a2e403cbbc666477b61b45e31d3b8ab458',
'1111111111111111111111111111111111111111111111111111111111111111',
'18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725'
]
describe('fromWIF', function() {
fixtures.valid.forEach(function(f) {
f.WIFs.forEach(function(wif) {
it('imports ' + wif.string + ' correctly', function() {
var privKey = ECKey.fromWIF(wif.string)
// compressed pubkeys
var cpubkeys = [
'024b12d9d7c77db68388b6ff7c89046174c871546436806bcd80d07c28ea811992',
'034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa',
'0250863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352'
]
var pubkeys = cpubkeys.map(function(x) {
return ECPubKey(x).toHex(false)
assert.equal(privKey.d.toString(), f.d)
assert.equal(privKey.pub.compressed, wif.compressed)
})
})
})
it('mainnet', function() {
var addresses = [
'19SgmoUj4xowEjwtXvNAtYTAgbvR9iBCui',
'1MsHWS1BnwMc3tLE8G35UXsS58fKipzB7a',
'16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM'
]
var compressedAddresses = [
'1AA4sjKW2aUmbtN3MtegdvhYtDBbDEke1q',
'1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9',
'1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs',
]
for (var i = 0; i < addresses.length; ++i) {
var priv = new ECKey(privkeys[i], false)
var pub = new ECPubKey(pubkeys[i], false)
var cpub = new ECPubKey(cpubkeys[i], true)
var addr = addresses[i]
var caddr = compressedAddresses[i]
assert.equal(priv.getAddress().toString(), addr)
assert.equal(pub.getAddress().toString(), addr)
assert.equal(cpub.getAddress().toString(), caddr)
}
fixtures.invalid.WIF.forEach(function(f) {
it('throws on ' + f.string, function() {
assert.throws(function() {
ECKey.fromWIF(f.string)
}, new RegExp(f.exception))
})
})
})
it('testnet', function() {
var addresses = [
'19SgmoUj4xowEjwtXvNAtYTAgbvR9iBCui',
'1MsHWS1BnwMc3tLE8G35UXsS58fKipzB7a',
'16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM'
]
var compressedAddresses = [
'1AA4sjKW2aUmbtN3MtegdvhYtDBbDEke1q',
'1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9',
'1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs',
]
describe('toWIF', function() {
fixtures.valid.forEach(function(f) {
f.WIFs.forEach(function(wif) {
it('exports ' + wif.string + ' correctly', function() {
var privKey = ECKey.fromWIF(wif.string)
var network = networks[wif.network]
var result = privKey.toWIF(network)
for (var i = 0; i < addresses.length; ++i) {
var priv = new ECKey(privkeys[i], false)
var pub = new ECPubKey(pubkeys[i], false)
var cpub = new ECPubKey(cpubkeys[i], true)
var addr = addresses[i]
var caddr = compressedAddresses[i]
assert.equal(priv.getAddress().toString(), addr)
assert.equal(pub.getAddress().toString(), addr)
assert.equal(cpub.getAddress().toString(), caddr)
}
assert.equal(result, wif.string)
})
})
})
})
describe('signing', function() {
var hpriv = 'ca48ec9783cf3ad0dfeff1fc254395a2e403cbbc666477b61b45e31d3b8ab458'
var hcpub = '024b12d9d7c77db68388b6ff7c89046174c871546436806bcd80d07c28ea811992'
var message = 'Vires in numeris'
describe('makeRandom', function() {
var exWIF = 'KwMWvwRJeFqxYyhZgNwYuYjbQENDAPAudQx5VEmKJrUZcq6aL2pv'
var exPrivKey = ECKey.fromWIF(exWIF)
var exBuffer = exPrivKey.d.toBuffer(32)
it('should verify against the private key', function() {
var priv = new ECKey(hpriv)
var signature = priv.sign(message)
describe('using default RNG', function() {
beforeEach(function() {
sinon.stub(secureRandom, 'randomBuffer').returns(exBuffer)
})
assert(priv.verify(message, signature))
})
afterEach(function() {
secureRandom.randomBuffer.restore()
})
it('should verify against the public key', function() {
var priv = new ECKey(hpriv)
var pub = new ECPubKey(hcpub, true)
var signature = priv.sign(message)
it('generates a ECKey', function() {
var privKey = ECKey.makeRandom()
assert(pub.verify(message, signature))
assert.equal(privKey.toWIF(), exWIF)
})
it('supports compression', function() {
assert.equal(ECKey.makeRandom(true).pub.compressed, true)
assert.equal(ECKey.makeRandom(false).pub.compressed, false)
})
})
it('should not verify against the wrong private key', function() {
var priv1 = new ECKey(hpriv)
var priv2 = new ECKey('1111111111111111111111111111111111111111111111111111111111111111')
it('allows a custom RNG to be used', function() {
function rng(size) {
return exBuffer.slice(0, size)
}
var signature = priv1.sign(message)
assert(!priv2.verify(message, signature))
var privKey = ECKey.makeRandom(undefined, rng)
assert.equal(privKey.toWIF(), exWIF)
})
})
describe('output of ECPubKey', function() {
var hcpub = '024b12d9d7c77db68388b6ff7c89046174c871546436806bcd80d07c28ea811992'
var hpub = '044b12d9d7c77db68388b6ff7c89046174c871546436806bcd80d07c28ea81199283fbec990dad6fb98f93f712d50cb874dd717de6a184158d63886dda3090f566'
describe('signing', function() {
var hash = crypto.sha256('Vires in numeris')
var priv = ECKey.makeRandom()
var signature = priv.sign(hash)
it('using toHex should support compression', function() {
var pub = new ECPubKey(hpub)
assert.equal(pub.toHex(true), hcpub)
assert.equal(pub.toHex(false), hpub)
it('should verify against the public key', function() {
assert(priv.pub.verify(hash, signature))
})
it('using toBytes should support compression', function() {
var pub = new ECPubKey(hpub)
it('should not verify against the wrong public key', function() {
var priv2 = ECKey.makeRandom()
assert.equal(bytesToHex(pub.toBytes(true)), hcpub)
assert.equal(bytesToHex(pub.toBytes(false)), hpub)
assert(!priv2.pub.verify(hash, signature))
})
})
})
var assert = require('assert')
var convert = require('../').convert
var ECKey = require('../src/eckey').ECKey
var Message = require('../').Message
var testnet = require('../').network.testnet.addressVersion
var networks = require('../src/networks')
var Address = require('../src/address')
var BigInteger = require('bigi')
var ECKey = require('../src/eckey')
var Message = require('../src/message')
var fixtures = require('./fixtures/message.json')
describe('Message', function() {
var msg
describe('magicHash', function() {
fixtures.valid.magicHash.forEach(function(f) {
it('produces the correct magicHash for \"' + f.message + '\" (' + f.network + ')', function() {
var network = networks[f.network]
var actual = Message.magicHash(f.message, network)
beforeEach(function() {
msg = 'vires is numeris'
assert.equal(actual.toString('hex'), f.magicHash)
})
})
})
describe('verify', function() {
var addr, sig, caddr, csig
it('accepts an Address object', function() {
var f = fixtures.valid.verify[0]
var network = networks[f.network]
beforeEach(function() {
addr = '16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM' // uncompressed
caddr = '1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs' // compressed
sig = convert.hexToBytes('1bc25ac0fb503abc9bad23f558742740fafaec1f52deaaf106b9759a5ce84c93921c4a669c5ec3dfeb7e2d7d177a2f49db407900874f6de2f701a4c16783776d8d')
csig = convert.hexToBytes('1fc25ac0fb503abc9bad23f558742740fafaec1f52deaaf106b9759a5ce84c93921c4a669c5ec3dfeb7e2d7d177a2f49db407900874f6de2f701a4c16783776d8d')
var address = Address.fromBase58Check(f.address)
var signature = new Buffer(f.signature, 'base64')
assert.ok(Message.verify(address, signature, f.message, network))
})
it('can verify a signed message', function() {
assert.ok(Message.verify(addr, sig, msg))
assert.ok(Message.verify(caddr, csig, msg))
})
fixtures.valid.verify.forEach(function(f) {
it('verifies a valid signature for \"' + f.message + '\" (' + f.network + ')', function() {
var network = networks[f.network]
it('will fail for the wrong message', function() {
assert.ok(!Message.verify(addr, sig, 'foobar'))
assert.ok(!Message.verify(caddr, csig, 'foobar'))
})
var signature = new Buffer(f.signature, 'base64')
assert.ok(Message.verify(f.address, signature, f.message, network))
it('will fail for the wrong public key', function() {
assert.ok(!Message.verify('1MsHWS1BnwMc3tLE8G35UXsS58fKipzB7a', sig, msg))
assert.ok(!Message.verify('1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9', csig, msg))
})
if (f.compressed) {
var compressedSignature = new Buffer(f.compressed.signature, 'base64')
it('supports alternate network addresses', function() {
var taddr = 'mxnQZKxSKjzaMgrdXzk35rif3u62TLDrg9'
var tsig = convert.base64ToBytes('IGucnrTku3KLCCHUMwq9anawfrlN8RK1HWMN+10LhsHJeysBdWfj5ohJcS/+oqrlVFNvEgbgEeAQUL6r3sZwnj8=')
assert.ok(Message.verify(taddr, tsig, msg))
assert.ok(!Message.verify(taddr, tsig, 'foobar'))
assert.ok(Message.verify(f.compressed.address, compressedSignature, f.message, network))
}
})
})
it('does not cross verify (compressed/uncompressed)', function() {
assert.ok(!Message.verify(addr, csig, msg))
assert.ok(!Message.verify(caddr, sig, msg))
fixtures.invalid.verify.forEach(function(f) {
it(f.description, function() {
var signature = new Buffer(f.signature, 'base64')
assert.ok(!Message.verify(f.address, signature, f.message))
})
})

@@ -55,26 +57,16 @@ })

describe('signing', function() {
describe('using the uncompressed public key', function(){
it('gives same signature as a compressed public key', function() {
var key = new ECKey(null) // uncompressed
var sig = Message.sign(key, msg)
fixtures.valid.signing.forEach(function(f) {
it(f.description, function() {
var network = networks[f.network]
var compressedKey = new ECKey(key, true) // compressed clone
var csig = Message.sign(compressedKey, msg) // FIXME: bad compression support
var privKey = new ECKey(new BigInteger(f.d), false)
var signature = Message.sign(privKey, f.message, network)
assert.equal(signature.toString('base64'), f.signature)
var addr = key.getPub().getAddress()
var caddr = compressedKey.getPub().getAddress()
assert.ok(Message.verify(addr, sig, msg))
assert.ok(Message.verify(caddr, csig, msg))
assert.notDeepEqual(sig.slice(0, 2), csig.slice(0, 2)) // unequal compression flags
assert.deepEqual(sig.slice(2), csig.slice(2)) // equal signatures
})
})
if (f.compressed) {
var compressedPrivKey = new ECKey(new BigInteger(f.d))
var compressedSignature = Message.sign(compressedPrivKey, f.message)
describe('testnet address', function(){
it('works', function(){
var key = new ECKey(null)
var sig = Message.sign(key, msg)
var addr = key.getAddress(testnet)
assert(Message.verify(addr, sig, msg))
assert.equal(compressedSignature.toString('base64'), f.compressed.signature)
}
})

@@ -81,0 +73,0 @@ })

@@ -1,113 +0,82 @@

var Script = require('../src/script.js')
var assert = require('assert')
var Address = require('../src/address.js')
var Network = require('../src/network.js')
var Util = require('../src/util.js')
var sha256ripe160 = Util.sha256ripe160
var Convert = require('../src/convert.js')
var bytesToHex = Convert.bytesToHex
var hexToBytes = Convert.hexToBytes
var opcodes = require('../src/opcodes')
describe('Script', function() {
var p2shScriptPubKey, pubkeyScriptPubkey, addressScriptSig
var Script = require('../src/script')
beforeEach(function(){
p2shScriptPubKey = "a914e8c300c87986efa84c37c0519929019ef86eb5b487"
pubkeyScriptPubKey = "76a9145a3acbc7bbcc97c5ff16f5909c9d7d3fadb293a888ac"
addressScriptSig = "48304502206becda98cecf7a545d1a640221438ff8912d9b505ede67e0138485111099f696022100ccd616072501310acba10feb97cecc918e21c8e92760cd35144efec7622938f30141040cd2d2ce17a1e9b2b3b2cb294d40eecf305a25b7e7bfdafae6bb2639f4ee399b3637706c3d377ec4ab781355add443ae864b134c5e523001c442186ea60f0eb8"
})
var fixtures = require('./fixtures/script.json')
describe('Script', function() {
describe('constructor', function() {
it('works for a byte array', function() {
assert.ok(new Script([]))
})
it('accepts valid parameters', function() {
var buffer = new Buffer([1])
var chunks = [1]
var script = new Script(buffer, chunks)
it('works when nothing is passed in', function() {
assert.ok(new Script())
assert.equal(script.buffer, buffer)
assert.equal(script.chunks, chunks)
})
it('throws an error when input is not an array', function() {
assert.throws(function(){ new Script({}) })
assert.throws(function(){ new Script({}) }, /Expected Buffer, got/)
})
})
describe('getOutType', function() {
it('works for p2sh', function() {
var script = Script.fromHex(p2shScriptPubKey)
assert.equal(script.getOutType(), 'P2SH')
describe('fromASM/toASM', function() {
fixtures.valid.forEach(function(f) {
it('decodes/encodes ' + f.description, function() {
assert.equal(Script.fromASM(f.asm).toASM(), f.asm)
})
})
it('works for pubkey', function() {
var script = Script.fromHex(pubkeyScriptPubKey)
assert.equal(script.getOutType(), 'Pubkey')
})
})
describe('getInType', function() {
it('works for address', function() {
var script = Script.fromHex(addressScriptSig)
assert.equal(script.getInType(), 'Address')
describe('fromHex/toHex', function() {
fixtures.valid.forEach(function(f) {
it('decodes/encodes ' + f.description, function() {
assert.equal(Script.fromHex(f.hex).toHex(), f.hex)
})
})
})
describe('getToAddress', function() {
it('works for p2sh type output', function() {
var script = Script.fromHex(p2shScriptPubKey)
assert.equal(script.getToAddress().toString(), '3NukJ6fYZJ5Kk8bPjycAnruZkE5Q7UW7i8')
})
describe('getHash', function() {
fixtures.valid.forEach(function(f) {
it('produces a HASH160 of \"' + f.asm + '\"', function() {
var script = Script.fromHex(f.hex)
it('works for pubkey type output', function() {
var script = Script.fromHex(pubkeyScriptPubKey)
assert.equal(script.getToAddress().toString(), '19E6FV3m3kEPoJD5Jz6dGKdKwTVvjsWUvu')
assert.equal(script.getHash().toString('hex'), f.hash)
})
})
})
describe('getFromAddress', function() {
it('works for address type input', function() {
var script = Script.fromHex(addressScriptSig)
assert.equal(script.getFromAddress().toString(), '1BBjuhF2jHxu7tPinyQGCuaNhEs6f5u59u')
describe('fromChunks', function() {
it('should match expected behaviour', function() {
var hash = new Buffer(32)
hash.fill(0)
var script = Script.fromChunks([
opcodes.OP_HASH160,
hash,
opcodes.OP_EQUAL
])
assert.equal(script.toHex(), 'a920000000000000000000000000000000000000000000000000000000000000000087')
})
})
describe('2-of-3 Multi-Signature', function() {
var compressedPubKeys = []
var numSigs, script, multisig, network
describe('without', function() {
var hex = 'a914e8c300c87986efa94c37c0519929019ef86eb5b487'
var script = Script.fromHex(hex)
beforeEach(function() {
compressedPubKeys = ['02ea1297665dd733d444f31ec2581020004892cdaaf3dd6c0107c615afb839785f',
'02fab2dea1458990793f56f42e4a47dbf35a12a351f26fa5d7e0cc7447eaafa21f',
'036c6802ce7e8113723dd92cdb852e492ebb157a871ca532c3cb9ed08248ff0e19']
numSigs = 2
network = Network.mainnet.p2shVersion
})
it('should return a script without the given value', function() {
var subScript = script.without(opcodes.OP_HASH160)
it('should create valid multi-sig address', function() {
script = Script.createMultiSigOutputScript(numSigs, compressedPubKeys.map(hexToBytes))
multisig = sha256ripe160(script.buffer)
var multiSigAddress = Address(multisig, network).toString()
assert.ok(Address.validate(multiSigAddress))
assert.equal(Address.getVersion(multiSigAddress), Network.mainnet.p2shVersion)
assert.equal(multiSigAddress,'32vYjxBb7pHJJyXgNk8UoK3BdRDxBzny2v')
assert.equal(subScript.toHex(), '14e8c300c87986efa94c37c0519929019ef86eb5b487')
})
it('should create valid redeemScript', function() {
var redeemScript = script.buffer
var deserialized = new Script(redeemScript)
var numOfSignatures = deserialized.chunks[deserialized.chunks.length - 2] - 80
var signaturesRequired = deserialized.chunks[0] - 80
var sigs = [
bytesToHex(deserialized.chunks[1]),
bytesToHex(deserialized.chunks[2]),
bytesToHex(deserialized.chunks[3])
]
it('shouldnt mutate the original script', function() {
var subScript = script.without(opcodes.OP_EQUAL)
assert.equal(numOfSignatures, 3)
assert.equal(signaturesRequired, 2)
assert.equal(sigs[0], '02ea1297665dd733d444f31ec2581020004892cdaaf3dd6c0107c615afb839785f')
assert.equal(sigs[1], '02fab2dea1458990793f56f42e4a47dbf35a12a351f26fa5d7e0cc7447eaafa21f')
assert.equal(sigs[2], '036c6802ce7e8113723dd92cdb852e492ebb157a871ca532c3cb9ed08248ff0e19')
assert.equal(Address(sha256ripe160(redeemScript), network).toString(), '32vYjxBb7pHJJyXgNk8UoK3BdRDxBzny2v')
assert.notEqual(subScript.toHex(), hex)
assert.equal(script.toHex(), hex)
})
})
})

@@ -1,233 +0,256 @@

var T = require('../src/transaction')
var Transaction = T.Transaction
var TransactionOut = T.TransactionOut
var convert = require('../src/convert')
var ECKey = require('../src/eckey').ECKey
var Script = require('../src/script')
var assert = require('assert')
var networks = require('../src/networks')
var scripts = require('../src/scripts')
var fixtureTxes = require('./fixtures/mainnet_tx')
var fixtureTx1Hex = fixtureTxes.prevTx
var fixtureTx2Hex = fixtureTxes.tx
var fixtureTxBigHex = fixtureTxes.bigTx
var Address = require('../src/address')
var ECKey = require('../src/eckey')
var Transaction = require('../src/transaction')
var fixtures = require('./fixtures/transaction')
// FIXME: what is a better way to do this, seems a bit odd
fixtures.valid.forEach(function(f) {
var Script = require('../src/script')
f.raw.ins.forEach(function(fin) {
fin.hash = new Buffer(fin.hash, 'hex')
fin.script = Script.fromHex(fin.script)
})
f.raw.outs.forEach(function(fout) {
fout.script = Script.fromHex(fout.script)
})
})
describe('Transaction', function() {
describe('deserialize', function() {
var tx, serializedTx
beforeEach(function() {
serializedTx = [
'0100000001344630cbff61fbc362f7e1ff2f11a344c29326e4ee96e78',
'7dc0d4e5cc02fd069000000004a493046022100ef89701f460e8660c8',
'0808a162bbf2d676f40a331a243592c36d6bd1f81d6bdf022100d29c0',
'72f1b18e59caba6e1f0b8cadeb373fd33a25feded746832ec179880c2',
'3901ffffffff0100f2052a010000001976a914dd40dedd8f7e3746662',
'4c4dacc6362d8e7be23dd88ac00000000'
].join('')
tx = Transaction.deserialize(serializedTx)
describe('fromBuffer/fromHex', function() {
fixtures.valid.forEach(function(f) {
it('imports ' + f.txid + ' correctly', function() {
var actual = Transaction.fromHex(f.hex)
assert.deepEqual(actual, f.raw)
})
})
it('returns the original after serialized again', function() {
var actual = tx.serialize()
var expected = convert.hexToBytes(serializedTx)
assert.deepEqual(actual, expected)
fixtures.invalid.fromBuffer.forEach(function(f) {
it('throws on ' + f.exception, function() {
assert.throws(function() {
Transaction.fromHex(f.hex)
}, new RegExp(f.exception))
})
})
})
it('decodes version correctly', function(){
assert.equal(tx.version, 1)
describe('toBuffer/toHex', function() {
fixtures.valid.forEach(function(f) {
it('exports ' + f.txid + ' correctly', function() {
var actual = Transaction.prototype.toBuffer.call(f.raw)
assert.equal(actual.toString('hex'), f.hex)
})
})
})
it('decodes locktime correctly', function(){
assert.equal(tx.locktime, 0)
describe('addInput', function() {
// FIXME: not as pretty as could be
// Probably a bit representative of the API
var prevTxHash, prevTxId, prevTx
beforeEach(function() {
var f = fixtures.valid[0]
prevTx = Transaction.fromHex(f.hex)
prevTxHash = prevTx.getHash()
prevTxId = prevTx.getId()
})
it('decodes inputs correctly', function(){
assert.equal(tx.ins.length, 1)
it('accepts a transaction id', function() {
var tx = new Transaction()
tx.addInput(prevTxId, 0)
var input = tx.ins[0]
assert.deepEqual(input.sequence, [255, 255, 255, 255])
assert.deepEqual(tx.ins[0].hash, prevTxHash)
})
assert.equal(input.outpoint.index, 0)
assert.equal(input.outpoint.hash, "69d02fc05c4e0ddc87e796eee42693c244a3112fffe1f762c3fb61ffcb304634")
it('accepts a transaction hash', function() {
var tx = new Transaction()
tx.addInput(prevTxHash, 0)
assert.equal(convert.bytesToHex(input.script.buffer),
"493046022100ef89701f460e8660c80808a162bbf2d676f40a331a243592c36d6bd1f81d6bdf022100d29c072f1b18e59caba6e1f0b8cadeb373fd33a25feded746832ec179880c23901")
assert.deepEqual(tx.ins[0].hash, prevTxHash)
})
it('decodes outputs correctly', function(){
assert.equal(tx.outs.length, 1)
it('accepts a Transaction object', function() {
var tx = new Transaction()
tx.addInput(prevTx, 0)
var output = tx.outs[0]
assert.equal(output.value, 5000000000)
assert.equal(convert.bytesToHex(output.script.toScriptHash()), "dd40dedd8f7e37466624c4dacc6362d8e7be23dd")
// assert.equal(output.address.toString(), "n1gqLjZbRH1biT5o4qiVMiNig8wcCPQeB9")
// TODO: address is wrong because it's a testnet transaction. Transaction needs to support testnet
assert.deepEqual(tx.ins[0].hash, prevTxHash)
})
it('assigns hash to deserialized object', function(){
var hashHex = "a9d4599e15b53f3eb531608ddb31f48c695c3d0b3538a6bda871e8b34f2f430c"
assert.deepEqual(tx.hash, convert.hexToBytes(hashHex))
it('returns an index', function() {
var tx = new Transaction()
assert.equal(tx.addInput(prevTxHash, 0), 0)
assert.equal(tx.addInput(prevTxHash, 0), 1)
})
it('decodes large inputs correctly', function() {
// transaction has only 1 input
it('defaults to DEFAULT_SEQUENCE', function() {
var tx = new Transaction()
tx.addInput("0cb859105100ebc3344f749c835c7af7d7103ec0d8cbc3d8ccbd5d28c3c36b57", 0)
tx.addOutput("15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3", 100)
tx.addInput(prevTxHash, 0)
// but we're going to replace the tx.ins.length VarInt with a 32-bit equivalent
// however the same resultant number of inputs (1)
var bytes = tx.serialize()
var mutated = bytes.slice(0, 4).concat([254, 1, 0, 0, 0], bytes.slice(5))
// the deserialized-serialized transaction should return to its original state (== tx)
var bytes2 = Transaction.deserialize(mutated).serialize()
assert.deepEqual(bytes, bytes2)
assert.equal(tx.ins[0].sequence, Transaction.DEFAULT_SEQUENCE)
})
})
describe('creating a transaction', function() {
var tx, prevTx
beforeEach(function() {
prevTx = Transaction.deserialize(fixtureTx1Hex)
tx = new Transaction()
})
fixtures.valid.forEach(function(f) {
it('should add the inputs for ' + f.txid + ' correctly', function() {
var tx = new Transaction()
describe('addInput', function(){
it('allows a Transaction object to be passed in', function(){
tx.addInput(prevTx, 0)
verifyTransactionIn()
})
f.raw.ins.forEach(function(txIn, i) {
var j = tx.addInput(txIn.hash, txIn.index, txIn.sequence)
it('allows a Transaction hash to be passed in', function(){
tx.addInput("0cb859105100ebc3344f749c835c7af7d7103ec0d8cbc3d8ccbd5d28c3c36b57", 0)
verifyTransactionIn()
assert.equal(i, j)
assert.deepEqual(tx.ins[i].hash, txIn.hash)
assert.equal(tx.ins[i].index, txIn.index)
var sequence = txIn.sequence
if (sequence == undefined) sequence = Transaction.DEFAULT_SEQUENCE
assert.equal(tx.ins[i].sequence, sequence)
})
})
})
it('allows a TransactionIn object to be passed in', function(){
var txCopy = tx.clone()
txCopy.addInput(prevTx, 0)
var transactionIn = txCopy.ins[0]
fixtures.invalid.addInput.forEach(function(f) {
it('throws on ' + f.exception, function() {
var tx = new Transaction()
var hash = new Buffer(f.hash, 'hex')
tx.addInput(transactionIn)
verifyTransactionIn()
assert.throws(function() {
tx.addInput(hash, f.index)
}, new RegExp(f.exception))
})
})
})
it('allows a string in the form of txhash:index to be passed in', function(){
tx.addInput("0cb859105100ebc3344f749c835c7af7d7103ec0d8cbc3d8ccbd5d28c3c36b57:0")
verifyTransactionIn()
})
describe('addOutput', function() {
// FIXME: not as pretty as could be
// Probably a bit representative of the API
var destAddressB58, destAddress, destScript
beforeEach(function() {
destAddressB58 = '15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3'
destAddress = Address.fromBase58Check(destAddressB58)
destScript = destAddress.toOutputScript()
})
function verifyTransactionIn(){
assert.equal(tx.ins.length, 1)
it('accepts an address string', function() {
var tx = new Transaction()
tx.addOutput(destAddressB58, 40000)
var input = tx.ins[0]
assert.deepEqual(input.sequence, [255, 255, 255, 255])
assert.deepEqual(tx.outs[0].script, destScript)
assert.equal(tx.outs[0].value, 40000)
})
assert.equal(input.outpoint.index, 0)
assert.equal(input.outpoint.hash, "0cb859105100ebc3344f749c835c7af7d7103ec0d8cbc3d8ccbd5d28c3c36b57")
it('accepts an Address', function() {
var tx = new Transaction()
tx.addOutput(destAddress, 40000)
assert.deepEqual(input.script.buffer, [])
}
assert.deepEqual(tx.outs[0].script, destScript)
assert.equal(tx.outs[0].value, 40000)
})
describe('addOutput', function(){
it('allows an address and a value to be passed in', function(){
tx.addOutput("15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3", 40000)
verifyTransactionOut()
})
it('accepts a scriptPubKey', function() {
var tx = new Transaction()
tx.addOutput(destScript, 40000)
it('allows a string in the form of address:index to be passed in', function(){
tx.addOutput("15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3:40000")
verifyTransactionOut()
})
assert.deepEqual(tx.outs[0].script, destScript)
assert.equal(tx.outs[0].value, 40000)
})
it('allows a TransactionOut object to be passed in', function(){
var txCopy = tx.clone()
txCopy.addOutput("15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3:40000")
var transactionOut = txCopy.outs[0]
it('returns an index', function() {
var tx = new Transaction()
assert.equal(tx.addOutput(destScript, 40000), 0)
assert.equal(tx.addOutput(destScript, 40000), 1)
})
tx.addOutput(transactionOut)
verifyTransactionOut()
})
fixtures.valid.forEach(function(f) {
it('should add the outputs for ' + f.txid + ' correctly', function() {
var tx = new Transaction()
function verifyTransactionOut(){
assert.equal(tx.outs.length, 1)
f.raw.outs.forEach(function(txOut, i) {
var j = tx.addOutput(txOut.script, txOut.value)
var output = tx.outs[0]
assert.equal(output.value, 40000)
assert.deepEqual(convert.bytesToHex(output.script.buffer), "76a9143443bc45c560866cfeabf1d52f50a6ed358c69f288ac")
}
assert.equal(i, j)
})
assert.deepEqual(tx.outs, f.raw.outs)
})
})
})
describe('sign', function(){
it('works', function(){
tx.addInput("0cb859105100ebc3344f749c835c7af7d7103ec0d8cbc3d8ccbd5d28c3c36b57:0")
tx.addOutput("15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3:40000")
tx.addOutput("1Bu3bhwRmevHLAy1JrRB6AfcxfgDG2vXRd:50000")
describe('clone', function() {
fixtures.valid.forEach(function(f) {
var expected = Transaction.fromHex(f.hex)
var actual = expected.clone()
var key = new ECKey('L44f7zxJ5Zw4EK9HZtyAnzCYz2vcZ5wiJf9AuwhJakiV4xVkxBeb')
tx.sign(0, key)
it('should have value equality', function() {
assert.deepEqual(actual, expected)
})
var pub = key.getPub().toBytes()
var script = prevTx.outs[0].script.buffer
var sig = tx.ins[0].script.chunks[0]
assert.equal(tx.validateSig(0, script, sig, pub), true)
it('should not have reference equality', function() {
assert.notEqual(actual, expected)
})
})
})
describe('validateSig', function(){
var validTx
describe('getId', function() {
fixtures.valid.forEach(function(f) {
it('should return the txid for ' + f.txid, function() {
var tx = Transaction.fromHex(f.hex)
var actual = tx.getId()
beforeEach(function() {
validTx = Transaction.deserialize(fixtureTx2Hex)
assert.equal(actual, f.txid)
})
})
})
it('returns true for valid signature', function(){
var key = new ECKey('L44f7zxJ5Zw4EK9HZtyAnzCYz2vcZ5wiJf9AuwhJakiV4xVkxBeb')
var pub = key.getPub().toBytes()
var script = prevTx.outs[0].script.buffer
var sig = validTx.ins[0].script.chunks[0]
describe('getHash', function() {
fixtures.valid.forEach(function(f) {
it('should return the hash for ' + f.txid, function() {
var tx = Transaction.fromHex(f.hex)
var actual = tx.getHash().toString('hex')
assert.equal(validTx.validateSig(0, script, sig, pub), true)
assert.equal(actual, f.hash)
})
})
})
describe('estimateFee', function(){
it('works for fixture tx 1', function(){
var tx = Transaction.deserialize(fixtureTx1Hex)
assert.equal(tx.estimateFee(), 20000)
})
// TODO:
// hashForSignature: [Function],
it('works for fixture big tx', function(){
var tx = Transaction.deserialize(fixtureTxBigHex)
assert.equal(tx.estimateFee(), 60000)
})
// FIXME: could be better
describe('signInput/validateInput', function() {
it('works for multi-sig redeem script', function() {
var tx = new Transaction()
tx.addInput('d6f72aab8ff86ff6289842a0424319bf2ddba85dc7c52757912297f948286389', 0)
tx.addOutput('mrCDrCybB6J1vRfbwM5hemdJz73FwDBC8r', 1)
it('allow feePerKb to be passed in as an argument', function(){
var tx = Transaction.deserialize(fixtureTx2Hex)
assert.equal(tx.estimateFee(10000), 10000)
var privKeys = [
'5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf',
'5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAvUcVfH'
].map(function(wif) {
return ECKey.fromWIF(wif)
})
var pubKeys = privKeys.map(function(eck) { return eck.pub })
var redeemScript = scripts.multisigOutput(2, pubKeys)
it('allow feePerKb to be set to 0', function(){
var tx = Transaction.deserialize(fixtureTx2Hex)
assert.equal(tx.estimateFee(0), 0)
var signatures = privKeys.map(function(privKey) {
return tx.signInput(0, redeemScript, privKey)
})
})
})
describe('TransactionOut', function() {
describe('scriptPubKey', function() {
it('returns hex string', function() {
var txOut = new TransactionOut({
value: 50000,
script: Script.createOutputScript("1AZpKpcfCzKDUeTFBQUL4MokQai3m3HMXv")
})
var redeemScriptSig = scripts.multisigInput(signatures)
var scriptSig = scripts.scriptHashInput(redeemScriptSig, redeemScript)
tx.setInputScript(0, scriptSig)
assert.equal(txOut.scriptPubKey(), "76a91468edf28474ee22f68dfe7e56e76c017c1701b84f88ac")
signatures.forEach(function(sig, i){
assert(tx.validateInput(0, redeemScript, privKeys[i].pub, sig))
})
var expected = '010000000189632848f99722915727c5c75da8db2dbf194342a0429828f66ff88fab2af7d600000000fd1b0100483045022100e5be20d440b2bbbc886161f9095fa6d0bca749a4e41d30064f30eb97adc7a1f5022061af132890d8e4e90fedff5e9365aeeb77021afd8ef1d5c114d575512e9a130a0147304402205054e38e9d7b5c10481b6b4991fde5704cd94d49e344406e3c2ce4d18a43bf8e022051d7ba8479865b53a48bee0cce86e89a25633af5b2918aa276859489e232f51c014c8752410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52aeffffffff0101000000000000001976a914751e76e8199196d454941c45d1b3a323f1433bd688ac00000000'
assert.equal(tx.toHex(), expected)
})
})
})

@@ -1,13 +0,12 @@

var Wallet = require('../src/wallet.js')
var HDNode = require('../src/hdwallet.js')
var T = require('../src/transaction.js')
var Transaction = T.Transaction
var TransactionOut = T.TransactionOut
var Script = require('../src/script.js')
var convert = require('../src/convert.js')
var assert = require('assert')
var crypto = require('../src/crypto')
var networks = require('../src/networks')
var sinon = require('sinon')
var SHA256 = require('crypto-js/sha256')
var Crypto = require('crypto-js')
var scripts = require('../src/scripts')
var Address = require('../src/address')
var HDNode = require('../src/hdnode')
var Transaction = require('../src/transaction')
var Wallet = require('../src/wallet')
var fixtureTxes = require('./fixtures/mainnet_tx')

@@ -17,6 +16,18 @@ var fixtureTx1Hex = fixtureTxes.prevTx

function fakeTxHash(i) {
var hash = new Buffer(32)
hash.fill(i)
return hash
}
function fakeTxId(i) {
var hash = fakeTxHash(i)
Array.prototype.reverse.call(hash)
return hash.toString('hex')
}
describe('Wallet', function() {
var seed, wallet
beforeEach(function(){
seed = convert.wordArrayToBytes(SHA256("don't use a string seed like this in real life"))
seed = crypto.sha256("don't use a string seed like this in real life")
wallet = new Wallet(seed)

@@ -26,10 +37,6 @@ })

describe('constructor', function() {
it('should be ok to call without new', function() {
assert.ok(Wallet(seed) instanceof Wallet)
it('defaults to Bitcoin network', function() {
assert.equal(wallet.getMasterKey().network, networks.bitcoin)
})
it('defaults to Bitcoin mainnet', function() {
assert.equal(wallet.getMasterKey().network, 'mainnet')
})
it("generates m/0' as the main account", function() {

@@ -62,7 +69,7 @@ var mainAccount = wallet.getAccountZero()

beforeEach(function() {
wallet = new Wallet(seed, {network: 'testnet'})
wallet = new Wallet(seed, networks.testnet)
})
it('uses the network if specified', function() {
assert.equal(wallet.getMasterKey().network, 'testnet')
assert.equal(wallet.getMasterKey().network, networks.testnet)
})

@@ -102,3 +109,3 @@ })

it('generate receiving addresses', function(){
var wallet = new Wallet(seed, {network: 'testnet'})
var wallet = new Wallet(seed, networks.testnet)
var expectedAddresses = [

@@ -117,3 +124,3 @@ "n1GyUANZand9Kw6hGSV9837cCC9FFUQzQa",

it('generates change addresses', function(){
var wallet = new Wallet(seed, {network: 'testnet'})
var wallet = new Wallet(seed, networks.testnet)
var expectedAddresses = ["mnXiDR4MKsFxcKJEZjx4353oXvo55iuptn"]

@@ -128,6 +135,6 @@

it('returns the private key at the given index of external account', function(){
var wallet = new Wallet(seed, {network: 'testnet'})
var wallet = new Wallet(seed, networks.testnet)
assertEqual(wallet.getPrivateKey(0), wallet.getExternalAccount().derive(0).priv)
assertEqual(wallet.getPrivateKey(1), wallet.getExternalAccount().derive(1).priv)
assertEqual(wallet.getPrivateKey(0), wallet.getExternalAccount().derive(0).privKey)
assertEqual(wallet.getPrivateKey(1), wallet.getExternalAccount().derive(1).privKey)
})

@@ -138,6 +145,6 @@ })

it('returns the private key at the given index of internal account', function(){
var wallet = new Wallet(seed, {network: 'testnet'})
var wallet = new Wallet(seed, networks.testnet)
assertEqual(wallet.getInternalPrivateKey(0), wallet.getInternalAccount().derive(0).priv)
assertEqual(wallet.getInternalPrivateKey(1), wallet.getInternalAccount().derive(1).priv)
assertEqual(wallet.getInternalPrivateKey(0), wallet.getInternalAccount().derive(0).privKey)
assertEqual(wallet.getInternalPrivateKey(1), wallet.getInternalAccount().derive(1).privKey)
})

@@ -148,3 +155,3 @@ })

it('returns the private key for the given address', function(){
var wallet = new Wallet(seed, {network: 'testnet'})
var wallet = new Wallet(seed, networks.testnet)
wallet.generateChangeAddress()

@@ -154,10 +161,14 @@ wallet.generateAddress()

assertEqual(wallet.getPrivateKeyForAddress("n2fiWrHqD6GM5GiEqkbWAc6aaZQp3ba93X"),
wallet.getExternalAccount().derive(1).priv)
assertEqual(wallet.getPrivateKeyForAddress("mnXiDR4MKsFxcKJEZjx4353oXvo55iuptn"),
wallet.getInternalAccount().derive(0).priv)
assertEqual(
wallet.getPrivateKeyForAddress("n2fiWrHqD6GM5GiEqkbWAc6aaZQp3ba93X"),
wallet.getExternalAccount().derive(1).privKey
)
assertEqual(
wallet.getPrivateKeyForAddress("mnXiDR4MKsFxcKJEZjx4353oXvo55iuptn"),
wallet.getInternalAccount().derive(0).privKey
)
})
it('raises an error when address is not found', function(){
var wallet = new Wallet(seed, {network: 'testnet'})
var wallet = new Wallet(seed, networks.testnet)
assert.throws(function() {

@@ -174,6 +185,6 @@ wallet.getPrivateKeyForAddress("n2fiWrHqD6GM5GiEqkbWAc6aaZQp3ba93X")

"hash":"6a4062273ac4f9ea4ffca52d9fd102b08f6c32faa0a4d1318e3a7b2e437bb9c7",
"hashLittleEndian":"c7b97b432e7b3a8e31d1a4a0fa326c8fb002d19f2da5fc4feaf9c43a2762406a",
"outputIndex": 0,
"address" : "1AZpKpcfCzKDUeTFBQUL4MokQai3m3HMXv",
"value": 20000
"value": 20000,
"pending": true
}

@@ -186,5 +197,6 @@ expectedOutputKey = expectedUtxo.hash + ":" + expectedUtxo.outputIndex

wallet.outputs[key] = {
receive: key,
from: key,
address: utxo.address,
value: utxo.value
value: utxo.value,
pending: utxo.pending
}

@@ -207,10 +219,2 @@ }

})
it('excludes spent outputs', function(){
addUtxoToOutput(expectedUtxo)
addUtxoToOutput(utxo1)
wallet.outputs[utxo1.hash + ':' + utxo1.outputIndex].spend = "sometxn:m"
assert.equal(wallet.getBalance(), 20000)
})
})

@@ -227,4 +231,6 @@

it('excludes spent outputs', function(){
wallet.outputs[expectedOutputKey].spend = "sometxn:m"
it("ignores pending spending outputs (outputs with 'to' property)", function(){
var output = wallet.outputs[expectedOutputKey]
output.to = fakeTxId(0) + ':' + 0
output.pending = true
assert.deepEqual(wallet.getUnspentOutputs(), [])

@@ -240,5 +246,3 @@ })

it('uses hashLittleEndian when hash is not present', function(){
delete utxo[0]['hash']
it('matches the expected behaviour', function(){
wallet.setUnspentOutputs(utxo)

@@ -248,25 +252,4 @@ verifyOutputs()

it('uses hash when hashLittleEndian is not present', function(){
delete utxo[0]['hashLittleEndian']
wallet.setUnspentOutputs(utxo)
verifyOutputs()
})
it('uses hash when both hash and hashLittleEndian are present', function(){
wallet.setUnspentOutputs(utxo)
verifyOutputs()
})
describe('required fields', function(){
it("throws an error when hash and hashLittleEndian are both missing", function(){
delete utxo[0]['hash']
delete utxo[0]['hashLittleEndian']
assert.throws(function() {
wallet.setUnspentOutputs(utxo)
}, /Invalid unspent output: key hash\(or hashLittleEndian\) is missing/)
});
['outputIndex', 'address', 'value'].forEach(function(field){
['outputIndex', 'address', 'hash', 'value'].forEach(function(field){
it("throws an error when " + field + " is missing", function(){

@@ -289,108 +272,138 @@ delete utxo[0][field]

})
})
describe('setUnspentOutputsAsync', function(){
var utxo
beforeEach(function(){
utxo = cloneObject([expectedUtxo])
})
describe('Process transaction', function(){
var addresses
var tx
afterEach(function(){
wallet.setUnspentOutputs.restore()
})
beforeEach(function(){
addresses = [
'115qa7iPZqn6as57hxLL8E9VUnhmGQxKWi',
'1Bu3bhwRmevHLAy1JrRB6AfcxfgDG2vXRd',
'1BBjuhF2jHxu7tPinyQGCuaNhEs6f5u59u'
]
it('calls setUnspentOutputs', function(done){
sinon.stub(wallet, "setUnspentOutputs")
tx = Transaction.fromHex(fixtureTx1Hex)
})
var callback = function(){
assert(wallet.setUnspentOutputs.calledWith(utxo))
done()
}
describe("processPendingTx", function(){
it("incoming: sets the pending flag on output", function(){
wallet.addresses = [addresses[0]]
wallet.processPendingTx(tx)
wallet.setUnspentOutputsAsync(utxo, callback)
verifyOutputAdded(0, true)
})
it('when setUnspentOutputs throws an error, it invokes callback with error', function(done){
sinon.stub(wallet, "setUnspentOutputs").throws()
describe("when tx ins outpoint contains a known txhash:i", function(){
var spendTx
beforeEach(function(){
wallet.addresses = [addresses[0]]
wallet.processConfirmedTx(tx)
var callback = function(err){
assert(err instanceof Error)
done()
}
wallet.setUnspentOutputsAsync(utxo, callback)
})
})
})
spendTx = Transaction.fromHex(fixtureTx2Hex)
})
describe('processTx', function(){
var tx
it("outgoing: sets the pending flag and 'to' on output", function(){
var txIn = spendTx.ins[0]
var txInId = new Buffer(txIn.hash)
Array.prototype.reverse.call(txInId)
txInId = txInId.toString('hex')
beforeEach(function(){
tx = Transaction.deserialize(fixtureTx1Hex)
var key = txInId + ':' + txIn.index
assert(!wallet.outputs[key].pending)
wallet.processPendingTx(spendTx)
assert(wallet.outputs[key].pending)
assert.equal(wallet.outputs[key].to, spendTx.getId() + ':' + 0)
})
})
})
describe("when tx outs contains an address owned by the wallet, an 'output' gets added to wallet.outputs", function(){
it("works for receive address", function(){
var totalOuts = outputCount()
wallet.addresses = [tx.outs[0].address.toString()]
describe('processConfirmedTx', function(){
it('does not fail on scripts with no corresponding Address', function() {
var pubKey = wallet.getPrivateKey(0).pub
var script = scripts.pubKeyOutput(pubKey)
var tx2 = new Transaction()
tx2.addInput(fakeTxId(1), 0)
wallet.processTx(tx)
// FIXME: Transaction doesn't support custom ScriptPubKeys... yet
// So for now, we hijack the script with our own, and undefine the cached address
tx2.addOutput(addresses[0], 10000)
tx2.outs[0].script = script
tx2.outs[0].address = undefined
assert.equal(outputCount(), totalOuts + 1)
verifyOutputAdded(0)
wallet.processConfirmedTx(tx2)
})
it("works for change address", function(){
var totalOuts = outputCount()
wallet.changeAddresses = [tx.outs[1].address.toString()]
describe("when tx outs contains an address owned by the wallet, an 'output' gets added to wallet.outputs", function(){
it("works for receive address", function(){
var totalOuts = outputCount()
wallet.processTx(tx)
wallet.addresses = [addresses[0]]
wallet.processConfirmedTx(tx)
assert.equal(outputCount(), totalOuts + 1)
verifyOutputAdded(1)
})
assert.equal(outputCount(), totalOuts + 1)
verifyOutputAdded(0, false)
})
function outputCount(){
return Object.keys(wallet.outputs).length
}
it("works for change address", function(){
var totalOuts = outputCount()
wallet.changeAddresses = [addresses[1]]
function verifyOutputAdded(index) {
var txOut = tx.outs[index]
var key = convert.bytesToHex(tx.getHash()) + ":" + index
var output = wallet.outputs[key]
assert.equal(output.receive, key)
assert.equal(output.value, txOut.value)
assert.equal(output.address, txOut.address)
}
})
wallet.processConfirmedTx(tx)
describe("when tx ins outpoint contains a known txhash:i, the corresponding 'output' gets updated", function(){
beforeEach(function(){
wallet.addresses = [tx.outs[0].address.toString()] // the address fixtureTx2 used as input
wallet.processTx(tx)
assert.equal(outputCount(), totalOuts + 1)
verifyOutputAdded(1, false)
})
tx = Transaction.deserialize(fixtureTx2Hex)
function outputCount(){
return Object.keys(wallet.outputs).length
}
})
it("does not add to wallet.outputs", function(){
var outputs = wallet.outputs
wallet.processTx(tx)
assert.deepEqual(wallet.outputs, outputs)
})
describe("when tx ins outpoint contains a known txhash:i", function(){
var spendTx
beforeEach(function(){
wallet.addresses = [addresses[0]] // the address fixtureTx2 used as input
wallet.processConfirmedTx(tx)
it("sets spend with the transaction hash and input index", function(){
wallet.processTx(tx)
spendTx = Transaction.fromHex(fixtureTx2Hex)
})
var txIn = tx.ins[0]
var key = txIn.outpoint.hash + ":" + txIn.outpoint.index
var output = wallet.outputs[key]
it("does not add to wallet.outputs", function(){
wallet.processConfirmedTx(spendTx)
assert.deepEqual(wallet.outputs, {})
})
assert.equal(output.spend, convert.bytesToHex(tx.getHash()) + ':' + 0)
it("deletes corresponding 'output'", function(){
var txIn = spendTx.ins[0]
var txInId = new Buffer(txIn.hash)
Array.prototype.reverse.call(txInId)
txInId = txInId.toString('hex')
var expected = txInId + ':' + txIn.index
assert(expected in wallet.outputs)
wallet.processConfirmedTx(spendTx)
assert(!(expected in wallet.outputs))
})
})
it("does nothing when none of the involved addresses belong to the wallet", function(){
wallet.processConfirmedTx(tx)
assert.deepEqual(wallet.outputs, {})
})
})
it("does nothing when none of the involved addresses belong to the wallet", function(){
var outputs = wallet.outputs
wallet.processTx(tx)
assert.deepEqual(wallet.outputs, outputs)
})
function verifyOutputAdded(index, pending) {
var txOut = tx.outs[index]
var key = tx.getId() + ":" + index
var output = wallet.outputs[key]
assert.equal(output.from, key)
assert.equal(output.value, txOut.value)
assert.equal(output.pending, pending)
var txOutAddress = Address.fromOutputScript(txOut.script).toString()
assert.equal(output.address, txOutAddress)
}
})

@@ -413,3 +426,3 @@

{
"hash": fakeTxHash(1),
"hash": fakeTxId(1),
"outputIndex": 0,

@@ -420,3 +433,3 @@ "address" : address1,

{
"hash": fakeTxHash(2),
"hash": fakeTxId(2),
"outputIndex": 1,

@@ -427,6 +440,6 @@ "address" : address1,

{
"hash": fakeTxHash(3),
"hash": fakeTxId(3),
"outputIndex": 0,
"address" : address2,
"value": 520000 // enough for value and fee
"value": 510000 // enough for value and fee
}

@@ -437,10 +450,3 @@ ]

describe('choosing utxo', function(){
it('calculates fees', function(){
var tx = wallet.createTx(to, value)
assert.equal(tx.ins.length, 1)
assert.deepEqual(tx.ins[0].outpoint, { hash: fakeTxHash(3), index: 0 })
})
describe('transaction fee', function(){
it('allows fee to be specified', function(){

@@ -450,27 +456,61 @@ var fee = 30000

assert.equal(tx.ins.length, 2)
assert.deepEqual(tx.ins[0].outpoint, { hash: fakeTxHash(3), index: 0 })
assert.deepEqual(tx.ins[1].outpoint, { hash: fakeTxHash(2), index: 1 })
assert.equal(getFee(wallet, tx), fee)
})
it('allows fee to be set to zero', function(){
value = 520000
value = 510000
var fee = 0
var tx = wallet.createTx(to, value, fee)
assert.equal(getFee(wallet, tx), fee)
})
it('does not overestimate fees when network has dustSoftThreshold', function(){
var wallet = new Wallet(seed, networks.litecoin)
var address = wallet.generateAddress()
wallet.setUnspentOutputs([{
hash: fakeTxId(0),
outputIndex: 0,
address: address,
value: 500000
}])
value = 200000
var tx = wallet.createTx(address, value)
assert.equal(getFee(wallet, tx), 100000)
})
function getFee(wallet, tx) {
var inputValue = tx.ins.reduce(function(memo, input){
var id = Array.prototype.reverse.call(input.hash).toString('hex')
return memo + wallet.outputs[id + ':' + input.index].value
}, 0)
return tx.outs.reduce(function(memo, output){
return memo - output.value
}, inputValue)
}
})
describe('choosing utxo', function(){
it('takes fees into account', function(){
var tx = wallet.createTx(to, value)
assert.equal(tx.ins.length, 1)
assert.deepEqual(tx.ins[0].outpoint, { hash: fakeTxHash(3), index: 0 })
assert.deepEqual(tx.ins[0].hash, fakeTxHash(3))
assert.equal(tx.ins[0].index, 0)
})
it('ignores spent outputs', function(){
it('ignores pending outputs', function(){
utxo.push(
{
"hash": fakeTxHash(4),
"hash": fakeTxId(4),
"outputIndex": 0,
"address" : address2,
"value": 530000 // enough but spent before createTx
"value": 530000,
"pending": true
}
)
wallet.setUnspentOutputs(utxo)
wallet.outputs[fakeTxHash(4) + ":" + 0].spend = fakeTxHash(5) + ":" + 0

@@ -480,6 +520,63 @@ var tx = wallet.createTx(to, value)

assert.equal(tx.ins.length, 1)
assert.deepEqual(tx.ins[0].outpoint, { hash: fakeTxHash(3), index: 0 })
assert.deepEqual(tx.ins[0].hash, fakeTxHash(3))
assert.equal(tx.ins[0].index, 0)
})
})
describe('works for testnet', function(){
it('should create transaction', function(){
var wallet = new Wallet(seed, networks.testnet)
var address = wallet.generateAddress()
wallet.setUnspentOutputs([{
hash: fakeTxId(0),
outputIndex: 0,
address: address,
value: value
}])
var to = 'mt7MyTVVEWnbwpF5hBn6fgnJcv95Syk2ue'
var toValue = value - 10000
var tx = wallet.createTx(to, toValue)
assert.equal(tx.outs.length, 1)
var outAddress = Address.fromOutputScript(tx.outs[0].script, networks.testnet)
assert.equal(outAddress.toString(), to)
assert.equal(tx.outs[0].value, toValue)
})
})
describe('changeAddress', function(){
it('should allow custom changeAddress', function(){
var wallet = new Wallet(seed, networks.testnet)
var address = wallet.generateAddress()
wallet.setUnspentOutputs([{
hash: fakeTxId(0),
outputIndex: 0,
address: address,
value: value
}])
assert.equal(wallet.getBalance(), value)
var changeAddress = 'mfrFjnKZUvTcvdAK2fUX5D8v1Epu5H8JCk'
var to = 'mt7MyTVVEWnbwpF5hBn6fgnJcv95Syk2ue'
var toValue = value / 2
var fee = 1e3
var tx = wallet.createTx(to, toValue, fee, changeAddress)
assert.equal(tx.outs.length, 2)
var outAddress0 = Address.fromOutputScript(tx.outs[0].script, networks.testnet)
var outAddress1 = Address.fromOutputScript(tx.outs[1].script, networks.testnet)
assert.equal(outAddress0.toString(), to)
assert.equal(tx.outs[0].value, toValue)
assert.equal(outAddress1.toString(), changeAddress)
assert.equal(tx.outs[1].value, value - (toValue + fee))
})
})
describe('transaction outputs', function(){

@@ -491,3 +588,5 @@ it('includes the specified address and amount', function(){

var out = tx.outs[0]
assert.equal(out.address, to)
var outAddress = Address.fromOutputScript(out.script)
assert.equal(outAddress.toString(), to)
assert.equal(out.value, value)

@@ -498,3 +597,3 @@ })

it('uses the last change address if there is any', function(){
var fee = 5000
var fee = 0
wallet.generateChangeAddress()

@@ -506,8 +605,10 @@ wallet.generateChangeAddress()

var out = tx.outs[1]
assert.equal(out.address, wallet.changeAddresses[1])
assert.equal(out.value, 15000)
var outAddress = Address.fromOutputScript(out.script)
assert.equal(outAddress.toString(), wallet.changeAddresses[1])
assert.equal(out.value, 10000)
})
it('generates a change address if there is not any', function(){
var fee = 5000
var fee = 0
assert.equal(wallet.changeAddresses.length, 0)

@@ -519,4 +620,6 @@

var out = tx.outs[1]
assert.equal(out.address, wallet.changeAddresses[0])
assert.equal(out.value, 15000)
var outAddress = Address.fromOutputScript(out.script)
assert.equal(outAddress.toString(), wallet.changeAddresses[0])
assert.equal(out.value, 10000)
})

@@ -550,7 +653,7 @@

it('throws an error', function(){
var value = 5430
var value = 546
assert.throws(function() {
wallet.createTx(to, value)
}, /Value must be above dust threshold/)
}, /546 must be above dust threshold \(546 Satoshis\)/)
})

@@ -565,62 +668,7 @@ })

wallet.createTx(to, value)
}, /Not enough money to send funds including transaction fee. Have: 1420000, needed: 1420001/)
}, /Not enough funds \(incl. fee\): 1410000 < 1410001/)
})
})
function fakeTxHash(i) {
return "txtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtx" + i
}
})
describe('createTxAsync', function(){
var to, value, fee
beforeEach(function(){
to = '15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3'
value = 500000
fee = 10000
})
afterEach(function(){
wallet.createTx.restore()
})
it('calls createTx', function(done){
sinon.stub(wallet, "createTx").returns("fakeTx")
var callback = function(err, tx){
assert(wallet.createTx.calledWith(to, value))
assert.equal(err, null)
assert.equal(tx, "fakeTx")
done()
}
wallet.createTxAsync(to, value, callback)
})
it('calls createTx correctly when fee is specified', function(done){
sinon.stub(wallet, "createTx").returns("fakeTx")
var callback = function(err, tx){
assert(wallet.createTx.calledWith(to, value, fee))
assert.equal(err, null)
assert.equal(tx, "fakeTx")
done()
}
wallet.createTxAsync(to, value, fee, callback)
})
it('when createTx throws an error, it invokes callback with error', function(done){
sinon.stub(wallet, "createTx").throws()
var callback = function(err, tx){
assert(err instanceof Error)
done()
}
wallet.createTxAsync(to, value, callback)
})
})
function assertEqual(obj1, obj2){

@@ -627,0 +675,0 @@ assert.equal(obj1.toString(), obj2.toString())

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc