bitcoinjs-lib
Advanced tools
Comparing version 0.1.3 to 0.2.0
{ | ||
"name": "bitcoinjs-lib", | ||
"version": "0.1.3", | ||
"version": "0.2.0", | ||
"description": "Client-side Bitcoin JavaScript library", | ||
"main": "./src/index.js", | ||
"keywords": [ | ||
@@ -12,14 +12,36 @@ "bitcoin", | ||
], | ||
"author": "Stefan Thomas <justmoon@members.fsf.org> (http://www.justmoon.net)", | ||
"repository" : { | ||
"type" : "git", | ||
"url" : "https://github.com/bitcoinjs/bitcoinjs-lib.git" | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/bitcoinjs/bitcoinjs-lib.git" | ||
}, | ||
"devDependencies" : { | ||
"pkginfo" : ">=0.2.1", | ||
"jake-uglify" : ">=1.0.0" | ||
"devDependencies": { | ||
"mocha": "1.18.2", | ||
"istanbul": "0.1.30", | ||
"uglify-js": "2.4.13", | ||
"node-browserify": "https://github.com/substack/node-browserify/tarball/master", | ||
"sinon": "1.9.0" | ||
}, | ||
"testling": { | ||
"browsers": [ | ||
"chrome/20..latest", | ||
"firefox/21..latest", | ||
"safari/latest", | ||
"opera/15..latest", | ||
"iphone/6..latest", | ||
"ipad/6..latest", | ||
"android-browser/4.2..latest" | ||
], | ||
"harness": "mocha-bdd", | ||
"files": "test/*.js" | ||
}, | ||
"scripts": { | ||
"test": "./node_modules/.bin/istanbul test ./node_modules/.bin/_mocha -- --reporter list test/*.js", | ||
"coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- --reporter list test/*.js", | ||
"compile": "./node_modules/.bin/browserify ./src/index.js -s Bitcoin | ./node_modules/.bin/uglifyjs > bitcoinjs-min.js" | ||
}, | ||
"dependencies": { | ||
"crypto-js": "3.1.2-3", | ||
"secure-random": "0.2.1" | ||
} | ||
} |
135
README.md
# bitcoinjs-lib | ||
A library containing Bitcoin client-side functionality in JavaScript, | ||
most notably ECDSA signing and verification. | ||
[![Build Status](https://travis-ci.org/bitcoinjs/bitcoinjs-lib.png?branch=master)](https://travis-ci.org/bitcoinjs/bitcoinjs-lib) | ||
# Status | ||
[![Browser Support](https://ci.testling.com/bitcoinjs/bitcoinjs-lib.png)](https://ci.testling.com/bitcoinjs/bitcoinjs-lib) | ||
This is currently pretty raw code. We're planning to clean it up, | ||
convert everything into CommonJS modules and put a flexible build | ||
system in place. | ||
A pure JavaScript Bitcoin library for node.js and browsers. Backed by (slowly improving) testing, proven by over a million wallet users. The backbone for almost all Bitcoin web wallets in production today. | ||
Prototype software, use at your own peril. | ||
**Warning**: Master is not stable. Expect the interface to change rapidly, including some of the examples below. This is not the original bitcoinjs-lib that was not updated for a while. The current bitcoinjs-lib has been refactored to clean things up, add new functionality and merge improvements from the community. If you are looking for the original, it will be tagged as `0.1.3`. We will use `0.2.x` for releases based on these changes, so be sure to use the `0.1.3` tag if you need the original version. | ||
# License | ||
## Features | ||
This library is free and open-source software released under the MIT | ||
license. | ||
- Bitcoin Testnet and Mainnet (production) support | ||
- [HD Wallets](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) | ||
- Highly secure random private key / address generation using [window.crypto.getRandomValues](https://developer.mozilla.org/en-US/docs/Web/API/Window.crypto) | ||
- ECDSA signing and verification | ||
- Transaction creation (pay-to-pubkey-hash), support for multisignature transactions | ||
- A (somewhat incomplete) wallet implementation, improvements ongoing | ||
# Copyright | ||
## Installation | ||
BitcoinJS (c) 2011-2012 Stefan Thomas | ||
Released under MIT license | ||
`npm install bitcoinjs-lib` | ||
Note: The npm version is currently out of date, are working to resolve this. The best way to use the latest code is to clone the repository. | ||
## Setup | ||
### Node.js | ||
var bitcoin = require('bitcoinjs-lib') | ||
From the repo: | ||
var bitcoin = require('./src/index.js') | ||
### Browser | ||
Compile `bitcoinjs-min.js` with the following command: | ||
$ npm run-script compile | ||
After loading this file in your browser, you will be able to use the global `Bitcoin` object. | ||
## Usage | ||
These examples assume you are running bitcoinjs-lib in the browser. | ||
### Generating a Bitcoin address | ||
```javascript | ||
key = new Bitcoin.ECKey() | ||
// Print your private key (a hex string) | ||
console.log(key.toString()) | ||
// => 8c112cf628362ecf4d482f68af2dbb50c8a2cb90d226215de925417aa9336a48 | ||
// Print your public key (defaults to a Bitcoin address) | ||
console.log(key.getPub().getAddress()) | ||
// => 14bZ7YWde4KdRb5YN7GYkToz3EHVCvRxkF | ||
``` | ||
### Creating a Transaction | ||
```javascript | ||
tx = new Bitcoin.Transaction() | ||
// Add the input (who is paying) of the form [previous transaction hash, index of the output to use] | ||
tx.addInput("aa94ab02c182214f090e99a0d57021caffd0f195a81c24602b1028b130b63e31", 0) | ||
// Add the output (who to pay to) of the form [payee's address, amount in satoshis] | ||
tx.addOutput("1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK", 15000) | ||
// Initialize a private key using hex | ||
key = new Bitcoin.ECKey("8c112cf628362ecf4d482f68af2dbb50c8a2cb90d226215de925417aa9336a48") | ||
// Sign the first input with the new key | ||
tx.sign(0, key) | ||
// Print transaction serialized as hex | ||
console.log(tx.serializeHex()) | ||
// => 0100000001313eb630b128102b60241ca895f1d0ffca2170d5a0990e094f2182c102ab94aa000000008a47304402200169f1f844936dc60df54e812345f5dd3e6681fea52e33c25154ad9cc23a330402204381ed8e73d74a95b15f312f33d5a0072c7a12dd6c3294df6e8efbe4aff27426014104e75628573696aed32d7656fb35e9c71ea08eb6492837e13d2662b9a36821d0fff992692fd14d74fdec20fae29128ba12653249cbeef521fc5eba84dde0689f27ffffffff01983a0000000000001976a914ad618cf4333b3b248f9744e8e81db2964d0ae39788ac00000000 | ||
// You could now push the transaction onto the Bitcoin network manually (see https://blockchain.info/pushtx) | ||
``` | ||
## Projects utilizing bitcoinjs-lib | ||
- [Blockchain.info Wallet](http://blockchain.info/wallet) | ||
- [Bitaddress.org](https://www.bitaddress.org) | ||
- [Coinpunk](https://coinpunk.com) | ||
- [DarkWallet](https://darkwallet.unsystem.net) | ||
- [GreenAddress](https://greenaddress.it) | ||
Feel free to send pull requests to have your project/startup listed here. | ||
## Contributing | ||
### Instructions | ||
1. Fork the repo | ||
2. Push changes to your fork | ||
3. Create a pull request | ||
### Running the test suite | ||
$ npm test | ||
## Alternatives | ||
- [Bitcore](https://github.com/bitpay/bitcore) | ||
- [Cryptocoin](https://github.com/cryptocoinjs/cryptocoin) | ||
## License | ||
This library is free and open-source software released under the MIT license. | ||
## Copyright | ||
BitcoinJS (c) 2011-2012 Stefan Thomas | ||
Released under MIT license | ||
http://bitcoinjs.org/ | ||
JSBN (c) 2003-2005 Tom Wu | ||
Released under BSD license | ||
JSBN (c) 2003-2005 Tom Wu | ||
Released under BSD license | ||
http://www-cs-students.stanford.edu/~tjw/jsbn/ | ||
CryptoJS (c) 2009–2012 by Jeff Mott | ||
Released under New BSD license | ||
CryptoJS (c) 2009–2012 by Jeff Mott | ||
Released under New BSD license | ||
http://code.google.com/p/crypto-js/ | ||
@@ -1,57 +0,65 @@ | ||
Bitcoin.Address = function (bytes) { | ||
if ("string" == typeof bytes) { | ||
bytes = Bitcoin.Address.decodeString(bytes); | ||
var base58 = require('./base58') | ||
var base58check = require('./base58check') | ||
var convert = require('./convert') | ||
var error = require('./util').error | ||
var mainnet = require('./network').mainnet.addressVersion | ||
function Address(bytes, version) { | ||
if (!(this instanceof Address)) { | ||
return new Address(bytes, version) | ||
} | ||
this.hash = bytes; | ||
this.version = 0x00; | ||
}; | ||
if (bytes instanceof Address) { | ||
this.hash = bytes.hash | ||
this.version = bytes.version | ||
} | ||
else if (typeof bytes === 'string') { | ||
if (bytes.length <= 35) { | ||
var decode = base58check.decode(bytes) | ||
this.hash = decode.payload | ||
this.version = decode.version | ||
} | ||
else if (bytes.length <= 40) { | ||
this.hash = convert.hexToBytes(bytes) | ||
this.version = version || mainnet | ||
} | ||
else { | ||
error('invalid or unrecognized input') | ||
} | ||
} | ||
else { | ||
this.hash = bytes | ||
this.version = version || mainnet | ||
} | ||
} | ||
/** | ||
* Serialize this object as a standard Bitcoin address. | ||
* | ||
* Returns the address as a base58-encoded string in the standardized format. | ||
*/ | ||
Bitcoin.Address.prototype.toString = function () { | ||
// Get a copy of the hash | ||
var hash = this.hash.slice(0); | ||
Address.prototype.toString = function () { | ||
return base58check.encode(this.hash.slice(0), this.version) | ||
} | ||
// Version | ||
hash.unshift(this.version); | ||
/** | ||
* Returns the version of an address, e.g. if the address belongs to the main | ||
* net or the test net. | ||
*/ | ||
Address.getVersion = function (address) { | ||
return base58.decode(address)[0] | ||
} | ||
var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true}); | ||
var bytes = hash.concat(checksum.slice(0,4)); | ||
return Bitcoin.Base58.encode(bytes); | ||
}; | ||
Bitcoin.Address.prototype.getHashBase64 = function () { | ||
return Crypto.util.bytesToBase64(this.hash); | ||
}; | ||
/** | ||
* Parse a Bitcoin address contained in a string. | ||
* Returns true if a bitcoin address is a valid address, otherwise false. | ||
*/ | ||
Bitcoin.Address.decodeString = function (string) { | ||
var bytes = Bitcoin.Base58.decode(string); | ||
var hash = bytes.slice(0, 21); | ||
var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true}); | ||
if (checksum[0] != bytes[21] || | ||
checksum[1] != bytes[22] || | ||
checksum[2] != bytes[23] || | ||
checksum[3] != bytes[24]) { | ||
throw "Checksum validation failed!"; | ||
Address.validate = function (address) { | ||
try { | ||
base58check.decode(address) | ||
return true | ||
} catch (e) { | ||
return false | ||
} | ||
} | ||
var version = hash.shift(); | ||
if (version != 0) { | ||
throw "Version "+version+" not supported!"; | ||
} | ||
return hash; | ||
}; | ||
module.exports = Address |
@@ -1,71 +0,82 @@ | ||
(function (Bitcoin) { | ||
Bitcoin.Base58 = { | ||
alphabet: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz", | ||
validRegex: /^[1-9A-HJ-NP-Za-km-z]+$/, | ||
base: BigInteger.valueOf(58), | ||
// Base58 encoding/decoding | ||
// Originally written by Mike Hearn for BitcoinJ | ||
// Copyright (c) 2011 Google Inc | ||
// Ported to JavaScript by Stefan Thomas | ||
/** | ||
* Convert a byte array to a base58-encoded string. | ||
* | ||
* Written by Mike Hearn for BitcoinJ. | ||
* Copyright (c) 2011 Google Inc. | ||
* | ||
* Ported to JavaScript by Stefan Thomas. | ||
*/ | ||
encode: function (input) { | ||
var bi = BigInteger.fromByteArrayUnsigned(input); | ||
var chars = []; | ||
var BigInteger = require('./jsbn/jsbn') | ||
while (bi.compareTo(B58.base) >= 0) { | ||
var mod = bi.mod(B58.base); | ||
chars.unshift(B58.alphabet[mod.intValue()]); | ||
bi = bi.subtract(mod).divide(B58.base); | ||
} | ||
chars.unshift(B58.alphabet[bi.intValue()]); | ||
// FIXME: ? This is a Base58Check alphabet | ||
var alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" | ||
var base = BigInteger.valueOf(58) | ||
// Convert leading zeros too. | ||
for (var i = 0; i < input.length; i++) { | ||
if (input[i] == 0x00) { | ||
chars.unshift(B58.alphabet[0]); | ||
} else break; | ||
} | ||
var alphabetMap = {} | ||
for (var i=0; i<alphabet.length; ++i) { | ||
var chr = alphabet[i] | ||
alphabetMap[chr] = BigInteger.valueOf(i) | ||
} | ||
return chars.join(''); | ||
}, | ||
// encode a byte array into a base58 encoded String | ||
// @return String | ||
function encode(buffer) { | ||
var bi = BigInteger.fromByteArrayUnsigned(buffer) | ||
var chars = [] | ||
/** | ||
* Convert a base58-encoded string to a byte array. | ||
* | ||
* Written by Mike Hearn for BitcoinJ. | ||
* Copyright (c) 2011 Google Inc. | ||
* | ||
* Ported to JavaScript by Stefan Thomas. | ||
*/ | ||
decode: function (input) { | ||
var bi = BigInteger.valueOf(0); | ||
var leadingZerosNum = 0; | ||
for (var i = input.length - 1; i >= 0; i--) { | ||
var alphaIndex = B58.alphabet.indexOf(input[i]); | ||
if (alphaIndex < 0) { | ||
throw "Invalid character"; | ||
} | ||
bi = bi.add(BigInteger.valueOf(alphaIndex) | ||
.multiply(B58.base.pow(input.length - 1 -i))); | ||
while (bi.compareTo(base) >= 0) { | ||
var mod = bi.mod(base) | ||
bi = bi.subtract(mod).divide(base) | ||
// This counts leading zero bytes | ||
if (input[i] == "1") leadingZerosNum++; | ||
else leadingZerosNum = 0; | ||
} | ||
var bytes = bi.toByteArrayUnsigned(); | ||
chars.push(alphabet[mod.intValue()]) | ||
} | ||
// Add leading zeros | ||
while (leadingZerosNum-- > 0) bytes.unshift(0); | ||
chars.push(alphabet[bi.intValue()]) | ||
return bytes; | ||
// Convert leading zeros too. | ||
for (var i=0; i<buffer.length; i++) { | ||
if (buffer[i] !== 0x00) break | ||
chars.push(alphabet[0]) | ||
} | ||
return chars.reverse().join('') | ||
} | ||
// decode a base58 encoded String into a byte array | ||
// @return Array | ||
function decode(str) { | ||
var num = BigInteger.valueOf(0) | ||
var leading_zero = 0 | ||
var seen_other = false | ||
for (var i=0; i<str.length; ++i) { | ||
var chr = str[i] | ||
var bi = alphabetMap[chr] | ||
// if we encounter an invalid character, decoding fails | ||
if (bi === undefined) { | ||
throw new Error('invalid base58 string: ' + str) | ||
} | ||
}; | ||
var B58 = Bitcoin.Base58; | ||
})( | ||
'undefined' != typeof Bitcoin ? Bitcoin : module.exports | ||
); | ||
num = num.multiply(base).add(bi) | ||
if (chr === '1' && !seen_other) { | ||
++leading_zero | ||
} else { | ||
seen_other = true | ||
} | ||
} | ||
var bytes = num.toByteArrayUnsigned() | ||
// remove leading zeros | ||
while (leading_zero-- > 0) { | ||
bytes.unshift(0) | ||
} | ||
return new Buffer(bytes) | ||
} | ||
module.exports = { | ||
encode: encode, | ||
decode: decode | ||
} |
652
src/ecdsa.js
@@ -1,475 +0,299 @@ | ||
function integerToBytes(i, len) { | ||
var bytes = i.toByteArrayUnsigned(); | ||
var sec = require('./jsbn/sec') | ||
var rng = require('secure-random') | ||
var BigInteger = require('./jsbn/jsbn') | ||
var convert = require('./convert') | ||
var HmacSHA256 = require('crypto-js/hmac-sha256') | ||
var ECPointFp = require('./jsbn/ec').ECPointFp | ||
var ecparams = sec("secp256k1") | ||
var P_OVER_FOUR = null | ||
if (len < bytes.length) { | ||
bytes = bytes.slice(bytes.length-len); | ||
} else while (len > bytes.length) { | ||
bytes.unshift(0); | ||
} | ||
function implShamirsTrick(P, k, Q, l) { | ||
var m = Math.max(k.bitLength(), l.bitLength()) | ||
var Z = P.add2D(Q) | ||
var R = P.curve.getInfinity() | ||
return bytes; | ||
}; | ||
for (var i = m - 1; i >= 0; --i) { | ||
R = R.twice2D() | ||
ECFieldElementFp.prototype.getByteLength = function () { | ||
return Math.floor((this.toBigInteger().bitLength() + 7) / 8); | ||
}; | ||
R.z = BigInteger.ONE | ||
ECPointFp.prototype.getEncoded = function (compressed) { | ||
var x = this.getX().toBigInteger(); | ||
var y = this.getY().toBigInteger(); | ||
// Get value as a 32-byte Buffer | ||
// Fixed length based on a patch by bitaddress.org and Casascius | ||
var enc = integerToBytes(x, 32); | ||
if (compressed) { | ||
if (y.isEven()) { | ||
// Compressed even pubkey | ||
// M = 02 || X | ||
enc.unshift(0x02); | ||
if (k.testBit(i)) { | ||
if (l.testBit(i)) { | ||
R = R.add2D(Z) | ||
} else { | ||
R = R.add2D(P) | ||
} | ||
} else { | ||
// Compressed uneven pubkey | ||
// M = 03 || X | ||
enc.unshift(0x03); | ||
if (l.testBit(i)) { | ||
R = R.add2D(Q) | ||
} | ||
} | ||
} else { | ||
// Uncompressed pubkey | ||
// M = 04 || X || Y | ||
enc.unshift(0x04); | ||
enc = enc.concat(integerToBytes(y, 32)); | ||
} | ||
return enc; | ||
}; | ||
ECPointFp.decodeFrom = function (curve, enc) { | ||
var type = enc[0]; | ||
var dataLen = enc.length-1; | ||
return R | ||
} | ||
// Extract x and y as byte arrays | ||
var xBa = enc.slice(1, 1 + dataLen/2); | ||
var yBa = enc.slice(1 + dataLen/2, 1 + dataLen); | ||
function deterministicGenerateK(hash,key) { | ||
var vArr = [] | ||
var kArr = [] | ||
for (var i = 0;i < 32;i++) vArr.push(1) | ||
for (var i = 0;i < 32;i++) kArr.push(0) | ||
var v = convert.bytesToWordArray(vArr) | ||
var k = convert.bytesToWordArray(kArr) | ||
// Prepend zero byte to prevent interpretation as negative integer | ||
xBa.unshift(0); | ||
yBa.unshift(0); | ||
k = HmacSHA256(convert.bytesToWordArray(vArr.concat([0]).concat(key).concat(hash)), k) | ||
v = HmacSHA256(v, k) | ||
vArr = convert.wordArrayToBytes(v) | ||
k = HmacSHA256(convert.bytesToWordArray(vArr.concat([1]).concat(key).concat(hash)), k) | ||
v = HmacSHA256(v,k) | ||
v = HmacSHA256(v,k) | ||
vArr = convert.wordArrayToBytes(v) | ||
return BigInteger.fromByteArrayUnsigned(vArr) | ||
} | ||
// Convert to BigIntegers | ||
var x = new BigInteger(xBa); | ||
var y = new BigInteger(yBa); | ||
var ECDSA = { | ||
getBigRandom: function (limit) { | ||
return new BigInteger(limit.bitLength(), rng). | ||
mod(limit.subtract(BigInteger.ONE)). | ||
add(BigInteger.ONE) | ||
}, | ||
sign: function (hash, priv) { | ||
var d = priv | ||
var n = ecparams.getN() | ||
var e = BigInteger.fromByteArrayUnsigned(hash) | ||
// Return point | ||
return new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y)); | ||
}; | ||
var k = deterministicGenerateK(hash,priv.toByteArrayUnsigned()) | ||
var G = ecparams.getG() | ||
var Q = G.multiply(k) | ||
var r = Q.getX().toBigInteger().mod(n) | ||
ECPointFp.prototype.add2D = function (b) { | ||
if(this.isInfinity()) return b; | ||
if(b.isInfinity()) return this; | ||
var s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n) | ||
if (this.x.equals(b.x)) { | ||
if (this.y.equals(b.y)) { | ||
// this = b, i.e. this must be doubled | ||
return this.twice(); | ||
return ECDSA.serializeSig(r, s) | ||
}, | ||
verify: function (hash, sig, pubkey) { | ||
var r,s | ||
if (Array.isArray(sig)) { | ||
var obj = ECDSA.parseSig(sig) | ||
r = obj.r | ||
s = obj.s | ||
} else if ("object" === typeof sig && sig.r && sig.s) { | ||
r = sig.r | ||
s = sig.s | ||
} else { | ||
throw new Error("Invalid value for signature") | ||
} | ||
// this = -b, i.e. the result is the point at infinity | ||
return this.curve.getInfinity(); | ||
} | ||
var x_x = b.x.subtract(this.x); | ||
var y_y = b.y.subtract(this.y); | ||
var gamma = y_y.divide(x_x); | ||
var Q | ||
if (pubkey instanceof ECPointFp) { | ||
Q = pubkey | ||
} else if (Array.isArray(pubkey)) { | ||
Q = ECPointFp.decodeFrom(ecparams.getCurve(), pubkey) | ||
} else { | ||
throw new Error("Invalid format for pubkey value, must be byte array or ECPointFp") | ||
} | ||
var e = BigInteger.fromByteArrayUnsigned(hash) | ||
var x3 = gamma.square().subtract(this.x).subtract(b.x); | ||
var y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y); | ||
return ECDSA.verifyRaw(e, r, s, Q) | ||
}, | ||
return new ECPointFp(this.curve, x3, y3); | ||
}; | ||
verifyRaw: function (e, r, s, Q) { | ||
var n = ecparams.getN() | ||
var G = ecparams.getG() | ||
ECPointFp.prototype.twice2D = function () { | ||
if (this.isInfinity()) return this; | ||
if (this.y.toBigInteger().signum() == 0) { | ||
// if y1 == 0, then (x1, y1) == (x1, -y1) | ||
// and hence this = -this and thus 2(x1, y1) == infinity | ||
return this.curve.getInfinity(); | ||
} | ||
if (r.compareTo(BigInteger.ONE) < 0 || r.compareTo(n) >= 0) { | ||
return false | ||
} | ||
var TWO = this.curve.fromBigInteger(BigInteger.valueOf(2)); | ||
var THREE = this.curve.fromBigInteger(BigInteger.valueOf(3)); | ||
var gamma = this.x.square().multiply(THREE).add(this.curve.a).divide(this.y.multiply(TWO)); | ||
var x3 = gamma.square().subtract(this.x.multiply(TWO)); | ||
var y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y); | ||
return new ECPointFp(this.curve, x3, y3); | ||
}; | ||
ECPointFp.prototype.multiply2D = function (k) { | ||
if(this.isInfinity()) return this; | ||
if(k.signum() == 0) return this.curve.getInfinity(); | ||
var e = k; | ||
var h = e.multiply(new BigInteger("3")); | ||
var neg = this.negate(); | ||
var R = this; | ||
var i; | ||
for (i = h.bitLength() - 2; i > 0; --i) { | ||
R = R.twice(); | ||
var hBit = h.testBit(i); | ||
var eBit = e.testBit(i); | ||
if (hBit != eBit) { | ||
R = R.add2D(hBit ? this : neg); | ||
if (s.compareTo(BigInteger.ONE) < 0 || s.compareTo(n) >= 0) { | ||
return false | ||
} | ||
} | ||
return R; | ||
}; | ||
var c = s.modInverse(n) | ||
ECPointFp.prototype.isOnCurve = function () { | ||
var x = this.getX().toBigInteger(); | ||
var y = this.getY().toBigInteger(); | ||
var a = this.curve.getA().toBigInteger(); | ||
var b = this.curve.getB().toBigInteger(); | ||
var n = this.curve.getQ(); | ||
var lhs = y.multiply(y).mod(n); | ||
var rhs = x.multiply(x).multiply(x) | ||
.add(a.multiply(x)).add(b).mod(n); | ||
return lhs.equals(rhs); | ||
}; | ||
var u1 = e.multiply(c).mod(n) | ||
var u2 = r.multiply(c).mod(n) | ||
ECPointFp.prototype.toString = function () { | ||
return '('+this.getX().toBigInteger().toString()+','+ | ||
this.getY().toBigInteger().toString()+')'; | ||
}; | ||
// TODO(!!!): For some reason Shamir's trick isn't working with | ||
// signed message verification!? Probably an implementation | ||
// error! | ||
//var point = implShamirsTrick(G, u1, Q, u2) | ||
var point = G.multiply(u1).add(Q.multiply(u2)) | ||
/** | ||
* Validate an elliptic curve point. | ||
* | ||
* See SEC 1, section 3.2.2.1: Elliptic Curve Public Key Validation Primitive | ||
*/ | ||
ECPointFp.prototype.validate = function () { | ||
var n = this.curve.getQ(); | ||
var v = point.getX().toBigInteger().mod(n) | ||
// Check Q != O | ||
if (this.isInfinity()) { | ||
throw new Error("Point is at infinity."); | ||
} | ||
return v.equals(r) | ||
}, | ||
// Check coordinate bounds | ||
var x = this.getX().toBigInteger(); | ||
var y = this.getY().toBigInteger(); | ||
if (x.compareTo(BigInteger.ONE) < 0 || | ||
x.compareTo(n.subtract(BigInteger.ONE)) > 0) { | ||
throw new Error('x coordinate out of bounds'); | ||
} | ||
if (y.compareTo(BigInteger.ONE) < 0 || | ||
y.compareTo(n.subtract(BigInteger.ONE)) > 0) { | ||
throw new Error('y coordinate out of bounds'); | ||
} | ||
/** | ||
* Serialize a signature into DER format. | ||
* | ||
* Takes two BigIntegers representing r and s and returns a byte array. | ||
*/ | ||
serializeSig: function (r, s) { | ||
var rBa = r.toByteArraySigned() | ||
var sBa = s.toByteArraySigned() | ||
// Check y^2 = x^3 + ax + b (mod n) | ||
if (!this.isOnCurve()) { | ||
throw new Error("Point is not on the curve."); | ||
} | ||
var sequence = [] | ||
sequence.push(0x02); // INTEGER | ||
sequence.push(rBa.length) | ||
sequence = sequence.concat(rBa) | ||
// Check nQ = 0 (Q is a scalar multiple of G) | ||
if (this.multiply(n).isInfinity()) { | ||
// TODO: This check doesn't work - fix. | ||
throw new Error("Point is not a scalar multiple of G."); | ||
} | ||
sequence.push(0x02); // INTEGER | ||
sequence.push(sBa.length) | ||
sequence = sequence.concat(sBa) | ||
return true; | ||
}; | ||
sequence.unshift(sequence.length) | ||
sequence.unshift(0x30); // SEQUENCE | ||
function dmp(v) { | ||
if (!(v instanceof BigInteger)) v = v.toBigInteger(); | ||
return Crypto.util.bytesToHex(v.toByteArrayUnsigned()); | ||
}; | ||
return sequence | ||
}, | ||
Bitcoin.ECDSA = (function () { | ||
var ecparams = getSECCurveByName("secp256k1"); | ||
var rng = new SecureRandom(); | ||
/** | ||
* Parses a byte array containing a DER-encoded signature. | ||
* | ||
* This function will return an object of the form: | ||
* | ||
* { | ||
* r: BigInteger, | ||
* s: BigInteger | ||
* } | ||
*/ | ||
parseSig: function (sig) { | ||
var cursor | ||
if (sig[0] != 0x30) { | ||
throw new Error("Signature not a valid DERSequence") | ||
} | ||
var P_OVER_FOUR = null; | ||
cursor = 2 | ||
if (sig[cursor] != 0x02) { | ||
throw new Error("First element in signature must be a DERInteger") | ||
} | ||
var rBa = sig.slice(cursor+2, cursor+2+sig[cursor+1]) | ||
function implShamirsTrick(P, k, Q, l) | ||
{ | ||
var m = Math.max(k.bitLength(), l.bitLength()); | ||
var Z = P.add2D(Q); | ||
var R = P.curve.getInfinity(); | ||
for (var i = m - 1; i >= 0; --i) { | ||
R = R.twice2D(); | ||
R.z = BigInteger.ONE; | ||
if (k.testBit(i)) { | ||
if (l.testBit(i)) { | ||
R = R.add2D(Z); | ||
} else { | ||
R = R.add2D(P); | ||
} | ||
} else { | ||
if (l.testBit(i)) { | ||
R = R.add2D(Q); | ||
} | ||
} | ||
cursor += 2+sig[cursor+1] | ||
if (sig[cursor] != 0x02) { | ||
throw new Error("Second element in signature must be a DERInteger") | ||
} | ||
var sBa = sig.slice(cursor+2, cursor+2+sig[cursor+1]) | ||
return R; | ||
}; | ||
cursor += 2+sig[cursor+1] | ||
var ECDSA = { | ||
getBigRandom: function (limit) { | ||
return new BigInteger(limit.bitLength(), rng) | ||
.mod(limit.subtract(BigInteger.ONE)) | ||
.add(BigInteger.ONE) | ||
; | ||
}, | ||
sign: function (hash, priv) { | ||
var d = priv; | ||
var n = ecparams.getN(); | ||
var e = BigInteger.fromByteArrayUnsigned(hash); | ||
//if (cursor != sig.length) | ||
// throw new Error("Extra bytes in signature") | ||
do { | ||
var k = ECDSA.getBigRandom(n); | ||
var G = ecparams.getG(); | ||
var Q = G.multiply(k); | ||
var r = Q.getX().toBigInteger().mod(n); | ||
} while (r.compareTo(BigInteger.ZERO) <= 0); | ||
var r = BigInteger.fromByteArrayUnsigned(rBa) | ||
var s = BigInteger.fromByteArrayUnsigned(sBa) | ||
var s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n); | ||
return {r: r, s: s} | ||
}, | ||
return ECDSA.serializeSig(r, s); | ||
}, | ||
parseSigCompact: function (sig) { | ||
if (sig.length !== 65) { | ||
throw new Error("Signature has the wrong length") | ||
} | ||
verify: function (hash, sig, pubkey) { | ||
var r,s; | ||
if (Bitcoin.Util.isArray(sig)) { | ||
var obj = ECDSA.parseSig(sig); | ||
r = obj.r; | ||
s = obj.s; | ||
} else if ("object" === typeof sig && sig.r && sig.s) { | ||
r = sig.r; | ||
s = sig.s; | ||
} else { | ||
throw "Invalid value for signature"; | ||
} | ||
// Signature is prefixed with a type byte storing three bits of | ||
// information. | ||
var i = sig[0] - 27 | ||
if (i < 0 || i > 7) { | ||
throw new Error("Invalid signature type") | ||
} | ||
var Q; | ||
if (pubkey instanceof ECPointFp) { | ||
Q = pubkey; | ||
} else if (Bitcoin.Util.isArray(pubkey)) { | ||
Q = ECPointFp.decodeFrom(ecparams.getCurve(), pubkey); | ||
} else { | ||
throw "Invalid format for pubkey value, must be byte array or ECPointFp"; | ||
} | ||
var e = BigInteger.fromByteArrayUnsigned(hash); | ||
var n = ecparams.getN() | ||
var r = BigInteger.fromByteArrayUnsigned(sig.slice(1, 33)).mod(n) | ||
var s = BigInteger.fromByteArrayUnsigned(sig.slice(33, 65)).mod(n) | ||
return ECDSA.verifyRaw(e, r, s, Q); | ||
}, | ||
return {r: r, s: s, i: i} | ||
}, | ||
verifyRaw: function (e, r, s, Q) { | ||
var n = ecparams.getN(); | ||
var G = ecparams.getG(); | ||
/** | ||
* Recover a public key from a signature. | ||
* | ||
* See SEC 1: Elliptic Curve Cryptography, section 4.1.6, "Public | ||
* Key Recovery Operation". | ||
* | ||
* http://www.secg.org/download/aid-780/sec1-v2.pdf | ||
*/ | ||
recoverPubKey: function (r, s, hash, i) { | ||
// The recovery parameter i has two bits. | ||
i = i & 3 | ||
if (r.compareTo(BigInteger.ONE) < 0 || | ||
r.compareTo(n) >= 0) | ||
return false; | ||
// The less significant bit specifies whether the y coordinate | ||
// of the compressed point is even or not. | ||
var isYEven = i & 1 | ||
if (s.compareTo(BigInteger.ONE) < 0 || | ||
s.compareTo(n) >= 0) | ||
return false; | ||
// The more significant bit specifies whether we should use the | ||
// first or second candidate key. | ||
var isSecondKey = i >> 1 | ||
var c = s.modInverse(n); | ||
var n = ecparams.getN() | ||
var G = ecparams.getG() | ||
var curve = ecparams.getCurve() | ||
var p = curve.getQ() | ||
var a = curve.getA().toBigInteger() | ||
var b = curve.getB().toBigInteger() | ||
var u1 = e.multiply(c).mod(n); | ||
var u2 = r.multiply(c).mod(n); | ||
// We precalculate (p + 1) / 4 where p is if the field order | ||
if (!P_OVER_FOUR) { | ||
P_OVER_FOUR = p.add(BigInteger.ONE).divide(BigInteger.valueOf(4)) | ||
} | ||
// TODO(!!!): For some reason Shamir's trick isn't working with | ||
// signed message verification!? Probably an implementation | ||
// error! | ||
//var point = implShamirsTrick(G, u1, Q, u2); | ||
var point = G.multiply(u1).add(Q.multiply(u2)); | ||
// 1.1 Compute x | ||
var x = isSecondKey ? r.add(n) : r | ||
var v = point.getX().toBigInteger().mod(n); | ||
// 1.3 Convert x to point | ||
var alpha = x.multiply(x).multiply(x).add(a.multiply(x)).add(b).mod(p) | ||
var beta = alpha.modPow(P_OVER_FOUR, p) | ||
return v.equals(r); | ||
}, | ||
// var xorOdd = beta.isEven() ? (i % 2) : ((i+1) % 2) | ||
// If beta is even, but y isn't or vice versa, then convert it, | ||
// otherwise we're done and y == beta. | ||
var y = (beta.isEven() ? !isYEven : isYEven) ? beta : p.subtract(beta) | ||
/** | ||
* Serialize a signature into DER format. | ||
* | ||
* Takes two BigIntegers representing r and s and returns a byte array. | ||
*/ | ||
serializeSig: function (r, s) { | ||
var rBa = r.toByteArraySigned(); | ||
var sBa = s.toByteArraySigned(); | ||
// 1.4 Check that nR is at infinity | ||
var R = new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y)) | ||
R.validate() | ||
var sequence = []; | ||
sequence.push(0x02); // INTEGER | ||
sequence.push(rBa.length); | ||
sequence = sequence.concat(rBa); | ||
// 1.5 Compute e from M | ||
var e = BigInteger.fromByteArrayUnsigned(hash) | ||
var eNeg = BigInteger.ZERO.subtract(e).mod(n) | ||
sequence.push(0x02); // INTEGER | ||
sequence.push(sBa.length); | ||
sequence = sequence.concat(sBa); | ||
// 1.6 Compute Q = r^-1 (sR - eG) | ||
var rInv = r.modInverse(n) | ||
var Q = implShamirsTrick(R, s, G, eNeg).multiply(rInv) | ||
sequence.unshift(sequence.length); | ||
sequence.unshift(0x30); // SEQUENCE | ||
Q.validate() | ||
if (!ECDSA.verifyRaw(e, r, s, Q)) { | ||
throw new Error("Pubkey recovery unsuccessful") | ||
} | ||
return sequence; | ||
}, | ||
return Q | ||
}, | ||
/** | ||
* Parses a byte array containing a DER-encoded signature. | ||
* | ||
* This function will return an object of the form: | ||
* | ||
* { | ||
* r: BigInteger, | ||
* s: BigInteger | ||
* } | ||
*/ | ||
parseSig: function (sig) { | ||
var cursor; | ||
if (sig[0] != 0x30) | ||
throw new Error("Signature not a valid DERSequence"); | ||
/** | ||
* Calculate pubkey extraction parameter. | ||
* | ||
* When extracting a pubkey from a signature, we have to | ||
* distinguish four different cases. Rather than putting this | ||
* burden on the verifier, Bitcoin includes a 2-bit value with the | ||
* signature. | ||
* | ||
* This function simply tries all four cases and returns the value | ||
* that resulted in a successful pubkey recovery. | ||
*/ | ||
calcPubKeyRecoveryParam: function (origPubKey, r, s, hash) { | ||
for (var i = 0; i < 4; i++) { | ||
var pubKey = ECDSA.recoverPubKey(r, s, hash, i) | ||
cursor = 2; | ||
if (sig[cursor] != 0x02) | ||
throw new Error("First element in signature must be a DERInteger");; | ||
var rBa = sig.slice(cursor+2, cursor+2+sig[cursor+1]); | ||
cursor += 2+sig[cursor+1]; | ||
if (sig[cursor] != 0x02) | ||
throw new Error("Second element in signature must be a DERInteger"); | ||
var sBa = sig.slice(cursor+2, cursor+2+sig[cursor+1]); | ||
cursor += 2+sig[cursor+1]; | ||
//if (cursor != sig.length) | ||
// throw new Error("Extra bytes in signature"); | ||
var r = BigInteger.fromByteArrayUnsigned(rBa); | ||
var s = BigInteger.fromByteArrayUnsigned(sBa); | ||
return {r: r, s: s}; | ||
}, | ||
parseSigCompact: function (sig) { | ||
if (sig.length !== 65) { | ||
throw "Signature has the wrong length"; | ||
if (pubKey.equals(origPubKey)) { | ||
return i | ||
} | ||
} | ||
// Signature is prefixed with a type byte storing three bits of | ||
// information. | ||
var i = sig[0] - 27; | ||
if (i < 0 || i > 7) { | ||
throw "Invalid signature type"; | ||
} | ||
throw new Error("Unable to find valid recovery factor") | ||
} | ||
} | ||
var n = ecparams.getN(); | ||
var r = BigInteger.fromByteArrayUnsigned(sig.slice(1, 33)).mod(n); | ||
var s = BigInteger.fromByteArrayUnsigned(sig.slice(33, 65)).mod(n); | ||
return {r: r, s: s, i: i}; | ||
}, | ||
/** | ||
* Recover a public key from a signature. | ||
* | ||
* See SEC 1: Elliptic Curve Cryptography, section 4.1.6, "Public | ||
* Key Recovery Operation". | ||
* | ||
* http://www.secg.org/download/aid-780/sec1-v2.pdf | ||
*/ | ||
recoverPubKey: function (r, s, hash, i) { | ||
// The recovery parameter i has two bits. | ||
i = i & 3; | ||
// The less significant bit specifies whether the y coordinate | ||
// of the compressed point is even or not. | ||
var isYEven = i & 1; | ||
// The more significant bit specifies whether we should use the | ||
// first or second candidate key. | ||
var isSecondKey = i >> 1; | ||
var n = ecparams.getN(); | ||
var G = ecparams.getG(); | ||
var curve = ecparams.getCurve(); | ||
var p = curve.getQ(); | ||
var a = curve.getA().toBigInteger(); | ||
var b = curve.getB().toBigInteger(); | ||
// We precalculate (p + 1) / 4 where p is if the field order | ||
if (!P_OVER_FOUR) { | ||
P_OVER_FOUR = p.add(BigInteger.ONE).divide(BigInteger.valueOf(4)); | ||
} | ||
// 1.1 Compute x | ||
var x = isSecondKey ? r.add(n) : r; | ||
// 1.3 Convert x to point | ||
var alpha = x.multiply(x).multiply(x).add(a.multiply(x)).add(b).mod(p); | ||
var beta = alpha.modPow(P_OVER_FOUR, p); | ||
var xorOdd = beta.isEven() ? (i % 2) : ((i+1) % 2); | ||
// If beta is even, but y isn't or vice versa, then convert it, | ||
// otherwise we're done and y == beta. | ||
var y = (beta.isEven() ? !isYEven : isYEven) ? beta : p.subtract(beta); | ||
// 1.4 Check that nR is at infinity | ||
var R = new ECPointFp(curve, | ||
curve.fromBigInteger(x), | ||
curve.fromBigInteger(y)); | ||
R.validate(); | ||
// 1.5 Compute e from M | ||
var e = BigInteger.fromByteArrayUnsigned(hash); | ||
var eNeg = BigInteger.ZERO.subtract(e).mod(n); | ||
// 1.6 Compute Q = r^-1 (sR - eG) | ||
var rInv = r.modInverse(n); | ||
var Q = implShamirsTrick(R, s, G, eNeg).multiply(rInv); | ||
Q.validate(); | ||
if (!ECDSA.verifyRaw(e, r, s, Q)) { | ||
throw "Pubkey recovery unsuccessful"; | ||
} | ||
var pubKey = new Bitcoin.ECKey(); | ||
pubKey.pub = Q; | ||
return pubKey; | ||
}, | ||
/** | ||
* Calculate pubkey extraction parameter. | ||
* | ||
* When extracting a pubkey from a signature, we have to | ||
* distinguish four different cases. Rather than putting this | ||
* burden on the verifier, Bitcoin includes a 2-bit value with the | ||
* signature. | ||
* | ||
* This function simply tries all four cases and returns the value | ||
* that resulted in a successful pubkey recovery. | ||
*/ | ||
calcPubkeyRecoveryParam: function (address, r, s, hash) | ||
{ | ||
for (var i = 0; i < 4; i++) { | ||
try { | ||
var pubkey = Bitcoin.ECDSA.recoverPubKey(r, s, hash, i); | ||
if (pubkey.getBitcoinAddress().toString() == address) { | ||
return i; | ||
} | ||
} catch (e) {} | ||
} | ||
throw "Unable to find valid recovery factor"; | ||
} | ||
}; | ||
return ECDSA; | ||
})(); | ||
module.exports = ECDSA |
275
src/eckey.js
@@ -1,131 +0,188 @@ | ||
Bitcoin.ECKey = (function () { | ||
var ECDSA = Bitcoin.ECDSA; | ||
var ecparams = getSECCurveByName("secp256k1"); | ||
var rng = new SecureRandom(); | ||
var Address = require('./address') | ||
var assert = require('assert') | ||
var convert = require('./convert') | ||
var base58check = require('./base58check') | ||
var BigInteger = require('./jsbn/jsbn') | ||
var ecdsa = require('./ecdsa') | ||
var ECPointFp = require('./jsbn/ec').ECPointFp | ||
var sec = require('./jsbn/sec') | ||
var Network = require('./network') | ||
var util = require('./util') | ||
var ecparams = sec("secp256k1") | ||
var ECKey = function (input) { | ||
if (!input) { | ||
// Generate new key | ||
var n = ecparams.getN(); | ||
this.priv = ECDSA.getBigRandom(n); | ||
} else if (input instanceof BigInteger) { | ||
// Input is a private key value | ||
this.priv = input; | ||
} else if (Bitcoin.Util.isArray(input)) { | ||
// Prepend zero byte to prevent interpretation as negative integer | ||
this.priv = BigInteger.fromByteArrayUnsigned(input); | ||
} else if ("string" == typeof input) { | ||
if (input.length == 51 && input[0] == '5') { | ||
// Base58 encoded private key | ||
this.priv = BigInteger.fromByteArrayUnsigned(ECKey.decodeString(input)); | ||
} else { | ||
// Prepend zero byte to prevent interpretation as negative integer | ||
this.priv = BigInteger.fromByteArrayUnsigned(Crypto.util.base64ToBytes(input)); | ||
} | ||
} | ||
this.compressed = !!ECKey.compressByDefault; | ||
}; | ||
// input can be nothing, array of bytes, hex string, or base58 string | ||
var ECKey = function (input, compressed) { | ||
if (!(this instanceof ECKey)) { return new ECKey(input, compressed) } | ||
if (!input) { | ||
// Generate new key | ||
var n = ecparams.getN() | ||
this.priv = ecdsa.getBigRandom(n) | ||
this.compressed = compressed || false | ||
} | ||
else this.import(input,compressed) | ||
} | ||
/** | ||
* Whether public keys should be returned compressed by default. | ||
*/ | ||
ECKey.compressByDefault = false; | ||
ECKey.prototype.import = function (input, compressed) { | ||
function has(li, v) { return li.indexOf(v) >= 0 } | ||
function fromBin(x) { return BigInteger.fromByteArrayUnsigned(x) } | ||
/** | ||
* Set whether the public key should be returned compressed or not. | ||
*/ | ||
ECKey.prototype.setCompressed = function (v) { | ||
this.compressed = !!v; | ||
}; | ||
this.priv = | ||
input instanceof ECKey ? input.priv | ||
: input instanceof BigInteger ? input.mod(ecparams.getN()) | ||
: Array.isArray(input) ? fromBin(input.slice(0, 32)) | ||
: typeof input != "string" ? null | ||
: input.length == 44 ? fromBin(convert.base64ToBytes(input)) | ||
: input.length == 51 && input[0] == '5' ? fromBin(base58check.decode(input).payload) | ||
: input.length == 51 && input[0] == '9' ? fromBin(base58check.decode(input).payload) | ||
: input.length == 52 && has('LK', input[0]) ? fromBin(base58check.decode(input).payload.slice(0, 32)) | ||
: input.length == 52 && input[0] == 'c' ? fromBin(base58check.decode(input).payload.slice(0, 32)) | ||
: has([64,65],input.length) ? fromBin(convert.hexToBytes(input.slice(0, 64))) | ||
: null | ||
/** | ||
* Return public key in DER encoding. | ||
*/ | ||
ECKey.prototype.getPub = function () { | ||
return this.getPubPoint().getEncoded(this.compressed); | ||
}; | ||
assert(this.priv !== null) | ||
/** | ||
* Return public point as ECPoint object. | ||
*/ | ||
ECKey.prototype.getPubPoint = function () { | ||
if (!this.pub) this.pub = ecparams.getG().multiply(this.priv); | ||
this.compressed = | ||
compressed !== undefined ? compressed | ||
: input instanceof ECKey ? input.compressed | ||
: input instanceof BigInteger ? false | ||
: Array.isArray(input) ? false | ||
: typeof input != "string" ? null | ||
: input.length == 44 ? false | ||
: input.length == 51 && input[0] == '5' ? false | ||
: input.length == 51 && input[0] == '9' ? false | ||
: input.length == 52 && has('LK', input[0]) ? true | ||
: input.length == 52 && input[0] == 'c' ? true | ||
: input.length == 64 ? false | ||
: input.length == 65 ? true | ||
: null | ||
return this.pub; | ||
}; | ||
assert(this.compressed !== null) | ||
} | ||
/** | ||
* Get the pubKeyHash for this key. | ||
* | ||
* This is calculated as RIPE160(SHA256([encoded pubkey])) and returned as | ||
* a byte array. | ||
*/ | ||
ECKey.prototype.getPubKeyHash = function () { | ||
if (this.pubKeyHash) return this.pubKeyHash; | ||
ECKey.prototype.getPub = function(compressed) { | ||
if (compressed === undefined) compressed = this.compressed | ||
return ECPubKey(ecparams.getG().multiply(this.priv), compressed) | ||
} | ||
return this.pubKeyHash = Bitcoin.Util.sha256ripe160(this.getPub()); | ||
}; | ||
ECKey.prototype.toBin = function() { | ||
return convert.bytesToString(this.toBytes()) | ||
} | ||
ECKey.prototype.getBitcoinAddress = function () { | ||
var hash = this.getPubKeyHash(); | ||
var addr = new Bitcoin.Address(hash); | ||
return addr; | ||
}; | ||
ECKey.version_bytes = { | ||
0: 128, | ||
111: 239 | ||
} | ||
ECKey.prototype.getExportedPrivateKey = function () { | ||
var hash = this.priv.toByteArrayUnsigned(); | ||
while (hash.length < 32) hash.unshift(0); | ||
hash.unshift(0x80); | ||
var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true}); | ||
var bytes = hash.concat(checksum.slice(0,4)); | ||
return Bitcoin.Base58.encode(bytes); | ||
}; | ||
ECKey.prototype.toWif = function(version) { | ||
version = version || Network.mainnet.addressVersion | ||
ECKey.prototype.setPub = function (pub) { | ||
this.pub = ECPointFp.decodeFrom(ecparams.getCurve(), pub); | ||
}; | ||
return base58check.encode(this.toBytes(), ECKey.version_bytes[version]) | ||
} | ||
ECKey.prototype.toString = function (format) { | ||
if (format === "base64") { | ||
return Crypto.util.bytesToBase64(this.priv.toByteArrayUnsigned()); | ||
} else { | ||
return Crypto.util.bytesToHex(this.priv.toByteArrayUnsigned()); | ||
} | ||
}; | ||
ECKey.prototype.toHex = function() { | ||
return convert.bytesToHex(this.toBytes()) | ||
} | ||
ECKey.prototype.sign = function (hash) { | ||
return ECDSA.sign(hash, this.priv); | ||
}; | ||
ECKey.prototype.toBytes = function() { | ||
var bytes = this.priv.toByteArrayUnsigned() | ||
if (this.compressed) bytes.push(1) | ||
return bytes | ||
} | ||
ECKey.prototype.verify = function (hash, sig) { | ||
return ECDSA.verify(hash, sig, this.getPub()); | ||
}; | ||
ECKey.prototype.toBase64 = function() { | ||
return convert.bytesToBase64(this.toBytes()) | ||
} | ||
/** | ||
* Parse an exported private key contained in a string. | ||
*/ | ||
ECKey.decodeString = function (string) { | ||
var bytes = Bitcoin.Base58.decode(string); | ||
ECKey.prototype.toString = ECKey.prototype.toHex | ||
var hash = bytes.slice(0, 33); | ||
ECKey.prototype.getAddress = function(version) { | ||
return this.getPub().getAddress(version) | ||
} | ||
var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true}); | ||
ECKey.prototype.add = function(key) { | ||
return ECKey(this.priv.add(ECKey(key).priv), this.compressed) | ||
} | ||
if (checksum[0] != bytes[33] || | ||
checksum[1] != bytes[34] || | ||
checksum[2] != bytes[35] || | ||
checksum[3] != bytes[36]) { | ||
throw "Checksum validation failed!"; | ||
} | ||
ECKey.prototype.multiply = function(key) { | ||
return ECKey(this.priv.multiply(ECKey(key).priv), this.compressed) | ||
} | ||
var version = hash.shift(); | ||
ECKey.prototype.sign = function(hash) { | ||
return ecdsa.sign(hash, this.priv) | ||
} | ||
if (version != 0x80) { | ||
throw "Version "+version+" not supported!"; | ||
} | ||
ECKey.prototype.verify = function(hash, sig) { | ||
return this.getPub().verify(hash, sig) | ||
} | ||
return hash; | ||
}; | ||
var ECPubKey = function(input, compressed) { | ||
if (!(this instanceof ECPubKey)) { | ||
return new ECPubKey(input, compressed) | ||
} | ||
return ECKey; | ||
})(); | ||
this.import(input, compressed) | ||
} | ||
ECPubKey.prototype.import = function(input, compressed) { | ||
var decode = function(x) { return ECPointFp.decodeFrom(ecparams.getCurve(), x) } | ||
this.pub = | ||
input instanceof ECPointFp ? input | ||
: input instanceof ECKey ? ecparams.getG().multiply(input.priv) | ||
: input instanceof ECPubKey ? input.pub | ||
: typeof input == "string" ? decode(convert.hexToBytes(input)) | ||
: Array.isArray(input) ? decode(input) | ||
: null | ||
assert(this.pub !== null) | ||
this.compressed = | ||
compressed ? compressed | ||
: input instanceof ECPointFp ? input.compressed | ||
: input instanceof ECPubKey ? input.compressed | ||
: (this.pub[0] < 4) | ||
} | ||
ECPubKey.prototype.add = function(key) { | ||
return ECPubKey(this.pub.add(ECPubKey(key).pub), this.compressed) | ||
} | ||
ECPubKey.prototype.multiply = function(key) { | ||
return ECPubKey(this.pub.multiply(ECKey(key).priv), this.compressed) | ||
} | ||
ECPubKey.prototype.toBytes = function(compressed) { | ||
if (compressed === undefined) compressed = this.compressed | ||
return this.pub.getEncoded(compressed) | ||
} | ||
ECPubKey.prototype.toHex = function(compressed) { | ||
return convert.bytesToHex(this.toBytes(compressed)) | ||
} | ||
ECPubKey.prototype.toBin = function(compressed) { | ||
return convert.bytesToString(this.toBytes(compressed)) | ||
} | ||
ECPubKey.prototype.toWif = function(version) { | ||
version = version || Network.mainnet.addressVersion | ||
return base58check.encode(this.toBytes(), version) | ||
} | ||
ECPubKey.prototype.toString = ECPubKey.prototype.toHex | ||
ECPubKey.prototype.getAddress = function(version) { | ||
version = version || Network.mainnet.addressVersion | ||
return new Address(util.sha256ripe160(this.toBytes()), version) | ||
} | ||
ECPubKey.prototype.verify = function(hash, sig) { | ||
return ecdsa.verify(hash, sig, this.toBytes()) | ||
} | ||
module.exports = { | ||
ECKey: ECKey, | ||
ECPubKey: ECPubKey | ||
} |
@@ -5,3 +5,4 @@ // Basic Javascript Elliptic Curve implementation | ||
// Requires jsbn.js and jsbn2.js | ||
var BigInteger = require('./jsbn'), | ||
sec = require('./sec'); | ||
@@ -318,1 +319,206 @@ // ---------------- | ||
ECCurveFp.prototype.decodePointHex = curveFpDecodePointHex; | ||
// prepends 0 if bytes < len | ||
// cuts off start if bytes > len | ||
function integerToBytes(i, len) { | ||
var bytes = i.toByteArrayUnsigned(); | ||
if (len < bytes.length) { | ||
bytes = bytes.slice(bytes.length-len); | ||
} else while (len > bytes.length) { | ||
bytes.unshift(0); | ||
} | ||
return bytes; | ||
}; | ||
ECFieldElementFp.prototype.getByteLength = function () { | ||
return Math.floor((this.toBigInteger().bitLength() + 7) / 8); | ||
}; | ||
ECPointFp.prototype.getEncoded = function (compressed) { | ||
var x = this.getX().toBigInteger(); | ||
var y = this.getY().toBigInteger(); | ||
// Get value as a 32-byte Buffer | ||
// Fixed length based on a patch by bitaddress.org and Casascius | ||
var enc = integerToBytes(x, 32); | ||
if (compressed) { | ||
if (y.isEven()) { | ||
// Compressed even pubkey | ||
// M = 02 || X | ||
enc.unshift(0x02); | ||
} else { | ||
// Compressed uneven pubkey | ||
// M = 03 || X | ||
enc.unshift(0x03); | ||
} | ||
} else { | ||
// Uncompressed pubkey | ||
// M = 04 || X || Y | ||
enc.unshift(0x04); | ||
enc = enc.concat(integerToBytes(y, 32)); | ||
} | ||
return enc; | ||
}; | ||
ECPointFp.decodeFrom = function (ecparams, enc) { | ||
var type = enc[0]; | ||
var dataLen = enc.length-1; | ||
// Extract x and y as byte arrays | ||
if (type == 4) { | ||
var xBa = enc.slice(1, 1 + dataLen/2), | ||
yBa = enc.slice(1 + dataLen/2, 1 + dataLen), | ||
x = BigInteger.fromByteArrayUnsigned(xBa), | ||
y = BigInteger.fromByteArrayUnsigned(yBa); | ||
} | ||
else { | ||
var xBa = enc.slice(1), | ||
x = BigInteger.fromByteArrayUnsigned(xBa), | ||
p = ecparams.getQ(), | ||
xCubedPlus7 = x.multiply(x).multiply(x).add(new BigInteger('7')).mod(p), | ||
pPlus1Over4 = p.add(new BigInteger('1')) | ||
.divide(new BigInteger('4')), | ||
y = xCubedPlus7.modPow(pPlus1Over4,p); | ||
if (y.mod(new BigInteger('2')).toString() != ''+(type % 2)) { | ||
y = p.subtract(y) | ||
} | ||
} | ||
// Prepend zero byte to prevent interpretation as negative integer | ||
// Convert to BigIntegers | ||
// Return point | ||
return new ECPointFp(ecparams, | ||
ecparams.fromBigInteger(x), | ||
ecparams.fromBigInteger(y)); | ||
}; | ||
ECPointFp.prototype.add2D = function (b) { | ||
if(this.isInfinity()) return b; | ||
if(b.isInfinity()) return this; | ||
if (this.x.equals(b.x)) { | ||
if (this.y.equals(b.y)) { | ||
// this = b, i.e. this must be doubled | ||
return this.twice(); | ||
} | ||
// this = -b, i.e. the result is the point at infinity | ||
return this.curve.getInfinity(); | ||
} | ||
var x_x = b.x.subtract(this.x); | ||
var y_y = b.y.subtract(this.y); | ||
var gamma = y_y.divide(x_x); | ||
var x3 = gamma.square().subtract(this.x).subtract(b.x); | ||
var y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y); | ||
return new ECPointFp(this.curve, x3, y3); | ||
}; | ||
ECPointFp.prototype.twice2D = function () { | ||
if (this.isInfinity()) return this; | ||
if (this.y.toBigInteger().signum() == 0) { | ||
// if y1 == 0, then (x1, y1) == (x1, -y1) | ||
// and hence this = -this and thus 2(x1, y1) == infinity | ||
return this.curve.getInfinity(); | ||
} | ||
var TWO = this.curve.fromBigInteger(BigInteger.valueOf(2)); | ||
var THREE = this.curve.fromBigInteger(BigInteger.valueOf(3)); | ||
var gamma = this.x.square().multiply(THREE).add(this.curve.a).divide(this.y.multiply(TWO)); | ||
var x3 = gamma.square().subtract(this.x.multiply(TWO)); | ||
var y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y); | ||
return new ECPointFp(this.curve, x3, y3); | ||
}; | ||
ECPointFp.prototype.multiply2D = function (k) { | ||
if(this.isInfinity()) return this; | ||
if(k.signum() == 0) return this.curve.getInfinity(); | ||
var e = k; | ||
var h = e.multiply(new BigInteger("3")); | ||
var neg = this.negate(); | ||
var R = this; | ||
var i; | ||
for (i = h.bitLength() - 2; i > 0; --i) { | ||
R = R.twice(); | ||
var hBit = h.testBit(i); | ||
var eBit = e.testBit(i); | ||
if (hBit != eBit) { | ||
R = R.add2D(hBit ? this : neg); | ||
} | ||
} | ||
return R; | ||
}; | ||
ECPointFp.prototype.isOnCurve = function () { | ||
var x = this.getX().toBigInteger(); | ||
var y = this.getY().toBigInteger(); | ||
var a = this.curve.getA().toBigInteger(); | ||
var b = this.curve.getB().toBigInteger(); | ||
var n = this.curve.getQ(); | ||
var lhs = y.multiply(y).mod(n); | ||
var rhs = x.multiply(x).multiply(x) | ||
.add(a.multiply(x)).add(b).mod(n); | ||
return lhs.equals(rhs); | ||
}; | ||
ECPointFp.prototype.toString = function () { | ||
return '('+this.getX().toBigInteger().toString()+','+ | ||
this.getY().toBigInteger().toString()+')'; | ||
}; | ||
/** | ||
* Validate an elliptic curve point. | ||
* | ||
* See SEC 1, section 3.2.2.1: Elliptic Curve Public Key Validation Primitive | ||
*/ | ||
ECPointFp.prototype.validate = function () { | ||
var n = this.curve.getQ(); | ||
// Check Q != O | ||
if (this.isInfinity()) { | ||
throw new Error("Point is at infinity."); | ||
} | ||
// Check coordinate bounds | ||
var x = this.getX().toBigInteger(); | ||
var y = this.getY().toBigInteger(); | ||
if (x.compareTo(BigInteger.ONE) < 0 || | ||
x.compareTo(n.subtract(BigInteger.ONE)) > 0) { | ||
throw new Error('x coordinate out of bounds'); | ||
} | ||
if (y.compareTo(BigInteger.ONE) < 0 || | ||
y.compareTo(n.subtract(BigInteger.ONE)) > 0) { | ||
throw new Error('y coordinate out of bounds'); | ||
} | ||
// Check y^2 = x^3 + ax + b (mod n) | ||
if (!this.isOnCurve()) { | ||
throw new Error("Point is not on the curve."); | ||
} | ||
// Check nQ = 0 (Q is a scalar multiple of G) | ||
if (this.multiply(n).isInfinity()) { | ||
// TODO: This check doesn't work - fix. | ||
throw new Error("Point is not a scalar multiple of G."); | ||
} | ||
return true; | ||
}; | ||
module.exports = ECCurveFp; | ||
module.exports.ECPointFp = ECPointFp; |
@@ -16,8 +16,15 @@ // Copyright (c) 2005 Tom Wu | ||
function BigInteger(a,b,c) { | ||
if(a != null) | ||
if (!(this instanceof BigInteger)) { | ||
return new BigInteger(a, b, c); | ||
} | ||
if(a != null) { | ||
if("number" == typeof a) this.fromNumber(a,b,c); | ||
else if(b == null && "string" != typeof a) this.fromString(a,256); | ||
else this.fromString(a,b); | ||
} | ||
} | ||
var proto = BigInteger.prototype; | ||
// return new, unset BigInteger | ||
@@ -71,2 +78,8 @@ function nbi() { return new BigInteger(null); } | ||
} | ||
// wtf? | ||
BigInteger.prototype.am = am1; | ||
dbits = 26; | ||
/* | ||
if(j_lm && (navigator.appName == "Microsoft Internet Explorer")) { | ||
@@ -84,6 +97,7 @@ BigInteger.prototype.am = am2; | ||
} | ||
*/ | ||
BigInteger.prototype.DB = dbits; | ||
BigInteger.prototype.DM = ((1<<dbits)-1); | ||
BigInteger.prototype.DV = (1<<dbits); | ||
var DV = BigInteger.prototype.DV = (1<<dbits); | ||
@@ -133,2 +147,4 @@ var BI_FP = 52; | ||
function bnpFromString(s,b) { | ||
var self = this; | ||
var k; | ||
@@ -141,5 +157,5 @@ if(b == 16) k = 4; | ||
else if(b == 4) k = 2; | ||
else { this.fromRadix(s,b); return; } | ||
this.t = 0; | ||
this.s = 0; | ||
else { self.fromRadix(s,b); return; } | ||
self.t = 0; | ||
self.s = 0; | ||
var i = s.length, mi = false, sh = 0; | ||
@@ -154,18 +170,18 @@ while(--i >= 0) { | ||
if(sh == 0) | ||
this[this.t++] = x; | ||
else if(sh+k > this.DB) { | ||
this[this.t-1] |= (x&((1<<(this.DB-sh))-1))<<sh; | ||
this[this.t++] = (x>>(this.DB-sh)); | ||
self[self.t++] = x; | ||
else if(sh+k > self.DB) { | ||
self[self.t-1] |= (x&((1<<(self.DB-sh))-1))<<sh; | ||
self[self.t++] = (x>>(self.DB-sh)); | ||
} | ||
else | ||
this[this.t-1] |= x<<sh; | ||
self[self.t-1] |= x<<sh; | ||
sh += k; | ||
if(sh >= this.DB) sh -= this.DB; | ||
if(sh >= self.DB) sh -= self.DB; | ||
} | ||
if(k == 8 && (s[0]&0x80) != 0) { | ||
this.s = -1; | ||
if(sh > 0) this[this.t-1] |= ((1<<(this.DB-sh))-1)<<sh; | ||
self.s = -1; | ||
if(sh > 0) self[self.t-1] |= ((1<<(self.DB-sh))-1)<<sh; | ||
} | ||
this.clamp(); | ||
if(mi) BigInteger.ZERO.subTo(this,this); | ||
self.clamp(); | ||
if(mi) BigInteger.ZERO.subTo(self,self); | ||
} | ||
@@ -181,3 +197,4 @@ | ||
function bnToString(b) { | ||
if(this.s < 0) return "-"+this.negate().toString(b); | ||
var self = this; | ||
if(self.s < 0) return "-"+self.negate().toString(b); | ||
var k; | ||
@@ -189,15 +206,15 @@ if(b == 16) k = 4; | ||
else if(b == 4) k = 2; | ||
else return this.toRadix(b); | ||
var km = (1<<k)-1, d, m = false, r = "", i = this.t; | ||
var p = this.DB-(i*this.DB)%k; | ||
else return self.toRadix(b); | ||
var km = (1<<k)-1, d, m = false, r = "", i = self.t; | ||
var p = self.DB-(i*self.DB)%k; | ||
if(i-- > 0) { | ||
if(p < this.DB && (d = this[i]>>p) > 0) { m = true; r = int2char(d); } | ||
if(p < self.DB && (d = self[i]>>p) > 0) { m = true; r = int2char(d); } | ||
while(i >= 0) { | ||
if(p < k) { | ||
d = (this[i]&((1<<p)-1))<<(k-p); | ||
d |= this[--i]>>(p+=this.DB-k); | ||
d = (self[i]&((1<<p)-1))<<(k-p); | ||
d |= self[--i]>>(p+=self.DB-k); | ||
} | ||
else { | ||
d = (this[i]>>(p-=k))&km; | ||
if(p <= 0) { p += this.DB; --i; } | ||
d = (self[i]>>(p-=k))&km; | ||
if(p <= 0) { p += self.DB; --i; } | ||
} | ||
@@ -263,14 +280,15 @@ if(d > 0) m = true; | ||
function bnpLShiftTo(n,r) { | ||
var bs = n%this.DB; | ||
var cbs = this.DB-bs; | ||
var self = this; | ||
var bs = n%self.DB; | ||
var cbs = self.DB-bs; | ||
var bm = (1<<cbs)-1; | ||
var ds = Math.floor(n/this.DB), c = (this.s<<bs)&this.DM, i; | ||
for(i = this.t-1; i >= 0; --i) { | ||
r[i+ds+1] = (this[i]>>cbs)|c; | ||
c = (this[i]&bm)<<bs; | ||
var ds = Math.floor(n/self.DB), c = (self.s<<bs)&self.DM, i; | ||
for(i = self.t-1; i >= 0; --i) { | ||
r[i+ds+1] = (self[i]>>cbs)|c; | ||
c = (self[i]&bm)<<bs; | ||
} | ||
for(i = ds-1; i >= 0; --i) r[i] = 0; | ||
r[ds] = c; | ||
r.t = this.t+ds+1; | ||
r.s = this.s; | ||
r.t = self.t+ds+1; | ||
r.s = self.s; | ||
r.clamp(); | ||
@@ -281,15 +299,16 @@ } | ||
function bnpRShiftTo(n,r) { | ||
r.s = this.s; | ||
var ds = Math.floor(n/this.DB); | ||
if(ds >= this.t) { r.t = 0; return; } | ||
var bs = n%this.DB; | ||
var cbs = this.DB-bs; | ||
var self = this; | ||
r.s = self.s; | ||
var ds = Math.floor(n/self.DB); | ||
if(ds >= self.t) { r.t = 0; return; } | ||
var bs = n%self.DB; | ||
var cbs = self.DB-bs; | ||
var bm = (1<<bs)-1; | ||
r[0] = this[ds]>>bs; | ||
for(var i = ds+1; i < this.t; ++i) { | ||
r[i-ds-1] |= (this[i]&bm)<<cbs; | ||
r[i-ds] = this[i]>>bs; | ||
r[0] = self[ds]>>bs; | ||
for(var i = ds+1; i < self.t; ++i) { | ||
r[i-ds-1] |= (self[i]&bm)<<cbs; | ||
r[i-ds] = self[i]>>bs; | ||
} | ||
if(bs > 0) r[this.t-ds-1] |= (this.s&bm)<<cbs; | ||
r.t = this.t-ds; | ||
if(bs > 0) r[self.t-ds-1] |= (self.s&bm)<<cbs; | ||
r.t = self.t-ds; | ||
r.clamp(); | ||
@@ -300,23 +319,24 @@ } | ||
function bnpSubTo(a,r) { | ||
var i = 0, c = 0, m = Math.min(a.t,this.t); | ||
var self = this; | ||
var i = 0, c = 0, m = Math.min(a.t,self.t); | ||
while(i < m) { | ||
c += this[i]-a[i]; | ||
r[i++] = c&this.DM; | ||
c >>= this.DB; | ||
c += self[i]-a[i]; | ||
r[i++] = c&self.DM; | ||
c >>= self.DB; | ||
} | ||
if(a.t < this.t) { | ||
if(a.t < self.t) { | ||
c -= a.s; | ||
while(i < this.t) { | ||
c += this[i]; | ||
r[i++] = c&this.DM; | ||
c >>= this.DB; | ||
while(i < self.t) { | ||
c += self[i]; | ||
r[i++] = c&self.DM; | ||
c >>= self.DB; | ||
} | ||
c += this.s; | ||
c += self.s; | ||
} | ||
else { | ||
c += this.s; | ||
c += self.s; | ||
while(i < a.t) { | ||
c -= a[i]; | ||
r[i++] = c&this.DM; | ||
c >>= this.DB; | ||
r[i++] = c&self.DM; | ||
c >>= self.DB; | ||
} | ||
@@ -326,3 +346,3 @@ c -= a.s; | ||
r.s = (c<0)?-1:0; | ||
if(c < -1) r[i++] = this.DV+c; | ||
if(c < -1) r[i++] = self.DV+c; | ||
else if(c > 0) r[i++] = c; | ||
@@ -366,13 +386,14 @@ r.t = i; | ||
function bnpDivRemTo(m,q,r) { | ||
var self = this; | ||
var pm = m.abs(); | ||
if(pm.t <= 0) return; | ||
var pt = this.abs(); | ||
var pt = self.abs(); | ||
if(pt.t < pm.t) { | ||
if(q != null) q.fromInt(0); | ||
if(r != null) this.copyTo(r); | ||
if(r != null) self.copyTo(r); | ||
return; | ||
} | ||
if(r == null) r = nbi(); | ||
var y = nbi(), ts = this.s, ms = m.s; | ||
var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus | ||
var y = nbi(), ts = self.s, ms = m.s; | ||
var nsh = self.DB-nbits(pm[pm.t-1]); // normalize modulus | ||
if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); } | ||
@@ -383,4 +404,4 @@ else { pm.copyTo(y); pt.copyTo(r); } | ||
if(y0 == 0) return; | ||
var yt = y0*(1<<this.F1)+((ys>1)?y[ys-2]>>this.F2:0); | ||
var d1 = this.FV/yt, d2 = (1<<this.F1)/yt, e = 1<<this.F2; | ||
var yt = y0*(1<<self.F1)+((ys>1)?y[ys-2]>>self.F2:0); | ||
var d1 = self.FV/yt, d2 = (1<<self.F1)/yt, e = 1<<self.F2; | ||
var i = r.t, j = i-ys, t = (q==null)?nbi():q; | ||
@@ -397,3 +418,3 @@ y.dlShiftTo(j,t); | ||
// Estimate quotient digit | ||
var qd = (r[--i]==y0)?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2); | ||
var qd = (r[--i]==y0)?self.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2); | ||
if((r[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out | ||
@@ -547,29 +568,753 @@ y.dlShiftTo(j,t); | ||
// protected | ||
BigInteger.prototype.copyTo = bnpCopyTo; | ||
BigInteger.prototype.fromInt = bnpFromInt; | ||
BigInteger.prototype.fromString = bnpFromString; | ||
BigInteger.prototype.clamp = bnpClamp; | ||
BigInteger.prototype.dlShiftTo = bnpDLShiftTo; | ||
BigInteger.prototype.drShiftTo = bnpDRShiftTo; | ||
BigInteger.prototype.lShiftTo = bnpLShiftTo; | ||
BigInteger.prototype.rShiftTo = bnpRShiftTo; | ||
BigInteger.prototype.subTo = bnpSubTo; | ||
BigInteger.prototype.multiplyTo = bnpMultiplyTo; | ||
BigInteger.prototype.squareTo = bnpSquareTo; | ||
BigInteger.prototype.divRemTo = bnpDivRemTo; | ||
BigInteger.prototype.invDigit = bnpInvDigit; | ||
BigInteger.prototype.isEven = bnpIsEven; | ||
BigInteger.prototype.exp = bnpExp; | ||
proto.copyTo = bnpCopyTo; | ||
proto.fromInt = bnpFromInt; | ||
proto.fromString = bnpFromString; | ||
proto.clamp = bnpClamp; | ||
proto.dlShiftTo = bnpDLShiftTo; | ||
proto.drShiftTo = bnpDRShiftTo; | ||
proto.lShiftTo = bnpLShiftTo; | ||
proto.rShiftTo = bnpRShiftTo; | ||
proto.subTo = bnpSubTo; | ||
proto.multiplyTo = bnpMultiplyTo; | ||
proto.squareTo = bnpSquareTo; | ||
proto.divRemTo = bnpDivRemTo; | ||
proto.invDigit = bnpInvDigit; | ||
proto.isEven = bnpIsEven; | ||
proto.exp = bnpExp; | ||
// public | ||
BigInteger.prototype.toString = bnToString; | ||
BigInteger.prototype.negate = bnNegate; | ||
BigInteger.prototype.abs = bnAbs; | ||
BigInteger.prototype.compareTo = bnCompareTo; | ||
BigInteger.prototype.bitLength = bnBitLength; | ||
BigInteger.prototype.mod = bnMod; | ||
BigInteger.prototype.modPowInt = bnModPowInt; | ||
proto.toString = bnToString; | ||
proto.negate = bnNegate; | ||
proto.abs = bnAbs; | ||
proto.compareTo = bnCompareTo; | ||
proto.bitLength = bnBitLength; | ||
proto.mod = bnMod; | ||
proto.modPowInt = bnModPowInt; | ||
//// jsbn2 | ||
function nbi() { return new BigInteger(null); } | ||
// (public) | ||
function bnClone() { var r = nbi(); this.copyTo(r); return r; } | ||
// (public) return value as integer | ||
function bnIntValue() { | ||
if(this.s < 0) { | ||
if(this.t == 1) return this[0]-this.DV; | ||
else if(this.t == 0) return -1; | ||
} | ||
else if(this.t == 1) return this[0]; | ||
else if(this.t == 0) return 0; | ||
// assumes 16 < DB < 32 | ||
return ((this[1]&((1<<(32-this.DB))-1))<<this.DB)|this[0]; | ||
} | ||
// (public) return value as byte | ||
function bnByteValue() { return (this.t==0)?this.s:(this[0]<<24)>>24; } | ||
// (public) return value as short (assumes DB>=16) | ||
function bnShortValue() { return (this.t==0)?this.s:(this[0]<<16)>>16; } | ||
// (protected) return x s.t. r^x < DV | ||
function bnpChunkSize(r) { return Math.floor(Math.LN2*this.DB/Math.log(r)); } | ||
// (public) 0 if this == 0, 1 if this > 0 | ||
function bnSigNum() { | ||
if(this.s < 0) return -1; | ||
else if(this.t <= 0 || (this.t == 1 && this[0] <= 0)) return 0; | ||
else return 1; | ||
} | ||
// (protected) convert to radix string | ||
function bnpToRadix(b) { | ||
if(b == null) b = 10; | ||
if(this.signum() == 0 || b < 2 || b > 36) return "0"; | ||
var cs = this.chunkSize(b); | ||
var a = Math.pow(b,cs); | ||
var d = nbv(a), y = nbi(), z = nbi(), r = ""; | ||
this.divRemTo(d,y,z); | ||
while(y.signum() > 0) { | ||
r = (a+z.intValue()).toString(b).substr(1) + r; | ||
y.divRemTo(d,y,z); | ||
} | ||
return z.intValue().toString(b) + r; | ||
} | ||
// (protected) convert from radix string | ||
function bnpFromRadix(s,b) { | ||
var self = this; | ||
self.fromInt(0); | ||
if(b == null) b = 10; | ||
var cs = self.chunkSize(b); | ||
var d = Math.pow(b,cs), mi = false, j = 0, w = 0; | ||
for(var i = 0; i < s.length; ++i) { | ||
var x = intAt(s,i); | ||
if(x < 0) { | ||
if(s.charAt(i) == "-" && self.signum() == 0) mi = true; | ||
continue; | ||
} | ||
w = b*w+x; | ||
if(++j >= cs) { | ||
self.dMultiply(d); | ||
self.dAddOffset(w,0); | ||
j = 0; | ||
w = 0; | ||
} | ||
} | ||
if(j > 0) { | ||
self.dMultiply(Math.pow(b,j)); | ||
self.dAddOffset(w,0); | ||
} | ||
if(mi) BigInteger.ZERO.subTo(self,self); | ||
} | ||
// (protected) alternate constructor | ||
function bnpFromNumber(a,b,c) { | ||
var self = this; | ||
if("number" == typeof b) { | ||
// new BigInteger(int,int,RNG) | ||
if(a < 2) self.fromInt(1); | ||
else { | ||
self.fromNumber(a,c); | ||
if(!self.testBit(a-1)) // force MSB set | ||
self.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,self); | ||
if(self.isEven()) self.dAddOffset(1,0); // force odd | ||
while(!self.isProbablePrime(b)) { | ||
self.dAddOffset(2,0); | ||
if(self.bitLength() > a) self.subTo(BigInteger.ONE.shiftLeft(a-1),self); | ||
} | ||
} | ||
} | ||
else { | ||
// new BigInteger(int,RNG) | ||
var t = a&7; | ||
var length = (a>>3)+1; | ||
var x = b(length, {array: true}); | ||
if(t > 0) x[0] &= ((1<<t)-1); else x[0] = 0; | ||
self.fromString(x,256); | ||
} | ||
} | ||
// (public) convert to bigendian byte array | ||
function bnToByteArray() { | ||
var self = this; | ||
var i = self.t, r = new Array(); | ||
r[0] = self.s; | ||
var p = self.DB-(i*self.DB)%8, d, k = 0; | ||
if(i-- > 0) { | ||
if(p < self.DB && (d = self[i]>>p) != (self.s&self.DM)>>p) | ||
r[k++] = d|(self.s<<(self.DB-p)); | ||
while(i >= 0) { | ||
if(p < 8) { | ||
d = (self[i]&((1<<p)-1))<<(8-p); | ||
d |= self[--i]>>(p+=self.DB-8); | ||
} | ||
else { | ||
d = (self[i]>>(p-=8))&0xff; | ||
if(p <= 0) { p += self.DB; --i; } | ||
} | ||
if((d&0x80) != 0) d |= -256; | ||
if(k === 0 && (self.s&0x80) != (d&0x80)) ++k; | ||
if(k > 0 || d != self.s) r[k++] = d; | ||
} | ||
} | ||
return r; | ||
} | ||
function bnEquals(a) { return(this.compareTo(a)==0); } | ||
function bnMin(a) { return(this.compareTo(a)<0)?this:a; } | ||
function bnMax(a) { return(this.compareTo(a)>0)?this:a; } | ||
// (protected) r = this op a (bitwise) | ||
function bnpBitwiseTo(a,op,r) { | ||
var self = this; | ||
var i, f, m = Math.min(a.t,self.t); | ||
for(i = 0; i < m; ++i) r[i] = op(self[i],a[i]); | ||
if(a.t < self.t) { | ||
f = a.s&self.DM; | ||
for(i = m; i < self.t; ++i) r[i] = op(self[i],f); | ||
r.t = self.t; | ||
} | ||
else { | ||
f = self.s&self.DM; | ||
for(i = m; i < a.t; ++i) r[i] = op(f,a[i]); | ||
r.t = a.t; | ||
} | ||
r.s = op(self.s,a.s); | ||
r.clamp(); | ||
} | ||
// (public) this & a | ||
function op_and(x,y) { return x&y; } | ||
function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; } | ||
// (public) this | a | ||
function op_or(x,y) { return x|y; } | ||
function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; } | ||
// (public) this ^ a | ||
function op_xor(x,y) { return x^y; } | ||
function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; } | ||
// (public) this & ~a | ||
function op_andnot(x,y) { return x&~y; } | ||
function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; } | ||
// (public) ~this | ||
function bnNot() { | ||
var r = nbi(); | ||
for(var i = 0; i < this.t; ++i) r[i] = this.DM&~this[i]; | ||
r.t = this.t; | ||
r.s = ~this.s; | ||
return r; | ||
} | ||
// (public) this << n | ||
function bnShiftLeft(n) { | ||
var r = nbi(); | ||
if(n < 0) this.rShiftTo(-n,r); else this.lShiftTo(n,r); | ||
return r; | ||
} | ||
// (public) this >> n | ||
function bnShiftRight(n) { | ||
var r = nbi(); | ||
if(n < 0) this.lShiftTo(-n,r); else this.rShiftTo(n,r); | ||
return r; | ||
} | ||
// return index of lowest 1-bit in x, x < 2^31 | ||
function lbit(x) { | ||
if(x == 0) return -1; | ||
var r = 0; | ||
if((x&0xffff) == 0) { x >>= 16; r += 16; } | ||
if((x&0xff) == 0) { x >>= 8; r += 8; } | ||
if((x&0xf) == 0) { x >>= 4; r += 4; } | ||
if((x&3) == 0) { x >>= 2; r += 2; } | ||
if((x&1) == 0) ++r; | ||
return r; | ||
} | ||
// (public) returns index of lowest 1-bit (or -1 if none) | ||
function bnGetLowestSetBit() { | ||
for(var i = 0; i < this.t; ++i) | ||
if(this[i] != 0) return i*this.DB+lbit(this[i]); | ||
if(this.s < 0) return this.t*this.DB; | ||
return -1; | ||
} | ||
// return number of 1 bits in x | ||
function cbit(x) { | ||
var r = 0; | ||
while(x != 0) { x &= x-1; ++r; } | ||
return r; | ||
} | ||
// (public) return number of set bits | ||
function bnBitCount() { | ||
var r = 0, x = this.s&this.DM; | ||
for(var i = 0; i < this.t; ++i) r += cbit(this[i]^x); | ||
return r; | ||
} | ||
// (public) true iff nth bit is set | ||
function bnTestBit(n) { | ||
var j = Math.floor(n/this.DB); | ||
if(j >= this.t) return(this.s!=0); | ||
return((this[j]&(1<<(n%this.DB)))!=0); | ||
} | ||
// (protected) this op (1<<n) | ||
function bnpChangeBit(n,op) { | ||
var r = BigInteger.ONE.shiftLeft(n); | ||
this.bitwiseTo(r,op,r); | ||
return r; | ||
} | ||
// (public) this | (1<<n) | ||
function bnSetBit(n) { return this.changeBit(n,op_or); } | ||
// (public) this & ~(1<<n) | ||
function bnClearBit(n) { return this.changeBit(n,op_andnot); } | ||
// (public) this ^ (1<<n) | ||
function bnFlipBit(n) { return this.changeBit(n,op_xor); } | ||
// (protected) r = this + a | ||
function bnpAddTo(a,r) { | ||
var self = this; | ||
var i = 0, c = 0, m = Math.min(a.t,self.t); | ||
while(i < m) { | ||
c += self[i]+a[i]; | ||
r[i++] = c&self.DM; | ||
c >>= self.DB; | ||
} | ||
if(a.t < self.t) { | ||
c += a.s; | ||
while(i < self.t) { | ||
c += self[i]; | ||
r[i++] = c&self.DM; | ||
c >>= self.DB; | ||
} | ||
c += self.s; | ||
} | ||
else { | ||
c += self.s; | ||
while(i < a.t) { | ||
c += a[i]; | ||
r[i++] = c&self.DM; | ||
c >>= self.DB; | ||
} | ||
c += a.s; | ||
} | ||
r.s = (c<0)?-1:0; | ||
if(c > 0) r[i++] = c; | ||
else if(c < -1) r[i++] = self.DV+c; | ||
r.t = i; | ||
r.clamp(); | ||
} | ||
// (public) this + a | ||
function bnAdd(a) { var r = nbi(); this.addTo(a,r); return r; } | ||
// (public) this - a | ||
function bnSubtract(a) { var r = nbi(); this.subTo(a,r); return r; } | ||
// (public) this * a | ||
function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; } | ||
// (public) this^2 | ||
function bnSquare() { var r = nbi(); this.squareTo(r); return r; } | ||
// (public) this / a | ||
function bnDivide(a) { var r = nbi(); this.divRemTo(a,r,null); return r; } | ||
// (public) this % a | ||
function bnRemainder(a) { var r = nbi(); this.divRemTo(a,null,r); return r; } | ||
// (public) [this/a,this%a] | ||
function bnDivideAndRemainder(a) { | ||
var q = nbi(), r = nbi(); | ||
this.divRemTo(a,q,r); | ||
return new Array(q,r); | ||
} | ||
// (protected) this *= n, this >= 0, 1 < n < DV | ||
function bnpDMultiply(n) { | ||
this[this.t] = this.am(0,n-1,this,0,0,this.t); | ||
++this.t; | ||
this.clamp(); | ||
} | ||
// (protected) this += n << w words, this >= 0 | ||
function bnpDAddOffset(n,w) { | ||
if(n == 0) return; | ||
while(this.t <= w) this[this.t++] = 0; | ||
this[w] += n; | ||
while(this[w] >= this.DV) { | ||
this[w] -= this.DV; | ||
if(++w >= this.t) this[this.t++] = 0; | ||
++this[w]; | ||
} | ||
} | ||
// A "null" reducer | ||
function NullExp() {} | ||
function nNop(x) { return x; } | ||
function nMulTo(x,y,r) { x.multiplyTo(y,r); } | ||
function nSqrTo(x,r) { x.squareTo(r); } | ||
NullExp.prototype.convert = nNop; | ||
NullExp.prototype.revert = nNop; | ||
NullExp.prototype.mulTo = nMulTo; | ||
NullExp.prototype.sqrTo = nSqrTo; | ||
// (public) this^e | ||
function bnPow(e) { return this.exp(e,new NullExp()); } | ||
// (protected) r = lower n words of "this * a", a.t <= n | ||
// "this" should be the larger one if appropriate. | ||
function bnpMultiplyLowerTo(a,n,r) { | ||
var i = Math.min(this.t+a.t,n); | ||
r.s = 0; // assumes a,this >= 0 | ||
r.t = i; | ||
while(i > 0) r[--i] = 0; | ||
var j; | ||
for(j = r.t-this.t; i < j; ++i) r[i+this.t] = this.am(0,a[i],r,i,0,this.t); | ||
for(j = Math.min(a.t,n); i < j; ++i) this.am(0,a[i],r,i,0,n-i); | ||
r.clamp(); | ||
} | ||
// (protected) r = "this * a" without lower n words, n > 0 | ||
// "this" should be the larger one if appropriate. | ||
function bnpMultiplyUpperTo(a,n,r) { | ||
--n; | ||
var i = r.t = this.t+a.t-n; | ||
r.s = 0; // assumes a,this >= 0 | ||
while(--i >= 0) r[i] = 0; | ||
for(i = Math.max(n-this.t,0); i < a.t; ++i) | ||
r[this.t+i-n] = this.am(n-i,a[i],r,0,0,this.t+i-n); | ||
r.clamp(); | ||
r.drShiftTo(1,r); | ||
} | ||
// Barrett modular reduction | ||
function Barrett(m) { | ||
// setup Barrett | ||
this.r2 = nbi(); | ||
this.q3 = nbi(); | ||
BigInteger.ONE.dlShiftTo(2*m.t,this.r2); | ||
this.mu = this.r2.divide(m); | ||
this.m = m; | ||
} | ||
function barrettConvert(x) { | ||
if(x.s < 0 || x.t > 2*this.m.t) return x.mod(this.m); | ||
else if(x.compareTo(this.m) < 0) return x; | ||
else { var r = nbi(); x.copyTo(r); this.reduce(r); return r; } | ||
} | ||
function barrettRevert(x) { return x; } | ||
// x = x mod m (HAC 14.42) | ||
function barrettReduce(x) { | ||
var self = this; | ||
x.drShiftTo(self.m.t-1,self.r2); | ||
if(x.t > self.m.t+1) { x.t = self.m.t+1; x.clamp(); } | ||
self.mu.multiplyUpperTo(self.r2,self.m.t+1,self.q3); | ||
self.m.multiplyLowerTo(self.q3,self.m.t+1,self.r2); | ||
while(x.compareTo(self.r2) < 0) x.dAddOffset(1,self.m.t+1); | ||
x.subTo(self.r2,x); | ||
while(x.compareTo(self.m) >= 0) x.subTo(self.m,x); | ||
} | ||
// r = x^2 mod m; x != r | ||
function barrettSqrTo(x,r) { x.squareTo(r); this.reduce(r); } | ||
// r = x*y mod m; x,y != r | ||
function barrettMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } | ||
Barrett.prototype.convert = barrettConvert; | ||
Barrett.prototype.revert = barrettRevert; | ||
Barrett.prototype.reduce = barrettReduce; | ||
Barrett.prototype.mulTo = barrettMulTo; | ||
Barrett.prototype.sqrTo = barrettSqrTo; | ||
// (public) this^e % m (HAC 14.85) | ||
function bnModPow(e,m) { | ||
var i = e.bitLength(), k, r = nbv(1), z; | ||
if(i <= 0) return r; | ||
else if(i < 18) k = 1; | ||
else if(i < 48) k = 3; | ||
else if(i < 144) k = 4; | ||
else if(i < 768) k = 5; | ||
else k = 6; | ||
if(i < 8) | ||
z = new Classic(m); | ||
else if(m.isEven()) | ||
z = new Barrett(m); | ||
else | ||
z = new Montgomery(m); | ||
// precomputation | ||
var g = new Array(), n = 3, k1 = k-1, km = (1<<k)-1; | ||
g[1] = z.convert(this); | ||
if(k > 1) { | ||
var g2 = nbi(); | ||
z.sqrTo(g[1],g2); | ||
while(n <= km) { | ||
g[n] = nbi(); | ||
z.mulTo(g2,g[n-2],g[n]); | ||
n += 2; | ||
} | ||
} | ||
var j = e.t-1, w, is1 = true, r2 = nbi(), t; | ||
i = nbits(e[j])-1; | ||
while(j >= 0) { | ||
if(i >= k1) w = (e[j]>>(i-k1))&km; | ||
else { | ||
w = (e[j]&((1<<(i+1))-1))<<(k1-i); | ||
if(j > 0) w |= e[j-1]>>(this.DB+i-k1); | ||
} | ||
n = k; | ||
while((w&1) == 0) { w >>= 1; --n; } | ||
if((i -= n) < 0) { i += this.DB; --j; } | ||
if(is1) { // ret == 1, don't bother squaring or multiplying it | ||
g[w].copyTo(r); | ||
is1 = false; | ||
} | ||
else { | ||
while(n > 1) { z.sqrTo(r,r2); z.sqrTo(r2,r); n -= 2; } | ||
if(n > 0) z.sqrTo(r,r2); else { t = r; r = r2; r2 = t; } | ||
z.mulTo(r2,g[w],r); | ||
} | ||
while(j >= 0 && (e[j]&(1<<i)) == 0) { | ||
z.sqrTo(r,r2); t = r; r = r2; r2 = t; | ||
if(--i < 0) { i = this.DB-1; --j; } | ||
} | ||
} | ||
return z.revert(r); | ||
} | ||
// (public) gcd(this,a) (HAC 14.54) | ||
function bnGCD(a) { | ||
var x = (this.s<0)?this.negate():this.clone(); | ||
var y = (a.s<0)?a.negate():a.clone(); | ||
if(x.compareTo(y) < 0) { var t = x; x = y; y = t; } | ||
var i = x.getLowestSetBit(), g = y.getLowestSetBit(); | ||
if(g < 0) return x; | ||
if(i < g) g = i; | ||
if(g > 0) { | ||
x.rShiftTo(g,x); | ||
y.rShiftTo(g,y); | ||
} | ||
while(x.signum() > 0) { | ||
if((i = x.getLowestSetBit()) > 0) x.rShiftTo(i,x); | ||
if((i = y.getLowestSetBit()) > 0) y.rShiftTo(i,y); | ||
if(x.compareTo(y) >= 0) { | ||
x.subTo(y,x); | ||
x.rShiftTo(1,x); | ||
} | ||
else { | ||
y.subTo(x,y); | ||
y.rShiftTo(1,y); | ||
} | ||
} | ||
if(g > 0) y.lShiftTo(g,y); | ||
return y; | ||
} | ||
// (protected) this % n, n < 2^26 | ||
function bnpModInt(n) { | ||
if(n <= 0) return 0; | ||
var d = this.DV%n, r = (this.s<0)?n-1:0; | ||
if(this.t > 0) | ||
if(d == 0) r = this[0]%n; | ||
else for(var i = this.t-1; i >= 0; --i) r = (d*r+this[i])%n; | ||
return r; | ||
} | ||
// (public) 1/this % m (HAC 14.61) | ||
function bnModInverse(m) { | ||
var ac = m.isEven(); | ||
if((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO; | ||
var u = m.clone(), v = this.clone(); | ||
var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1); | ||
while(u.signum() != 0) { | ||
while(u.isEven()) { | ||
u.rShiftTo(1,u); | ||
if(ac) { | ||
if(!a.isEven() || !b.isEven()) { a.addTo(this,a); b.subTo(m,b); } | ||
a.rShiftTo(1,a); | ||
} | ||
else if(!b.isEven()) b.subTo(m,b); | ||
b.rShiftTo(1,b); | ||
} | ||
while(v.isEven()) { | ||
v.rShiftTo(1,v); | ||
if(ac) { | ||
if(!c.isEven() || !d.isEven()) { c.addTo(this,c); d.subTo(m,d); } | ||
c.rShiftTo(1,c); | ||
} | ||
else if(!d.isEven()) d.subTo(m,d); | ||
d.rShiftTo(1,d); | ||
} | ||
if(u.compareTo(v) >= 0) { | ||
u.subTo(v,u); | ||
if(ac) a.subTo(c,a); | ||
b.subTo(d,b); | ||
} | ||
else { | ||
v.subTo(u,v); | ||
if(ac) c.subTo(a,c); | ||
d.subTo(b,d); | ||
} | ||
} | ||
if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO; | ||
if(d.compareTo(m) >= 0) return d.subtract(m); | ||
if(d.signum() < 0) d.addTo(m,d); else return d; | ||
if(d.signum() < 0) return d.add(m); else return d; | ||
} | ||
// protected | ||
proto.chunkSize = bnpChunkSize; | ||
proto.toRadix = bnpToRadix; | ||
proto.fromRadix = bnpFromRadix; | ||
proto.fromNumber = bnpFromNumber; | ||
proto.bitwiseTo = bnpBitwiseTo; | ||
proto.changeBit = bnpChangeBit; | ||
proto.addTo = bnpAddTo; | ||
proto.dMultiply = bnpDMultiply; | ||
proto.dAddOffset = bnpDAddOffset; | ||
proto.multiplyLowerTo = bnpMultiplyLowerTo; | ||
proto.multiplyUpperTo = bnpMultiplyUpperTo; | ||
proto.modInt = bnpModInt; | ||
// public | ||
proto.clone = bnClone; | ||
proto.intValue = bnIntValue; | ||
proto.byteValue = bnByteValue; | ||
proto.shortValue = bnShortValue; | ||
proto.signum = bnSigNum; | ||
proto.toByteArray = bnToByteArray; | ||
proto.equals = bnEquals; | ||
proto.min = bnMin; | ||
proto.max = bnMax; | ||
proto.and = bnAnd; | ||
proto.or = bnOr; | ||
proto.xor = bnXor; | ||
proto.andNot = bnAndNot; | ||
proto.not = bnNot; | ||
proto.shiftLeft = bnShiftLeft; | ||
proto.shiftRight = bnShiftRight; | ||
proto.getLowestSetBit = bnGetLowestSetBit; | ||
proto.bitCount = bnBitCount; | ||
proto.testBit = bnTestBit; | ||
proto.setBit = bnSetBit; | ||
proto.clearBit = bnClearBit; | ||
proto.flipBit = bnFlipBit; | ||
proto.add = bnAdd; | ||
proto.subtract = bnSubtract; | ||
proto.multiply = bnMultiply; | ||
proto.divide = bnDivide; | ||
proto.remainder = bnRemainder; | ||
proto.divideAndRemainder = bnDivideAndRemainder; | ||
proto.modPow = bnModPow; | ||
proto.modInverse = bnModInverse; | ||
proto.pow = bnPow; | ||
proto.gcd = bnGCD; | ||
// JSBN-specific extension | ||
proto.square = bnSquare; | ||
// BigInteger interfaces not implemented in jsbn: | ||
// BigInteger(int signum, byte[] magnitude) | ||
// double doubleValue() | ||
// float floatValue() | ||
// int hashCode() | ||
// long longValue() | ||
// static BigInteger valueOf(long val) | ||
// "constants" | ||
BigInteger.ZERO = nbv(0); | ||
BigInteger.ONE = nbv(1); | ||
BigInteger.valueOf = nbv; | ||
/// bitcoinjs addons | ||
/** | ||
* Turns a byte array into a big integer. | ||
* | ||
* This function will interpret a byte array as a big integer in big | ||
* endian notation and ignore leading zeros. | ||
*/ | ||
BigInteger.fromByteArrayUnsigned = function(ba) { | ||
// FIXME: BigInteger doesn't yet support Buffers | ||
if (Buffer.isBuffer(ba)) { | ||
ba = Array.prototype.map.bind(ba, function(x) { return x })() | ||
} | ||
if (!ba.length) { | ||
return new BigInteger.valueOf(0); | ||
} else if (ba[0] & 0x80) { | ||
// Prepend a zero so the BigInteger class doesn't mistake this | ||
// for a negative integer. | ||
return new BigInteger([0].concat(ba)); | ||
} else { | ||
return new BigInteger(ba); | ||
} | ||
}; | ||
/** | ||
* Parse a signed big integer byte representation. | ||
* | ||
* For details on the format please see BigInteger.toByteArraySigned. | ||
*/ | ||
BigInteger.fromByteArraySigned = function(ba) { | ||
// Check for negative value | ||
if (ba[0] & 0x80) { | ||
// Remove sign bit | ||
ba[0] &= 0x7f; | ||
return BigInteger.fromByteArrayUnsigned(ba).negate(); | ||
} else { | ||
return BigInteger.fromByteArrayUnsigned(ba); | ||
} | ||
}; | ||
/** | ||
* Returns a byte array representation of the big integer. | ||
* | ||
* This returns the absolute of the contained value in big endian | ||
* form. A value of zero results in an empty array. | ||
*/ | ||
BigInteger.prototype.toByteArrayUnsigned = function() { | ||
var ba = this.abs().toByteArray(); | ||
// Empty array, nothing to do | ||
if (!ba.length) { | ||
return ba; | ||
} | ||
// remove leading 0 | ||
if (ba[0] === 0) { | ||
ba = ba.slice(1); | ||
} | ||
// all values must be positive | ||
for (var i=0 ; i<ba.length ; ++i) { | ||
ba[i] = (ba[i] < 0) ? ba[i] + 256 : ba[i]; | ||
} | ||
return ba; | ||
}; | ||
/* | ||
* Converts big integer to signed byte representation. | ||
* | ||
* The format for this value uses the most significant bit as a sign | ||
* bit. If the most significant bit is already occupied by the | ||
* absolute value, an extra byte is prepended and the sign bit is set | ||
* there. | ||
* | ||
* Examples: | ||
* | ||
* 0 => 0x00 | ||
* 1 => 0x01 | ||
* -1 => 0x81 | ||
* 127 => 0x7f | ||
* -127 => 0xff | ||
* 128 => 0x0080 | ||
* -128 => 0x8080 | ||
* 255 => 0x00ff | ||
* -255 => 0x80ff | ||
* 16300 => 0x3fac | ||
* -16300 => 0xbfac | ||
* 62300 => 0x00f35c | ||
* -62300 => 0x80f35c | ||
*/ | ||
BigInteger.prototype.toByteArraySigned = function() { | ||
var val = this.toByteArrayUnsigned(); | ||
var neg = this.s < 0; | ||
// if the first bit is set, we always unshift | ||
// either unshift 0x80 or 0x00 | ||
if (val[0] & 0x80) { | ||
val.unshift((neg) ? 0x80 : 0x00); | ||
} | ||
// if the first bit isn't set, set it if negative | ||
else if (neg) { | ||
val[0] |= 0x80; | ||
} | ||
return val; | ||
}; | ||
module.exports = BigInteger; |
@@ -5,2 +5,5 @@ // Named EC curves | ||
var ECCurveFp = require('./ec'); | ||
var BigInteger = require('./jsbn'); | ||
// ---------------- | ||
@@ -175,1 +178,3 @@ // X9ECParameters | ||
} | ||
module.exports = getSECCurveByName; |
@@ -1,69 +0,64 @@ | ||
/** | ||
* Implements Bitcoin's feature for signing arbitrary messages. | ||
*/ | ||
Bitcoin.Message = (function () { | ||
var Message = {}; | ||
/// Implements Bitcoin's feature for signing arbitrary messages. | ||
Message.magicPrefix = "Bitcoin Signed Message:\n"; | ||
var Address = require('./address') | ||
var convert = require('./convert') | ||
var ecdsa = require('./ecdsa') | ||
var ECPubKey = require('./eckey').ECPubKey | ||
var SHA256 = require('crypto-js/sha256') | ||
Message.makeMagicMessage = function (message) { | ||
var magicBytes = Crypto.charenc.UTF8.stringToBytes(Message.magicPrefix); | ||
var messageBytes = Crypto.charenc.UTF8.stringToBytes(message); | ||
// FIXME: magicHash is incompatible with other magic messages | ||
var magicBytes = convert.stringToBytes('Bitcoin Signed Message:\n') | ||
var buffer = []; | ||
buffer = buffer.concat(Bitcoin.Util.numToVarInt(magicBytes.length)); | ||
buffer = buffer.concat(magicBytes); | ||
buffer = buffer.concat(Bitcoin.Util.numToVarInt(messageBytes.length)); | ||
buffer = buffer.concat(messageBytes); | ||
function magicHash(message) { | ||
var messageBytes = convert.stringToBytes(message) | ||
return buffer; | ||
}; | ||
var buffer = [].concat( | ||
convert.numToVarInt(magicBytes.length), | ||
magicBytes, | ||
convert.numToVarInt(messageBytes.length), | ||
messageBytes | ||
) | ||
Message.getHash = function (message) { | ||
var buffer = Message.makeMagicMessage(message); | ||
return Crypto.SHA256(Crypto.SHA256(buffer, {asBytes: true}), {asBytes: true}); | ||
}; | ||
return convert.wordArrayToBytes(SHA256(SHA256(convert.bytesToWordArray(buffer)))) | ||
} | ||
Message.signMessage = function (key, message, compressed) { | ||
var hash = Message.getHash(message); | ||
// TODO: parameterize compression instead of using ECKey.compressed | ||
function sign(key, message) { | ||
var hash = magicHash(message) | ||
var sig = key.sign(hash) | ||
var obj = ecdsa.parseSig(sig) | ||
var i = ecdsa.calcPubKeyRecoveryParam(key.getPub().pub, obj.r, obj.s, hash) | ||
var sig = key.sign(hash); | ||
i += 27 | ||
if (key.compressed) { | ||
i += 4 | ||
} | ||
var obj = Bitcoin.ECDSA.parseSig(sig); | ||
var rBa = obj.r.toByteArrayUnsigned() | ||
var sBa = obj.s.toByteArrayUnsigned() | ||
var address = key.getBitcoinAddress().toString(); | ||
var i = Bitcoin.ECDSA.calcPubkeyRecoveryParam(address, obj.r, obj.s, hash); | ||
// Pad to 32 bytes per value | ||
while (rBa.length < 32) rBa.unshift(0); | ||
while (sBa.length < 32) sBa.unshift(0); | ||
i += 27; | ||
if (compressed) i += 4; | ||
sig = [i].concat(rBa, sBa) | ||
var rBa = obj.r.toByteArrayUnsigned(); | ||
var sBa = obj.s.toByteArrayUnsigned(); | ||
return sig | ||
} | ||
// Pad to 32 bytes per value | ||
while (rBa.length < 32) rBa.unshift(0); | ||
while (sBa.length < 32) sBa.unshift(0); | ||
function verify(address, sig, message) { | ||
sig = ecdsa.parseSigCompact(sig) | ||
sig = [i].concat(rBa).concat(sBa); | ||
var pubKey = new ECPubKey(ecdsa.recoverPubKey(sig.r, sig.s, magicHash(message), sig.i)) | ||
var isCompressed = !!(sig.i & 4) | ||
pubKey.compressed = isCompressed | ||
return Crypto.util.bytesToBase64(sig); | ||
}; | ||
address = new Address(address) | ||
return pubKey.getAddress(address.version).toString() === address.toString() | ||
} | ||
Message.verifyMessage = function (address, sig, message) { | ||
sig = Crypto.util.base64ToBytes(sig); | ||
sig = Bitcoin.ECDSA.parseSigCompact(sig); | ||
var hash = Message.getHash(message); | ||
var isCompressed = !!(sig.i & 4); | ||
var pubKey = Bitcoin.ECDSA.recoverPubKey(sig.r, sig.s, hash, sig.i); | ||
pubKey.setCompressed(isCompressed); | ||
var expectedAddress = pubKey.getBitcoinAddress().toString(); | ||
return (address === expectedAddress); | ||
}; | ||
return Message; | ||
})(); | ||
module.exports = { | ||
magicHash: magicHash, | ||
sign: sign, | ||
verify: verify | ||
} |
@@ -1,11 +0,3 @@ | ||
(function () { | ||
var Opcode = Bitcoin.Opcode = function (num) { | ||
this.code = num; | ||
}; | ||
Opcode.prototype.toString = function () { | ||
return Opcode.reverseMap[this.code]; | ||
}; | ||
Opcode.map = { | ||
var Opcode = { | ||
map: { | ||
// push value | ||
@@ -147,9 +139,10 @@ OP_0 : 0, | ||
OP_INVALIDOPCODE : 255 | ||
}; | ||
}, | ||
reverseMap: [] | ||
} | ||
Opcode.reverseMap = []; | ||
for(var i in Opcode.map) { | ||
Opcode.reverseMap[Opcode.map[i]] = i | ||
} | ||
for (var i in Opcode.map) { | ||
Opcode.reverseMap[Opcode.map[i]] = i; | ||
} | ||
})(); | ||
module.exports = Opcode |
@@ -1,347 +0,391 @@ | ||
(function () { | ||
var Opcode = Bitcoin.Opcode; | ||
var Opcode = require('./opcode') | ||
var util = require('./util') | ||
var convert = require('./convert') | ||
var Address = require('./address') | ||
var network = require('./network') | ||
// Make opcodes available as pseudo-constants | ||
for (var i in Opcode.map) { | ||
eval("var " + i + " = " + Opcode.map[i] + ";"); | ||
var Script = function(data) { | ||
this.buffer = data || [] | ||
if(!Array.isArray(this.buffer)) { | ||
throw new Error('expect Script to be initialized with Array, but got ' + data) | ||
} | ||
this.parse() | ||
} | ||
var Script = Bitcoin.Script = function (data) { | ||
if (!data) { | ||
this.buffer = []; | ||
} else if ("string" == typeof data) { | ||
this.buffer = Crypto.util.base64ToBytes(data); | ||
} else if (Bitcoin.Util.isArray(data)) { | ||
this.buffer = data; | ||
} else if (data instanceof Script) { | ||
this.buffer = data.buffer; | ||
Script.fromHex = function(data) { | ||
return new Script(convert.hexToBytes(data)) | ||
} | ||
Script.fromPubKey = function(str) { | ||
var script = new Script() | ||
var s = str.split(' ') | ||
for (var i in s) { | ||
if (Opcode.map.hasOwnProperty(s[i])) { | ||
script.writeOp(Opcode.map[s[i]]) | ||
} else { | ||
throw new Error("Invalid script"); | ||
script.writeBytes(convert.hexToBytes(s[i])) | ||
} | ||
} | ||
return script | ||
} | ||
this.parse(); | ||
}; | ||
Script.fromScriptSig = function(str) { | ||
var script = new Script() | ||
var s = str.split(' ') | ||
for (var i in s) { | ||
if (Opcode.map.hasOwnProperty(s[i])) { | ||
script.writeOp(Opcode.map[s[i]]) | ||
} else { | ||
script.writeBytes(convert.hexToBytes(s[i])) | ||
} | ||
} | ||
return script | ||
} | ||
/** | ||
* Update the parsed script representation. | ||
* | ||
* Each Script object stores the script in two formats. First as a raw byte | ||
* array and second as an array of "chunks", such as opcodes and pieces of | ||
* data. | ||
* | ||
* This method updates the chunks cache. Normally this is called by the | ||
* constructor and you don't need to worry about it. However, if you change | ||
* the script buffer manually, you should update the chunks using this method. | ||
*/ | ||
Script.prototype.parse = function () { | ||
var self = this; | ||
/** | ||
* Update the parsed script representation. | ||
* | ||
* Each Script object stores the script in two formats. First as a raw byte | ||
* array and second as an array of 'chunks', such as opcodes and pieces of | ||
* data. | ||
* | ||
* This method updates the chunks cache. Normally this is called by the | ||
* constructor and you don't need to worry about it. However, if you change | ||
* the script buffer manually, you should update the chunks using this method. | ||
*/ | ||
Script.prototype.parse = function() { | ||
var self = this | ||
this.chunks = []; | ||
this.chunks = [] | ||
// Cursor | ||
var i = 0; | ||
// Cursor | ||
var i = 0 | ||
// Read n bytes and store result as a chunk | ||
function readChunk(n) { | ||
self.chunks.push(self.buffer.slice(i, i + n)); | ||
i += n; | ||
}; | ||
// Read n bytes and store result as a chunk | ||
function readChunk(n) { | ||
self.chunks.push(self.buffer.slice(i, i + n)) | ||
i += n | ||
} | ||
while (i < this.buffer.length) { | ||
var opcode = this.buffer[i++]; | ||
if (opcode >= 0xF0) { | ||
// Two byte opcode | ||
opcode = (opcode << 8) | this.buffer[i++]; | ||
} | ||
while (i < this.buffer.length) { | ||
var opcode = this.buffer[i++] | ||
if (opcode >= 0xF0) { | ||
// Two byte opcode | ||
opcode = (opcode << 8) | this.buffer[i++] | ||
} | ||
var len; | ||
if (opcode > 0 && opcode < OP_PUSHDATA1) { | ||
// Read some bytes of data, opcode value is the length of data | ||
readChunk(opcode); | ||
} else if (opcode == OP_PUSHDATA1) { | ||
len = this.buffer[i++]; | ||
readChunk(len); | ||
} else if (opcode == OP_PUSHDATA2) { | ||
len = (this.buffer[i++] << 8) | this.buffer[i++]; | ||
readChunk(len); | ||
} else if (opcode == OP_PUSHDATA4) { | ||
len = (this.buffer[i++] << 24) | | ||
(this.buffer[i++] << 16) | | ||
(this.buffer[i++] << 8) | | ||
this.buffer[i++]; | ||
readChunk(len); | ||
} else { | ||
this.chunks.push(opcode); | ||
} | ||
var len | ||
if (opcode > 0 && opcode < Opcode.map.OP_PUSHDATA1) { | ||
// Read some bytes of data, opcode value is the length of data | ||
readChunk(opcode) | ||
} else if (opcode == Opcode.map.OP_PUSHDATA1) { | ||
len = this.buffer[i++] | ||
readChunk(len) | ||
} else if (opcode == Opcode.map.OP_PUSHDATA2) { | ||
len = (this.buffer[i++] << 8) | this.buffer[i++] | ||
readChunk(len) | ||
} else if (opcode == Opcode.map.OP_PUSHDATA4) { | ||
len = (this.buffer[i++] << 24) | | ||
(this.buffer[i++] << 16) | | ||
(this.buffer[i++] << 8) | | ||
this.buffer[i++] | ||
readChunk(len) | ||
} else { | ||
this.chunks.push(opcode) | ||
} | ||
}; | ||
} | ||
} | ||
/** | ||
* Compare the script to known templates of scriptPubKey. | ||
* | ||
* This method will compare the script to a small number of standard script | ||
* templates and return a string naming the detected type. | ||
* | ||
* Currently supported are: | ||
* Address: | ||
* Paying to a Bitcoin address which is the hash of a pubkey. | ||
* OP_DUP OP_HASH160 [pubKeyHash] OP_EQUALVERIFY OP_CHECKSIG | ||
* | ||
* Pubkey: | ||
* Paying to a public key directly. | ||
* [pubKey] OP_CHECKSIG | ||
* | ||
* Strange: | ||
* Any other script (no template matched). | ||
*/ | ||
Script.prototype.getOutType = function () { | ||
if (this.chunks[this.chunks.length-1] == OP_CHECKMULTISIG && this.chunks[this.chunks.length-2] <= 3) { | ||
/** | ||
* Compare the script to known templates of scriptPubKey. | ||
* | ||
* This method will compare the script to a small number of standard script | ||
* templates and return a string naming the detected type. | ||
* | ||
* Currently supported are: | ||
* Address: | ||
* Paying to a Bitcoin address which is the hash of a pubkey. | ||
* OP_DUP OP_HASH160 [pubKeyHash] OP_EQUALVERIFY OP_CHECKSIG | ||
* | ||
* Pubkey: | ||
* Paying to a public key directly. | ||
* [pubKey] OP_CHECKSIG | ||
* | ||
* Strange: | ||
* Any other script (no template matched). | ||
*/ | ||
Script.prototype.getOutType = function() { | ||
if (this.chunks[this.chunks.length - 1] == Opcode.map.OP_EQUAL && | ||
this.chunks[0] == Opcode.map.OP_HASH160 && | ||
this.chunks.length == 3) { | ||
// Transfer to M-OF-N | ||
return 'Multisig'; | ||
return 'P2SH' | ||
} else if (this.chunks.length == 5 && | ||
this.chunks[0] == OP_DUP && | ||
this.chunks[1] == OP_HASH160 && | ||
this.chunks[3] == OP_EQUALVERIFY && | ||
this.chunks[4] == OP_CHECKSIG) { | ||
this.chunks[0] == Opcode.map.OP_DUP && | ||
this.chunks[1] == Opcode.map.OP_HASH160 && | ||
this.chunks[3] == Opcode.map.OP_EQUALVERIFY && | ||
this.chunks[4] == Opcode.map.OP_CHECKSIG) { | ||
// Transfer to Bitcoin address | ||
return 'Address'; | ||
} else if (this.chunks.length == 2 && | ||
this.chunks[1] == OP_CHECKSIG) { | ||
// Transfer to IP address | ||
return 'Pubkey'; | ||
return 'Pubkey' | ||
} else { | ||
return 'Strange'; | ||
} | ||
return 'Strange' | ||
} | ||
} | ||
/** | ||
* Returns the affected address hash for this output. | ||
* | ||
* For standard transactions, this will return the hash of the pubKey that | ||
* can spend this output. | ||
* | ||
* In the future, for payToScriptHash outputs, this will return the | ||
* scriptHash. Note that non-standard and standard payToScriptHash transactions | ||
* look the same | ||
* | ||
* This method is useful for indexing transactions. | ||
*/ | ||
Script.prototype.simpleOutHash = function () | ||
{ | ||
switch (this.getOutType()) { | ||
case 'Address': | ||
return this.chunks[2]; | ||
case 'Pubkey': | ||
return Bitcoin.Util.sha256ripe160(this.chunks[0]); | ||
default: | ||
throw new Error("Encountered non-standard scriptPubKey"); | ||
} | ||
}; | ||
/** | ||
* Returns the address corresponding to this output in hash160 form. | ||
* Assumes strange scripts are P2SH | ||
*/ | ||
Script.prototype.toScriptHash = function() { | ||
var outType = this.getOutType() | ||
/** | ||
* Old name for Script#simpleOutHash. | ||
* | ||
* @deprecated | ||
*/ | ||
Script.prototype.simpleOutPubKeyHash = Script.prototype.simpleOutHash; | ||
if (outType == 'Pubkey') { | ||
return this.chunks[2] | ||
} | ||
/** | ||
* Compare the script to known templates of scriptSig. | ||
* | ||
* This method will compare the script to a small number of standard script | ||
* templates and return a string naming the detected type. | ||
* | ||
* WARNING: Use this method with caution. It merely represents a heuristic | ||
* based on common transaction formats. A non-standard transaction could | ||
* very easily match one of these templates by accident. | ||
* | ||
* Currently supported are: | ||
* Address: | ||
* Paying to a Bitcoin address which is the hash of a pubkey. | ||
* [sig] [pubKey] | ||
* | ||
* Pubkey: | ||
* Paying to a public key directly. | ||
* [sig] | ||
* | ||
* Strange: | ||
* Any other script (no template matched). | ||
*/ | ||
Script.prototype.getInType = function () | ||
{ | ||
if (this.chunks.length == 1 && | ||
Bitcoin.Util.isArray(this.chunks[0])) { | ||
// Direct IP to IP transactions only have the signature in their scriptSig. | ||
// TODO: We could also check that the length of the data is correct. | ||
return 'Pubkey'; | ||
} else if (this.chunks.length == 2 && | ||
Bitcoin.Util.isArray(this.chunks[0]) && | ||
Bitcoin.Util.isArray(this.chunks[1])) { | ||
return 'Address'; | ||
} else { | ||
return 'Strange'; | ||
} | ||
}; | ||
if (outType == 'P2SH') { | ||
return util.sha256ripe160(this.buffer) | ||
} | ||
/** | ||
* Returns the affected public key for this input. | ||
* | ||
* This currently only works with payToPubKeyHash transactions. It will also | ||
* work in the future for standard payToScriptHash transactions that use a | ||
* single public key. | ||
* | ||
* However for multi-key and other complex transactions, this will only return | ||
* one of the keys or raise an error. Therefore, it is recommended for indexing | ||
* purposes to use Script#simpleInHash or Script#simpleOutHash instead. | ||
* | ||
* @deprecated | ||
*/ | ||
Script.prototype.simpleInPubKey = function () | ||
{ | ||
switch (this.getInType()) { | ||
return util.sha256ripe160(this.buffer) | ||
} | ||
//TODO: support testnet | ||
Script.prototype.getToAddress = function() { | ||
var outType = this.getOutType() | ||
if (outType == 'Pubkey') { | ||
return new Address(this.chunks[2]) | ||
} | ||
if (outType == 'P2SH') { | ||
return new Address(this.chunks[1], 5) | ||
} | ||
return new Address(this.chunks[1], 5) | ||
} | ||
//TODO: support testnet | ||
Script.prototype.getFromAddress = function(){ | ||
return new Address(this.simpleInHash()) | ||
} | ||
/** | ||
* Compare the script to known templates of scriptSig. | ||
* | ||
* This method will compare the script to a small number of standard script | ||
* templates and return a string naming the detected type. | ||
* | ||
* WARNING: Use this method with caution. It merely represents a heuristic | ||
* based on common transaction formats. A non-standard transaction could | ||
* very easily match one of these templates by accident. | ||
* | ||
* Currently supported are: | ||
* Address: | ||
* Paying to a Bitcoin address which is the hash of a pubkey. | ||
* [sig] [pubKey] | ||
* | ||
* Pubkey: | ||
* Paying to a public key directly. | ||
* [sig] | ||
* | ||
* Multisig: | ||
* Paying to M-of-N public keys. | ||
* | ||
* Strange: | ||
* Any other script (no template matched). | ||
*/ | ||
Script.prototype.getInType = function() { | ||
if (this.chunks.length == 1 && | ||
Array.isArray(this.chunks[0])) { | ||
// Direct IP to IP transactions only have the signature in their scriptSig. | ||
// TODO: We could also check that the length of the data is correct. | ||
return 'Pubkey' | ||
} else if (this.chunks.length == 2 && | ||
Array.isArray(this.chunks[0]) && | ||
Array.isArray(this.chunks[1])) { | ||
return 'Address' | ||
} else if (this.chunks[0] == Opcode.map.OP_0 && | ||
this.chunks.slice(1).reduce(function(t, chunk, i) { | ||
return t && Array.isArray(chunk) && (chunk[0] == 48 || i == this.chunks.length - 1) | ||
}, true)) { | ||
return 'Multisig' | ||
} else { | ||
return 'Strange' | ||
} | ||
} | ||
/** | ||
* Returns the affected public key for this input. | ||
* | ||
* This currently only works with payToPubKeyHash transactions. It will also | ||
* work in the future for standard payToScriptHash transactions that use a | ||
* single public key. | ||
* | ||
* However for multi-key and other complex transactions, this will only return | ||
* one of the keys or raise an error. Therefore, it is recommended for indexing | ||
* purposes to use Script#simpleInHash or Script#simpleOutHash instead. | ||
* | ||
* @deprecated | ||
*/ | ||
Script.prototype.simpleInPubKey = function() { | ||
switch (this.getInType()) { | ||
case 'Address': | ||
return this.chunks[1]; | ||
return this.chunks[1] | ||
case 'Pubkey': | ||
// TODO: Theoretically, we could recover the pubkey from the sig here. | ||
// See https://bitcointalk.org/?topic=6430.0 | ||
throw new Error("Script does not contain pubkey."); | ||
throw new Error('Script does not contain pubkey') | ||
default: | ||
throw new Error("Encountered non-standard scriptSig"); | ||
} | ||
}; | ||
throw new Error('Encountered non-standard scriptSig') | ||
} | ||
} | ||
/** | ||
* Returns the affected address hash for this input. | ||
* | ||
* For standard transactions, this will return the hash of the pubKey that | ||
* can spend this output. | ||
* | ||
* In the future, for standard payToScriptHash inputs, this will return the | ||
* scriptHash. | ||
* | ||
* Note: This function provided for convenience. If you have the corresponding | ||
* scriptPubKey available, you are urged to use Script#simpleOutHash instead | ||
* as it is more reliable for non-standard payToScriptHash transactions. | ||
* | ||
* This method is useful for indexing transactions. | ||
*/ | ||
Script.prototype.simpleInHash = function () | ||
{ | ||
return Bitcoin.Util.sha256ripe160(this.simpleInPubKey()); | ||
}; | ||
/** | ||
* Returns the affected address hash for this input. | ||
* | ||
* For standard transactions, this will return the hash of the pubKey that | ||
* can spend this output. | ||
* | ||
* In the future, for standard payToScriptHash inputs, this will return the | ||
* scriptHash. | ||
* | ||
* Note: This function provided for convenience. If you have the corresponding | ||
* scriptPubKey available, you are urged to use Script#simpleOutHash instead | ||
* as it is more reliable for non-standard payToScriptHash transactions. | ||
* | ||
* This method is useful for indexing transactions. | ||
*/ | ||
Script.prototype.simpleInHash = function() { | ||
return util.sha256ripe160(this.simpleInPubKey()) | ||
} | ||
/** | ||
* Old name for Script#simpleInHash. | ||
* | ||
* @deprecated | ||
*/ | ||
Script.prototype.simpleInPubKeyHash = Script.prototype.simpleInHash; | ||
/** | ||
* Old name for Script#simpleInHash. | ||
* | ||
* @deprecated | ||
*/ | ||
Script.prototype.simpleInPubKeyHash = Script.prototype.simpleInHash | ||
/** | ||
* Add an op code to the script. | ||
*/ | ||
Script.prototype.writeOp = function (opcode) | ||
{ | ||
this.buffer.push(opcode); | ||
this.chunks.push(opcode); | ||
}; | ||
/** | ||
* Add an op code to the script. | ||
*/ | ||
Script.prototype.writeOp = function(opcode) { | ||
this.buffer.push(opcode) | ||
this.chunks.push(opcode) | ||
} | ||
/** | ||
* Add a data chunk to the script. | ||
*/ | ||
Script.prototype.writeBytes = function (data) | ||
{ | ||
if (data.length < OP_PUSHDATA1) { | ||
this.buffer.push(data.length); | ||
} else if (data.length <= 0xff) { | ||
this.buffer.push(OP_PUSHDATA1); | ||
this.buffer.push(data.length); | ||
} else if (data.length <= 0xffff) { | ||
this.buffer.push(OP_PUSHDATA2); | ||
this.buffer.push(data.length & 0xff); | ||
this.buffer.push((data.length >>> 8) & 0xff); | ||
} else { | ||
this.buffer.push(OP_PUSHDATA4); | ||
this.buffer.push(data.length & 0xff); | ||
this.buffer.push((data.length >>> 8) & 0xff); | ||
this.buffer.push((data.length >>> 16) & 0xff); | ||
this.buffer.push((data.length >>> 24) & 0xff); | ||
} | ||
this.buffer = this.buffer.concat(data); | ||
this.chunks.push(data); | ||
}; | ||
/** | ||
* Add a data chunk to the script. | ||
*/ | ||
Script.prototype.writeBytes = function(data) { | ||
// FIXME: Script module doesn't support buffers yet | ||
if (Buffer.isBuffer(data)) { | ||
data = Array.prototype.map.bind(data, function(x) { return x })() | ||
} | ||
/** | ||
* Create a standard payToPubKeyHash output. | ||
*/ | ||
Script.createOutputScript = function (address) | ||
{ | ||
var script = new Script(); | ||
script.writeOp(OP_DUP); | ||
script.writeOp(OP_HASH160); | ||
script.writeBytes(address.hash); | ||
script.writeOp(OP_EQUALVERIFY); | ||
script.writeOp(OP_CHECKSIG); | ||
return script; | ||
}; | ||
/** | ||
* Extract bitcoin addresses from an output script | ||
*/ | ||
Script.prototype.extractAddresses = function (addresses) | ||
{ | ||
switch (this.getOutType()) { | ||
case 'Address': | ||
addresses.push(new Address(this.chunks[2])); | ||
return 1; | ||
case 'Pubkey': | ||
addresses.push(new Address(Util.sha256ripe160(this.chunks[0]))); | ||
return 1; | ||
case 'Multisig': | ||
for (var i = 1; i < this.chunks.length-2; ++i) { | ||
addresses.push(new Address(Util.sha256ripe160(this.chunks[i]))); | ||
} | ||
return this.chunks[0] - OP_1 + 1; | ||
default: | ||
throw new Error("Encountered non-standard scriptPubKey"); | ||
} | ||
}; | ||
if (data.length < Opcode.map.OP_PUSHDATA1) { | ||
this.buffer.push(data.length) | ||
} else if (data.length <= 0xff) { | ||
this.buffer.push(Opcode.map.OP_PUSHDATA1) | ||
this.buffer.push(data.length) | ||
} else if (data.length <= 0xffff) { | ||
this.buffer.push(Opcode.map.OP_PUSHDATA2) | ||
this.buffer.push(data.length & 0xff) | ||
this.buffer.push((data.length >>> 8) & 0xff) | ||
} else { | ||
this.buffer.push(Opcode.map.OP_PUSHDATA4) | ||
this.buffer.push(data.length & 0xff) | ||
this.buffer.push((data.length >>> 8) & 0xff) | ||
this.buffer.push((data.length >>> 16) & 0xff) | ||
this.buffer.push((data.length >>> 24) & 0xff) | ||
} | ||
this.buffer = this.buffer.concat(data) | ||
this.chunks.push(data) | ||
} | ||
/** | ||
* Create an m-of-n output script | ||
*/ | ||
Script.createMultiSigOutputScript = function (m, pubkeys) | ||
{ | ||
var script = new Bitcoin.Script(); | ||
script.writeOp(OP_1 + m - 1); | ||
for (var i = 0; i < pubkeys.length; ++i) { | ||
script.writeBytes(pubkeys[i]); | ||
} | ||
script.writeOp(OP_1 + pubkeys.length - 1); | ||
/** | ||
* Create an output for an address | ||
*/ | ||
Script.createOutputScript = function(address) { | ||
var script = new Script() | ||
address = new Address(address) | ||
if (address.version == network.mainnet.p2shVersion || | ||
address.version == network.testnet.p2shVersion) { | ||
// Standard pay-to-script-hash | ||
script.writeOp(Opcode.map.OP_HASH160) | ||
script.writeBytes(address.hash) | ||
script.writeOp(Opcode.map.OP_EQUAL) | ||
} | ||
else { | ||
// Standard pay-to-pubkey-hash | ||
script.writeOp(Opcode.map.OP_DUP) | ||
script.writeOp(Opcode.map.OP_HASH160) | ||
script.writeBytes(address.hash) | ||
script.writeOp(Opcode.map.OP_EQUALVERIFY) | ||
script.writeOp(Opcode.map.OP_CHECKSIG) | ||
} | ||
return script | ||
} | ||
script.writeOp(OP_CHECKMULTISIG); | ||
/** | ||
* Extract pubkeys from a multisig script | ||
*/ | ||
return script; | ||
}; | ||
Script.prototype.extractPubkeys = function() { | ||
return this.chunks.filter(function(chunk) { | ||
return(chunk[0] == 4 && chunk.length == 65 || chunk[0] < 4 && chunk.length == 33) | ||
}) | ||
} | ||
/** | ||
* Create a standard payToPubKeyHash input. | ||
*/ | ||
Script.createInputScript = function (signature, pubKey) | ||
{ | ||
var script = new Script(); | ||
script.writeBytes(signature); | ||
script.writeBytes(pubKey); | ||
return script; | ||
}; | ||
/** | ||
* Create an m-of-n output script | ||
*/ | ||
Script.createMultiSigOutputScript = function(m, pubkeys) { | ||
var script = new Script() | ||
pubkeys = pubkeys.sort() | ||
Script.prototype.clone = function () | ||
{ | ||
return new Script(this.buffer); | ||
}; | ||
})(); | ||
script.writeOp(Opcode.map.OP_1 + m - 1) | ||
for (var i = 0; i < pubkeys.length; ++i) { | ||
script.writeBytes(pubkeys[i]) | ||
} | ||
script.writeOp(Opcode.map.OP_1 + pubkeys.length - 1) | ||
script.writeOp(Opcode.map.OP_CHECKMULTISIG) | ||
return script | ||
} | ||
/** | ||
* Create a standard payToPubKeyHash input. | ||
*/ | ||
Script.createInputScript = function(signature, pubKey) { | ||
var script = new Script() | ||
script.writeBytes(signature) | ||
script.writeBytes(pubKey) | ||
return script | ||
} | ||
/** | ||
* Create a multisig input | ||
*/ | ||
Script.createMultiSigInputScript = function(signatures, script) { | ||
script = new Script(script) | ||
var k = script.chunks[0][0] | ||
//Not enough sigs | ||
if (signatures.length < k) return false; | ||
var inScript = new Script() | ||
inScript.writeOp(Opcode.map.OP_0) | ||
signatures.map(function(sig) { | ||
inScript.writeBytes(sig) | ||
}) | ||
inScript.writeBytes(script.buffer) | ||
return inScript | ||
} | ||
Script.prototype.clone = function() { | ||
return new Script(this.buffer) | ||
} | ||
module.exports = Script |
@@ -1,445 +0,448 @@ | ||
(function () { | ||
var Script = Bitcoin.Script; | ||
var BigInteger = require('./jsbn/jsbn') | ||
var Script = require('./script') | ||
var util = require('./util') | ||
var convert = require('./convert') | ||
var ECKey = require('./eckey').ECKey | ||
var ECDSA = require('./ecdsa') | ||
var Address = require('./address') | ||
var SHA256 = require('crypto-js/sha256') | ||
var Transaction = Bitcoin.Transaction = function (doc) { | ||
this.version = 1; | ||
this.lock_time = 0; | ||
this.ins = []; | ||
this.outs = []; | ||
this.timestamp = null; | ||
this.block = null; | ||
var Transaction = function (doc) { | ||
if (!(this instanceof Transaction)) { return new Transaction(doc) } | ||
this.version = 1 | ||
this.locktime = 0 | ||
this.ins = [] | ||
this.outs = [] | ||
this.defaultSequence = [255, 255, 255, 255] // 0xFFFFFFFF | ||
if (doc) { | ||
if (doc.hash) this.hash = doc.hash; | ||
if (doc.version) this.version = doc.version; | ||
if (doc.lock_time) this.lock_time = doc.lock_time; | ||
if (doc.ins && doc.ins.length) { | ||
for (var i = 0; i < doc.ins.length; i++) { | ||
this.addInput(new TransactionIn(doc.ins[i])); | ||
} | ||
} | ||
if (doc.outs && doc.outs.length) { | ||
for (var i = 0; i < doc.outs.length; i++) { | ||
this.addOutput(new TransactionOut(doc.outs[i])); | ||
} | ||
} | ||
if (doc.timestamp) this.timestamp = doc.timestamp; | ||
if (doc.block) this.block = doc.block; | ||
if (doc) { | ||
if (typeof doc == "string" || Array.isArray(doc)) { | ||
doc = Transaction.deserialize(doc) | ||
} | ||
}; | ||
/** | ||
* Turn transaction data into Transaction objects. | ||
* | ||
* Takes an array of plain JavaScript objects containing transaction data and | ||
* returns an array of Transaction objects. | ||
*/ | ||
Transaction.objectify = function (txs) { | ||
var objs = []; | ||
for (var i = 0; i < txs.length; i++) { | ||
objs.push(new Transaction(txs[i])); | ||
if (doc.hash) this.hash = doc.hash; | ||
if (doc.version) this.version = doc.version; | ||
if (doc.locktime) this.locktime = doc.locktime; | ||
if (doc.ins && doc.ins.length) { | ||
doc.ins.forEach(function(input) { | ||
this.addInput(new TransactionIn(input)) | ||
}, this) | ||
} | ||
return objs; | ||
}; | ||
/** | ||
* Create a new txin. | ||
* | ||
* Can be called with an existing TransactionIn object to add it to the | ||
* transaction. Or it can be called with a Transaction object and an integer | ||
* output index, in which case a new TransactionIn object pointing to the | ||
* referenced output will be created. | ||
* | ||
* Note that this method does not sign the created input. | ||
*/ | ||
Transaction.prototype.addInput = function (tx, outIndex) { | ||
if (arguments[0] instanceof TransactionIn) { | ||
this.ins.push(arguments[0]); | ||
} else { | ||
this.ins.push(new TransactionIn({ | ||
outpoint: { | ||
hash: tx.hash, | ||
index: outIndex | ||
}, | ||
script: new Bitcoin.Script(), | ||
sequence: 4294967295 | ||
})); | ||
if (doc.outs && doc.outs.length) { | ||
doc.outs.forEach(function(output) { | ||
this.addOutput(new TransactionOut(output)) | ||
}, this) | ||
} | ||
}; | ||
/** | ||
* Create a new txout. | ||
* | ||
* Can be called with an existing TransactionOut object to add it to the | ||
* transaction. Or it can be called with an Address object and a BigInteger | ||
* for the amount, in which case a new TransactionOut object with those | ||
* values will be created. | ||
*/ | ||
Transaction.prototype.addOutput = function (address, value) { | ||
if (arguments[0] instanceof TransactionOut) { | ||
this.outs.push(arguments[0]); | ||
} else { | ||
if (value instanceof BigInteger) { | ||
value = value.toByteArrayUnsigned().reverse(); | ||
while (value.length < 8) value.push(0); | ||
} else if (Bitcoin.Util.isArray(value)) { | ||
// Nothing to do | ||
} | ||
this.hash = this.hash || this.getHash() | ||
} | ||
} | ||
this.outs.push(new TransactionOut({ | ||
value: value, | ||
script: Script.createOutputScript(address) | ||
})); | ||
} | ||
}; | ||
/** | ||
* Create a new txin. | ||
* | ||
* Can be called with any of: | ||
* | ||
* - An existing TransactionIn object | ||
* - A transaction and an index | ||
* - A transaction hash and an index | ||
* - A single string argument of the form txhash:index | ||
* | ||
* Note that this method does not sign the created input. | ||
*/ | ||
Transaction.prototype.addInput = function (tx, outIndex) { | ||
if (arguments[0] instanceof TransactionIn) { | ||
this.ins.push(arguments[0]) | ||
} | ||
else if (arguments[0].length > 65) { | ||
var args = arguments[0].split(':') | ||
return this.addInput(args[0], args[1]) | ||
} | ||
else { | ||
var hash = typeof tx === "string" ? tx : tx.hash | ||
hash = Array.isArray(hash) ? convert.bytesToHex(hash) : hash | ||
/** | ||
* Serialize this transaction. | ||
* | ||
* Returns the transaction as a byte array in the standard Bitcoin binary | ||
* format. This method is byte-perfect, i.e. the resulting byte array can | ||
* be hashed to get the transaction's standard Bitcoin hash. | ||
*/ | ||
Transaction.prototype.serialize = function () | ||
{ | ||
var buffer = []; | ||
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(this.version)]).reverse()); | ||
buffer = buffer.concat(Bitcoin.Util.numToVarInt(this.ins.length)); | ||
for (var i = 0; i < this.ins.length; i++) { | ||
var txin = this.ins[i]; | ||
buffer = buffer.concat(Crypto.util.base64ToBytes(txin.outpoint.hash)); | ||
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(txin.outpoint.index)]).reverse()); | ||
var scriptBytes = txin.script.buffer; | ||
buffer = buffer.concat(Bitcoin.Util.numToVarInt(scriptBytes.length)); | ||
buffer = buffer.concat(scriptBytes); | ||
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(txin.sequence)]).reverse()); | ||
} | ||
buffer = buffer.concat(Bitcoin.Util.numToVarInt(this.outs.length)); | ||
for (var i = 0; i < this.outs.length; i++) { | ||
var txout = this.outs[i]; | ||
buffer = buffer.concat(txout.value); | ||
var scriptBytes = txout.script.buffer; | ||
buffer = buffer.concat(Bitcoin.Util.numToVarInt(scriptBytes.length)); | ||
buffer = buffer.concat(scriptBytes); | ||
} | ||
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(this.lock_time)]).reverse()); | ||
this.ins.push(new TransactionIn({ | ||
outpoint: { | ||
hash: hash, | ||
index: outIndex | ||
}, | ||
script: new Script(), | ||
sequence: this.defaultSequence | ||
})) | ||
} | ||
} | ||
return buffer; | ||
}; | ||
/** | ||
* Create a new txout. | ||
* | ||
* Can be called with: | ||
* | ||
* i) An existing TransactionOut object | ||
* ii) An address object or an address and a value | ||
* iii) An address:value string | ||
* | ||
*/ | ||
Transaction.prototype.addOutput = function (address, value) { | ||
if (arguments[0] instanceof TransactionOut) { | ||
this.outs.push(arguments[0]) | ||
return | ||
} | ||
var OP_CODESEPARATOR = 171; | ||
if (arguments[0].indexOf(':') >= 0) { | ||
var args = arguments[0].split(':') | ||
address = args[0] | ||
value = parseInt(args[1]) | ||
} | ||
var SIGHASH_ALL = 1; | ||
var SIGHASH_NONE = 2; | ||
var SIGHASH_SINGLE = 3; | ||
var SIGHASH_ANYONECANPAY = 80; | ||
this.outs.push(new TransactionOut({ | ||
value: value, | ||
script: Script.createOutputScript(address) | ||
})) | ||
} | ||
/** | ||
* 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. | ||
*/ | ||
Transaction.prototype.hashTransactionForSignature = | ||
function (connectedScript, inIndex, hashType) | ||
{ | ||
var txTmp = this.clone(); | ||
/** | ||
* Serialize this transaction. | ||
* | ||
* Returns the transaction as a byte array in the standard Bitcoin binary | ||
* format. This method is byte-perfect, i.e. the resulting byte array can | ||
* be hashed to get the transaction's standard Bitcoin hash. | ||
*/ | ||
Transaction.prototype.serialize = function () { | ||
var buffer = [] | ||
buffer = buffer.concat(convert.numToBytes(parseInt(this.version), 4)) | ||
buffer = buffer.concat(convert.numToVarInt(this.ins.length)) | ||
// In case concatenating two scripts ends up with two codeseparators, | ||
// or an extra one at the end, this prevents all those possible | ||
// incompatibilities. | ||
/*scriptCode = scriptCode.filter(function (val) { | ||
return val !== OP_CODESEPARATOR; | ||
});*/ | ||
this.ins.forEach(function(txin) { | ||
// Why do blockchain.info, blockexplorer.com, sx and just about everybody | ||
// else use little-endian hashes? No idea... | ||
buffer = buffer.concat(convert.hexToBytes(txin.outpoint.hash).reverse()) | ||
// Blank out other inputs' signatures | ||
for (var i = 0; i < txTmp.ins.length; i++) { | ||
txTmp.ins[i].script = new Script(); | ||
} | ||
buffer = buffer.concat(convert.numToBytes(parseInt(txin.outpoint.index), 4)) | ||
txTmp.ins[inIndex].script = connectedScript; | ||
var scriptBytes = txin.script.buffer | ||
buffer = buffer.concat(convert.numToVarInt(scriptBytes.length)) | ||
buffer = buffer.concat(scriptBytes) | ||
buffer = buffer.concat(txin.sequence) | ||
}) | ||
// Blank out some of the outputs | ||
if ((hashType & 0x1f) == SIGHASH_NONE) { | ||
txTmp.outs = []; | ||
buffer = buffer.concat(convert.numToVarInt(this.outs.length)) | ||
// Let the others update at will | ||
for (var i = 0; i < txTmp.ins.length; i++) | ||
if (i != inIndex) | ||
txTmp.ins[i].sequence = 0; | ||
} else if ((hashType & 0x1f) == SIGHASH_SINGLE) { | ||
// TODO: Implement | ||
} | ||
this.outs.forEach(function(txout) { | ||
buffer = buffer.concat(convert.numToBytes(txout.value,8)) | ||
// Blank out other inputs completely, not recommended for open transactions | ||
if (hashType & SIGHASH_ANYONECANPAY) { | ||
txTmp.ins = [txTmp.ins[inIndex]]; | ||
} | ||
var scriptBytes = txout.script.buffer | ||
buffer = buffer.concat(convert.numToVarInt(scriptBytes.length)) | ||
buffer = buffer.concat(scriptBytes) | ||
}) | ||
var buffer = txTmp.serialize(); | ||
buffer = buffer.concat(convert.numToBytes(parseInt(this.locktime), 4)) | ||
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(hashType)]).reverse()); | ||
return buffer | ||
} | ||
var hash1 = Crypto.SHA256(buffer, {asBytes: true}); | ||
Transaction.prototype.serializeHex = function() { | ||
return convert.bytesToHex(this.serialize()) | ||
} | ||
return Crypto.SHA256(hash1, {asBytes: true}); | ||
}; | ||
//var OP_CODESEPARATOR = 171 | ||
/** | ||
* Calculate and return the transaction's hash. | ||
*/ | ||
Transaction.prototype.getHash = function () | ||
{ | ||
var buffer = this.serialize(); | ||
return Crypto.SHA256(Crypto.SHA256(buffer, {asBytes: true}), {asBytes: true}); | ||
}; | ||
var SIGHASH_ALL = 1 | ||
var SIGHASH_NONE = 2 | ||
var SIGHASH_SINGLE = 3 | ||
var SIGHASH_ANYONECANPAY = 80 | ||
/** | ||
* Create a copy of this transaction object. | ||
*/ | ||
Transaction.prototype.clone = function () | ||
{ | ||
var newTx = new Transaction(); | ||
newTx.version = this.version; | ||
newTx.lock_time = this.lock_time; | ||
for (var i = 0; i < this.ins.length; i++) { | ||
var txin = this.ins[i].clone(); | ||
newTx.addInput(txin); | ||
} | ||
for (var i = 0; i < this.outs.length; i++) { | ||
var txout = this.outs[i].clone(); | ||
newTx.addOutput(txout); | ||
} | ||
return newTx; | ||
}; | ||
/** | ||
* 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. | ||
*/ | ||
Transaction.prototype.hashTransactionForSignature = | ||
function (connectedScript, inIndex, hashType) | ||
{ | ||
var txTmp = this.clone() | ||
/** | ||
* Analyze how this transaction affects a wallet. | ||
* | ||
* Returns an object with properties 'impact', 'type' and 'addr'. | ||
* | ||
* 'impact' is an object, see Transaction#calcImpact. | ||
* | ||
* 'type' can be one of the following: | ||
* | ||
* recv: | ||
* This is an incoming transaction, the wallet received money. | ||
* 'addr' contains the first address in the wallet that receives money | ||
* from this transaction. | ||
* | ||
* self: | ||
* This is an internal transaction, money was sent within the wallet. | ||
* 'addr' is undefined. | ||
* | ||
* sent: | ||
* This is an outgoing transaction, money was sent out from the wallet. | ||
* 'addr' contains the first external address, i.e. the recipient. | ||
* | ||
* other: | ||
* This method was unable to detect what the transaction does. Either it | ||
*/ | ||
Transaction.prototype.analyze = function (wallet) { | ||
if (!(wallet instanceof Bitcoin.Wallet)) return null; | ||
// In case concatenating two scripts ends up with two codeseparators, | ||
// or an extra one at the end, this prevents all those possible | ||
// incompatibilities. | ||
/*scriptCode = scriptCode.filter(function (val) { | ||
return val !== OP_CODESEPARATOR | ||
});*/ | ||
var allFromMe = true, | ||
allToMe = true, | ||
firstRecvHash = null, | ||
firstMeRecvHash = null, | ||
firstSendHash = null; | ||
// Blank out other inputs' signatures | ||
txTmp.ins.forEach(function(txin) { | ||
txin.script = new Script() | ||
}) | ||
for (var i = this.outs.length-1; i >= 0; i--) { | ||
var txout = this.outs[i]; | ||
var hash = txout.script.simpleOutPubKeyHash(); | ||
if (!wallet.hasHash(hash)) { | ||
allToMe = false; | ||
} else { | ||
firstMeRecvHash = hash; | ||
txTmp.ins[inIndex].script = connectedScript | ||
// Blank out some of the outputs | ||
if ((hashType & 0x1f) == SIGHASH_NONE) { | ||
txTmp.outs = [] | ||
// Let the others update at will | ||
txTmp.ins.forEach(function(txin, i) { | ||
if (i != inIndex) { | ||
txTmp.ins[i].sequence = 0 | ||
} | ||
firstRecvHash = hash; | ||
} | ||
for (var i = this.ins.length-1; i >= 0; i--) { | ||
var txin = this.ins[i]; | ||
firstSendHash = txin.script.simpleInPubKeyHash(); | ||
if (!wallet.hasHash(firstSendHash)) { | ||
allFromMe = false; | ||
break; | ||
} | ||
} | ||
}) | ||
var impact = this.calcImpact(wallet); | ||
} else if ((hashType & 0x1f) == SIGHASH_SINGLE) { | ||
// TODO: Implement | ||
} | ||
var analysis = {}; | ||
// Blank out other inputs completely, not recommended for open transactions | ||
if (hashType & SIGHASH_ANYONECANPAY) { | ||
txTmp.ins = [txTmp.ins[inIndex]] | ||
} | ||
analysis.impact = impact; | ||
var buffer = txTmp.serialize() | ||
if (impact.sign > 0 && impact.value.compareTo(BigInteger.ZERO) > 0) { | ||
analysis.type = 'recv'; | ||
analysis.addr = new Bitcoin.Address(firstMeRecvHash); | ||
} else if (allFromMe && allToMe) { | ||
analysis.type = 'self'; | ||
} else if (allFromMe) { | ||
analysis.type = 'sent'; | ||
// TODO: Right now, firstRecvHash is the first output, which - if the | ||
// transaction was not generated by this library could be the | ||
// change address. | ||
analysis.addr = new Bitcoin.Address(firstRecvHash); | ||
} else { | ||
analysis.type = "other"; | ||
} | ||
buffer = buffer.concat(convert.numToBytes(parseInt(hashType), 4)) | ||
buffer = convert.bytesToWordArray(buffer) | ||
return analysis; | ||
}; | ||
return convert.wordArrayToBytes(SHA256(SHA256(buffer))) | ||
} | ||
/** | ||
* Get a human-readable version of the data returned by Transaction#analyze. | ||
* | ||
* This is merely a convenience function. Clients should consider implementing | ||
* this themselves based on their UI, I18N, etc. | ||
*/ | ||
Transaction.prototype.getDescription = function (wallet) { | ||
var analysis = this.analyze(wallet); | ||
/** | ||
* Calculate and return the transaction's hash. | ||
* Reverses hash since blockchain.info, blockexplorer.com and others | ||
* use little-endian hashes for some stupid reason | ||
*/ | ||
Transaction.prototype.getHash = function () | ||
{ | ||
var buffer = convert.bytesToWordArray(this.serialize()) | ||
return convert.wordArrayToBytes(SHA256(SHA256(buffer))).reverse() | ||
} | ||
if (!analysis) return ""; | ||
Transaction.prototype.clone = function () | ||
{ | ||
var newTx = new Transaction() | ||
newTx.version = this.version | ||
newTx.locktime = this.locktime | ||
switch (analysis.type) { | ||
case 'recv': | ||
return "Received with "+analysis.addr; | ||
break; | ||
this.ins.forEach(function(txin) { | ||
newTx.addInput(txin.clone()) | ||
}) | ||
case 'sent': | ||
return "Payment to "+analysis.addr; | ||
break; | ||
this.outs.forEach(function(txout) { | ||
newTx.addOutput(txout.clone()) | ||
}) | ||
case 'self': | ||
return "Payment to yourself"; | ||
break; | ||
return newTx | ||
} | ||
case 'other': | ||
default: | ||
return ""; | ||
} | ||
}; | ||
Transaction.deserialize = function(buffer) { | ||
if (typeof buffer == "string") { | ||
buffer = convert.hexToBytes(buffer) | ||
} | ||
var pos = 0 | ||
var readAsInt = function(bytes) { | ||
if (bytes === 0) return 0; | ||
pos++; | ||
return buffer[pos-1] + readAsInt(bytes-1) * 256 | ||
} | ||
var readVarInt = function() { | ||
var bytes = buffer.slice(pos, pos + 9) // maximum possible number of bytes to read | ||
var result = convert.varIntToNum(bytes) | ||
/** | ||
* Get the total amount of a transaction's outputs. | ||
*/ | ||
Transaction.prototype.getTotalOutValue = function () { | ||
var totalValue = BigInteger.ZERO; | ||
for (var j = 0; j < this.outs.length; j++) { | ||
var txout = this.outs[j]; | ||
totalValue = totalValue.add(Bitcoin.Util.valueToBigInt(txout.value)); | ||
} | ||
return totalValue; | ||
}; | ||
pos += result.bytes.length | ||
return result.number | ||
} | ||
var readBytes = function(bytes) { | ||
pos += bytes | ||
return buffer.slice(pos - bytes, pos) | ||
} | ||
var readVarString = function() { | ||
var size = readVarInt() | ||
return readBytes(size) | ||
} | ||
var obj = { | ||
ins: [], | ||
outs: [] | ||
} | ||
obj.version = readAsInt(4) | ||
var ins = readVarInt() | ||
var i | ||
/** | ||
* Old name for Transaction#getTotalOutValue. | ||
* | ||
* @deprecated | ||
*/ | ||
Transaction.prototype.getTotalValue = Transaction.prototype.getTotalOutValue; | ||
for (i = 0; i < ins; i++) { | ||
obj.ins.push({ | ||
outpoint: { | ||
hash: convert.bytesToHex(readBytes(32).reverse()), | ||
index: readAsInt(4) | ||
}, | ||
script: new Script(readVarString()), | ||
sequence: readBytes(4) | ||
}) | ||
} | ||
var outs = readVarInt() | ||
/** | ||
* Calculates the impact a transaction has on this wallet. | ||
* | ||
* Based on the its public keys, the wallet will calculate the | ||
* credit or debit of this transaction. | ||
* | ||
* It will return an object with two properties: | ||
* - sign: 1 or -1 depending on sign of the calculated impact. | ||
* - value: amount of calculated impact | ||
* | ||
* @returns Object Impact on wallet | ||
*/ | ||
Transaction.prototype.calcImpact = function (wallet) { | ||
if (!(wallet instanceof Bitcoin.Wallet)) return BigInteger.ZERO; | ||
for (i = 0; i < outs; i++) { | ||
obj.outs.push({ | ||
value: convert.bytesToNum(readBytes(8)), | ||
script: new Script(readVarString()) | ||
}) | ||
} | ||
// Calculate credit to us from all outputs | ||
var valueOut = BigInteger.ZERO; | ||
for (var j = 0; j < this.outs.length; j++) { | ||
var txout = this.outs[j]; | ||
var hash = Crypto.util.bytesToBase64(txout.script.simpleOutPubKeyHash()); | ||
if (wallet.hasHash(hash)) { | ||
valueOut = valueOut.add(Bitcoin.Util.valueToBigInt(txout.value)); | ||
} | ||
} | ||
obj.locktime = readAsInt(4) | ||
// Calculate debit to us from all ins | ||
var valueIn = BigInteger.ZERO; | ||
for (var j = 0; j < this.ins.length; j++) { | ||
var txin = this.ins[j]; | ||
var hash = Crypto.util.bytesToBase64(txin.script.simpleInPubKeyHash()); | ||
if (wallet.hasHash(hash)) { | ||
var fromTx = wallet.txIndex[txin.outpoint.hash]; | ||
if (fromTx) { | ||
valueIn = valueIn.add(Bitcoin.Util.valueToBigInt(fromTx.outs[txin.outpoint.index].value)); | ||
} | ||
} | ||
} | ||
if (valueOut.compareTo(valueIn) >= 0) { | ||
return { | ||
sign: 1, | ||
value: valueOut.subtract(valueIn) | ||
}; | ||
} else { | ||
return { | ||
sign: -1, | ||
value: valueIn.subtract(valueOut) | ||
}; | ||
} | ||
}; | ||
return new Transaction(obj) | ||
} | ||
var TransactionIn = Bitcoin.TransactionIn = function (data) | ||
{ | ||
this.outpoint = data.outpoint; | ||
if (data.script instanceof Script) { | ||
this.script = data.script; | ||
} else { | ||
this.script = new Script(data.script); | ||
} | ||
this.sequence = data.sequence; | ||
}; | ||
/** | ||
* Signs a standard output at some index with the given key | ||
*/ | ||
Transaction.prototype.sign = function(index, key, type) { | ||
type = type || SIGHASH_ALL | ||
key = new ECKey(key) | ||
TransactionIn.prototype.clone = function () | ||
{ | ||
var newTxin = new TransactionIn({ | ||
outpoint: { | ||
hash: this.outpoint.hash, | ||
index: this.outpoint.index | ||
}, | ||
script: this.script.clone(), | ||
sequence: this.sequence | ||
}); | ||
return newTxin; | ||
}; | ||
// TODO: getPub is slow, sha256ripe160 probably is too. | ||
// This could be sped up a lot by providing these as inputs. | ||
var pub = key.getPub().toBytes(), | ||
hash160 = util.sha256ripe160(pub), | ||
script = Script.createOutputScript(new Address(hash160)), | ||
hash = this.hashTransactionForSignature(script, index, type), | ||
sig = key.sign(hash).concat([type]) | ||
this.ins[index].script = Script.createInputScript(sig, pub) | ||
} | ||
var TransactionOut = Bitcoin.TransactionOut = function (data) | ||
{ | ||
if (data.script instanceof Script) { | ||
this.script = data.script; | ||
} else { | ||
this.script = new Script(data.script); | ||
} | ||
// Takes outputs of the form [{ output: 'txhash:index', address: 'address' },...] | ||
Transaction.prototype.signWithKeys = function(keys, outputs, type) { | ||
type = type || SIGHASH_ALL | ||
if (Bitcoin.Util.isArray(data.value)) { | ||
this.value = data.value; | ||
} else if ("string" == typeof data.value) { | ||
var valueHex = (new BigInteger(data.value, 10)).toString(16); | ||
while (valueHex.length < 16) valueHex = "0" + valueHex; | ||
this.value = Crypto.util.hexToBytes(valueHex); | ||
var addrdata = keys.map(function(key) { | ||
key = new ECKey(key) | ||
return { | ||
key: key, | ||
address: key.getAddress().toString() | ||
} | ||
}; | ||
}) | ||
TransactionOut.prototype.clone = function () | ||
{ | ||
var newTxout = new TransactionOut({ | ||
script: this.script.clone(), | ||
value: this.value.slice(0) | ||
}); | ||
return newTxout; | ||
}; | ||
})(); | ||
var hmap = {} | ||
outputs.forEach(function(o) { | ||
hmap[o.output] = o | ||
}) | ||
for (var i = 0; i < this.ins.length; i++) { | ||
var outpoint = this.ins[i].outpoint.hash + ':' + this.ins[i].outpoint.index | ||
var histItem = hmap[outpoint] | ||
if (!histItem) continue; | ||
var thisInputAddrdata = addrdata.filter(function(a) { | ||
return a.address == histItem.address | ||
}) | ||
if (thisInputAddrdata.length === 0) continue; | ||
this.sign(i,thisInputAddrdata[0].key) | ||
} | ||
} | ||
/** | ||
* Signs a P2SH output at some index with the given key | ||
*/ | ||
Transaction.prototype.p2shsign = function(index, script, key, type) { | ||
script = new Script(script) | ||
key = new ECKey(key) | ||
type = type || SIGHASH_ALL | ||
var hash = this.hashTransactionForSignature(script, index, type), | ||
sig = key.sign(hash).concat([type]) | ||
return sig | ||
} | ||
Transaction.prototype.multisign = Transaction.prototype.p2shsign | ||
Transaction.prototype.applyMultisigs = function(index, script, sigs/*, type*/) { | ||
this.ins[index].script = Script.createMultiSigInputScript(sigs, script) | ||
} | ||
Transaction.prototype.validateSig = function(index, script, sig, pub) { | ||
script = new Script(script) | ||
var hash = this.hashTransactionForSignature(script,index,1) | ||
return ECDSA.verify(hash, convert.coerceToBytes(sig), | ||
convert.coerceToBytes(pub)) | ||
} | ||
Transaction.feePerKb = 20000 | ||
Transaction.prototype.estimateFee = function(feePerKb){ | ||
var uncompressedInSize = 180 | ||
var outSize = 34 | ||
var fixedPadding = 34 | ||
if(feePerKb == undefined) feePerKb = Transaction.feePerKb; | ||
var size = this.ins.length * uncompressedInSize + this.outs.length * outSize + fixedPadding | ||
return feePerKb * Math.ceil(size / 1000) | ||
} | ||
var TransactionIn = function (data) { | ||
if (typeof data == "string") { | ||
this.outpoint = { hash: data.split(':')[0], index: data.split(':')[1] } | ||
} else if (data.outpoint) { | ||
this.outpoint = data.outpoint | ||
} else { | ||
this.outpoint = { hash: data.hash, index: data.index } | ||
} | ||
if (data.scriptSig) { | ||
this.script = Script.fromScriptSig(data.scriptSig) | ||
} else if (data.script) { | ||
this.script = data.script | ||
} else { | ||
this.script = new Script(data.script) | ||
} | ||
this.sequence = data.sequence || this.defaultSequence | ||
} | ||
TransactionIn.prototype.clone = function () { | ||
return new TransactionIn({ | ||
outpoint: { | ||
hash: this.outpoint.hash, | ||
index: this.outpoint.index | ||
}, | ||
script: this.script.clone(), | ||
sequence: this.sequence | ||
}) | ||
} | ||
var TransactionOut = function (data) { | ||
this.script = | ||
data.script instanceof Script ? data.script.clone() | ||
: Array.isArray(data.script) ? new Script(data.script) | ||
: typeof data.script == "string" ? new Script(convert.hexToBytes(data.script)) | ||
: data.scriptPubKey ? Script.fromScriptSig(data.scriptPubKey) | ||
: data.address ? Script.createOutputScript(data.address) | ||
: new Script() | ||
if (this.script.buffer.length > 0) this.address = this.script.getToAddress(); | ||
this.value = | ||
Array.isArray(data.value) ? convert.bytesToNum(data.value) | ||
: "string" == typeof data.value ? parseInt(data.value) | ||
: data.value instanceof BigInteger ? parseInt(data.value.toString()) | ||
: data.value | ||
} | ||
TransactionOut.prototype.clone = function() { | ||
var newTxout = new TransactionOut({ | ||
script: this.script.clone(), | ||
value: this.value | ||
}) | ||
return newTxout | ||
} | ||
TransactionOut.prototype.scriptPubKey = function() { | ||
return convert.bytesToHex(this.script.buffer) | ||
} | ||
module.exports = { | ||
Transaction: Transaction, | ||
TransactionIn: TransactionIn, | ||
TransactionOut: TransactionOut | ||
} |
235
src/util.js
@@ -1,228 +0,13 @@ | ||
// BigInteger monkey patching | ||
BigInteger.valueOf = nbv; | ||
var convert = require('./convert.js') | ||
var Crypto = require('crypto-js') | ||
var RIPEMD160 = Crypto.RIPEMD160 | ||
var SHA256 = Crypto.SHA256 | ||
/** | ||
* Returns a byte array representation of the big integer. | ||
* | ||
* This returns the absolute of the contained value in big endian | ||
* form. A value of zero results in an empty array. | ||
*/ | ||
BigInteger.prototype.toByteArrayUnsigned = function () { | ||
var ba = this.abs().toByteArray(); | ||
if (ba.length) { | ||
if (ba[0] == 0) { | ||
ba = ba.slice(1); | ||
} | ||
return ba.map(function (v) { | ||
return (v < 0) ? v + 256 : v; | ||
}); | ||
} else { | ||
// Empty array, nothing to do | ||
return ba; | ||
} | ||
}; | ||
exports.sha256ripe160 = function (data) { | ||
var wordArray = RIPEMD160(SHA256(convert.bytesToWordArray(data))) | ||
return convert.wordArrayToBytes(wordArray) | ||
} | ||
/** | ||
* Turns a byte array into a big integer. | ||
* | ||
* This function will interpret a byte array as a big integer in big | ||
* endian notation and ignore leading zeros. | ||
*/ | ||
BigInteger.fromByteArrayUnsigned = function (ba) { | ||
if (!ba.length) { | ||
return ba.valueOf(0); | ||
} else if (ba[0] & 0x80) { | ||
// Prepend a zero so the BigInteger class doesn't mistake this | ||
// for a negative integer. | ||
return new BigInteger([0].concat(ba)); | ||
} else { | ||
return new BigInteger(ba); | ||
} | ||
}; | ||
/** | ||
* Converts big integer to signed byte representation. | ||
* | ||
* The format for this value uses a the most significant bit as a sign | ||
* bit. If the most significant bit is already occupied by the | ||
* absolute value, an extra byte is prepended and the sign bit is set | ||
* there. | ||
* | ||
* Examples: | ||
* | ||
* 0 => 0x00 | ||
* 1 => 0x01 | ||
* -1 => 0x81 | ||
* 127 => 0x7f | ||
* -127 => 0xff | ||
* 128 => 0x0080 | ||
* -128 => 0x8080 | ||
* 255 => 0x00ff | ||
* -255 => 0x80ff | ||
* 16300 => 0x3fac | ||
* -16300 => 0xbfac | ||
* 62300 => 0x00f35c | ||
* -62300 => 0x80f35c | ||
*/ | ||
BigInteger.prototype.toByteArraySigned = function () { | ||
var val = this.abs().toByteArrayUnsigned(); | ||
var neg = this.compareTo(BigInteger.ZERO) < 0; | ||
if (neg) { | ||
if (val[0] & 0x80) { | ||
val.unshift(0x80); | ||
} else { | ||
val[0] |= 0x80; | ||
} | ||
} else { | ||
if (val[0] & 0x80) { | ||
val.unshift(0x00); | ||
} | ||
} | ||
return val; | ||
}; | ||
/** | ||
* Parse a signed big integer byte representation. | ||
* | ||
* For details on the format please see BigInteger.toByteArraySigned. | ||
*/ | ||
BigInteger.fromByteArraySigned = function (ba) { | ||
// Check for negative value | ||
if (ba[0] & 0x80) { | ||
// Remove sign bit | ||
ba[0] &= 0x7f; | ||
return BigInteger.fromByteArrayUnsigned(ba).negate(); | ||
} else { | ||
return BigInteger.fromByteArrayUnsigned(ba); | ||
} | ||
}; | ||
// Console ignore | ||
var names = ["log", "debug", "info", "warn", "error", "assert", "dir", | ||
"dirxml", "group", "groupEnd", "time", "timeEnd", "count", | ||
"trace", "profile", "profileEnd"]; | ||
if ("undefined" == typeof window.console) window.console = {}; | ||
for (var i = 0; i < names.length; ++i) | ||
if ("undefined" == typeof window.console[names[i]]) | ||
window.console[names[i]] = function() {}; | ||
// Bitcoin utility functions | ||
Bitcoin.Util = { | ||
/** | ||
* Cross-browser compatibility version of Array.isArray. | ||
*/ | ||
isArray: Array.isArray || function(o) | ||
{ | ||
return Object.prototype.toString.call(o) === '[object Array]'; | ||
}, | ||
/** | ||
* Create an array of a certain length filled with a specific value. | ||
*/ | ||
makeFilledArray: function (len, val) | ||
{ | ||
var array = []; | ||
var i = 0; | ||
while (i < len) { | ||
array[i++] = val; | ||
} | ||
return array; | ||
}, | ||
/** | ||
* Turn an integer into a "var_int". | ||
* | ||
* "var_int" is a variable length integer used by Bitcoin's binary format. | ||
* | ||
* Returns a byte array. | ||
*/ | ||
numToVarInt: function (i) | ||
{ | ||
if (i < 0xfd) { | ||
// unsigned char | ||
return [i]; | ||
} else if (i <= 1<<16) { | ||
// unsigned short (LE) | ||
return [0xfd, i >>> 8, i & 255]; | ||
} else if (i <= 1<<32) { | ||
// unsigned int (LE) | ||
return [0xfe].concat(Crypto.util.wordsToBytes([i])); | ||
} else { | ||
// unsigned long long (LE) | ||
return [0xff].concat(Crypto.util.wordsToBytes([i >>> 32, i])); | ||
} | ||
}, | ||
/** | ||
* Parse a Bitcoin value byte array, returning a BigInteger. | ||
*/ | ||
valueToBigInt: function (valueBuffer) | ||
{ | ||
if (valueBuffer instanceof BigInteger) return valueBuffer; | ||
// Prepend zero byte to prevent interpretation as negative integer | ||
return BigInteger.fromByteArrayUnsigned(valueBuffer); | ||
}, | ||
/** | ||
* Format a Bitcoin value as a string. | ||
* | ||
* Takes a BigInteger or byte-array and returns that amount of Bitcoins in a | ||
* nice standard formatting. | ||
* | ||
* Examples: | ||
* 12.3555 | ||
* 0.1234 | ||
* 900.99998888 | ||
* 34.00 | ||
*/ | ||
formatValue: function (valueBuffer) { | ||
var value = this.valueToBigInt(valueBuffer).toString(); | ||
var integerPart = value.length > 8 ? value.substr(0, value.length-8) : '0'; | ||
var decimalPart = value.length > 8 ? value.substr(value.length-8) : value; | ||
while (decimalPart.length < 8) decimalPart = "0"+decimalPart; | ||
decimalPart = decimalPart.replace(/0*$/, ''); | ||
while (decimalPart.length < 2) decimalPart += "0"; | ||
return integerPart+"."+decimalPart; | ||
}, | ||
/** | ||
* Parse a floating point string as a Bitcoin value. | ||
* | ||
* Keep in mind that parsing user input is messy. You should always display | ||
* the parsed value back to the user to make sure we understood his input | ||
* correctly. | ||
*/ | ||
parseValue: function (valueString) { | ||
// TODO: Detect other number formats (e.g. comma as decimal separator) | ||
var valueComp = valueString.split('.'); | ||
var integralPart = valueComp[0]; | ||
var fractionalPart = valueComp[1] || "0"; | ||
while (fractionalPart.length < 8) fractionalPart += "0"; | ||
fractionalPart = fractionalPart.replace(/^0+/g, ''); | ||
var value = BigInteger.valueOf(parseInt(integralPart)); | ||
value = value.multiply(BigInteger.valueOf(100000000)); | ||
value = value.add(BigInteger.valueOf(parseInt(fractionalPart))); | ||
return value; | ||
}, | ||
/** | ||
* Calculate RIPEMD160(SHA256(data)). | ||
* | ||
* Takes an arbitrary byte array as inputs and returns the hash as a byte | ||
* array. | ||
*/ | ||
sha256ripe160: function (data) { | ||
return Crypto.RIPEMD160(Crypto.SHA256(data, {asBytes: true}), {asBytes: true}); | ||
} | ||
}; | ||
for (var i in Crypto.util) { | ||
if (Crypto.util.hasOwnProperty(i)) { | ||
Bitcoin.Util[i] = Crypto.util[i]; | ||
} | ||
exports.error = function (msg) { | ||
throw new Error(msg) | ||
} |
@@ -1,308 +0,311 @@ | ||
Bitcoin.Wallet = (function () { | ||
var Script = Bitcoin.Script, | ||
TransactionIn = Bitcoin.TransactionIn, | ||
TransactionOut = Bitcoin.TransactionOut; | ||
var convert = require('./convert') | ||
var Transaction = require('./transaction').Transaction | ||
var HDNode = require('./hdwallet.js') | ||
var rng = require('secure-random') | ||
var Wallet = function () { | ||
// Keychain | ||
// | ||
// The keychain is stored as a var in this closure to make accidental | ||
// serialization less likely. | ||
// | ||
// Any functions accessing this value therefore have to be defined in | ||
// the closure of this constructor. | ||
var keys = []; | ||
function Wallet(seed, options) { | ||
if (!(this instanceof Wallet)) { return new Wallet(seed, options); } | ||
// Public hashes of our keys | ||
this.addressHashes = []; | ||
var options = options || {} | ||
var network = options.network || 'mainnet' | ||
// Transaction data | ||
this.txIndex = {}; | ||
this.unspentOuts = []; | ||
// Stored in a closure to make accidental serialization less likely | ||
var masterkey = null | ||
var me = this | ||
var accountZero = null | ||
var internalAccount = null | ||
var externalAccount = null | ||
// Other fields | ||
this.addressPointer = 0; | ||
// Addresses | ||
this.addresses = [] | ||
this.changeAddresses = [] | ||
/** | ||
* Add a key to the keychain. | ||
* | ||
* The corresponding public key can be provided as a second parameter. This | ||
* adds it to the cache in the ECKey object and avoid the need to | ||
* expensively calculate it later. | ||
*/ | ||
this.addKey = function (key, pub) { | ||
if (!(key instanceof Bitcoin.ECKey)) { | ||
key = new Bitcoin.ECKey(key); | ||
} | ||
keys.push(key); | ||
// Transaction output data | ||
this.outputs = {} | ||
if (pub) { | ||
if ("string" === typeof pub) { | ||
pub = Crypto.util.base64ToBytes(pub); | ||
} | ||
key.setPub(pub); | ||
} | ||
// Make a new master key | ||
this.newMasterKey = function(seed, network) { | ||
if (!seed) seed = rng(32, { array: true }); | ||
masterkey = new HDNode(seed, network) | ||
this.addressHashes.push(key.getBitcoinAddress().getHashBase64()); | ||
}; | ||
// HD first-level child derivation method should be private | ||
// See https://bitcointalk.org/index.php?topic=405179.msg4415254#msg4415254 | ||
accountZero = masterkey.derivePrivate(0) | ||
externalAccount = accountZero.derive(0) | ||
internalAccount = accountZero.derive(1) | ||
/** | ||
* Add multiple keys at once. | ||
*/ | ||
this.addKeys = function (keys, pubs) { | ||
if ("string" === typeof keys) { | ||
keys = keys.split(','); | ||
} | ||
if ("string" === typeof pubs) { | ||
pubs = pubs.split(','); | ||
} | ||
var i; | ||
if (Array.isArray(pubs) && keys.length == pubs.length) { | ||
for (i = 0; i < keys.length; i++) { | ||
this.addKey(keys[i], pubs[i]); | ||
} | ||
} else { | ||
for (i = 0; i < keys.length; i++) { | ||
this.addKey(keys[i]); | ||
} | ||
} | ||
}; | ||
me.addresses = [] | ||
me.changeAddresses = [] | ||
/** | ||
* Get the key chain. | ||
* | ||
* Returns an array of base64-encoded private values. | ||
*/ | ||
this.getKeys = function () { | ||
var serializedWallet = []; | ||
me.outputs = {} | ||
} | ||
this.newMasterKey(seed, network) | ||
for (var i = 0; i < keys.length; i++) { | ||
serializedWallet.push(keys[i].toString('base64')); | ||
} | ||
return serializedWallet; | ||
}; | ||
this.generateAddress = function() { | ||
var key = externalAccount.derive(this.addresses.length) | ||
this.addresses.push(key.getAddress().toString()) | ||
return this.addresses[this.addresses.length - 1] | ||
} | ||
/** | ||
* Get the public keys. | ||
* | ||
* Returns an array of base64-encoded public keys. | ||
*/ | ||
this.getPubKeys = function () { | ||
var pubs = []; | ||
this.generateChangeAddress = function() { | ||
var key = internalAccount.derive(this.changeAddresses.length) | ||
this.changeAddresses.push(key.getAddress().toString()) | ||
return this.changeAddresses[this.changeAddresses.length - 1] | ||
} | ||
for (var i = 0; i < keys.length; i++) { | ||
pubs.push(Crypto.util.bytesToBase64(keys[i].getPub())); | ||
} | ||
this.getBalance = function() { | ||
return this.getUnspentOutputs().reduce(function(memo, output){ | ||
return memo + output.value | ||
}, 0) | ||
} | ||
return pubs; | ||
}; | ||
this.getUnspentOutputs = function() { | ||
var utxo = [] | ||
/** | ||
* Delete all keys. | ||
*/ | ||
this.clear = function () { | ||
keys = []; | ||
}; | ||
for(var key in this.outputs){ | ||
var output = this.outputs[key] | ||
if(!output.spend) utxo.push(outputToUnspentOutput(output)) | ||
} | ||
/** | ||
* Return the number of keys in this wallet. | ||
*/ | ||
this.getLength = function () { | ||
return keys.length; | ||
}; | ||
return utxo | ||
} | ||
/** | ||
* Get the addresses for this wallet. | ||
* | ||
* Returns an array of Address objects. | ||
*/ | ||
this.getAllAddresses = function () { | ||
var addresses = []; | ||
for (var i = 0; i < keys.length; i++) { | ||
addresses.push(keys[i].getBitcoinAddress()); | ||
} | ||
return addresses; | ||
}; | ||
this.setUnspentOutputs = function(utxo) { | ||
var outputs = {} | ||
this.getCurAddress = function () { | ||
if (keys[this.addressPointer]) { | ||
return keys[this.addressPointer].getBitcoinAddress(); | ||
} else { | ||
return null; | ||
} | ||
}; | ||
utxo.forEach(function(uo){ | ||
validateUnspentOutput(uo) | ||
var o = unspentOutputToOutput(uo) | ||
outputs[o.receive] = o | ||
}) | ||
/** | ||
* Go to the next address. | ||
* | ||
* If there are no more new addresses available, one will be generated | ||
* automatically. | ||
*/ | ||
this.getNextAddress = function () { | ||
this.addressPointer++; | ||
if (!keys[this.addressPointer]) { | ||
this.generateAddress(); | ||
this.outputs = outputs | ||
} | ||
this.setUnspentOutputsAsync = function(utxo, callback) { | ||
var error = null | ||
try { | ||
this.setUnspentOutputs(utxo) | ||
} catch(err) { | ||
error = err | ||
} finally { | ||
process.nextTick(function(){ callback(error) }) | ||
} | ||
} | ||
function outputToUnspentOutput(output){ | ||
var hashAndIndex = output.receive.split(":") | ||
return { | ||
hash: hashAndIndex[0], | ||
hashLittleEndian: convert.reverseEndian(hashAndIndex[0]), | ||
outputIndex: parseInt(hashAndIndex[1]), | ||
address: output.address, | ||
value: output.value | ||
} | ||
} | ||
function unspentOutputToOutput(o) { | ||
var hash = o.hash || convert.reverseEndian(o.hashLittleEndian) | ||
var key = hash + ":" + o.outputIndex | ||
return { | ||
receive: key, | ||
address: o.address, | ||
value: o.value | ||
} | ||
} | ||
function validateUnspentOutput(uo) { | ||
var missingField | ||
if (isNullOrUndefined(uo.hash) && isNullOrUndefined(uo.hashLittleEndian)) { | ||
missingField = "hash(or hashLittleEndian)" | ||
} | ||
var requiredKeys = ['outputIndex', 'address', 'value'] | ||
requiredKeys.forEach(function (key) { | ||
if (isNullOrUndefined(uo[key])){ | ||
missingField = key | ||
} | ||
return keys[this.addressPointer].getBitcoinAddress(); | ||
}; | ||
}) | ||
/** | ||
* Sign a hash with a key. | ||
* | ||
* This method expects the pubKeyHash as the first parameter and the hash | ||
* to be signed as the second parameter. | ||
*/ | ||
this.signWithKey = function (pubKeyHash, hash) { | ||
pubKeyHash = Crypto.util.bytesToBase64(pubKeyHash); | ||
for (var i = 0; i < this.addressHashes.length; i++) { | ||
if (this.addressHashes[i] == pubKeyHash) { | ||
return keys[i].sign(hash); | ||
if (missingField) { | ||
var message = [ | ||
'Invalid unspent output: key', missingField, 'is missing.', | ||
'A valid unspent output must contain' | ||
] | ||
message.push(requiredKeys.join(', ')) | ||
message.push("and hash(or hashLittleEndian)") | ||
throw new Error(message.join(' ')) | ||
} | ||
} | ||
function isNullOrUndefined(value) { | ||
return value == undefined | ||
} | ||
this.processTx = function(tx) { | ||
var txhash = convert.bytesToHex(tx.getHash()) | ||
tx.outs.forEach(function(txOut, i){ | ||
var address = txOut.address.toString() | ||
if (isMyAddress(address)) { | ||
var output = txhash+':'+i | ||
me.outputs[output] = { | ||
receive: output, | ||
value: txOut.value, | ||
address: address, | ||
} | ||
} | ||
throw new Error("Missing key for signature"); | ||
}; | ||
}) | ||
/** | ||
* Retrieve the corresponding pubKey for a pubKeyHash. | ||
* | ||
* This function only works if the pubKey in question is part of this | ||
* wallet. | ||
*/ | ||
this.getPubKeyFromHash = function (pubKeyHash) { | ||
pubKeyHash = Crypto.util.bytesToBase64(pubKeyHash); | ||
for (var i = 0; i < this.addressHashes.length; i++) { | ||
if (this.addressHashes[i] == pubKeyHash) { | ||
return keys[i].getPub(); | ||
} | ||
tx.ins.forEach(function(txIn, i){ | ||
var op = txIn.outpoint | ||
var o = me.outputs[op.hash+':'+op.index] | ||
if (o) { | ||
o.spend = txhash+':'+i | ||
} | ||
throw new Error("Hash unknown"); | ||
}; | ||
}; | ||
}) | ||
} | ||
Wallet.prototype.generateAddress = function () { | ||
this.addKey(new Bitcoin.ECKey()); | ||
}; | ||
this.createTx = function(to, value, fixedFee) { | ||
checkDust(value) | ||
/** | ||
* Add a transaction to the wallet's processed transaction. | ||
* | ||
* This will add a transaction to the wallet, updating its balance and | ||
* available unspent outputs. | ||
*/ | ||
Wallet.prototype.process = function (tx) { | ||
if (this.txIndex[tx.hash]) return; | ||
var tx = new Transaction() | ||
tx.addOutput(to, value) | ||
var j; | ||
var k; | ||
var hash; | ||
// Gather outputs | ||
for (j = 0; j < tx.outs.length; j++) { | ||
var txout = new TransactionOut(tx.outs[j]); | ||
hash = Crypto.util.bytesToBase64(txout.script.simpleOutPubKeyHash()); | ||
for (k = 0; k < this.addressHashes.length; k++) { | ||
if (this.addressHashes[k] === hash) { | ||
this.unspentOuts.push({tx: tx, index: j, out: txout}); | ||
break; | ||
} | ||
} | ||
} | ||
var utxo = getCandidateOutputs(value) | ||
var totalInValue = 0 | ||
for(var i=0; i<utxo.length; i++){ | ||
var output = utxo[i] | ||
tx.addInput(output.receive) | ||
// Remove spent outputs | ||
for (j = 0; j < tx.ins.length; j++) { | ||
var txin = new TransactionIn(tx.ins[j]); | ||
var pubkey = txin.script.simpleInPubKey(); | ||
hash = Crypto.util.bytesToBase64(Bitcoin.Util.sha256ripe160(pubkey)); | ||
for (k = 0; k < this.addressHashes.length; k++) { | ||
if (this.addressHashes[k] === hash) { | ||
for (var l = 0; l < this.unspentOuts.length; l++) { | ||
if (txin.outpoint.hash == this.unspentOuts[l].tx.hash && | ||
txin.outpoint.index == this.unspentOuts[l].index) { | ||
this.unspentOuts.splice(l, 1); | ||
} | ||
} | ||
break; | ||
} | ||
totalInValue += output.value | ||
if(totalInValue < value) continue | ||
var fee = fixedFee == undefined ? estimateFeePadChangeOutput(tx) : fixedFee | ||
if(totalInValue < value + fee) continue | ||
var change = totalInValue - value - fee | ||
if(change > 0 && !isDust(change)) { | ||
tx.addOutput(getChangeAddress(), change) | ||
} | ||
break | ||
} | ||
// Index transaction | ||
this.txIndex[tx.hash] = tx; | ||
}; | ||
checkInsufficientFund(totalInValue, value, fee) | ||
Wallet.prototype.getBalance = function () { | ||
var balance = BigInteger.valueOf(0); | ||
for (var i = 0; i < this.unspentOuts.length; i++) { | ||
var txout = this.unspentOuts[i].out; | ||
balance = balance.add(Bitcoin.Util.valueToBigInt(txout.value)); | ||
} | ||
return balance; | ||
}; | ||
this.sign(tx) | ||
Wallet.prototype.createSend = function (address, sendValue, feeValue) { | ||
var selectedOuts = []; | ||
var txValue = sendValue.add(feeValue); | ||
var availableValue = BigInteger.ZERO; | ||
var i; | ||
for (i = 0; i < this.unspentOuts.length; i++) { | ||
selectedOuts.push(this.unspentOuts[i]); | ||
availableValue = availableValue.add(Bitcoin.Util.valueToBigInt(this.unspentOuts[i].out.value)); | ||
return tx | ||
} | ||
if (availableValue.compareTo(txValue) >= 0) break; | ||
this.createTxAsync = function(to, value, fixedFee, callback){ | ||
if(fixedFee instanceof Function) { | ||
callback = fixedFee | ||
fixedFee = undefined | ||
} | ||
var tx = null | ||
var error = null | ||
if (availableValue.compareTo(txValue) < 0) { | ||
throw new Error('Insufficient funds.'); | ||
try { | ||
tx = this.createTx(to, value, fixedFee) | ||
} catch(err) { | ||
error = err | ||
} finally { | ||
process.nextTick(function(){ callback(error, tx) }) | ||
} | ||
} | ||
this.dustThreshold = 5430 | ||
function isDust(amount) { | ||
return amount <= me.dustThreshold | ||
} | ||
var changeValue = availableValue.subtract(txValue); | ||
var sendTx = new Bitcoin.Transaction(); | ||
for (i = 0; i < selectedOuts.length; i++) { | ||
sendTx.addInput(selectedOuts[i].tx, selectedOuts[i].index); | ||
function checkDust(value){ | ||
if (isNullOrUndefined(value) || isDust(value)) { | ||
throw new Error("Value must be above dust threshold") | ||
} | ||
} | ||
sendTx.addOutput(address, sendValue); | ||
if (changeValue.compareTo(BigInteger.ZERO) > 0) { | ||
sendTx.addOutput(this.getNextAddress(), changeValue); | ||
function getCandidateOutputs(value){ | ||
var unspent = [] | ||
for (var key in me.outputs){ | ||
var output = me.outputs[key] | ||
if(!output.spend) unspent.push(output) | ||
} | ||
var hashType = 1; // SIGHASH_ALL | ||
var sortByValueDesc = unspent.sort(function(o1, o2){ | ||
return o2.value - o1.value | ||
}) | ||
for (i = 0; i < sendTx.ins.length; i++) { | ||
var hash = sendTx.hashTransactionForSignature(selectedOuts[i].out.script, i, hashType); | ||
var pubKeyHash = selectedOuts[i].out.script.simpleOutPubKeyHash(); | ||
var signature = this.signWithKey(pubKeyHash, hash); | ||
return sortByValueDesc | ||
} | ||
// Append hash type | ||
signature.push(parseInt(hashType, 10)); | ||
function estimateFeePadChangeOutput(tx){ | ||
var tmpTx = tx.clone() | ||
tmpTx.addOutput(getChangeAddress(), 0) | ||
return tmpTx.estimateFee() | ||
} | ||
sendTx.ins[i].script = Script.createInputScript(signature, this.getPubKeyFromHash(pubKeyHash)); | ||
function getChangeAddress() { | ||
if(me.changeAddresses.length === 0) me.generateChangeAddress(); | ||
return me.changeAddresses[me.changeAddresses.length - 1] | ||
} | ||
function checkInsufficientFund(totalInValue, value, fee) { | ||
if(totalInValue < value + fee) { | ||
throw new Error('Not enough money to send funds including transaction fee. Have: ' + | ||
totalInValue + ', needed: ' + (value + fee)) | ||
} | ||
} | ||
return sendTx; | ||
}; | ||
this.sign = function(tx) { | ||
tx.ins.forEach(function(inp,i) { | ||
var output = me.outputs[inp.outpoint.hash + ':' + inp.outpoint.index] | ||
if (output) { | ||
tx.sign(i, me.getPrivateKeyForAddress(output.address)) | ||
} | ||
}) | ||
return tx | ||
} | ||
Wallet.prototype.clearTransactions = function () { | ||
this.txIndex = {}; | ||
this.unspentOuts = []; | ||
}; | ||
this.getMasterKey = function() { return masterkey } | ||
this.getAccountZero = function() { return accountZero } | ||
this.getInternalAccount = function() { return internalAccount } | ||
this.getExternalAccount = function() { return externalAccount } | ||
/** | ||
* Check to see if a pubKeyHash belongs to this wallet. | ||
*/ | ||
Wallet.prototype.hasHash = function (hash) { | ||
if (Bitcoin.Util.isArray(hash)) hash = Crypto.util.bytesToBase64(hash); | ||
this.getPrivateKey = function(index) { | ||
return externalAccount.derive(index).priv | ||
} | ||
// TODO: Just create an object with base64 hashes as keys for faster lookup | ||
for (var k = 0; k < this.addressHashes.length; k++) { | ||
if (this.addressHashes[k] === hash) return true; | ||
this.getInternalPrivateKey = function(index) { | ||
return internalAccount.derive(index).priv | ||
} | ||
this.getPrivateKeyForAddress = function(address) { | ||
var index | ||
if((index = this.addresses.indexOf(address)) > -1) { | ||
return this.getPrivateKey(index) | ||
} else if((index = this.changeAddresses.indexOf(address)) > -1) { | ||
return this.getInternalPrivateKey(index) | ||
} else { | ||
throw new Error('Unknown address. Make sure the address is from the keychain and has been generated.') | ||
} | ||
return false; | ||
}; | ||
} | ||
return Wallet; | ||
})(); | ||
function isReceiveAddress(address){ | ||
return me.addresses.indexOf(address) > -1 | ||
} | ||
function isChangeAddress(address){ | ||
return me.changeAddresses.indexOf(address) > -1 | ||
} | ||
function isMyAddress(address) { | ||
return isReceiveAddress(address) || isChangeAddress(address) | ||
} | ||
} | ||
module.exports = Wallet |
Sorry, the diff of this file is not supported yet
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
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
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
Non-existent author
Supply chain riskThe package was published by an npm account that no longer exists.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
2
133
0
214457
2
5
38
5614
2
+ Addedcrypto-js@3.1.2-3
+ Addedsecure-random@0.2.1
+ Addedcrypto-js@3.1.2-3(transitive)
+ Addedsecure-random@0.2.1(transitive)