bitcoinjs-lib
Advanced tools
Comparing version 2.2.0 to 2.3.0
@@ -0,1 +1,21 @@ | ||
# 2.3.0 | ||
__added__ | ||
- Added `HDNode.prototype.isNeutered` (#536) | ||
- Added `HDNode.prototype.derivePath` (#538) | ||
- Added typeforce checking for `HDNode.prototype.derive*` (#539) | ||
- Added `Transaction.prototype.isCoinbase` (#578) | ||
- Added `Block.prototype.checkMerkleRoot` (#580) | ||
- Added `Block.calculateMerkleRoot` (#580) | ||
- Added `TransactionBuilder.prototype.setVersion` (#599) | ||
- Added `script.isWitnessPubKeyHashOutput` (#602) | ||
- Added `script.isWitnessScriptHashOutput` (#602) | ||
- Added `script.witnessPubKeyHashOutput` (#602) | ||
- Added `script.witnessScriptHashOutput` (#602) | ||
- Added `script.witnessScriptHashInput` (#602) | ||
__fixed__ | ||
- Fixed "BIP32 is undefined" when network list given to `HDNode` but no compatible version found (#550) | ||
- Fixed `writePushDataInt` output to adhere to minimal data push policy (#617) | ||
# 2.2.0 | ||
@@ -2,0 +22,0 @@ __added__ |
{ | ||
"name": "bitcoinjs-lib", | ||
"version": "2.2.0", | ||
"version": "2.3.0", | ||
"description": "Client-side Bitcoin JavaScript library", | ||
@@ -47,2 +47,5 @@ "main": "./src/index.js", | ||
}, | ||
"files": [ | ||
"src" | ||
], | ||
"config": { | ||
@@ -74,3 +77,3 @@ "blanket": { | ||
"typeforce": "^1.6.2", | ||
"wif": "^1.1.0" | ||
"wif": "^2.0.1" | ||
}, | ||
@@ -84,2 +87,3 @@ "devDependencies": { | ||
"httpify": "^1.0.0", | ||
"minimaldata": "^1.0.0", | ||
"mocha": "^2.2.0", | ||
@@ -90,3 +94,4 @@ "proxyquire": "^1.4.0", | ||
"travis-cov": "^0.2.0" | ||
} | ||
}, | ||
"license": "MIT" | ||
} |
@@ -11,3 +11,3 @@ # BitcoinJS (bitcoinjs-lib) | ||
The pure JavaScript Bitcoin library for node.js and browsers. | ||
A continued implementation of the original `0.1.3` version used by over a million wallet users; the backbone for almost all Bitcoin web wallets in production today. | ||
Used by over a million wallet users and the backbone for almost all Bitcoin web wallets in production today. | ||
@@ -32,8 +32,6 @@ | ||
If you are thinking of using the master branch of this library in production, *stop*. | ||
Master is not stable; it is our development branch, and only tagged releases may be classified as stable. | ||
If you are thinking of using the master branch of this library in production, **stop**. | ||
Master is not stable; it is our development branch, and [only tagged releases may be classified as stable](https://github.com/bitcoinjs/bitcoinjs-lib/tags). | ||
If you are looking for the original, it is tagged as `0.1.3`. Unless you need it for dependency reasons, it is strongly recommended that you use (or upgrade to) the newest version, which adds major functionality, cleans up the interface, fixes many bugs, and adds over 1,300 more tests. | ||
## Installation | ||
@@ -54,10 +52,9 @@ | ||
If you're familiar with how to use browserify, ignore this and proceed normally. | ||
These steps are advisory only and allow you to use the API to its full extent. | ||
These steps are advisory only, and may not be necessary for your application. | ||
[Browserify](https://github.com/substack/node-browserify) is assumed to be installed for these steps. | ||
From your repository, create a `foobar.js` file | ||
From your repository, create an `index.js` file | ||
``` javascript | ||
var foobar = { | ||
module.exports = { | ||
base58: require('bs58'), | ||
@@ -69,15 +66,16 @@ bitcoin: require('bitcoinjs-lib'), | ||
} | ||
``` | ||
module.exports = foobar | ||
Install each of the above packages locally | ||
``` bash | ||
npm install bs58 bitcoinjs-lib ecurve bigi buffer | ||
``` | ||
Each of these included packages are seperate to `bitcoinjs-lib`, and must be installed separately. | ||
They are however used in the bitcoinjs-lib public API. | ||
After installation, use browserify to compile `index.js` for use in the browser: | ||
``` bash | ||
$ browserify index.js --standalone foo > app.js | ||
``` | ||
Using browserify, compile `foobar.js` for use in the browser: | ||
You will now be able to use `<script src="app.js" />` in your browser, with each of the above exports accessible via the global `foo` object (or whatever you chose for the `--standalone` parameter above). | ||
$ browserify foobar.js -s foobar > foobar.js | ||
You will then be able to load `foobar.js` into your browser, with each of the dependencies above accessible from the global `foobar` object. | ||
**NOTE**: See our package.json for the currently supported version of browserify used by this repository. | ||
@@ -87,4 +85,18 @@ | ||
This is because of the function-name-duck-typing used in [typeforce](https://github.com/dcousens/typeforce). | ||
Example: | ||
``` bash | ||
uglifyjs ... --mangle --reserved 'Array,BigInteger,Boolean,Buffer,ECPair,Function,Number,Point' | ||
``` | ||
### Flow | ||
Definitions for [Flow typechecker](https://flowtype.org/) are available in flow-typed repository. | ||
[You can either download them directly](https://github.com/flowtype/flow-typed/blob/master/definitions/npm/bitcoinjs-lib_v2.x.x/flow_%3E%3Dv0.17.x/bitcoinjs-lib_v2.x.x.js) from the repo, or with the flow-typed CLI | ||
# npm install -g flow-typed | ||
$ flow-typed install -f 0.27 bitcoinjs-lib@2.2.0 # 0.27 for flow version, 2.2.0 for bitcoinjs-lib version | ||
The definitions are complete and up to date with version 2.2.0. The definitions are maintained by [@runn1ng](https://github.com/runn1ng). | ||
## Examples | ||
@@ -94,16 +106,19 @@ | ||
- [Generate a random address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/basic.js#L8) | ||
- [Generate a random address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/basic.js#L9) | ||
- [Generate a address from a SHA256 hash](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/basic.js#L20) | ||
- [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) | ||
- [Verify a Bitcoin message](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/advanced.js#L17) | ||
- [Generate a address and WIF for Litecoin](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/basic.js#L30) | ||
- [Import an address via WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/basic.js#L44) | ||
- [Create a Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/basic.js#L51) | ||
- [Sign a Bitcoin message](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/advanced.js#L8) | ||
- [Verify a Bitcoin message](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/advanced.js#L16) | ||
- [Create an OP RETURN transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/advanced.js#L24) | ||
- [Create a 2-of-3 multisig P2SH address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/multisig.js#L8) | ||
- [Spend from a 2-of-4 multisig P2SH address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/multisig.js#L22) | ||
- [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#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) | ||
- [Create a 2-of-3 multisig P2SH address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/multisig.js#L9) | ||
- [Spend from a 2-of-4 multisig P2SH address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/multisig.js#L25) | ||
- [Generate a single-key stealth address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L11) | ||
- [Generate a dual-key stealth address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L48) | ||
- [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#L14) | ||
- [Recover a Private key from duplicate R values in a signature](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L60) | ||
- [Create a CLTV locked transaction where the expiry is past](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L36) | ||
- [Create a CLTV locked transaction where the parties bypass the expiry](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L70) | ||
- [Create a CLTV locked transaction which fails due to expiry in the future](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L102) | ||
@@ -118,6 +133,3 @@ If you have a use case that you feel could be listed here, please [ask for it](https://github.com/bitcoinjs/bitcoinjs-lib/issues/new)! | ||
- [Blocktrail](https://www.blocktrail.com/) | ||
- [Brainwallet](https://brainwallet.github.io) | ||
- [Coinkite](https://coinkite.com) | ||
- [Coinpunk](https://coinpunk.com) | ||
- [Dark Wallet](https://darkwallet.unsystem.net) | ||
- [Dark Wallet](https://www.darkwallet.is/) | ||
- [DecentralBank](http://decentralbank.com/) | ||
@@ -127,4 +139,2 @@ - [Dogechain Wallet](https://dogechain.info) | ||
- [GreenAddress](https://greenaddress.it) | ||
- [Hive Wallet](https://www.hivewallet.com) | ||
- [QuickCoin](https://wallet.quickcoin.co) | ||
- [Robocoin](https://wallet.robocoin.com) | ||
@@ -137,8 +147,7 @@ - [Skyhook ATM](http://projectskyhook.com) | ||
Stefan Thomas is the inventor and creator of this project. His pioneering work made Bitcoin web wallets possible. | ||
Daniel Cousens, Wei Lu, JP Richardson and Kyle Drake lead the major refactor of the library from 0.1.3 to 1.0.0. | ||
Since then, many people have contributed. [Click here](https://github.com/bitcoinjs/bitcoinjs-lib/graphs/contributors) to see the comprehensive list. | ||
Daniel Cousens, Wei Lu, JP Richardson and Kyle Drake lead the major refactor of the library from 0.1.3 to 1.0.0. | ||
## Contributing | ||
@@ -165,3 +174,3 @@ | ||
- [BIP66](https://github.com/bitcoinjs/bip66) - Strict DER signature decoding | ||
- [BIP69](https://github.com/bitcoinjs/bip69) - Mnemonic generation for deterministic keys | ||
- [BIP69](https://github.com/bitcoinjs/bip69) - Lexicographical Indexing of Transaction Inputs and Outputs | ||
- [Base58](https://github.com/cryptocoinjs/bs58) - Base58 encoding/decoding | ||
@@ -168,0 +177,0 @@ - [Base58 Check](https://github.com/bitcoinjs/bs58check) - Base58 check encoding/decoding |
@@ -0,4 +1,6 @@ | ||
var createHash = require('create-hash') | ||
var bufferutils = require('./bufferutils') | ||
var bcrypto = require('./crypto') | ||
var compare = require('buffer-compare') | ||
var bufferCompare = require('buffer-compare') | ||
var bufferReverse = require('buffer-reverse') | ||
@@ -74,3 +76,3 @@ var Transaction = require('./transaction') | ||
Block.prototype.getId = function () { | ||
return [].reverse.call(this.getHash()).toString('hex') | ||
return bufferReverse(this.getHash()).toString('hex') | ||
} | ||
@@ -136,9 +138,38 @@ | ||
Block.calculateMerkleRoot = function (transactions) { | ||
var length = transactions.length | ||
if (length === 0) throw TypeError('Cannot compute merkle root for zero transactions') | ||
var hashes = transactions.map(function (transaction) { return transaction.getHash() }) | ||
while (length > 1) { | ||
var j = 0 | ||
for (var i = 0; i < length; i += 2, ++j) { | ||
var hasher = createHash('sha256') | ||
hasher.update(hashes[i]) | ||
hasher.update(i + 1 !== length ? hashes[i + 1] : hashes[i]) | ||
hashes[j] = bcrypto.sha256(hasher.digest()) | ||
} | ||
length = j | ||
} | ||
return hashes[0] | ||
} | ||
Block.prototype.checkMerkleRoot = function () { | ||
if (!this.transactions) return false | ||
var actualMerkleRoot = Block.calculateMerkleRoot(this.transactions) | ||
return bufferCompare(this.merkleRoot, actualMerkleRoot) === 0 | ||
} | ||
Block.prototype.checkProofOfWork = function () { | ||
var hash = [].reverse.call(this.getHash()) | ||
var hash = bufferReverse(this.getHash()) | ||
var target = Block.calculateTarget(this.bits) | ||
return compare(hash, target) <= 0 | ||
return bufferCompare(hash, target) <= 0 | ||
} | ||
module.exports = Block |
@@ -1,2 +0,2 @@ | ||
var opcodes = require('./opcodes') | ||
var opcodes = require('./opcodes.json') | ||
@@ -13,4 +13,4 @@ // https://github.com/feross/buffer/blob/master/index.js#L1127 | ||
return i < opcodes.OP_PUSHDATA1 ? 1 | ||
: i < 0xff ? 2 | ||
: i < 0xffff ? 3 | ||
: i <= 0xff ? 2 | ||
: i <= 0xffff ? 3 | ||
: 5 | ||
@@ -17,0 +17,0 @@ } |
@@ -0,3 +1,3 @@ | ||
var baddress = require('./address') | ||
var bcrypto = require('./crypto') | ||
var bs58check = require('bs58check') | ||
var ecdsa = require('./ecdsa') | ||
@@ -61,15 +61,21 @@ var randomBytes = require('randombytes') | ||
ECPair.fromWIF = function (string, network) { | ||
network = network || NETWORKS.bitcoin | ||
var buffer = bs58check.decode(string) | ||
var decoded = wif.decode(string) | ||
var version = decoded.version | ||
// [network, ...] | ||
if (types.Array(network)) { | ||
var version = buffer[0] | ||
network = network.filter(function (network) { | ||
return version === network.wif | ||
}).pop() || {} | ||
}).pop() | ||
if (!network) throw new Error('Unknown network version') | ||
// network | ||
} else { | ||
network = network || NETWORKS.bitcoin | ||
if (version !== network.wif) throw new Error('Invalid network version') | ||
} | ||
var decoded = wif.decodeRaw(network.wif, buffer) | ||
var d = BigInteger.fromBuffer(decoded.d) | ||
var d = BigInteger.fromBuffer(decoded.privateKey) | ||
@@ -99,10 +105,3 @@ return new ECPair(d, null, { | ||
ECPair.prototype.getAddress = function () { | ||
var pubKey = this.getPublicKeyBuffer() | ||
var pubKeyHash = bcrypto.hash160(pubKey) | ||
var payload = new Buffer(21) | ||
payload.writeUInt8(this.network.pubKeyHash, 0) | ||
pubKeyHash.copy(payload, 1) | ||
return bs58check.encode(payload) | ||
return baddress.toBase58Check(bcrypto.hash160(this.getPublicKeyBuffer()), this.getNetwork().pubKeyHash) | ||
} | ||
@@ -109,0 +108,0 @@ |
@@ -67,4 +67,6 @@ var base58check = require('bs58check') | ||
version === network.bip32.public | ||
}).pop() || {} | ||
}).pop() | ||
if (!network) throw new Error('Unknown network version') | ||
// otherwise, assume a network object (or default to bitcoin) | ||
@@ -76,3 +78,3 @@ } else { | ||
if (version !== network.bip32.private && | ||
version !== network.bip32.public) throw new Error('Invalid network') | ||
version !== network.bip32.public) throw new Error('Invalid network version') | ||
@@ -175,3 +177,3 @@ // 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, ... | ||
var network = this.keyPair.network | ||
var version = this.keyPair.d ? network.bip32.private : network.bip32.public | ||
var version = (!this.isNeutered()) ? network.bip32.private : network.bip32.public | ||
var buffer = new Buffer(78) | ||
@@ -196,3 +198,3 @@ | ||
// 33 bytes: the public key or private key data | ||
if (this.keyPair.d) { | ||
if (!this.isNeutered()) { | ||
// 0x00 + k for private keys | ||
@@ -213,2 +215,4 @@ buffer.writeUInt8(0, 45) | ||
HDNode.prototype.derive = function (index) { | ||
typeforce(types.UInt32, index) | ||
var isHardened = index >= HDNode.HIGHEST_BIT | ||
@@ -219,3 +223,3 @@ var data = new Buffer(37) | ||
if (isHardened) { | ||
if (!this.keyPair.d) throw new TypeError('Could not derive hardened child key') | ||
if (this.isNeutered()) throw new TypeError('Could not derive hardened child key') | ||
@@ -248,3 +252,3 @@ // data = 0x00 || ser256(kpar) || ser32(index) | ||
var derivedKeyPair | ||
if (this.keyPair.d) { | ||
if (!this.isNeutered()) { | ||
// ki = parse256(IL) + kpar (mod n) | ||
@@ -287,2 +291,4 @@ var ki = pIL.add(this.keyPair.d).mod(curve.n) | ||
HDNode.prototype.deriveHardened = function (index) { | ||
typeforce(types.UInt31, index) | ||
// Only derives hardened private keys by default | ||
@@ -292,4 +298,34 @@ return this.derive(index + HDNode.HIGHEST_BIT) | ||
// Private === not neutered | ||
// Public === neutered | ||
HDNode.prototype.isNeutered = function () { | ||
return !(this.keyPair.d) | ||
} | ||
HDNode.prototype.derivePath = function (path) { | ||
typeforce(types.Bip32Path, path) | ||
var splitPath = path.split('/') | ||
if (splitPath[0] === 'm') { | ||
if (this.parentFingerprint) { | ||
throw new Error('Not a master node') | ||
} | ||
splitPath = splitPath.slice(1) | ||
} | ||
return splitPath.reduce(function (prevHd, indexStr) { | ||
var index | ||
if (indexStr.slice(-1) === "'") { | ||
index = parseInt(indexStr.slice(0, -1), 10) | ||
return prevHd.deriveHardened(index) | ||
} else { | ||
index = parseInt(indexStr, 10) | ||
return prevHd.derive(index) | ||
} | ||
}, this) | ||
} | ||
HDNode.prototype.toString = HDNode.prototype.toBase58 | ||
module.exports = HDNode |
@@ -14,4 +14,4 @@ module.exports = { | ||
networks: require('./networks'), | ||
opcodes: require('./opcodes'), | ||
opcodes: require('./opcodes.json'), | ||
script: require('./script') | ||
} |
@@ -6,3 +6,3 @@ var bip66 = require('bip66') | ||
var OPS = require('./opcodes') | ||
var OPS = require('./opcodes.json') | ||
var REVERSE_OPS = (function () { | ||
@@ -210,2 +210,18 @@ var result = {} | ||
function isWitnessPubKeyHashOutput (script) { | ||
var buffer = compile(script) | ||
return buffer.length === 22 && | ||
buffer[0] === OPS.OP_0 && | ||
buffer[1] === 0x14 | ||
} | ||
function isWitnessScriptHashOutput (script) { | ||
var buffer = compile(script) | ||
return buffer.length === 34 && | ||
buffer[0] === OPS.OP_0 && | ||
buffer[1] === 0x20 | ||
} | ||
// allowIncomplete is to account for combining signatures | ||
@@ -258,3 +274,7 @@ // See https://github.com/bitcoin/bitcoin/blob/f425050546644a36b0b8e0eb2f6934a3e0f6f80f/src/script/sign.cpp#L195-L197 | ||
if (isPubKeyHashOutput(chunks)) { | ||
if (isWitnessPubKeyHashOutput(chunks)) { | ||
return 'witnesspubkeyhash' | ||
} else if (isWitnessScriptHashOutput(chunks)) { | ||
return 'witnessscripthash' | ||
} else if (isPubKeyHashOutput(chunks)) { | ||
return 'pubkeyhash' | ||
@@ -325,2 +345,16 @@ } else if (isScriptHashOutput(chunks)) { | ||
// OP_0 {pubKeyHash} | ||
function witnessPubKeyHashOutput (pubKeyHash) { | ||
typeforce(types.Hash160bit, pubKeyHash) | ||
return compile([OPS.OP_0, pubKeyHash]) | ||
} | ||
// OP_0 {scriptHash} | ||
function witnessScriptHashOutput (scriptHash) { | ||
typeforce(types.Hash256bit, scriptHash) | ||
return compile([OPS.OP_0, scriptHash]) | ||
} | ||
// {signature} | ||
@@ -351,2 +385,7 @@ function pubKeyInput (signature) { | ||
// <scriptSig> {serialized scriptPubKey script} | ||
function witnessScriptHashInput (scriptSig, scriptPubKey) { | ||
return scriptHashInput(scriptSig, scriptPubKey) | ||
} | ||
// OP_0 [signatures ...] | ||
@@ -391,5 +430,8 @@ function multisigInput (signatures, scriptPubKey) { | ||
isScriptHashOutput: isScriptHashOutput, | ||
isWitnessPubKeyHashOutput: isWitnessPubKeyHashOutput, | ||
isWitnessScriptHashOutput: isWitnessScriptHashOutput, | ||
isMultisigInput: isMultisigInput, | ||
isMultisigOutput: isMultisigOutput, | ||
isNullDataOutput: isNullDataOutput, | ||
classifyOutput: classifyOutput, | ||
@@ -400,2 +442,6 @@ classifyInput: classifyInput, | ||
scriptHashOutput: scriptHashOutput, | ||
witnessPubKeyHashOutput: witnessPubKeyHashOutput, | ||
witnessScriptHashInput: witnessScriptHashInput, | ||
witnessScriptHashOutput: witnessScriptHashOutput, | ||
multisigOutput: multisigOutput, | ||
@@ -402,0 +448,0 @@ pubKeyInput: pubKeyInput, |
@@ -5,4 +5,5 @@ var baddress = require('./address') | ||
var bufferEquals = require('buffer-equals') | ||
var bufferReverse = require('buffer-reverse') | ||
var networks = require('./networks') | ||
var ops = require('./opcodes') | ||
var ops = require('./opcodes.json') | ||
var typeforce = require('typeforce') | ||
@@ -48,82 +49,92 @@ var types = require('./types') | ||
function extractInput (transaction, txIn, vin) { | ||
var redeemScript | ||
var scriptSig = txIn.script | ||
var scriptSigChunks = bscript.decompile(scriptSig) | ||
if (txIn.script.length === 0) return {} | ||
var prevOutScript | ||
var prevOutType = bscript.classifyInput(scriptSig, true) | ||
var scriptType | ||
var scriptSigChunks = bscript.decompile(txIn.script) | ||
var prevOutType = bscript.classifyInput(scriptSigChunks, true) | ||
// Re-classify if scriptHash | ||
if (prevOutType === 'scripthash') { | ||
redeemScript = scriptSigChunks.slice(-1)[0] | ||
prevOutScript = bscript.scriptHashOutput(bcrypto.hash160(redeemScript)) | ||
function processScript (scriptType, scriptSigChunks, redeemScriptChunks) { | ||
// ensure chunks are decompiled | ||
scriptSigChunks = bscript.decompile(scriptSigChunks) | ||
redeemScriptChunks = redeemScriptChunks ? bscript.decompile(redeemScriptChunks) : undefined | ||
scriptSig = bscript.compile(scriptSigChunks.slice(0, -1)) | ||
scriptSigChunks = scriptSigChunks.slice(0, -1) | ||
var hashType, pubKeys, signatures, prevOutScript, redeemScript, redeemScriptType, result, parsed | ||
scriptType = bscript.classifyInput(scriptSig, true) | ||
} else { | ||
scriptType = prevOutType | ||
} | ||
switch (scriptType) { | ||
case 'scripthash': | ||
redeemScript = scriptSigChunks.slice(-1)[0] | ||
scriptSigChunks = bscript.compile(scriptSigChunks.slice(0, -1)) | ||
// pre-empt redeemScript decompilation | ||
var redeemScriptChunks | ||
if (redeemScript) { | ||
redeemScriptChunks = bscript.decompile(redeemScript) | ||
} | ||
redeemScriptType = bscript.classifyInput(scriptSigChunks, true) | ||
prevOutScript = bscript.scriptHashOutput(bcrypto.hash160(redeemScript)) | ||
// Extract hashType, pubKeys and signatures | ||
var hashType, parsed, pubKeys, signatures | ||
result = processScript(redeemScriptType, scriptSigChunks, bscript.decompile(redeemScript)) | ||
switch (scriptType) { | ||
case 'pubkeyhash': | ||
parsed = ECSignature.parseScriptSignature(scriptSigChunks[0]) | ||
hashType = parsed.hashType | ||
pubKeys = scriptSigChunks.slice(1) | ||
signatures = [parsed.signature] | ||
prevOutScript = bscript.pubKeyHashOutput(bcrypto.hash160(pubKeys[0])) | ||
result.prevOutScript = prevOutScript | ||
result.redeemScript = redeemScript | ||
result.redeemScriptType = redeemScriptType | ||
break | ||
return result | ||
case 'pubkey': | ||
parsed = ECSignature.parseScriptSignature(scriptSigChunks[0]) | ||
hashType = parsed.hashType | ||
signatures = [parsed.signature] | ||
case 'pubkeyhash': | ||
parsed = ECSignature.parseScriptSignature(scriptSigChunks[0]) | ||
hashType = parsed.hashType | ||
pubKeys = scriptSigChunks.slice(1) | ||
signatures = [parsed.signature] | ||
prevOutScript = bscript.pubKeyHashOutput(bcrypto.hash160(pubKeys[0])) | ||
if (redeemScript) { | ||
pubKeys = redeemScriptChunks.slice(0, 1) | ||
} | ||
break | ||
break | ||
case 'pubkey': | ||
parsed = ECSignature.parseScriptSignature(scriptSigChunks[0]) | ||
hashType = parsed.hashType | ||
signatures = [parsed.signature] | ||
case 'multisig': | ||
signatures = scriptSigChunks.slice(1).map(function (chunk) { | ||
if (chunk === ops.OP_0) return undefined | ||
if (redeemScriptChunks) { | ||
pubKeys = redeemScriptChunks.slice(0, 1) | ||
} | ||
var parsed = ECSignature.parseScriptSignature(chunk) | ||
hashType = parsed.hashType | ||
break | ||
return parsed.signature | ||
}) | ||
case 'multisig': | ||
signatures = scriptSigChunks.slice(1).map(function (chunk) { | ||
if (chunk === ops.OP_0) return undefined | ||
if (redeemScript) { | ||
pubKeys = redeemScriptChunks.slice(1, -2) | ||
parsed = ECSignature.parseScriptSignature(chunk) | ||
hashType = parsed.hashType | ||
if (pubKeys.length !== signatures.length) { | ||
signatures = fixMSSignatures(transaction, vin, pubKeys, signatures, redeemScript, hashType, redeemScript) | ||
return parsed.signature | ||
}) | ||
if (redeemScriptChunks) { | ||
pubKeys = redeemScriptChunks.slice(1, -2) | ||
if (pubKeys.length !== signatures.length) { | ||
signatures = fixMSSignatures(transaction, vin, pubKeys, signatures, bscript.compile(redeemScriptChunks), hashType, redeemScript) | ||
} | ||
} | ||
} | ||
break | ||
break | ||
} | ||
return { | ||
hashType: hashType, | ||
pubKeys: pubKeys, | ||
signatures: signatures, | ||
prevOutScript: prevOutScript, | ||
redeemScript: redeemScript, | ||
redeemScriptType: redeemScriptType | ||
} | ||
} | ||
// Extract hashType, pubKeys, signatures and prevOutScript | ||
var result = processScript(prevOutType, scriptSigChunks) | ||
return { | ||
hashType: hashType, | ||
prevOutScript: prevOutScript, | ||
hashType: result.hashType, | ||
prevOutScript: result.prevOutScript, | ||
prevOutType: prevOutType, | ||
pubKeys: pubKeys, | ||
redeemScript: redeemScript, | ||
scriptType: scriptType, | ||
signatures: signatures | ||
pubKeys: result.pubKeys, | ||
redeemScript: result.redeemScript, | ||
redeemScriptType: result.redeemScriptType, | ||
signatures: result.signatures | ||
} | ||
@@ -157,2 +168,9 @@ } | ||
TransactionBuilder.prototype.setVersion = function (version) { | ||
typeforce(types.UInt32, version) | ||
// XXX: this might eventually become more complex depending on what the versions represent | ||
this.tx.version = version | ||
} | ||
TransactionBuilder.fromTransaction = function (transaction, network) { | ||
@@ -182,5 +200,2 @@ var txb = new TransactionBuilder(network) | ||
// Ignore empty scripts | ||
if (txIn.script.length === 0) return {} | ||
return extractInput(transaction, txIn, vin) | ||
@@ -196,3 +211,3 @@ }) | ||
// transaction hashs's are displayed in reverse order, un-reverse it | ||
txHash = [].reverse.call(new Buffer(txHash, 'hex')) | ||
txHash = bufferReverse(new Buffer(txHash, 'hex')) | ||
@@ -296,2 +311,44 @@ // is it a Transaction object? | ||
function buildFromInputData (input, scriptType, parentType, redeemScript, allowIncomplete) { | ||
var scriptSig | ||
switch (scriptType) { | ||
case 'pubkeyhash': | ||
var pkhSignature = input.signatures[0].toScriptSignature(input.hashType) | ||
scriptSig = bscript.pubKeyHashInput(pkhSignature, input.pubKeys[0]) | ||
break | ||
case 'pubkey': | ||
var pkSignature = input.signatures[0].toScriptSignature(input.hashType) | ||
scriptSig = bscript.pubKeyInput(pkSignature) | ||
break | ||
case 'multisig': | ||
var msSignatures = input.signatures.map(function (signature) { | ||
return signature && signature.toScriptSignature(input.hashType) | ||
}) | ||
// fill in blanks with OP_0 | ||
if (allowIncomplete) { | ||
for (var i = 0; i < msSignatures.length; ++i) { | ||
msSignatures[i] = msSignatures[i] || ops.OP_0 | ||
} | ||
// remove blank signatures | ||
} else { | ||
msSignatures = msSignatures.filter(function (x) { return x }) | ||
} | ||
scriptSig = bscript.multisigInput(msSignatures, allowIncomplete ? undefined : redeemScript) | ||
break | ||
} | ||
// wrap as scriptHash if necessary | ||
if (parentType === 'scripthash') { | ||
scriptSig = bscript.scriptHashInput(scriptSig, redeemScript) | ||
} | ||
return scriptSig | ||
} | ||
TransactionBuilder.prototype.__build = function (allowIncomplete) { | ||
@@ -307,3 +364,3 @@ if (!allowIncomplete) { | ||
this.inputs.forEach(function (input, index) { | ||
var scriptType = input.scriptType | ||
var scriptType = input.redeemScriptType || input.prevOutType | ||
var scriptSig | ||
@@ -320,47 +377,42 @@ | ||
if (input.signatures) { | ||
switch (scriptType) { | ||
case 'pubkeyhash': | ||
var pkhSignature = input.signatures[0].toScriptSignature(input.hashType) | ||
scriptSig = bscript.pubKeyHashInput(pkhSignature, input.pubKeys[0]) | ||
break | ||
scriptSig = buildFromInputData(input, scriptType, input.prevOutType, input.redeemScript, allowIncomplete) | ||
} | ||
case 'multisig': | ||
var msSignatures = input.signatures.map(function (signature) { | ||
return signature && signature.toScriptSignature(input.hashType) | ||
}) | ||
// did we build a scriptSig? Buffer('') is allowed | ||
if (scriptSig) { | ||
tx.setInputScript(index, scriptSig) | ||
} | ||
}) | ||
// fill in blanks with OP_0 | ||
if (allowIncomplete) { | ||
for (var i = 0; i < msSignatures.length; ++i) { | ||
msSignatures[i] = msSignatures[i] || ops.OP_0 | ||
} | ||
return tx | ||
} | ||
// remove blank signatures | ||
} else { | ||
msSignatures = msSignatures.filter(function (x) { return x }) | ||
} | ||
function extractFromOutputScript (outputScript, keyPair, kpPubKey) { | ||
var scriptType = bscript.classifyOutput(outputScript) | ||
var outputScriptChunks = bscript.decompile(outputScript) | ||
var redeemScript = allowIncomplete ? undefined : input.redeemScript | ||
scriptSig = bscript.multisigInput(msSignatures, redeemScript) | ||
break | ||
switch (scriptType) { | ||
case 'pubkeyhash': | ||
var pkh1 = outputScriptChunks[2] | ||
var pkh2 = bcrypto.hash160(keyPair.getPublicKeyBuffer()) | ||
case 'pubkey': | ||
var pkSignature = input.signatures[0].toScriptSignature(input.hashType) | ||
scriptSig = bscript.pubKeyInput(pkSignature) | ||
break | ||
if (!bufferEquals(pkh1, pkh2)) throw new Error('privateKey cannot sign for this input') | ||
return { | ||
pubKeys: [kpPubKey], | ||
scriptType: scriptType | ||
} | ||
} | ||
// did we build a scriptSig? | ||
if (scriptSig) { | ||
// wrap as scriptHash if necessary | ||
if (input.prevOutType === 'scripthash') { | ||
scriptSig = bscript.scriptHashInput(scriptSig, input.redeemScript) | ||
case 'pubkey': | ||
return { | ||
pubKeys: outputScriptChunks.slice(0, 1), | ||
scriptType: scriptType | ||
} | ||
tx.setInputScript(index, scriptSig) | ||
} | ||
}) | ||
return tx | ||
case 'multisig': | ||
return { | ||
pubKeys: outputScriptChunks.slice(1, -2), | ||
scriptType: scriptType | ||
} | ||
} | ||
} | ||
@@ -378,3 +430,3 @@ | ||
input.pubKeys && | ||
input.scriptType && | ||
input.redeemScriptType && | ||
input.signatures && | ||
@@ -384,2 +436,3 @@ input.signatures.length === input.pubKeys.length | ||
var kpPubKey = keyPair.getPublicKeyBuffer() | ||
var signatureScript | ||
@@ -407,30 +460,5 @@ // are we ready to sign? | ||
var scriptType = bscript.classifyOutput(redeemScript) | ||
var redeemScriptChunks = bscript.decompile(redeemScript) | ||
var pubKeys | ||
var extracted = extractFromOutputScript(redeemScript, keyPair, kpPubKey) | ||
if (!extracted) throw new Error('RedeemScript not supported "' + bscript.toASM(redeemScript) + '"') | ||
switch (scriptType) { | ||
case 'multisig': | ||
pubKeys = redeemScriptChunks.slice(1, -2) | ||
break | ||
case 'pubkeyhash': | ||
var pkh1 = redeemScriptChunks[2] | ||
var pkh2 = bcrypto.hash160(keyPair.getPublicKeyBuffer()) | ||
if (!bufferEquals(pkh1, pkh2)) throw new Error('privateKey cannot sign for this input') | ||
pubKeys = [kpPubKey] | ||
break | ||
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 | ||
@@ -442,6 +470,6 @@ if (!input.prevOutScript) { | ||
input.pubKeys = pubKeys | ||
input.pubKeys = extracted.pubKeys | ||
input.redeemScript = redeemScript | ||
input.scriptType = scriptType | ||
input.signatures = pubKeys.map(function () { return undefined }) | ||
input.redeemScriptType = extracted.scriptType | ||
input.signatures = extracted.pubKeys.map(function () { return undefined }) | ||
} else { | ||
@@ -455,2 +483,3 @@ // pay-to-scriptHash is not possible without a redeemScript | ||
input.prevOutType = 'pubkeyhash' | ||
input.pubKeys = [kpPubKey] | ||
@@ -469,3 +498,3 @@ input.scriptType = input.prevOutType | ||
// ready to sign? | ||
var signatureScript = input.redeemScript || input.prevOutScript | ||
signatureScript = signatureScript || input.redeemScript || input.prevOutScript | ||
var signatureHash = this.tx.hashForSignature(index, signatureScript, hashType) | ||
@@ -478,4 +507,3 @@ | ||
var signature = keyPair.sign(signatureHash) | ||
input.signatures[i] = signature | ||
input.signatures[i] = keyPair.sign(signatureHash) | ||
@@ -482,0 +510,0 @@ return true |
var bcrypto = require('./crypto') | ||
var bscript = require('./script') | ||
var bufferutils = require('./bufferutils') | ||
var opcodes = require('./opcodes') | ||
var bufferReverse = require('buffer-reverse') | ||
var opcodes = require('./opcodes.json') | ||
var typeforce = require('typeforce') | ||
@@ -84,7 +85,13 @@ var types = require('./types') | ||
Transaction.isCoinbaseHash = function (buffer) { | ||
return Array.prototype.every.call(buffer, function (x) { | ||
return x === 0 | ||
}) | ||
typeforce(types.Hash256bit, buffer) | ||
for (var i = 0; i < 32; ++i) { | ||
if (buffer[i] !== 0) return false | ||
} | ||
return true | ||
} | ||
Transaction.prototype.isCoinbase = function () { | ||
return this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash) | ||
} | ||
var EMPTY_SCRIPT = new Buffer(0) | ||
@@ -165,2 +172,6 @@ | ||
var VALUE_UINT64_MAX = new Buffer('ffffffffffffffff', 'hex') | ||
var BLANK_OUTPUT = { | ||
script: EMPTY_SCRIPT, | ||
valueBuffer: VALUE_UINT64_MAX | ||
} | ||
@@ -181,57 +192,51 @@ /** | ||
var txTmp = this.clone() | ||
// 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) { | ||
// ignore OP_CODESEPARATOR | ||
var ourScript = bscript.compile(bscript.decompile(prevOutScript).filter(function (x) { | ||
return x !== opcodes.OP_CODESEPARATOR | ||
})) | ||
var i | ||
// blank out other inputs' signatures | ||
txTmp.ins.forEach(function (input) { input.script = EMPTY_SCRIPT }) | ||
txTmp.ins[inIndex].script = hashScript | ||
var txTmp = this.clone() | ||
// blank out some of the inputs | ||
// SIGHASH_NONE: ignore all outputs? (wildcard payee) | ||
if ((hashType & 0x1f) === Transaction.SIGHASH_NONE) { | ||
// wildcard payee | ||
txTmp.outs = [] | ||
// let the others update at will | ||
// ignore sequence numbers (except at inIndex) | ||
txTmp.ins.forEach(function (input, i) { | ||
if (i !== inIndex) { | ||
input.sequence = 0 | ||
} | ||
if (i === inIndex) return | ||
input.sequence = 0 | ||
}) | ||
// SIGHASH_SINGLE: ignore all outputs, except at the same index? | ||
} 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 | ||
if (inIndex >= this.outs.length) return ONE | ||
txTmp.outs = txTmp.outs.slice(0, nOut + 1) | ||
// truncate outputs after | ||
txTmp.outs.length = inIndex + 1 | ||
// blank all other outputs (clear scriptPubKey, value === -1) | ||
var stubOut = { | ||
script: EMPTY_SCRIPT, | ||
valueBuffer: VALUE_UINT64_MAX | ||
// "blank" outputs before | ||
for (var i = 0; i < inIndex; i++) { | ||
txTmp.outs[i] = BLANK_OUTPUT | ||
} | ||
for (i = 0; i < nOut; i++) { | ||
txTmp.outs[i] = stubOut | ||
} | ||
// ignore sequence numbers (except at inIndex) | ||
txTmp.ins.forEach(function (input, i) { | ||
if (i === inIndex) return | ||
// let the others update at will | ||
txTmp.ins.forEach(function (input, i) { | ||
if (i !== inIndex) { | ||
input.sequence = 0 | ||
} | ||
input.sequence = 0 | ||
}) | ||
} | ||
// blank out other inputs completely, not recommended for open transactions | ||
// SIGHASH_ANYONECANPAY: ignore inputs entirely? | ||
if (hashType & Transaction.SIGHASH_ANYONECANPAY) { | ||
txTmp.ins[0] = txTmp.ins[inIndex] | ||
txTmp.ins = txTmp.ins.slice(0, 1) | ||
txTmp.ins = [txTmp.ins[inIndex]] | ||
txTmp.ins[0].script = ourScript | ||
// SIGHASH_ALL: only ignore input scripts | ||
} else { | ||
// "blank" others input scripts | ||
txTmp.ins.forEach(function (input) { input.script = EMPTY_SCRIPT }) | ||
txTmp.ins[inIndex].script = ourScript | ||
} | ||
@@ -242,3 +247,3 @@ | ||
buffer.writeInt32LE(hashType, buffer.length - 4) | ||
txTmp.toBuffer().copy(buffer, 0) | ||
txTmp.toBuffer(buffer, 0) | ||
@@ -254,9 +259,9 @@ return bcrypto.hash256(buffer) | ||
// transaction hash's are displayed in reverse order | ||
return [].reverse.call(this.getHash()).toString('hex') | ||
return bufferReverse(this.getHash()).toString('hex') | ||
} | ||
Transaction.prototype.toBuffer = function () { | ||
var buffer = new Buffer(this.byteLength()) | ||
Transaction.prototype.toBuffer = function (buffer, initialOffset) { | ||
if (!buffer) buffer = new Buffer(this.byteLength()) | ||
var offset = 0 | ||
var offset = initialOffset || 0 | ||
function writeSlice (slice) { | ||
@@ -307,2 +312,5 @@ slice.copy(buffer, offset) | ||
// avoid slicing unless necessary | ||
if (initialOffset !== undefined) return buffer.slice(initialOffset, offset) | ||
return buffer | ||
@@ -309,0 +317,0 @@ } |
@@ -15,5 +15,9 @@ var typeforce = require('typeforce') | ||
var UINT53_MAX = Math.pow(2, 53) - 1 | ||
var UINT31_MAX = Math.pow(2, 31) - 1 | ||
function UInt2 (value) { return (value & 3) === value } | ||
function UInt8 (value) { return (value & 0xff) === value } | ||
function UInt32 (value) { return (value >>> 0) === value } | ||
function UInt31 (value) { | ||
return UInt32(value) && value <= UINT31_MAX | ||
} | ||
function UInt53 (value) { | ||
@@ -26,2 +30,7 @@ return typeforce.Number(value) && | ||
function Bip32Path (value) { | ||
return typeforce.String(value) && | ||
value.match(/^(m\/)?(\d+'?\/)*\d+'?$/) | ||
} | ||
// external dependent types | ||
@@ -56,4 +65,6 @@ var BigInt = typeforce.quacksLike('BigInteger') | ||
UInt8: UInt8, | ||
UInt31: UInt31, | ||
UInt32: UInt32, | ||
UInt53: UInt53 | ||
UInt53: UInt53, | ||
Bip32Path: Bip32Path | ||
} | ||
@@ -60,0 +71,0 @@ |
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
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
93392
2276
186
12
21
+ Addedwif@2.0.6(transitive)
- Removedwif@1.2.1(transitive)
Updatedwif@^2.0.1