bitcoinjs-lib
Advanced tools
Comparing version 1.5.8 to 2.0.0
{ | ||
"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 | ||
} |
162
src/ecdsa.js
@@ -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 |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
176
81615
9
11
21
2052
1
+ Addedbip66@^1.1.0
+ Addedwif@^1.1.0
+ Addedbip66@1.1.5(transitive)
+ Addedwif@1.2.1(transitive)
Updatedtypeforce@^1.3.0