Socket
Socket
Sign inDemoInstall

bitcoinjs-lib

Package Overview
Dependencies
Maintainers
4
Versions
88
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

bitcoinjs-lib - npm Package Compare versions

Comparing version 1.5.8 to 2.0.0

CHANGELOG.md

43

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

@@ -35,9 +35,9 @@ "main": "./src/index.js",

"scripts": {
"compile": "browserify ./src/index.js -s bitcoin > bitcoin.js",
"coverage": "istanbul cover _mocha -- test/*.js",
"coveralls": "npm run-script coverage && coveralls < coverage/lcov.info",
"integration": "mocha --reporter list test/integration/*.js",
"coverage": "mocha --require blanket -R travis-cov",
"coverage-local": "mocha --require blanket -R html-cov",
"integration": "mocha test/integration/",
"prepublish": "npm run test",
"standard": "standard",
"test": "npm run-script unit",
"unit": "istanbul test mocha -- --reporter list test/*.js"
"test": "npm run standard && npm run unit",
"unit": "mocha"
},

@@ -48,4 +48,19 @@ "repository": {

},
"config": {
"blanket": {
"pattern": [
""
],
"data-cover-never": [
"node_modules",
"test"
]
},
"travis-cov": {
"threshold": 99
}
},
"dependencies": {
"bigi": "^1.4.0",
"bip66": "^1.1.0",
"bs58check": "^1.0.5",

@@ -56,16 +71,18 @@ "create-hash": "^1.1.0",

"randombytes": "^2.0.1",
"typeforce": "^1.0.0"
"typeforce": "^1.3.0",
"wif": "^1.1.0"
},
"devDependencies": {
"async": "^0.9.0",
"browserify": "^9.0.3",
"blanket": "^1.1.0",
"browserify": "^10.0.0",
"bs58": "^2.0.1",
"cb-helloblock": "^0.4.13",
"coveralls": "^2.11.2",
"istanbul": "^0.3.5",
"cb-http-client": "^0.2.0",
"httpify": "^1.0.0",
"mocha": "^2.2.0",
"proxyquire": "^1.4.0",
"sinon": "^1.12.2",
"standard": "^2.11.0"
"standard": "^5.0.0",
"travis-cov": "^0.2.0"
}
}
# 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](http://img.shields.io/npm/v/bitcoinjs-lib.svg)](https://www.npmjs.org/package/bitcoinjs-lib)
[![tip for next commit](http://tip4commit.com/projects/735.svg)](http://tip4commit.com/projects/735)
[![NPM](https://nodei.co/npm/bitcoinjs-lib.png)](https://nodei.co/npm/bitcoinjs-lib/)
[![js-standard-style](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard)
The pure JavaScript Bitcoin library for node.js and browsers.

@@ -80,3 +81,6 @@ 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.

**NOTE**: When uglifying the javascript, you must exclude the following variable names from being mangled: `Array`, `BigInteger`, `Boolean`, `Buffer`, `ECPair`, `Function`, `Number`, `Point` and `Script`.
This is because of the function-name-duck-typing used in [typeforce](https://github.com/dcousens/typeforce).
## Examples

@@ -88,4 +92,5 @@

- [Generate a address from a SHA256 hash](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/basic.js#L20)
- [Import an address via WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/basic.js#L29)
- [Create a Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/basic.js#L36)
- [Generate a address and WIF for Litecoin](https://github.com/bitcoin/bitcoinjs-lib/blob/master/test/integration/basic.js#L29)
- [Import an address via WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/basic.js#L43)
- [Create a Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/basic.js#L50)
- [Sign a Bitcoin message](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/advanced.js#L9)

@@ -97,5 +102,5 @@ - [Verify a Bitcoin message](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/advanced.js#L17)

- [Generate a single-key stealth address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L7)
- [Generate a dual-key stealth address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L42)
- [Recover a BIP32 parent private key from the parent public key and a derived non-hardened child private key](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L44)
- [Recover a Private key from duplicate R values in a signature](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L90)
- [Generate a dual-key stealth address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L52)
- [Recover a BIP32 parent private key from the parent public key and a derived non-hardened child private key](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L54)
- [Recover a Private key from duplicate R values in a signature](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L101)

@@ -146,4 +151,11 @@

- [BIP39](https://github.com/weilu/bip39) - Mnemonic code for generating deterministic keys
- [BIP38](https://github.com/cryptocoinjs/bip38) - Passphrase-protected private keys
- [BIP21](https://github.com/bitcoinjs/bip21) - A BIP21 compatible URL encoding utility library
- [BIP38](https://github.com/bitcoinjs/bip38) - Mnemonic generation for deterministic keys
- [BIP39](https://github.com/bitcoinjs/bip39) - Passphrase-protected private keys
- [BIP32-Utils](https://github.com/bitcoinjs/bip32-utils) - A set of utilities for working with BIP32
- [BIP32-Wallet](https://github.com/bitcoinjs/bip32-wallet) - A BIP32 Wallet backed by bitcoinjs-lib, lite on features but heavily tested
- [BIP66](https://github.com/bitcoinjs/bip66) - Strict DER signature decoding
- [BIP69](https://github.com/bitcoinjs/bip69) - Mnemonic generation for deterministic keys
- [Base58](https://github.com/cryptocoinjs/bs58) - Base58 encoding/decoding
- [Base58 Check](https://github.com/bitcoinjs/bs58check) - Base58 check encoding/decoding
- [BCoin](https://github.com/indutny/bcoin) - BIP37 / Bloom Filters / SPV client

@@ -166,3 +178,3 @@ - [insight](https://github.com/bitpay/insight) - A bitcoin blockchain API for web wallets.

BitcoinJS (c) 2011-2014 Bitcoinjs-lib contributors
BitcoinJS (c) 2011-2015 Bitcoinjs-lib contributors
Released under MIT license

@@ -1,62 +0,53 @@

var assert = require('assert')
var base58check = require('bs58check')
var typeForce = require('typeforce')
var bs58check = require('bs58check')
var bscript = require('./script')
var networks = require('./networks')
var scripts = require('./scripts')
var typeforce = require('typeforce')
var types = require('./types')
function findScriptTypeByVersion (version) {
for (var networkName in networks) {
var network = networks[networkName]
function fromBase58Check (address) {
var payload = bs58check.decode(address)
if (payload.length < 21) throw new TypeError(address + ' is too short')
if (payload.length > 21) throw new TypeError(address + ' is too long')
if (version === network.pubKeyHash) return 'pubkeyhash'
if (version === network.scriptHash) return 'scripthash'
}
}
function Address (hash, version) {
typeForce('Buffer', hash)
assert.strictEqual(hash.length, 20, 'Invalid hash length')
assert.strictEqual(version & 0xff, version, 'Invalid version byte')
this.hash = hash
this.version = version
}
Address.fromBase58Check = function (string) {
var payload = base58check.decode(string)
var version = payload.readUInt8(0)
var version = payload[0]
var hash = payload.slice(1)
return new Address(hash, version)
return { hash: hash, version: version }
}
Address.fromOutputScript = function (script, network) {
function fromOutputScript (scriptPubKey, network) {
network = network || networks.bitcoin
if (scripts.isPubKeyHashOutput(script)) return new Address(script.chunks[2], network.pubKeyHash)
if (scripts.isScriptHashOutput(script)) return new Address(script.chunks[1], network.scriptHash)
var chunks = bscript.decompile(scriptPubKey)
if (bscript.isPubKeyHashOutput(chunks)) return toBase58Check(chunks[2], network.pubKeyHash)
if (bscript.isScriptHashOutput(chunks)) return toBase58Check(chunks[1], network.scriptHash)
assert(false, script.toASM() + ' has no matching Address')
throw new Error(bscript.toASM(chunks) + ' has no matching Address')
}
Address.prototype.toBase58Check = function () {
function toBase58Check (hash, version) {
typeforce(types.tuple(types.Hash160bit, types.UInt8), arguments)
var payload = new Buffer(21)
payload.writeUInt8(this.version, 0)
this.hash.copy(payload, 1)
payload.writeUInt8(version, 0)
hash.copy(payload, 1)
return base58check.encode(payload)
return bs58check.encode(payload)
}
Address.prototype.toOutputScript = function () {
var scriptType = findScriptTypeByVersion(this.version)
function toOutputScript (address, network) {
network = network || networks.bitcoin
if (scriptType === 'pubkeyhash') return scripts.pubKeyHashOutput(this.hash)
if (scriptType === 'scripthash') return scripts.scriptHashOutput(this.hash)
var decode = fromBase58Check(address)
if (decode.version === network.pubKeyHash) return bscript.pubKeyHashOutput(decode.hash)
if (decode.version === network.scriptHash) return bscript.scriptHashOutput(decode.hash)
assert(false, this.toString() + ' has no matching Script')
throw new Error(address + ' has no matching Script')
}
Address.prototype.toString = Address.prototype.toBase58Check
module.exports = Address
module.exports = {
fromBase58Check: fromBase58Check,
fromOutputScript: fromOutputScript,
toBase58Check: toBase58Check,
toOutputScript: toOutputScript
}

@@ -1,4 +0,3 @@

var assert = require('assert')
var bufferutils = require('./bufferutils')
var crypto = require('./crypto')
var bcrypto = require('./crypto')

@@ -17,3 +16,3 @@ var Transaction = require('./transaction')

Block.fromBuffer = function (buffer) {
assert(buffer.length >= 80, 'Buffer too small (< 80 bytes)')
if (buffer.length < 80) throw new Error('Buffer too small (< 80 bytes)')

@@ -48,7 +47,6 @@ var offset = 0

// FIXME: poor performance
function readTransaction () {
var tx = Transaction.fromBuffer(buffer.slice(offset), true)
offset += tx.toBuffer().length
offset += tx.byteLength()
return tx

@@ -73,3 +71,3 @@ }

Block.prototype.getHash = function () {
return crypto.hash256(this.toBuffer(true))
return bcrypto.hash256(this.toBuffer(true))
}

@@ -76,0 +74,0 @@

@@ -1,2 +0,1 @@

var assert = require('assert')
var opcodes = require('./opcodes')

@@ -6,6 +5,6 @@

function verifuint (value, max) {
assert(typeof value === 'number', 'cannot write a non-number as a number')
assert(value >= 0, 'specified a negative value for writing an unsigned value')
assert(value <= max, 'value is larger than maximum value for type')
assert(Math.floor(value) === value, 'value has a fractional component')
if (typeof value !== 'number') throw new Error('cannot write a non-number as a number')
if (value < 0) throw new Error('specified a negative value for writing an unsigned value')
if (value > max) throw new Error('value is larger than maximum value for type')
if (Math.floor(value) !== value) throw new Error('value has a fractional component')
}

@@ -44,3 +43,3 @@

if (offset + 5 > buffer.length) return null
assert.equal(opcode, opcodes.OP_PUSHDATA4, 'Unexpected opcode')
if (opcode !== opcodes.OP_PUSHDATA4) throw new Error('Unexpected opcode')

@@ -173,2 +172,12 @@ number = buffer.readUInt32LE(offset + 1)

function equal (a, b) {
if (a.length !== b.length) return false
for (var i = 0; i < a.length; ++i) {
if (a[i] !== b[i]) return false
}
return true
}
function reverse (buffer) {

@@ -181,2 +190,3 @@ var buffer2 = new Buffer(buffer)

module.exports = {
equal: equal,
pushDataSize: pushDataSize,

@@ -183,0 +193,0 @@ readPushDataInt: readPushDataInt,

@@ -23,23 +23,8 @@ var createHash = require('create-hash')

// FIXME: Name not consistent with others
var createHmac = require('create-hmac')
function HmacSHA256 (buffer, secret) {
console.warn('Hmac* functions are deprecated for removal in 2.0.0, use node crypto instead')
return createHmac('sha256', secret).update(buffer).digest()
}
function HmacSHA512 (buffer, secret) {
console.warn('Hmac* functions are deprecated for removal in 2.0.0, use node crypto instead')
return createHmac('sha512', secret).update(buffer).digest()
}
module.exports = {
hash160: hash160,
hash256: hash256,
ripemd160: ripemd160,
sha1: sha1,
sha256: sha256,
hash160: hash160,
hash256: hash256,
HmacSHA256: HmacSHA256,
HmacSHA512: HmacSHA512
sha256: sha256
}

@@ -1,4 +0,4 @@

var assert = require('assert')
var createHmac = require('create-hmac')
var typeForce = require('typeforce')
var typeforce = require('typeforce')
var types = require('./types')

@@ -11,39 +11,13 @@ var BigInteger = require('bigi')

var ecurve = require('ecurve')
var secp256k1 = ecurve.getCurveByName('secp256k1')
// https://tools.ietf.org/html/rfc6979#section-3.2
function deterministicGenerateK (curve, hash, d, checkSig) {
typeForce('Buffer', hash)
typeForce('BigInteger', d)
function deterministicGenerateK (hash, x, checkSig) {
typeforce(types.tuple(
types.Hash256bit,
types.Buffer256bit,
types.Function
), arguments)
// FIXME: remove/uncomment for 2.0.0
// typeForce('Function', checkSig)
if (typeof checkSig !== 'function') {
console.warn('deterministicGenerateK requires a checkSig callback in 2.0.0, see #337 for more information')
checkSig = function (k) {
var G = curve.G
var n = curve.n
var e = BigInteger.fromBuffer(hash)
var Q = G.multiply(k)
if (curve.isInfinity(Q))
return false
var r = Q.affineX.mod(n)
if (r.signum() === 0)
return false
var s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n)
if (s.signum() === 0)
return false
return true
}
}
// sanity check
assert.equal(hash.length, 32, 'Hash must be 256 bit')
var x = d.toBuffer(32)
var k = new Buffer(32)

@@ -88,3 +62,3 @@ var v = new Buffer(32)

// Step H3, repeat until T is within the interval [1, n - 1] and is suitable for ECDSA
while ((T.signum() <= 0) || (T.compareTo(curve.n) >= 0) || !checkSig(T)) {
while (T.signum() <= 0 || T.compareTo(secp256k1.n) >= 0 || !checkSig(T)) {
k = createHmac('sha256', k)

@@ -106,22 +80,23 @@ .update(v)

function sign (curve, hash, d) {
var r, s
var N_OVER_TWO = secp256k1.n.shiftRight(1)
function sign (hash, d) {
typeforce(types.tuple(types.Hash256bit, types.BigInt), arguments)
var x = d.toBuffer(32)
var e = BigInteger.fromBuffer(hash)
var n = curve.n
var G = curve.G
var n = secp256k1.n
var G = secp256k1.G
deterministicGenerateK(curve, hash, d, function (k) {
var r, s
deterministicGenerateK(hash, x, function (k) {
var Q = G.multiply(k)
if (curve.isInfinity(Q))
return false
if (secp256k1.isInfinity(Q)) return false
r = Q.affineX.mod(n)
if (r.signum() === 0)
return false
if (r.signum() === 0) return false
s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n)
if (s.signum() === 0)
return false
if (s.signum() === 0) return false

@@ -131,4 +106,2 @@ return true

var N_OVER_TWO = n.shiftRight(1)
// enforce low S values, see bip62: 'low s values in signatures'

@@ -142,6 +115,12 @@ if (s.compareTo(N_OVER_TWO) > 0) {

function verifyRaw (curve, e, signature, Q) {
var n = curve.n
var G = curve.G
function verify (hash, signature, Q) {
typeforce(types.tuple(
types.Hash256bit,
types.ECSignature,
types.ECPoint
), arguments)
var n = secp256k1.n
var G = secp256k1.G
var r = signature.r

@@ -154,17 +133,27 @@ var s = signature.s

// c = s^-1 mod n
var c = s.modInverse(n)
// 1.4.2 H = Hash(M), already done by the user
// 1.4.3 e = H
var e = BigInteger.fromBuffer(hash)
// Compute s^-1
var sInv = s.modInverse(n)
// 1.4.4 Compute u1 = es^−1 mod n
// u2 = rs^−1 mod n
var u1 = e.multiply(c).mod(n)
var u2 = r.multiply(c).mod(n)
var u1 = e.multiply(sInv).mod(n)
var u2 = r.multiply(sInv).mod(n)
// 1.4.5 Compute R = (xR, yR) = u1G + u2Q
// 1.4.5 Compute R = (xR, yR)
// R = u1G + u2Q
var R = G.multiplyTwo(u1, Q, u2)
var v = R.affineX.mod(n)
// 1.4.5 (cont.) Enforce R is not at infinity
if (curve.isInfinity(R)) return false
if (secp256k1.isInfinity(R)) return false
// 1.4.6 Convert the field element R.x to an integer
var xR = R.affineX
// 1.4.7 Set v = xR mod n
var v = xR.mod(n)
// 1.4.8 If v = r, output "valid", and if v != r, output "invalid"

@@ -174,10 +163,2 @@ return v.equals(r)

function verify (curve, hash, signature, Q) {
// 1.4.2 H = Hash(M), already done by the user
// 1.4.3 e = H
var e = BigInteger.fromBuffer(hash)
return verifyRaw(curve, e, signature, Q)
}
/**

@@ -191,13 +172,16 @@ * Recover a public key from a signature.

*/
function recoverPubKey (curve, e, signature, i) {
assert.strictEqual(i & 3, i, 'Recovery param is more than two bits')
function recoverPubKey (e, signature, i) {
typeforce(types.tuple(
types.BigInt,
types.ECSignature,
types.UInt2
), arguments)
var n = curve.n
var G = curve.G
var n = secp256k1.n
var G = secp256k1.G
var r = signature.r
var s = signature.s
assert(r.signum() > 0 && r.compareTo(n) < 0, 'Invalid r value')
assert(s.signum() > 0 && s.compareTo(n) < 0, 'Invalid s value')
if (r.signum() <= 0 || r.compareTo(n) >= 0) throw new Error('Invalid r value')
if (s.signum() <= 0 || s.compareTo(n) >= 0) throw new Error('Invalid s value')

@@ -213,8 +197,11 @@ // A set LSB signifies that the y-coordinate is odd

var x = isSecondKey ? r.add(n) : r
var R = curve.pointFromX(isYOdd, x)
var R = secp256k1.pointFromX(isYOdd, x)
// 1.4 Check that nR is at infinity
var nR = R.multiply(n)
assert(curve.isInfinity(nR), 'nR is not a valid curve point')
if (!secp256k1.isInfinity(nR)) throw new Error('nR is not a valid curve point')
// Compute r^-1
var rInv = r.modInverse(n)
// Compute -e from e

@@ -225,7 +212,6 @@ var eNeg = e.negate().mod(n)

// Q = r^-1 (sR + -eG)
var rInv = r.modInverse(n)
var Q = R.multiplyTwo(s, G, eNeg).multiply(rInv)
curve.validate(Q)
secp256k1.validate(Q)
return Q

@@ -245,5 +231,11 @@ }

*/
function calcPubKeyRecoveryParam (curve, e, signature, Q) {
function calcPubKeyRecoveryParam (e, signature, Q) {
typeforce(types.tuple(
types.BigInt,
types.ECSignature,
types.ECPoint
), arguments)
for (var i = 0; i < 4; i++) {
var Qprime = recoverPubKey(curve, e, signature, i)
var Qprime = recoverPubKey(e, signature, i)

@@ -265,3 +257,5 @@ // 1.6.2 Verify Q

verify: verify,
verifyRaw: verifyRaw
// TODO: remove
__curve: secp256k1
}

@@ -1,3 +0,4 @@

var assert = require('assert')
var typeForce = require('typeforce')
var bip66 = require('bip66')
var typeforce = require('typeforce')
var types = require('./types')

@@ -7,4 +8,3 @@ var BigInteger = require('bigi')

function ECSignature (r, s) {
typeForce('BigInteger', r)
typeForce('BigInteger', s)
typeforce(types.tuple(types.BigInt, types.BigInt), arguments)

@@ -16,11 +16,9 @@ this.r = r

ECSignature.parseCompact = function (buffer) {
assert.equal(buffer.length, 65, 'Invalid signature length')
var i = buffer.readUInt8(0) - 27
if (buffer.length !== 65) throw new Error('Invalid signature length')
// At most 3 bits
assert.equal(i, i & 7, 'Invalid signature parameter')
var compressed = !!(i & 4)
var flagByte = buffer.readUInt8(0) - 27
if (flagByte !== (flagByte & 7)) throw new Error('Invalid signature parameter')
// Recovery param only
i = i & 3
var compressed = !!(flagByte & 4)
var recoveryParam = flagByte & 3

@@ -32,3 +30,3 @@ var r = BigInteger.fromBuffer(buffer.slice(1, 33))

compressed: compressed,
i: i,
i: recoveryParam,
signature: new ECSignature(r, s)

@@ -39,34 +37,6 @@ }

ECSignature.fromDER = function (buffer) {
assert.equal(buffer.readUInt8(0), 0x30, 'Not a DER sequence')
assert.equal(buffer.readUInt8(1), buffer.length - 2, 'Invalid sequence length')
assert.equal(buffer.readUInt8(2), 0x02, 'Expected a DER integer')
var decode = bip66.decode(buffer)
var r = BigInteger.fromDERInteger(decode.r)
var s = BigInteger.fromDERInteger(decode.s)
var rLen = buffer.readUInt8(3)
assert(rLen > 0, 'R length is zero')
var offset = 4 + rLen
assert.equal(buffer.readUInt8(offset), 0x02, 'Expected a DER integer (2)')
var sLen = buffer.readUInt8(offset + 1)
assert(sLen > 0, 'S length is zero')
var rB = buffer.slice(4, offset)
var sB = buffer.slice(offset + 2)
offset += 2 + sLen
if (rLen > 1 && rB.readUInt8(0) === 0x00) {
assert(rB.readUInt8(1) & 0x80, 'R value excessively padded')
}
if (sLen > 1 && sB.readUInt8(0) === 0x00) {
assert(sB.readUInt8(1) & 0x80, 'S value excessively padded')
}
assert.equal(offset, buffer.length, 'Invalid DER encoding')
var r = BigInteger.fromDERInteger(rB)
var s = BigInteger.fromDERInteger(sB)
assert(r.signum() >= 0, 'R value is negative')
assert(s.signum() >= 0, 'S value is negative')
return new ECSignature(r, s)

@@ -80,3 +50,3 @@ }

assert(hashTypeMod > 0x00 && hashTypeMod < 0x04, 'Invalid hashType ' + hashType)
if (hashTypeMod <= 0x00 || hashTypeMod >= 0x04) throw new Error('Invalid hashType ' + hashType)

@@ -106,19 +76,6 @@ return {

ECSignature.prototype.toDER = function () {
var rBa = this.r.toDERInteger()
var sBa = this.s.toDERInteger()
var r = new Buffer(this.r.toDERInteger())
var s = new Buffer(this.s.toDERInteger())
var sequence = []
// INTEGER
sequence.push(0x02, rBa.length)
sequence = sequence.concat(rBa)
// INTEGER
sequence.push(0x02, sBa.length)
sequence = sequence.concat(sBa)
// SEQUENCE
sequence.unshift(0x30, sequence.length)
return new Buffer(sequence)
return bip66.encode(r, s)
}

@@ -128,3 +85,3 @@

var hashTypeMod = hashType & ~0x80
assert(hashTypeMod > 0x00 && hashTypeMod < 0x04, 'Invalid hashType ' + hashType)
if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType)

@@ -131,0 +88,0 @@ var hashTypeBuffer = new Buffer(1)

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

var assert = require('assert')
var base58check = require('bs58check')
var bcrypto = require('./crypto')
var createHmac = require('create-hmac')
var typeForce = require('typeforce')
var networks = require('./networks')
var typeforce = require('typeforce')
var types = require('./types')
var NETWORKS = require('./networks')
var BigInteger = require('bigi')
var ECKey = require('./eckey')
var ECPubKey = require('./ecpubkey')
var ECPair = require('./ecpair')

@@ -15,22 +14,8 @@ var ecurve = require('ecurve')

function findBIP32NetworkByVersion (version) {
for (var name in networks) {
var network = networks[name]
function HDNode (keyPair, chainCode) {
typeforce(types.tuple('ECPair', types.Buffer256bit), arguments)
if (version === network.bip32.private || version === network.bip32.public) {
return network
}
}
if (!keyPair.compressed) throw new TypeError('BIP32 only allows compressed keyPairs')
assert(false, 'Could not find network for ' + version.toString(16))
}
function HDNode (K, chainCode, network) {
network = network || networks.bitcoin
typeForce('Buffer', chainCode)
assert.equal(chainCode.length, 32, 'Expected chainCode length of 32, got ' + chainCode.length)
assert(network.bip32, 'Unknown BIP32 constants for network')
this.keyPair = keyPair
this.chainCode = chainCode

@@ -40,28 +25,13 @@ this.depth = 0

this.parentFingerprint = 0x00000000
this.network = network
if (K instanceof BigInteger) {
this.privKey = new ECKey(K, true)
this.pubKey = this.privKey.pub
} else if (K instanceof ECKey) {
assert(K.pub.compressed, 'ECKey must be compressed')
this.privKey = K
this.pubKey = K.pub
} else if (K instanceof ECPubKey) {
assert(K.compressed, 'ECPubKey must be compressed')
this.pubKey = K
} else {
this.pubKey = new ECPubKey(K, true)
}
}
HDNode.MASTER_SECRET = new Buffer('Bitcoin seed')
HDNode.HIGHEST_BIT = 0x80000000
HDNode.LENGTH = 78
HDNode.MASTER_SECRET = new Buffer('Bitcoin seed')
HDNode.fromSeedBuffer = function (seed, network) {
typeForce('Buffer', seed)
typeforce(types.tuple(types.Buffer, types.maybe(types.Network)), arguments)
assert(seed.length >= 16, 'Seed should be at least 128 bits')
assert(seed.length <= 64, 'Seed should be at most 512 bits')
if (seed.length < 16) throw new TypeError('Seed should be at least 128 bits')
if (seed.length > 64) throw new TypeError('Seed should be at most 512 bits')

@@ -73,6 +43,9 @@ var I = createHmac('sha512', HDNode.MASTER_SECRET).update(seed).digest()

// In case IL is 0 or >= n, the master key is invalid
// This is handled by `new ECKey` in the HDNode constructor
// This is handled by the ECPair constructor
var pIL = BigInteger.fromBuffer(IL)
var keyPair = new ECPair(pIL, null, {
network: network
})
return new HDNode(pIL, IR, network)
return new HDNode(keyPair, IR)
}

@@ -84,27 +57,27 @@

HDNode.fromBase58 = function (string, network) {
return HDNode.fromBuffer(base58check.decode(string), network, true)
}
HDNode.fromBase58 = function (string, networks) {
var buffer = base58check.decode(string)
if (buffer.length !== 78) throw new Error('Invalid buffer length')
// FIXME: remove in 2.x.y
HDNode.fromBuffer = function (buffer, network, __ignoreDeprecation) {
if (!__ignoreDeprecation) {
console.warn('HDNode.fromBuffer() is deprecated for removal in 2.x.y, use fromBase58 instead')
}
assert.strictEqual(buffer.length, HDNode.LENGTH, 'Invalid buffer length')
// 4 byte: version bytes
// 4 bytes: version bytes
var version = buffer.readUInt32BE(0)
var network
if (network) {
assert(version === network.bip32.private || version === network.bip32.public, "Network doesn't match")
// list of networks?
if (Array.isArray(networks)) {
network = networks.filter(function (network) {
return version === network.bip32.private ||
version === network.bip32.public
}).pop() || {}
// auto-detect
// otherwise, assume a network object (or default to bitcoin)
} else {
network = findBIP32NetworkByVersion(version)
network = networks || NETWORKS.bitcoin
}
if (version !== network.bip32.private &&
version !== network.bip32.public) throw new Error('Invalid network')
// 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, ...
var depth = buffer.readUInt8(4)
var depth = buffer[4]

@@ -114,3 +87,3 @@ // 4 bytes: the fingerprint of the parent's key (0x00000000 if master key)

if (depth === 0) {
assert.strictEqual(parentFingerprint, 0x00000000, 'Invalid parent fingerprint')
if (parentFingerprint !== 0x00000000) throw new Error('Invalid parent fingerprint')
}

@@ -121,20 +94,22 @@

var index = buffer.readUInt32BE(9)
assert(depth > 0 || index === 0, 'Invalid index')
if (depth === 0 && index !== 0) throw new Error('Invalid index')
// 32 bytes: the chain code
var chainCode = buffer.slice(13, 45)
var data, hd
var keyPair
// 33 bytes: private key data (0x00 + k)
if (version === network.bip32.private) {
assert.strictEqual(buffer.readUInt8(45), 0x00, 'Invalid private key')
data = buffer.slice(46, 78)
var d = BigInteger.fromBuffer(data)
hd = new HDNode(d, chainCode, network)
if (buffer.readUInt8(45) !== 0x00) throw new Error('Invalid private key')
var d = BigInteger.fromBuffer(buffer.slice(46, 78))
keyPair = new ECPair(d, null, {
network: network
})
// 33 bytes: public key data (0x02 + X or 0x03 + X)
} else {
data = buffer.slice(45, 78)
var Q = ecurve.Point.decodeFrom(curve, data)
assert.equal(Q.compressed, true, 'Invalid public key')
var Q = ecurve.Point.decodeFrom(curve, buffer.slice(45, 78))
if (!Q.compressed) throw new Error('Invalid public key')

@@ -145,5 +120,8 @@ // Verify that the X coordinate in the public point corresponds to a point on the curve.

hd = new HDNode(Q, chainCode, network)
keyPair = new ECPair(null, Q, {
network: network
})
}
var hd = new HDNode(keyPair, chainCode)
hd.depth = depth

@@ -156,9 +134,8 @@ hd.index = index

// FIXME: remove in 2.x.y
HDNode.fromHex = function (hex, network) {
return HDNode.fromBuffer(new Buffer(hex, 'hex'), network)
HDNode.prototype.getAddress = function () {
return this.keyPair.getAddress()
}
HDNode.prototype.getIdentifier = function () {
return bcrypto.hash160(this.pubKey.toBuffer())
return bcrypto.hash160(this.keyPair.getPublicKeyBuffer())
}

@@ -170,8 +147,8 @@

HDNode.prototype.getAddress = function () {
return this.pubKey.getAddress(this.network)
}
HDNode.prototype.neutered = function () {
var neuteredKeyPair = new ECPair(null, this.keyPair.Q, {
network: this.keyPair.network
})
HDNode.prototype.neutered = function () {
var neutered = new HDNode(this.pubKey.Q, this.chainCode, this.network)
var neutered = new HDNode(neuteredKeyPair, this.chainCode)
neutered.depth = this.depth

@@ -184,23 +161,9 @@ neutered.index = this.index

HDNode.prototype.toBase58 = function (isPrivate) {
return base58check.encode(this.toBuffer(isPrivate, true))
}
HDNode.prototype.toBase58 = function (__isPrivate) {
if (__isPrivate !== undefined) throw new TypeError('Unsupported argument in 2.0.0')
// FIXME: remove in 2.x.y
HDNode.prototype.toBuffer = function (isPrivate, __ignoreDeprecation) {
if (isPrivate === undefined) {
isPrivate = !!this.privKey
// FIXME: remove in 2.x.y
} else {
console.warn('isPrivate flag is deprecated, please use the .neutered() method instead')
}
if (!__ignoreDeprecation) {
console.warn('HDNode.toBuffer() is deprecated for removal in 2.x.y, use toBase58 instead')
}
// Version
var version = isPrivate ? this.network.bip32.private : this.network.bip32.public
var buffer = new Buffer(HDNode.LENGTH)
var network = this.keyPair.network
var version = this.keyPair.d ? network.bip32.private : network.bip32.public
var buffer = new Buffer(78)

@@ -210,3 +173,2 @@ // 4 bytes: version bytes

// Depth
// 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, ....

@@ -219,3 +181,3 @@ buffer.writeUInt8(this.depth, 4)

// 4 bytes: child number. This is the number i in xi = xpar/i, with xi the key being serialized.
// This is encoded in Big endian. (0x00000000 if master key)
// This is encoded in big endian. (0x00000000 if master key)
buffer.writeUInt32BE(this.index, 9)

@@ -227,39 +189,29 @@

// 33 bytes: the public key or private key data
if (isPrivate) {
// FIXME: remove in 2.x.y
assert(this.privKey, 'Missing private key')
if (this.keyPair.d) {
// 0x00 + k for private keys
buffer.writeUInt8(0, 45)
this.privKey.d.toBuffer(32).copy(buffer, 46)
this.keyPair.d.toBuffer(32).copy(buffer, 46)
// 33 bytes: the public key
} else {
// X9.62 encoding for public keys
this.pubKey.toBuffer().copy(buffer, 45)
this.keyPair.getPublicKeyBuffer().copy(buffer, 45)
}
return buffer
return base58check.encode(buffer)
}
// FIXME: remove in 2.x.y
HDNode.prototype.toHex = function (isPrivate) {
return this.toBuffer(isPrivate).toString('hex')
}
// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#child-key-derivation-ckd-functions
HDNode.prototype.derive = function (index) {
var isHardened = index >= HDNode.HIGHEST_BIT
var indexBuffer = new Buffer(4)
indexBuffer.writeUInt32BE(index, 0)
var data = new Buffer(37)
var data
// Hardened child
if (isHardened) {
assert(this.privKey, 'Could not derive hardened child key')
if (!this.keyPair.d) throw new TypeError('Could not derive hardened child key')
// data = 0x00 || ser256(kpar) || ser32(index)
data = Buffer.concat([
this.privKey.d.toBuffer(33),
indexBuffer
])
data[0] = 0x00
this.keyPair.d.toBuffer(32).copy(data, 1)
data.writeUInt32BE(index, 33)

@@ -270,6 +222,4 @@ // Normal child

// = serP(Kpar) || ser32(index)
data = Buffer.concat([
this.pubKey.toBuffer(),
indexBuffer
])
this.keyPair.getPublicKeyBuffer().copy(data, 0)
data.writeUInt32BE(index, 33)
}

@@ -289,6 +239,6 @@

// Private parent key -> private child key
var hd
if (this.privKey) {
var derivedKeyPair
if (this.keyPair.d) {
// ki = parse256(IL) + kpar (mod n)
var ki = pIL.add(this.privKey.d).mod(curve.n)
var ki = pIL.add(this.keyPair.d).mod(curve.n)

@@ -300,3 +250,5 @@ // In case ki == 0, proceed with the next value for i

hd = new HDNode(ki, IR, this.network)
derivedKeyPair = new ECPair(ki, null, {
network: this.keyPair.network
})

@@ -307,3 +259,3 @@ // Public parent key -> public child key

// = G*IL + Kpar
var Ki = curve.G.multiply(pIL).add(this.pubKey.Q)
var Ki = curve.G.multiply(pIL).add(this.keyPair.Q)

@@ -315,5 +267,8 @@ // In case Ki is the point at infinity, proceed with the next value for i

hd = new HDNode(Ki, IR, this.network)
derivedKeyPair = new ECPair(null, Ki, {
network: this.keyPair.network
})
}
var hd = new HDNode(derivedKeyPair, IR)
hd.depth = this.depth + 1

@@ -320,0 +275,0 @@ hd.index = index

module.exports = {
Address: require('./address'),
base58check: require('./base58check'),
Block: require('./block'),
bufferutils: require('./bufferutils'),
crypto: require('./crypto'),
ecdsa: require('./ecdsa'),
ECKey: require('./eckey'),
ECPubKey: require('./ecpubkey'),
ECPair: require('./ecpair'),
ECSignature: require('./ecsignature'),
Message: require('./message'),
opcodes: require('./opcodes'),
HDNode: require('./hdnode'),
Script: require('./script'),
scripts: require('./scripts'),
Transaction: require('./transaction'),
TransactionBuilder: require('./transaction_builder'),
address: require('./address'),
bufferutils: require('./bufferutils'),
crypto: require('./crypto'),
message: require('./message'),
networks: require('./networks'),
Wallet: require('./wallet')
opcodes: require('./opcodes'),
script: require('./script')
}
var bufferutils = require('./bufferutils')
var crypto = require('./crypto')
var bcrypto = require('./crypto')
var ecdsa = require('./ecdsa')

@@ -7,29 +7,25 @@ var networks = require('./networks')

var BigInteger = require('bigi')
var ECPubKey = require('./ecpubkey')
var ECPair = require('./ecpair')
var ECSignature = require('./ecsignature')
var ecurve = require('ecurve')
var ecparams = ecurve.getCurveByName('secp256k1')
function magicHash (message, network) {
var magicPrefix = new Buffer(network.magicPrefix)
var messagePrefix = new Buffer(network.messagePrefix)
var messageBuffer = new Buffer(message)
var lengthBuffer = bufferutils.varIntBuffer(messageBuffer.length)
var buffer = Buffer.concat([magicPrefix, lengthBuffer, messageBuffer])
return crypto.hash256(buffer)
var buffer = Buffer.concat([messagePrefix, lengthBuffer, messageBuffer])
return bcrypto.hash256(buffer)
}
function sign (privKey, message, network) {
function sign (keyPair, message, network) {
network = network || networks.bitcoin
var hash = magicHash(message, network)
var signature = privKey.sign(hash)
var signature = keyPair.sign(hash)
var e = BigInteger.fromBuffer(hash)
var i = ecdsa.calcPubKeyRecoveryParam(ecparams, e, signature, privKey.pub.Q)
var i = ecdsa.calcPubKeyRecoveryParam(e, signature, keyPair.Q)
return signature.toCompact(i, privKey.pub.compressed)
return signature.toCompact(i, keyPair.compressed)
}
// TODO: network could be implied from address
function verify (address, signature, message, network) {

@@ -45,6 +41,10 @@ if (!Buffer.isBuffer(signature)) {

var e = BigInteger.fromBuffer(hash)
var Q = ecdsa.recoverPubKey(ecparams, e, parsed.signature, parsed.i)
var Q = ecdsa.recoverPubKey(e, parsed.signature, parsed.i)
var pubKey = new ECPubKey(Q, parsed.compressed)
return pubKey.getAddress(network).toString() === address.toString()
var keyPair = new ECPair(null, Q, {
compressed: parsed.compressed,
network: network
})
return keyPair.getAddress() === address
}

@@ -51,0 +51,0 @@

// https://en.bitcoin.it/wiki/List_of_address_prefixes
// Dogecoin BIP32 is a proposed standard: https://bitcointalk.org/index.php?topic=409731
var networks = {
module.exports = {
bitcoin: {
magicPrefix: '\x18Bitcoin Signed Message:\n',
messagePrefix: '\x18Bitcoin Signed Message:\n',
bip32: {

@@ -14,8 +14,6 @@ public: 0x0488b21e,

wif: 0x80,
dustThreshold: 546, // https://github.com/bitcoin/bitcoin/blob/v0.9.2/src/core.h#L151-L162
feePerKb: 10000, // https://github.com/bitcoin/bitcoin/blob/v0.9.2/src/main.cpp#L53
estimateFee: estimateFee('bitcoin')
dustThreshold: 546 // https://github.com/bitcoin/bitcoin/blob/v0.9.2/src/core.h#L151-L162
},
testnet: {
magicPrefix: '\x18Bitcoin Signed Message:\n',
messagePrefix: '\x18Bitcoin Signed Message:\n',
bip32: {

@@ -28,8 +26,6 @@ public: 0x043587cf,

wif: 0xef,
dustThreshold: 546,
feePerKb: 10000,
estimateFee: estimateFee('testnet')
dustThreshold: 546
},
litecoin: {
magicPrefix: '\x19Litecoin Signed Message:\n',
messagePrefix: '\x19Litecoin Signed Message:\n',
bip32: {

@@ -42,9 +38,6 @@ public: 0x019da462,

wif: 0xb0,
dustThreshold: 0, // https://github.com/litecoin-project/litecoin/blob/v0.8.7.2/src/main.cpp#L360-L365
dustSoftThreshold: 100000, // https://github.com/litecoin-project/litecoin/blob/v0.8.7.2/src/main.h#L53
feePerKb: 100000, // https://github.com/litecoin-project/litecoin/blob/v0.8.7.2/src/main.cpp#L56
estimateFee: estimateFee('litecoin')
dustThreshold: 0 // https://github.com/litecoin-project/litecoin/blob/v0.8.7.2/src/main.cpp#L360-L365
},
dogecoin: {
magicPrefix: '\x19Dogecoin Signed Message:\n',
messagePrefix: '\x19Dogecoin Signed Message:\n',
bip32: {

@@ -57,97 +50,4 @@ public: 0x02facafd,

wif: 0x9e,
dustThreshold: 0, // https://github.com/dogecoin/dogecoin/blob/v1.7.1/src/core.h#L155-L160
dustSoftThreshold: 100000000, // https://github.com/dogecoin/dogecoin/blob/v1.7.1/src/main.h#L62
feePerKb: 100000000, // https://github.com/dogecoin/dogecoin/blob/v1.7.1/src/main.cpp#L58
estimateFee: estimateFee('dogecoin')
},
viacoin: {
magicPrefix: '\x18Viacoin Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4
},
pubKeyHash: 0x47,
scriptHash: 0x21,
wif: 0xc7,
dustThreshold: 560,
dustSoftThreshold: 100000,
feePerKb: 100000, //
estimateFee: estimateFee('viacoin')
},
viacointestnet: {
magicPrefix: '\x18Viacoin Signed Message:\n',
bip32: {
public: 0x043587cf,
private: 0x04358394
},
pubKeyHash: 0x7f,
scriptHash: 0xc4,
wif: 0xff,
dustThreshold: 560,
dustSoftThreshold: 100000,
feePerKb: 100000,
estimateFee: estimateFee('viacointestnet')
},
gamerscoin: {
magicPrefix: '\x19Gamerscoin Signed Message:\n',
bip32: {
public: 0x019da462,
private: 0x019d9cfe
},
pubKeyHash: 0x26,
scriptHash: 0x05,
wif: 0xA6,
dustThreshold: 0, // https://github.com/gamers-coin/gamers-coinv3/blob/master/src/main.cpp#L358-L363
dustSoftThreshold: 100000, // https://github.com/gamers-coin/gamers-coinv3/blob/master/src/main.cpp#L51
feePerKb: 100000, // https://github.com/gamers-coin/gamers-coinv3/blob/master/src/main.cpp#L54
estimateFee: estimateFee('gamerscoin')
},
jumbucks: {
magicPrefix: '\x19Jumbucks Signed Message:\n',
bip32: {
public: 0x037a689a,
private: 0x037a6460
},
pubKeyHash: 0x2b,
scriptHash: 0x05,
wif: 0xab,
dustThreshold: 0,
dustSoftThreshold: 10000,
feePerKb: 10000,
estimateFee: estimateFee('jumbucks')
},
zetacoin: {
magicPrefix: '\x18Zetacoin Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4
},
pubKeyHash: 0x50,
scriptHash: 0x09,
wif: 0xe0,
dustThreshold: 546, // https://github.com/zetacoin/zetacoin/blob/master/src/core.h#L159
feePerKb: 10000, // https://github.com/zetacoin/zetacoin/blob/master/src/main.cpp#L54
estimateFee: estimateFee('zetacoin')
dustThreshold: 0 // https://github.com/dogecoin/dogecoin/blob/v1.7.1/src/core.h#L155-L160
}
}
function estimateFee (type) {
return function (tx) {
var network = networks[type]
var baseFee = network.feePerKb
var byteSize = tx.toBuffer().length
var fee = baseFee * Math.ceil(byteSize / 1000)
if (network.dustSoftThreshold === undefined) return fee
tx.outs.forEach(function (e) {
if (e.value < network.dustSoftThreshold) {
fee += baseFee
}
})
return fee
}
}
module.exports = networks

@@ -1,32 +0,83 @@

var assert = require('assert')
var bip66 = require('bip66')
var bufferutils = require('./bufferutils')
var crypto = require('./crypto')
var typeForce = require('typeforce')
var opcodes = require('./opcodes')
var typeforce = require('typeforce')
var types = require('./types')
function Script (buffer, chunks) {
typeForce('Buffer', buffer)
typeForce('Array', chunks)
var OPS = require('./opcodes')
var REVERSE_OPS = []
for (var op in OPS) {
var code = OPS[op]
REVERSE_OPS[code] = op
}
this.buffer = buffer
this.chunks = chunks
function toASM (chunks) {
if (types.Buffer(chunks)) {
chunks = decompile(chunks)
}
return chunks.map(function (chunk) {
// data?
if (Buffer.isBuffer(chunk)) return chunk.toString('hex')
// opcode!
return REVERSE_OPS[chunk]
}).join(' ')
}
Script.fromASM = function (asm) {
var strChunks = asm.split(' ')
var chunks = strChunks.map(function (strChunk) {
function fromASM (asm) {
typeforce(types.String, asm)
return compile(asm.split(' ').map(function (chunkStr) {
// opcode?
if (OPS[chunkStr] !== undefined) return OPS[chunkStr]
// data!
return new Buffer(chunkStr, 'hex')
}))
}
function compile (chunks) {
// TODO: remove me
if (types.Buffer(chunks)) return chunks
typeforce(types.Array, chunks)
var bufferSize = chunks.reduce(function (accum, chunk) {
// data chunk
if (Buffer.isBuffer(chunk)) {
return accum + bufferutils.pushDataSize(chunk.length) + chunk.length
}
// opcode
if (strChunk in opcodes) {
return opcodes[strChunk]
return accum + 1
}, 0.0)
var buffer = new Buffer(bufferSize)
var offset = 0
chunks.forEach(function (chunk) {
// data chunk
if (Buffer.isBuffer(chunk)) {
offset += bufferutils.writePushDataInt(buffer, chunk.length, offset)
chunk.copy(buffer, offset)
offset += chunk.length
// opcode
} else {
return new Buffer(strChunk, 'hex')
buffer.writeUInt8(chunk, offset)
offset += 1
}
})
return Script.fromChunks(chunks)
if (offset !== buffer.length) throw new Error('Could not decode chunks')
return buffer
}
Script.fromBuffer = function (buffer) {
function decompile (buffer) {
// TODO: remove me
if (types.Array(buffer)) return buffer
typeforce(types.Buffer, buffer)
var chunks = []

@@ -36,14 +87,14 @@ var i = 0

while (i < buffer.length) {
var opcode = buffer.readUInt8(i)
var opcode = buffer[i]
// data chunk
if ((opcode > opcodes.OP_0) && (opcode <= opcodes.OP_PUSHDATA4)) {
if ((opcode > OPS.OP_0) && (opcode <= OPS.OP_PUSHDATA4)) {
var d = bufferutils.readPushDataInt(buffer, i)
// did reading a pushDataInt fail? return non-chunked script
if (d === null) return new Script(buffer, [])
// did reading a pushDataInt fail? empty script
if (d === null) return []
i += d.size
// attempt to read too much data?
if (i + d.number > buffer.length) return new Script(buffer, [])
// attempt to read too much data? empty script
if (i + d.number > buffer.length) return []

@@ -63,84 +114,286 @@ var data = buffer.slice(i, i + d.number)

return new Script(buffer, chunks)
return chunks
}
Script.fromChunks = function (chunks) {
typeForce('Array', chunks)
function isCanonicalPubKey (buffer) {
if (!Buffer.isBuffer(buffer)) return false
if (buffer.length < 33) return false
var bufferSize = chunks.reduce(function (accum, chunk) {
// data chunk
if (Buffer.isBuffer(chunk)) {
return accum + bufferutils.pushDataSize(chunk.length) + chunk.length
}
switch (buffer[0]) {
case 0x02:
case 0x03:
return buffer.length === 33
case 0x04:
return buffer.length === 65
}
// opcode
return accum + 1
}, 0.0)
return false
}
var buffer = new Buffer(bufferSize)
var offset = 0
function isCanonicalSignature (buffer) {
if (!Buffer.isBuffer(buffer)) return false
if (!isDefinedHashType(buffer[buffer.length - 1])) return false
chunks.forEach(function (chunk) {
// data chunk
if (Buffer.isBuffer(chunk)) {
offset += bufferutils.writePushDataInt(buffer, chunk.length, offset)
return bip66.check(buffer.slice(0, -1))
}
chunk.copy(buffer, offset)
offset += chunk.length
function isDefinedHashType (hashType) {
var hashTypeMod = hashType & ~0x80
// opcode
} else {
buffer.writeUInt8(chunk, offset)
offset += 1
}
})
// return hashTypeMod > SIGHASH_ALL && hashTypeMod < SIGHASH_SINGLE
return hashTypeMod > 0x00 && hashTypeMod < 0x04
}
assert.equal(offset, buffer.length, 'Could not decode chunks')
return new Script(buffer, chunks)
function isPubKeyHashInput (script) {
var chunks = decompile(script)
return chunks.length === 2 &&
isCanonicalSignature(chunks[0]) &&
isCanonicalPubKey(chunks[1])
}
Script.fromHex = function (hex) {
return Script.fromBuffer(new Buffer(hex, 'hex'))
function isPubKeyHashOutput (script) {
var chunks = decompile(script)
return chunks.length === 5 &&
chunks[0] === OPS.OP_DUP &&
chunks[1] === OPS.OP_HASH160 &&
Buffer.isBuffer(chunks[2]) &&
chunks[2].length === 20 &&
chunks[3] === OPS.OP_EQUALVERIFY &&
chunks[4] === OPS.OP_CHECKSIG
}
Script.EMPTY = Script.fromChunks([])
function isPubKeyInput (script) {
var chunks = decompile(script)
Script.prototype.getHash = function () {
return crypto.hash160(this.buffer)
return chunks.length === 1 &&
isCanonicalSignature(chunks[0])
}
// 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
}))
function isPubKeyOutput (script) {
var chunks = decompile(script)
return chunks.length === 2 &&
isCanonicalPubKey(chunks[0]) &&
chunks[1] === OPS.OP_CHECKSIG
}
var reverseOps = []
for (var op in opcodes) {
var code = opcodes[op]
reverseOps[code] = op
function isScriptHashInput (script, allowIncomplete) {
var chunks = decompile(script)
if (chunks.length < 2) return false
var lastChunk = chunks[chunks.length - 1]
if (!Buffer.isBuffer(lastChunk)) return false
var scriptSigChunks = chunks.slice(0, -1)
var redeemScriptChunks = decompile(lastChunk)
// is redeemScript a valid script?
if (redeemScriptChunks.length === 0) return false
return classifyInput(scriptSigChunks, allowIncomplete) === classifyOutput(redeemScriptChunks)
}
Script.prototype.toASM = function () {
return this.chunks.map(function (chunk) {
// data chunk
if (Buffer.isBuffer(chunk)) {
return chunk.toString('hex')
function isScriptHashOutput (script) {
var chunks = decompile(script)
// opcode
} else {
return reverseOps[chunk]
}
}).join(' ')
return chunks.length === 3 &&
chunks[0] === OPS.OP_HASH160 &&
Buffer.isBuffer(chunks[1]) &&
chunks[1].length === 20 &&
chunks[2] === OPS.OP_EQUAL
}
Script.prototype.toBuffer = function () {
return this.buffer
// allowIncomplete is to account for combining signatures
// See https://github.com/bitcoin/bitcoin/blob/f425050546644a36b0b8e0eb2f6934a3e0f6f80f/src/script/sign.cpp#L195-L197
function isMultisigInput (script, allowIncomplete) {
var chunks = decompile(script)
if (chunks.length < 2) return false
if (chunks[0] !== OPS.OP_0) return false
if (allowIncomplete) {
return chunks.slice(1).every(function (chunk) {
return chunk === OPS.OP_0 || isCanonicalSignature(chunk)
})
}
return chunks.slice(1).every(isCanonicalSignature)
}
Script.prototype.toHex = function () {
return this.toBuffer().toString('hex')
function isMultisigOutput (script) {
var chunks = decompile(script)
if (chunks.length < 4) return false
if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) return false
var mOp = chunks[0]
if (mOp === OPS.OP_0) return false
if (mOp < OPS.OP_1) return false
if (mOp > OPS.OP_16) return false
var nOp = chunks[chunks.length - 2]
if (nOp === OPS.OP_0) return false
if (nOp < OPS.OP_1) return false
if (nOp > OPS.OP_16) return false
var m = mOp - (OPS.OP_1 - 1)
var n = nOp - (OPS.OP_1 - 1)
if (n < m) return false
var pubKeys = chunks.slice(1, -2)
if (n < pubKeys.length) return false
return pubKeys.every(isCanonicalPubKey)
}
module.exports = Script
function isNullDataOutput (script) {
var chunks = decompile(script)
return chunks[0] === OPS.OP_RETURN
}
function classifyOutput (script) {
var chunks = decompile(script)
if (isPubKeyHashOutput(chunks)) {
return 'pubkeyhash'
} else if (isScriptHashOutput(chunks)) {
return 'scripthash'
} else if (isMultisigOutput(chunks)) {
return 'multisig'
} else if (isPubKeyOutput(chunks)) {
return 'pubkey'
} else if (isNullDataOutput(chunks)) {
return 'nulldata'
}
return 'nonstandard'
}
function classifyInput (script, allowIncomplete) {
var chunks = decompile(script)
if (isPubKeyHashInput(chunks)) {
return 'pubkeyhash'
} else if (isMultisigInput(chunks, allowIncomplete)) {
return 'multisig'
} else if (isScriptHashInput(chunks, allowIncomplete)) {
return 'scripthash'
} else if (isPubKeyInput(chunks)) {
return 'pubkey'
}
return 'nonstandard'
}
// Standard Script Templates
// {pubKey} OP_CHECKSIG
function pubKeyOutput (pubKey) {
return compile([pubKey, OPS.OP_CHECKSIG])
}
// OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG
function pubKeyHashOutput (pubKeyHash) {
typeforce(types.Hash160bit, pubKeyHash)
return compile([OPS.OP_DUP, OPS.OP_HASH160, pubKeyHash, OPS.OP_EQUALVERIFY, OPS.OP_CHECKSIG])
}
// OP_HASH160 {scriptHash} OP_EQUAL
function scriptHashOutput (scriptHash) {
typeforce(types.Hash160bit, scriptHash)
return compile([OPS.OP_HASH160, scriptHash, OPS.OP_EQUAL])
}
// m [pubKeys ...] n OP_CHECKMULTISIG
function multisigOutput (m, pubKeys) {
typeforce(types.tuple(types.Number, [types.Buffer]), arguments)
var n = pubKeys.length
if (n < m) throw new Error('Not enough pubKeys provided')
return compile([].concat(
(OPS.OP_1 - 1) + m,
pubKeys,
(OPS.OP_1 - 1) + n,
OPS.OP_CHECKMULTISIG
))
}
// {signature}
function pubKeyInput (signature) {
typeforce(types.Buffer, signature)
return compile([signature])
}
// {signature} {pubKey}
function pubKeyHashInput (signature, pubKey) {
typeforce(types.tuple(types.Buffer, types.Buffer), arguments)
return compile([signature, pubKey])
}
// <scriptSig> {serialized scriptPubKey script}
function scriptHashInput (scriptSig, scriptPubKey) {
var scriptSigChunks = decompile(scriptSig)
var serializedScriptPubKey = compile(scriptPubKey)
return compile([].concat(
scriptSigChunks,
serializedScriptPubKey
))
}
// OP_0 [signatures ...]
function multisigInput (signatures, scriptPubKey) {
if (scriptPubKey) {
var chunks = decompile(scriptPubKey)
if (!isMultisigOutput(chunks)) throw new Error('Expected multisig scriptPubKey')
var mOp = chunks[0]
var nOp = chunks[chunks.length - 2]
var m = mOp - (OPS.OP_1 - 1)
var n = nOp - (OPS.OP_1 - 1)
if (signatures.length < m) throw new Error('Not enough signatures provided')
if (signatures.length > n) throw new Error('Too many signatures provided')
}
return compile([].concat(OPS.OP_0, signatures))
}
function nullDataOutput (data) {
return compile([OPS.OP_RETURN, data])
}
module.exports = {
compile: compile,
decompile: decompile,
fromASM: fromASM,
toASM: toASM,
isCanonicalPubKey: isCanonicalPubKey,
isCanonicalSignature: isCanonicalSignature,
isDefinedHashType: isDefinedHashType,
isPubKeyHashInput: isPubKeyHashInput,
isPubKeyHashOutput: isPubKeyHashOutput,
isPubKeyInput: isPubKeyInput,
isPubKeyOutput: isPubKeyOutput,
isScriptHashInput: isScriptHashInput,
isScriptHashOutput: isScriptHashOutput,
isMultisigInput: isMultisigInput,
isMultisigOutput: isMultisigOutput,
isNullDataOutput: isNullDataOutput,
classifyOutput: classifyOutput,
classifyInput: classifyInput,
pubKeyOutput: pubKeyOutput,
pubKeyHashOutput: pubKeyHashOutput,
scriptHashOutput: scriptHashOutput,
multisigOutput: multisigOutput,
pubKeyInput: pubKeyInput,
pubKeyHashInput: pubKeyHashInput,
scriptHashInput: scriptHashInput,
multisigInput: multisigInput,
nullDataOutput: nullDataOutput
}

@@ -1,15 +0,51 @@

var assert = require('assert')
var baddress = require('./address')
var bcrypto = require('./crypto')
var bscript = require('./script')
var bufferutils = require('./bufferutils')
var networks = require('./networks')
var ops = require('./opcodes')
var scripts = require('./scripts')
var ECPubKey = require('./ecpubkey')
var ECPair = require('./ecpair')
var ECSignature = require('./ecsignature')
var Script = require('./script')
var Transaction = require('./transaction')
function extractInput (txIn) {
// re-orders signatures to match pubKeys, fills undefined otherwise
function fixMSSignatures (transaction, vin, pubKeys, signatures, prevOutScript, hashType, skipPubKey) {
// maintain a local copy of unmatched signatures
var unmatched = signatures.slice()
var cache = {}
return pubKeys.map(function (pubKey) {
// skip optionally provided pubKey
if (skipPubKey && bufferutils.equal(skipPubKey, pubKey)) return undefined
var matched
var keyPair2 = ECPair.fromPublicKeyBuffer(pubKey)
// check for a matching signature
unmatched.some(function (signature, i) {
// skip if undefined || OP_0
if (!signature) return false
var signatureHash = cache[hashType] = cache[hashType] || transaction.hashForSignature(vin, prevOutScript, hashType)
if (!keyPair2.verify(signatureHash, signature)) return false
// remove matched signature from unmatched
unmatched[i] = undefined
matched = signature
return true
})
return matched || undefined
})
}
function extractInput (transaction, txIn, vin) {
var redeemScript
var scriptSig = txIn.script
var scriptSigChunks = bscript.decompile(scriptSig)
var prevOutScript
var prevOutType = scripts.classifyInput(scriptSig, true)
var prevOutType = bscript.classifyInput(scriptSig, true)
var scriptType

@@ -19,7 +55,9 @@

if (prevOutType === 'scripthash') {
redeemScript = Script.fromBuffer(scriptSig.chunks.slice(-1)[0])
prevOutScript = scripts.scriptHashOutput(redeemScript.getHash())
redeemScript = scriptSigChunks.slice(-1)[0]
prevOutScript = bscript.scriptHashOutput(bcrypto.hash160(redeemScript))
scriptSig = Script.fromChunks(scriptSig.chunks.slice(0, -1))
scriptType = scripts.classifyInput(scriptSig, true)
scriptSig = bscript.compile(scriptSigChunks.slice(0, -1))
scriptSigChunks = scriptSigChunks.slice(0, -1)
scriptType = bscript.classifyInput(scriptSig, true)
} else {

@@ -29,2 +67,8 @@ scriptType = prevOutType

// pre-empt redeemScript decompilation
var redeemScriptChunks
if (redeemScript) {
redeemScriptChunks = bscript.decompile(redeemScript)
}
// Extract hashType, pubKeys and signatures

@@ -34,14 +78,13 @@ var hashType, parsed, pubKeys, signatures

switch (scriptType) {
case 'pubkeyhash': {
parsed = ECSignature.parseScriptSignature(scriptSig.chunks[0])
case 'pubkeyhash':
parsed = ECSignature.parseScriptSignature(scriptSigChunks[0])
hashType = parsed.hashType
pubKeys = [ECPubKey.fromBuffer(scriptSig.chunks[1])]
pubKeys = scriptSigChunks.slice(1)
signatures = [parsed.signature]
prevOutScript = pubKeys[0].getAddress().toOutputScript()
prevOutScript = bscript.pubKeyHashOutput(bcrypto.hash160(pubKeys[0]))
break
}
case 'pubkey': {
parsed = ECSignature.parseScriptSignature(scriptSig.chunks[0])
case 'pubkey':
parsed = ECSignature.parseScriptSignature(scriptSigChunks[0])
hashType = parsed.hashType

@@ -51,11 +94,10 @@ signatures = [parsed.signature]

if (redeemScript) {
pubKeys = [ECPubKey.fromBuffer(redeemScript.chunks[0])]
pubKeys = redeemScriptChunks.slice(0, 1)
}
break
}
case 'multisig': {
signatures = scriptSig.chunks.slice(1).map(function (chunk) {
if (chunk === ops.OP_0) return chunk
case 'multisig':
signatures = scriptSigChunks.slice(1).map(function (chunk) {
if (chunk === ops.OP_0) return undefined

@@ -69,7 +111,10 @@ var parsed = ECSignature.parseScriptSignature(chunk)

if (redeemScript) {
pubKeys = redeemScript.chunks.slice(1, -2).map(ECPubKey.fromBuffer)
pubKeys = redeemScriptChunks.slice(1, -2)
if (pubKeys.length !== signatures.length) {
signatures = fixMSSignatures(transaction, vin, pubKeys, signatures, redeemScript, hashType, redeemScript)
}
}
break
}
}

@@ -88,6 +133,7 @@

function TransactionBuilder () {
function TransactionBuilder (network) {
this.prevTxMap = {}
this.prevOutScripts = {}
this.prevOutTypes = {}
this.network = network || networks.bitcoin

@@ -98,4 +144,4 @@ this.inputs = []

TransactionBuilder.fromTransaction = function (transaction) {
var txb = new TransactionBuilder()
TransactionBuilder.fromTransaction = function (transaction, network) {
var txb = new TransactionBuilder(network)

@@ -117,10 +163,12 @@ // Copy other transaction fields

// Extract/add signatures
txb.inputs = transaction.ins.map(function (txIn) {
// TODO: remove me after testcase added
assert(!Transaction.isCoinbaseHash(txIn.hash), 'coinbase inputs not supported')
txb.inputs = transaction.ins.map(function (txIn, vin) {
// TODO: verify whether extractInput is sane with coinbase scripts
if (Transaction.isCoinbaseHash(txIn.hash)) {
throw new Error('coinbase inputs not supported')
}
// Ignore empty scripts
if (txIn.script.buffer.length === 0) return {}
if (txIn.script.length === 0) return {}
return extractInput(txIn)
return extractInput(transaction, txIn, vin)
})

@@ -131,20 +179,13 @@

TransactionBuilder.prototype.addInput = function (prevTx, index, sequence, prevOutScript) {
var prevOutHash
TransactionBuilder.prototype.addInput = function (txHash, vout, sequence, prevOutScript) {
// is it a hex string?
if (typeof txHash === 'string') {
// transaction hashs's are displayed in reverse order, un-reverse it
txHash = new Buffer(txHash, 'hex')
Array.prototype.reverse.call(txHash)
// txId
if (typeof prevTx === 'string') {
prevOutHash = new Buffer(prevTx, 'hex')
// TxId hex is big-endian, we want little-endian hash
Array.prototype.reverse.call(prevOutHash)
// Transaction
} else if (prevTx instanceof Transaction) {
prevOutHash = prevTx.getHash()
prevOutScript = prevTx.outs[index].script
// txHash
} else {
prevOutHash = prevTx
// is it a Transaction object?
} else if (txHash instanceof Transaction) {
prevOutScript = txHash.outs[vout].script
txHash = txHash.getHash()
}

@@ -154,15 +195,18 @@

if (prevOutScript) {
var prevOutType = scripts.classifyOutput(prevOutScript)
var prevOutScriptChunks = bscript.decompile(prevOutScript)
var prevOutType = bscript.classifyOutput(prevOutScriptChunks)
// if we can, extract pubKey information
switch (prevOutType) {
case 'multisig': {
input.pubKeys = prevOutScript.chunks.slice(1, -2).map(ECPubKey.fromBuffer)
case 'multisig':
input.pubKeys = prevOutScriptChunks.slice(1, -2)
input.signatures = input.pubKeys.map(function () { return undefined })
break
}
case 'pubkey': {
input.pubKeys = prevOutScript.chunks.slice(0, 1).map(ECPubKey.fromBuffer)
case 'pubkey':
input.pubKeys = prevOutScriptChunks.slice(0, 1)
input.signatures = [undefined]
break
}
}

@@ -178,12 +222,14 @@

assert(this.inputs.every(function (input2) {
var valid = this.inputs.every(function (input2) {
if (input2.hashType === undefined) return true
return input2.hashType & Transaction.SIGHASH_ANYONECANPAY
}), 'No, this would invalidate signatures')
})
var prevOut = prevOutHash.toString('hex') + ':' + index
assert(!(prevOut in this.prevTxMap), 'Transaction is already an input')
if (!valid) throw new Error('No, this would invalidate signatures')
var vin = this.tx.addInput(prevOutHash, index, sequence)
var prevOut = txHash.toString('hex') + ':' + vout
if (this.prevTxMap[prevOut]) throw new Error('Transaction is already an input')
var vin = this.tx.addInput(txHash, vout, sequence)
this.inputs[vin] = input

@@ -196,8 +242,15 @@ this.prevTxMap[prevOut] = vin

TransactionBuilder.prototype.addOutput = function (scriptPubKey, value) {
assert(this.inputs.every(function (input) {
var valid = this.inputs.every(function (input) {
if (input.hashType === undefined) return true
return (input.hashType & 0x1f) === Transaction.SIGHASH_SINGLE
}), 'No, this would invalidate signatures')
})
if (!valid) throw new Error('No, this would invalidate signatures')
// Attempt to get a script if it's a base58 address string
if (typeof scriptPubKey === 'string') {
scriptPubKey = baddress.toOutputScript(scriptPubKey, this.network)
}
return this.tx.addOutput(scriptPubKey, value)

@@ -213,6 +266,6 @@ }

var canSignTypes = {
'pubkeyhash': true,
var canBuildTypes = {
'multisig': true,
'pubkey': true
'pubkey': true,
'pubkeyhash': true
}

@@ -222,4 +275,4 @@

if (!allowIncomplete) {
assert(this.tx.ins.length > 0, 'Transaction has no inputs')
assert(this.tx.outs.length > 0, 'Transaction has no outputs')
if (!this.tx.ins.length) throw new Error('Transaction has no inputs')
if (!this.tx.outs.length) throw new Error('Transaction has no outputs')
}

@@ -229,3 +282,3 @@

// Create script signatures from signature meta-data
// Create script signatures from inputs
this.inputs.forEach(function (input, index) {

@@ -236,5 +289,7 @@ var scriptType = input.scriptType

if (!allowIncomplete) {
assert(!!scriptType, 'Transaction is not complete')
assert(scriptType in canSignTypes, scriptType + ' not supported')
assert(input.signatures, 'Transaction is missing signatures')
if (!scriptType) throw new Error('Transaction is not complete')
if (!canBuildTypes[scriptType]) throw new Error(scriptType + ' not supported')
// XXX: only relevant to types that need signatures
if (!input.signatures) throw new Error('Transaction is missing signatures')
}

@@ -244,10 +299,8 @@

switch (scriptType) {
case 'pubkeyhash': {
case 'pubkeyhash':
var pkhSignature = input.signatures[0].toScriptSignature(input.hashType)
scriptSig = scripts.pubKeyHashInput(pkhSignature, input.pubKeys[0])
scriptSig = bscript.pubKeyHashInput(pkhSignature, input.pubKeys[0])
break
}
case 'multisig': {
// Array.prototype.map is sparse-compatible
case 'multisig':
var msSignatures = input.signatures.map(function (signature) {

@@ -260,8 +313,7 @@ return signature && signature.toScriptSignature(input.hashType)

for (var i = 0; i < msSignatures.length; ++i) {
if (msSignatures[i]) continue
msSignatures[i] = msSignatures[i] || ops.OP_0
}
msSignatures[i] = ops.OP_0
}
// remove blank signatures
} else {
// Array.prototype.filter returns non-sparse array
msSignatures = msSignatures.filter(function (x) { return x })

@@ -271,11 +323,9 @@ }

var redeemScript = allowIncomplete ? undefined : input.redeemScript
scriptSig = scripts.multisigInput(msSignatures, redeemScript)
scriptSig = bscript.multisigInput(msSignatures, redeemScript)
break
}
case 'pubkey': {
case 'pubkey':
var pkSignature = input.signatures[0].toScriptSignature(input.hashType)
scriptSig = scripts.pubKeyInput(pkSignature)
scriptSig = bscript.pubKeyInput(pkSignature)
break
}
}

@@ -288,3 +338,3 @@ }

if (input.prevOutType === 'scripthash') {
scriptSig = scripts.scriptHashInput(scriptSig, input.redeemScript)
scriptSig = bscript.scriptHashInput(scriptSig, input.redeemScript)
}

@@ -299,4 +349,5 @@

TransactionBuilder.prototype.sign = function (index, privKey, redeemScript, hashType) {
assert(index in this.inputs, 'No input at index: ' + index)
TransactionBuilder.prototype.sign = function (index, keyPair, redeemScript, hashType) {
if (keyPair.network !== this.network) throw new Error('Inconsistent network')
if (!this.inputs[index]) throw new Error('No input at index: ' + index)
hashType = hashType || Transaction.SIGHASH_ALL

@@ -310,12 +361,15 @@

input.scriptType &&
input.signatures
input.signatures &&
input.signatures.length === input.pubKeys.length
// are we almost ready to sign?
var kpPubKey = keyPair.getPublicKeyBuffer()
// are we ready to sign?
if (canSign) {
// if redeemScript was provided, enforce consistency
if (redeemScript) {
assert.deepEqual(input.redeemScript, redeemScript, 'Inconsistent redeemScript')
if (!bufferutils.equal(input.redeemScript, redeemScript)) throw new Error('Inconsistent redeemScript')
}
assert.equal(input.hashType, hashType, 'Inconsistent hashType')
if (input.hashType !== hashType) throw new Error('Inconsistent hashType')

@@ -328,35 +382,39 @@ // no? prepare

if (input.prevOutScript) {
assert.equal(input.prevOutType, 'scripthash', 'PrevOutScript must be P2SH')
if (input.prevOutType !== 'scripthash') throw new Error('PrevOutScript must be P2SH')
var scriptHash = input.prevOutScript.chunks[1]
assert.deepEqual(scriptHash, redeemScript.getHash(), 'RedeemScript does not match ' + scriptHash.toString('hex'))
var scriptHash = bscript.decompile(input.prevOutScript)[1]
if (!bufferutils.equal(scriptHash, bcrypto.hash160(redeemScript))) throw new Error('RedeemScript does not match ' + scriptHash.toString('hex'))
}
var scriptType = scripts.classifyOutput(redeemScript)
assert(scriptType in canSignTypes, 'RedeemScript not supported (' + scriptType + ')')
var scriptType = bscript.classifyOutput(redeemScript)
var redeemScriptChunks = bscript.decompile(redeemScript)
var pubKeys
var pubKeys = []
switch (scriptType) {
case 'multisig': {
pubKeys = redeemScript.chunks.slice(1, -2).map(ECPubKey.fromBuffer)
case 'multisig':
pubKeys = redeemScriptChunks.slice(1, -2)
break
}
case 'pubkeyhash': {
var pkh1 = redeemScript.chunks[2]
var pkh2 = privKey.pub.getAddress().hash
case 'pubkeyhash':
var pkh1 = redeemScriptChunks[2]
var pkh2 = bcrypto.hash160(keyPair.getPublicKeyBuffer())
assert.deepEqual(pkh1, pkh2, 'privateKey cannot sign for this input')
pubKeys = [privKey.pub]
if (!bufferutils.equal(pkh1, pkh2)) throw new Error('privateKey cannot sign for this input')
pubKeys = [kpPubKey]
break
}
case 'pubkey': {
pubKeys = redeemScript.chunks.slice(0, 1).map(ECPubKey.fromBuffer)
case 'pubkey':
pubKeys = redeemScriptChunks.slice(0, 1)
break
}
default:
throw new Error('RedeemScript not supported (' + scriptType + ')')
}
// if we don't have a prevOutScript, generate a P2SH script
if (!input.prevOutScript) {
input.prevOutScript = scripts.scriptHashOutput(redeemScript.getHash())
input.prevOutScript = bscript.scriptHashOutput(bcrypto.hash160(redeemScript))
input.prevOutType = 'scripthash'

@@ -368,17 +426,17 @@ }

input.scriptType = scriptType
// cannot be pay-to-scriptHash
input.signatures = pubKeys.map(function () { return undefined })
} else {
assert.notEqual(input.prevOutType, 'scripthash', 'PrevOutScript is P2SH, missing redeemScript')
// pay-to-scriptHash is not possible without a redeemScript
if (input.prevOutType === 'scripthash') throw new Error('PrevOutScript is P2SH, missing redeemScript')
// can we otherwise sign this?
if (input.scriptType) {
assert(input.pubKeys, input.scriptType + ' not supported')
// we know nothin' Jon Snow, assume pubKeyHash
} else {
input.prevOutScript = privKey.pub.getAddress().toOutputScript()
// if we don't have a scriptType, assume pubKeyHash otherwise
if (!input.scriptType) {
input.prevOutScript = bscript.pubKeyHashOutput(bcrypto.hash160(keyPair.getPublicKeyBuffer()))
input.prevOutType = 'pubkeyhash'
input.pubKeys = [privKey.pub]
input.pubKeys = [kpPubKey]
input.scriptType = input.prevOutType
input.signatures = [undefined]
} else {
// throw if we can't sign with it
if (!input.pubKeys || !input.signatures) throw new Error(input.scriptType + ' not supported')
}

@@ -388,43 +446,22 @@ }

input.hashType = hashType
input.signatures = input.signatures || []
}
// ready to sign?
var signatureScript = input.redeemScript || input.prevOutScript
var signatureHash = this.tx.hashForSignature(index, signatureScript, hashType)
// enforce signature order matches public keys
if (input.scriptType === 'multisig' && input.redeemScript && input.signatures.length !== input.pubKeys.length) {
// maintain a local copy of unmatched signatures
var unmatched = input.signatures.slice()
input.signatures = input.pubKeys.map(function (pubKey) {
var match
// check for any matching signatures
unmatched.some(function (signature, i) {
if (!pubKey.verify(signatureHash, signature)) return false
match = signature
// remove matched signature from unmatched
unmatched.splice(i, 1)
return true
})
return match || undefined
})
}
// enforce in order signing of public keys
assert(input.pubKeys.some(function (pubKey, i) {
if (!privKey.pub.Q.equals(pubKey.Q)) return false
var valid = input.pubKeys.some(function (pubKey, i) {
if (!bufferutils.equal(kpPubKey, pubKey)) return false
if (input.signatures[i]) throw new Error('Signature already exists')
assert(!input.signatures[i], 'Signature already exists')
var signature = privKey.sign(signatureHash)
var signature = keyPair.sign(signatureHash)
input.signatures[i] = signature
return true
}, this), 'privateKey cannot sign for this input')
})
if (!valid) throw new Error('Key pair cannot sign for this input')
}
module.exports = TransactionBuilder

@@ -1,12 +0,8 @@

var assert = require('assert')
var bufferutils = require('./bufferutils')
var crypto = require('./crypto')
var typeForce = require('typeforce')
var bcrypto = require('./crypto')
var opcodes = require('./opcodes')
var scripts = require('./scripts')
var bscript = require('./script')
var typeforce = require('typeforce')
var types = require('./types')
var Address = require('./address')
var ECSignature = require('./ecsignature')
var Script = require('./script')
function Transaction () {

@@ -25,3 +21,3 @@ this.version = 1

Transaction.fromBuffer = function (buffer, __disableAssert) {
Transaction.fromBuffer = function (buffer, __noStrict) {
var offset = 0

@@ -52,9 +48,5 @@ function readSlice (n) {

function readScript () {
return Script.fromBuffer(readSlice(readVarInt()))
return readSlice(readVarInt())
}
function readGenerationScript () {
return new Script(readSlice(readVarInt()), [])
}
var tx = new Transaction()

@@ -65,19 +57,8 @@ tx.version = readUInt32()

for (var i = 0; i < vinLen; ++i) {
var hash = readSlice(32)
if (Transaction.isCoinbaseHash(hash)) {
tx.ins.push({
hash: hash,
index: readUInt32(),
script: readGenerationScript(),
sequence: readUInt32()
})
} else {
tx.ins.push({
hash: hash,
index: readUInt32(),
script: readScript(),
sequence: readUInt32()
})
}
tx.ins.push({
hash: readSlice(32),
index: readUInt32(),
script: readScript(),
sequence: readUInt32()
})
}

@@ -95,5 +76,4 @@

if (!__disableAssert) {
assert.equal(offset, buffer.length, 'Transaction has unexpected data')
}
if (__noStrict) return tx
if (offset !== buffer.length) throw new Error('Transaction has unexpected data')

@@ -113,33 +93,16 @@ return tx

/**
* Create a new txIn.
*
* Can be called with any of:
*
* - A transaction and an index
* - A transaction hash and an index
*
* Note that this method does not sign the created input.
*/
Transaction.prototype.addInput = function (hash, index, sequence, script) {
if (sequence === undefined || sequence === null) {
sequence = Transaction.DEFAULT_SEQUENCE
}
var EMPTY_SCRIPT = new Buffer(0)
script = script || Script.EMPTY
Transaction.prototype.addInput = function (hash, index, sequence, scriptSig) {
typeforce(types.tuple(
types.Hash256bit,
types.UInt32,
types.maybe(types.UInt32),
types.maybe(types.Buffer)
), arguments)
if (typeof hash === 'string') {
// TxId hex is big-endian, we need little-endian
hash = bufferutils.reverse(new Buffer(hash, 'hex'))
} else if (hash instanceof Transaction) {
hash = hash.getHash()
if (types.Null(sequence)) {
sequence = Transaction.DEFAULT_SEQUENCE
}
typeForce('Buffer', hash)
typeForce('Number', index)
typeForce('Number', sequence)
typeForce('Script', script)
assert.equal(hash.length, 32, 'Expected hash length of 32, got ' + hash.length)
// Add the input and return the input's index

@@ -149,3 +112,3 @@ return (this.ins.push({

index: index,
script: script,
script: scriptSig || EMPTY_SCRIPT,
sequence: sequence

@@ -155,25 +118,5 @@ }) - 1)

/**
* Create a new txOut.
*
* Can be called with:
*
* - A base58 address string and a value
* - An Address object and a value
* - A scriptPubKey Script and a value
*/
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)
}
typeforce(types.tuple(types.Buffer, types.UInt53), arguments)
// Attempt to get a valid script if it's an Address object
if (scriptPubKey instanceof Address) {
scriptPubKey = scriptPubKey.toOutputScript()
}
typeForce('Script', scriptPubKey)
typeForce('Number', value)
// Add the output and return the output's index

@@ -186,2 +129,18 @@ return (this.outs.push({

Transaction.prototype.byteLength = function () {
function scriptSize (someScript) {
var length = someScript.length
return bufferutils.varIntSize(length) + length
}
return (
8 +
bufferutils.varIntSize(this.ins.length) +
bufferutils.varIntSize(this.outs.length) +
this.ins.reduce(function (sum, input) { return sum + 40 + scriptSize(input.script) }, 0) +
this.outs.reduce(function (sum, output) { return sum + 8 + scriptSize(output.script) }, 0)
)
}
Transaction.prototype.clone = function () {

@@ -211,62 +170,90 @@ var newTx = new Transaction()

var ONE = new Buffer('0000000000000000000000000000000000000000000000000000000000000001', 'hex')
var VALUE_UINT64_MAX = new Buffer('ffffffffffffffff', 'hex')
/**
* Hash transaction for signing a specific input.
*
* Bitcoin uses a different hash for each signed transaction input. This
* method copies the transaction, makes the necessary changes based on the
* hashType, serializes and finally hashes the result. This hash can then be
* used to sign the transaction input in question.
* Bitcoin uses a different hash for each signed transaction input.
* This method copies the transaction, makes the necessary changes based on the
* hashType, and then hashes the result.
* This hash can then be used to sign the provided transaction input.
*/
Transaction.prototype.hashForSignature = function (inIndex, prevOutScript, hashType) {
// FIXME: remove in 2.x.y
if (arguments[0] instanceof Script) {
console.warn('hashForSignature(prevOutScript, inIndex, ...) has been deprecated. Use hashForSignature(inIndex, prevOutScript, ...)')
typeforce(types.tuple(types.UInt32, types.Buffer, /* types.UInt8 */ types.Number), arguments)
// swap the arguments (must be stored in tmp, arguments is special)
var tmp = arguments[0]
inIndex = arguments[1]
prevOutScript = tmp
}
// https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L29
if (inIndex >= this.ins.length) return ONE
typeForce('Number', inIndex)
typeForce('Script', prevOutScript)
typeForce('Number', hashType)
var txTmp = this.clone()
assert(inIndex >= 0, 'Invalid vin index')
assert(inIndex < this.ins.length, 'Invalid vin index')
// in case concatenating two scripts ends up with two codeseparators,
// or an extra one at the end, this prevents all those possible incompatibilities.
var hashScript = bscript.compile(bscript.decompile(prevOutScript).filter(function (x) {
return x !== opcodes.OP_CODESEPARATOR
}))
var i
var txTmp = this.clone()
var hashScript = prevOutScript.without(opcodes.OP_CODESEPARATOR)
// Blank out other inputs' signatures
txTmp.ins.forEach(function (txIn) {
txIn.script = Script.EMPTY
})
// blank out other inputs' signatures
txTmp.ins.forEach(function (input) { input.script = EMPTY_SCRIPT })
txTmp.ins[inIndex].script = hashScript
var hashTypeModifier = hashType & 0x1f
// blank out some of the inputs
if ((hashType & 0x1f) === Transaction.SIGHASH_NONE) {
// wildcard payee
txTmp.outs = []
if (hashTypeModifier === Transaction.SIGHASH_NONE) {
assert(false, 'SIGHASH_NONE not yet supported')
} else if (hashTypeModifier === Transaction.SIGHASH_SINGLE) {
assert(false, 'SIGHASH_SINGLE not yet supported')
// let the others update at will
txTmp.ins.forEach(function (input, i) {
if (i !== inIndex) {
input.sequence = 0
}
})
} else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE) {
var nOut = inIndex
// only lock-in the txOut payee at same index as txIn
// https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L60
if (nOut >= this.outs.length) return ONE
txTmp.outs = txTmp.outs.slice(0, nOut + 1)
// blank all other outputs (clear scriptPubKey, value === -1)
var stubOut = {
script: EMPTY_SCRIPT,
valueBuffer: VALUE_UINT64_MAX
}
for (i = 0; i < nOut; i++) {
txTmp.outs[i] = stubOut
}
// let the others update at will
txTmp.ins.forEach(function (input, i) {
if (i !== inIndex) {
input.sequence = 0
}
})
}
// blank out other inputs completely, not recommended for open transactions
if (hashType & Transaction.SIGHASH_ANYONECANPAY) {
assert(false, 'SIGHASH_ANYONECANPAY not yet supported')
txTmp.ins[0] = txTmp.ins[inIndex]
txTmp.ins = txTmp.ins.slice(0, 1)
}
var hashTypeBuffer = new Buffer(4)
hashTypeBuffer.writeInt32LE(hashType, 0)
// serialize and hash
var buffer = new Buffer(txTmp.byteLength() + 4)
buffer.writeInt32LE(hashType, buffer.length - 4)
txTmp.toBuffer().copy(buffer, 0)
var buffer = Buffer.concat([txTmp.toBuffer(), hashTypeBuffer])
return crypto.hash256(buffer)
return bcrypto.hash256(buffer)
}
Transaction.prototype.getHash = function () {
return crypto.hash256(this.toBuffer())
return bcrypto.hash256(this.toBuffer())
}
Transaction.prototype.getId = function () {
// TxHash is little-endian, we need big-endian
// transaction hash's are displayed in reverse order
return bufferutils.reverse(this.getHash()).toString('hex')

@@ -276,16 +263,4 @@ }

Transaction.prototype.toBuffer = function () {
function scriptSize (script) {
var length = script.buffer.length
var buffer = new Buffer(this.byteLength())
return bufferutils.varIntSize(length) + length
}
var buffer = new Buffer(
8 +
bufferutils.varIntSize(this.ins.length) +
bufferutils.varIntSize(this.outs.length) +
this.ins.reduce(function (sum, input) { return sum + 40 + scriptSize(input.script) }, 0) +
this.outs.reduce(function (sum, output) { return sum + 8 + scriptSize(output.script) }, 0)
)
var offset = 0

@@ -318,4 +293,4 @@ function writeSlice (slice) {

writeUInt32(txIn.index)
writeVarInt(txIn.script.buffer.length)
writeSlice(txIn.script.buffer)
writeVarInt(txIn.script.length)
writeSlice(txIn.script)
writeUInt32(txIn.sequence)

@@ -326,5 +301,10 @@ })

this.outs.forEach(function (txOut) {
writeUInt64(txOut.value)
writeVarInt(txOut.script.buffer.length)
writeSlice(txOut.script.buffer)
if (!txOut.valueBuffer) {
writeUInt64(txOut.value)
} else {
writeSlice(txOut.valueBuffer)
}
writeVarInt(txOut.script.length)
writeSlice(txOut.script)
})

@@ -341,42 +321,8 @@

Transaction.prototype.setInputScript = function (index, script) {
typeForce('Number', index)
typeForce('Script', script)
Transaction.prototype.setInputScript = function (index, scriptSig) {
typeforce(types.tuple(types.Number, types.Buffer), arguments)
this.ins[index].script = script
this.ins[index].script = scriptSig
}
// FIXME: remove in 2.x.y
Transaction.prototype.sign = function (index, privKey, hashType) {
console.warn('Transaction.prototype.sign is deprecated. Use TransactionBuilder instead.')
var prevOutScript = privKey.pub.getAddress().toOutputScript()
var signature = this.signInput(index, prevOutScript, privKey, hashType)
var scriptSig = scripts.pubKeyHashInput(signature, privKey.pub)
this.setInputScript(index, scriptSig)
}
// FIXME: remove in 2.x.y
Transaction.prototype.signInput = function (index, prevOutScript, privKey, hashType) {
console.warn('Transaction.prototype.signInput is deprecated. Use TransactionBuilder instead.')
hashType = hashType || Transaction.SIGHASH_ALL
var hash = this.hashForSignature(index, prevOutScript, hashType)
var signature = privKey.sign(hash)
return signature.toScriptSignature(hashType)
}
// FIXME: remove in 2.x.y
Transaction.prototype.validateInput = function (index, prevOutScript, pubKey, buffer) {
console.warn('Transaction.prototype.validateInput is deprecated. Use TransactionBuilder instead.')
var parsed = ECSignature.parseScriptSignature(buffer)
var hash = this.hashForSignature(index, prevOutScript, parsed.hashType)
return pubKey.verify(hash, parsed.signature)
}
module.exports = Transaction
SocketSocket SOC 2 Logo

Product

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

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc