Browse Source
- no longer is the global Bitcoin used for modules - cleaner and more maintainable code - add more testshk-custom-address
35 changed files with 3393 additions and 3161 deletions
@ -0,0 +1,59 @@ |
|||||
|
// convert to/from various values
|
||||
|
|
||||
|
var base64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
||||
|
|
||||
|
// Convert a byte array to a hex string
|
||||
|
module.exports.bytesToHex = function(bytes) { |
||||
|
for (var hex = [], i = 0; i < bytes.length; i++) { |
||||
|
hex.push((bytes[i] >>> 4).toString(16)); |
||||
|
hex.push((bytes[i] & 0xF).toString(16)); |
||||
|
} |
||||
|
return hex.join(""); |
||||
|
}; |
||||
|
|
||||
|
// Convert a hex string to a byte array
|
||||
|
module.exports.hexToBytes = function(hex) { |
||||
|
for (var bytes = [], c = 0; c < hex.length; c += 2) |
||||
|
bytes.push(parseInt(hex.substr(c, 2), 16)); |
||||
|
return bytes; |
||||
|
} |
||||
|
|
||||
|
// Convert a byte array to a base-64 string
|
||||
|
module.exports.bytesToBase64 = function(bytes) { |
||||
|
// Use browser-native function if it exists
|
||||
|
if (typeof btoa == "function") return btoa(Binary.bytesToString(bytes)); |
||||
|
|
||||
|
for(var base64 = [], i = 0; i < bytes.length; i += 3) { |
||||
|
var triplet = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]; |
||||
|
for (var j = 0; j < 4; j++) { |
||||
|
if (i * 8 + j * 6 <= bytes.length * 8) |
||||
|
base64.push(base64map.charAt((triplet >>> 6 * (3 - j)) & 0x3F)); |
||||
|
else base64.push("="); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return base64.join(""); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// Convert a base-64 string to a byte array
|
||||
|
module.exports.base64ToBytes = function(base64) { |
||||
|
// Use browser-native function if it exists
|
||||
|
if (typeof atob == "function") return Binary.stringToBytes(atob(base64)); |
||||
|
|
||||
|
// Remove non-base-64 characters
|
||||
|
base64 = base64.replace(/[^A-Z0-9+\/]/ig, ""); |
||||
|
|
||||
|
for (var bytes = [], i = 0, imod4 = 0; i < base64.length; imod4 = ++i % 4) { |
||||
|
if (imod4 == 0) continue; |
||||
|
bytes.push(((base64map.indexOf(base64.charAt(i - 1)) & (Math.pow(2, -2 * imod4 + 8) - 1)) << (imod4 * 2)) | |
||||
|
(base64map.indexOf(base64.charAt(i)) >>> (6 - imod4 * 2))); |
||||
|
} |
||||
|
|
||||
|
return bytes; |
||||
|
} |
||||
|
|
||||
|
// utf8 and binary?
|
||||
|
//stringToBytes
|
||||
|
//bytesToString
|
||||
|
|
@ -1,7 +0,0 @@ |
|||||
/* |
|
||||
* Crypto-JS v2.0.0 |
|
||||
* http://code.google.com/p/crypto-js/
|
|
||||
* Copyright (c) 2009, Jeff Mott. All rights reserved. |
|
||||
* http://code.google.com/p/crypto-js/wiki/License
|
|
||||
*/ |
|
||||
(function(){var c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";var d=window.Crypto={};var a=d.util={rotl:function(h,g){return(h<<g)|(h>>>(32-g))},rotr:function(h,g){return(h<<(32-g))|(h>>>g)},endian:function(h){if(h.constructor==Number){return a.rotl(h,8)&16711935|a.rotl(h,24)&4278255360}for(var g=0;g<h.length;g++){h[g]=a.endian(h[g])}return h},randomBytes:function(h){for(var g=[];h>0;h--){g.push(Math.floor(Math.random()*256))}return g},bytesToWords:function(h){for(var k=[],j=0,g=0;j<h.length;j++,g+=8){k[g>>>5]|=h[j]<<(24-g%32)}return k},wordsToBytes:function(i){for(var h=[],g=0;g<i.length*32;g+=8){h.push((i[g>>>5]>>>(24-g%32))&255)}return h},bytesToHex:function(g){for(var j=[],h=0;h<g.length;h++){j.push((g[h]>>>4).toString(16));j.push((g[h]&15).toString(16))}return j.join("")},hexToBytes:function(h){for(var g=[],i=0;i<h.length;i+=2){g.push(parseInt(h.substr(i,2),16))}return g},bytesToBase64:function(h){if(typeof btoa=="function"){return btoa(e.bytesToString(h))}for(var g=[],l=0;l<h.length;l+=3){var m=(h[l]<<16)|(h[l+1]<<8)|h[l+2];for(var k=0;k<4;k++){if(l*8+k*6<=h.length*8){g.push(c.charAt((m>>>6*(3-k))&63))}else{g.push("=")}}}return g.join("")},base64ToBytes:function(h){if(typeof atob=="function"){return e.stringToBytes(atob(h))}h=h.replace(/[^A-Z0-9+\/]/ig,"");for(var g=[],j=0,k=0;j<h.length;k=++j%4){if(k==0){continue}g.push(((c.indexOf(h.charAt(j-1))&(Math.pow(2,-2*k+8)-1))<<(k*2))|(c.indexOf(h.charAt(j))>>>(6-k*2)))}return g}};d.mode={};var b=d.charenc={};var f=b.UTF8={stringToBytes:function(g){return e.stringToBytes(unescape(encodeURIComponent(g)))},bytesToString:function(g){return decodeURIComponent(escape(e.bytesToString(g)))}};var e=b.Binary={stringToBytes:function(j){for(var g=[],h=0;h<j.length;h++){g.push(j.charCodeAt(h))}return g},bytesToString:function(g){for(var j=[],h=0;h<g.length;h++){j.push(String.fromCharCode(g[h]))}return j.join("")}}})(); |
|
@ -1,7 +0,0 @@ |
|||||
/* |
|
||||
* Crypto-JS v2.0.0 |
|
||||
* http://code.google.com/p/crypto-js/
|
|
||||
* Copyright (c) 2009, Jeff Mott. All rights reserved. |
|
||||
* http://code.google.com/p/crypto-js/wiki/License
|
|
||||
*/ |
|
||||
(function(){var g=Crypto,b=g.util,c=g.charenc,f=c.UTF8,e=c.Binary;var a=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298];var d=g.SHA256=function(j,h){var i=b.wordsToBytes(d._sha256(j));return h&&h.asBytes?i:h&&h.asString?e.bytesToString(i):b.bytesToHex(i)};d._sha256=function(q){if(q.constructor==String){q=f.stringToBytes(q)}var y=b.bytesToWords(q),z=q.length*8,r=[1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225],s=[],K,J,I,G,F,E,D,C,B,A,p,o;y[z>>5]|=128<<(24-z%32);y[((z+64>>9)<<4)+15]=z;for(var B=0;B<y.length;B+=16){K=r[0];J=r[1];I=r[2];G=r[3];F=r[4];E=r[5];D=r[6];C=r[7];for(var A=0;A<64;A++){if(A<16){s[A]=y[A+B]}else{var n=s[A-15],u=s[A-2],M=((n<<25)|(n>>>7))^((n<<14)|(n>>>18))^(n>>>3),L=((u<<15)|(u>>>17))^((u<<13)|(u>>>19))^(u>>>10);s[A]=M+(s[A-7]>>>0)+L+(s[A-16]>>>0)}var t=F&E^~F&D,k=K&J^K&I^J&I,x=((K<<30)|(K>>>2))^((K<<19)|(K>>>13))^((K<<10)|(K>>>22)),v=((F<<26)|(F>>>6))^((F<<21)|(F>>>11))^((F<<7)|(F>>>25));p=(C>>>0)+v+t+(a[A])+(s[A]>>>0);o=x+k;C=D;D=E;E=F;F=G+p;G=I;I=J;J=K;K=p+o}r[0]+=K;r[1]+=J;r[2]+=I;r[3]+=G;r[4]+=F;r[5]+=E;r[6]+=D;r[7]+=C}return r};d._blocksize=16})(); |
|
@ -1,131 +1,176 @@ |
|||||
Bitcoin.ECKey = (function () { |
var BigInteger = require('./jsbn/jsbn'); |
||||
var ECDSA = Bitcoin.ECDSA; |
var sec = require('./jsbn/sec'); |
||||
var ecparams = getSECCurveByName("secp256k1"); |
var base58 = require('./base58'); |
||||
var rng = new SecureRandom(); |
var Crypto = require('./crypto-js/crypto'); |
||||
|
var util = require('./util'); |
||||
var ECKey = function (input) { |
var conv = require('./convert'); |
||||
if (!input) { |
var Address = require('./address'); |
||||
// Generate new key
|
var ecdsa = require('./ecdsa'); |
||||
var n = ecparams.getN(); |
|
||||
this.priv = ECDSA.getBigRandom(n); |
var ecparams = sec("secp256k1"); |
||||
} else if (input instanceof BigInteger) { |
|
||||
// Input is a private key value
|
// input can be nothing, array of bytes, hex string, or base58 string
|
||||
this.priv = input; |
var ECKey = function (input) { |
||||
} else if (Bitcoin.Util.isArray(input)) { |
if (!(this instanceof ECKey)) { |
||||
// Prepend zero byte to prevent interpretation as negative integer
|
return new ECKey(input); |
||||
this.priv = BigInteger.fromByteArrayUnsigned(input); |
} |
||||
} else if ("string" == typeof input) { |
|
||||
if (input.length == 51 && input[0] == '5') { |
this.compressed = !!ECKey.compressByDefault; |
||||
// Base58 encoded private key
|
|
||||
this.priv = BigInteger.fromByteArrayUnsigned(ECKey.decodeString(input)); |
if (!input) { |
||||
} else { |
// Generate new key
|
||||
// Prepend zero byte to prevent interpretation as negative integer
|
var n = ecparams.getN(); |
||||
this.priv = BigInteger.fromByteArrayUnsigned(Crypto.util.base64ToBytes(input)); |
this.priv = ecdsa.getBigRandom(n); |
||||
} |
} else if (input instanceof BigInteger) { |
||||
|
// Input is a private key value
|
||||
|
this.priv = input; |
||||
|
} else if (util.isArray(input)) { |
||||
|
// Prepend zero byte to prevent interpretation as negative integer
|
||||
|
this.priv = BigInteger.fromByteArrayUnsigned(input); |
||||
|
this.compressed = false; |
||||
|
} else if ("string" == typeof input) { |
||||
|
if (input.length == 51 && input[0] == '5') { |
||||
|
// Base58 encoded private key
|
||||
|
this.priv = BigInteger.fromByteArrayUnsigned(ECKey.decodeString(input)); |
||||
|
this.compressed = false; |
||||
} |
} |
||||
this.compressed = !!ECKey.compressByDefault; |
else if (input.length == 52 && (input[0] === 'K' || input[0] === 'L')) { |
||||
}; |
// Base58 encoded private key
|
||||
|
this.priv = BigInteger.fromByteArrayUnsigned(ECKey.decodeString(input)); |
||||
/** |
this.compressed = true; |
||||
* Whether public keys should be returned compressed by default. |
|
||||
*/ |
|
||||
ECKey.compressByDefault = false; |
|
||||
|
|
||||
/** |
|
||||
* Set whether the public key should be returned compressed or not. |
|
||||
*/ |
|
||||
ECKey.prototype.setCompressed = function (v) { |
|
||||
this.compressed = !!v; |
|
||||
}; |
|
||||
|
|
||||
/** |
|
||||
* Return public key in DER encoding. |
|
||||
*/ |
|
||||
ECKey.prototype.getPub = function () { |
|
||||
return this.getPubPoint().getEncoded(this.compressed); |
|
||||
}; |
|
||||
|
|
||||
/** |
|
||||
* Return public point as ECPoint object. |
|
||||
*/ |
|
||||
ECKey.prototype.getPubPoint = function () { |
|
||||
if (!this.pub) this.pub = ecparams.getG().multiply(this.priv); |
|
||||
|
|
||||
return this.pub; |
|
||||
}; |
|
||||
|
|
||||
/** |
|
||||
* 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; |
|
||||
|
|
||||
return this.pubKeyHash = Bitcoin.Util.sha256ripe160(this.getPub()); |
|
||||
}; |
|
||||
|
|
||||
ECKey.prototype.getBitcoinAddress = function () { |
|
||||
var hash = this.getPubKeyHash(); |
|
||||
var addr = new Bitcoin.Address(hash); |
|
||||
return addr; |
|
||||
}; |
|
||||
|
|
||||
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.setPub = function (pub) { |
|
||||
this.pub = ECPointFp.decodeFrom(ecparams.getCurve(), pub); |
|
||||
}; |
|
||||
|
|
||||
ECKey.prototype.toString = function (format) { |
|
||||
if (format === "base64") { |
|
||||
return Crypto.util.bytesToBase64(this.priv.toByteArrayUnsigned()); |
|
||||
} else { |
} else { |
||||
return Crypto.util.bytesToHex(this.priv.toByteArrayUnsigned()); |
// hex string?
|
||||
} |
// //wtf is base64 here for?
|
||||
}; |
// Prepend zero byte to prevent interpretation as negative integer
|
||||
|
this.priv = BigInteger.fromByteArrayUnsigned(conv.base64ToBytes(input)); |
||||
ECKey.prototype.sign = function (hash) { |
|
||||
return ECDSA.sign(hash, this.priv); |
|
||||
}; |
|
||||
|
|
||||
ECKey.prototype.verify = function (hash, sig) { |
|
||||
return ECDSA.verify(hash, sig, this.getPub()); |
|
||||
}; |
|
||||
|
|
||||
/** |
|
||||
* Parse an exported private key contained in a string. |
|
||||
*/ |
|
||||
ECKey.decodeString = function (string) { |
|
||||
var bytes = Bitcoin.Base58.decode(string); |
|
||||
|
|
||||
var hash = bytes.slice(0, 33); |
|
||||
|
|
||||
var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true}); |
|
||||
|
|
||||
if (checksum[0] != bytes[33] || |
|
||||
checksum[1] != bytes[34] || |
|
||||
checksum[2] != bytes[35] || |
|
||||
checksum[3] != bytes[36]) { |
|
||||
throw "Checksum validation failed!"; |
|
||||
} |
|
||||
|
|
||||
var version = hash.shift(); |
|
||||
|
|
||||
if (version != 0x80) { |
|
||||
throw "Version "+version+" not supported!"; |
|
||||
} |
} |
||||
|
} |
||||
return hash; |
}; |
||||
}; |
|
||||
|
// TODO(shtylman) methods
|
||||
return ECKey; |
// wallet import format (base58 check with meta info)
|
||||
})(); |
// fromWIF
|
||||
|
// toWIF
|
||||
|
// fromBytes
|
||||
|
// toBytes
|
||||
|
// fromHex
|
||||
|
// toHex
|
||||
|
|
||||
|
/** |
||||
|
* Whether public keys should be returned compressed by default. |
||||
|
*/ |
||||
|
ECKey.compressByDefault = false; |
||||
|
|
||||
|
/** |
||||
|
* Set whether the public key should be returned compressed or not. |
||||
|
*/ |
||||
|
ECKey.prototype.setCompressed = function (v) { |
||||
|
this.compressed = !!v; |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* Return public key in DER encoding. |
||||
|
*/ |
||||
|
ECKey.prototype.getPub = function () { |
||||
|
return this.getPubPoint().getEncoded(this.compressed); |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* Return public point as ECPoint object. |
||||
|
*/ |
||||
|
ECKey.prototype.getPubPoint = function () { |
||||
|
if (!this.pub) this.pub = ecparams.getG().multiply(this.priv); |
||||
|
|
||||
|
return this.pub; |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* 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; |
||||
|
|
||||
|
return this.pubKeyHash = util.sha256ripe160(this.getPub()); |
||||
|
}; |
||||
|
|
||||
|
ECKey.prototype.getBitcoinAddress = function () { |
||||
|
var hash = this.getPubKeyHash(); |
||||
|
var addr = new Address(hash); |
||||
|
return addr; |
||||
|
}; |
||||
|
|
||||
|
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.setPub = function (pub) { |
||||
|
this.pub = ECPointFp.decodeFrom(ecparams.getCurve(), pub); |
||||
|
}; |
||||
|
|
||||
|
ECKey.prototype.toString = function (format) { |
||||
|
if (format === "base64") { |
||||
|
return conv.bytesToBase64(this.priv.toByteArrayUnsigned()); |
||||
|
} else { |
||||
|
return Crypto.util.bytesToHex(this.priv.toByteArrayUnsigned()); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
ECKey.prototype.sign = function (hash) { |
||||
|
return ecdsa.sign(hash, this.priv); |
||||
|
}; |
||||
|
|
||||
|
ECKey.prototype.verify = function (hash, sig) { |
||||
|
return ecdsa.verify(hash, sig, this.getPub()); |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* Parse an exported private key contained in a string. |
||||
|
*/ |
||||
|
ECKey.decodeString = function (string) { |
||||
|
var bytes = base58.decode(string); |
||||
|
|
||||
|
if (bytes.length !== 37 && bytes.length !== 38) { |
||||
|
throw new Error('not a valid base58 encoded private key'); |
||||
|
} |
||||
|
|
||||
|
//Format:
|
||||
|
//* uncompressed: 0x80 + [32-byte secret] + [4 bytes of Hash() of
|
||||
|
//previous 33 bytes], base58 encoded
|
||||
|
//* compressed: 0x80 + [32-byte secret] + 0x01 + [4 bytes of Hash()
|
||||
|
//previous 34 bytes], base58 encoded
|
||||
|
|
||||
|
if (bytes[33] === 0x01) { |
||||
|
// compressed
|
||||
|
} |
||||
|
|
||||
|
var hash = bytes.slice(0, 33); |
||||
|
|
||||
|
/* |
||||
|
var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true}); |
||||
|
|
||||
|
if (checksum[0] != bytes[33] || |
||||
|
checksum[1] != bytes[34] || |
||||
|
checksum[2] != bytes[35] || |
||||
|
checksum[3] != bytes[36]) { |
||||
|
throw "Checksum validation failed!"; |
||||
|
} |
||||
|
*/ |
||||
|
|
||||
|
var version = hash.shift(); |
||||
|
|
||||
|
if (version != 0x80) { |
||||
|
throw "Version "+version+" not supported!"; |
||||
|
} |
||||
|
|
||||
|
return hash; |
||||
|
}; |
||||
|
|
||||
|
module.exports = ECKey; |
||||
|
@ -0,0 +1,46 @@ |
|||||
|
// Bit-wise rotate left
|
||||
|
var rotl = function (n, b) { |
||||
|
return (n << b) | (n >>> (32 - b)); |
||||
|
}; |
||||
|
|
||||
|
// Bit-wise rotate right
|
||||
|
var rotr = function (n, b) { |
||||
|
return (n << (32 - b)) | (n >>> b); |
||||
|
}; |
||||
|
|
||||
|
// Swap big-endian to little-endian and vice versa
|
||||
|
var endian = function (n) { |
||||
|
// If number given, swap endian
|
||||
|
if (n.constructor == Number) { |
||||
|
return rotl(n, 8) & 0x00FF00FF | rotl(n, 24) & 0xFF00FF00; |
||||
|
} |
||||
|
|
||||
|
// Else, assume array and swap all items
|
||||
|
for (var i = 0; i < n.length; i++) { |
||||
|
n[i] = endian(n[i]); |
||||
|
} |
||||
|
return n; |
||||
|
} |
||||
|
|
||||
|
module.exports = { |
||||
|
Address: require('./address'), |
||||
|
Key: require('./eckey'), |
||||
|
BigInteger: require('./jsbn/jsbn'), |
||||
|
Script: require('./script'), |
||||
|
Opcode: require('./opcode'), |
||||
|
Transaction: require('./transaction').Transaction, |
||||
|
TransactionIn: require('./transaction').TransactionIn, |
||||
|
TransactionOut: require('./transaction').TransactionOut, |
||||
|
ECPointFp: require('./jsbn/ec').ECPointFp, |
||||
|
Wallet: require('./wallet'), |
||||
|
|
||||
|
ecdsa: require('./ecdsa'), |
||||
|
|
||||
|
// base58 encoding/decoding to bytes
|
||||
|
base58: require('./base58'), |
||||
|
|
||||
|
// conversions
|
||||
|
convert: require('./convert'), |
||||
|
|
||||
|
endian: endian |
||||
|
} |
File diff suppressed because it is too large
@ -1,656 +0,0 @@ |
|||||
// Copyright (c) 2005-2009 Tom Wu
|
|
||||
// All Rights Reserved.
|
|
||||
// See "LICENSE" for details.
|
|
||||
|
|
||||
// Extended JavaScript BN functions, required for RSA private ops.
|
|
||||
|
|
||||
// Version 1.1: new BigInteger("0", 10) returns "proper" zero
|
|
||||
// Version 1.2: square() API, isProbablePrime fix
|
|
||||
|
|
||||
// (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) { |
|
||||
this.fromInt(0); |
|
||||
if(b == null) b = 10; |
|
||||
var cs = this.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) == "-" && this.signum() == 0) mi = true; |
|
||||
continue; |
|
||||
} |
|
||||
w = b*w+x; |
|
||||
if(++j >= cs) { |
|
||||
this.dMultiply(d); |
|
||||
this.dAddOffset(w,0); |
|
||||
j = 0; |
|
||||
w = 0; |
|
||||
} |
|
||||
} |
|
||||
if(j > 0) { |
|
||||
this.dMultiply(Math.pow(b,j)); |
|
||||
this.dAddOffset(w,0); |
|
||||
} |
|
||||
if(mi) BigInteger.ZERO.subTo(this,this); |
|
||||
} |
|
||||
|
|
||||
// (protected) alternate constructor
|
|
||||
function bnpFromNumber(a,b,c) { |
|
||||
if("number" == typeof b) { |
|
||||
// new BigInteger(int,int,RNG)
|
|
||||
if(a < 2) this.fromInt(1); |
|
||||
else { |
|
||||
this.fromNumber(a,c); |
|
||||
if(!this.testBit(a-1)) // force MSB set
|
|
||||
this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this); |
|
||||
if(this.isEven()) this.dAddOffset(1,0); // force odd
|
|
||||
while(!this.isProbablePrime(b)) { |
|
||||
this.dAddOffset(2,0); |
|
||||
if(this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a-1),this); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
else { |
|
||||
// new BigInteger(int,RNG)
|
|
||||
var x = new Array(), t = a&7; |
|
||||
x.length = (a>>3)+1; |
|
||||
b.nextBytes(x); |
|
||||
if(t > 0) x[0] &= ((1<<t)-1); else x[0] = 0; |
|
||||
this.fromString(x,256); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// (public) convert to bigendian byte array
|
|
||||
function bnToByteArray() { |
|
||||
var i = this.t, r = new Array(); |
|
||||
r[0] = this.s; |
|
||||
var p = this.DB-(i*this.DB)%8, d, k = 0; |
|
||||
if(i-- > 0) { |
|
||||
if(p < this.DB && (d = this[i]>>p) != (this.s&this.DM)>>p) |
|
||||
r[k++] = d|(this.s<<(this.DB-p)); |
|
||||
while(i >= 0) { |
|
||||
if(p < 8) { |
|
||||
d = (this[i]&((1<<p)-1))<<(8-p); |
|
||||
d |= this[--i]>>(p+=this.DB-8); |
|
||||
} |
|
||||
else { |
|
||||
d = (this[i]>>(p-=8))&0xff; |
|
||||
if(p <= 0) { p += this.DB; --i; } |
|
||||
} |
|
||||
if((d&0x80) != 0) d |= -256; |
|
||||
if(k == 0 && (this.s&0x80) != (d&0x80)) ++k; |
|
||||
if(k > 0 || d != this.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 i, f, m = Math.min(a.t,this.t); |
|
||||
for(i = 0; i < m; ++i) r[i] = op(this[i],a[i]); |
|
||||
if(a.t < this.t) { |
|
||||
f = a.s&this.DM; |
|
||||
for(i = m; i < this.t; ++i) r[i] = op(this[i],f); |
|
||||
r.t = this.t; |
|
||||
} |
|
||||
else { |
|
||||
f = this.s&this.DM; |
|
||||
for(i = m; i < a.t; ++i) r[i] = op(f,a[i]); |
|
||||
r.t = a.t; |
|
||||
} |
|
||||
r.s = op(this.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 i = 0, c = 0, m = Math.min(a.t,this.t); |
|
||||
while(i < m) { |
|
||||
c += this[i]+a[i]; |
|
||||
r[i++] = c&this.DM; |
|
||||
c >>= this.DB; |
|
||||
} |
|
||||
if(a.t < this.t) { |
|
||||
c += a.s; |
|
||||
while(i < this.t) { |
|
||||
c += this[i]; |
|
||||
r[i++] = c&this.DM; |
|
||||
c >>= this.DB; |
|
||||
} |
|
||||
c += this.s; |
|
||||
} |
|
||||
else { |
|
||||
c += this.s; |
|
||||
while(i < a.t) { |
|
||||
c += a[i]; |
|
||||
r[i++] = c&this.DM; |
|
||||
c >>= this.DB; |
|
||||
} |
|
||||
c += a.s; |
|
||||
} |
|
||||
r.s = (c<0)?-1:0; |
|
||||
if(c > 0) r[i++] = c; |
|
||||
else if(c < -1) r[i++] = this.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) { |
|
||||
x.drShiftTo(this.m.t-1,this.r2); |
|
||||
if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); } |
|
||||
this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3); |
|
||||
this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2); |
|
||||
while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1); |
|
||||
x.subTo(this.r2,x); |
|
||||
while(x.compareTo(this.m) >= 0) x.subTo(this.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; |
|
||||
} |
|
||||
|
|
||||
var lowprimes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997]; |
|
||||
var lplim = (1<<26)/lowprimes[lowprimes.length-1]; |
|
||||
|
|
||||
// (public) test primality with certainty >= 1-.5^t
|
|
||||
function bnIsProbablePrime(t) { |
|
||||
var i, x = this.abs(); |
|
||||
if(x.t == 1 && x[0] <= lowprimes[lowprimes.length-1]) { |
|
||||
for(i = 0; i < lowprimes.length; ++i) |
|
||||
if(x[0] == lowprimes[i]) return true; |
|
||||
return false; |
|
||||
} |
|
||||
if(x.isEven()) return false; |
|
||||
i = 1; |
|
||||
while(i < lowprimes.length) { |
|
||||
var m = lowprimes[i], j = i+1; |
|
||||
while(j < lowprimes.length && m < lplim) m *= lowprimes[j++]; |
|
||||
m = x.modInt(m); |
|
||||
while(i < j) if(m%lowprimes[i++] == 0) return false; |
|
||||
} |
|
||||
return x.millerRabin(t); |
|
||||
} |
|
||||
|
|
||||
// (protected) true if probably prime (HAC 4.24, Miller-Rabin)
|
|
||||
function bnpMillerRabin(t) { |
|
||||
var n1 = this.subtract(BigInteger.ONE); |
|
||||
var k = n1.getLowestSetBit(); |
|
||||
if(k <= 0) return false; |
|
||||
var r = n1.shiftRight(k); |
|
||||
t = (t+1)>>1; |
|
||||
if(t > lowprimes.length) t = lowprimes.length; |
|
||||
var a = nbi(); |
|
||||
for(var i = 0; i < t; ++i) { |
|
||||
//Pick bases at random, instead of starting at 2
|
|
||||
a.fromInt(lowprimes[Math.floor(Math.random()*lowprimes.length)]); |
|
||||
var y = a.modPow(r,this); |
|
||||
if(y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) { |
|
||||
var j = 1; |
|
||||
while(j++ < k && y.compareTo(n1) != 0) { |
|
||||
y = y.modPowInt(2,this); |
|
||||
if(y.compareTo(BigInteger.ONE) == 0) return false; |
|
||||
} |
|
||||
if(y.compareTo(n1) != 0) return false; |
|
||||
} |
|
||||
} |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
// protected
|
|
||||
BigInteger.prototype.chunkSize = bnpChunkSize; |
|
||||
BigInteger.prototype.toRadix = bnpToRadix; |
|
||||
BigInteger.prototype.fromRadix = bnpFromRadix; |
|
||||
BigInteger.prototype.fromNumber = bnpFromNumber; |
|
||||
BigInteger.prototype.bitwiseTo = bnpBitwiseTo; |
|
||||
BigInteger.prototype.changeBit = bnpChangeBit; |
|
||||
BigInteger.prototype.addTo = bnpAddTo; |
|
||||
BigInteger.prototype.dMultiply = bnpDMultiply; |
|
||||
BigInteger.prototype.dAddOffset = bnpDAddOffset; |
|
||||
BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo; |
|
||||
BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo; |
|
||||
BigInteger.prototype.modInt = bnpModInt; |
|
||||
BigInteger.prototype.millerRabin = bnpMillerRabin; |
|
||||
|
|
||||
// public
|
|
||||
BigInteger.prototype.clone = bnClone; |
|
||||
BigInteger.prototype.intValue = bnIntValue; |
|
||||
BigInteger.prototype.byteValue = bnByteValue; |
|
||||
BigInteger.prototype.shortValue = bnShortValue; |
|
||||
BigInteger.prototype.signum = bnSigNum; |
|
||||
BigInteger.prototype.toByteArray = bnToByteArray; |
|
||||
BigInteger.prototype.equals = bnEquals; |
|
||||
BigInteger.prototype.min = bnMin; |
|
||||
BigInteger.prototype.max = bnMax; |
|
||||
BigInteger.prototype.and = bnAnd; |
|
||||
BigInteger.prototype.or = bnOr; |
|
||||
BigInteger.prototype.xor = bnXor; |
|
||||
BigInteger.prototype.andNot = bnAndNot; |
|
||||
BigInteger.prototype.not = bnNot; |
|
||||
BigInteger.prototype.shiftLeft = bnShiftLeft; |
|
||||
BigInteger.prototype.shiftRight = bnShiftRight; |
|
||||
BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit; |
|
||||
BigInteger.prototype.bitCount = bnBitCount; |
|
||||
BigInteger.prototype.testBit = bnTestBit; |
|
||||
BigInteger.prototype.setBit = bnSetBit; |
|
||||
BigInteger.prototype.clearBit = bnClearBit; |
|
||||
BigInteger.prototype.flipBit = bnFlipBit; |
|
||||
BigInteger.prototype.add = bnAdd; |
|
||||
BigInteger.prototype.subtract = bnSubtract; |
|
||||
BigInteger.prototype.multiply = bnMultiply; |
|
||||
BigInteger.prototype.divide = bnDivide; |
|
||||
BigInteger.prototype.remainder = bnRemainder; |
|
||||
BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder; |
|
||||
BigInteger.prototype.modPow = bnModPow; |
|
||||
BigInteger.prototype.modInverse = bnModInverse; |
|
||||
BigInteger.prototype.pow = bnPow; |
|
||||
BigInteger.prototype.gcd = bnGCD; |
|
||||
BigInteger.prototype.isProbablePrime = bnIsProbablePrime; |
|
||||
|
|
||||
// JSBN-specific extension
|
|
||||
BigInteger.prototype.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)
|
|
@ -1,69 +1,67 @@ |
|||||
/** |
/** |
||||
* Implements Bitcoin's feature for signing arbitrary messages. |
* Implements Bitcoin's feature for signing arbitrary messages. |
||||
*/ |
*/ |
||||
Bitcoin.Message = (function () { |
var Message = {}; |
||||
var Message = {}; |
|
||||
|
|
||||
Message.magicPrefix = "Bitcoin Signed Message:\n"; |
Message.magicPrefix = "Bitcoin Signed Message:\n"; |
||||
|
|
||||
Message.makeMagicMessage = function (message) { |
Message.makeMagicMessage = function (message) { |
||||
var magicBytes = Crypto.charenc.UTF8.stringToBytes(Message.magicPrefix); |
var magicBytes = Crypto.charenc.UTF8.stringToBytes(Message.magicPrefix); |
||||
var messageBytes = Crypto.charenc.UTF8.stringToBytes(message); |
var messageBytes = Crypto.charenc.UTF8.stringToBytes(message); |
||||
|
|
||||
var buffer = []; |
var buffer = []; |
||||
buffer = buffer.concat(Bitcoin.Util.numToVarInt(magicBytes.length)); |
buffer = buffer.concat(Bitcoin.Util.numToVarInt(magicBytes.length)); |
||||
buffer = buffer.concat(magicBytes); |
buffer = buffer.concat(magicBytes); |
||||
buffer = buffer.concat(Bitcoin.Util.numToVarInt(messageBytes.length)); |
buffer = buffer.concat(Bitcoin.Util.numToVarInt(messageBytes.length)); |
||||
buffer = buffer.concat(messageBytes); |
buffer = buffer.concat(messageBytes); |
||||
|
|
||||
return buffer; |
return buffer; |
||||
}; |
}; |
||||
|
|
||||
Message.getHash = function (message) { |
Message.getHash = function (message) { |
||||
var buffer = Message.makeMagicMessage(message); |
var buffer = Message.makeMagicMessage(message); |
||||
return Crypto.SHA256(Crypto.SHA256(buffer, {asBytes: true}), {asBytes: true}); |
return Crypto.SHA256(Crypto.SHA256(buffer, {asBytes: true}), {asBytes: true}); |
||||
}; |
}; |
||||
|
|
||||
Message.signMessage = function (key, message, compressed) { |
Message.signMessage = function (key, message, compressed) { |
||||
var hash = Message.getHash(message); |
var hash = Message.getHash(message); |
||||
|
|
||||
var sig = key.sign(hash); |
var sig = key.sign(hash); |
||||
|
|
||||
var obj = Bitcoin.ECDSA.parseSig(sig); |
var obj = Bitcoin.ECDSA.parseSig(sig); |
||||
|
|
||||
var address = key.getBitcoinAddress().toString(); |
var address = key.getBitcoinAddress().toString(); |
||||
var i = Bitcoin.ECDSA.calcPubkeyRecoveryParam(address, obj.r, obj.s, hash); |
var i = Bitcoin.ECDSA.calcPubkeyRecoveryParam(address, obj.r, obj.s, hash); |
||||
|
|
||||
i += 27; |
i += 27; |
||||
if (compressed) i += 4; |
if (compressed) i += 4; |
||||
|
|
||||
var rBa = obj.r.toByteArrayUnsigned(); |
var rBa = obj.r.toByteArrayUnsigned(); |
||||
var sBa = obj.s.toByteArrayUnsigned(); |
var sBa = obj.s.toByteArrayUnsigned(); |
||||
|
|
||||
// Pad to 32 bytes per value
|
// Pad to 32 bytes per value
|
||||
while (rBa.length < 32) rBa.unshift(0); |
while (rBa.length < 32) rBa.unshift(0); |
||||
while (sBa.length < 32) sBa.unshift(0); |
while (sBa.length < 32) sBa.unshift(0); |
||||
|
|
||||
sig = [i].concat(rBa).concat(sBa); |
sig = [i].concat(rBa).concat(sBa); |
||||
|
|
||||
return Crypto.util.bytesToBase64(sig); |
return Crypto.util.bytesToBase64(sig); |
||||
}; |
}; |
||||
|
|
||||
Message.verifyMessage = function (address, sig, message) { |
Message.verifyMessage = function (address, sig, message) { |
||||
sig = Crypto.util.base64ToBytes(sig); |
sig = Crypto.util.base64ToBytes(sig); |
||||
sig = Bitcoin.ECDSA.parseSigCompact(sig); |
sig = Bitcoin.ECDSA.parseSigCompact(sig); |
||||
|
|
||||
var hash = Message.getHash(message); |
var hash = Message.getHash(message); |
||||
|
|
||||
var isCompressed = !!(sig.i & 4); |
var isCompressed = !!(sig.i & 4); |
||||
var pubKey = Bitcoin.ECDSA.recoverPubKey(sig.r, sig.s, hash, sig.i); |
var pubKey = Bitcoin.ECDSA.recoverPubKey(sig.r, sig.s, hash, sig.i); |
||||
|
|
||||
pubKey.setCompressed(isCompressed); |
pubKey.setCompressed(isCompressed); |
||||
|
|
||||
var expectedAddress = pubKey.getBitcoinAddress().toString(); |
var expectedAddress = pubKey.getBitcoinAddress().toString(); |
||||
|
|
||||
return (address === expectedAddress); |
return (address === expectedAddress); |
||||
}; |
}; |
||||
|
|
||||
return Message; |
module.exports = Message; |
||||
})(); |
|
||||
|
@ -1,154 +1,154 @@ |
|||||
(function () { |
var Opcode = function (num) { |
||||
var Opcode = Bitcoin.Opcode = function (num) { |
this.code = num; |
||||
this.code = num; |
}; |
||||
}; |
|
||||
|
Opcode.prototype.toString = function () { |
||||
Opcode.prototype.toString = function () { |
return Opcode.reverseMap[this.code]; |
||||
return Opcode.reverseMap[this.code]; |
}; |
||||
}; |
|
||||
|
Opcode.map = { |
||||
Opcode.map = { |
// push value
|
||||
// push value
|
OP_0 : 0, |
||||
OP_0 : 0, |
OP_FALSE : 0, |
||||
OP_FALSE : 0, |
OP_PUSHDATA1 : 76, |
||||
OP_PUSHDATA1 : 76, |
OP_PUSHDATA2 : 77, |
||||
OP_PUSHDATA2 : 77, |
OP_PUSHDATA4 : 78, |
||||
OP_PUSHDATA4 : 78, |
OP_1NEGATE : 79, |
||||
OP_1NEGATE : 79, |
OP_RESERVED : 80, |
||||
OP_RESERVED : 80, |
OP_1 : 81, |
||||
OP_1 : 81, |
OP_TRUE : 81, |
||||
OP_TRUE : 81, |
OP_2 : 82, |
||||
OP_2 : 82, |
OP_3 : 83, |
||||
OP_3 : 83, |
OP_4 : 84, |
||||
OP_4 : 84, |
OP_5 : 85, |
||||
OP_5 : 85, |
OP_6 : 86, |
||||
OP_6 : 86, |
OP_7 : 87, |
||||
OP_7 : 87, |
OP_8 : 88, |
||||
OP_8 : 88, |
OP_9 : 89, |
||||
OP_9 : 89, |
OP_10 : 90, |
||||
OP_10 : 90, |
OP_11 : 91, |
||||
OP_11 : 91, |
OP_12 : 92, |
||||
OP_12 : 92, |
OP_13 : 93, |
||||
OP_13 : 93, |
OP_14 : 94, |
||||
OP_14 : 94, |
OP_15 : 95, |
||||
OP_15 : 95, |
OP_16 : 96, |
||||
OP_16 : 96, |
|
||||
|
// control
|
||||
// control
|
OP_NOP : 97, |
||||
OP_NOP : 97, |
OP_VER : 98, |
||||
OP_VER : 98, |
OP_IF : 99, |
||||
OP_IF : 99, |
OP_NOTIF : 100, |
||||
OP_NOTIF : 100, |
OP_VERIF : 101, |
||||
OP_VERIF : 101, |
OP_VERNOTIF : 102, |
||||
OP_VERNOTIF : 102, |
OP_ELSE : 103, |
||||
OP_ELSE : 103, |
OP_ENDIF : 104, |
||||
OP_ENDIF : 104, |
OP_VERIFY : 105, |
||||
OP_VERIFY : 105, |
OP_RETURN : 106, |
||||
OP_RETURN : 106, |
|
||||
|
// stack ops
|
||||
// stack ops
|
OP_TOALTSTACK : 107, |
||||
OP_TOALTSTACK : 107, |
OP_FROMALTSTACK : 108, |
||||
OP_FROMALTSTACK : 108, |
OP_2DROP : 109, |
||||
OP_2DROP : 109, |
OP_2DUP : 110, |
||||
OP_2DUP : 110, |
OP_3DUP : 111, |
||||
OP_3DUP : 111, |
OP_2OVER : 112, |
||||
OP_2OVER : 112, |
OP_2ROT : 113, |
||||
OP_2ROT : 113, |
OP_2SWAP : 114, |
||||
OP_2SWAP : 114, |
OP_IFDUP : 115, |
||||
OP_IFDUP : 115, |
OP_DEPTH : 116, |
||||
OP_DEPTH : 116, |
OP_DROP : 117, |
||||
OP_DROP : 117, |
OP_DUP : 118, |
||||
OP_DUP : 118, |
OP_NIP : 119, |
||||
OP_NIP : 119, |
OP_OVER : 120, |
||||
OP_OVER : 120, |
OP_PICK : 121, |
||||
OP_PICK : 121, |
OP_ROLL : 122, |
||||
OP_ROLL : 122, |
OP_ROT : 123, |
||||
OP_ROT : 123, |
OP_SWAP : 124, |
||||
OP_SWAP : 124, |
OP_TUCK : 125, |
||||
OP_TUCK : 125, |
|
||||
|
// splice ops
|
||||
// splice ops
|
OP_CAT : 126, |
||||
OP_CAT : 126, |
OP_SUBSTR : 127, |
||||
OP_SUBSTR : 127, |
OP_LEFT : 128, |
||||
OP_LEFT : 128, |
OP_RIGHT : 129, |
||||
OP_RIGHT : 129, |
OP_SIZE : 130, |
||||
OP_SIZE : 130, |
|
||||
|
// bit logic
|
||||
// bit logic
|
OP_INVERT : 131, |
||||
OP_INVERT : 131, |
OP_AND : 132, |
||||
OP_AND : 132, |
OP_OR : 133, |
||||
OP_OR : 133, |
OP_XOR : 134, |
||||
OP_XOR : 134, |
OP_EQUAL : 135, |
||||
OP_EQUAL : 135, |
OP_EQUALVERIFY : 136, |
||||
OP_EQUALVERIFY : 136, |
OP_RESERVED1 : 137, |
||||
OP_RESERVED1 : 137, |
OP_RESERVED2 : 138, |
||||
OP_RESERVED2 : 138, |
|
||||
|
// numeric
|
||||
// numeric
|
OP_1ADD : 139, |
||||
OP_1ADD : 139, |
OP_1SUB : 140, |
||||
OP_1SUB : 140, |
OP_2MUL : 141, |
||||
OP_2MUL : 141, |
OP_2DIV : 142, |
||||
OP_2DIV : 142, |
OP_NEGATE : 143, |
||||
OP_NEGATE : 143, |
OP_ABS : 144, |
||||
OP_ABS : 144, |
OP_NOT : 145, |
||||
OP_NOT : 145, |
OP_0NOTEQUAL : 146, |
||||
OP_0NOTEQUAL : 146, |
|
||||
|
OP_ADD : 147, |
||||
OP_ADD : 147, |
OP_SUB : 148, |
||||
OP_SUB : 148, |
OP_MUL : 149, |
||||
OP_MUL : 149, |
OP_DIV : 150, |
||||
OP_DIV : 150, |
OP_MOD : 151, |
||||
OP_MOD : 151, |
OP_LSHIFT : 152, |
||||
OP_LSHIFT : 152, |
OP_RSHIFT : 153, |
||||
OP_RSHIFT : 153, |
|
||||
|
OP_BOOLAND : 154, |
||||
OP_BOOLAND : 154, |
OP_BOOLOR : 155, |
||||
OP_BOOLOR : 155, |
OP_NUMEQUAL : 156, |
||||
OP_NUMEQUAL : 156, |
OP_NUMEQUALVERIFY : 157, |
||||
OP_NUMEQUALVERIFY : 157, |
OP_NUMNOTEQUAL : 158, |
||||
OP_NUMNOTEQUAL : 158, |
OP_LESSTHAN : 159, |
||||
OP_LESSTHAN : 159, |
OP_GREATERTHAN : 160, |
||||
OP_GREATERTHAN : 160, |
OP_LESSTHANOREQUAL : 161, |
||||
OP_LESSTHANOREQUAL : 161, |
OP_GREATERTHANOREQUAL : 162, |
||||
OP_GREATERTHANOREQUAL : 162, |
OP_MIN : 163, |
||||
OP_MIN : 163, |
OP_MAX : 164, |
||||
OP_MAX : 164, |
|
||||
|
OP_WITHIN : 165, |
||||
OP_WITHIN : 165, |
|
||||
|
// crypto
|
||||
// crypto
|
OP_RIPEMD160 : 166, |
||||
OP_RIPEMD160 : 166, |
OP_SHA1 : 167, |
||||
OP_SHA1 : 167, |
OP_SHA256 : 168, |
||||
OP_SHA256 : 168, |
OP_HASH160 : 169, |
||||
OP_HASH160 : 169, |
OP_HASH256 : 170, |
||||
OP_HASH256 : 170, |
OP_CODESEPARATOR : 171, |
||||
OP_CODESEPARATOR : 171, |
OP_CHECKSIG : 172, |
||||
OP_CHECKSIG : 172, |
OP_CHECKSIGVERIFY : 173, |
||||
OP_CHECKSIGVERIFY : 173, |
OP_CHECKMULTISIG : 174, |
||||
OP_CHECKMULTISIG : 174, |
OP_CHECKMULTISIGVERIFY : 175, |
||||
OP_CHECKMULTISIGVERIFY : 175, |
|
||||
|
// expansion
|
||||
// expansion
|
OP_NOP1 : 176, |
||||
OP_NOP1 : 176, |
OP_NOP2 : 177, |
||||
OP_NOP2 : 177, |
OP_NOP3 : 178, |
||||
OP_NOP3 : 178, |
OP_NOP4 : 179, |
||||
OP_NOP4 : 179, |
OP_NOP5 : 180, |
||||
OP_NOP5 : 180, |
OP_NOP6 : 181, |
||||
OP_NOP6 : 181, |
OP_NOP7 : 182, |
||||
OP_NOP7 : 182, |
OP_NOP8 : 183, |
||||
OP_NOP8 : 183, |
OP_NOP9 : 184, |
||||
OP_NOP9 : 184, |
OP_NOP10 : 185, |
||||
OP_NOP10 : 185, |
|
||||
|
// template matching params
|
||||
// template matching params
|
OP_PUBKEYHASH : 253, |
||||
OP_PUBKEYHASH : 253, |
OP_PUBKEY : 254, |
||||
OP_PUBKEY : 254, |
OP_INVALIDOPCODE : 255 |
||||
OP_INVALIDOPCODE : 255 |
}; |
||||
}; |
|
||||
|
Opcode.reverseMap = []; |
||||
Opcode.reverseMap = []; |
|
||||
|
for (var i in Opcode.map) { |
||||
for (var i in Opcode.map) { |
Opcode.reverseMap[Opcode.map[i]] = i; |
||||
Opcode.reverseMap[Opcode.map[i]] = i; |
} |
||||
} |
|
||||
})(); |
module.exports = Opcode; |
||||
|
@ -1,347 +1,369 @@ |
|||||
(function () { |
var Opcode = require('./opcode'); |
||||
var Opcode = Bitcoin.Opcode; |
var util = require('./util'); |
||||
|
var conv = require('./convert'); |
||||
|
|
||||
// Make opcodes available as pseudo-constants
|
var Script = function (data) { |
||||
for (var i in Opcode.map) { |
if (!data) { |
||||
eval("var " + i + " = " + Opcode.map[i] + ";"); |
this.buffer = []; |
||||
|
} else if ("string" == typeof data) { |
||||
|
this.buffer = Crypto.util.base64ToBytes(data); |
||||
|
} else if (util.isArray(data)) { |
||||
|
this.buffer = data; |
||||
|
} else if (data instanceof Script) { |
||||
|
this.buffer = data.buffer; |
||||
|
} else { |
||||
|
throw new Error("Invalid script"); |
||||
} |
} |
||||
|
|
||||
var Script = Bitcoin.Script = function (data) { |
this.parse(); |
||||
if (!data) { |
}; |
||||
this.buffer = []; |
|
||||
} else if ("string" == typeof data) { |
Script.fromPubKey = function(str) { |
||||
this.buffer = Crypto.util.base64ToBytes(data); |
var script = new Script(); |
||||
} else if (Bitcoin.Util.isArray(data)) { |
var s = str.split(" "); |
||||
this.buffer = data; |
for (var i in s) { |
||||
} else if (data instanceof Script) { |
if (Opcode.map.hasOwnProperty(s[i])){ |
||||
this.buffer = data.buffer; |
script.writeOp(Opcode.map[s[i]]); |
||||
} else { |
} else { |
||||
throw new Error("Invalid script"); |
script.writeBytes(conv.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(conv.hexToBytes(s[i])); |
||||
|
} |
||||
|
} |
||||
|
return script; |
||||
|
}; |
||||
|
|
||||
/** |
/** |
||||
* Update the parsed script representation. |
* Update the parsed script representation. |
||||
* |
* |
||||
* Each Script object stores the script in two formats. First as a raw byte |
* 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 |
* array and second as an array of "chunks", such as opcodes and pieces of |
||||
* data. |
* data. |
||||
* |
* |
||||
* This method updates the chunks cache. Normally this is called by the |
* 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 |
* 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. |
* the script buffer manually, you should update the chunks using this method. |
||||
*/ |
*/ |
||||
Script.prototype.parse = function () { |
Script.prototype.parse = function () { |
||||
var self = this; |
var self = this; |
||||
|
|
||||
this.chunks = []; |
this.chunks = []; |
||||
|
|
||||
// Cursor
|
// Cursor
|
||||
var i = 0; |
var i = 0; |
||||
|
|
||||
// Read n bytes and store result as a chunk
|
// Read n bytes and store result as a chunk
|
||||
function readChunk(n) { |
function readChunk(n) { |
||||
self.chunks.push(self.buffer.slice(i, i + n)); |
self.chunks.push(self.buffer.slice(i, i + n)); |
||||
i += n; |
i += n; |
||||
}; |
}; |
||||
|
|
||||
while (i < this.buffer.length) { |
while (i < this.buffer.length) { |
||||
var opcode = this.buffer[i++]; |
var opcode = this.buffer[i++]; |
||||
if (opcode >= 0xF0) { |
if (opcode >= 0xF0) { |
||||
// Two byte opcode
|
// Two byte opcode
|
||||
opcode = (opcode << 8) | this.buffer[i++]; |
opcode = (opcode << 8) | this.buffer[i++]; |
||||
} |
} |
||||
|
|
||||
var len; |
var len; |
||||
if (opcode > 0 && opcode < OP_PUSHDATA1) { |
if (opcode > 0 && opcode < Opcode.map.OP_PUSHDATA1) { |
||||
// Read some bytes of data, opcode value is the length of data
|
// Read some bytes of data, opcode value is the length of data
|
||||
readChunk(opcode); |
readChunk(opcode); |
||||
} else if (opcode == OP_PUSHDATA1) { |
} else if (opcode == Opcode.map.OP_PUSHDATA1) { |
||||
len = this.buffer[i++]; |
len = this.buffer[i++]; |
||||
readChunk(len); |
readChunk(len); |
||||
} else if (opcode == OP_PUSHDATA2) { |
} else if (opcode == Opcode.map.OP_PUSHDATA2) { |
||||
len = (this.buffer[i++] << 8) | this.buffer[i++]; |
len = (this.buffer[i++] << 8) | this.buffer[i++]; |
||||
readChunk(len); |
readChunk(len); |
||||
} else if (opcode == OP_PUSHDATA4) { |
} else if (opcode == Opcode.map.OP_PUSHDATA4) { |
||||
len = (this.buffer[i++] << 24) | |
len = (this.buffer[i++] << 24) | |
||||
(this.buffer[i++] << 16) | |
(this.buffer[i++] << 16) | |
||||
(this.buffer[i++] << 8) | |
(this.buffer[i++] << 8) | |
||||
this.buffer[i++]; |
this.buffer[i++]; |
||||
readChunk(len); |
readChunk(len); |
||||
} else { |
} else { |
||||
this.chunks.push(opcode); |
this.chunks.push(opcode); |
||||
} |
|
||||
} |
} |
||||
}; |
} |
||||
|
}; |
||||
|
|
||||
/** |
/** |
||||
* Compare the script to known templates of scriptPubKey. |
* Compare the script to known templates of scriptPubKey. |
||||
* |
* |
||||
* This method will compare the script to a small number of standard script |
* This method will compare the script to a small number of standard script |
||||
* templates and return a string naming the detected type. |
* templates and return a string naming the detected type. |
||||
* |
* |
||||
* Currently supported are: |
* Currently supported are: |
||||
* Address: |
* Address: |
||||
* Paying to a Bitcoin address which is the hash of a pubkey. |
* Paying to a Bitcoin address which is the hash of a pubkey. |
||||
* OP_DUP OP_HASH160 [pubKeyHash] OP_EQUALVERIFY OP_CHECKSIG |
* OP_DUP OP_HASH160 [pubKeyHash] OP_EQUALVERIFY OP_CHECKSIG |
||||
* |
* |
||||
* Pubkey: |
* Pubkey: |
||||
* Paying to a public key directly. |
* Paying to a public key directly. |
||||
* [pubKey] OP_CHECKSIG |
* [pubKey] OP_CHECKSIG |
||||
* |
* |
||||
* Strange: |
* Strange: |
||||
* Any other script (no template matched). |
* Any other script (no template matched). |
||||
*/ |
*/ |
||||
Script.prototype.getOutType = function () { |
Script.prototype.getOutType = function () { |
||||
|
if (this.chunks[this.chunks.length-1] == Opcode.map.OP_CHECKMULTISIG && this.chunks[this.chunks.length-2] <= 3) { |
||||
|
// Transfer to M-OF-N
|
||||
|
return 'Multisig'; |
||||
|
} else if (this.chunks.length == 5 && |
||||
|
this.chunks[0] == Opcode.map.OP_DUP && |
||||
|
this.chunks[1] == Opcode.map.OP_HASH160 && |
||||
|
this.chunks[3] == Opcode.map.OP_EQUALVERIFY && |
||||
|
this.chunks[4] == Opcode.map.OP_CHECKSIG) { |
||||
|
// Transfer to Bitcoin address
|
||||
|
return 'Address'; |
||||
|
} else if (this.chunks.length == 2 && |
||||
|
this.chunks[1] == Opcode.map.OP_CHECKSIG) { |
||||
|
// Transfer to IP address
|
||||
|
return 'Pubkey'; |
||||
|
} else { |
||||
|
return 'Strange'; |
||||
|
} |
||||
|
} |
||||
|
|
||||
if (this.chunks[this.chunks.length-1] == OP_CHECKMULTISIG && this.chunks[this.chunks.length-2] <= 3) { |
/** |
||||
// Transfer to M-OF-N
|
* Returns the affected address hash for this output. |
||||
return 'Multisig'; |
* |
||||
} else if (this.chunks.length == 5 && |
* For standard transactions, this will return the hash of the pubKey that |
||||
this.chunks[0] == OP_DUP && |
* can spend this output. |
||||
this.chunks[1] == OP_HASH160 && |
* |
||||
this.chunks[3] == OP_EQUALVERIFY && |
* In the future, for payToScriptHash outputs, this will return the |
||||
this.chunks[4] == OP_CHECKSIG) { |
* scriptHash. Note that non-standard and standard payToScriptHash transactions |
||||
// Transfer to Bitcoin address
|
* look the same |
||||
return 'Address'; |
* |
||||
} else if (this.chunks.length == 2 && |
* This method is useful for indexing transactions. |
||||
this.chunks[1] == OP_CHECKSIG) { |
*/ |
||||
// Transfer to IP address
|
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: " + this.getOutType()); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* Old name for Script#simpleOutHash. |
||||
|
* |
||||
|
* @deprecated |
||||
|
*/ |
||||
|
Script.prototype.simpleOutPubKeyHash = Script.prototype.simpleOutHash; |
||||
|
|
||||
|
/** |
||||
|
* 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'; |
return 'Pubkey'; |
||||
|
} else if (this.chunks.length == 2 && |
||||
|
util.isArray(this.chunks[0]) && |
||||
|
util.isArray(this.chunks[1])) { |
||||
|
return 'Address'; |
||||
} else { |
} else { |
||||
return 'Strange'; |
return 'Strange'; |
||||
} |
} |
||||
} |
}; |
||||
|
|
||||
/** |
/** |
||||
* Returns the affected address hash for this output. |
* Returns the affected public key for this input. |
||||
* |
* |
||||
* For standard transactions, this will return the hash of the pubKey that |
* This currently only works with payToPubKeyHash transactions. It will also |
||||
* can spend this output. |
* work in the future for standard payToScriptHash transactions that use a |
||||
* |
* single public key. |
||||
* In the future, for payToScriptHash outputs, this will return the |
* |
||||
* scriptHash. Note that non-standard and standard payToScriptHash transactions |
* However for multi-key and other complex transactions, this will only return |
||||
* look the same |
* one of the keys or raise an error. Therefore, it is recommended for indexing |
||||
* |
* purposes to use Script#simpleInHash or Script#simpleOutHash instead. |
||||
* This method is useful for indexing transactions. |
* |
||||
*/ |
* @deprecated |
||||
Script.prototype.simpleOutHash = function () |
*/ |
||||
{ |
Script.prototype.simpleInPubKey = function () |
||||
switch (this.getOutType()) { |
{ |
||||
case 'Address': |
switch (this.getInType()) { |
||||
return this.chunks[2]; |
case 'Address': |
||||
case 'Pubkey': |
return this.chunks[1]; |
||||
return Bitcoin.Util.sha256ripe160(this.chunks[0]); |
case 'Pubkey': |
||||
default: |
// TODO: Theoretically, we could recover the pubkey from the sig here.
|
||||
throw new Error("Encountered non-standard scriptPubKey"); |
// See https://bitcointalk.org/?topic=6430.0
|
||||
} |
throw new Error("Script does not contain pubkey."); |
||||
}; |
default: |
||||
|
throw new Error("Encountered non-standard scriptSig"); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
/** |
/** |
||||
* Old name for Script#simpleOutHash. |
* Returns the affected address hash for this input. |
||||
* |
* |
||||
* @deprecated |
* For standard transactions, this will return the hash of the pubKey that |
||||
*/ |
* can spend this output. |
||||
Script.prototype.simpleOutPubKeyHash = Script.prototype.simpleOutHash; |
* |
||||
|
* 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()); |
||||
|
}; |
||||
|
|
||||
/** |
/** |
||||
* Compare the script to known templates of scriptSig. |
* Old name for Script#simpleInHash. |
||||
* |
* |
||||
* This method will compare the script to a small number of standard script |
* @deprecated |
||||
* templates and return a string naming the detected type. |
*/ |
||||
* |
Script.prototype.simpleInPubKeyHash = Script.prototype.simpleInHash; |
||||
* 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'; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
/** |
/** |
||||
* Returns the affected public key for this input. |
* Add an op code to the script. |
||||
* |
*/ |
||||
* This currently only works with payToPubKeyHash transactions. It will also |
Script.prototype.writeOp = function (opcode) |
||||
* work in the future for standard payToScriptHash transactions that use a |
{ |
||||
* single public key. |
this.buffer.push(opcode); |
||||
* |
this.chunks.push(opcode); |
||||
* However for multi-key and other complex transactions, this will only return |
}; |
||||
* one of the keys or raise an error. Therefore, it is recommended for indexing |
|
||||
* purposes to use Script#simpleInHash or Script#simpleOutHash instead. |
|
||||
* |
|
||||
* @deprecated |
|
||||
*/ |
|
||||
Script.prototype.simpleInPubKey = function () |
|
||||
{ |
|
||||
switch (this.getInType()) { |
|
||||
case 'Address': |
|
||||
return this.chunks[1]; |
|
||||
case 'Pubkey': |
|
||||
// TODO: Theoretically, we could recover the pubkey from the sig here.
|
|
||||
// See https://bitcointalk.org/?topic=6430.0
|
|
||||
throw new Error("Script does not contain pubkey."); |
|
||||
default: |
|
||||
throw new Error("Encountered non-standard scriptSig"); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
/** |
/** |
||||
* Returns the affected address hash for this input. |
* Add a data chunk to the script. |
||||
* |
*/ |
||||
* For standard transactions, this will return the hash of the pubKey that |
Script.prototype.writeBytes = function (data) |
||||
* can spend this output. |
{ |
||||
* |
if (data.length < Opcode.map.OP_PUSHDATA1) { |
||||
* In the future, for standard payToScriptHash inputs, this will return the |
this.buffer.push(data.length); |
||||
* scriptHash. |
} else if (data.length <= 0xff) { |
||||
* |
this.buffer.push(Opcode.map.OP_PUSHDATA1); |
||||
* Note: This function provided for convenience. If you have the corresponding |
this.buffer.push(data.length); |
||||
* scriptPubKey available, you are urged to use Script#simpleOutHash instead |
} else if (data.length <= 0xffff) { |
||||
* as it is more reliable for non-standard payToScriptHash transactions. |
this.buffer.push(Opcode.map.OP_PUSHDATA2); |
||||
* |
this.buffer.push(data.length & 0xff); |
||||
* This method is useful for indexing transactions. |
this.buffer.push((data.length >>> 8) & 0xff); |
||||
*/ |
} else { |
||||
Script.prototype.simpleInHash = function () |
this.buffer.push(Opcode.map.OP_PUSHDATA4); |
||||
{ |
this.buffer.push(data.length & 0xff); |
||||
return Bitcoin.Util.sha256ripe160(this.simpleInPubKey()); |
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); |
||||
|
}; |
||||
|
|
||||
/** |
/** |
||||
* Old name for Script#simpleInHash. |
* Create a standard payToPubKeyHash output. |
||||
* |
*/ |
||||
* @deprecated |
Script.createOutputScript = function (address) |
||||
*/ |
{ |
||||
Script.prototype.simpleInPubKeyHash = Script.prototype.simpleInHash; |
var script = new Script(); |
||||
|
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; |
||||
|
}; |
||||
|
|
||||
/** |
|
||||
* 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. |
* Extract bitcoin addresses from an output script |
||||
*/ |
*/ |
||||
Script.prototype.writeBytes = function (data) |
Script.prototype.extractAddresses = function (addresses) |
||||
{ |
{ |
||||
if (data.length < OP_PUSHDATA1) { |
switch (this.getOutType()) { |
||||
this.buffer.push(data.length); |
case 'Address': |
||||
} else if (data.length <= 0xff) { |
addresses.push(new Address(this.chunks[2])); |
||||
this.buffer.push(OP_PUSHDATA1); |
return 1; |
||||
this.buffer.push(data.length); |
case 'Pubkey': |
||||
} else if (data.length <= 0xffff) { |
addresses.push(new Address(Util.sha256ripe160(this.chunks[0]))); |
||||
this.buffer.push(OP_PUSHDATA2); |
return 1; |
||||
this.buffer.push(data.length & 0xff); |
case 'Multisig': |
||||
this.buffer.push((data.length >>> 8) & 0xff); |
for (var i = 1; i < this.chunks.length-2; ++i) { |
||||
} else { |
addresses.push(new Address(Util.sha256ripe160(this.chunks[i]))); |
||||
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); |
return this.chunks[0] - OP_1 + 1; |
||||
this.chunks.push(data); |
default: |
||||
}; |
throw new Error("Encountered non-standard scriptPubKey"); |
||||
|
} |
||||
/** |
}; |
||||
* 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 |
* Create an m-of-n output script |
||||
*/ |
*/ |
||||
Script.prototype.extractAddresses = function (addresses) |
Script.createMultiSigOutputScript = function (m, pubkeys) |
||||
{ |
{ |
||||
switch (this.getOutType()) { |
var script = new Script(); |
||||
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"); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
/** |
script.writeOp(Opcode.map.OP_1 + m - 1); |
||||
* 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]); |
||||
|
} |
||||
|
|
||||
for (var i = 0; i < pubkeys.length; ++i) { |
script.writeOp(Opcode.map.OP_1 + pubkeys.length - 1); |
||||
script.writeBytes(pubkeys[i]); |
|
||||
} |
|
||||
|
|
||||
script.writeOp(OP_1 + pubkeys.length - 1); |
script.writeOp(Opcode.map.OP_CHECKMULTISIG); |
||||
|
|
||||
script.writeOp(OP_CHECKMULTISIG); |
return script; |
||||
|
}; |
||||
|
|
||||
return script; |
/** |
||||
}; |
* Create a standard payToPubKeyHash input. |
||||
|
*/ |
||||
|
Script.createInputScript = function (signature, pubKey) |
||||
|
{ |
||||
|
var script = new Script(); |
||||
|
script.writeBytes(signature); |
||||
|
script.writeBytes(pubKey); |
||||
|
return script; |
||||
|
}; |
||||
|
|
||||
/** |
Script.prototype.clone = function () |
||||
* Create a standard payToPubKeyHash input. |
{ |
||||
*/ |
return new Script(this.buffer); |
||||
Script.createInputScript = function (signature, pubKey) |
}; |
||||
{ |
|
||||
var script = new Script(); |
|
||||
script.writeBytes(signature); |
|
||||
script.writeBytes(pubKey); |
|
||||
return script; |
|
||||
}; |
|
||||
|
|
||||
Script.prototype.clone = function () |
module.exports = Script; |
||||
{ |
|
||||
return new Script(this.buffer); |
|
||||
}; |
|
||||
})(); |
|
||||
|
@ -1,445 +1,467 @@ |
|||||
(function () { |
var BigInteger = require('./jsbn/jsbn'); |
||||
var Script = Bitcoin.Script; |
var Script = require('./script'); |
||||
|
var util = require('./util'); |
||||
var Transaction = Bitcoin.Transaction = function (doc) { |
var conv = require('./convert'); |
||||
this.version = 1; |
var Crypto = require('./crypto-js/crypto'); |
||||
this.lock_time = 0; |
|
||||
this.ins = []; |
var Transaction = function (doc) { |
||||
this.outs = []; |
this.version = 1; |
||||
this.timestamp = null; |
this.lock_time = 0; |
||||
this.block = null; |
this.ins = []; |
||||
|
this.outs = []; |
||||
if (doc) { |
this.timestamp = null; |
||||
if (doc.hash) this.hash = doc.hash; |
this.block = null; |
||||
if (doc.version) this.version = doc.version; |
|
||||
if (doc.lock_time) this.lock_time = doc.lock_time; |
if (doc) { |
||||
if (doc.ins && doc.ins.length) { |
if (doc.hash) this.hash = doc.hash; |
||||
for (var i = 0; i < doc.ins.length; i++) { |
if (doc.version) this.version = doc.version; |
||||
this.addInput(new TransactionIn(doc.ins[i])); |
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; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
/** |
|
||||
* 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])); |
|
||||
} |
|
||||
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) { |
||||
|
for (var i = 0; i < doc.outs.length; i++) { |
||||
/** |
this.addOutput(new TransactionOut(doc.outs[i])); |
||||
* 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.outs.push(new TransactionOut({ |
|
||||
value: value, |
|
||||
script: Script.createOutputScript(address) |
|
||||
})); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
/** |
|
||||
* 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()); |
|
||||
|
|
||||
return buffer; |
|
||||
}; |
|
||||
|
|
||||
var OP_CODESEPARATOR = 171; |
|
||||
|
|
||||
var SIGHASH_ALL = 1; |
|
||||
var SIGHASH_NONE = 2; |
|
||||
var SIGHASH_SINGLE = 3; |
|
||||
var SIGHASH_ANYONECANPAY = 80; |
|
||||
|
|
||||
/** |
|
||||
* 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(); |
|
||||
|
|
||||
// In case concatenating two scripts ends up with two codeseparators,
|
|
||||
// or an extra one at the end, this prevents all those possible
|
|
||||
// incompatibilities.
|
|
||||
/*scriptCode = scriptCode.filter(function (val) { |
|
||||
return val !== OP_CODESEPARATOR; |
|
||||
});*/ |
|
||||
|
|
||||
// Blank out other inputs' signatures
|
|
||||
for (var i = 0; i < txTmp.ins.length; i++) { |
|
||||
txTmp.ins[i].script = new Script(); |
|
||||
} |
|
||||
|
|
||||
txTmp.ins[inIndex].script = connectedScript; |
|
||||
|
|
||||
// Blank out some of the outputs
|
|
||||
if ((hashType & 0x1f) == SIGHASH_NONE) { |
|
||||
txTmp.outs = []; |
|
||||
|
|
||||
// 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
|
|
||||
} |
} |
||||
|
if (doc.timestamp) this.timestamp = doc.timestamp; |
||||
// Blank out other inputs completely, not recommended for open transactions
|
if (doc.block) this.block = doc.block; |
||||
if (hashType & SIGHASH_ANYONECANPAY) { |
} |
||||
txTmp.ins = [txTmp.ins[inIndex]]; |
}; |
||||
} |
|
||||
|
/** |
||||
var buffer = txTmp.serialize(); |
* Turn transaction data into Transaction objects. |
||||
|
* |
||||
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(hashType)]).reverse()); |
* Takes an array of plain JavaScript objects containing transaction data and |
||||
|
* returns an array of Transaction objects. |
||||
var hash1 = Crypto.SHA256(buffer, {asBytes: true}); |
*/ |
||||
|
Transaction.objectify = function (txs) { |
||||
return Crypto.SHA256(hash1, {asBytes: true}); |
var objs = []; |
||||
}; |
for (var i = 0; i < txs.length; i++) { |
||||
|
objs.push(new Transaction(txs[i])); |
||||
/** |
} |
||||
* Calculate and return the transaction's hash. |
return objs; |
||||
*/ |
}; |
||||
Transaction.prototype.getHash = function () |
|
||||
{ |
/** |
||||
var buffer = this.serialize(); |
* Create a new txin. |
||||
return Crypto.SHA256(Crypto.SHA256(buffer, {asBytes: true}), {asBytes: true}); |
* |
||||
}; |
* 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 |
||||
* Create a copy of this transaction object. |
* referenced output will be created. |
||||
*/ |
* |
||||
Transaction.prototype.clone = function () |
* Note that this method does not sign the created input. |
||||
{ |
*/ |
||||
var newTx = new Transaction(); |
Transaction.prototype.addInput = function (tx, outIndex) { |
||||
newTx.version = this.version; |
if (arguments[0] instanceof TransactionIn) { |
||||
newTx.lock_time = this.lock_time; |
this.ins.push(arguments[0]); |
||||
for (var i = 0; i < this.ins.length; i++) { |
} else { |
||||
var txin = this.ins[i].clone(); |
this.ins.push(new TransactionIn({ |
||||
newTx.addInput(txin); |
outpoint: { |
||||
} |
hash: tx.hash, |
||||
for (var i = 0; i < this.outs.length; i++) { |
index: outIndex |
||||
var txout = this.outs[i].clone(); |
}, |
||||
newTx.addOutput(txout); |
script: new Script(), |
||||
} |
sequence: 4294967295 |
||||
return newTx; |
})); |
||||
}; |
} |
||||
|
}; |
||||
/** |
|
||||
* Analyze how this transaction affects a wallet. |
/** |
||||
* |
* Create a new txout. |
||||
* Returns an object with properties 'impact', 'type' and 'addr'. |
* |
||||
* |
* Can be called with an existing TransactionOut object to add it to the |
||||
* 'impact' is an object, see Transaction#calcImpact. |
* 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 |
||||
* 'type' can be one of the following: |
* values will be created. |
||||
* |
*/ |
||||
* recv: |
Transaction.prototype.addOutput = function (address, value) { |
||||
* This is an incoming transaction, the wallet received money. |
if (arguments[0] instanceof TransactionOut) { |
||||
* 'addr' contains the first address in the wallet that receives money |
this.outs.push(arguments[0]); |
||||
* from this transaction. |
} else { |
||||
* |
if (value instanceof BigInteger) { |
||||
* self: |
value = value.toByteArrayUnsigned().reverse(); |
||||
* This is an internal transaction, money was sent within the wallet. |
while (value.length < 8) value.push(0); |
||||
* 'addr' is undefined. |
} else if (Bitcoin.Util.isArray(value)) { |
||||
* |
// Nothing to do
|
||||
* 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; |
|
||||
|
|
||||
var allFromMe = true, |
|
||||
allToMe = true, |
|
||||
firstRecvHash = null, |
|
||||
firstMeRecvHash = null, |
|
||||
firstSendHash = null; |
|
||||
|
|
||||
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; |
|
||||
} |
|
||||
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); |
this.outs.push(new TransactionOut({ |
||||
|
value: value, |
||||
var analysis = {}; |
script: Script.createOutputScript(address) |
||||
|
})); |
||||
analysis.impact = impact; |
} |
||||
|
}; |
||||
if (impact.sign > 0 && impact.value.compareTo(BigInteger.ZERO) > 0) { |
|
||||
analysis.type = 'recv'; |
// TODO(shtylman) crypto sha uses this also
|
||||
analysis.addr = new Bitcoin.Address(firstMeRecvHash); |
// Convert a byte array to big-endian 32-bit words
|
||||
} else if (allFromMe && allToMe) { |
var bytesToWords = function (bytes) { |
||||
analysis.type = 'self'; |
for (var words = [], i = 0, b = 0; i < bytes.length; i++, b += 8) |
||||
} else if (allFromMe) { |
words[b >>> 5] |= bytes[i] << (24 - b % 32); |
||||
analysis.type = 'sent'; |
return words; |
||||
// TODO: Right now, firstRecvHash is the first output, which - if the
|
}; |
||||
// transaction was not generated by this library could be the
|
|
||||
// change address.
|
// Convert big-endian 32-bit words to a byte array
|
||||
analysis.addr = new Bitcoin.Address(firstRecvHash); |
var wordsToBytes = function (words) { |
||||
} else { |
for (var bytes = [], b = 0; b < words.length * 32; b += 8) |
||||
analysis.type = "other"; |
bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF); |
||||
|
return bytes; |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* 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(wordsToBytes([parseInt(this.version)]).reverse()); |
||||
|
buffer = buffer.concat(util.numToVarInt(this.ins.length)); |
||||
|
for (var i = 0; i < this.ins.length; i++) { |
||||
|
var txin = this.ins[i]; |
||||
|
buffer = buffer.concat(conv.base64ToBytes(txin.outpoint.hash)); |
||||
|
buffer = buffer.concat(wordsToBytes([parseInt(txin.outpoint.index)]).reverse()); |
||||
|
var scriptBytes = txin.script.buffer; |
||||
|
buffer = buffer.concat(util.numToVarInt(scriptBytes.length)); |
||||
|
buffer = buffer.concat(scriptBytes); |
||||
|
buffer = buffer.concat(wordsToBytes([parseInt(txin.sequence)]).reverse()); |
||||
|
} |
||||
|
buffer = buffer.concat(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(util.numToVarInt(scriptBytes.length)); |
||||
|
buffer = buffer.concat(scriptBytes); |
||||
|
} |
||||
|
buffer = buffer.concat(wordsToBytes([parseInt(this.lock_time)]).reverse()); |
||||
|
|
||||
|
return buffer; |
||||
|
}; |
||||
|
|
||||
|
var OP_CODESEPARATOR = 171; |
||||
|
|
||||
|
var SIGHASH_ALL = 1; |
||||
|
var SIGHASH_NONE = 2; |
||||
|
var SIGHASH_SINGLE = 3; |
||||
|
var SIGHASH_ANYONECANPAY = 80; |
||||
|
|
||||
|
/** |
||||
|
* 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(); |
||||
|
|
||||
|
// In case concatenating two scripts ends up with two codeseparators,
|
||||
|
// or an extra one at the end, this prevents all those possible
|
||||
|
// incompatibilities.
|
||||
|
/*scriptCode = scriptCode.filter(function (val) { |
||||
|
return val !== OP_CODESEPARATOR; |
||||
|
});*/ |
||||
|
|
||||
|
// Blank out other inputs' signatures
|
||||
|
for (var i = 0; i < txTmp.ins.length; i++) { |
||||
|
txTmp.ins[i].script = new Script(); |
||||
|
} |
||||
|
|
||||
|
txTmp.ins[inIndex].script = connectedScript; |
||||
|
|
||||
|
// Blank out some of the outputs
|
||||
|
if ((hashType & 0x1f) == SIGHASH_NONE) { |
||||
|
txTmp.outs = []; |
||||
|
|
||||
|
// 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
|
||||
|
} |
||||
|
|
||||
|
// Blank out other inputs completely, not recommended for open transactions
|
||||
|
if (hashType & SIGHASH_ANYONECANPAY) { |
||||
|
txTmp.ins = [txTmp.ins[inIndex]]; |
||||
|
} |
||||
|
|
||||
|
var buffer = txTmp.serialize(); |
||||
|
|
||||
|
buffer = buffer.concat(wordsToBytes([parseInt(hashType)]).reverse()); |
||||
|
|
||||
|
var hash1 = Crypto.SHA256(buffer, {asBytes: true}); |
||||
|
|
||||
|
return Crypto.SHA256(hash1, {asBytes: true}); |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* 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}); |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* 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; |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* 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; |
||||
|
|
||||
|
var allFromMe = true, |
||||
|
allToMe = true, |
||||
|
firstRecvHash = null, |
||||
|
firstMeRecvHash = null, |
||||
|
firstSendHash = null; |
||||
|
|
||||
|
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; |
||||
} |
} |
||||
|
firstRecvHash = hash; |
||||
return analysis; |
} |
||||
}; |
for (var i = this.ins.length-1; i >= 0; i--) { |
||||
|
var txin = this.ins[i]; |
||||
/** |
firstSendHash = txin.script.simpleInPubKeyHash(); |
||||
* Get a human-readable version of the data returned by Transaction#analyze. |
if (!wallet.hasHash(firstSendHash)) { |
||||
* |
allFromMe = false; |
||||
* 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); |
|
||||
|
|
||||
if (!analysis) return ""; |
|
||||
|
|
||||
switch (analysis.type) { |
|
||||
case 'recv': |
|
||||
return "Received with "+analysis.addr; |
|
||||
break; |
|
||||
|
|
||||
case 'sent': |
|
||||
return "Payment to "+analysis.addr; |
|
||||
break; |
|
||||
|
|
||||
case 'self': |
|
||||
return "Payment to yourself"; |
|
||||
break; |
break; |
||||
|
|
||||
case 'other': |
|
||||
default: |
|
||||
return ""; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
/** |
|
||||
* 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; |
} |
||||
}; |
|
||||
|
var impact = this.calcImpact(wallet); |
||||
/** |
|
||||
* Old name for Transaction#getTotalOutValue. |
var analysis = {}; |
||||
* |
|
||||
* @deprecated |
analysis.impact = impact; |
||||
*/ |
|
||||
Transaction.prototype.getTotalValue = Transaction.prototype.getTotalOutValue; |
if (impact.sign > 0 && impact.value.compareTo(BigInteger.ZERO) > 0) { |
||||
|
analysis.type = 'recv'; |
||||
/** |
analysis.addr = new Bitcoin.Address(firstMeRecvHash); |
||||
* Calculates the impact a transaction has on this wallet. |
} else if (allFromMe && allToMe) { |
||||
* |
analysis.type = 'self'; |
||||
* Based on the its public keys, the wallet will calculate the |
} else if (allFromMe) { |
||||
* credit or debit of this transaction. |
analysis.type = 'sent'; |
||||
* |
// TODO: Right now, firstRecvHash is the first output, which - if the
|
||||
* It will return an object with two properties: |
// transaction was not generated by this library could be the
|
||||
* - sign: 1 or -1 depending on sign of the calculated impact. |
// change address.
|
||||
* - value: amount of calculated impact |
analysis.addr = new Bitcoin.Address(firstRecvHash); |
||||
* |
} else { |
||||
* @returns Object Impact on wallet |
analysis.type = "other"; |
||||
*/ |
} |
||||
Transaction.prototype.calcImpact = function (wallet) { |
|
||||
if (!(wallet instanceof Bitcoin.Wallet)) return BigInteger.ZERO; |
return analysis; |
||||
|
}; |
||||
// Calculate credit to us from all outputs
|
|
||||
var valueOut = BigInteger.ZERO; |
/** |
||||
for (var j = 0; j < this.outs.length; j++) { |
* Get a human-readable version of the data returned by Transaction#analyze. |
||||
var txout = this.outs[j]; |
* |
||||
var hash = Crypto.util.bytesToBase64(txout.script.simpleOutPubKeyHash()); |
* This is merely a convenience function. Clients should consider implementing |
||||
if (wallet.hasHash(hash)) { |
* this themselves based on their UI, I18N, etc. |
||||
valueOut = valueOut.add(Bitcoin.Util.valueToBigInt(txout.value)); |
*/ |
||||
} |
Transaction.prototype.getDescription = function (wallet) { |
||||
|
var analysis = this.analyze(wallet); |
||||
|
|
||||
|
if (!analysis) return ""; |
||||
|
|
||||
|
switch (analysis.type) { |
||||
|
case 'recv': |
||||
|
return "Received with "+analysis.addr; |
||||
|
break; |
||||
|
|
||||
|
case 'sent': |
||||
|
return "Payment to "+analysis.addr; |
||||
|
break; |
||||
|
|
||||
|
case 'self': |
||||
|
return "Payment to yourself"; |
||||
|
break; |
||||
|
|
||||
|
case 'other': |
||||
|
default: |
||||
|
return ""; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* 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; |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* Old name for Transaction#getTotalOutValue. |
||||
|
* |
||||
|
* @deprecated |
||||
|
*/ |
||||
|
Transaction.prototype.getTotalValue = Transaction.prototype.getTotalOutValue; |
||||
|
|
||||
|
/** |
||||
|
* 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; |
||||
|
|
||||
|
// 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)); |
||||
} |
} |
||||
|
} |
||||
// Calculate debit to us from all ins
|
|
||||
var valueIn = BigInteger.ZERO; |
// Calculate debit to us from all ins
|
||||
for (var j = 0; j < this.ins.length; j++) { |
var valueIn = BigInteger.ZERO; |
||||
var txin = this.ins[j]; |
for (var j = 0; j < this.ins.length; j++) { |
||||
var hash = Crypto.util.bytesToBase64(txin.script.simpleInPubKeyHash()); |
var txin = this.ins[j]; |
||||
if (wallet.hasHash(hash)) { |
var hash = Crypto.util.bytesToBase64(txin.script.simpleInPubKeyHash()); |
||||
var fromTx = wallet.txIndex[txin.outpoint.hash]; |
if (wallet.hasHash(hash)) { |
||||
if (fromTx) { |
var fromTx = wallet.txIndex[txin.outpoint.hash]; |
||||
valueIn = valueIn.add(Bitcoin.Util.valueToBigInt(fromTx.outs[txin.outpoint.index].value)); |
if (fromTx) { |
||||
} |
valueIn = valueIn.add(Bitcoin.Util.valueToBigInt(fromTx.outs[txin.outpoint.index].value)); |
||||
} |
} |
||||
} |
} |
||||
if (valueOut.compareTo(valueIn) >= 0) { |
} |
||||
return { |
if (valueOut.compareTo(valueIn) >= 0) { |
||||
sign: 1, |
return { |
||||
value: valueOut.subtract(valueIn) |
sign: 1, |
||||
}; |
value: valueOut.subtract(valueIn) |
||||
} else { |
}; |
||||
return { |
} else { |
||||
sign: -1, |
return { |
||||
value: valueIn.subtract(valueOut) |
sign: -1, |
||||
}; |
value: valueIn.subtract(valueOut) |
||||
} |
}; |
||||
}; |
} |
||||
|
}; |
||||
var TransactionIn = Bitcoin.TransactionIn = function (data) |
|
||||
{ |
var TransactionIn = function (data) |
||||
this.outpoint = data.outpoint; |
{ |
||||
if (data.script instanceof Script) { |
this.outpoint = data.outpoint; |
||||
this.script = data.script; |
if (data.script instanceof Script) { |
||||
} else { |
this.script = data.script; |
||||
this.script = new Script(data.script); |
} else { |
||||
} |
//this.script = new Script(data.script);
|
||||
this.sequence = data.sequence; |
this.script = Script.fromScriptSig(data.scriptSig); |
||||
}; |
} |
||||
|
this.sequence = data.sequence; |
||||
TransactionIn.prototype.clone = function () |
}; |
||||
{ |
|
||||
var newTxin = new TransactionIn({ |
TransactionIn.prototype.clone = function () |
||||
outpoint: { |
{ |
||||
hash: this.outpoint.hash, |
var newTxin = new TransactionIn({ |
||||
index: this.outpoint.index |
outpoint: { |
||||
}, |
hash: this.outpoint.hash, |
||||
script: this.script.clone(), |
index: this.outpoint.index |
||||
sequence: this.sequence |
}, |
||||
}); |
script: this.script.clone(), |
||||
return newTxin; |
sequence: this.sequence |
||||
}; |
}); |
||||
|
return newTxin; |
||||
var TransactionOut = Bitcoin.TransactionOut = function (data) |
}; |
||||
{ |
|
||||
if (data.script instanceof Script) { |
var TransactionOut = function (data) |
||||
this.script = data.script; |
{ |
||||
} else { |
if (data.script instanceof Script) { |
||||
this.script = new Script(data.script); |
this.script = data.script; |
||||
} |
} else { |
||||
|
//this.script = new Script(data.script);
|
||||
if (Bitcoin.Util.isArray(data.value)) { |
this.script = Script.fromPubKey(data.scriptPubKey); |
||||
this.value = data.value; |
} |
||||
} else if ("string" == typeof data.value) { |
|
||||
var valueHex = (new BigInteger(data.value, 10)).toString(16); |
if (util.isArray(data.value)) { |
||||
while (valueHex.length < 16) valueHex = "0" + valueHex; |
this.value = data.value; |
||||
this.value = Crypto.util.hexToBytes(valueHex); |
} else if ("string" == typeof data.value) { |
||||
} |
var valueHex = (new BigInteger(data.value, 10)).toString(16); |
||||
}; |
while (valueHex.length < 16) valueHex = "0" + valueHex; |
||||
|
this.value = conv.hexToBytes(valueHex); |
||||
TransactionOut.prototype.clone = function () |
} |
||||
{ |
}; |
||||
var newTxout = new TransactionOut({ |
|
||||
script: this.script.clone(), |
TransactionOut.prototype.clone = function () |
||||
value: this.value.slice(0) |
{ |
||||
}); |
var newTxout = new TransactionOut({ |
||||
return newTxout; |
script: this.script.clone(), |
||||
}; |
value: this.value.slice(0) |
||||
})(); |
}); |
||||
|
return newTxout; |
||||
|
}; |
||||
|
|
||||
|
module.exports.Transaction = Transaction; |
||||
|
module.exports.TransactionIn = TransactionIn; |
||||
|
module.exports.TransactionOut = TransactionOut; |
||||
|
|
||||
|
@ -1,308 +1,323 @@ |
|||||
Bitcoin.Wallet = (function () { |
var Script = require('./script'); |
||||
var Script = Bitcoin.Script, |
var ECKey = require('./eckey'); |
||||
TransactionIn = Bitcoin.TransactionIn, |
var conv = require('./convert'); |
||||
TransactionOut = Bitcoin.TransactionOut; |
var util = require('./util'); |
||||
|
|
||||
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 = []; |
|
||||
|
|
||||
// Public hashes of our keys
|
|
||||
this.addressHashes = []; |
|
||||
|
|
||||
// Transaction data
|
|
||||
this.txIndex = {}; |
|
||||
this.unspentOuts = []; |
|
||||
|
|
||||
// Other fields
|
|
||||
this.addressPointer = 0; |
|
||||
|
|
||||
/** |
|
||||
* 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); |
|
||||
|
|
||||
if (pub) { |
|
||||
if ("string" === typeof pub) { |
|
||||
pub = Crypto.util.base64ToBytes(pub); |
|
||||
} |
|
||||
key.setPub(pub); |
|
||||
} |
|
||||
|
|
||||
this.addressHashes.push(key.getBitcoinAddress().getHashBase64()); |
|
||||
}; |
|
||||
|
|
||||
/** |
var BigInteger = require('./jsbn/jsbn'); |
||||
* 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]); |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
/** |
|
||||
* Get the key chain. |
|
||||
* |
|
||||
* Returns an array of base64-encoded private values. |
|
||||
*/ |
|
||||
this.getKeys = function () { |
|
||||
var serializedWallet = []; |
|
||||
|
|
||||
for (var i = 0; i < keys.length; i++) { |
|
||||
serializedWallet.push(keys[i].toString('base64')); |
|
||||
} |
|
||||
|
|
||||
return serializedWallet; |
var Transaction = require('./transaction').Transaction; |
||||
}; |
var TransactionIn = require('./transaction').TransactionIn; |
||||
|
var TransactionOut = require('./transaction').TransactionOut; |
||||
|
|
||||
/** |
var Wallet = function () { |
||||
* Get the public keys. |
// Keychain
|
||||
* |
//
|
||||
* Returns an array of base64-encoded public keys. |
// The keychain is stored as a var in this closure to make accidental
|
||||
*/ |
// serialization less likely.
|
||||
this.getPubKeys = function () { |
//
|
||||
var pubs = []; |
// Any functions accessing this value therefore have to be defined in
|
||||
|
// the closure of this constructor.
|
||||
|
var keys = []; |
||||
|
|
||||
for (var i = 0; i < keys.length; i++) { |
// Public hashes of our keys
|
||||
pubs.push(Crypto.util.bytesToBase64(keys[i].getPub())); |
this.addressHashes = []; |
||||
} |
|
||||
|
|
||||
return pubs; |
// Transaction data
|
||||
}; |
this.txIndex = {}; |
||||
|
this.unspentOuts = []; |
||||
/** |
|
||||
* Delete all keys. |
|
||||
*/ |
|
||||
this.clear = function () { |
|
||||
keys = []; |
|
||||
}; |
|
||||
|
|
||||
/** |
|
||||
* Return the number of keys in this wallet. |
|
||||
*/ |
|
||||
this.getLength = function () { |
|
||||
return keys.length; |
|
||||
}; |
|
||||
|
|
||||
/** |
|
||||
* 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.getCurAddress = function () { |
|
||||
if (keys[this.addressPointer]) { |
|
||||
return keys[this.addressPointer].getBitcoinAddress(); |
|
||||
} else { |
|
||||
return null; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
/** |
|
||||
* 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(); |
|
||||
} |
|
||||
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); |
|
||||
} |
|
||||
} |
|
||||
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(); |
|
||||
} |
|
||||
} |
|
||||
throw new Error("Hash unknown"); |
|
||||
}; |
|
||||
}; |
|
||||
|
|
||||
Wallet.prototype.generateAddress = function () { |
// Other fields
|
||||
this.addKey(new Bitcoin.ECKey()); |
this.addressPointer = 0; |
||||
}; |
|
||||
|
|
||||
/** |
/** |
||||
* Add a transaction to the wallet's processed transaction. |
* Add a key to the keychain. |
||||
* |
* |
||||
* This will add a transaction to the wallet, updating its balance and |
* The corresponding public key can be provided as a second parameter. This |
||||
* available unspent outputs. |
* adds it to the cache in the ECKey object and avoid the need to |
||||
|
* expensively calculate it later. |
||||
*/ |
*/ |
||||
Wallet.prototype.process = function (tx) { |
this.addKey = function (key, pub) { |
||||
if (this.txIndex[tx.hash]) return; |
if (!(key instanceof ECKey)) { |
||||
|
key = new ECKey(key); |
||||
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; |
|
||||
} |
|
||||
} |
|
||||
} |
} |
||||
|
keys.push(key); |
||||
|
|
||||
// Remove spent outputs
|
if (pub) { |
||||
for (j = 0; j < tx.ins.length; j++) { |
if ("string" === typeof pub) { |
||||
var txin = new TransactionIn(tx.ins[j]); |
pub = Crypto.util.base64ToBytes(pub); |
||||
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; |
|
||||
} |
|
||||
} |
} |
||||
|
key.setPub(pub); |
||||
} |
} |
||||
|
|
||||
// Index transaction
|
this.addressHashes.push(key.getBitcoinAddress().getHashBase64()); |
||||
this.txIndex[tx.hash] = tx; |
|
||||
}; |
}; |
||||
|
|
||||
Wallet.prototype.getBalance = function () { |
/** |
||||
var balance = BigInteger.valueOf(0); |
* Add multiple keys at once. |
||||
for (var i = 0; i < this.unspentOuts.length; i++) { |
*/ |
||||
var txout = this.unspentOuts[i].out; |
this.addKeys = function (keys, pubs) { |
||||
balance = balance.add(Bitcoin.Util.valueToBigInt(txout.value)); |
if ("string" === typeof keys) { |
||||
|
keys = keys.split(','); |
||||
} |
} |
||||
return balance; |
if ("string" === typeof pubs) { |
||||
}; |
pubs = pubs.split(','); |
||||
|
|
||||
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)); |
|
||||
|
|
||||
if (availableValue.compareTo(txValue) >= 0) break; |
|
||||
} |
} |
||||
|
var i; |
||||
if (availableValue.compareTo(txValue) < 0) { |
if (Array.isArray(pubs) && keys.length == pubs.length) { |
||||
throw new Error('Insufficient funds.'); |
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]); |
||||
|
} |
||||
} |
} |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* Get the key chain. |
||||
|
* |
||||
|
* Returns an array of base64-encoded private values. |
||||
|
*/ |
||||
|
this.getKeys = function () { |
||||
|
var serializedWallet = []; |
||||
|
|
||||
var changeValue = availableValue.subtract(txValue); |
for (var i = 0; i < keys.length; i++) { |
||||
|
serializedWallet.push(keys[i].toString('base64')); |
||||
|
} |
||||
|
|
||||
var sendTx = new Bitcoin.Transaction(); |
return serializedWallet; |
||||
|
}; |
||||
|
|
||||
for (i = 0; i < selectedOuts.length; i++) { |
/** |
||||
sendTx.addInput(selectedOuts[i].tx, selectedOuts[i].index); |
* Get the public keys. |
||||
} |
* |
||||
|
* Returns an array of base64-encoded public keys. |
||||
|
*/ |
||||
|
this.getPubKeys = function () { |
||||
|
var pubs = []; |
||||
|
|
||||
sendTx.addOutput(address, sendValue); |
for (var i = 0; i < keys.length; i++) { |
||||
if (changeValue.compareTo(BigInteger.ZERO) > 0) { |
pubs.push(Crypto.util.bytesToBase64(keys[i].getPub())); |
||||
sendTx.addOutput(this.getNextAddress(), changeValue); |
|
||||
} |
} |
||||
|
|
||||
var hashType = 1; // SIGHASH_ALL
|
return pubs; |
||||
|
}; |
||||
|
|
||||
for (i = 0; i < sendTx.ins.length; i++) { |
/** |
||||
var hash = sendTx.hashTransactionForSignature(selectedOuts[i].out.script, i, hashType); |
* Delete all keys. |
||||
var pubKeyHash = selectedOuts[i].out.script.simpleOutPubKeyHash(); |
*/ |
||||
var signature = this.signWithKey(pubKeyHash, hash); |
this.clear = function () { |
||||
|
keys = []; |
||||
|
}; |
||||
|
|
||||
// Append hash type
|
/** |
||||
signature.push(parseInt(hashType, 10)); |
* Return the number of keys in this wallet. |
||||
|
*/ |
||||
|
this.getLength = function () { |
||||
|
return keys.length; |
||||
|
}; |
||||
|
|
||||
sendTx.ins[i].script = Script.createInputScript(signature, this.getPubKeyFromHash(pubKeyHash)); |
/** |
||||
|
* 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; |
||||
return sendTx; |
|
||||
}; |
}; |
||||
|
|
||||
Wallet.prototype.clearTransactions = function () { |
this.getCurAddress = function () { |
||||
this.txIndex = {}; |
if (keys[this.addressPointer]) { |
||||
this.unspentOuts = []; |
return keys[this.addressPointer].getBitcoinAddress(); |
||||
|
} else { |
||||
|
return null; |
||||
|
} |
||||
}; |
}; |
||||
|
|
||||
/** |
/** |
||||
* Check to see if a pubKeyHash belongs to this wallet. |
* Go to the next address. |
||||
|
* |
||||
|
* If there are no more new addresses available, one will be generated |
||||
|
* automatically. |
||||
*/ |
*/ |
||||
Wallet.prototype.hasHash = function (hash) { |
this.getNextAddress = function () { |
||||
if (Bitcoin.Util.isArray(hash)) hash = Crypto.util.bytesToBase64(hash); |
if (keys.length === 0) { |
||||
|
this.generateAddress(); |
||||
|
} |
||||
|
else { |
||||
|
} |
||||
|
|
||||
// TODO: Just create an object with base64 hashes as keys for faster lookup
|
/* |
||||
for (var k = 0; k < this.addressHashes.length; k++) { |
this.addressPointer++; |
||||
if (this.addressHashes[k] === hash) return true; |
if (!keys[this.addressPointer]) { |
||||
|
this.generateAddress(); |
||||
} |
} |
||||
return false; |
*/ |
||||
|
// TODO(shtylman) this shit is trying to be too smart
|
||||
|
// it is making a new address when it shouldn't
|
||||
|
// it should just stop being so "smart" and just do what it is told
|
||||
|
return keys[this.addressPointer].getBitcoinAddress(); |
||||
}; |
}; |
||||
|
|
||||
return Wallet; |
/** |
||||
})(); |
* 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 = conv.bytesToBase64(pubKeyHash); |
||||
|
for (var i = 0; i < this.addressHashes.length; i++) { |
||||
|
if (this.addressHashes[i] == pubKeyHash) { |
||||
|
return keys[i].sign(hash); |
||||
|
} |
||||
|
} |
||||
|
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 = conv.bytesToBase64(pubKeyHash); |
||||
|
for (var i = 0; i < this.addressHashes.length; i++) { |
||||
|
if (this.addressHashes[i] == pubKeyHash) { |
||||
|
return keys[i].getPub(); |
||||
|
} |
||||
|
} |
||||
|
throw new Error("Hash unknown"); |
||||
|
}; |
||||
|
}; |
||||
|
|
||||
|
Wallet.prototype.generateAddress = function () { |
||||
|
this.addKey(new ECKey()); |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* 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 j; |
||||
|
var k; |
||||
|
var hash; |
||||
|
// Gather outputs
|
||||
|
for (j = 0; j < tx.out.length; j++) { |
||||
|
var txout = new TransactionOut(tx.out[j]); |
||||
|
hash = conv.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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Remove spent outputs
|
||||
|
for (j = 0; j < tx.in.length; j++) { |
||||
|
var txin = new TransactionIn(tx.in[j]); |
||||
|
var pubkey = txin.script.simpleInPubKey(); |
||||
|
hash = conv.bytesToBase64(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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Index transaction
|
||||
|
this.txIndex[tx.hash] = tx; |
||||
|
}; |
||||
|
|
||||
|
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(util.valueToBigInt(txout.value)); |
||||
|
} |
||||
|
return balance; |
||||
|
}; |
||||
|
|
||||
|
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++) { |
||||
|
var txout = this.unspentOuts[i]; |
||||
|
selectedOuts.push(txout); |
||||
|
availableValue = availableValue.add(util.valueToBigInt(txout.out.value)); |
||||
|
|
||||
|
if (availableValue.compareTo(txValue) >= 0) break; |
||||
|
} |
||||
|
|
||||
|
if (availableValue.compareTo(txValue) < 0) { |
||||
|
throw new Error('Insufficient funds.'); |
||||
|
} |
||||
|
|
||||
|
var changeValue = availableValue.subtract(txValue); |
||||
|
|
||||
|
var sendTx = new Transaction(); |
||||
|
|
||||
|
for (i = 0; i < selectedOuts.length; i++) { |
||||
|
sendTx.addInput(selectedOuts[i].tx, selectedOuts[i].index); |
||||
|
} |
||||
|
|
||||
|
sendTx.addOutput(address, sendValue); |
||||
|
if (changeValue.compareTo(BigInteger.ZERO) > 0) { |
||||
|
sendTx.addOutput(this.getNextAddress(), changeValue); |
||||
|
} |
||||
|
|
||||
|
var hashType = 1; // SIGHASH_ALL
|
||||
|
|
||||
|
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); |
||||
|
|
||||
|
// Append hash type
|
||||
|
signature.push(parseInt(hashType, 10)); |
||||
|
|
||||
|
sendTx.ins[i].script = Script.createInputScript(signature, this.getPubKeyFromHash(pubKeyHash)); |
||||
|
} |
||||
|
|
||||
|
return sendTx; |
||||
|
}; |
||||
|
|
||||
|
Wallet.prototype.clearTransactions = function () { |
||||
|
this.txIndex = {}; |
||||
|
this.unspentOuts = []; |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* 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); |
||||
|
|
||||
|
// 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; |
||||
|
} |
||||
|
return false; |
||||
|
}; |
||||
|
|
||||
|
module.exports = Wallet; |
||||
|
@ -0,0 +1,46 @@ |
|||||
|
var assert = require('assert'); |
||||
|
var Address = require('../').Address; |
||||
|
|
||||
|
test('string', function() { |
||||
|
var addr = '18fN1QTGWmHWCA9r2dyDH6FbMEyc7XHmQQ'; |
||||
|
assert.equal((new Address(addr)).toString(), addr); |
||||
|
}); |
||||
|
|
||||
|
test('valid', function() { |
||||
|
function validate(addr, type) { |
||||
|
assert.ok(Address.validate(addr, type)); |
||||
|
}; |
||||
|
|
||||
|
validate('1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa'); |
||||
|
validate('1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa', 'prod'); |
||||
|
validate('mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhef'); |
||||
|
validate('mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhef', 'testnet'); |
||||
|
|
||||
|
validate('12KYrjTdVGjFMtaxERSk3gphreJ5US8aUP'); |
||||
|
validate('12QeMLzSrB8XH8FvEzPMVoRxVAzTr5XM2y'); |
||||
|
validate('1oNLrsHnBcR6dpaBpwz3LSwutbUNkNSjs'); |
||||
|
validate('1SQHtwR5oJRKLfiWQ2APsAd9miUc4k2ez'); |
||||
|
validate('116CGDLddrZhMrTwhCVJXtXQpxygTT1kHd'); |
||||
|
|
||||
|
// p2sh addresses
|
||||
|
validate('3NJZLcZEEYBpxYEUGewU4knsQRn1WM5Fkt'); |
||||
|
validate('3NJZLcZEEYBpxYEUGewU4knsQRn1WM5Fkt', 'prod'); |
||||
|
validate('2MxKEf2su6FGAUfCEAHreGFQvEYrfYNHvL7'); |
||||
|
validate('2MxKEf2su6FGAUfCEAHreGFQvEYrfYNHvL7', 'testnet'); |
||||
|
}); |
||||
|
|
||||
|
|
||||
|
test('invalid', function() { |
||||
|
function invalid(addr, type) { |
||||
|
assert.ok(!Address.validate(addr, type)); |
||||
|
}; |
||||
|
|
||||
|
invalid(''); |
||||
|
invalid('mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhe'); |
||||
|
invalid('1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa', 'testnet'); |
||||
|
invalid('mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhef', 'prod'); |
||||
|
|
||||
|
// invalid base58 string
|
||||
|
invalid('%%@'); |
||||
|
}); |
||||
|
|
@ -0,0 +1,15 @@ |
|||||
|
var assert = require('assert'); |
||||
|
var base58 = require('../').base58; |
||||
|
var conv = require('../').convert; |
||||
|
|
||||
|
test('decode base58', function() { |
||||
|
var enc = '5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'; |
||||
|
var hex = '800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d507a5b8d'; |
||||
|
assert.deepEqual(base58.decode(enc), conv.hexToBytes(hex)); |
||||
|
}); |
||||
|
|
||||
|
test('encode base58', function() { |
||||
|
var enc = '5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'; |
||||
|
var hex = '800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d507a5b8d'; |
||||
|
assert.equal(base58.encode(conv.hexToBytes(hex)), enc); |
||||
|
}); |
@ -0,0 +1,24 @@ |
|||||
|
var assert = require('assert'); |
||||
|
var conv = require('../').convert; |
||||
|
|
||||
|
var bytesToHex = conv.bytesToHex; |
||||
|
var hexToBytes = conv.hexToBytes; |
||||
|
|
||||
|
test('bytesToHex', function() { |
||||
|
assert.equal(bytesToHex([0, 1, 2, 255]), '000102ff'); |
||||
|
}); |
||||
|
|
||||
|
test('hexToBytes', function() { |
||||
|
assert.deepEqual(hexToBytes('000102ff'), [0, 1, 2, 255]); |
||||
|
}); |
||||
|
|
||||
|
test('bytesToHex - hexToBytes', function() { |
||||
|
var bytes = []; |
||||
|
for (var i=0 ; i<256 ; ++i) { |
||||
|
bytes.push(i); |
||||
|
} |
||||
|
|
||||
|
var hex = bytesToHex(bytes); |
||||
|
assert.equal(hex.length, 512); |
||||
|
assert.deepEqual(hexToBytes(hex), bytes); |
||||
|
}); |
@ -0,0 +1,20 @@ |
|||||
|
var assert = require('assert'); |
||||
|
var sec = require('../src/jsbn/sec'); |
||||
|
var ecdsa = require('../').ecdsa; |
||||
|
|
||||
|
var ecparams = sec('secp256k1'); |
||||
|
|
||||
|
test("Point multiplication", function () { |
||||
|
var G = ecparams.getG(); |
||||
|
var n = ecparams.getN(); |
||||
|
|
||||
|
assert.ok(G.multiply(n).isInfinity(), "Gn is infinite"); |
||||
|
|
||||
|
var k = ecdsa.getBigRandom(n); |
||||
|
var P = G.multiply(k); |
||||
|
assert.ok(!P.isInfinity(), "kG is not infinite"); |
||||
|
assert.ok(P.isOnCurve(), "kG on curve"); |
||||
|
assert.ok(P.multiply(n).isInfinity(), "kGn is infinite"); |
||||
|
|
||||
|
assert.ok(P.validate(), "kG validates as a public key"); |
||||
|
}); |
@ -1,36 +0,0 @@ |
|||||
<!DOCTYPE html> |
|
||||
<html> |
|
||||
<head> |
|
||||
<title>BitcoinJS-lib Test Suite</title> |
|
||||
<link rel="stylesheet" href="../vendor/qunit/qunit.css" type="text/css" media="screen"> |
|
||||
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script> |
|
||||
<script type="text/javascript" src="../vendor/qunit/qunit.js"></script> |
|
||||
<script type="text/javascript" src="../src/crypto-js/crypto.js"></script> |
|
||||
<script type="text/javascript" src="../src/crypto-js/sha256.js"></script> |
|
||||
<script type="text/javascript" src="../src/jsbn/prng4.js"></script> |
|
||||
<script type="text/javascript" src="../src/jsbn/rng.js"></script> |
|
||||
<script type="text/javascript" src="../src/jsbn/jsbn.js"></script> |
|
||||
<script type="text/javascript" src="../src/jsbn/jsbn2.js"></script> |
|
||||
|
|
||||
<script type="text/javascript" src="../src/jsbn/ec.js"></script> |
|
||||
<script type="text/javascript" src="../src/jsbn/sec.js"></script> |
|
||||
<script type="text/javascript" src="../src/events/eventemitter.js"></script> |
|
||||
<script type="text/javascript" src="../src/bitcoin.js"></script> |
|
||||
<script type="text/javascript" src="../src/util.js"></script> |
|
||||
<script type="text/javascript" src="../src/base58.js"></script> |
|
||||
|
|
||||
<script type="text/javascript" src="../src/address.js"></script> |
|
||||
<script type="text/javascript" src="../src/ecdsa.js"></script> |
|
||||
<script type="text/javascript" src="../src/eckey.js"></script> |
|
||||
<script type="text/javascript" src="../src/paillier.js"></script> |
|
||||
<script type="text/javascript" src="test.js"></script> |
|
||||
</head> |
|
||||
<body> |
|
||||
<h1 id="qunit-header">BitcoinJS-lib Test Suite</h1> |
|
||||
<h2 id="qunit-banner"></h2> |
|
||||
<div id="qunit-testrunner-toolbar"></div> |
|
||||
<h2 id="qunit-userAgent"></h2> |
|
||||
<ol id="qunit-tests"></ol> |
|
||||
<div id="qunit-fixture"></div> |
|
||||
</body> |
|
||||
</html> |
|
@ -0,0 +1,24 @@ |
|||||
|
var assert = require('assert'); |
||||
|
var BigInteger = require('../').BigInteger; |
||||
|
var bytesToHex = require('../').convert.bytesToHex; |
||||
|
|
||||
|
test('toByteArraySigned', function() { |
||||
|
function hex(num) { |
||||
|
var bytes = BigInteger.valueOf(num).toByteArraySigned(); |
||||
|
var hex = bytesToHex(bytes); |
||||
|
return '0x' + hex; |
||||
|
} |
||||
|
|
||||
|
assert.equal(hex( 0), '0x'); |
||||
|
assert.equal(hex( 1), '0x01'); |
||||
|
assert.equal(hex(-1), '0x81'); |
||||
|
assert.equal(hex( 127), '0x7f'); |
||||
|
assert.equal(hex(-127), '0xff'); |
||||
|
assert.equal(hex( 255), '0x00ff'); |
||||
|
assert.equal(hex(-255), '0x80ff'); |
||||
|
assert.equal(hex( 16300), '0x3fac'); |
||||
|
assert.equal(hex(-16300), '0xbfac'); |
||||
|
assert.equal(hex( 62300), '0x00f35c'); |
||||
|
assert.equal(hex(-62300), '0x80f35c'); |
||||
|
}); |
||||
|
|
@ -0,0 +1,35 @@ |
|||||
|
var assert = require('assert'); |
||||
|
var Key = require('../').Key; |
||||
|
var bytesToHex = require('../').convert.bytesToHex; |
||||
|
var hexToBytes = require('../').convert.hexToBytes; |
||||
|
var base58 = require('../').base58; |
||||
|
|
||||
|
// get public key from private key
|
||||
|
test('from private base58', function() { |
||||
|
|
||||
|
var priv = '18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725'; |
||||
|
var pub = '0450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b23522cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6'; |
||||
|
var key = Key(hexToBytes(priv)); |
||||
|
|
||||
|
assert.equal(bytesToHex(key.getPub()), pub); |
||||
|
assert.equal(key.compressed, false); |
||||
|
|
||||
|
var priv = '5HwoXVkHoRM8sL2KmNRS217n1g8mPPBomrY7yehCuXC1115WWsh'; |
||||
|
var pub = '044f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa385b6b1b8ead809ca67454d9683fcf2ba03456d6fe2c4abe2b07f0fbdbb2f1c1'; |
||||
|
var addr = '1MsHWS1BnwMc3tLE8G35UXsS58fKipzB7a'; |
||||
|
var key = Key(priv); |
||||
|
|
||||
|
assert.equal(key.compressed, false); |
||||
|
assert.equal(bytesToHex(key.getPub()), pub); |
||||
|
assert.equal(key.getBitcoinAddress().toString(), addr); |
||||
|
|
||||
|
var priv = 'KwntMbt59tTsj8xqpqYqRRWufyjGunvhSyeMo3NTYpFYzZbXJ5Hp'; |
||||
|
var pub = '034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa' |
||||
|
var addr = '1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9'; |
||||
|
var key = Key(priv); |
||||
|
|
||||
|
assert.equal(key.compressed, true); |
||||
|
assert.equal(bytesToHex(key.getPub()), pub); |
||||
|
assert.equal(key.getBitcoinAddress().toString(), addr); |
||||
|
}); |
||||
|
|
@ -0,0 +1 @@ |
|||||
|
--ui qunit |
Loading…
Reference in new issue