diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..ed05f88 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,6 @@ +language: node_js +node_js: + - "0.11" + - "0.10" + - "0.8" + - "0.6" diff --git a/Jakefile.js b/Jakefile.js deleted file mode 100644 index e7b974a..0000000 --- a/Jakefile.js +++ /dev/null @@ -1,53 +0,0 @@ -var pkginfo = require('pkginfo')(module); -var minify = require('jake-uglify').minify; - -var headerJS = "\ -/**\n\ - * BitcoinJS-lib v"+exports.version+"-default\n\ - * Copyright (c) 2011 BitcoinJS Project\n\ - * \n\ - * This program is free software; you can redistribute it and/or modify\n\ - * it under the terms of the MIT license.\n\ - */"; - -task({'default': [ - 'build/bitcoinjs-min.js', - 'build/bitcoinjs-exit-min.js' -]}); - -desc('General-purpose build containing most features'); -minify({'build/bitcoinjs-min.js': [ - 'src/crypto-js/crypto.js', - 'src/crypto-js/sha256.js', - 'src/crypto-js/ripemd160.js', - 'src/jsbn/prng4.js', - 'src/jsbn/rng.js', - 'src/jsbn/jsbn.js', - 'src/jsbn/jsbn2.js', - - 'src/jsbn/ec.js', - 'src/jsbn/sec.js', - 'src/events/eventemitter.js', - 'src/bitcoin.js', - 'src/util.js', - 'src/base58.js', - - 'src/address.js', - 'src/ecdsa.js', - 'src/eckey.js', - 'src/opcode.js', - 'src/script.js', - 'src/transaction.js', - - 'src/wallet.js', - 'src/txdb.js' -]}, { - header: headerJS -}); - -desc('Exit node client implementation'); -minify({'build/bitcoinjs-exit-min.js': [ - 'src/exit/client.js' -]}, { - header: headerJS -}); diff --git a/README.md b/README.md index 9eac4f1..bdf65ca 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,14 @@ system in place. Prototype software, use at your own peril. +# How to use + +* Run `npm run-script compile` to compile to a browser-friendly minified +file. Once in the browser, the global Bitcoin object will contain everything +you need. +* To use in NodeJS, install this package as you would any other and +put in `var Bitcoin = require('bitcoinjs-lib')`. + # License This library is free and open-source software released under the MIT diff --git a/bitcoinjs-min.js b/bitcoinjs-min.js new file mode 100644 index 0000000..42c6739 --- /dev/null +++ b/bitcoinjs-min.js @@ -0,0 +1,3 @@ +!function(e){if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.Bitcoin=e()}}(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o=0){var mod=bi.mod(base);chars.push(alphabet[mod.intValue()]);bi=bi.subtract(mod).divide(base)}chars.push(alphabet[bi.intValue()]);for(var i=0;i0){bytes.unshift(0)}return bytes};module.exports.decodeHex=function(input){return module.exports.decode(conv.hexToBytes(input))};module.exports.checkEncode=function(input,vbyte){vbyte=vbyte||0;var front=[vbyte].concat(input);var checksum=Crypto.SHA256(Crypto.SHA256(front,{asBytes:true}),{asBytes:true}).slice(0,4);return module.exports.encode(front.concat(checksum))};module.exports.checkEncodeHex=function(input,vbyte){return conv.bytesToHex(module.exports.encode(input))};module.exports.checkDecode=function(input){var bytes=module.exports.decode(input),front=bytes.slice(0,bytes.length-4),back=bytes.slice(bytes.length-4);var checksum=Crypto.SHA256(Crypto.SHA256(front,{asBytes:true}),{asBytes:true}).slice(0,4);if(""+checksum!=""+back){throw new Error("Checksum failed")}var o=front.slice(1);o.version=front[0];return o};module.exports.checkDecodeHex=function(input){return module.exports.checkDecode(conv.hexToBytes(input))}},{"./convert":4,"./crypto-js/crypto":5,"./jsbn/jsbn":14}],3:[function(require,module,exports){var Script=require("./script"),util=require("./util"),conv=require("./convert"),ECKey=require("./eckey").ECKey,ECPubKey=require("./eckey").ECPubKey,base58=require("./base58"),Crypto=require("./crypto-js/crypto"),ECPointFp=require("./jsbn/ec").ECPointFp,sec=require("./jsbn/sec"),ecparams=sec("secp256k1");var BIP32key=function(opts){if(!opts)opts={};if(typeof opts=="string"){try{opts=BIP32key.prototype.deserialize(opts)}catch(e){opts=BIP32key.prototype.fromMasterKey(opts)}}this.vbytes=opts.vbytes;this.depth=opts.depth;this.fingerprint=opts.fingerprint;this.i=opts.i;this.chaincode=opts.chaincode;this.key=opts.key;this.type=conv.bytesToString(this.vbytes)==PRIVDERIV?"priv":"pub";return this};var PRIVDERIV=BIP32key.PRIVDERIV="ˆ­ä";var PUBDERIV=BIP32key.PUBDERIV="ˆ²";BIP32key.prototype.deserialize=function(str){var bytes=base58.decode(str);var front=bytes.slice(0,bytes.length-4),back=bytes.slice(bytes.length-4);var checksum=Crypto.SHA256(Crypto.SHA256(front,{asBytes:true}),{asBytes:true}).slice(0,4);if(""+checksum!=""+back){throw new Error("Checksum failed")}var type=conv.bytesToString(bytes.slice(0,4))==PRIVDERIV?"priv":"pub";return new BIP32key({type:type,vbytes:bytes.slice(0,4),depth:bytes[4],fingerprint:bytes.slice(5,9),i:util.bytesToNum(bytes.slice(9,13).reverse()),chaincode:bytes.slice(13,45),key:type=="priv"?new ECKey(bytes.slice(46,78).concat([1]),true):new ECPubKey(bytes.slice(45,78),true)})};BIP32key.prototype.serialize=function(){var bytes=this.vbytes.concat([this.depth],this.fingerprint,util.numToBytes(this.i,4).reverse(),this.chaincode,this.type=="priv"?[0].concat(this.key.export("bytes").slice(0,32)):this.key.export("bytes"));var checksum=Crypto.SHA256(Crypto.SHA256(bytes,{asBytes:true}),{asBytes:true}).slice(0,4);return base58.encode(bytes.concat(checksum))};BIP32key.prototype.ckd=function(i){var priv,pub,newkey,fingerprint,blob,I;if(this.type=="priv"){priv=this.key.export("bytes");pub=this.key.getPub().export("bytes")}else pub=this.key.export("bytes");if(i>=2147483648){if(!priv)throw new Error("Can't do private derivation on public key!");blob=[0].concat(priv.slice(0,32),util.numToBytes(i,4).reverse())}else blob=pub.concat(util.numToBytes(i,4).reverse());I=Crypto.HMAC(Crypto.SHA512,blob,this.chaincode,{asBytes:true});if(this.type=="priv"){newkey=this.key.add(ECKey(I.slice(0,32).concat([1])));fingerprint=util.sha256ripe160(this.key.getPub().export("bytes")).slice(0,4)}else{newkey=this.key.add(ECKey(I.slice(0,32).concat([1])).getPub());fingerprint=util.sha256ripe160(this.key.export("bytes")).slice(0,4)}return new BIP32key({vbytes:this.vbytes,type:this.type,depth:this.depth+1,fingerprint:fingerprint,i:i,chaincode:I.slice(32),key:newkey})};BIP32key.prototype.clone=function(){return new BIP32key(this)};BIP32key.prototype.privtopub=BIP32key.prototype.getPub=function(){if(this.type=="pub")return this.clone();return new BIP32key({vbytes:conv.stringToBytes(PUBDERIV),type:"pub",depth:this.depth,fingerprint:this.fingerprint,i:this.i,chaincode:this.chaincode,key:this.key.getPub()})};BIP32key.prototype.fromMasterKey=function(seed){var I=Bitcoin.Crypto.HMAC(Bitcoin.Crypto.SHA512,seed,"Bitcoin seed",{asBytes:true});return new BIP32key({vbytes:conv.stringToBytes(PRIVDERIV),type:"priv",depth:0,fingerprint:[0,0,0,0],i:0,chaincode:I.slice(32),key:new ECKey(I.slice(0,32).concat([1]),true)})};BIP32key.prototype.getKey=function(){return this.key};module.exports=BIP32key},{"./base58":2,"./convert":4,"./crypto-js/crypto":5,"./eckey":11,"./jsbn/ec":13,"./jsbn/sec":17,"./script":20,"./util":22}],4:[function(require,module,exports){var base64map="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";String.prototype.lpad=function(padString,length){var str=this;while(str.length>>6*(3-j)&63));else base64.push("=")}}return base64.join("")};module.exports.base64ToBytes=function(base64){base64=base64.replace(/[^A-Z0-9+\/]/gi,"");for(var bytes=[],i=0,imod4=0;i>>6-imod4*2)}return bytes};module.exports.coerceToBytes=function(input){if(typeof input=="string")return module.exports.hexToBytes(input);return input};module.exports.binToBytes=function(bin){return bin.match(/......../g).map(function(x){return parseInt(x,2)})};module.exports.bytesToBin=function(bytes){return bytes.map(function(x){return x.toString(2).lpad("0",8)}).join("")};module.exports.bytesToString=function(bytes){return bytes.map(function(x){return String.fromCharCode(x)}).join("")};module.exports.stringToBytes=function(string){return string.split("").map(function(x){return x.charCodeAt(0)})}},{}],5:[function(require,module,exports){var Crypto=module.exports={};var util=Crypto.util={randomBytes:function(n){for(var bytes=[];n>0;n--)bytes.push(Math.floor(Math.random()*256));return bytes}};Crypto.mode={};var charenc=Crypto.charenc={};var UTF8=charenc.UTF8={stringToBytes:function(str){return Binary.stringToBytes(unescape(encodeURIComponent(str)))},bytesToString:function(bytes){return decodeURIComponent(escape(Binary.bytesToString(bytes)))}};var Binary=charenc.Binary={stringToBytes:function(str){for(var bytes=[],i=0;ihasher._blocksize)key=hasher(key,{asBytes:true});var okey=key.slice(0),ikey=key.slice(0);for(var i=0;i>>5]|=bytes[i]<<24-b%32}return words};var wordsToBytes=function(words){var bytes=[];for(var b=0;b>>5]>>>24-b%32&255)}return bytes};var processBlock=function(H,M,offset){for(var i=0;i<16;i++){var offset_i=offset+i;var M_offset_i=M[offset_i];M[offset_i]=(M_offset_i<<8|M_offset_i>>>24)&16711935|(M_offset_i<<24|M_offset_i>>>8)&4278255360}var al,bl,cl,dl,el;var ar,br,cr,dr,er;ar=al=H[0];br=bl=H[1];cr=cl=H[2];dr=dl=H[3];er=el=H[4];var t;for(var i=0;i<80;i+=1){t=al+M[offset+zl[i]]|0;if(i<16){t+=f1(bl,cl,dl)+hl[0]}else if(i<32){t+=f2(bl,cl,dl)+hl[1]}else if(i<48){t+=f3(bl,cl,dl)+hl[2]}else if(i<64){t+=f4(bl,cl,dl)+hl[3]}else{t+=f5(bl,cl,dl)+hl[4]}t=t|0;t=rotl(t,sl[i]);t=t+el|0;al=el;el=dl;dl=rotl(cl,10);cl=bl;bl=t;t=ar+M[offset+zr[i]]|0;if(i<16){t+=f5(br,cr,dr)+hr[0]}else if(i<32){t+=f4(br,cr,dr)+hr[1]}else if(i<48){t+=f3(br,cr,dr)+hr[2]}else if(i<64){t+=f2(br,cr,dr)+hr[3]}else{t+=f1(br,cr,dr)+hr[4]}t=t|0;t=rotl(t,sr[i]);t=t+er|0;ar=er;er=dr;dr=rotl(cr,10);cr=br;br=t}t=H[1]+cl+dr|0;H[1]=H[2]+dl+er|0;H[2]=H[3]+el+ar|0;H[3]=H[4]+al+br|0;H[4]=H[0]+bl+cr|0;H[0]=t};function f1(x,y,z){return x^y^z}function f2(x,y,z){return x&y|~x&z}function f3(x,y,z){return(x|~y)^z}function f4(x,y,z){return x&z|y&~z}function f5(x,y,z){return x^(y|~z)}function rotl(x,n){return x<>>32-n}module.exports=function(message,options){if(message.constructor===String){message=UTF8.stringToBytes(message)}var H=[1732584193,4023233417,2562383102,271733878,3285377520];var m=bytesToWords(message);var nBitsLeft=message.length*8;var nBitsTotal=message.length*8;m[nBitsLeft>>>5]|=128<<24-nBitsLeft%32;m[(nBitsLeft+64>>>9<<4)+14]=(nBitsTotal<<8|nBitsTotal>>>24)&16711935|(nBitsTotal<<24|nBitsTotal>>>8)&4278255360;for(var i=0;i>>24)&16711935|(H_i<<24|H_i>>>8)&4278255360}var digestbytes=wordsToBytes(H);return options&&options.asBytes?digestbytes:options&&options.asString?Binary.bytesToString(digestbytes):conv.bytesToHex(digestbytes)}},{"../convert":4,"./crypto":5}],8:[function(require,module,exports){var conv=require("../convert"),util=require("../util");var K=[];(function(){function isPrime(n){var sqrtN=Math.sqrt(n);for(var factor=2;factor<=sqrtN;factor++){if(!(n%factor)){return false}}return true}function getFractionalBits(n){return(n-(n|0))*4294967296|0}var n=2;var nPrime=0;while(nPrime<64){if(isPrime(n)){K[nPrime]=getFractionalBits(Math.pow(n,1/3));nPrime++}n++}})();var W=[];var processBlock=function(H,M,offset){var a=H[0];var b=H[1];var c=H[2];var d=H[3];var e=H[4];var f=H[5];var g=H[6];var h=H[7];for(var i=0;i<64;i++){if(i<16){W[i]=M[offset+i]|0}else{var gamma0x=W[i-15];var gamma0=(gamma0x<<25|gamma0x>>>7)^(gamma0x<<14|gamma0x>>>18)^gamma0x>>>3;var gamma1x=W[i-2];var gamma1=(gamma1x<<15|gamma1x>>>17)^(gamma1x<<13|gamma1x>>>19)^gamma1x>>>10;W[i]=gamma0+W[i-7]+gamma1+W[i-16]}var ch=e&f^~e&g;var maj=a&b^a&c^b&c;var sigma0=(a<<30|a>>>2)^(a<<19|a>>>13)^(a<<10|a>>>22);var sigma1=(e<<26|e>>>6)^(e<<21|e>>>11)^(e<<7|e>>>25);var t1=h+sigma1+ch+K[i]+W[i];var t2=sigma0+maj;h=g;g=f;f=e;e=d+t1|0;d=c;c=b;b=a;a=t1+t2|0}H[0]=H[0]+a|0;H[1]=H[1]+b|0;H[2]=H[2]+c|0;H[3]=H[3]+d|0;H[4]=H[4]+e|0;H[5]=H[5]+f|0;H[6]=H[6]+g|0;H[7]=H[7]+h|0};module.exports=function(message,options){var H=[1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225];if(message.constructor===String){message=conv.stringToBytes(message)}var m=util.bytesToWords(message);var l=message.length*8;m[l>>5]|=128<<24-l%32;m[(l+64>>9<<4)+15]=l;for(var i=0;i>>1|gamma0xl<<31)^(gamma0xh>>>8|gamma0xl<<24)^gamma0xh>>>7;var gamma0l=(gamma0xl>>>1|gamma0xh<<31)^(gamma0xl>>>8|gamma0xh<<24)^(gamma0xl>>>7|gamma0xh<<25);var gamma1x=W[i-2];var gamma1xh=gamma1x[0];var gamma1xl=gamma1x[1];var gamma1h=(gamma1xh>>>19|gamma1xl<<13)^(gamma1xh<<3|gamma1xl>>>29)^gamma1xh>>>6;var gamma1l=(gamma1xl>>>19|gamma1xh<<13)^(gamma1xl<<3|gamma1xh>>>29)^(gamma1xl>>>6|gamma1xh<<26);var Wi7=W[i-7];var Wi7h=Wi7[0];var Wi7l=Wi7[1];var Wi16=W[i-16];var Wi16h=Wi16[0];var Wi16l=Wi16[1];var Wil=gamma0l+Wi7l;var Wih=gamma0h+Wi7h+(Wil>>>0>>0?1:0);var Wil=Wil+gamma1l;var Wih=Wih+gamma1h+(Wil>>>0>>0?1:0);var Wil=Wil+Wi16l;var Wih=Wih+Wi16h+(Wil>>>0>>0?1:0);Wi[0]=Wih;Wi[1]=Wil}var chh=eh&fh^~eh&gh;var chl=el&fl^~el≷var majh=ah&bh^ah&ch^bh&ch;var majl=al&bl^al&cl^bl&cl;var sigma0h=(ah>>>28|al<<4)^(ah<<30|al>>>2)^(ah<<25|al>>>7);var sigma0l=(al>>>28|ah<<4)^(al<<30|ah>>>2)^(al<<25|ah>>>7);var sigma1h=(eh>>>14|el<<18)^(eh>>>18|el<<14)^(eh<<23|el>>>9);var sigma1l=(el>>>14|eh<<18)^(el>>>18|eh<<14)^(el<<23|eh>>>9);var Ki=K[i];var Kih=Ki[0];var Kil=Ki[1];var t1l=hl+sigma1l;var t1h=hh+sigma1h+(t1l>>>0>>0?1:0);var t1l=t1l+chl;var t1h=t1h+chh+(t1l>>>0>>0?1:0);var t1l=t1l+Kil;var t1h=t1h+Kih+(t1l>>>0>>0?1:0);var t1l=t1l+Wil;var t1h=t1h+Wih+(t1l>>>0>>0?1:0);var t2l=sigma0l+majl;var t2h=sigma0h+majh+(t2l>>>0>>0?1:0);hh=gh;hl=gl;gh=fh;gl=fl;fh=eh;fl=el;el=dl+t1l|0;eh=dh+t1h+(el>>>0
>>0?1:0)|0;dh=ch;dl=cl;ch=bh;cl=bl;bh=ah;bl=al;al=t1l+t2l|0;ah=t1h+t2h+(al>>>0>>0?1:0)|0}H0l=H0[1]=H0l+al;H0[0]=H0h+ah+(H0l>>>0>>0?1:0);H1l=H1[1]=H1l+bl;H1[0]=H1h+bh+(H1l>>>0>>0?1:0);H2l=H2[1]=H2l+cl;H2[0]=H2h+ch+(H2l>>>0>>0?1:0);H3l=H3[1]=H3l+dl;H3[0]=H3h+dh+(H3l>>>0
>>0?1:0);H4l=H4[1]=H4l+el;H4[0]=H4h+eh+(H4l>>>0>>0?1:0);H5l=H5[1]=H5l+fl;H5[0]=H5h+fh+(H5l>>>0>>0?1:0);H6l=H6[1]=H6l+gl;H6[0]=H6h+gh+(H6l>>>0>>0?1:0);H7l=H7[1]=H7l+hl;H7[0]=H7h+hh+(H7l>>>0>>0?1:0);return H};module.exports=function(message,options){var H=[[1779033703,4089235720],[3144134277,2227873595],[1013904242,4271175723],[2773480762,1595750129],[1359893119,2917565137],[2600822924,725511199],[528734635,4215389547],[1541459225,327033209]];if(message.constructor===String){message=conv.stringToBytes(message)}var m=util.bytesToWords(message);var l=message.length*8;m[l>>5]|=128<<24-l%32;m[(l+64>>10<<5)+31]=l;for(var i=0;i=0;--i){R=R.twice2D();R.z=BigInteger.ONE;if(k.testBit(i)){if(l.testBit(i)){R=R.add2D(Z)}else{R=R.add2D(P)}}else{if(l.testBit(i)){R=R.add2D(Q)}}}return R}function deterministicGenerateK(hash,key){var v=[];var k=[];for(var i=0;i<32;i++)v.push(1);for(var i=0;i<32;i++)k.push(0);k=Bitcoin.Crypto.HMAC(Bitcoin.Crypto.SHA256,v.concat([0]).concat(key).concat(hash),k,{asBytes:true});v=Bitcoin.Crypto.HMAC(Bitcoin.Crypto.SHA256,v,k,{asBytes:true});k=Bitcoin.Crypto.HMAC(Bitcoin.Crypto.SHA256,v.concat([1]).concat(key).concat(hash),k,{asBytes:true});v=Bitcoin.Crypto.HMAC(Bitcoin.Crypto.SHA256,v,k,{asBytes:true});v=Bitcoin.Crypto.HMAC(Bitcoin.Crypto.SHA256,v,k,{asBytes:true});return Bitcoin.BigInteger.fromByteArrayUnsigned(v)}var ECDSA={getBigRandom:function(limit){return new BigInteger(limit.bitLength(),rng).mod(limit.subtract(BigInteger.ONE)).add(BigInteger.ONE)},sign:function(hash,priv){var d=priv;var n=ecparams.getN();var e=BigInteger.fromByteArrayUnsigned(hash);var k=deterministicGenerateK(hash,priv.toByteArrayUnsigned());var G=ecparams.getG();var Q=G.multiply(k);var r=Q.getX().toBigInteger().mod(n);var s=k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n);return ECDSA.serializeSig(r,s)},verify:function(hash,sig,pubkey){var r,s;if(util.isArray(sig)){var obj=ECDSA.parseSig(sig);r=obj.r;s=obj.s}else if("object"===typeof sig&&sig.r&&sig.s){r=sig.r;s=sig.s}else{throw new Error("Invalid value for signature")}var Q;if(pubkey instanceof ECPointFp){Q=pubkey}else if(util.isArray(pubkey)){Q=ECPointFp.decodeFrom(ecparams.getCurve(),pubkey)}else{throw new Error("Invalid format for pubkey value, must be byte array or ECPointFp")}var e=BigInteger.fromByteArrayUnsigned(hash);return ECDSA.verifyRaw(e,r,s,Q)},verifyRaw:function(e,r,s,Q){var n=ecparams.getN();var G=ecparams.getG();if(r.compareTo(BigInteger.ONE)<0||r.compareTo(n)>=0)return false;if(s.compareTo(BigInteger.ONE)<0||s.compareTo(n)>=0)return false;var c=s.modInverse(n);var u1=e.multiply(c).mod(n);var u2=r.multiply(c).mod(n);var point=G.multiply(u1).add(Q.multiply(u2));var v=point.getX().toBigInteger().mod(n);return v.equals(r)},serializeSig:function(r,s){var rBa=r.toByteArraySigned();var sBa=s.toByteArraySigned();var sequence=[];sequence.push(2);sequence.push(rBa.length);sequence=sequence.concat(rBa);sequence.push(2);sequence.push(sBa.length);sequence=sequence.concat(sBa);sequence.unshift(sequence.length);sequence.unshift(48);return sequence},parseSig:function(sig){var cursor;if(sig[0]!=48)throw new Error("Signature not a valid DERSequence");cursor=2;if(sig[cursor]!=2)throw new Error("First element in signature must be a DERInteger");var rBa=sig.slice(cursor+2,cursor+2+sig[cursor+1]);cursor+=2+sig[cursor+1];if(sig[cursor]!=2)throw new Error("Second element in signature must be a DERInteger");var sBa=sig.slice(cursor+2,cursor+2+sig[cursor+1]);cursor+=2+sig[cursor+1];var r=BigInteger.fromByteArrayUnsigned(rBa);var s=BigInteger.fromByteArrayUnsigned(sBa);return{r:r,s:s}},parseSigCompact:function(sig){if(sig.length!==65){throw new Error("Signature has the wrong length")}var i=sig[0]-27;if(i<0||i>7){throw new Error("Invalid signature type")}var n=ecparams.getN();var r=BigInteger.fromByteArrayUnsigned(sig.slice(1,33)).mod(n);var s=BigInteger.fromByteArrayUnsigned(sig.slice(33,65)).mod(n);return{r:r,s:s,i:i}},recoverPubKey:function(r,s,hash,i){i=i&3;var isYEven=i&1;var isSecondKey=i>>1;var n=ecparams.getN();var G=ecparams.getG();var curve=ecparams.getCurve();var p=curve.getQ();var a=curve.getA().toBigInteger();var b=curve.getB().toBigInteger();if(!P_OVER_FOUR){P_OVER_FOUR=p.add(BigInteger.ONE).divide(BigInteger.valueOf(4))}var x=isSecondKey?r.add(n):r;var alpha=x.multiply(x).multiply(x).add(a.multiply(x)).add(b).mod(p);var beta=alpha.modPow(P_OVER_FOUR,p);var xorOdd=beta.isEven()?i%2:(i+1)%2;var y=(beta.isEven()?!isYEven:isYEven)?beta:p.subtract(beta);var R=new ECPointFp(curve,curve.fromBigInteger(x),curve.fromBigInteger(y));R.validate();var e=BigInteger.fromByteArrayUnsigned(hash);var eNeg=BigInteger.ZERO.subtract(e).mod(n);var rInv=r.modInverse(n);var Q=implShamirsTrick(R,s,G,eNeg).multiply(rInv);Q.validate();if(!ECDSA.verifyRaw(e,r,s,Q)){throw new Error("Pubkey recovery unsuccessful")}var ECPubKey=require("./eckey").ECPubKey;return ECPubKey(Q)},calcPubkeyRecoveryParam:function(origPubkey,r,s,hash){var address=origPubkey.getBitcoinAddress().toString();for(var i=0;i<4;i++){var pubkey=ECDSA.recoverPubKey(r,s,hash,i);pubkey.compressed=origPubkey.compressed;if(pubkey.getBitcoinAddress().toString()==address){return i}}throw new Error("Unable to find valid recovery factor")}};module.exports=ECDSA},{"./convert":4,"./eckey":11,"./jsbn/ec":13,"./jsbn/jsbn":14,"./jsbn/rng":16,"./jsbn/sec":17,"./util":22}],11:[function(require,module,exports){var BigInteger=require("./jsbn/jsbn");var sec=require("./jsbn/sec");var base58=require("./base58");var Crypto=require("./crypto-js/crypto");var util=require("./util");var conv=require("./convert");var Address=require("./address");var ecdsa=require("./ecdsa");var ECPointFp=require("./jsbn/ec").ECPointFp;var ecparams=sec("secp256k1");var ECKey=function(input,compressed){if(!(this instanceof ECKey)){return new ECKey(input)}if(!input){var n=ecparams.getN();this.priv=ecdsa.getBigRandom(n);this.compressed=compressed||false}else this.import(input,compressed)};ECKey.prototype.import=function(input,compressed){function has(li,v){return li.indexOf(v)>=0}function fromBin(x){return BigInteger.fromByteArrayUnsigned(x)}this.priv=input instanceof ECKey?input.priv:input instanceof BigInteger?input.mod(ecparams.getN()):util.isArray(input)?fromBin(input.slice(0,32)):typeof input!="string"?null:input.length==51&&input[0]=="5"?fromBin(base58.checkDecode(input)):input.length==52&&has("LK",input[0])?fromBin(base58.checkDecode(input)):has([64,65],input.length)?fromBin(conv.hexToBytes(input.slice(0,64))):null;this.compressed=arguments.length>1?compressed:input instanceof ECKey?input.compressed:input instanceof BigInteger?false:util.isArray(input)?false:typeof input!="string"?null:input.length==51&&input[0]=="5"?false:input.length==52&&has("LK",input[0])?true:input.length==64?false:input.length==65?true:null};ECKey.prototype.getPub=function(){return ECPubKey(ecparams.getG().multiply(this.priv),this.compressed)};ECKey.prototype.export=function(format){var bytes=this.priv.toByteArrayUnsigned();if(this.compressed)bytes.push(1);return format==="base58"?base58.checkEncode(bytes,128):format==="bin"?conv.bytesToString(bytes):format==="bytes"?bytes:format==="hex"?conv.bytesToHex(bytes):bytes};ECKey.prototype.toString=function(format){return""+this.export(format)};ECKey.prototype.getBitcoinAddress=function(v){return this.getPub().getBitcoinAddress(v)};ECKey.prototype.add=function(key){return ECKey(this.priv.add(ECKey(key).priv),this.compressed)};ECKey.prototype.multiply=function(key){return ECKey(this.priv.multiply(ECKey(key).priv),this.compressed)};var ECPubKey=function(input,compressed){if(!(this instanceof ECPubKey)){return new ECPubKey(input,compressed)}if(!input){var n=ecparams.getN();this.pub=ecparams.getG().multiply(ecdsa.getBigRandom(n));this.compressed=compressed||false}else this.import(input,compressed)};ECPubKey.prototype.import=function(input,compressed){var decode=function(x){return ECPointFp.decodeFrom(ecparams.getCurve(),x)};this.pub=input instanceof ECPointFp?input:input instanceof ECKey?ecparams.getG().multiply(input.priv):input instanceof ECPubKey?input.pub:typeof input=="string"?decode(conv.hexToBytes(input)):util.isArray(input)?decode(input):ecparams.getG().multiply(ecdsa.getBigRandom(ecparams.getN()));this.compressed=arguments.length>1?compressed:input instanceof ECPointFp?input.compressed:input instanceof ECPubKey?input.compressed:this.pub[0]<4};ECPubKey.prototype.add=function(key){return ECPubKey(this.pub.add(ECPubKey(key).pub),this.compressed)};ECPubKey.prototype.multiply=function(key){return ECPubKey(this.pub.multiply(ECKey(key).priv),this.compressed)};ECPubKey.prototype.export=function(formt){var o=this.pub.getEncoded(this.compressed);return formt=="hex"?conv.bytesToHex(o):formt=="bin"?conv.bytesToString(o):o};ECPubKey.prototype.toString=function(format){return""+this.export(format)};ECPubKey.prototype.getBitcoinAddress=function(v){return new Address(util.sha256ripe160(this.export()),v)};ECKey.prototype.sign=function(hash){return ecdsa.sign(hash,this.priv)};ECKey.prototype.verify=function(hash,sig){return ecdsa.verify(hash,sig,this.getPub())};module.exports={ECKey:ECKey,ECPubKey:ECPubKey}},{"./address":1,"./base58":2,"./convert":4,"./crypto-js/crypto":5,"./ecdsa":10,"./jsbn/ec":13,"./jsbn/jsbn":14,"./jsbn/sec":17,"./util":22}],12:[function(require,module,exports){var rotl=function(n,b){return n<>>32-b};var rotr=function(n,b){return n<<32-b|n>>>b};var endian=function(n){if(n.constructor==Number){return rotl(n,8)&16711935|rotl(n,24)&4278255360}for(var i=0;i0;--i){R=R.twice();var hBit=h.testBit(i);var eBit=e.testBit(i);if(hBit!=eBit){R=R.add(hBit?this:neg)}}return R}function pointFpMultiplyTwo(j,x,k){var i;if(j.bitLength()>k.bitLength())i=j.bitLength()-1;else i=k.bitLength()-1;var R=this.curve.getInfinity();var both=this.add(x);while(i>=0){R=R.twice();if(j.testBit(i)){if(k.testBit(i)){R=R.add(both)}else{R=R.add(this)}}else{if(k.testBit(i)){R=R.add(x)}}--i}return R}ECPointFp.prototype.getX=pointFpGetX;ECPointFp.prototype.getY=pointFpGetY;ECPointFp.prototype.equals=pointFpEquals;ECPointFp.prototype.isInfinity=pointFpIsInfinity;ECPointFp.prototype.negate=pointFpNegate;ECPointFp.prototype.add=pointFpAdd;ECPointFp.prototype.twice=pointFpTwice;ECPointFp.prototype.multiply=pointFpMultiply;ECPointFp.prototype.multiplyTwo=pointFpMultiplyTwo;function ECCurveFp(q,a,b){this.q=q;this.a=this.fromBigInteger(a);this.b=this.fromBigInteger(b);this.infinity=new ECPointFp(this,null,null)}function curveFpGetQ(){return this.q}function curveFpGetA(){return this.a}function curveFpGetB(){return this.b}function curveFpEquals(other){if(other==this)return true;return this.q.equals(other.q)&&this.a.equals(other.a)&&this.b.equals(other.b)}function curveFpGetInfinity(){return this.infinity}function curveFpFromBigInteger(x){return new ECFieldElementFp(this.q,x)}function curveFpDecodePointHex(s){switch(parseInt(s.substr(0,2),16)){case 0:return this.infinity;case 2:case 3:return null;case 4:case 6:case 7:var len=(s.length-2)/2;var xHex=s.substr(2,len);var yHex=s.substr(len+2,len);return new ECPointFp(this,this.fromBigInteger(new BigInteger(xHex,16)),this.fromBigInteger(new BigInteger(yHex,16)));default:return null}}ECCurveFp.prototype.getQ=curveFpGetQ;ECCurveFp.prototype.getA=curveFpGetA;ECCurveFp.prototype.getB=curveFpGetB;ECCurveFp.prototype.equals=curveFpEquals;ECCurveFp.prototype.getInfinity=curveFpGetInfinity;ECCurveFp.prototype.fromBigInteger=curveFpFromBigInteger;ECCurveFp.prototype.decodePointHex=curveFpDecodePointHex;function integerToBytes(i,len){var bytes=i.toByteArrayUnsigned();if(lenbytes.length){bytes.unshift(0)}return bytes}ECFieldElementFp.prototype.getByteLength=function(){return Math.floor((this.toBigInteger().bitLength()+7)/8)};ECPointFp.prototype.getEncoded=function(compressed){var x=this.getX().toBigInteger();var y=this.getY().toBigInteger();var enc=integerToBytes(x,32);if(compressed){if(y.isEven()){enc.unshift(2)}else{enc.unshift(3)}}else{enc.unshift(4);enc=enc.concat(integerToBytes(y,32))}return enc};ECPointFp.decodeFrom=function(ecparams,enc){var type=enc[0];var dataLen=enc.length-1;if(type==4){var xBa=enc.slice(1,1+dataLen/2),yBa=enc.slice(1+dataLen/2,1+dataLen),x=BigInteger.fromByteArrayUnsigned(xBa),y=BigInteger.fromByteArrayUnsigned(yBa)}else{var xBa=enc.slice(1),x=BigInteger.fromByteArrayUnsigned(xBa),p=ecparams.getQ(),xCubedPlus7=x.multiply(x).multiply(x).add(new Bitcoin.BigInteger("7")).mod(p),pPlus1Over4=p.add(new Bitcoin.BigInteger("1")).divide(new Bitcoin.BigInteger("4")),y=xCubedPlus7.modPow(pPlus1Over4,p);if(y.mod(new Bitcoin.BigInteger("2")).toString()!=""+type%2){y=p.subtract(y)}}return new ECPointFp(ecparams,ecparams.fromBigInteger(x),ecparams.fromBigInteger(y))};ECPointFp.prototype.add2D=function(b){if(this.isInfinity())return b;if(b.isInfinity())return this;if(this.x.equals(b.x)){if(this.y.equals(b.y)){return this.twice()}return this.curve.getInfinity()}var x_x=b.x.subtract(this.x);var y_y=b.y.subtract(this.y);var gamma=y_y.divide(x_x);var x3=gamma.square().subtract(this.x).subtract(b.x);var y3=gamma.multiply(this.x.subtract(x3)).subtract(this.y);return new ECPointFp(this.curve,x3,y3)};ECPointFp.prototype.twice2D=function(){if(this.isInfinity())return this;if(this.y.toBigInteger().signum()==0){return this.curve.getInfinity()}var TWO=this.curve.fromBigInteger(BigInteger.valueOf(2));var THREE=this.curve.fromBigInteger(BigInteger.valueOf(3));var gamma=this.x.square().multiply(THREE).add(this.curve.a).divide(this.y.multiply(TWO));var x3=gamma.square().subtract(this.x.multiply(TWO));var y3=gamma.multiply(this.x.subtract(x3)).subtract(this.y);return new ECPointFp(this.curve,x3,y3)};ECPointFp.prototype.multiply2D=function(k){if(this.isInfinity())return this;if(k.signum()==0)return this.curve.getInfinity();var e=k;var h=e.multiply(new BigInteger("3"));var neg=this.negate();var R=this;var i;for(i=h.bitLength()-2;i>0;--i){R=R.twice();var hBit=h.testBit(i);var eBit=e.testBit(i);if(hBit!=eBit){R=R.add2D(hBit?this:neg)}}return R};ECPointFp.prototype.isOnCurve=function(){var x=this.getX().toBigInteger();var y=this.getY().toBigInteger();var a=this.curve.getA().toBigInteger();var b=this.curve.getB().toBigInteger();var n=this.curve.getQ();var lhs=y.multiply(y).mod(n);var rhs=x.multiply(x).multiply(x).add(a.multiply(x)).add(b).mod(n);return lhs.equals(rhs)};ECPointFp.prototype.toString=function(){return"("+this.getX().toBigInteger().toString()+","+this.getY().toBigInteger().toString()+")"};ECPointFp.prototype.validate=function(){var n=this.curve.getQ();if(this.isInfinity()){throw new Error("Point is at infinity.")}var x=this.getX().toBigInteger();var y=this.getY().toBigInteger();if(x.compareTo(BigInteger.ONE)<0||x.compareTo(n.subtract(BigInteger.ONE))>0){throw new Error("x coordinate out of bounds")}if(y.compareTo(BigInteger.ONE)<0||y.compareTo(n.subtract(BigInteger.ONE))>0){throw new Error("y coordinate out of bounds")}if(!this.isOnCurve()){throw new Error("Point is not on the curve.")}if(this.multiply(n).isInfinity()){throw new Error("Point is not a scalar multiple of G.")}return true};module.exports=ECCurveFp;module.exports.ECPointFp=ECPointFp},{"./jsbn":14,"./sec":17}],14:[function(require,module,exports){var dbits;var canary=0xdeadbeefcafe;var j_lm=(canary&16777215)==15715070;function BigInteger(a,b,c){if(!(this instanceof BigInteger)){return new BigInteger(a,b,c)}if(a!=null){if("number"==typeof a)this.fromNumber(a,b,c);else if(b==null&&"string"!=typeof a)this.fromString(a,256);else this.fromString(a,b)}}var proto=BigInteger.prototype;function nbi(){return new BigInteger(null)}function am1(i,x,w,j,c,n){while(--n>=0){var v=x*this[i++]+w[j]+c;c=Math.floor(v/67108864);w[j++]=v&67108863}return c}function am2(i,x,w,j,c,n){var xl=x&32767,xh=x>>15;while(--n>=0){var l=this[i]&32767;var h=this[i++]>>15;var m=xh*l+h*xl;l=xl*l+((m&32767)<<15)+w[j]+(c&1073741823);c=(l>>>30)+(m>>>15)+xh*h+(c>>>30);w[j++]=l&1073741823}return c}function am3(i,x,w,j,c,n){var xl=x&16383,xh=x>>14;while(--n>=0){var l=this[i]&16383;var h=this[i++]>>14;var m=xh*l+h*xl;l=xl*l+((m&16383)<<14)+w[j]+c;c=(l>>28)+(m>>14)+xh*h;w[j++]=l&268435455}return c}BigInteger.prototype.am=am1;dbits=26;BigInteger.prototype.DB=dbits;BigInteger.prototype.DM=(1<=0;--i)r[i]=this[i];r.t=this.t;r.s=this.s}function bnpFromInt(x){this.t=1;this.s=x<0?-1:0;if(x>0)this[0]=x;else if(x<-1)this[0]=x+DV;else this.t=0}function nbv(i){var r=nbi();r.fromInt(i);return r}function bnpFromString(s,b){var self=this;var k;if(b==16)k=4;else if(b==8)k=3;else if(b==256)k=8;else if(b==2)k=1;else if(b==32)k=5;else if(b==4)k=2;else{self.fromRadix(s,b);return}self.t=0;self.s=0;var i=s.length,mi=false,sh=0;while(--i>=0){var x=k==8?s[i]&255:intAt(s,i);if(x<0){if(s.charAt(i)=="-")mi=true;continue}mi=false;if(sh==0)self[self.t++]=x;else if(sh+k>self.DB){self[self.t-1]|=(x&(1<>self.DB-sh}else self[self.t-1]|=x<=self.DB)sh-=self.DB}if(k==8&&(s[0]&128)!=0){self.s=-1;if(sh>0)self[self.t-1]|=(1<0&&this[this.t-1]==c)--this.t}function bnToString(b){var self=this;if(self.s<0)return"-"+self.negate().toString(b);var k;if(b==16)k=4;else if(b==8)k=3;else if(b==2)k=1;else if(b==32)k=5;else if(b==4)k=2;else return self.toRadix(b);var km=(1<0){if(p>p)>0){m=true;r=int2char(d)}while(i>=0){if(p>(p+=self.DB-k)}else{d=self[i]>>(p-=k)&km;if(p<=0){p+=self.DB;--i}}if(d>0)m=true;if(m)r+=int2char(d)}}return m?r:"0"}function bnNegate(){var r=nbi();BigInteger.ZERO.subTo(this,r);return r}function bnAbs(){return this.s<0?this.negate():this}function bnCompareTo(a){var r=this.s-a.s;if(r!=0)return r;var i=this.t;r=i-a.t;if(r!=0)return this.s<0?-r:r;while(--i>=0)if((r=this[i]-a[i])!=0)return r;return 0}function nbits(x){var r=1,t;if((t=x>>>16)!=0){x=t;r+=16}if((t=x>>8)!=0){x=t;r+=8}if((t=x>>4)!=0){x=t;r+=4}if((t=x>>2)!=0){x=t;r+=2}if((t=x>>1)!=0){x=t;r+=1}return r}function bnBitLength(){if(this.t<=0)return 0;return this.DB*(this.t-1)+nbits(this[this.t-1]^this.s&this.DM)}function bnpDLShiftTo(n,r){var i;for(i=this.t-1;i>=0;--i)r[i+n]=this[i];for(i=n-1;i>=0;--i)r[i]=0;r.t=this.t+n;r.s=this.s}function bnpDRShiftTo(n,r){for(var i=n;i=0;--i){r[i+ds+1]=self[i]>>cbs|c;c=(self[i]&bm)<=0;--i)r[i]=0;r[ds]=c;r.t=self.t+ds+1;r.s=self.s;r.clamp()}function bnpRShiftTo(n,r){var self=this;r.s=self.s;var ds=Math.floor(n/self.DB);if(ds>=self.t){r.t=0;return}var bs=n%self.DB;var cbs=self.DB-bs;var bm=(1<>bs;for(var i=ds+1;i>bs}if(bs>0)r[self.t-ds-1]|=(self.s&bm)<>=self.DB}if(a.t>=self.DB}c+=self.s}else{c+=self.s;while(i>=self.DB}c-=a.s}r.s=c<0?-1:0;if(c<-1)r[i++]=self.DV+c;else if(c>0)r[i++]=c;r.t=i;r.clamp()}function bnpMultiplyTo(a,r){var x=this.abs(),y=a.abs();var i=x.t;r.t=i+y.t;while(--i>=0)r[i]=0;for(i=0;i=0)r[i]=0;for(i=0;i=x.DV){r[i+x.t]-=x.DV;r[i+x.t+1]=1}}if(r.t>0)r[r.t-1]+=x.am(i,x[i],r,2*i,0,1);r.s=0;r.clamp()}function bnpDivRemTo(m,q,r){var self=this;var pm=m.abs();if(pm.t<=0)return;var pt=self.abs();if(pt.t0){pm.lShiftTo(nsh,y);pt.lShiftTo(nsh,r)}else{pm.copyTo(y);pt.copyTo(r)}var ys=y.t;var y0=y[ys-1];if(y0==0)return;var yt=y0*(1<1?y[ys-2]>>self.F2:0);var d1=self.FV/yt,d2=(1<=0){r[r.t++]=1;r.subTo(t,r)}BigInteger.ONE.dlShiftTo(ys,t);t.subTo(y,y);while(y.t=0){var qd=r[--i]==y0?self.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2);if((r[i]+=y.am(0,qd,r,j,0,ys))0)r.rShiftTo(nsh,r);if(ts<0)BigInteger.ZERO.subTo(r,r)}function bnMod(a){var r=nbi();this.abs().divRemTo(a,null,r);if(this.s<0&&r.compareTo(BigInteger.ZERO)>0)a.subTo(r,r);return r}function Classic(m){this.m=m}function cConvert(x){if(x.s<0||x.compareTo(this.m)>=0)return x.mod(this.m);else return x}function cRevert(x){return x}function cReduce(x){x.divRemTo(this.m,null,x)}function cMulTo(x,y,r){x.multiplyTo(y,r);this.reduce(r)}function cSqrTo(x,r){x.squareTo(r);this.reduce(r)}Classic.prototype.convert=cConvert;Classic.prototype.revert=cRevert;Classic.prototype.reduce=cReduce;Classic.prototype.mulTo=cMulTo;Classic.prototype.sqrTo=cSqrTo;function bnpInvDigit(){if(this.t<1)return 0;var x=this[0];if((x&1)==0)return 0;var y=x&3;y=y*(2-(x&15)*y)&15;y=y*(2-(x&255)*y)&255;y=y*(2-((x&65535)*y&65535))&65535;y=y*(2-x*y%this.DV)%this.DV;return y>0?this.DV-y:-y}function Montgomery(m){this.m=m;this.mp=m.invDigit();this.mpl=this.mp&32767;this.mph=this.mp>>15;this.um=(1<0)this.m.subTo(r,r);return r}function montRevert(x){var r=nbi();x.copyTo(r);this.reduce(r);return r}function montReduce(x){while(x.t<=this.mt2)x[x.t++]=0;for(var i=0;i>15)*this.mpl&this.um)<<15)&x.DM;j=i+this.m.t;x[j]+=this.m.am(0,u0,x,i,0,this.m.t);while(x[j]>=x.DV){x[j]-=x.DV;x[++j]++}}x.clamp();x.drShiftTo(this.m.t,x);if(x.compareTo(this.m)>=0)x.subTo(this.m,x)}function montSqrTo(x,r){x.squareTo(r);this.reduce(r)}function montMulTo(x,y,r){x.multiplyTo(y,r);this.reduce(r)}Montgomery.prototype.convert=montConvert;Montgomery.prototype.revert=montRevert;Montgomery.prototype.reduce=montReduce;Montgomery.prototype.mulTo=montMulTo;Montgomery.prototype.sqrTo=montSqrTo;function bnpIsEven(){return(this.t>0?this[0]&1:this.s)==0}function bnpExp(e,z){if(e>4294967295||e<1)return BigInteger.ONE;var r=nbi(),r2=nbi(),g=z.convert(this),i=nbits(e)-1;g.copyTo(r);while(--i>=0){z.sqrTo(r,r2);if((e&1<0)z.mulTo(r2,g,r);else{var t=r;r=r2;r2=t}}return z.revert(r)}function bnModPowInt(e,m){var z;if(e<256||m.isEven())z=new Classic(m);else z=new Montgomery(m);return this.exp(e,z)}proto.copyTo=bnpCopyTo;proto.fromInt=bnpFromInt;proto.fromString=bnpFromString;proto.clamp=bnpClamp;proto.dlShiftTo=bnpDLShiftTo;proto.drShiftTo=bnpDRShiftTo;proto.lShiftTo=bnpLShiftTo;proto.rShiftTo=bnpRShiftTo;proto.subTo=bnpSubTo;proto.multiplyTo=bnpMultiplyTo;proto.squareTo=bnpSquareTo;proto.divRemTo=bnpDivRemTo;proto.invDigit=bnpInvDigit;proto.isEven=bnpIsEven;proto.exp=bnpExp;proto.toString=bnToString;proto.negate=bnNegate;proto.abs=bnAbs;proto.compareTo=bnCompareTo;proto.bitLength=bnBitLength;proto.mod=bnMod;proto.modPowInt=bnModPowInt;function nbi(){return new BigInteger(null)}function bnClone(){var r=nbi();this.copyTo(r);return r}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;return(this[1]&(1<<32-this.DB)-1)<>24}function bnShortValue(){return this.t==0?this.s:this[0]<<16>>16}function bnpChunkSize(r){return Math.floor(Math.LN2*this.DB/Math.log(r))}function bnSigNum(){if(this.s<0)return-1;else if(this.t<=0||this.t==1&&this[0]<=0)return 0;else return 1}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}function bnpFromRadix(s,b){var self=this;self.fromInt(0);if(b==null)b=10;var cs=self.chunkSize(b);var d=Math.pow(b,cs),mi=false,j=0,w=0;for(var i=0;i=cs){self.dMultiply(d);self.dAddOffset(w,0);j=0;w=0}}if(j>0){self.dMultiply(Math.pow(b,j));self.dAddOffset(w,0)}if(mi)BigInteger.ZERO.subTo(self,self)}function bnpFromNumber(a,b,c){var self=this;if("number"==typeof b){if(a<2)self.fromInt(1);else{self.fromNumber(a,c);if(!self.testBit(a-1))self.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,self);if(self.isEven())self.dAddOffset(1,0);while(!self.isProbablePrime(b)){self.dAddOffset(2,0);if(self.bitLength()>a)self.subTo(BigInteger.ONE.shiftLeft(a-1),self)}}}else{var x=new Array,t=a&7;x.length=(a>>3)+1;b.nextBytes(x);if(t>0)x[0]&=(1<0){if(p>p)!=(self.s&self.DM)>>p)r[k++]=d|self.s<=0){if(p<8){d=(self[i]&(1<>(p+=self.DB-8)}else{d=self[i]>>(p-=8)&255;if(p<=0){p+=self.DB;--i}}if((d&128)!=0)d|=-256;if(k===0&&(self.s&128)!=(d&128))++k;if(k>0||d!=self.s)r[k++]=d}}return r}function bnEquals(a){return this.compareTo(a)==0}function bnMin(a){return this.compareTo(a)<0?this:a}function bnMax(a){return this.compareTo(a)>0?this:a}function bnpBitwiseTo(a,op,r){var self=this;var i,f,m=Math.min(a.t,self.t);for(i=0;i>=16;r+=16}if((x&255)==0){x>>=8;r+=8}if((x&15)==0){x>>=4;r+=4}if((x&3)==0){x>>=2;r+=2}if((x&1)==0)++r;return r}function bnGetLowestSetBit(){for(var i=0;i=this.t)return this.s!=0;return(this[j]&1<>=self.DB}if(a.t>=self.DB}c+=self.s}else{c+=self.s;while(i>=self.DB}c+=a.s}r.s=c<0?-1:0;if(c>0)r[i++]=c;else if(c<-1)r[i++]=self.DV+c;r.t=i;r.clamp()}function bnAdd(a){var r=nbi();this.addTo(a,r);return r}function bnSubtract(a){var r=nbi();this.subTo(a,r);return r}function bnMultiply(a){var r=nbi();this.multiplyTo(a,r);return r}function bnSquare(){var r=nbi();this.squareTo(r);return r}function bnDivide(a){var r=nbi();this.divRemTo(a,r,null);return r}function bnRemainder(a){var r=nbi();this.divRemTo(a,null,r);return r}function bnDivideAndRemainder(a){var q=nbi(),r=nbi();this.divRemTo(a,q,r);return new Array(q,r)}function bnpDMultiply(n){this[this.t]=this.am(0,n-1,this,0,0,this.t);++this.t;this.clamp()}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]}}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;function bnPow(e){return this.exp(e,new NullExp)}function bnpMultiplyLowerTo(a,n,r){var i=Math.min(this.t+a.t,n);r.s=0;r.t=i;while(i>0)r[--i]=0;var j;for(j=r.t-this.t;i=0)r[i]=0;for(i=Math.max(n-this.t,0);i2*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}function barrettReduce(x){var self=this;x.drShiftTo(self.m.t-1,self.r2);if(x.t>self.m.t+1){x.t=self.m.t+1;x.clamp()}self.mu.multiplyUpperTo(self.r2,self.m.t+1,self.q3);self.m.multiplyLowerTo(self.q3,self.m.t+1,self.r2);while(x.compareTo(self.r2)<0)x.dAddOffset(1,self.m.t+1);x.subTo(self.r2,x);while(x.compareTo(self.m)>=0)x.subTo(self.m,x)}function barrettSqrTo(x,r){x.squareTo(r);this.reduce(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;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);var g=new Array,n=3,k1=k-1,km=(1<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<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){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<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}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}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}proto.chunkSize=bnpChunkSize;proto.toRadix=bnpToRadix;proto.fromRadix=bnpFromRadix;proto.fromNumber=bnpFromNumber;proto.bitwiseTo=bnpBitwiseTo;proto.changeBit=bnpChangeBit;proto.addTo=bnpAddTo;proto.dMultiply=bnpDMultiply;proto.dAddOffset=bnpDAddOffset;proto.multiplyLowerTo=bnpMultiplyLowerTo;proto.multiplyUpperTo=bnpMultiplyUpperTo;proto.modInt=bnpModInt;proto.clone=bnClone;proto.intValue=bnIntValue;proto.byteValue=bnByteValue;proto.shortValue=bnShortValue;proto.signum=bnSigNum;proto.toByteArray=bnToByteArray;proto.equals=bnEquals;proto.min=bnMin;proto.max=bnMax;proto.and=bnAnd;proto.or=bnOr;proto.xor=bnXor;proto.andNot=bnAndNot;proto.not=bnNot;proto.shiftLeft=bnShiftLeft;proto.shiftRight=bnShiftRight;proto.getLowestSetBit=bnGetLowestSetBit;proto.bitCount=bnBitCount;proto.testBit=bnTestBit;proto.setBit=bnSetBit;proto.clearBit=bnClearBit;proto.flipBit=bnFlipBit;proto.add=bnAdd;proto.subtract=bnSubtract;proto.multiply=bnMultiply;proto.divide=bnDivide;proto.remainder=bnRemainder;proto.divideAndRemainder=bnDivideAndRemainder;proto.modPow=bnModPow;proto.modInverse=bnModInverse;proto.pow=bnPow;proto.gcd=bnGCD;proto.square=bnSquare;BigInteger.ZERO=nbv(0);BigInteger.ONE=nbv(1);BigInteger.valueOf=nbv;BigInteger.fromByteArrayUnsigned=function(ba){if(!ba.length){return new BigInteger.valueOf(0)}else if(ba[0]&128){return new BigInteger([0].concat(ba))}else{return new BigInteger(ba)}};BigInteger.fromByteArraySigned=function(ba){if(ba[0]&128){ba[0]&=127;return BigInteger.fromByteArrayUnsigned(ba).negate()}else{return BigInteger.fromByteArrayUnsigned(ba)}};BigInteger.prototype.toByteArrayUnsigned=function(){var ba=this.abs().toByteArray();if(!ba.length){return ba}if(ba[0]===0){ba=ba.slice(1)}for(var i=0;i>8&255;rng_pool[rng_pptr++]^=x>>16&255;rng_pool[rng_pptr++]^=x>>24&255;if(rng_pptr>=rng_psize)rng_pptr-=rng_psize}function rng_seed_time(){rng_seed_int((new Date).getTime())}if(rng_pool==null){rng_pool=new Array;rng_pptr=0;var t;while(rng_pptr>>8;rng_pool[rng_pptr++]=t&255}rng_pptr=0;rng_seed_time()}function rng_get_byte(){if(rng_state==null){rng_seed_time();rng_state=prng_newstate();rng_state.init(rng_pool);for(rng_pptr=0;rng_pptr=240){opcode=opcode<<8|this.buffer[i++]}var len;if(opcode>0&&opcode>>8&255)}else{this.buffer.push(Opcode.map.OP_PUSHDATA4);this.buffer.push(data.length&255);this.buffer.push(data.length>>>8&255);this.buffer.push(data.length>>>16&255);this.buffer.push(data.length>>>24&255)}this.buffer=this.buffer.concat(data);this.chunks.push(data)};Script.createOutputScript=function(address){var script=new Script;address=new Address(address);if(!address.version){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)}else{script.writeOp(Opcode.map.OP_HASH160);script.writeBytes(address.hash);script.writeOp(Opcode.map.OP_EQUAL)}return script};Script.prototype.extractPubkeys=function(){return this.chunks.filter(function(chunk){return chunk[0]==4&&chunk.length==65||chunk[0]<4&&chunk.length==33})};Script.createMultiSigOutputScript=function(m,pubkeys){var script=new Script;pubkeys=pubkeys.sort();script.writeOp(Opcode.map.OP_1+m-1);for(var i=0;i65){var args=arguments[0].split(":");return this.addInput(args[0],args[1])}else{this.ins.push(new TransactionIn({outpoint:{hash:tx.hash||tx,index:outIndex},script:new Script,sequence:4294967295}))}};Transaction.prototype.addOutput=function(address,value){if(arguments[0]instanceof TransactionOut){this.outs.push(arguments[0]);return}if(arguments[0].indexOf(":")>=0){var args=arguments[0].split(":");address=args[0];value=parseInt(args[1])}this.outs.push(new TransactionOut({value:value,script:Script.createOutputScript(address)}))};Transaction.prototype.serialize=function(){var buffer=[];buffer=buffer.concat(util.numToBytes(parseInt(this.version),4));buffer=buffer.concat(util.numToVarInt(this.ins.length));for(var i=0;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);var analysis={};analysis.impact=impact;if(impact.sign>0&&impact.value>0){analysis.type="recv";analysis.addr=new Address(firstMeRecvHash)}else if(allFromMe&&allToMe){analysis.type="self"}else if(allFromMe){analysis.type="sent";analysis.addr=new Address(firstRecvHash)}else{analysis.type="other"}return analysis};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""}};Transaction.prototype.getTotalOutValue=function(){return this.outs.reduce(function(t,o){return t+o.value},0)};Transaction.prototype.getTotalValue=Transaction.prototype.getTotalOutValue;Transaction.prototype.calcImpact=function(wallet){if(!(wallet instanceof Wallet))return 0;var valueOut=this.outs.filter(function(o){return wallet.hasHash(conv.bytesToHex(o.script.simpleOutPubKeyHash()))}).reduce(function(t,o){return t+o.value},0);var valueIn=this.ins.filter(function(i){return wallet.hasHash(conv.bytesToHex(i.script.simpleInPubKeyHash()))&&wallet.txIndex[i.outpoint.hash]}).reduce(function(t,i){return t+wallet.txIndex[i.outpoint.hash].outs[i.outpoint.index].value},0);if(valueOut>valueIn){return{sign:1,value:valueOut-valueIn}}else{return{sign:-1,value:valueIn-valueOut}}};Transaction.deserialize=function(buffer){if(typeof buffer=="string"){buffer=conv.hexToBytes(buffer)}var pos=0;var readAsInt=function(bytes){if(bytes==0)return 0;pos++;return buffer[pos-1]+readAsInt(bytes-1)*256};var readVarInt=function(){pos++;if(buffer[pos-1]<253){return buffer[pos-1]}return readAsInt(buffer[pos-1]-251)};var readBytes=function(bytes){pos+=bytes;return buffer.slice(pos-bytes,pos)};var readVarString=function(){var size=readVarInt();return readBytes(size)};var obj={ins:[],outs:[]};obj.version=readAsInt(4);var ins=readVarInt();for(var i=0;i0)this.address=this.script.toAddress();this.value=util.isArray(data.value)?util.bytesToNum(data.value):"string"==typeof data.value?parseInt(data.value):data.value instanceof BigInteger?parseInt(data.value.toString()):data.value};TransactionOut.prototype.clone=function(){var newTxout=new TransactionOut({script:this.script.clone(),value:this.value});return newTxout};module.exports.Transaction=Transaction;module.exports.TransactionIn=TransactionIn;module.exports.TransactionOut=TransactionOut},{"./address":1,"./convert":4,"./crypto-js/crypto":5,"./ecdsa":10,"./eckey":11,"./jsbn/jsbn":14,"./script":20,"./util":22,"./wallet":23}],22:[function(require,module,exports){var BigInteger=require("./jsbn/jsbn");var Crypto=require("./crypto-js/crypto");module.exports={isArray:Array.isArray||function(o){return Object.prototype.toString.call(o)==="[object Array]"},numToBytes:function(num,bytes){if(bytes===undefined)bytes=8;if(bytes==0)return[];else return[num%256].concat(module.exports.numToBytes(Math.floor(num/256),bytes-1))},bytesToNum:function(bytes){if(bytes.length==0)return 0;else return bytes[0]+256*module.exports.bytesToNum(bytes.slice(1))},numToVarInt:function(num){var m=module.exports;if(num<253)return[num];else if(num<65536)return[253].concat(m.numToBytes(num,2));else if(num<4294967296)return[254].concat(m.numToBytes(num,4));else return[253].concat(m.numToBytes(num,8))},bytesToWords:function(bytes){var words=[];for(var i=0,b=0;i>>5]|=bytes[i]<<24-b%32}return words},wordsToBytes:function(words){var bytes=[];for(var b=0;b>>5]>>>24-b%32&255)}return bytes},sha256ripe160:function(data){return Crypto.RIPEMD160(Crypto.SHA256(data,{asBytes:true}),{asBytes:true})},error:function(msg){throw new Error(msg)}}},{"./crypto-js/crypto":5,"./jsbn/jsbn":14}],23:[function(require,module,exports){var Script=require("./script");var ECKey=require("./eckey").ECKey;var conv=require("./convert");var util=require("./util");var BigInteger=require("./jsbn/jsbn");var BIP32key=require("./bip32");var Transaction=require("./transaction").Transaction;var TransactionIn=require("./transaction").TransactionIn;var TransactionOut=require("./transaction").TransactionOut;var SecureRandom=require("./jsbn/rng");var rng=new SecureRandom;var Wallet=function(seed){var keys=[];var masterkey=null;var me=this;this.addresses=[];this.outputs={};this.newMasterKey=function(seed){if(!seed){var seedBytes=new Array(32);rng.nextBytes(seedBytes);seed=conv.bytesToString(seedBytes)}masterkey=new BIP32key(seed);keys=[]};this.newMasterKey(seed);this.generateAddress=function(){keys.push(masterkey.ckd(keys.length).key);this.addresses.push(keys[keys.length-1].getBitcoinAddress().toString());return this.addresses[this.addresses.length-1]};this.processTx=function(tx,verified){var txhash=conv.bytesToHex(tx.getHash());for(var i=0;i=0){me.outputs[txhash+":"+i]={output:txhash+":"+i,value:tx.outs[i].value,address:tx.outs[i].address.toString(),timestamp:(new Date).getTime()/1e3,pending:true}}}for(var i=0;io.timestamp+1200)delete this.outputs[o];if(o.spendpending&&t>o.timestamp+1200){o.spendpending=false;o.spend=false;delete o.timestamp}}};var peoInterval=setInterval(this.processExistingOutputs,1e4);this.getUtxoToPay=function(value){var h=[];for(var out in this.outputs)h.push(this.outputs[out]);var utxo=h.filter(function(x){return!x.spend});var valuecompare=function(a,b){return a.value>b.value};var high=utxo.filter(function(o){return o.value>=value}).sort(valuecompare);if(high.length>0)return[high[0]];utxo.sort(valuecompare);var totalval=0;for(var i=0;i=value)return utxo.slice(0,i+1)}throw"Not enough money to send funds including transaction fee. Have: "+totalval/1e8+", needed: "+value/1e8};this.mkSend=function(to,value,fee){var utxo=this.getUtxoToPay(value+fee);var sum=utxo.reduce(function(t,o){return t+o.value},0),remainder=sum-value-fee;if(value<5430)throw new Error("Amount below dust threshold!");var unspentOuts=0;for(var o in this.outputs){if(!this.outputs[o].spend)unspentOuts+=1;if(unspentOuts>=5)return}var change=this.addresses[this.addresses.length-1];var toOut={address:to,value:value},changeOut={address:change,value:remainder};halfChangeOut={address:change,value:Math.floor(remainder/2)};var outs=remainder<5430?[toOut]:remainder<10860?[toOut,changeOut]:unspentOuts==5?[toOut,changeOut]:[toOut,halfChangeOut,halfChangeOut];var tx=new Bitcoin.Transaction({ins:utxo.map(function(x){return x.output}),outs:outs});this.sign(tx);return tx};this.mkSendToOutputs=function(outputs,changeIndex,fee){var value=outputs.reduce(function(t,o){return t+o.value},0),utxo=this.getUtxoToPay(value+fee),sum=utxo.reduce(function(t,p){return t+o.value},0);utxo[changeIndex].value+=sum-value-fee;var tx=new Bitcoin.Transaction({ins:utxo.map(function(x){return x.output}),outs:outputs});this.sign(tx);return tx};this.sign=function(tx){tx.ins.map(function(inp,i){var inp=inp.outpoint.hash+":"+inp.outpoint.index;if(me.outputs[inp]){var address=me.outputs[inp].address,ind=me.addresses.indexOf(address);if(ind>=0){var key=keys[ind];tx.sign(ind,key)}}});return tx};this.getMasterKey=function(){return masterkey};this.getPrivateKey=function(index){if(typeof index=="string")return keys.filter(function(i,k){return addresses[i]==index})[0];else return keys[index]};this.getPrivateKeys=function(){return keys}};module.exports=Wallet},{"./bip32":3,"./convert":4,"./eckey":11,"./jsbn/jsbn":14,"./jsbn/rng":16,"./script":20,"./transaction":21,"./util":22}]},{},[12])(12)}); \ No newline at end of file diff --git a/build/bitcoinjs-exit-min.js b/build/bitcoinjs-exit-min.js deleted file mode 100644 index 7de2757..0000000 --- a/build/bitcoinjs-exit-min.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * BitcoinJS-lib v0.1.0-default - * Copyright (c) 2011 BitcoinJS Project - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. - */ -(function(a,b,c,d,e){function f(a,b,c){this.setUri(a,b,c),this.unique=1,this.connected=!1,this.callbacks=[]}a.ExitNode=f,b.EventEmitter.augment(f),f.prototype.setUri=function(a,b,c){this.uri=(c?"https://":"http://")+a+":"+b},f.prototype.connect=function(a){this.wallet=a,delete c.sockets[this.uri],c.j=[],this.socket=c.connect(this.uri),this.socket.on("connect",d.proxy(this.handleConnect,this)),this.socket.on("error",function(){console.log("error, test")}),this.socket.on("message",d.proxy(this.handleMessage,this)),this.socket.on("disconnect",d.proxy(this.handleDisconnect,this))},f.prototype.disconnect=function(){this.socket&&(this.socket.disconnect(),this.socket=null,this.connected=!1),this.trigger("connectStatus",{status:"unknown"})},f.prototype.call=function(a,b,c){this.socket.send(d.toJSON({method:a,params:[b],id:this.unique})),c&&(this.callbacks[this.unique]=c),this.unique++},f.prototype.handleConnect=function(){var a=this;this.connected=!0},f.prototype.listen=function(a){self.call("pubkeysRegister",{keys:a.join(",")},function(a,b){if(a){console.error("Could not register public keys");return}self.call("pubkeysListen",{handle:b.handle},function(a,b){self.trigger("blockInit",{height:b.height}),self.trigger("txData",{confirmed:!0,txs:b.txs}),self.trigger("connectStatus",{status:"ok"})}),self.call("pubkeysUnconfirmed",{handle:b.handle},function(a,b){self.trigger("txData",{confirmed:!1,txs:b.txs})})})},f.prototype.handleMessage=function(a){"undefined"!=typeof a.result&&"function"==typeof this.callbacks[a.id]?this.callbacks[a.id](a.error,a.result):"undefined"!=typeof a.method&&this.trigger(a.method,a.params[0])},f.prototype.handleDisconnect=function(){},f.prototype.query=function(a,b,c,e){"function"==typeof c&&(e=c,c=!1),b=b||{},e="function"==typeof e?e:function(){};var f=this.uri+"/"+a;c&&(f+="?callback=?"),d.getJSON(f,b,e)}})("undefined"!=typeof Bitcoin?Bitcoin:module.exports,"undefined"!=typeof Bitcoin?Bitcoin:require("bitcoinjs-lib"),"undefined"!=typeof io?io:require("io"),jQuery,this) diff --git a/build/bitcoinjs-min.js b/build/bitcoinjs-min.js deleted file mode 100644 index 5124d8d..0000000 --- a/build/bitcoinjs-min.js +++ /dev/null @@ -1,28 +0,0 @@ -/** - * BitcoinJS-lib v0.1.3-default - * Copyright (c) 2011 BitcoinJS Project - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. - */ -(function(){var e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",t=window.Crypto={},n=t.util={rotl:function(e,t){return e<>>32-t},rotr:function(e,t){return e<<32-t|e>>>t},endian:function(e){if(e.constructor==Number)return n.rotl(e,8)&16711935|n.rotl(e,24)&4278255360;for(var t=0;t0;e--)t.push(Math.floor(Math.random()*256));return t},bytesToWords:function(e){for(var t=[],n=0,r=0;n>>5]|=e[n]<<24-r%32;return t},wordsToBytes:function(e){for(var t=[],n=0;n>>5]>>>24-n%32&255);return t},bytesToHex:function(e){for(var t=[],n=0;n>>4).toString(16)),t.push((e[n]&15).toString(16));return t.join("")},hexToBytes:function(e){for(var t=[],n=0;n>>6*(3-o)&63)):n.push("=")}return n.join("")},base64ToBytes:function(t){if(typeof atob=="function")return s.stringToBytes(atob(t));t=t.replace(/[^A-Z0-9+\/]/ig,"");for(var n=[],r=0,i=0;r>>6-i*2)}return n}};t.mode={};var r=t.charenc={},i=r.UTF8={stringToBytes:function(e){return s.stringToBytes(unescape(encodeURIComponent(e)))},bytesToString:function(e){return decodeURIComponent(escape(s.bytesToString(e)))}},s=r.Binary={stringToBytes:function(e){for(var t=[],n=0;n>5]|=128<<24-i%32,n[(i+64>>9<<4)+15]=i;for(var m=0;m>>7)^(w<<14|w>>>18)^w>>>3,x=(E<<15|E>>>17)^(E<<13|E>>>19)^E>>>10;u[g]=S+(u[g-7]>>>0)+x+(u[g-16]>>>0)}var T=h&p^~h&d,N=a&f^a&l^f&l,C=(a<<30|a>>>2)^(a<<19|a>>>13)^(a<<10|a>>>22),k=(h<<26|h>>>6)^(h<<21|h>>>11)^(h<<7|h>>>25);y=(v>>>0)+k+T+s[g]+(u[g]>>>0),b=C+N,v=d,d=p,p=h,h=c+y,c=l,l=f,f=a,a=y+b}o[0]+=a,o[1]+=f,o[2]+=l,o[3]+=c,o[4]+=h,o[5]+=p,o[6]+=d,o[7]+=v}return o},o._blocksize=16})(); -(function(){function o(e,t,n,r){return 0<=e&&e<=15?t^n^r:16<=e&&e<=31?t&n|~t&r:32<=e&&e<=47?(t|~n)^r:48<=e&&e<=63?t&r|n&~r:64<=e&&e<=79?t^(n|~r):"rmd160_f: j out of range"}function u(e){return 0<=e&&e<=15?0:16<=e&&e<=31?1518500249:32<=e&&e<=47?1859775393:48<=e&&e<=63?2400959708:64<=e&&e<=79?2840853838:"rmd160_K1: j out of range"}function a(e){return 0<=e&&e<=15?1352829926:16<=e&&e<=31?1548603684:32<=e&&e<=47?1836072691:48<=e&&e<=63?2053994217:64<=e&&e<=79?0:"rmd160_K2: j out of range"}function p(e,t){var n=(e&65535)+(t&65535),r=(e>>16)+(t>>16)+(n>>16);return r<<16|n&65535}function d(e,t){return e<>>32-t}var e=Crypto,t=e.util,n=e.charenc,r=n.UTF8,i=n.Binary;t.bytesToLWords=function(e){var t=Array(e.length>>2);for(var n=0;n>5]|=(e[n/8]&255)<>5]>>>n%32&255);return t};var s=e.RIPEMD160=function(e,n){var r=t.lWordsToBytes(s._rmd160(e));return n&&n.asBytes?r:n&&n.asString?i.bytesToString(r):t.bytesToHex(r)};s._rmd160=function(e){e.constructor==String&&(e=r.stringToBytes(e));var n=t.bytesToLWords(e),i=e.length*8;n[i>>5]|=128<>>9<<4)+14]=i;var s=1732584193,v=4023233417,m=2562383102,g=271733878,y=3285377520;for(var b=0;b>8&255,rng_pool[rng_pptr++]^=e>>16&255,rng_pool[rng_pptr++]^=e>>24&255,rng_pptr>=rng_psize&&(rng_pptr-=rng_psize)}function rng_seed_time(){rng_seed_int((new Date).getTime())}function rng_get_byte(){if(rng_state==null){rng_seed_time(),rng_state=prng_newstate(),rng_state.init(rng_pool);for(rng_pptr=0;rng_pptr>>8,rng_pool[rng_pptr++]=t&255;rng_pptr=0,rng_seed_time()}SecureRandom.prototype.nextBytes=rng_get_bytes; -function BigInteger(e,t,n){e!=null&&("number"==typeof e?this.fromNumber(e,t,n):t==null&&"string"!=typeof e?this.fromString(e,256):this.fromString(e,t))}function nbi(){return new BigInteger(null)}function am1(e,t,n,r,i,s){while(--s>=0){var o=t*this[e++]+n[r]+i;i=Math.floor(o/67108864),n[r++]=o&67108863}return i}function am2(e,t,n,r,i,s){var o=t&32767,u=t>>15;while(--s>=0){var a=this[e]&32767,f=this[e++]>>15,l=u*a+f*o;a=o*a+((l&32767)<<15)+n[r]+(i&1073741823),i=(a>>>30)+(l>>>15)+u*f+(i>>>30),n[r++]=a&1073741823}return i}function am3(e,t,n,r,i,s){var o=t&16383,u=t>>14;while(--s>=0){var a=this[e]&16383,f=this[e++]>>14,l=u*a+f*o;a=o*a+((l&16383)<<14)+n[r]+i,i=(a>>28)+(l>>14)+u*f,n[r++]=a&268435455}return i}function int2char(e){return BI_RM.charAt(e)}function intAt(e,t){var n=BI_RC[e.charCodeAt(t)];return n==null?-1:n}function bnpCopyTo(e){for(var t=this.t-1;t>=0;--t)e[t]=this[t];e.t=this.t,e.s=this.s}function bnpFromInt(e){this.t=1,this.s=e<0?-1:0,e>0?this[0]=e:e<-1?this[0]=e+DV:this.t=0}function nbv(e){var t=nbi();return t.fromInt(e),t}function bnpFromString(e,t){var n;if(t==16)n=4;else if(t==8)n=3;else if(t==256)n=8;else if(t==2)n=1;else if(t==32)n=5;else{if(t!=4){this.fromRadix(e,t);return}n=2}this.t=0,this.s=0;var r=e.length,i=!1,s=0;while(--r>=0){var o=n==8?e[r]&255:intAt(e,r);if(o<0){e.charAt(r)=="-"&&(i=!0);continue}i=!1,s==0?this[this.t++]=o:s+n>this.DB?(this[this.t-1]|=(o&(1<>this.DB-s):this[this.t-1]|=o<=this.DB&&(s-=this.DB)}n==8&&(e[0]&128)!=0&&(this.s=-1,s>0&&(this[this.t-1]|=(1<0&&this[this.t-1]==e)--this.t}function bnToString(e){if(this.s<0)return"-"+this.negate().toString(e);var t;if(e==16)t=4;else if(e==8)t=3;else if(e==2)t=1;else if(e==32)t=5;else{if(e!=4)return this.toRadix(e);t=2}var n=(1<0){u>u)>0&&(i=!0,s=int2char(r));while(o>=0)u>(u+=this.DB-t)):(r=this[o]>>(u-=t)&n,u<=0&&(u+=this.DB,--o)),r>0&&(i=!0),i&&(s+=int2char(r))}return i?s:"0"}function bnNegate(){var e=nbi();return BigInteger.ZERO.subTo(this,e),e}function bnAbs(){return this.s<0?this.negate():this}function bnCompareTo(e){var t=this.s-e.s;if(t!=0)return t;var n=this.t;t=n-e.t;if(t!=0)return this.s<0?-t:t;while(--n>=0)if((t=this[n]-e[n])!=0)return t;return 0}function nbits(e){var t=1,n;return(n=e>>>16)!=0&&(e=n,t+=16),(n=e>>8)!=0&&(e=n,t+=8),(n=e>>4)!=0&&(e=n,t+=4),(n=e>>2)!=0&&(e=n,t+=2),(n=e>>1)!=0&&(e=n,t+=1),t}function bnBitLength(){return this.t<=0?0:this.DB*(this.t-1)+nbits(this[this.t-1]^this.s&this.DM)}function bnpDLShiftTo(e,t){var n;for(n=this.t-1;n>=0;--n)t[n+e]=this[n];for(n=e-1;n>=0;--n)t[n]=0;t.t=this.t+e,t.s=this.s}function bnpDRShiftTo(e,t){for(var n=e;n=0;--u)t[u+s+1]=this[u]>>r|o,o=(this[u]&i)<=0;--u)t[u]=0;t[s]=o,t.t=this.t+s+1,t.s=this.s,t.clamp()}function bnpRShiftTo(e,t){t.s=this.s;var n=Math.floor(e/this.DB);if(n>=this.t){t.t=0;return}var r=e%this.DB,i=this.DB-r,s=(1<>r;for(var o=n+1;o>r;r>0&&(t[this.t-n-1]|=(this.s&s)<>=this.DB;if(e.t>=this.DB;r+=this.s}else{r+=this.s;while(n>=this.DB;r-=e.s}t.s=r<0?-1:0,r<-1?t[n++]=this.DV+r:r>0&&(t[n++]=r),t.t=n,t.clamp()}function bnpMultiplyTo(e,t){var n=this.abs(),r=e.abs(),i=n.t;t.t=i+r.t;while(--i>=0)t[i]=0;for(i=0;i=0)e[n]=0;for(n=0;n=t.DV&&(e[n+t.t]-=t.DV,e[n+t.t+1]=1)}e.t>0&&(e[e.t-1]+=t.am(n,t[n],e,2*n,0,1)),e.s=0,e.clamp()}function bnpDivRemTo(e,t,n){var r=e.abs();if(r.t<=0)return;var i=this.abs();if(i.t0?(r.lShiftTo(a,s),i.lShiftTo(a,n)):(r.copyTo(s),i.copyTo(n));var f=s.t,l=s[f-1];if(l==0)return;var c=l*(1<1?s[f-2]>>this.F2:0),h=this.FV/c,p=(1<=0&&(n[n.t++]=1,n.subTo(g,n)),BigInteger.ONE.dlShiftTo(f,g),g.subTo(s,s);while(s.t=0){var y=n[--v]==l?this.DM:Math.floor(n[v]*h+(n[v-1]+d)*p);if((n[v]+=s.am(0,y,n,m,0,f))0&&n.rShiftTo(a,n),o<0&&BigInteger.ZERO.subTo(n,n)}function bnMod(e){var t=nbi();return this.abs().divRemTo(e,null,t),this.s<0&&t.compareTo(BigInteger.ZERO)>0&&e.subTo(t,t),t}function Classic(e){this.m=e}function cConvert(e){return e.s<0||e.compareTo(this.m)>=0?e.mod(this.m):e}function cRevert(e){return e}function cReduce(e){e.divRemTo(this.m,null,e)}function cMulTo(e,t,n){e.multiplyTo(t,n),this.reduce(n)}function cSqrTo(e,t){e.squareTo(t),this.reduce(t)}function bnpInvDigit(){if(this.t<1)return 0;var e=this[0];if((e&1)==0)return 0;var t=e&3;return t=t*(2-(e&15)*t)&15,t=t*(2-(e&255)*t)&255,t=t*(2-((e&65535)*t&65535))&65535,t=t*(2-e*t%this.DV)%this.DV,t>0?this.DV-t:-t}function Montgomery(e){this.m=e,this.mp=e.invDigit(),this.mpl=this.mp&32767,this.mph=this.mp>>15,this.um=(1<0&&this.m.subTo(t,t),t}function montRevert(e){var t=nbi();return e.copyTo(t),this.reduce(t),t}function montReduce(e){while(e.t<=this.mt2)e[e.t++]=0;for(var t=0;t>15)*this.mpl&this.um)<<15)&e.DM;n=t+this.m.t,e[n]+=this.m.am(0,r,e,t,0,this.m.t);while(e[n]>=e.DV)e[n]-=e.DV,e[++n]++}e.clamp(),e.drShiftTo(this.m.t,e),e.compareTo(this.m)>=0&&e.subTo(this.m,e)}function montSqrTo(e,t){e.squareTo(t),this.reduce(t)}function montMulTo(e,t,n){e.multiplyTo(t,n),this.reduce(n)}function bnpIsEven(){return(this.t>0?this[0]&1:this.s)==0}function bnpExp(e,t){if(e>4294967295||e<1)return BigInteger.ONE;var n=nbi(),r=nbi(),i=t.convert(this),s=nbits(e)-1;i.copyTo(n);while(--s>=0){t.sqrTo(n,r);if((e&1<0)t.mulTo(r,i,n);else{var o=n;n=r,r=o}}return t.revert(n)}function bnModPowInt(e,t){var n;return e<256||t.isEven()?n=new Classic(t):n=new Montgomery(t),this.exp(e,n)}var dbits,canary=0xdeadbeefcafe,j_lm=(canary&16777215)==15715070;j_lm&&navigator.appName=="Microsoft Internet Explorer"?(BigInteger.prototype.am=am2,dbits=30):j_lm&&navigator.appName!="Netscape"?(BigInteger.prototype.am=am1,dbits=26):(BigInteger.prototype.am=am3,dbits=28),BigInteger.prototype.DB=dbits,BigInteger.prototype.DM=(1<>24}function bnShortValue(){return this.t==0?this.s:this[0]<<16>>16}function bnpChunkSize(e){return Math.floor(Math.LN2*this.DB/Math.log(e))}function bnSigNum(){return this.s<0?-1:this.t<=0||this.t==1&&this[0]<=0?0:1}function bnpToRadix(e){e==null&&(e=10);if(this.signum()==0||e<2||e>36)return"0";var t=this.chunkSize(e),n=Math.pow(e,t),r=nbv(n),i=nbi(),s=nbi(),o="";this.divRemTo(r,i,s);while(i.signum()>0)o=(n+s.intValue()).toString(e).substr(1)+o,i.divRemTo(r,i,s);return s.intValue().toString(e)+o}function bnpFromRadix(e,t){this.fromInt(0),t==null&&(t=10);var n=this.chunkSize(t),r=Math.pow(t,n),i=!1,s=0,o=0;for(var u=0;u=n&&(this.dMultiply(r),this.dAddOffset(o,0),s=0,o=0)}s>0&&(this.dMultiply(Math.pow(t,s)),this.dAddOffset(o,0)),i&&BigInteger.ZERO.subTo(this,this)}function bnpFromNumber(e,t,n){if("number"==typeof t)if(e<2)this.fromInt(1);else{this.fromNumber(e,n),this.testBit(e-1)||this.bitwiseTo(BigInteger.ONE.shiftLeft(e-1),op_or,this),this.isEven()&&this.dAddOffset(1,0);while(!this.isProbablePrime(t))this.dAddOffset(2,0),this.bitLength()>e&&this.subTo(BigInteger.ONE.shiftLeft(e-1),this)}else{var r=new Array,i=e&7;r.length=(e>>3)+1,t.nextBytes(r),i>0?r[0]&=(1<0){n>n)!=(this.s&this.DM)>>n&&(t[i++]=r|this.s<=0){n<8?(r=(this[e]&(1<>(n+=this.DB-8)):(r=this[e]>>(n-=8)&255,n<=0&&(n+=this.DB,--e)),(r&128)!=0&&(r|=-256),i==0&&(this.s&128)!=(r&128)&&++i;if(i>0||r!=this.s)t[i++]=r}}return t}function bnEquals(e){return this.compareTo(e)==0}function bnMin(e){return this.compareTo(e)<0?this:e}function bnMax(e){return this.compareTo(e)>0?this:e}function bnpBitwiseTo(e,t,n){var r,i,s=Math.min(e.t,this.t);for(r=0;r>=16,t+=16),(e&255)==0&&(e>>=8,t+=8),(e&15)==0&&(e>>=4,t+=4),(e&3)==0&&(e>>=2,t+=2),(e&1)==0&&++t,t}function bnGetLowestSetBit(){for(var e=0;e=this.t?this.s!=0:(this[t]&1<>=this.DB;if(e.t>=this.DB;r+=this.s}else{r+=this.s;while(n>=this.DB;r+=e.s}t.s=r<0?-1:0,r>0?t[n++]=r:r<-1&&(t[n++]=this.DV+r),t.t=n,t.clamp()}function bnAdd(e){var t=nbi();return this.addTo(e,t),t}function bnSubtract(e){var t=nbi();return this.subTo(e,t),t}function bnMultiply(e){var t=nbi();return this.multiplyTo(e,t),t}function bnSquare(){var e=nbi();return this.squareTo(e),e}function bnDivide(e){var t=nbi();return this.divRemTo(e,t,null),t}function bnRemainder(e){var t=nbi();return this.divRemTo(e,null,t),t}function bnDivideAndRemainder(e){var t=nbi(),n=nbi();return this.divRemTo(e,t,n),new Array(t,n)}function bnpDMultiply(e){this[this.t]=this.am(0,e-1,this,0,0,this.t),++this.t,this.clamp()}function bnpDAddOffset(e,t){if(e==0)return;while(this.t<=t)this[this.t++]=0;this[t]+=e;while(this[t]>=this.DV)this[t]-=this.DV,++t>=this.t&&(this[this.t++]=0),++this[t]}function NullExp(){}function nNop(e){return e}function nMulTo(e,t,n){e.multiplyTo(t,n)}function nSqrTo(e,t){e.squareTo(t)}function bnPow(e){return this.exp(e,new NullExp)}function bnpMultiplyLowerTo(e,t,n){var r=Math.min(this.t+e.t,t);n.s=0,n.t=r;while(r>0)n[--r]=0;var i;for(i=n.t-this.t;r=0)n[r]=0;for(r=Math.max(t-this.t,0);r2*this.m.t)return e.mod(this.m);if(e.compareTo(this.m)<0)return e;var t=nbi();return e.copyTo(t),this.reduce(t),t}function barrettRevert(e){return e}function barrettReduce(e){e.drShiftTo(this.m.t-1,this.r2),e.t>this.m.t+1&&(e.t=this.m.t+1,e.clamp()),this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3),this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);while(e.compareTo(this.r2)<0)e.dAddOffset(1,this.m.t+1);e.subTo(this.r2,e);while(e.compareTo(this.m)>=0)e.subTo(this.m,e)}function barrettSqrTo(e,t){e.squareTo(t),this.reduce(t)}function barrettMulTo(e,t,n){e.multiplyTo(t,n),this.reduce(n)}function bnModPow(e,t){var n=e.bitLength(),r,i=nbv(1),s;if(n<=0)return i;n<18?r=1:n<48?r=3:n<144?r=4:n<768?r=5:r=6,n<8?s=new Classic(t):t.isEven()?s=new Barrett(t):s=new Montgomery(t);var o=new Array,u=3,a=r-1,f=(1<1){var l=nbi();s.sqrTo(o[1],l);while(u<=f)o[u]=nbi(),s.mulTo(l,o[u-2],o[u]),u+=2}var c=e.t-1,h,p=!0,d=nbi(),v;n=nbits(e[c])-1;while(c>=0){n>=a?h=e[c]>>n-a&f:(h=(e[c]&(1<0&&(h|=e[c-1]>>this.DB+n-a)),u=r;while((h&1)==0)h>>=1,--u;(n-=u)<0&&(n+=this.DB,--c);if(p)o[h].copyTo(i),p=!1;else{while(u>1)s.sqrTo(i,d),s.sqrTo(d,i),u-=2;u>0?s.sqrTo(i,d):(v=i,i=d,d=v),s.mulTo(d,o[h],i)}while(c>=0&&(e[c]&1<0&&(t.rShiftTo(s,t),n.rShiftTo(s,n));while(t.signum()>0)(i=t.getLowestSetBit())>0&&t.rShiftTo(i,t),(i=n.getLowestSetBit())>0&&n.rShiftTo(i,n),t.compareTo(n)>=0?(t.subTo(n,t),t.rShiftTo(1,t)):(n.subTo(t,n),n.rShiftTo(1,n));return s>0&&n.lShiftTo(s,n),n}function bnpModInt(e){if(e<=0)return 0;var t=this.DV%e,n=this.s<0?e-1:0;if(this.t>0)if(t==0)n=this[0]%e;else for(var r=this.t-1;r>=0;--r)n=(t*n+this[r])%e;return n}function bnModInverse(e){var t=e.isEven();if(this.isEven()&&t||e.signum()==0)return BigInteger.ZERO;var n=e.clone(),r=this.clone(),i=nbv(1),s=nbv(0),o=nbv(0),u=nbv(1);while(n.signum()!=0){while(n.isEven()){n.rShiftTo(1,n);if(t){if(!i.isEven()||!s.isEven())i.addTo(this,i),s.subTo(e,s);i.rShiftTo(1,i)}else s.isEven()||s.subTo(e,s);s.rShiftTo(1,s)}while(r.isEven()){r.rShiftTo(1,r);if(t){if(!o.isEven()||!u.isEven())o.addTo(this,o),u.subTo(e,u);o.rShiftTo(1,o)}else u.isEven()||u.subTo(e,u);u.rShiftTo(1,u)}n.compareTo(r)>=0?(n.subTo(r,n),t&&i.subTo(o,i),s.subTo(u,s)):(r.subTo(n,r),t&&o.subTo(i,o),u.subTo(s,u))}return r.compareTo(BigInteger.ONE)!=0?BigInteger.ZERO:u.compareTo(e)>=0?u.subtract(e):u.signum()<0?(u.addTo(e,u),u.signum()<0?u.add(e):u):u}function bnIsProbablePrime(e){var t,n=this.abs();if(n.t==1&&n[0]<=lowprimes[lowprimes.length-1]){for(t=0;t>1,e>lowprimes.length&&(e=lowprimes.length);var i=nbi();for(var s=0;s0;--s){i=i.twice();var o=n.testBit(s),u=t.testBit(s);o!=u&&(i=i.add(o?this:r))}return i}function pointFpMultiplyTwo(e,t,n){var r;e.bitLength()>n.bitLength()?r=e.bitLength()-1:r=n.bitLength()-1;var i=this.curve.getInfinity(),s=this.add(t);while(r>=0)i=i.twice(),e.testBit(r)?n.testBit(r)?i=i.add(s):i=i.add(this):n.testBit(r)&&(i=i.add(t)),--r;return i}function ECCurveFp(e,t,n){this.q=e,this.a=this.fromBigInteger(t),this.b=this.fromBigInteger(n),this.infinity=new ECPointFp(this,null,null)}function curveFpGetQ(){return this.q}function curveFpGetA(){return this.a}function curveFpGetB(){return this.b}function curveFpEquals(e){return e==this?!0:this.q.equals(e.q)&&this.a.equals(e.a)&&this.b.equals(e.b)}function curveFpGetInfinity(){return this.infinity}function curveFpFromBigInteger(e){return new ECFieldElementFp(this.q,e)}function curveFpDecodePointHex(e){switch(parseInt(e.substr(0,2),16)){case 0:return this.infinity;case 2:case 3:return null;case 4:case 6:case 7:var t=(e.length-2)/2,n=e.substr(2,t),r=e.substr(t+2,t);return new ECPointFp(this,this.fromBigInteger(new BigInteger(n,16)),this.fromBigInteger(new BigInteger(r,16)));default:return null}}ECFieldElementFp.prototype.equals=feFpEquals,ECFieldElementFp.prototype.toBigInteger=feFpToBigInteger,ECFieldElementFp.prototype.negate=feFpNegate,ECFieldElementFp.prototype.add=feFpAdd,ECFieldElementFp.prototype.subtract=feFpSubtract,ECFieldElementFp.prototype.multiply=feFpMultiply,ECFieldElementFp.prototype.square=feFpSquare,ECFieldElementFp.prototype.divide=feFpDivide,ECPointFp.prototype.getX=pointFpGetX,ECPointFp.prototype.getY=pointFpGetY,ECPointFp.prototype.equals=pointFpEquals,ECPointFp.prototype.isInfinity=pointFpIsInfinity,ECPointFp.prototype.negate=pointFpNegate,ECPointFp.prototype.add=pointFpAdd,ECPointFp.prototype.twice=pointFpTwice,ECPointFp.prototype.multiply=pointFpMultiply,ECPointFp.prototype.multiplyTwo=pointFpMultiplyTwo,ECCurveFp.prototype.getQ=curveFpGetQ,ECCurveFp.prototype.getA=curveFpGetA,ECCurveFp.prototype.getB=curveFpGetB,ECCurveFp.prototype.equals=curveFpEquals,ECCurveFp.prototype.getInfinity=curveFpGetInfinity,ECCurveFp.prototype.fromBigInteger=curveFpFromBigInteger,ECCurveFp.prototype.decodePointHex=curveFpDecodePointHex; -function X9ECParameters(e,t,n,r){this.curve=e,this.g=t,this.n=n,this.h=r}function x9getCurve(){return this.curve}function x9getG(){return this.g}function x9getN(){return this.n}function x9getH(){return this.h}function fromHex(e){return new BigInteger(e,16)}function secp128r1(){var e=fromHex("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF"),t=fromHex("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC"),n=fromHex("E87579C11079F43DD824993C2CEE5ED3"),r=fromHex("FFFFFFFE0000000075A30D1B9038A115"),i=BigInteger.ONE,s=new ECCurveFp(e,t,n),o=s.decodePointHex("04161FF7528B899B2D0C28607CA52C5B86CF5AC8395BAFEB13C02DA292DDED7A83");return new X9ECParameters(s,o,r,i)}function secp160k1(){var e=fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73"),t=BigInteger.ZERO,n=fromHex("7"),r=fromHex("0100000000000000000001B8FA16DFAB9ACA16B6B3"),i=BigInteger.ONE,s=new ECCurveFp(e,t,n),o=s.decodePointHex("043B4C382CE37AA192A4019E763036F4F5DD4D7EBB938CF935318FDCED6BC28286531733C3F03C4FEE");return new X9ECParameters(s,o,r,i)}function secp160r1(){var e=fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF"),t=fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC"),n=fromHex("1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45"),r=fromHex("0100000000000000000001F4C8F927AED3CA752257"),i=BigInteger.ONE,s=new ECCurveFp(e,t,n),o=s.decodePointHex("044A96B5688EF573284664698968C38BB913CBFC8223A628553168947D59DCC912042351377AC5FB32");return new X9ECParameters(s,o,r,i)}function secp192k1(){var e=fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37"),t=BigInteger.ZERO,n=fromHex("3"),r=fromHex("FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D"),i=BigInteger.ONE,s=new ECCurveFp(e,t,n),o=s.decodePointHex("04DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D");return new X9ECParameters(s,o,r,i)}function secp192r1(){var e=fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF"),t=fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC"),n=fromHex("64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1"),r=fromHex("FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831"),i=BigInteger.ONE,s=new ECCurveFp(e,t,n),o=s.decodePointHex("04188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF101207192B95FFC8DA78631011ED6B24CDD573F977A11E794811");return new X9ECParameters(s,o,r,i)}function secp224r1(){var e=fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001"),t=fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE"),n=fromHex("B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4"),r=fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D"),i=BigInteger.ONE,s=new ECCurveFp(e,t,n),o=s.decodePointHex("04B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34");return new X9ECParameters(s,o,r,i)}function secp256k1(){var e=fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F"),t=BigInteger.ZERO,n=fromHex("7"),r=fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"),i=BigInteger.ONE,s=new ECCurveFp(e,t,n),o=s.decodePointHex("0479BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8");return new X9ECParameters(s,o,r,i)}function secp256r1(){var e=fromHex("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF"),t=fromHex("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC"),n=fromHex("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B"),r=fromHex("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"),i=BigInteger.ONE,s=new ECCurveFp(e,t,n),o=s.decodePointHex("046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5");return new X9ECParameters(s,o,r,i)}function getSECCurveByName(e){return e=="secp128r1"?secp128r1():e=="secp160k1"?secp160k1():e=="secp160r1"?secp160r1():e=="secp192k1"?secp192k1():e=="secp192r1"?secp192r1():e=="secp224r1"?secp224r1():e=="secp256k1"?secp256k1():e=="secp256r1"?secp256r1():null}X9ECParameters.prototype.getCurve=x9getCurve,X9ECParameters.prototype.getG=x9getG,X9ECParameters.prototype.getN=x9getN,X9ECParameters.prototype.getH=x9getH; -var EventEmitter=function(){};EventEmitter.prototype.on=function(e,t,n){n||(n=this),this._listeners||(this._listeners={}),this._listeners[e]||(this._listeners[e]=[]),this._unbinders||(this._unbinders={}),this._unbinders[e]||(this._unbinders[e]=[]);var r=function(e){t.apply(n,[e])};this._unbinders[e].push(t),this._listeners[e].push(r)},EventEmitter.prototype.trigger=function(e,t){t===undefined&&(t={}),this._listeners||(this._listeners={});if(!this._listeners[e])return;var n=this._listeners[e].length;while(n--)this._listeners[e][n](t)},EventEmitter.prototype.removeListener=function(e,t){this._unbinders||(this._unbinders={});if(!this._unbinders[e])return;var n=this._unbinders[e].length;while(n--)this._unbinders[e][n]===t&&(this._unbinders[e].splice(n,1),this._listeners[e].splice(n,1))},EventEmitter.augment=function(e){for(var t in EventEmitter.prototype)e[t]||(e[t]=EventEmitter.prototype[t])}; -(function(e){var t=e;"object"!=typeof module&&(t.EventEmitter=EventEmitter)})("object"==typeof module?module.exports:window.Bitcoin={}); -BigInteger.valueOf=nbv,BigInteger.prototype.toByteArrayUnsigned=function(){var e=this.abs().toByteArray();return e.length?(e[0]==0&&(e=e.slice(1)),e.map(function(e){return e<0?e+256:e})):e},BigInteger.fromByteArrayUnsigned=function(e){return e.length?e[0]&128?new BigInteger([0].concat(e)):new BigInteger(e):e.valueOf(0)},BigInteger.prototype.toByteArraySigned=function(){var e=this.abs().toByteArrayUnsigned(),t=this.compareTo(BigInteger.ZERO)<0;return t?e[0]&128?e.unshift(128):e[0]|=128:e[0]&128&&e.unshift(0),e},BigInteger.fromByteArraySigned=function(e){return e[0]&128?(e[0]&=127,BigInteger.fromByteArrayUnsigned(e).negate()):BigInteger.fromByteArrayUnsigned(e)};var names=["log","debug","info","warn","error","assert","dir","dirxml","group","groupEnd","time","timeEnd","count","trace","profile","profileEnd"];"undefined"==typeof window.console&&(window.console={});for(var i=0;i>>8,e&255]:e<=1?[254].concat(Crypto.util.wordsToBytes([e])):[255].concat(Crypto.util.wordsToBytes([e>>>32,e]))},valueToBigInt:function(e){return e instanceof BigInteger?e:BigInteger.fromByteArrayUnsigned(e)},formatValue:function(e){var t=this.valueToBigInt(e).toString(),n=t.length>8?t.substr(0,t.length-8):"0",r=t.length>8?t.substr(t.length-8):t;while(r.length<8)r="0"+r;r=r.replace(/0*$/,"");while(r.length<2)r+="0";return n+"."+r},parseValue:function(e){var t=e.split("."),n=t[0],r=t[1]||"0";while(r.length<8)r+="0";r=r.replace(/^0+/g,"");var i=BigInteger.valueOf(parseInt(n));return i=i.multiply(BigInteger.valueOf(1e8)),i=i.add(BigInteger.valueOf(parseInt(r))),i},sha256ripe160:function(e){return Crypto.RIPEMD160(Crypto.SHA256(e,{asBytes:!0}),{asBytes:!0})}};for(var i in Crypto.util)Crypto.util.hasOwnProperty(i)&&(Bitcoin.Util[i]=Crypto.util[i]); -(function(e){e.Base58={alphabet:"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",validRegex:/^[1-9A-HJ-NP-Za-km-z]+$/,base:BigInteger.valueOf(58),encode:function(e){var n=BigInteger.fromByteArrayUnsigned(e),r=[];while(n.compareTo(t.base)>=0){var i=n.mod(t.base);r.unshift(t.alphabet[i.intValue()]),n=n.subtract(i).divide(t.base)}r.unshift(t.alphabet[n.intValue()]);for(var s=0;s=0;i--){var s=t.alphabet.indexOf(e[i]);if(s<0)throw"Invalid character";n=n.add(BigInteger.valueOf(s).multiply(t.base.pow(e.length-1-i))),e[i]=="1"?r++:r=0}var o=n.toByteArrayUnsigned();while(r-->0)o.unshift(0);return o}};var t=e.Base58})("undefined"!=typeof Bitcoin?Bitcoin:module.exports); -Bitcoin.Address=function(e){"string"==typeof e&&(e=Bitcoin.Address.decodeString(e)),this.hash=e,this.version=0},Bitcoin.Address.prototype.toString=function(){var e=this.hash.slice(0);e.unshift(this.version);var t=Crypto.SHA256(Crypto.SHA256(e,{asBytes:!0}),{asBytes:!0}),n=e.concat(t.slice(0,4));return Bitcoin.Base58.encode(n)},Bitcoin.Address.prototype.getHashBase64=function(){return Crypto.util.bytesToBase64(this.hash)},Bitcoin.Address.decodeString=function(e){var t=Bitcoin.Base58.decode(e),n=t.slice(0,21),r=Crypto.SHA256(Crypto.SHA256(n,{asBytes:!0}),{asBytes:!0});if(r[0]!=t[21]||r[1]!=t[22]||r[2]!=t[23]||r[3]!=t[24])throw"Checksum validation failed!";var i=n.shift();if(i!=0)throw"Version "+i+" not supported!";return n}; -function integerToBytes(e,t){var n=e.toByteArrayUnsigned();if(tn.length)n.unshift(0);return n}function dmp(e){return e instanceof BigInteger||(e=e.toBigInteger()),Crypto.util.bytesToHex(e.toByteArrayUnsigned())}ECFieldElementFp.prototype.getByteLength=function(){return Math.floor((this.toBigInteger().bitLength()+7)/8)},ECPointFp.prototype.getEncoded=function(e){var t=this.getX().toBigInteger(),n=this.getY().toBigInteger(),r=integerToBytes(t,32);return e?n.isEven()?r.unshift(2):r.unshift(3):(r.unshift(4),r=r.concat(integerToBytes(n,32))),r},ECPointFp.decodeFrom=function(e,t){var n=t[0],r=t.length-1,i=t.slice(1,1+r/2),s=t.slice(1+r/2,1+r);i.unshift(0),s.unshift(0);var o=new BigInteger(i),u=new BigInteger(s);return new ECPointFp(e,e.fromBigInteger(o),e.fromBigInteger(u))},ECPointFp.prototype.add2D=function(e){if(this.isInfinity())return e;if(e.isInfinity())return this;if(this.x.equals(e.x))return this.y.equals(e.y)?this.twice():this.curve.getInfinity();var t=e.x.subtract(this.x),n=e.y.subtract(this.y),r=n.divide(t),i=r.square().subtract(this.x).subtract(e.x),s=r.multiply(this.x.subtract(i)).subtract(this.y);return new ECPointFp(this.curve,i,s)},ECPointFp.prototype.twice2D=function(){if(this.isInfinity())return this;if(this.y.toBigInteger().signum()==0)return this.curve.getInfinity();var e=this.curve.fromBigInteger(BigInteger.valueOf(2)),t=this.curve.fromBigInteger(BigInteger.valueOf(3)),n=this.x.square().multiply(t).add(this.curve.a).divide(this.y.multiply(e)),r=n.square().subtract(this.x.multiply(e)),i=n.multiply(this.x.subtract(r)).subtract(this.y);return new ECPointFp(this.curve,r,i)},ECPointFp.prototype.multiply2D=function(e){if(this.isInfinity())return this;if(e.signum()==0)return this.curve.getInfinity();var t=e,n=t.multiply(new BigInteger("3")),r=this.negate(),i=this,s;for(s=n.bitLength()-2;s>0;--s){i=i.twice();var o=n.testBit(s),u=t.testBit(s);o!=u&&(i=i.add2D(o?this:r))}return i},ECPointFp.prototype.isOnCurve=function(){var e=this.getX().toBigInteger(),t=this.getY().toBigInteger(),n=this.curve.getA().toBigInteger(),r=this.curve.getB().toBigInteger(),i=this.curve.getQ(),s=t.multiply(t).mod(i),o=e.multiply(e).multiply(e).add(n.multiply(e)).add(r).mod(i);return s.equals(o)},ECPointFp.prototype.toString=function(){return"("+this.getX().toBigInteger().toString()+","+this.getY().toBigInteger().toString()+")"},ECPointFp.prototype.validate=function(){var e=this.curve.getQ();if(this.isInfinity())throw new Error("Point is at infinity.");var t=this.getX().toBigInteger(),n=this.getY().toBigInteger();if(t.compareTo(BigInteger.ONE)<0||t.compareTo(e.subtract(BigInteger.ONE))>0)throw new Error("x coordinate out of bounds");if(n.compareTo(BigInteger.ONE)<0||n.compareTo(e.subtract(BigInteger.ONE))>0)throw new Error("y coordinate out of bounds");if(!this.isOnCurve())throw new Error("Point is not on the curve.");if(this.multiply(e).isInfinity())throw new Error("Point is not a scalar multiple of G.");return!0},Bitcoin.ECDSA=function(){function r(e,t,n,r){var i=Math.max(t.bitLength(),r.bitLength()),s=e.add2D(n),o=e.curve.getInfinity();for(var u=i-1;u>=0;--u)o=o.twice2D(),o.z=BigInteger.ONE,t.testBit(u)?r.testBit(u)?o=o.add2D(s):o=o.add2D(e):r.testBit(u)&&(o=o.add2D(n));return o}var e=getSECCurveByName("secp256k1"),t=new SecureRandom,n=null,i={getBigRandom:function(e){return(new BigInteger(e.bitLength(),t)).mod(e.subtract(BigInteger.ONE)).add(BigInteger.ONE)},sign:function(t,n){var r=n,s=e.getN(),o=BigInteger.fromByteArrayUnsigned(t);do var u=i.getBigRandom(s),a=e.getG(),f=a.multiply(u),l=f.getX().toBigInteger().mod(s);while(l.compareTo(BigInteger.ZERO)<=0);var c=u.modInverse(s).multiply(o.add(r.multiply(l))).mod(s);return i.serializeSig(l,c)},verify:function(t,n,r){var s,o;if(Bitcoin.Util.isArray(n)){var u=i.parseSig(n);s=u.r,o=u.s}else{if("object"!=typeof n||!n.r||!n.s)throw"Invalid value for signature";s=n.r,o=n.s}var a;if(r instanceof ECPointFp)a=r;else{if(!Bitcoin.Util.isArray(r))throw"Invalid format for pubkey value, must be byte array or ECPointFp";a=ECPointFp.decodeFrom(e.getCurve(),r)}var f=BigInteger.fromByteArrayUnsigned(t);return i.verifyRaw(f,s,o,a)},verifyRaw:function(t,n,r,i){var s=e.getN(),o=e.getG();if(n.compareTo(BigInteger.ONE)<0||n.compareTo(s)>=0)return!1;if(r.compareTo(BigInteger.ONE)<0||r.compareTo(s)>=0)return!1;var u=r.modInverse(s),a=t.multiply(u).mod(s),f=n.multiply(u).mod(s),l=o.multiply(a).add(i.multiply(f)),c=l.getX().toBigInteger().mod(s);return c.equals(n)},serializeSig:function(e,t){var n=e.toByteArraySigned(),r=t.toByteArraySigned(),i=[];return i.push(2),i.push(n.length),i=i.concat(n),i.push(2),i.push(r.length),i=i.concat(r),i.unshift(i.length),i.unshift(48),i},parseSig:function(e){var t;if(e[0]!=48)throw new Error("Signature not a valid DERSequence");t=2;if(e[t]!=2)throw new Error("First element in signature must be a DERInteger");var n=e.slice(t+2,t+2+e[t+1]);t+=2+e[t+1];if(e[t]!=2)throw new Error("Second element in signature must be a DERInteger");var r=e.slice(t+2,t+2+e[t+1]);t+=2+e[t+1];var i=BigInteger.fromByteArrayUnsigned(n),s=BigInteger.fromByteArrayUnsigned(r);return{r:i,s:s}},parseSigCompact:function(t){if(t.length!==65)throw"Signature has the wrong length";var n=t[0]-27;if(n<0||n>7)throw"Invalid signature type";var r=e.getN(),i=BigInteger.fromByteArrayUnsigned(t.slice(1,33)).mod(r),s=BigInteger.fromByteArrayUnsigned(t.slice(33,65)).mod(r);return{r:i,s:s,i:n}},recoverPubKey:function(t,s,o,u){u&=3;var a=u&1,f=u>>1,l=e.getN(),c=e.getG(),h=e.getCurve(),p=h.getQ(),d=h.getA().toBigInteger(),v=h.getB().toBigInteger();n||(n=p.add(BigInteger.ONE).divide(BigInteger.valueOf(4)));var m=f?t.add(l):t,g=m.multiply(m).multiply(m).add(d.multiply(m)).add(v).mod(p),y=g.modPow(n,p),b=y.isEven()?u%2:(u+1)%2,w=(y.isEven()?!a:a)?y:p.subtract(y),E=new ECPointFp(h,h.fromBigInteger(m),h.fromBigInteger(w));E.validate();var S=BigInteger.fromByteArrayUnsigned(o),x=BigInteger.ZERO.subtract(S).mod(l),T=t.modInverse(l),N=r(E,s,c,x).multiply(T);console.log("G.x: ",Crypto.util.bytesToHex(c.x.toBigInteger().toByteArrayUnsigned())),console.log("G.y: ",Crypto.util.bytesToHex(c.y.toBigInteger().toByteArrayUnsigned())),console.log("s: ",Crypto.util.bytesToHex(T.toByteArrayUnsigned())),console.log("Q.x: ",Crypto.util.bytesToHex(N.x.toBigInteger().toByteArrayUnsigned())),console.log("Q.y: ",Crypto.util.bytesToHex(N.y.toBigInteger().toByteArrayUnsigned())),N.validate();if(!i.verifyRaw(S,t,s,N))throw"Pubkey recovery unsuccessful";var C=new Bitcoin.ECKey;return C.pub=N,C},calcPubkeyRecoveryParam:function(e,t,n,r){for(var i=0;i<4;i++)try{var s=Bitcoin.ECDSA.recoverPubKey(t,n,r,i);if(s.getBitcoinAddress().toString()==e)return i}catch(o){}throw"Unable to find valid recovery factor"}};return i}(); -Bitcoin.ECKey=function(){var e=Bitcoin.ECDSA,t=getSECCurveByName("secp256k1"),n=new SecureRandom,r=function(n){if(!n){var i=t.getN();this.priv=e.getBigRandom(i)}else n instanceof BigInteger?this.priv=n:Bitcoin.Util.isArray(n)?this.priv=BigInteger.fromByteArrayUnsigned(n):"string"==typeof n&&(n.length==51&&n[0]=="5"?this.priv=BigInteger.fromByteArrayUnsigned(r.decodeString(n)):this.priv=BigInteger.fromByteArrayUnsigned(Crypto.util.base64ToBytes(n)));this.compressed=!!r.compressByDefault};return r.compressByDefault=!1,r.prototype.setCompressed=function(e){this.compressed=!!e},r.prototype.getPub=function(){return this.getPubPoint().getEncoded(this.compressed)},r.prototype.getPubPoint=function(){return this.pub||(this.pub=t.getG().multiply(this.priv)),this.pub},r.prototype.getPubKeyHash=function(){return this.pubKeyHash?this.pubKeyHash:this.pubKeyHash=Bitcoin.Util.sha256ripe160(this.getPub())},r.prototype.getBitcoinAddress=function(){var e=this.getPubKeyHash(),t=new Bitcoin.Address(e);return t},r.prototype.getExportedPrivateKey=function(){var e=this.priv.toByteArrayUnsigned();while(e.length<32)e.unshift(0);e.unshift(128);var t=Crypto.SHA256(Crypto.SHA256(e,{asBytes:!0}),{asBytes:!0}),n=e.concat(t.slice(0,4));return Bitcoin.Base58.encode(n)},r.prototype.setPub=function(e){this.pub=ECPointFp.decodeFrom(t.getCurve(),e)},r.prototype.toString=function(e){return e==="base64"?Crypto.util.bytesToBase64(this.priv.toByteArrayUnsigned()):Crypto.util.bytesToHex(this.priv.toByteArrayUnsigned())},r.prototype.sign=function(t){return e.sign(t,this.priv)},r.prototype.verify=function(t,n){return e.verify(t,n,this.getPub())},r.decodeString=function(e){var t=Bitcoin.Base58.decode(e),n=t.slice(0,33),r=Crypto.SHA256(Crypto.SHA256(n,{asBytes:!0}),{asBytes:!0});if(r[0]!=t[33]||r[1]!=t[34]||r[2]!=t[35]||r[3]!=t[36])throw"Checksum validation failed!";var i=n.shift();if(i!=128)throw"Version "+i+" not supported!";return n},r}(); -(function(){var e=Bitcoin.Opcode=function(e){this.code=e};e.prototype.toString=function(){return e.reverseMap[this.code]},e.map={OP_0:0,OP_FALSE:0,OP_PUSHDATA1:76,OP_PUSHDATA2:77,OP_PUSHDATA4:78,OP_1NEGATE:79,OP_RESERVED:80,OP_1:81,OP_TRUE:81,OP_2:82,OP_3:83,OP_4:84,OP_5:85,OP_6:86,OP_7:87,OP_8:88,OP_9:89,OP_10:90,OP_11:91,OP_12:92,OP_13:93,OP_14:94,OP_15:95,OP_16:96,OP_NOP:97,OP_VER:98,OP_IF:99,OP_NOTIF:100,OP_VERIF:101,OP_VERNOTIF:102,OP_ELSE:103,OP_ENDIF:104,OP_VERIFY:105,OP_RETURN:106,OP_TOALTSTACK:107,OP_FROMALTSTACK:108,OP_2DROP:109,OP_2DUP:110,OP_3DUP:111,OP_2OVER:112,OP_2ROT:113,OP_2SWAP:114,OP_IFDUP:115,OP_DEPTH:116,OP_DROP:117,OP_DUP:118,OP_NIP:119,OP_OVER:120,OP_PICK:121,OP_ROLL:122,OP_ROT:123,OP_SWAP:124,OP_TUCK:125,OP_CAT:126,OP_SUBSTR:127,OP_LEFT:128,OP_RIGHT:129,OP_SIZE:130,OP_INVERT:131,OP_AND:132,OP_OR:133,OP_XOR:134,OP_EQUAL:135,OP_EQUALVERIFY:136,OP_RESERVED1:137,OP_RESERVED2:138,OP_1ADD:139,OP_1SUB:140,OP_2MUL:141,OP_2DIV:142,OP_NEGATE:143,OP_ABS:144,OP_NOT:145,OP_0NOTEQUAL:146,OP_ADD:147,OP_SUB:148,OP_MUL:149,OP_DIV:150,OP_MOD:151,OP_LSHIFT:152,OP_RSHIFT:153,OP_BOOLAND:154,OP_BOOLOR:155,OP_NUMEQUAL:156,OP_NUMEQUALVERIFY:157,OP_NUMNOTEQUAL:158,OP_LESSTHAN:159,OP_GREATERTHAN:160,OP_LESSTHANOREQUAL:161,OP_GREATERTHANOREQUAL:162,OP_MIN:163,OP_MAX:164,OP_WITHIN:165,OP_RIPEMD160:166,OP_SHA1:167,OP_SHA256:168,OP_HASH160:169,OP_HASH256:170,OP_CODESEPARATOR:171,OP_CHECKSIG:172,OP_CHECKSIGVERIFY:173,OP_CHECKMULTISIG:174,OP_CHECKMULTISIGVERIFY:175,OP_NOP1:176,OP_NOP2:177,OP_NOP3:178,OP_NOP4:179,OP_NOP5:180,OP_NOP6:181,OP_NOP7:182,OP_NOP8:183,OP_NOP9:184,OP_NOP10:185,OP_PUBKEYHASH:253,OP_PUBKEY:254,OP_INVALIDOPCODE:255},e.reverseMap=[];for(var t in e.map)e.reverseMap[e.map[t]]=t})(); -(function(){var Opcode=Bitcoin.Opcode;for(var i in Opcode.map)eval("var "+i+" = "+Opcode.map[i]+";");var Script=Bitcoin.Script=function(e){if(!e)this.buffer=[];else if("string"==typeof e)this.buffer=Crypto.util.base64ToBytes(e);else if(Bitcoin.Util.isArray(e))this.buffer=e;else{if(!(e instanceof Script))throw new Error("Invalid script");this.buffer=e.buffer}this.parse()};Script.prototype.parse=function(){function n(n){e.chunks.push(e.buffer.slice(t,t+n)),t+=n}var e=this;this.chunks=[];var t=0;while(t=240&&(r=r<<8|this.buffer[t++]);var i;r>0&&r>>8&255)):(this.buffer.push(OP_PUSHDATA4),this.buffer.push(e.length&255),this.buffer.push(e.length>>>8&255),this.buffer.push(e.length>>>16&255),this.buffer.push(e.length>>>24&255)),this.buffer=this.buffer.concat(e),this.chunks.push(e)},Script.createOutputScript=function(e){var t=new Script;return t.writeOp(OP_DUP),t.writeOp(OP_HASH160),t.writeBytes(e.hash),t.writeOp(OP_EQUALVERIFY),t.writeOp(OP_CHECKSIG),t},Script.prototype.extractAddresses=function(e){switch(this.getOutType()){case"Address":return e.push(new Address(this.chunks[2])),1;case"Pubkey":return e.push(new Address(Util.sha256ripe160(this.chunks[0]))),1;case"Multisig":for(var t=1;t=0;o--){var u=this.outs[o],a=u.script.simpleOutPubKeyHash();e.hasHash(a)?i=a:n=!1,r=a}for(var o=this.ins.length-1;o>=0;o--){var f=this.ins[o];s=f.script.simpleInPubKeyHash();if(!e.hasHash(s)){t=!1;break}}var l=this.calcImpact(e),c={};return c.impact=l,l.sign>0&&l.value.compareTo(BigInteger.ZERO)>0?(c.type="recv",c.addr=new Bitcoin.Address(i)):t&&n?c.type="self":t?(c.type="sent",c.addr=new Bitcoin.Address(r)):c.type="other",c}return null},t.prototype.getDescription=function(e){var t=this.analyze(e);if(!t)return"";switch(t.type){case"recv":return"Received with "+t.addr;case"sent":return"Payment to "+t.addr;case"self":return"Payment to yourself";case"other":default:return""}},t.prototype.getTotalOutValue=function(){var e=BigInteger.ZERO;for(var t=0;t=0?{sign:1,value:t.subtract(s)}:{sign:-1,value:s.subtract(t)}}return BigInteger.ZERO};var u=Bitcoin.TransactionIn=function(t){this.outpoint=t.outpoint,t.script instanceof e?this.script=t.script:this.script=new e(t.script),this.sequence=t.sequence};u.prototype.clone=function(){var e=new u({outpoint:{hash:this.outpoint.hash,index:this.outpoint.index},script:this.script.clone(),sequence:this.sequence});return e};var a=Bitcoin.TransactionOut=function(t){t.script instanceof e?this.script=t.script:this.script=new e(t.script);if(Bitcoin.Util.isArray(t.value))this.value=t.value;else if("string"==typeof t.value){var n=(new BigInteger(t.value,10)).toString(16);while(n.length<16)n="0"+n;this.value=Crypto.util.hexToBytes(n)}};a.prototype.clone=function(){var e=new a({script:this.script.clone(),value:this.value.slice(0)});return e}})(); -Bitcoin.Wallet=function(){var e=Bitcoin.Script,t=Bitcoin.TransactionIn,n=Bitcoin.TransactionOut,r=function(){var e=[];this.addressHashes=[],this.txIndex={},this.unspentOuts=[],this.addressPointer=0,this.addKey=function(t,n){t instanceof Bitcoin.ECKey||(t=new Bitcoin.ECKey(t)),e.push(t),n&&("string"==typeof n&&(n=Crypto.util.base64ToBytes(n)),t.setPub(n)),this.addressHashes.push(t.getBitcoinAddress().getHashBase64())},this.addKeys=function(e,t){"string"==typeof e&&(e=e.split(",")),"string"==typeof t&&(t=t.split(","));var n;if(Array.isArray(t)&&e.length==t.length)for(n=0;n=0)break}if(o.compareTo(s)<0)throw new Error("Insufficient funds.");var a=o.subtract(s),f=new Bitcoin.Transaction;for(u=0;u0&&f.addOutput(this.getNextAddress(),a);var l=1;for(u=0;u=0.2.1", - "jake-uglify" : ">=1.0.0" + "mocha": "1.8.1", + "istanbul": "0.1.30", + "uglify-js": "*", + "node-browserify": "https://github.com/substack/node-browserify/tarball/master" + }, + + "testling": { + "browsers": ["ie/9..latest", "firefox/15..latest", "chrome/22..latest"], + "harness" : "mocha-qunit", + "files": "test/*.js" + }, + + "scripts": { + "test": "istanbul test ./node_modules/.bin/_mocha -- --reporter list test/*.js", + "compile": "browserify src/index.js -s Bitcoin | uglifyjs > bitcoinjs-min.js" } } diff --git a/src/address.js b/src/address.js index 3013764..57b6038 100644 --- a/src/address.js +++ b/src/address.js @@ -1,10 +1,35 @@ -Bitcoin.Address = function (bytes) { - if ("string" == typeof bytes) { - bytes = Bitcoin.Address.decodeString(bytes); - } - this.hash = bytes; +var base58 = require('./base58'); +var Crypto = require('./crypto-js/crypto'); +var conv = require('./convert'); +var util = require('./util'); - this.version = 0x00; +var address_types = { + prod: 0, + testnet: 111 +}; + +var p2sh_types = { + prod: 5, + testnet: 196 +}; + +var Address = function (bytes, version) { + if (arguments[0] instanceof Address) { + this.hash = arguments[0].hash; + this.version = arguments[0].version; + } + else if (typeof bytes === 'string') { + this.hash = + bytes.length <= 34 ? base58.checkDecode(bytes) + : bytes.length <= 40 ? conv.hexToBytes(bytes) + : util.error('Bad input'); + + this.version = version || this.hash.version || 0; + } + else { + this.hash = bytes; + this.version = version || 0; + } }; /** @@ -12,46 +37,32 @@ Bitcoin.Address = function (bytes) { * * Returns the address as a base58-encoded string in the standardized format. */ -Bitcoin.Address.prototype.toString = function () { - // Get a copy of the hash - var hash = this.hash.slice(0); - - // Version - hash.unshift(this.version); - - var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true}); - - var bytes = hash.concat(checksum.slice(0,4)); - - return Bitcoin.Base58.encode(bytes); +Address.prototype.toString = function () { + return base58.checkEncode(this.hash.slice(0),this.version); }; -Bitcoin.Address.prototype.getHashBase64 = function () { - return Crypto.util.bytesToBase64(this.hash); +Address.prototype.getHash = function () { + return conv.bytesToHex(this.hash); +}; + +Address.getVersion = function(string) { + return base58.decode(string)[0]; +} + +Address.validate = function(string) { + try { + base58.checkDecode(string); + return true; + } catch (e) { + return false; + } }; /** * Parse a Bitcoin address contained in a string. */ -Bitcoin.Address.decodeString = function (string) { - var bytes = Bitcoin.Base58.decode(string); - - var hash = bytes.slice(0, 21); - - var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true}); - - if (checksum[0] != bytes[21] || - checksum[1] != bytes[22] || - checksum[2] != bytes[23] || - checksum[3] != bytes[24]) { - throw "Checksum validation failed!"; - } - - var version = hash.shift(); - - if (version != 0) { - throw "Version "+version+" not supported!"; - } - - return hash; +Address.decodeString = function (string) { + return base58.checkDecode(string); }; + +module.exports = Address; diff --git a/src/base58.js b/src/base58.js index 9313c55..2382cb4 100644 --- a/src/base58.js +++ b/src/base58.js @@ -1,71 +1,119 @@ -(function (Bitcoin) { - Bitcoin.Base58 = { - alphabet: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz", - validRegex: /^[1-9A-HJ-NP-Za-km-z]+$/, - base: BigInteger.valueOf(58), - /** - * Convert a byte array to a base58-encoded string. - * - * Written by Mike Hearn for BitcoinJ. - * Copyright (c) 2011 Google Inc. - * - * Ported to JavaScript by Stefan Thomas. - */ - encode: function (input) { - var bi = BigInteger.fromByteArrayUnsigned(input); - var chars = []; +// https://en.bitcoin.it/wiki/Base58Check_encoding - while (bi.compareTo(B58.base) >= 0) { - var mod = bi.mod(B58.base); - chars.unshift(B58.alphabet[mod.intValue()]); - bi = bi.subtract(mod).divide(B58.base); - } - chars.unshift(B58.alphabet[bi.intValue()]); +var BigInteger = require('./jsbn/jsbn'); +var Crypto = require('./crypto-js/crypto'); +var conv = require('./convert'); - // Convert leading zeros too. - for (var i = 0; i < input.length; i++) { - if (input[i] == 0x00) { - chars.unshift(B58.alphabet[0]); - } else break; - } +var alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; +var base = BigInteger.valueOf(58); - return chars.join(''); - }, +var positions = {}; +for (var i=0 ; i < alphabet.length ; ++i) { + positions[alphabet[i]] = i; +} - /** - * Convert a base58-encoded string to a byte array. - * - * Written by Mike Hearn for BitcoinJ. - * Copyright (c) 2011 Google Inc. - * - * Ported to JavaScript by Stefan Thomas. - */ - decode: function (input) { - var bi = BigInteger.valueOf(0); - var leadingZerosNum = 0; - for (var i = input.length - 1; i >= 0; i--) { - var alphaIndex = B58.alphabet.indexOf(input[i]); - if (alphaIndex < 0) { - throw "Invalid character"; - } - bi = bi.add(BigInteger.valueOf(alphaIndex) - .multiply(B58.base.pow(input.length - 1 -i))); +// Convert a byte array to a base58-encoded string. +// Written by Mike Hearn for BitcoinJ. +// Copyright (c) 2011 Google Inc. +// Ported to JavaScript by Stefan Thomas. +module.exports.encode = function (input) { + var bi = BigInteger.fromByteArrayUnsigned(input); + var chars = []; - // This counts leading zero bytes - if (input[i] == "1") leadingZerosNum++; - else leadingZerosNum = 0; - } - var bytes = bi.toByteArrayUnsigned(); - - // Add leading zeros - while (leadingZerosNum-- > 0) bytes.unshift(0); - - return bytes; + while (bi.compareTo(base) >= 0) { + var mod = bi.mod(base); + chars.push(alphabet[mod.intValue()]); + bi = bi.subtract(mod).divide(base); } - }; + chars.push(alphabet[bi.intValue()]); + + // Convert leading zeros too. + for (var i = 0; i < input.length; i++) { + if (input[i] == 0x00) { + chars.push(alphabet[0]); + } else break; + } + + return chars.reverse().join(''); +}, + +module.exports.encodeHex = function (input) { + return conv.bytesToHex(module.exports.encode(input)); +} + +// decode a base58 string into a byte array +// input should be a base58 encoded string +// @return Array +module.exports.decode = function (input) { + + var base = BigInteger.valueOf(58); + + var length = input.length; + var num = BigInteger.valueOf(0); + var leading_zero = 0; + var seen_other = false; + for (var i=0; i 0) { + bytes.unshift(0); + } + + return bytes; +} + +module.exports.decodeHex = function (input) { + return module.exports.decode(conv.hexToBytes(input)); +} + +module.exports.checkEncode = function(input, vbyte) { + vbyte = vbyte || 0; + var front = [vbyte].concat(input); + var checksum = Crypto.SHA256(Crypto.SHA256(front, {asBytes: true}), {asBytes: true}) + .slice(0,4); + return module.exports.encode(front.concat(checksum)); +} + +module.exports.checkEncodeHex = function (input, vbyte) { + return conv.bytesToHex(module.exports.encode(input)); +} + +module.exports.checkDecode = function(input) { + var bytes = module.exports.decode(input), + front = bytes.slice(0,bytes.length-4), + back = bytes.slice(bytes.length-4); + var checksum = Crypto.SHA256(Crypto.SHA256(front,{asBytes: true}), {asBytes: true}) + .slice(0,4); + if (""+checksum != ""+back) { + throw new Error("Checksum failed"); + } + var o = front.slice(1); + o.version = front[0]; + return o; +} + +module.exports.checkDecodeHex = function (input) { + return module.exports.checkDecode(conv.hexToBytes(input)); +} + - var B58 = Bitcoin.Base58; -})( - 'undefined' != typeof Bitcoin ? Bitcoin : module.exports -); diff --git a/src/bitcoin.js b/src/bitcoin.js deleted file mode 100755 index e14d36f..0000000 --- a/src/bitcoin.js +++ /dev/null @@ -1,175 +0,0 @@ -(function (exports) { - var Bitcoin = exports; - - if ('object' !== typeof module) { - Bitcoin.EventEmitter = EventEmitter; - } -})( - 'object' === typeof module ? module.exports : (window.Bitcoin = {}) -); - -/* - function makeKeypair() - { - // Generate private key - var n = ecparams.getN(); - var n1 = n.subtract(BigInteger.ONE); - var r = new BigInteger(n.bitLength(), rng); - - var privateKey = r.mod(n1).add(BigInteger.ONE); - - // Generate public key - var G = ecparams.getG(); - var publicPoint = G.multiply(privateKey); - - return {priv: privateKey, pubkey: publicPoint}; - }; - - function serializeTransaction(tx) - { - var buffer = []; - buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(tx.version)])); - buffer = buffer.concat(numToVarInt(tx.ins.length)); - for (var i = 0; i < tx.ins.length; i++) { - var txin = tx.ins[i]; - buffer = buffer.concat(Crypto.util.base64ToBytes(txin.outpoint.hash)); - buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(txin.index)])); - var scriptBytes = Crypto.util.base64ToBytes(txin.script); - buffer = buffer.concat(numToVarInt(scriptBytes.length)); - buffer = buffer.concat(scriptBytes); - buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(txin.sequence)])); - } - buffer = buffer.concat(numToVarInt(tx.outs.length)); - for (var i = 0; i < tx.outs.length; i++) { - var txout = tx.outs[i]; - var valueHex = (new BigInteger(txout.value, 10)).toString(16); - while (valueHex.length < 16) valueHex = "0" + valueHex; - buffer = buffer.concat(Crypto.util.hexToBytes(valueHex)); - var scriptBytes = Crypto.util.base64ToBytes(txout.script); - buffer = buffer.concat(numToVarInt(scriptBytes.length)); - buffer = buffer.concat(scriptBytes); - } - buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(tx.lock_time)])); - - return buffer; - }; - - var OP_CODESEPARATOR = 171; - - var SIGHASH_ALL = 1; - var SIGHASH_NONE = 2; - var SIGHASH_SINGLE = 3; - var SIGHASH_ANYONECANPAY = 80; - - function hashTransactionForSignature(scriptCode, tx, inIndex, hashType) - { - // TODO: We need to actually deep copy here - var txTmp = tx; - - // 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 = Crypto.util.bytesToBase64([]); - } - txTmp.ins[inIndex].script = Crypto.util.bytesToBase64(scriptCode); - - // 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 = serializeTransaction(txTmp); - - buffer.concat(Crypto.util.wordsToBytes([parseInt(hashType)])); - - console.log(buffer); - - return Crypto.SHA256(Crypto.SHA256(buffer, {asBytes: true}), {asBytes: true}); - }; - - function verifyTransactionSignature(tx) { - var hash = hashTransactionForSignature([], tx, 0, 0); - return Crypto.util.bytesToHex(hash); - }; - - function numToVarInt(i) - { - // TODO: THIS IS TOTALLY UNTESTED! - if (i < 0xfd) { - // unsigned char - return [i]; - } else if (i <= 1<<16) { - // unsigned short (LE) - return [0xfd, i >>> 8, i & 255]; - } else if (i <= 1<<32) { - // unsigned int (LE) - return [0xfe].concat(Crypto.util.wordsToBytes([i])); - } else { - // unsigned long long (LE) - return [0xff].concat(Crypto.util.wordsToBytes([i >>> 32, i])); - } - }; - - var testTx = { - "version":"1", - "lock_time":"0", - "block": { - "hash":"N/A", - "height":115806 - }, - "index":6, - "hash":"WUFzjKubG1kqfJWMb4qZdlhU2F3l5NGXN7AUg8Jwl14=", - "ins":[{ - "outpoint":{ - "hash":"nqcbMM1oRhfLdZga11q7x0CpUMujm+vtxHXO9V0gnwE=", - "index":0 - }, - "script":"RzBEAiB2XXkx1pca9SlfCmCGNUVf+h2sAFBttcxG1VnypIcvEgIgXrOp7LSdYBYp3nPsQAz8BOLD3K4pAlXfZImP1rkzk2EBQQRi7NcODzNfnVqLtG79Axp5UF6EhFIhCmzqKqssfKpfCIOmzCuXEeDFUFvFzeGLJx5N+wp2qRS1TqYezGD3yERk", - "sequence":4294967295 - }], - "outs":[{ - "value":"3000000000", - "script":"dqkUBLZwqhAPRVgZvwI8MN5gLHbU8NOIrA==" - },{ - "value":"25937000000", - "script":"dqkUQ82gJ0O5vOBg6yK5/yorLLV5zLKIrA==" - }] - }; - - TODO: Make this stuff into test cases ;) - $(function () { - var key = new Bitcoin.ECKey(Crypto.util.hexToBytes("5c0b98e524ad188ddef35dc6abba13c34a351a05409e5d285403718b93336a4a")); - key = new Bitcoin.ECKey(Crypto.util.hexToBytes("180cb41c7c600be951b5d3d0a7334acc7506173875834f7a6c4c786a28fcbb19")); - //console.log(key.getBitcoinAddress().toString()); - //var message = Crypto.util.hexToBytes("2aec28d323ee7b06a799d540d224b351161fe48967174ca5e43164e86137da11"); - //message = [0]; - //var out = key.sign(message); - //console.log("pubkey: "+Crypto.util.bytesToHex(key.getPub())); - //console.log("sig: "+Crypto.util.bytesToHex(out)); - - //console.log(key.verify(message, out)); - - //console.log(Bitcoin.ECDSA.verify(message, Crypto.util.hexToBytes("3046022100dffbc26774fc841bbe1c1362fd643609c6e42dcb274763476d87af2c0597e89e022100c59e3c13b96b316cae9fa0ab0260612c7a133a6fe2b3445b6bf80b3123bf274d"), Crypto.util.hexToBytes("0401de173aa944eacf7e44e5073baca93fb34fe4b7897a1c82c92dfdc8a1f75ef58cd1b06e8052096980cb6e1ad6d3df143c34b3d7394bae2782a4df570554c2fb"))); - - //console.log(Bitcoin.ECDSA.verify(Crypto.util.hexToBytes("230aba77ccde46bb17fcb0295a92c0cc42a6ea9f439aaadeb0094625f49e6ed8"), Crypto.util.hexToBytes("3046022100a3ee5408f0003d8ef00ff2e0537f54ba09771626ff70dca1f01296b05c510e85022100d4dc70a5bb50685b65833a97e536909a6951dd247a2fdbde6688c33ba6d6407501"),Crypto.util.hexToBytes("04a19c1f07c7a0868d86dbb37510305843cc730eb3bea8a99d92131f44950cecd923788419bfef2f635fad621d753f30d4b4b63b29da44b4f3d92db974537ad5a4"))); - //console.log(Bitcoin.ECDSA.verify(Crypto.util.hexToBytes("c2c75bb77d7a5acddceb1d45ceef58e7451fd0d3abc9d4c16df7848eefafe00d"), Crypto.util.hexToBytes("3045022100ff9362dadcbf1f6ef954bc8eb27144bbb4f49abd32be1eb04c311151dcf4bcf802205112c2ca6a25aefb8be98bf460c5a9056c01253f31e118d80b81ec9604e3201a01"),Crypto.util.hexToBytes("04fe62ce7892ec209310c176ef7f06565865e286e8699e884603657efa9aa51086785099d544d4e04f1f7b4b065205c1783fade8daf4ba1e0d1962292e8eb722cd"))); - }); - // -*/ diff --git a/src/convert.js b/src/convert.js new file mode 100644 index 0000000..0079145 --- /dev/null +++ b/src/convert.js @@ -0,0 +1,74 @@ +// convert to/from various values + +var base64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +String.prototype.lpad = function(padString, length) { + var str = this; + while (str.length < length) str = padString + str; + return str; +} + +// Convert a byte array to a hex string +module.exports.bytesToHex = function(bytes) { + return bytes.map(function(x) { return x.toString(16).lpad('0',2) }).join(''); +}; + +// Convert a hex string to a byte array +module.exports.hexToBytes = function(hex) { + return hex.match(/../g).map(function(x) { return parseInt(x,16) }); +} + + // Convert a byte array to a base-64 string +module.exports.bytesToBase64 = function(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) { + + // 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; +} + +// Hex only (allowing bin would be potentially risky, as 01010101 = \x01 * 4 or 85) +module.exports.coerceToBytes = function(input) { + if (typeof input == "string") return module.exports.hexToBytes(input); + return input; +} + +module.exports.binToBytes = function(bin) { + return bin.match(/......../g).map(function(x) { return parseInt(x,2) }); +} + +module.exports.bytesToBin = function(bytes) { + return bytes.map(function(x) { return x.toString(2).lpad('0',8) }).join(''); +} + +module.exports.bytesToString = function(bytes) { + return bytes.map(function(x){ return String.fromCharCode(x) }).join(''); +} + +module.exports.stringToBytes = function(string) { + return string.split('').map(function(x) { return x.charCodeAt(0) }); +} + +// utf8 diff --git a/src/crypto-js/crypto-min.js b/src/crypto-js/crypto-min.js deleted file mode 100755 index dc5b80d..0000000 --- a/src/crypto-js/crypto-min.js +++ /dev/null @@ -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<>>(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;g0;h--){g.push(Math.floor(Math.random()*256))}return g},bytesToWords:function(h){for(var k=[],j=0,g=0;j>>5]|=h[j]<<(24-g%32)}return k},wordsToBytes:function(i){for(var h=[],g=0;g>>5]>>>(24-g%32))&255)}return h},bytesToHex:function(g){for(var j=[],h=0;h>>4).toString(16));j.push((g[h]&15).toString(16))}return j.join("")},hexToBytes:function(h){for(var g=[],i=0;i>>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>>(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>> (32 - b)); - }, - - // Bit-wise rotate right - rotr: function (n, b) { - return (n << (32 - b)) | (n >>> b); - }, - - // Swap big-endian to little-endian and vice versa - endian: function (n) { - - // If number given, swap endian - if (n.constructor == Number) { - return util.rotl(n, 8) & 0x00FF00FF | - util.rotl(n, 24) & 0xFF00FF00; - } - - // Else, assume array and swap all items - for (var i = 0; i < n.length; i++) - n[i] = util.endian(n[i]); - return n; - - }, - - // Generate an array of any length of random bytes - randomBytes: function (n) { - for (var bytes = []; n > 0; n--) - bytes.push(Math.floor(Math.random() * 256)); - return bytes; - }, - - // Convert a byte array to big-endian 32-bit words - bytesToWords: function (bytes) { - for (var words = [], i = 0, b = 0; i < bytes.length; i++, b += 8) - words[b >>> 5] |= bytes[i] << (24 - b % 32); - return words; - }, - - // Convert big-endian 32-bit words to a byte array - wordsToBytes: function (words) { - for (var bytes = [], b = 0; b < words.length * 32; b += 8) - bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF); - return bytes; - }, - - // Convert a byte array to a hex string - 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 - 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 - 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 - 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; - - } - -}; - -// Crypto mode namespace -Crypto.mode = {}; - -// Crypto character encodings -var charenc = Crypto.charenc = {}; - -// UTF-8 encoding -var UTF8 = charenc.UTF8 = { - - // Convert a string to a byte array - stringToBytes: function (str) { - return Binary.stringToBytes(unescape(encodeURIComponent(str))); - }, - - // Convert a byte array to a string - bytesToString: function (bytes) { - return decodeURIComponent(escape(Binary.bytesToString(bytes))); - } - -}; - -// Binary encoding -var Binary = charenc.Binary = { - - // Convert a string to a byte array - stringToBytes: function (str) { - for (var bytes = [], i = 0; i < str.length; i++) - bytes.push(str.charCodeAt(i)); - return bytes; - }, - - // Convert a byte array to a string - bytesToString: function (bytes) { - for (var str = [], i = 0; i < bytes.length; i++) - str.push(String.fromCharCode(bytes[i])); - return str.join(""); - } - -}; - -})(); +/*! + * 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 + */ + +// Global Crypto object +var Crypto = module.exports = {}; + +// Crypto utilities +var util = Crypto.util = { + + // Generate an array of any length of random bytes + randomBytes: function (n) { + for (var bytes = []; n > 0; n--) + bytes.push(Math.floor(Math.random() * 256)); + return bytes; + }, + +}; + +// Crypto mode namespace +Crypto.mode = {}; + +// Crypto character encodings +var charenc = Crypto.charenc = {}; + +// UTF-8 encoding +var UTF8 = charenc.UTF8 = { + + // Convert a string to a byte array + stringToBytes: function (str) { + return Binary.stringToBytes(unescape(encodeURIComponent(str))); + }, + + // Convert a byte array to a string + bytesToString: function (bytes) { + return decodeURIComponent(escape(Binary.bytesToString(bytes))); + } + +}; + +// Binary encoding +var Binary = charenc.Binary = { + + // Convert a string to a byte array + stringToBytes: function (str) { + for (var bytes = [], i = 0; i < str.length; i++) + bytes.push(str.charCodeAt(i)); + return bytes; + }, + + // Convert a byte array to a string + bytesToString: function (bytes) { + for (var str = [], i = 0; i < bytes.length; i++) + str.push(String.fromCharCode(bytes[i])); + return str.join(""); + } + +}; + +module.exports.SHA256 = require('./sha256'); +module.exports.SHA512 = require('./sha512'); +module.exports.RIPEMD160 = require('./ripemd160'); +module.exports.HMAC = require('./hmac'); diff --git a/src/crypto-js/hmac.js b/src/crypto-js/hmac.js new file mode 100644 index 0000000..d849992 --- /dev/null +++ b/src/crypto-js/hmac.js @@ -0,0 +1,38 @@ +/*! + * 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 + */ + +var conv = require('../convert'), + util = require('../util'); + +// Shortcuts + +module.exports = function (hasher, message, key, options) { + + // Convert to byte arrays + if (message.constructor == String) message = conv.stringToBytes(message); + if (key.constructor == String) key = conv.stringToBytes(key); + /* else, assume byte arrays already */ + + // Allow arbitrary length keys + if (key.length > hasher._blocksize) + key = hasher(key, { asBytes: true }); + + // XOR keys with pad constants + var okey = key.slice(0), + ikey = key.slice(0); + for (var i = 0; i < hasher._blocksize; i++) { + okey[i] ^= 0x5C; + ikey[i] ^= 0x36; + } + + var hmacbytes = hasher(okey.concat(hasher(ikey.concat(message), { asBytes: true })), { asBytes: true }); + + return options && options.asBytes ? hmacbytes : + options && options.asString ? conv.bytesToString(hmacbytes) : + conv.bytesToHex(hmacbytes); + +}; diff --git a/src/crypto-js/ripemd160.js b/src/crypto-js/ripemd160.js old mode 100755 new mode 100644 index ff4b2a6..c0b04de --- a/src/crypto-js/ripemd160.js +++ b/src/crypto-js/ripemd160.js @@ -1,170 +1,209 @@ -/*! - * 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 - * - * A JavaScript implementation of the RIPEMD-160 Algorithm - * Version 2.2 Copyright Jeremy Lin, Paul Johnston 2000 - 2009. - * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet - * Distributed under the BSD License - * See http://pajhome.org.uk/crypt/md5 for details. - * Also http://www.ocf.berkeley.edu/~jjlin/jsotp/ - * Ported to Crypto-JS by Stefan Thomas. +/* +CryptoJS v3.1.2 +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +/** @preserve +(c) 2012 by Cédric Mesnil. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +var conv = require('../convert'); +var UTF8 = require('./crypto').charenc.UTF8; + +// Constants table +var zl = [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, + 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, + 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, + 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13]; +var zr = [ + 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, + 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, + 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, + 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, + 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11]; +var sl = [ + 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, + 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, + 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, + 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, + 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6 ]; +var sr = [ + 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, + 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, + 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, + 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, + 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11 ]; + +var hl = [ 0x00000000, 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xA953FD4E]; +var hr = [ 0x50A28BE6, 0x5C4DD124, 0x6D703EF3, 0x7A6D76E9, 0x00000000]; + +var bytesToWords = function (bytes) { + var words = []; + for (var i = 0, b = 0; i < bytes.length; i++, b += 8) { + words[b >>> 5] |= bytes[i] << (24 - b % 32); + } + return words; +}; + +var wordsToBytes = function (words) { + var bytes = []; + for (var b = 0; b < words.length * 32; b += 8) { + bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF); + } + return bytes; +}; + +var processBlock = function (H, M, offset) { + + // Swap endian + for (var i = 0; i < 16; i++) { + var offset_i = offset + i; + var M_offset_i = M[offset_i]; + + // Swap + M[offset_i] = ( + (((M_offset_i << 8) | (M_offset_i >>> 24)) & 0x00ff00ff) | + (((M_offset_i << 24) | (M_offset_i >>> 8)) & 0xff00ff00) + ); + } + + // Working variables + var al, bl, cl, dl, el; + var ar, br, cr, dr, er; + + ar = al = H[0]; + br = bl = H[1]; + cr = cl = H[2]; + dr = dl = H[3]; + er = el = H[4]; + // Computation + var t; + for (var i = 0; i < 80; i += 1) { + t = (al + M[offset+zl[i]])|0; + if (i<16){ + t += f1(bl,cl,dl) + hl[0]; + } else if (i<32) { + t += f2(bl,cl,dl) + hl[1]; + } else if (i<48) { + t += f3(bl,cl,dl) + hl[2]; + } else if (i<64) { + t += f4(bl,cl,dl) + hl[3]; + } else {// if (i<80) { + t += f5(bl,cl,dl) + hl[4]; + } + t = t|0; + t = rotl(t,sl[i]); + t = (t+el)|0; + al = el; + el = dl; + dl = rotl(cl, 10); + cl = bl; + bl = t; + + t = (ar + M[offset+zr[i]])|0; + if (i<16){ + t += f5(br,cr,dr) + hr[0]; + } else if (i<32) { + t += f4(br,cr,dr) + hr[1]; + } else if (i<48) { + t += f3(br,cr,dr) + hr[2]; + } else if (i<64) { + t += f2(br,cr,dr) + hr[3]; + } else {// if (i<80) { + t += f1(br,cr,dr) + hr[4]; + } + t = t|0; + t = rotl(t,sr[i]) ; + t = (t+er)|0; + ar = er; + er = dr; + dr = rotl(cr, 10); + cr = br; + br = t; + } + // Intermediate hash value + t = (H[1] + cl + dr)|0; + H[1] = (H[2] + dl + er)|0; + H[2] = (H[3] + el + ar)|0; + H[3] = (H[4] + al + br)|0; + H[4] = (H[0] + bl + cr)|0; + H[0] = t; +}; + +function f1(x, y, z) { + return ((x) ^ (y) ^ (z)); + +} + +function f2(x, y, z) { + return (((x)&(y)) | ((~x)&(z))); +} + +function f3(x, y, z) { + return (((x) | (~(y))) ^ (z)); +} + +function f4(x, y, z) { + return (((x) & (z)) | ((y)&(~(z)))); +} + +function f5(x, y, z) { + return ((x) ^ ((y) |(~(z)))); + +} + +function rotl(x,n) { + return (x<>>(32-n)); +} + +/** + * RIPEMD160 hash algorithm. */ +module.exports = function(message, options) { -(function () { - // Shortcuts - var C = Crypto, - util = C.util, - charenc = C.charenc, - UTF8 = charenc.UTF8, - Binary = charenc.Binary; + if (message.constructor === String) { + message = UTF8.stringToBytes(message); + } - // Convert a byte array to little-endian 32-bit words - util.bytesToLWords = function (bytes) { + var H = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0]; + var m = bytesToWords(message); - var output = Array(bytes.length >> 2); - for (var i = 0; i < output.length; i++) - output[i] = 0; - for (var i = 0; i < bytes.length * 8; i += 8) - output[i>>5] |= (bytes[i / 8] & 0xFF) << (i%32); - return output; - }; + var nBitsLeft = message.length * 8; + var nBitsTotal = message.length * 8; - // Convert little-endian 32-bit words to a byte array - util.lWordsToBytes = function (words) { - var output = []; - for (var i = 0; i < words.length * 32; i += 8) - output.push((words[i>>5] >>> (i % 32)) & 0xff); - return output; - }; + // Add padding + m[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32); + m[(((nBitsLeft + 64) >>> 9) << 4) + 14] = ( + (((nBitsTotal << 8) | (nBitsTotal >>> 24)) & 0x00ff00ff) | + (((nBitsTotal << 24) | (nBitsTotal >>> 8)) & 0xff00ff00) + ); - // Public API - var RIPEMD160 = C.RIPEMD160 = function (message, options) { - var digestbytes = util.lWordsToBytes(RIPEMD160._rmd160(message)); - return options && options.asBytes ? digestbytes : - options && options.asString ? Binary.bytesToString(digestbytes) : - util.bytesToHex(digestbytes); - }; + for (var i=0 ; i>> 24)) & 0x00ff00ff) | + (((H_i << 24) | (H_i >>> 8)) & 0xff00ff00); + } - /* append padding */ - x[len >> 5] |= 0x80 << (len % 32); - x[(((len + 64) >>> 9) << 4) + 14] = len; - - var h0 = 0x67452301; - var h1 = 0xefcdab89; - var h2 = 0x98badcfe; - var h3 = 0x10325476; - var h4 = 0xc3d2e1f0; - - for (var i = 0; i < x.length; i += 16) { - var T; - var A1 = h0, B1 = h1, C1 = h2, D1 = h3, E1 = h4; - var A2 = h0, B2 = h1, C2 = h2, D2 = h3, E2 = h4; - for (var j = 0; j <= 79; ++j) { - T = safe_add(A1, rmd160_f(j, B1, C1, D1)); - T = safe_add(T, x[i + rmd160_r1[j]]); - T = safe_add(T, rmd160_K1(j)); - T = safe_add(bit_rol(T, rmd160_s1[j]), E1); - A1 = E1; E1 = D1; D1 = bit_rol(C1, 10); C1 = B1; B1 = T; - T = safe_add(A2, rmd160_f(79-j, B2, C2, D2)); - T = safe_add(T, x[i + rmd160_r2[j]]); - T = safe_add(T, rmd160_K2(j)); - T = safe_add(bit_rol(T, rmd160_s2[j]), E2); - A2 = E2; E2 = D2; D2 = bit_rol(C2, 10); C2 = B2; B2 = T; - } - T = safe_add(h1, safe_add(C1, D2)); - h1 = safe_add(h2, safe_add(D1, E2)); - h2 = safe_add(h3, safe_add(E1, A2)); - h3 = safe_add(h4, safe_add(A1, B2)); - h4 = safe_add(h0, safe_add(B1, C2)); - h0 = T; - } - return [h0, h1, h2, h3, h4]; - } - - function rmd160_f(j, x, y, z) - { - return ( 0 <= j && j <= 15) ? (x ^ y ^ z) : - (16 <= j && j <= 31) ? (x & y) | (~x & z) : - (32 <= j && j <= 47) ? (x | ~y) ^ z : - (48 <= j && j <= 63) ? (x & z) | (y & ~z) : - (64 <= j && j <= 79) ? x ^ (y | ~z) : - "rmd160_f: j out of range"; - } - function rmd160_K1(j) - { - return ( 0 <= j && j <= 15) ? 0x00000000 : - (16 <= j && j <= 31) ? 0x5a827999 : - (32 <= j && j <= 47) ? 0x6ed9eba1 : - (48 <= j && j <= 63) ? 0x8f1bbcdc : - (64 <= j && j <= 79) ? 0xa953fd4e : - "rmd160_K1: j out of range"; - } - function rmd160_K2(j) - { - return ( 0 <= j && j <= 15) ? 0x50a28be6 : - (16 <= j && j <= 31) ? 0x5c4dd124 : - (32 <= j && j <= 47) ? 0x6d703ef3 : - (48 <= j && j <= 63) ? 0x7a6d76e9 : - (64 <= j && j <= 79) ? 0x00000000 : - "rmd160_K2: j out of range"; - } - var rmd160_r1 = [ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, - 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, - 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, - 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13 - ]; - var rmd160_r2 = [ - 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, - 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, - 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, - 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, - 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11 - ]; - var rmd160_s1 = [ - 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, - 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, - 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, - 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, - 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6 - ]; - var rmd160_s2 = [ - 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, - 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, - 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, - 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, - 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11 - ]; - - /* - * Add integers, wrapping at 2^32. This uses 16-bit operations internally - * to work around bugs in some JS interpreters. - */ - function safe_add(x, y) - { - var lsw = (x & 0xFFFF) + (y & 0xFFFF); - var msw = (x >> 16) + (y >> 16) + (lsw >> 16); - return (msw << 16) | (lsw & 0xFFFF); - } - - /* - * Bitwise rotate a 32-bit number to the left. - */ - function bit_rol(num, cnt) - { - return (num << cnt) | (num >>> (32 - cnt)); - } -})(); + var digestbytes = wordsToBytes(H); + return options && options.asBytes ? digestbytes : + options && options.asString ? Binary.bytesToString(digestbytes) : + conv.bytesToHex(digestbytes); +}; diff --git a/src/crypto-js/sha256-min.js b/src/crypto-js/sha256-min.js deleted file mode 100755 index d3a4118..0000000 --- a/src/crypto-js/sha256-min.js +++ /dev/null @@ -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>>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})(); \ No newline at end of file diff --git a/src/crypto-js/sha256.js b/src/crypto-js/sha256.js old mode 100755 new mode 100644 index c5a2d01..ce9f87b --- a/src/crypto-js/sha256.js +++ b/src/crypto-js/sha256.js @@ -1,133 +1,134 @@ -/*! - * 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 +/* +CryptoJS v3.1.2 +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ + +var conv = require('../convert'), + util = require('../util'); + +// Initialization round constants tables +var K = []; + +// Compute constants +(function () { + function isPrime(n) { + var sqrtN = Math.sqrt(n); + for (var factor = 2; factor <= sqrtN; factor++) { + if (!(n % factor)) { + return false; + } + } + + return true; + } + + function getFractionalBits(n) { + return ((n - (n | 0)) * 0x100000000) | 0; + } + + var n = 2; + var nPrime = 0; + while (nPrime < 64) { + if (isPrime(n)) { + K[nPrime] = getFractionalBits(Math.pow(n, 1 / 3)); + nPrime++; + } + + n++; + } +}()); + +// Reusable object +var W = []; + +var processBlock = function (H, M, offset) { + + // Working variables + var a = H[0]; + var b = H[1]; + var c = H[2]; + var d = H[3]; + var e = H[4]; + var f = H[5]; + var g = H[6]; + var h = H[7]; + + // Computation + for (var i = 0; i < 64; i++) { + if (i < 16) { + W[i] = M[offset + i] | 0; + } else { + var gamma0x = W[i - 15]; + var gamma0 = ((gamma0x << 25) | (gamma0x >>> 7)) ^ + ((gamma0x << 14) | (gamma0x >>> 18)) ^ + (gamma0x >>> 3); + + var gamma1x = W[i - 2]; + var gamma1 = ((gamma1x << 15) | (gamma1x >>> 17)) ^ + ((gamma1x << 13) | (gamma1x >>> 19)) ^ + (gamma1x >>> 10); + + W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16]; + } + + var ch = (e & f) ^ (~e & g); + var maj = (a & b) ^ (a & c) ^ (b & c); + + var sigma0 = ((a << 30) | (a >>> 2)) ^ ((a << 19) | (a >>> 13)) ^ ((a << 10) | (a >>> 22)); + var sigma1 = ((e << 26) | (e >>> 6)) ^ ((e << 21) | (e >>> 11)) ^ ((e << 7) | (e >>> 25)); + + var t1 = h + sigma1 + ch + K[i] + W[i]; + var t2 = sigma0 + maj; + + h = g; + g = f; + f = e; + e = (d + t1) | 0; + d = c; + c = b; + b = a; + a = (t1 + t2) | 0; + } + + // Intermediate hash value + H[0] = (H[0] + a) | 0; + H[1] = (H[1] + b) | 0; + H[2] = (H[2] + c) | 0; + H[3] = (H[3] + d) | 0; + H[4] = (H[4] + e) | 0; + H[5] = (H[5] + f) | 0; + H[6] = (H[6] + g) | 0; + H[7] = (H[7] + h) | 0; +}; + +/** + * SHA-256 hash algorithm. */ -(function(){ +module.exports = function(message, options) {; -// Shortcuts -var C = Crypto, - util = C.util, - charenc = C.charenc, - UTF8 = charenc.UTF8, - Binary = charenc.Binary; + var H =[ 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, + 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 ]; -// Constants -var K = [ 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, - 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, - 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, - 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, - 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, - 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, - 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, - 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, - 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, - 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, - 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, - 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, - 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, - 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, - 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, - 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2 ]; + if (message.constructor === String) { + message = conv.stringToBytes(message); + } -// Public API -var SHA256 = C.SHA256 = function (message, options) { - var digestbytes = util.wordsToBytes(SHA256._sha256(message)); - return options && options.asBytes ? digestbytes : - options && options.asString ? Binary.bytesToString(digestbytes) : - util.bytesToHex(digestbytes); + var m = util.bytesToWords(message); + var l = message.length * 8; + + m[l >> 5] |= 0x80 << (24 - l % 32); + m[((l + 64 >> 9) << 4) + 15] = l; + + for (var i=0 ; i> 5] |= 0x80 << (24 - l % 32); - m[((l + 64 >> 9) << 4) + 15] = l; - - for (var i = 0; i < m.length; i += 16) { - - a = H[0]; - b = H[1]; - c = H[2]; - d = H[3]; - e = H[4]; - f = H[5]; - g = H[6]; - h = H[7]; - - for (var j = 0; j < 64; j++) { - - if (j < 16) w[j] = m[j + i]; - else { - - var gamma0x = w[j - 15], - gamma1x = w[j - 2], - gamma0 = ((gamma0x << 25) | (gamma0x >>> 7)) ^ - ((gamma0x << 14) | (gamma0x >>> 18)) ^ - (gamma0x >>> 3), - gamma1 = ((gamma1x << 15) | (gamma1x >>> 17)) ^ - ((gamma1x << 13) | (gamma1x >>> 19)) ^ - (gamma1x >>> 10); - - w[j] = gamma0 + (w[j - 7] >>> 0) + - gamma1 + (w[j - 16] >>> 0); - - } - - var ch = e & f ^ ~e & g, - maj = a & b ^ a & c ^ b & c, - sigma0 = ((a << 30) | (a >>> 2)) ^ - ((a << 19) | (a >>> 13)) ^ - ((a << 10) | (a >>> 22)), - sigma1 = ((e << 26) | (e >>> 6)) ^ - ((e << 21) | (e >>> 11)) ^ - ((e << 7) | (e >>> 25)); - - - t1 = (h >>> 0) + sigma1 + ch + (K[j]) + (w[j] >>> 0); - t2 = sigma0 + maj; - - h = g; - g = f; - f = e; - e = d + t1; - d = c; - c = b; - b = a; - a = t1 + t2; - - } - - H[0] += a; - H[1] += b; - H[2] += c; - H[3] += d; - H[4] += e; - H[5] += f; - H[6] += g; - H[7] += h; - - } - - return H; - -}; - -// Package private blocksize -SHA256._blocksize = 16; - -})(); +module.exports._blocksize = 64 diff --git a/src/crypto-js/sha512.js b/src/crypto-js/sha512.js new file mode 100644 index 0000000..d9bc3e7 --- /dev/null +++ b/src/crypto-js/sha512.js @@ -0,0 +1,239 @@ +var conv = require('../convert'); +var util = require('../util'); + + var K = [ + [0x428a2f98, 0xd728ae22], [0x71374491, 0x23ef65cd], + [0xb5c0fbcf, 0xec4d3b2f], [0xe9b5dba5, 0x8189dbbc], + [0x3956c25b, 0xf348b538], [0x59f111f1, 0xb605d019], + [0x923f82a4, 0xaf194f9b], [0xab1c5ed5, 0xda6d8118], + [0xd807aa98, 0xa3030242], [0x12835b01, 0x45706fbe], + [0x243185be, 0x4ee4b28c], [0x550c7dc3, 0xd5ffb4e2], + [0x72be5d74, 0xf27b896f], [0x80deb1fe, 0x3b1696b1], + [0x9bdc06a7, 0x25c71235], [0xc19bf174, 0xcf692694], + [0xe49b69c1, 0x9ef14ad2], [0xefbe4786, 0x384f25e3], + [0x0fc19dc6, 0x8b8cd5b5], [0x240ca1cc, 0x77ac9c65], + [0x2de92c6f, 0x592b0275], [0x4a7484aa, 0x6ea6e483], + [0x5cb0a9dc, 0xbd41fbd4], [0x76f988da, 0x831153b5], + [0x983e5152, 0xee66dfab], [0xa831c66d, 0x2db43210], + [0xb00327c8, 0x98fb213f], [0xbf597fc7, 0xbeef0ee4], + [0xc6e00bf3, 0x3da88fc2], [0xd5a79147, 0x930aa725], + [0x06ca6351, 0xe003826f], [0x14292967, 0x0a0e6e70], + [0x27b70a85, 0x46d22ffc], [0x2e1b2138, 0x5c26c926], + [0x4d2c6dfc, 0x5ac42aed], [0x53380d13, 0x9d95b3df], + [0x650a7354, 0x8baf63de], [0x766a0abb, 0x3c77b2a8], + [0x81c2c92e, 0x47edaee6], [0x92722c85, 0x1482353b], + [0xa2bfe8a1, 0x4cf10364], [0xa81a664b, 0xbc423001], + [0xc24b8b70, 0xd0f89791], [0xc76c51a3, 0x0654be30], + [0xd192e819, 0xd6ef5218], [0xd6990624, 0x5565a910], + [0xf40e3585, 0x5771202a], [0x106aa070, 0x32bbd1b8], + [0x19a4c116, 0xb8d2d0c8], [0x1e376c08, 0x5141ab53], + [0x2748774c, 0xdf8eeb99], [0x34b0bcb5, 0xe19b48a8], + [0x391c0cb3, 0xc5c95a63], [0x4ed8aa4a, 0xe3418acb], + [0x5b9cca4f, 0x7763e373], [0x682e6ff3, 0xd6b2b8a3], + [0x748f82ee, 0x5defb2fc], [0x78a5636f, 0x43172f60], + [0x84c87814, 0xa1f0ab72], [0x8cc70208, 0x1a6439ec], + [0x90befffa, 0x23631e28], [0xa4506ceb, 0xde82bde9], + [0xbef9a3f7, 0xb2c67915], [0xc67178f2, 0xe372532b], + [0xca273ece, 0xea26619c], [0xd186b8c7, 0x21c0c207], + [0xeada7dd6, 0xcde0eb1e], [0xf57d4f7f, 0xee6ed178], + [0x06f067aa, 0x72176fba], [0x0a637dc5, 0xa2c898a6], + [0x113f9804, 0xbef90dae], [0x1b710b35, 0x131c471b], + [0x28db77f5, 0x23047d84], [0x32caab7b, 0x40c72493], + [0x3c9ebe0a, 0x15c9bebc], [0x431d67c4, 0x9c100d4c], + [0x4cc5d4be, 0xcb3e42b6], [0x597f299c, 0xfc657e2a], + [0x5fcb6fab, 0x3ad6faec], [0x6c44198c, 0x4a475817] + ]; + // Reusable objects + var W = []; + for (var i = 0; i < 80; i++) W.push([0,0]) + + /** + * SHA-512 hash algorithm. + */ + var processBlock = function (H, M, offset) { + // Shortcuts + var H0 = H[0]; + var H1 = H[1]; + var H2 = H[2]; + var H3 = H[3]; + var H4 = H[4]; + var H5 = H[5]; + var H6 = H[6]; + var H7 = H[7]; + + var H0h = H0[0]; + var H0l = H0[1]; + var H1h = H1[0]; + var H1l = H1[1]; + var H2h = H2[0]; + var H2l = H2[1]; + var H3h = H3[0]; + var H3l = H3[1]; + var H4h = H4[0]; + var H4l = H4[1]; + var H5h = H5[0]; + var H5l = H5[1]; + var H6h = H6[0]; + var H6l = H6[1]; + var H7h = H7[0]; + var H7l = H7[1]; + + // Working variables + var ah = H0h; + var al = H0l; + var bh = H1h; + var bl = H1l; + var ch = H2h; + var cl = H2l; + var dh = H3h; + var dl = H3l; + var eh = H4h; + var el = H4l; + var fh = H5h; + var fl = H5l; + var gh = H6h; + var gl = H6l; + var hh = H7h; + var hl = H7l; + + // Rounds + for (var i = 0; i < 80; i++) { + // Shortcut + var Wi = W[i]; + + // Extend message + if (i < 16) { + var Wih = Wi[0] = M[offset + i * 2] | 0; + var Wil = Wi[1] = M[offset + i * 2 + 1] | 0; + } else { + // Gamma0 + var gamma0x = W[i - 15]; + var gamma0xh = gamma0x[0]; + var gamma0xl = gamma0x[1]; + var gamma0h = ((gamma0xh >>> 1) | (gamma0xl << 31)) ^ ((gamma0xh >>> 8) | (gamma0xl << 24)) ^ (gamma0xh >>> 7); + var gamma0l = ((gamma0xl >>> 1) | (gamma0xh << 31)) ^ ((gamma0xl >>> 8) | (gamma0xh << 24)) ^ ((gamma0xl >>> 7) | (gamma0xh << 25)); + + // Gamma1 + var gamma1x = W[i - 2]; + var gamma1xh = gamma1x[0]; + var gamma1xl = gamma1x[1]; + var gamma1h = ((gamma1xh >>> 19) | (gamma1xl << 13)) ^ ((gamma1xh << 3) | (gamma1xl >>> 29)) ^ (gamma1xh >>> 6); + var gamma1l = ((gamma1xl >>> 19) | (gamma1xh << 13)) ^ ((gamma1xl << 3) | (gamma1xh >>> 29)) ^ ((gamma1xl >>> 6) | (gamma1xh << 26)); + + // W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16] + var Wi7 = W[i - 7]; + var Wi7h = Wi7[0]; + var Wi7l = Wi7[1]; + + var Wi16 = W[i - 16]; + var Wi16h = Wi16[0]; + var Wi16l = Wi16[1]; + + var Wil = gamma0l + Wi7l; + var Wih = gamma0h + Wi7h + ((Wil >>> 0) < (gamma0l >>> 0) ? 1 : 0); + var Wil = Wil + gamma1l; + var Wih = Wih + gamma1h + ((Wil >>> 0) < (gamma1l >>> 0) ? 1 : 0); + var Wil = Wil + Wi16l; + var Wih = Wih + Wi16h + ((Wil >>> 0) < (Wi16l >>> 0) ? 1 : 0); + + Wi[0] = Wih; + Wi[1] = Wil; + } + + var chh = (eh & fh) ^ (~eh & gh); + var chl = (el & fl) ^ (~el & gl); + var majh = (ah & bh) ^ (ah & ch) ^ (bh & ch); + var majl = (al & bl) ^ (al & cl) ^ (bl & cl); + + var sigma0h = ((ah >>> 28) | (al << 4)) ^ ((ah << 30) | (al >>> 2)) ^ ((ah << 25) | (al >>> 7)); + var sigma0l = ((al >>> 28) | (ah << 4)) ^ ((al << 30) | (ah >>> 2)) ^ ((al << 25) | (ah >>> 7)); + var sigma1h = ((eh >>> 14) | (el << 18)) ^ ((eh >>> 18) | (el << 14)) ^ ((eh << 23) | (el >>> 9)); + var sigma1l = ((el >>> 14) | (eh << 18)) ^ ((el >>> 18) | (eh << 14)) ^ ((el << 23) | (eh >>> 9)); + + // t1 = h + sigma1 + ch + K[i] + W[i] + var Ki = K[i]; + var Kih = Ki[0]; + var Kil = Ki[1]; + + var t1l = hl + sigma1l; + var t1h = hh + sigma1h + ((t1l >>> 0) < (hl >>> 0) ? 1 : 0); + var t1l = t1l + chl; + var t1h = t1h + chh + ((t1l >>> 0) < (chl >>> 0) ? 1 : 0); + var t1l = t1l + Kil; + var t1h = t1h + Kih + ((t1l >>> 0) < (Kil >>> 0) ? 1 : 0); + var t1l = t1l + Wil; + var t1h = t1h + Wih + ((t1l >>> 0) < (Wil >>> 0) ? 1 : 0); + + // t2 = sigma0 + maj + var t2l = sigma0l + majl; + var t2h = sigma0h + majh + ((t2l >>> 0) < (sigma0l >>> 0) ? 1 : 0); + + // Update working variables + hh = gh; + hl = gl; + gh = fh; + gl = fl; + fh = eh; + fl = el; + el = (dl + t1l) | 0; + eh = (dh + t1h + ((el >>> 0) < (dl >>> 0) ? 1 : 0)) | 0; + dh = ch; + dl = cl; + ch = bh; + cl = bl; + bh = ah; + bl = al; + al = (t1l + t2l) | 0; + ah = (t1h + t2h + ((al >>> 0) < (t1l >>> 0) ? 1 : 0)) | 0; + } + + // Intermediate hash value + H0l = H0[1] = (H0l + al); + H0[0] = (H0h + ah + ((H0l >>> 0) < (al >>> 0) ? 1 : 0)); + H1l = H1[1] = (H1l + bl); + H1[0] = (H1h + bh + ((H1l >>> 0) < (bl >>> 0) ? 1 : 0)); + H2l = H2[1] = (H2l + cl); + H2[0] = (H2h + ch + ((H2l >>> 0) < (cl >>> 0) ? 1 : 0)); + H3l = H3[1] = (H3l + dl); + H3[0] = (H3h + dh + ((H3l >>> 0) < (dl >>> 0) ? 1 : 0)); + H4l = H4[1] = (H4l + el); + H4[0] = (H4h + eh + ((H4l >>> 0) < (el >>> 0) ? 1 : 0)); + H5l = H5[1] = (H5l + fl); + H5[0] = (H5h + fh + ((H5l >>> 0) < (fl >>> 0) ? 1 : 0)); + H6l = H6[1] = (H6l + gl); + H6[0] = (H6h + gh + ((H6l >>> 0) < (gl >>> 0) ? 1 : 0)); + H7l = H7[1] = (H7l + hl); + H7[0] = (H7h + hh + ((H7l >>> 0) < (hl >>> 0) ? 1 : 0)); + return H + } + +module.exports = function(message, options) {; + + var H = [ + [0x6a09e667, 0xf3bcc908], [0xbb67ae85, 0x84caa73b], + [0x3c6ef372, 0xfe94f82b], [0xa54ff53a, 0x5f1d36f1], + [0x510e527f, 0xade682d1], [0x9b05688c, 0x2b3e6c1f], + [0x1f83d9ab, 0xfb41bd6b], [0x5be0cd19, 0x137e2179] + ]; + + if (message.constructor === String) { + message = conv.stringToBytes(message); + } + + var m = util.bytesToWords(message); + var l = message.length * 8; + + m[l >> 5] |= 0x80 << (24 - l % 32); + m[((l + 64 >> 10) << 5) + 31] = l; + + for (var i=0 ; i bytes.length) { - bytes.unshift(0); - } +var ECPointFp = require('./jsbn/ec').ECPointFp; - return bytes; -}; +var rng = new SecureRandom(); +var ecparams = sec("secp256k1"); +var P_OVER_FOUR = null; -ECFieldElementFp.prototype.getByteLength = function () { - return Math.floor((this.toBigInteger().bitLength() + 7) / 8); -}; +function implShamirsTrick(P, k, Q, l) +{ + var m = Math.max(k.bitLength(), l.bitLength()); + var Z = P.add2D(Q); + var R = P.curve.getInfinity(); -ECPointFp.prototype.getEncoded = function (compressed) { - var x = this.getX().toBigInteger(); - var y = this.getY().toBigInteger(); + for (var i = m - 1; i >= 0; --i) { + R = R.twice2D(); - // Get value as a 32-byte Buffer - // Fixed length based on a patch by bitaddress.org and Casascius - var enc = integerToBytes(x, 32); + R.z = BigInteger.ONE; - if (compressed) { - if (y.isEven()) { - // Compressed even pubkey - // M = 02 || X - enc.unshift(0x02); + if (k.testBit(i)) { + if (l.testBit(i)) { + R = R.add2D(Z); + } else { + R = R.add2D(P); + } } else { - // Compressed uneven pubkey - // M = 03 || X - enc.unshift(0x03); - } - } else { - // Uncompressed pubkey - // M = 04 || X || Y - enc.unshift(0x04); - enc = enc.concat(integerToBytes(y, 32)); - } - return enc; -}; - -ECPointFp.decodeFrom = function (curve, enc) { - var type = enc[0]; - var dataLen = enc.length-1; - - // Extract x and y as byte arrays - var xBa = enc.slice(1, 1 + dataLen/2); - var yBa = enc.slice(1 + dataLen/2, 1 + dataLen); - - // Prepend zero byte to prevent interpretation as negative integer - xBa.unshift(0); - yBa.unshift(0); - - // Convert to BigIntegers - var x = new BigInteger(xBa); - var y = new BigInteger(yBa); - - // Return point - return new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y)); -}; - -ECPointFp.prototype.add2D = function (b) { - if(this.isInfinity()) return b; - if(b.isInfinity()) return this; - - if (this.x.equals(b.x)) { - if (this.y.equals(b.y)) { - // this = b, i.e. this must be doubled - return this.twice(); - } - // this = -b, i.e. the result is the point at infinity - return this.curve.getInfinity(); - } - - var x_x = b.x.subtract(this.x); - var y_y = b.y.subtract(this.y); - var gamma = y_y.divide(x_x); - - var x3 = gamma.square().subtract(this.x).subtract(b.x); - var y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y); - - return new ECPointFp(this.curve, x3, y3); -}; - -ECPointFp.prototype.twice2D = function () { - if (this.isInfinity()) return this; - if (this.y.toBigInteger().signum() == 0) { - // if y1 == 0, then (x1, y1) == (x1, -y1) - // and hence this = -this and thus 2(x1, y1) == infinity - return this.curve.getInfinity(); - } - - var TWO = this.curve.fromBigInteger(BigInteger.valueOf(2)); - var THREE = this.curve.fromBigInteger(BigInteger.valueOf(3)); - var gamma = this.x.square().multiply(THREE).add(this.curve.a).divide(this.y.multiply(TWO)); - - var x3 = gamma.square().subtract(this.x.multiply(TWO)); - var y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y); - - return new ECPointFp(this.curve, x3, y3); -}; - -ECPointFp.prototype.multiply2D = function (k) { - if(this.isInfinity()) return this; - if(k.signum() == 0) return this.curve.getInfinity(); - - var e = k; - var h = e.multiply(new BigInteger("3")); - - var neg = this.negate(); - var R = this; - - var i; - for (i = h.bitLength() - 2; i > 0; --i) { - R = R.twice(); - - var hBit = h.testBit(i); - var eBit = e.testBit(i); - - if (hBit != eBit) { - R = R.add2D(hBit ? this : neg); + if (l.testBit(i)) { + R = R.add2D(Q); + } } } return R; }; -ECPointFp.prototype.isOnCurve = function () { - var x = this.getX().toBigInteger(); - var y = this.getY().toBigInteger(); - var a = this.curve.getA().toBigInteger(); - var b = this.curve.getB().toBigInteger(); - var n = this.curve.getQ(); - var lhs = y.multiply(y).mod(n); - var rhs = x.multiply(x).multiply(x) - .add(a.multiply(x)).add(b).mod(n); - return lhs.equals(rhs); -}; +function deterministicGenerateK(hash,key) { + var v = []; + var k = []; + for (var i = 0;i < 32;i++) v.push(1); + for (var i = 0;i < 32;i++) k.push(0); + k = Crypto.HMAC(Crypto.SHA256,v.concat([0]).concat(key).concat(hash),k,{ asBytes: true }) + v = Crypto.HMAC(Crypto.SHA256,v,k,{ asBytes: true }) + k = Crypto.HMAC(Crypto.SHA256,v.concat([1]).concat(key).concat(hash),k,{ asBytes: true }) + v = Crypto.HMAC(Crypto.SHA256,v,k,{ asBytes: true }) + v = Crypto.HMAC(Crypto.SHA256,v,k,{ asBytes: true }) + return BigInteger.fromByteArrayUnsigned(v); +} -ECPointFp.prototype.toString = function () { - return '('+this.getX().toBigInteger().toString()+','+ - this.getY().toBigInteger().toString()+')'; -}; +var ECDSA = { + getBigRandom: function (limit) { + return new BigInteger(limit.bitLength(), rng) + .mod(limit.subtract(BigInteger.ONE)) + .add(BigInteger.ONE) + ; + }, + sign: function (hash, priv) { + var d = priv; + var n = ecparams.getN(); + var e = BigInteger.fromByteArrayUnsigned(hash); -/** - * Validate an elliptic curve point. - * - * See SEC 1, section 3.2.2.1: Elliptic Curve Public Key Validation Primitive - */ -ECPointFp.prototype.validate = function () { - var n = this.curve.getQ(); + var k = deterministicGenerateK(hash,priv.toByteArrayUnsigned()) + var G = ecparams.getG(); + var Q = G.multiply(k); + var r = Q.getX().toBigInteger().mod(n); - // Check Q != O - if (this.isInfinity()) { - throw new Error("Point is at infinity."); - } + var s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n); - // Check coordinate bounds - var x = this.getX().toBigInteger(); - var y = this.getY().toBigInteger(); - if (x.compareTo(BigInteger.ONE) < 0 || - x.compareTo(n.subtract(BigInteger.ONE)) > 0) { - throw new Error('x coordinate out of bounds'); - } - if (y.compareTo(BigInteger.ONE) < 0 || - y.compareTo(n.subtract(BigInteger.ONE)) > 0) { - throw new Error('y coordinate out of bounds'); - } + return ECDSA.serializeSig(r, s); + }, - // Check y^2 = x^3 + ax + b (mod n) - if (!this.isOnCurve()) { - throw new Error("Point is not on the curve."); - } + verify: function (hash, sig, pubkey) { + var r,s; + if (util.isArray(sig)) { + var obj = ECDSA.parseSig(sig); + r = obj.r; + s = obj.s; + } else if ("object" === typeof sig && sig.r && sig.s) { + r = sig.r; + s = sig.s; + } else { + throw new Error("Invalid value for signature"); + } - // Check nQ = 0 (Q is a scalar multiple of G) - if (this.multiply(n).isInfinity()) { - // TODO: This check doesn't work - fix. - throw new Error("Point is not a scalar multiple of G."); - } + var Q; + if (pubkey instanceof ECPointFp) { + Q = pubkey; + } else if (util.isArray(pubkey)) { + Q = ECPointFp.decodeFrom(ecparams.getCurve(), pubkey); + } else { + throw new Error("Invalid format for pubkey value, must be byte array or ECPointFp"); + } + var e = BigInteger.fromByteArrayUnsigned(hash); - return true; -}; + return ECDSA.verifyRaw(e, r, s, Q); + }, -function dmp(v) { - if (!(v instanceof BigInteger)) v = v.toBigInteger(); - return Crypto.util.bytesToHex(v.toByteArrayUnsigned()); -}; + verifyRaw: function (e, r, s, Q) { + var n = ecparams.getN(); + var G = ecparams.getG(); -Bitcoin.ECDSA = (function () { - var ecparams = getSECCurveByName("secp256k1"); - var rng = new SecureRandom(); + if (r.compareTo(BigInteger.ONE) < 0 || + r.compareTo(n) >= 0) + return false; - var P_OVER_FOUR = null; + if (s.compareTo(BigInteger.ONE) < 0 || + s.compareTo(n) >= 0) + return false; - function implShamirsTrick(P, k, Q, l) + var c = s.modInverse(n); + + var u1 = e.multiply(c).mod(n); + var u2 = r.multiply(c).mod(n); + + // TODO(!!!): For some reason Shamir's trick isn't working with + // signed message verification!? Probably an implementation + // error! + //var point = implShamirsTrick(G, u1, Q, u2); + var point = G.multiply(u1).add(Q.multiply(u2)); + + var v = point.getX().toBigInteger().mod(n); + + return v.equals(r); + }, + + /** + * Serialize a signature into DER format. + * + * Takes two BigIntegers representing r and s and returns a byte array. + */ + serializeSig: function (r, s) { + var rBa = r.toByteArraySigned(); + var sBa = s.toByteArraySigned(); + + var sequence = []; + sequence.push(0x02); // INTEGER + sequence.push(rBa.length); + sequence = sequence.concat(rBa); + + sequence.push(0x02); // INTEGER + sequence.push(sBa.length); + sequence = sequence.concat(sBa); + + sequence.unshift(sequence.length); + sequence.unshift(0x30); // SEQUENCE + + return sequence; + }, + + /** + * Parses a byte array containing a DER-encoded signature. + * + * This function will return an object of the form: + * + * { + * r: BigInteger, + * s: BigInteger + * } + */ + parseSig: function (sig) { + var cursor; + if (sig[0] != 0x30) + throw new Error("Signature not a valid DERSequence"); + + cursor = 2; + if (sig[cursor] != 0x02) + throw new Error("First element in signature must be a DERInteger");; + var rBa = sig.slice(cursor+2, cursor+2+sig[cursor+1]); + + cursor += 2+sig[cursor+1]; + if (sig[cursor] != 0x02) + throw new Error("Second element in signature must be a DERInteger"); + var sBa = sig.slice(cursor+2, cursor+2+sig[cursor+1]); + + cursor += 2+sig[cursor+1]; + + //if (cursor != sig.length) + // throw new Error("Extra bytes in signature"); + + var r = BigInteger.fromByteArrayUnsigned(rBa); + var s = BigInteger.fromByteArrayUnsigned(sBa); + + return {r: r, s: s}; + }, + + parseSigCompact: function (sig) { + if (sig.length !== 65) { + throw new Error("Signature has the wrong length"); + } + + // Signature is prefixed with a type byte storing three bits of + // information. + var i = sig[0] - 27; + if (i < 0 || i > 7) { + throw new Error("Invalid signature type"); + } + + var n = ecparams.getN(); + var r = BigInteger.fromByteArrayUnsigned(sig.slice(1, 33)).mod(n); + var s = BigInteger.fromByteArrayUnsigned(sig.slice(33, 65)).mod(n); + + return {r: r, s: s, i: i}; + }, + + /** + * Recover a public key from a signature. + * + * See SEC 1: Elliptic Curve Cryptography, section 4.1.6, "Public + * Key Recovery Operation". + * + * http://www.secg.org/download/aid-780/sec1-v2.pdf + */ + recoverPubKey: function (r, s, hash, i) { + // The recovery parameter i has two bits. + i = i & 3; + + // The less significant bit specifies whether the y coordinate + // of the compressed point is even or not. + var isYEven = i & 1; + + // The more significant bit specifies whether we should use the + // first or second candidate key. + var isSecondKey = i >> 1; + + var n = ecparams.getN(); + var G = ecparams.getG(); + var curve = ecparams.getCurve(); + var p = curve.getQ(); + var a = curve.getA().toBigInteger(); + var b = curve.getB().toBigInteger(); + + // We precalculate (p + 1) / 4 where p is if the field order + if (!P_OVER_FOUR) { + P_OVER_FOUR = p.add(BigInteger.ONE).divide(BigInteger.valueOf(4)); + } + + // 1.1 Compute x + var x = isSecondKey ? r.add(n) : r; + + // 1.3 Convert x to point + var alpha = x.multiply(x).multiply(x).add(a.multiply(x)).add(b).mod(p); + var beta = alpha.modPow(P_OVER_FOUR, p); + + var xorOdd = beta.isEven() ? (i % 2) : ((i+1) % 2); + // If beta is even, but y isn't or vice versa, then convert it, + // otherwise we're done and y == beta. + var y = (beta.isEven() ? !isYEven : isYEven) ? beta : p.subtract(beta); + + // 1.4 Check that nR is at infinity + var R = new ECPointFp(curve, + curve.fromBigInteger(x), + curve.fromBigInteger(y)); + R.validate(); + + // 1.5 Compute e from M + var e = BigInteger.fromByteArrayUnsigned(hash); + var eNeg = BigInteger.ZERO.subtract(e).mod(n); + + // 1.6 Compute Q = r^-1 (sR - eG) + var rInv = r.modInverse(n); + var Q = implShamirsTrick(R, s, G, eNeg).multiply(rInv); + + Q.validate(); + if (!ECDSA.verifyRaw(e, r, s, Q)) { + throw new Error("Pubkey recovery unsuccessful"); + } + + // TODO (shtylman) this is stupid because this file and eckey + // have circular dependencies + var ECPubKey = require('./eckey').ECPubKey; + return ECPubKey(Q); + }, + + /** + * Calculate pubkey extraction parameter. + * + * When extracting a pubkey from a signature, we have to + * distinguish four different cases. Rather than putting this + * burden on the verifier, Bitcoin includes a 2-bit value with the + * signature. + * + * This function simply tries all four cases and returns the value + * that resulted in a successful pubkey recovery. + */ + calcPubkeyRecoveryParam: function (origPubkey, r, s, hash) { - var m = Math.max(k.bitLength(), l.bitLength()); - var Z = P.add2D(Q); - var R = P.curve.getInfinity(); - - for (var i = m - 1; i >= 0; --i) { - R = R.twice2D(); - - R.z = BigInteger.ONE; - - if (k.testBit(i)) { - if (l.testBit(i)) { - R = R.add2D(Z); - } else { - R = R.add2D(P); - } - } else { - if (l.testBit(i)) { - R = R.add2D(Q); - } + var address = origPubkey.getBitcoinAddress().toString(); + for (var i = 0; i < 4; i++) { + var pubkey = ECDSA.recoverPubKey(r, s, hash, i); + pubkey.compressed = origPubkey.compressed; + if (pubkey.getBitcoinAddress().toString() == address) { + return i; } } - return R; - }; + throw new Error("Unable to find valid recovery factor"); + } +}; - var ECDSA = { - getBigRandom: function (limit) { - return new BigInteger(limit.bitLength(), rng) - .mod(limit.subtract(BigInteger.ONE)) - .add(BigInteger.ONE) - ; - }, - sign: function (hash, priv) { - var d = priv; - var n = ecparams.getN(); - var e = BigInteger.fromByteArrayUnsigned(hash); +module.exports = ECDSA; - do { - var k = ECDSA.getBigRandom(n); - var G = ecparams.getG(); - var Q = G.multiply(k); - var r = Q.getX().toBigInteger().mod(n); - } while (r.compareTo(BigInteger.ZERO) <= 0); - - var s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n); - - return ECDSA.serializeSig(r, s); - }, - - verify: function (hash, sig, pubkey) { - var r,s; - if (Bitcoin.Util.isArray(sig)) { - var obj = ECDSA.parseSig(sig); - r = obj.r; - s = obj.s; - } else if ("object" === typeof sig && sig.r && sig.s) { - r = sig.r; - s = sig.s; - } else { - throw "Invalid value for signature"; - } - - var Q; - if (pubkey instanceof ECPointFp) { - Q = pubkey; - } else if (Bitcoin.Util.isArray(pubkey)) { - Q = ECPointFp.decodeFrom(ecparams.getCurve(), pubkey); - } else { - throw "Invalid format for pubkey value, must be byte array or ECPointFp"; - } - var e = BigInteger.fromByteArrayUnsigned(hash); - - return ECDSA.verifyRaw(e, r, s, Q); - }, - - verifyRaw: function (e, r, s, Q) { - var n = ecparams.getN(); - var G = ecparams.getG(); - - if (r.compareTo(BigInteger.ONE) < 0 || - r.compareTo(n) >= 0) - return false; - - if (s.compareTo(BigInteger.ONE) < 0 || - s.compareTo(n) >= 0) - return false; - - var c = s.modInverse(n); - - var u1 = e.multiply(c).mod(n); - var u2 = r.multiply(c).mod(n); - - // TODO(!!!): For some reason Shamir's trick isn't working with - // signed message verification!? Probably an implementation - // error! - //var point = implShamirsTrick(G, u1, Q, u2); - var point = G.multiply(u1).add(Q.multiply(u2)); - - var v = point.getX().toBigInteger().mod(n); - - return v.equals(r); - }, - - /** - * Serialize a signature into DER format. - * - * Takes two BigIntegers representing r and s and returns a byte array. - */ - serializeSig: function (r, s) { - var rBa = r.toByteArraySigned(); - var sBa = s.toByteArraySigned(); - - var sequence = []; - sequence.push(0x02); // INTEGER - sequence.push(rBa.length); - sequence = sequence.concat(rBa); - - sequence.push(0x02); // INTEGER - sequence.push(sBa.length); - sequence = sequence.concat(sBa); - - sequence.unshift(sequence.length); - sequence.unshift(0x30); // SEQUENCE - - return sequence; - }, - - /** - * Parses a byte array containing a DER-encoded signature. - * - * This function will return an object of the form: - * - * { - * r: BigInteger, - * s: BigInteger - * } - */ - parseSig: function (sig) { - var cursor; - if (sig[0] != 0x30) - throw new Error("Signature not a valid DERSequence"); - - cursor = 2; - if (sig[cursor] != 0x02) - throw new Error("First element in signature must be a DERInteger");; - var rBa = sig.slice(cursor+2, cursor+2+sig[cursor+1]); - - cursor += 2+sig[cursor+1]; - if (sig[cursor] != 0x02) - throw new Error("Second element in signature must be a DERInteger"); - var sBa = sig.slice(cursor+2, cursor+2+sig[cursor+1]); - - cursor += 2+sig[cursor+1]; - - //if (cursor != sig.length) - // throw new Error("Extra bytes in signature"); - - var r = BigInteger.fromByteArrayUnsigned(rBa); - var s = BigInteger.fromByteArrayUnsigned(sBa); - - return {r: r, s: s}; - }, - - parseSigCompact: function (sig) { - if (sig.length !== 65) { - throw "Signature has the wrong length"; - } - - // Signature is prefixed with a type byte storing three bits of - // information. - var i = sig[0] - 27; - if (i < 0 || i > 7) { - throw "Invalid signature type"; - } - - var n = ecparams.getN(); - var r = BigInteger.fromByteArrayUnsigned(sig.slice(1, 33)).mod(n); - var s = BigInteger.fromByteArrayUnsigned(sig.slice(33, 65)).mod(n); - - return {r: r, s: s, i: i}; - }, - - /** - * Recover a public key from a signature. - * - * See SEC 1: Elliptic Curve Cryptography, section 4.1.6, "Public - * Key Recovery Operation". - * - * http://www.secg.org/download/aid-780/sec1-v2.pdf - */ - recoverPubKey: function (r, s, hash, i) { - // The recovery parameter i has two bits. - i = i & 3; - - // The less significant bit specifies whether the y coordinate - // of the compressed point is even or not. - var isYEven = i & 1; - - // The more significant bit specifies whether we should use the - // first or second candidate key. - var isSecondKey = i >> 1; - - var n = ecparams.getN(); - var G = ecparams.getG(); - var curve = ecparams.getCurve(); - var p = curve.getQ(); - var a = curve.getA().toBigInteger(); - var b = curve.getB().toBigInteger(); - - // We precalculate (p + 1) / 4 where p is if the field order - if (!P_OVER_FOUR) { - P_OVER_FOUR = p.add(BigInteger.ONE).divide(BigInteger.valueOf(4)); - } - - // 1.1 Compute x - var x = isSecondKey ? r.add(n) : r; - - // 1.3 Convert x to point - var alpha = x.multiply(x).multiply(x).add(a.multiply(x)).add(b).mod(p); - var beta = alpha.modPow(P_OVER_FOUR, p); - - var xorOdd = beta.isEven() ? (i % 2) : ((i+1) % 2); - // If beta is even, but y isn't or vice versa, then convert it, - // otherwise we're done and y == beta. - var y = (beta.isEven() ? !isYEven : isYEven) ? beta : p.subtract(beta); - - // 1.4 Check that nR is at infinity - var R = new ECPointFp(curve, - curve.fromBigInteger(x), - curve.fromBigInteger(y)); - R.validate(); - - // 1.5 Compute e from M - var e = BigInteger.fromByteArrayUnsigned(hash); - var eNeg = BigInteger.ZERO.subtract(e).mod(n); - - // 1.6 Compute Q = r^-1 (sR - eG) - var rInv = r.modInverse(n); - var Q = implShamirsTrick(R, s, G, eNeg).multiply(rInv); - - Q.validate(); - if (!ECDSA.verifyRaw(e, r, s, Q)) { - throw "Pubkey recovery unsuccessful"; - } - - var pubKey = new Bitcoin.ECKey(); - pubKey.pub = Q; - return pubKey; - }, - - /** - * Calculate pubkey extraction parameter. - * - * When extracting a pubkey from a signature, we have to - * distinguish four different cases. Rather than putting this - * burden on the verifier, Bitcoin includes a 2-bit value with the - * signature. - * - * This function simply tries all four cases and returns the value - * that resulted in a successful pubkey recovery. - */ - calcPubkeyRecoveryParam: function (address, r, s, hash) - { - for (var i = 0; i < 4; i++) { - try { - var pubkey = Bitcoin.ECDSA.recoverPubKey(r, s, hash, i); - if (pubkey.getBitcoinAddress().toString() == address) { - return i; - } - } catch (e) {} - } - throw "Unable to find valid recovery factor"; - } - }; - - return ECDSA; -})(); diff --git a/src/eckey.js b/src/eckey.js index a01367b..6ec4f0d 100644 --- a/src/eckey.js +++ b/src/eckey.js @@ -1,131 +1,179 @@ -Bitcoin.ECKey = (function () { - var ECDSA = Bitcoin.ECDSA; - var ecparams = getSECCurveByName("secp256k1"); - var rng = new SecureRandom(); +var BigInteger = require('./jsbn/jsbn'); +var sec = require('./jsbn/sec'); +var base58 = require('./base58'); +var Crypto = require('./crypto-js/crypto'); +var util = require('./util'); +var conv = require('./convert'); +var Address = require('./address'); +var ecdsa = require('./ecdsa'); +var ECPointFp = require('./jsbn/ec').ECPointFp; - var ECKey = function (input) { +var ecparams = sec("secp256k1"); + +// input can be nothing, array of bytes, hex string, or base58 string +var ECKey = function (input,compressed) { + if (!(this instanceof ECKey)) { return new ECKey(input); } if (!input) { - // Generate new key - var n = ecparams.getN(); - this.priv = ECDSA.getBigRandom(n); - } else if (input instanceof BigInteger) { - // Input is a private key value - this.priv = input; - } else if (Bitcoin.Util.isArray(input)) { - // Prepend zero byte to prevent interpretation as negative integer - this.priv = BigInteger.fromByteArrayUnsigned(input); - } else if ("string" == typeof input) { - if (input.length == 51 && input[0] == '5') { - // Base58 encoded private key - this.priv = BigInteger.fromByteArrayUnsigned(ECKey.decodeString(input)); - } else { - // Prepend zero byte to prevent interpretation as negative integer - this.priv = BigInteger.fromByteArrayUnsigned(Crypto.util.base64ToBytes(input)); - } + // Generate new key + var n = ecparams.getN(); + this.priv = ecdsa.getBigRandom(n); + this.compressed = compressed || false; } - this.compressed = !!ECKey.compressByDefault; - }; + else this.import(input,compressed) +}; - /** - * Whether public keys should be returned compressed by default. - */ - ECKey.compressByDefault = false; +ECKey.prototype.import = function (input,compressed) { + function has(li,v) { return li.indexOf(v) >= 0 } + function fromBin(x) { return BigInteger.fromByteArrayUnsigned(x) } + this.priv = + input instanceof ECKey ? input.priv + : input instanceof BigInteger ? input.mod(ecparams.getN()) + : util.isArray(input) ? fromBin(input.slice(0,32)) + : typeof input != "string" ? null + : input.length == 51 && input[0] == '5' ? fromBin(base58.checkDecode(input)) + : input.length == 52 && has('LK',input[0]) ? fromBin(base58.checkDecode(input)) + : has([64,65],input.length) ? fromBin(conv.hexToBytes(input.slice(0,64))) + : null - /** - * Set whether the public key should be returned compressed or not. - */ - ECKey.prototype.setCompressed = function (v) { - this.compressed = !!v; - }; + this.compressed = + compressed !== undefined ? compressed + : input instanceof ECKey ? input.compressed + : input instanceof BigInteger ? false + : util.isArray(input) ? false + : typeof input != "string" ? null + : input.length == 51 && input[0] == '5' ? false + : input.length == 52 && has('LK',input[0]) ? true + : input.length == 64 ? false + : input.length == 65 ? true + : null +}; - /** - * Return public key in DER encoding. - */ - ECKey.prototype.getPub = function () { - return this.getPubPoint().getEncoded(this.compressed); - }; +ECKey.prototype.getPub = function(compressed) { + if (compressed === undefined) compressed = this.compressed + return ECPubKey(ecparams.getG().multiply(this.priv),compressed) +} - /** - * Return public point as ECPoint object. - */ - ECKey.prototype.getPubPoint = function () { - if (!this.pub) this.pub = ecparams.getG().multiply(this.priv); +/** + * @deprecated Reserved keyword, factory pattern. Use toHex, toBytes, etc. + */ +ECKey.prototype['export'] = function(format) { + format || (format = 'hex') + return this['to' + format.substr(0, 1).toUpperCase() + format.substr(1)]() +}; - return this.pub; - }; +ECKey.prototype.toBin = function() { + return conv.bytesToString(this.toBytes()) +} - /** - * Get the pubKeyHash for this key. - * - * This is calculated as RIPE160(SHA256([encoded pubkey])) and returned as - * a byte array. - */ - ECKey.prototype.getPubKeyHash = function () { - if (this.pubKeyHash) return this.pubKeyHash; +ECKey.prototype.toBase58 = function() { + return base58.checkEncode(this.toBytes(), 128) +} - return this.pubKeyHash = Bitcoin.Util.sha256ripe160(this.getPub()); - }; +ECKey.prototype.toWif = ECKey.prototype.toBase58 - ECKey.prototype.getBitcoinAddress = function () { - var hash = this.getPubKeyHash(); - var addr = new Bitcoin.Address(hash); - return addr; - }; +ECKey.prototype.toHex = function() { + return conv.bytesToHex(this.toBytes()) +} - 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.toBytes = function() { + var bytes = this.priv.toByteArrayUnsigned(); + if (this.compressed) bytes.push(1) + return bytes +} - ECKey.prototype.setPub = function (pub) { - this.pub = ECPointFp.decodeFrom(ecparams.getCurve(), pub); - }; +ECKey.prototype.toString = ECKey.prototype.toBase58 - ECKey.prototype.toString = function (format) { - if (format === "base64") { - return Crypto.util.bytesToBase64(this.priv.toByteArrayUnsigned()); - } else { - return Crypto.util.bytesToHex(this.priv.toByteArrayUnsigned()); +ECKey.prototype.getBitcoinAddress = function(v) { + return this.getPub().getBitcoinAddress(v) +} + +ECKey.prototype.add = function(key) { + return ECKey(this.priv.add(ECKey(key).priv),this.compressed) +} + +ECKey.prototype.multiply = function(key) { + return ECKey(this.priv.multiply(ECKey(key).priv),this.compressed) +} + +var ECPubKey = function(input,compressed) { + if (!(this instanceof ECPubKey)) { return new ECPubKey(input,compressed); } + if (!input) { + // Generate new key + var n = ecparams.getN(); + this.pub = ecparams.getG().multiply(ecdsa.getBigRandom(n)) + this.compressed = compressed || false; } - }; + else this.import(input,compressed) +} - ECKey.prototype.sign = function (hash) { - return ECDSA.sign(hash, this.priv); - }; +ECPubKey.prototype.import = function(input,compressed) { + var decode = function(x) { return ECPointFp.decodeFrom(ecparams.getCurve(), x) } + this.pub = + input instanceof ECPointFp ? input + : input instanceof ECKey ? ecparams.getG().multiply(input.priv) + : input instanceof ECPubKey ? input.pub + : typeof input == "string" ? decode(conv.hexToBytes(input)) + : util.isArray(input) ? decode(input) + : ecparams.getG().multiply(ecdsa.getBigRandom(ecparams.getN())) - ECKey.prototype.verify = function (hash, sig) { - return ECDSA.verify(hash, sig, this.getPub()); - }; + this.compressed = + arguments.length > 1 ? compressed + : input instanceof ECPointFp ? input.compressed + : input instanceof ECPubKey ? input.compressed + : (this.pub[0] < 4) +} - /** - * Parse an exported private key contained in a string. - */ - ECKey.decodeString = function (string) { - var bytes = Bitcoin.Base58.decode(string); +ECPubKey.prototype.add = function(key) { + return ECPubKey(this.pub.add(ECPubKey(key).pub),this.compressed) +} - var hash = bytes.slice(0, 33); +ECPubKey.prototype.multiply = function(key) { + return ECPubKey(this.pub.multiply(ECKey(key).priv),this.compressed) +} - var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true}); +ECPubKey.prototype['export'] = function(format) { + format || (format = 'hex') + return this['to' + format.substr(0, 1).toUpperCase() + format.substr(1)]() +} - if (checksum[0] != bytes[33] || - checksum[1] != bytes[34] || - checksum[2] != bytes[35] || - checksum[3] != bytes[36]) { - throw "Checksum validation failed!"; - } +ECPubKey.prototype.toBytes = function(compressed) { + if (compressed === undefined) compressed = this.compressed + return this.pub.getEncoded(compressed) +} - var version = hash.shift(); +ECPubKey.prototype.toHex = function() { + return conv.bytesToHex(this.toBytes()) +} - if (version != 0x80) { - throw "Version "+version+" not supported!"; - } +ECPubKey.prototype.toBin = function() { + return conv.bytesToString(this.toBytes()) +} - return hash; - }; +ECPubKey.prototype.toBase58 = function() { + return base58.checkEncode(this.toBytes(), 128) +} - return ECKey; -})(); +ECPubKey.prototype.toWif = ECPubKey.prototype.toBase58 + +ECPubKey.prototype.toString = function() { + return this.getBitcoinAddress().toString() +} + +ECPubKey.prototype.getBitcoinAddress = function(v) { + return new Address(util.sha256ripe160(this.toBytes()), v); +} + +ECKey.prototype.sign = function (hash) { + return ecdsa.sign(hash, this.priv); +}; + +ECKey.prototype.verify = function (hash, sig) { + return ecdsa.verify(hash, sig, this.getPub()['export']('bytes')); +}; + +/** + * Parse an exported private key contained in a string. + */ + + +module.exports = { ECKey: ECKey, ECPubKey: ECPubKey }; diff --git a/src/hdwallet.js b/src/hdwallet.js new file mode 100644 index 0000000..00a02c0 --- /dev/null +++ b/src/hdwallet.js @@ -0,0 +1,235 @@ +var convert = require('./convert.js') +, base58 = require('./base58.js') +, assert = require('assert') +, format = require('util').format +, util = require('./util.js') +, Crypto = require('./crypto-js/crypto.js') +, ECKey = require('./eckey.js').ECKey +, ECPubKey = require('./eckey.js').ECPubKey +, Address = require('./address.js') + +var HDWallet = module.exports = function(seed, network) { + if (seed === undefined) return + + var I = Crypto.HMAC(Crypto.SHA512, seed, 'Bitcoin seed', { asBytes: true }) + this.chaincode = I.slice(32) + this.priv = new ECKey(I.slice(0, 32).concat([1]), true) + this.pub = this.priv.getPub() + this.network = network || 'Bitcoin' + this.index = 0 + this.depth = 0 +} + +HDWallet.HIGHEST_BIT = 0x80000000 +HDWallet.LENGTH = 78 + +HDWallet.VERSIONS = { + Bitcoin: [0x0488B21E, 0x0488ADE4], + BitcoinTest: [0x043587CF, 0x04358394] +} + +function arrayEqual(a, b) { + return !(a < b || a > b) +} + +HDWallet.getChecksum = function(buffer) { + assert.equal(buffer.length, HDWallet.LENGTH) + return Crypto.SHA256(Crypto.SHA256(buffer, { asBytes: true }), { asBytes: true }).slice(0, 4) +} + +HDWallet.fromMasterHex = function(hex) { + var bytes = convert.hexToBytes(hex) + return new HDWallet(convert.bytesToString(bytes)) +} + +HDWallet.fromBase58 = function(input) { + var buffer = base58.decode(input) + + if (buffer.length == HDWallet.LENGTH + 4) { + var expectedChecksum = buffer.slice(HDWallet.LENGTH, HDWallet.LENGTH + 4) + buffer = buffer.slice(0, HDWallet.LENGTH) + var actualChecksum = HDWallet.getChecksum(buffer) + + if (!arrayEqual(expectedChecksum, actualChecksum)) { + throw new Error('Checksum mismatch') + } + } + + return HDWallet.fromBytes(buffer) +} + +HDWallet.fromHex = function(input) { + return HDWallet.fromBytes(convert.hexToBytes(input)) +} + +HDWallet.fromBytes = function(input) { + // This 78 byte structure can be encoded like other Bitcoin data in Base58. (+32 bits checksum) + if (input.length != HDWallet.LENGTH) { + throw new Error(format('Invalid input length, %s. Expected %s.', input.length, HDWallet.LENGTH)) + } + + var hd = new HDWallet() + + // 4 byte: version bytes (mainnet: 0x0488B21E public, 0x0488ADE4 private; + // testnet: 0x043587CF public, 0x04358394 private) + var versionBytes = input.slice(0, 4) + , versionWord = util.bytesToWords(versionBytes)[0] + , type + + Object.keys(HDWallet.VERSIONS).forEach(function(name) { + HDWallet.VERSIONS[name].forEach(function(word, i) { + if (versionWord != word) return + type = i ? 'private' : 'public' + hd.network = name + }) + }) + + if (!hd.network) { + throw new Error(format('Could not find version %s', convert.bytesToHex(versionBytes))) + } + + // 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, ... + hd.depth = input[4] + + // 4 bytes: the fingerprint of the parent's key (0x00000000 if master key) + hd.parentFingerprint = input.slice(5, 9) + assert((hd.depth === 0) == arrayEqual(hd.parentFingerprint, [0, 0, 0, 0])) + + // 4 bytes: child number. This is the number i in xi = xpar/i, with xi the key being serialized. + // This is encoded in MSB order. (0x00000000 if master key) + hd.index = util.bytesToNum(input.slice(9, 13).reverse()) + assert(hd.depth > 0 || hd.index === 0) + + // 32 bytes: the chain code + hd.chaincode = input.slice(13, 45) + + // 33 bytes: the public key or private key data (0x02 + X or 0x03 + X for + // public keys, 0x00 + k for private keys) + if (type == 'private') { + hd.priv = new ECKey(input.slice(46, 78).concat([1]), true) + hd.pub = hd.priv.getPub() + } else { + hd.pub = new ECPubKey(input.slice(45, 78), true) + } + + return hd +} + +HDWallet.prototype.getIdentifier = function() { + return util.sha256ripe160(this.pub.toBytes()) +} + +HDWallet.prototype.getFingerprint = function() { + return this.getIdentifier().slice(0, 4) +} + +HDWallet.prototype.getBitcoinAddress = function() { + var test = this.network.match(/Test$/) + return new Address(util.sha256ripe160(this.pub.toBytes()), test ? 111 : 0) +} + +HDWallet.prototype.toBytes = function(priv) { + var buffer = [] + + // Version + // 4 byte: version bytes (mainnet: 0x0488B21E public, 0x0488ADE4 private; testnet: 0x043587CF public, + // 0x04358394 private) + var vBytes = util.wordsToBytes([HDWallet.VERSIONS[this.network][priv ? 1 : 0]]) + + buffer = buffer.concat(vBytes) + assert.equal(buffer.length, 4) + + // Depth + // 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, .... + buffer.push(this.depth) + assert.equal(buffer.length, 4 + 1) + + // 4 bytes: the fingerprint of the parent's key (0x00000000 if master key) + buffer = buffer.concat(this.depth ? this.parentFingerprint : [0, 0, 0, 0]) + assert.equal(buffer.length, 4 + 1 + 4) + + // 4 bytes: child number. This is the number i in xi = xpar/i, with xi the key being serialized. + // This is encoded in MSB order. (0x00000000 if master key) + buffer = buffer.concat(util.numToBytes(this.index, 4).reverse()) + assert.equal(buffer.length, 4 + 1 + 4 + 4) + + // 32 bytes: the chain code + buffer = buffer.concat(this.chaincode) + assert.equal(buffer.length, 4 + 1 + 4 + 4 + 32) + + // 33 bytes: the public key or private key data + // (0x02 + X or 0x03 + X for public keys, 0x00 + k for private keys) + if (priv) { + assert(this.priv, 'Cannot serialize to private without private key') + buffer.push(0) + buffer = buffer.concat(this.priv.toBytes().slice(0, 32)) + } else { + buffer = buffer.concat(this.pub.toBytes(true)) + } + + return buffer +} + +HDWallet.prototype.toHex = function(priv) { + var bytes = this.toBytes(priv) + return convert.bytesToHex(bytes) +} + +HDWallet.prototype.toBase58 = function(priv) { + var buffer = this.toBytes(priv) + , checksum = HDWallet.getChecksum(buffer) + buffer = buffer.concat(checksum) + return base58.encode(buffer) +} + +HDWallet.prototype.derive = function(i) { + var I + , iBytes = util.numToBytes(i, 4).reverse() + , cPar = this.chaincode + , usePriv = i >= HDWallet.HIGHEST_BIT + + if (usePriv) { + assert(this.priv, 'Private derive on public key') + + // If 1, private derivation is used: + // let I = HMAC-SHA512(Key = cpar, Data = 0x00 || kpar || i) [Note:] + var kPar = this.priv.toBytes().slice(0, 32) + I = Crypto.HMAC(Crypto.SHA512, [0].concat(kPar, iBytes), cPar, { asBytes: true }) + } else { + // If 0, public derivation is used: + // let I = HMAC-SHA512(Key = cpar, Data = χ(kpar*G) || i) + var KPar = this.pub.toBytes(true) + I = Crypto.HMAC(Crypto.SHA512, KPar.concat(iBytes), cPar, { asBytes: true }) + } + + // Split I = IL || IR into two 32-byte sequences, IL and IR. + var IL = I.slice(0, 32) + , IR = I.slice(32) + + var hd = new HDWallet() + hd.network = this.network + + if (this.priv) { + // ki = IL + kpar (mod n). + hd.priv = this.priv.add(new ECKey(IL.concat([1]))) + hd.priv.compressed = true + hd.pub = hd.priv.getPub() + } else { + // Ki = (IL + kpar)*G = IL*G + Kpar + hd.pub = this.pub.add(new ECKey(IL.concat([1])).getPub()) + } + + // ci = IR. + hd.chaincode = IR + hd.parentFingerprint = this.getFingerprint() + hd.depth = this.depth + 1 + hd.index = i + hd.pub.compressed = true + return hd +} + +HDWallet.prototype.derivePrivate = function(index) { + return this.derive(index + HDWallet.HIGHEST_BIT) +} + +HDWallet.prototype.toString = HDWallet.prototype.toBase58 diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..559c35b --- /dev/null +++ b/src/index.js @@ -0,0 +1,54 @@ +// 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; +} + +var Key = require('./eckey'); + +module.exports = { + Address: require('./address'), + Key: Key.ECKey, + ECKey: Key.ECKey, + ECPubKey: Key.ECPubKey, + Message: require('./message'), + BigInteger: require('./jsbn/jsbn'), + Crypto: require('./crypto-js/crypto'), + Script: require('./script'), + Opcode: require('./opcode'), + Transaction: require('./transaction').Transaction, + Util: require('./util'), + TransactionIn: require('./transaction').TransactionIn, + TransactionOut: require('./transaction').TransactionOut, + ECPointFp: require('./jsbn/ec').ECPointFp, + Wallet: require('./wallet'), + + ecdsa: require('./ecdsa'), + HDWallet: require('./hdwallet.js'), + + // base58 encoding/decoding to bytes + base58: require('./base58'), + + // conversions + convert: require('./convert'), + + endian: endian +} diff --git a/src/jsbn/ec.js b/src/jsbn/ec.js index 43ded3e..a7642e8 100644 --- a/src/jsbn/ec.js +++ b/src/jsbn/ec.js @@ -2,7 +2,8 @@ // Ported loosely from BouncyCastle's Java EC code // Only Fp curves implemented for now -// Requires jsbn.js and jsbn2.js +var BigInteger = require('./jsbn'), + sec = require('./sec'); // ---------------- // ECFieldElementFp @@ -314,3 +315,208 @@ ECCurveFp.prototype.equals = curveFpEquals; ECCurveFp.prototype.getInfinity = curveFpGetInfinity; ECCurveFp.prototype.fromBigInteger = curveFpFromBigInteger; ECCurveFp.prototype.decodePointHex = curveFpDecodePointHex; + +// prepends 0 if bytes < len +// cuts off start if bytes > len +function integerToBytes(i, len) { + var bytes = i.toByteArrayUnsigned(); + + if (len < bytes.length) { + bytes = bytes.slice(bytes.length-len); + } else while (len > bytes.length) { + bytes.unshift(0); + } + + return bytes; +}; + +ECFieldElementFp.prototype.getByteLength = function () { + return Math.floor((this.toBigInteger().bitLength() + 7) / 8); +}; + +ECPointFp.prototype.getEncoded = function (compressed) { + var x = this.getX().toBigInteger(); + var y = this.getY().toBigInteger(); + + // Get value as a 32-byte Buffer + // Fixed length based on a patch by bitaddress.org and Casascius + var enc = integerToBytes(x, 32); + + if (compressed) { + if (y.isEven()) { + // Compressed even pubkey + // M = 02 || X + enc.unshift(0x02); + } else { + // Compressed uneven pubkey + // M = 03 || X + enc.unshift(0x03); + } + } else { + // Uncompressed pubkey + // M = 04 || X || Y + enc.unshift(0x04); + enc = enc.concat(integerToBytes(y, 32)); + } + return enc; +}; + +ECPointFp.decodeFrom = function (ecparams, enc) { + var type = enc[0]; + var dataLen = enc.length-1; + + // Extract x and y as byte arrays + if (type == 4) { + var xBa = enc.slice(1, 1 + dataLen/2), + yBa = enc.slice(1 + dataLen/2, 1 + dataLen), + x = BigInteger.fromByteArrayUnsigned(xBa), + y = BigInteger.fromByteArrayUnsigned(yBa); + } + else { + var xBa = enc.slice(1), + x = BigInteger.fromByteArrayUnsigned(xBa), + p = ecparams.getQ(), + xCubedPlus7 = x.multiply(x).multiply(x).add(new BigInteger('7')).mod(p), + pPlus1Over4 = p.add(new BigInteger('1')) + .divide(new BigInteger('4')), + y = xCubedPlus7.modPow(pPlus1Over4,p); + if (y.mod(new BigInteger('2')).toString() != ''+(type % 2)) { + y = p.subtract(y) + } + } + + // Prepend zero byte to prevent interpretation as negative integer + + // Convert to BigIntegers + + // Return point + return new ECPointFp(ecparams, + ecparams.fromBigInteger(x), + ecparams.fromBigInteger(y)); +}; + +ECPointFp.prototype.add2D = function (b) { + if(this.isInfinity()) return b; + if(b.isInfinity()) return this; + + if (this.x.equals(b.x)) { + if (this.y.equals(b.y)) { + // this = b, i.e. this must be doubled + return this.twice(); + } + // this = -b, i.e. the result is the point at infinity + return this.curve.getInfinity(); + } + + var x_x = b.x.subtract(this.x); + var y_y = b.y.subtract(this.y); + var gamma = y_y.divide(x_x); + + var x3 = gamma.square().subtract(this.x).subtract(b.x); + var y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y); + + return new ECPointFp(this.curve, x3, y3); +}; + +ECPointFp.prototype.twice2D = function () { + if (this.isInfinity()) return this; + if (this.y.toBigInteger().signum() == 0) { + // if y1 == 0, then (x1, y1) == (x1, -y1) + // and hence this = -this and thus 2(x1, y1) == infinity + return this.curve.getInfinity(); + } + + var TWO = this.curve.fromBigInteger(BigInteger.valueOf(2)); + var THREE = this.curve.fromBigInteger(BigInteger.valueOf(3)); + var gamma = this.x.square().multiply(THREE).add(this.curve.a).divide(this.y.multiply(TWO)); + + var x3 = gamma.square().subtract(this.x.multiply(TWO)); + var y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y); + + return new ECPointFp(this.curve, x3, y3); +}; + +ECPointFp.prototype.multiply2D = function (k) { + if(this.isInfinity()) return this; + if(k.signum() == 0) return this.curve.getInfinity(); + + var e = k; + var h = e.multiply(new BigInteger("3")); + + var neg = this.negate(); + var R = this; + + var i; + for (i = h.bitLength() - 2; i > 0; --i) { + R = R.twice(); + + var hBit = h.testBit(i); + var eBit = e.testBit(i); + + if (hBit != eBit) { + R = R.add2D(hBit ? this : neg); + } + } + + return R; +}; + +ECPointFp.prototype.isOnCurve = function () { + var x = this.getX().toBigInteger(); + var y = this.getY().toBigInteger(); + var a = this.curve.getA().toBigInteger(); + var b = this.curve.getB().toBigInteger(); + var n = this.curve.getQ(); + var lhs = y.multiply(y).mod(n); + var rhs = x.multiply(x).multiply(x) + .add(a.multiply(x)).add(b).mod(n); + return lhs.equals(rhs); +}; + +ECPointFp.prototype.toString = function () { + return '('+this.getX().toBigInteger().toString()+','+ + this.getY().toBigInteger().toString()+')'; +}; + +/** + * Validate an elliptic curve point. + * + * See SEC 1, section 3.2.2.1: Elliptic Curve Public Key Validation Primitive + */ +ECPointFp.prototype.validate = function () { + var n = this.curve.getQ(); + + // Check Q != O + if (this.isInfinity()) { + throw new Error("Point is at infinity."); + } + + // Check coordinate bounds + var x = this.getX().toBigInteger(); + var y = this.getY().toBigInteger(); + if (x.compareTo(BigInteger.ONE) < 0 || + x.compareTo(n.subtract(BigInteger.ONE)) > 0) { + throw new Error('x coordinate out of bounds'); + } + if (y.compareTo(BigInteger.ONE) < 0 || + y.compareTo(n.subtract(BigInteger.ONE)) > 0) { + throw new Error('y coordinate out of bounds'); + } + + // Check y^2 = x^3 + ax + b (mod n) + if (!this.isOnCurve()) { + throw new Error("Point is not on the curve."); + } + + // Check nQ = 0 (Q is a scalar multiple of G) + if (this.multiply(n).isInfinity()) { + // TODO: This check doesn't work - fix. + throw new Error("Point is not a scalar multiple of G."); + } + + return true; +}; + + +module.exports = ECCurveFp; +module.exports.ECPointFp = ECPointFp; diff --git a/src/jsbn/jsbn.js b/src/jsbn/jsbn.js index 40bb9e2..6618f37 100644 --- a/src/jsbn/jsbn.js +++ b/src/jsbn/jsbn.js @@ -13,12 +13,19 @@ var j_lm = ((canary&0xffffff)==0xefcafe); // (public) Constructor function BigInteger(a,b,c) { - if(a != null) + if (!(this instanceof BigInteger)) { + return new BigInteger(a, b, c); + } + + if(a != null) { if("number" == typeof a) this.fromNumber(a,b,c); else if(b == null && "string" != typeof a) this.fromString(a,256); else this.fromString(a,b); + } } +var proto = BigInteger.prototype; + // return new, unset BigInteger function nbi() { return new BigInteger(null); } @@ -67,6 +74,12 @@ function am3(i,x,w,j,c,n) { } return c; } + +// wtf? +BigInteger.prototype.am = am1; +dbits = 26; + +/* if(j_lm && (navigator.appName == "Microsoft Internet Explorer")) { BigInteger.prototype.am = am2; dbits = 30; @@ -79,10 +92,11 @@ else { // Mozilla/Netscape seems to prefer am3 BigInteger.prototype.am = am3; dbits = 28; } +*/ BigInteger.prototype.DB = dbits; BigInteger.prototype.DM = ((1<= 0) { var x = (k==8)?s[i]&0xff:intAt(s,i); @@ -146,22 +162,22 @@ function bnpFromString(s,b) { } mi = false; if(sh == 0) - this[this.t++] = x; - else if(sh+k > this.DB) { - this[this.t-1] |= (x&((1<<(this.DB-sh))-1))<>(this.DB-sh)); + self[self.t++] = x; + else if(sh+k > self.DB) { + self[self.t-1] |= (x&((1<<(self.DB-sh))-1))<>(self.DB-sh)); } else - this[this.t-1] |= x<= this.DB) sh -= this.DB; + if(sh >= self.DB) sh -= self.DB; } if(k == 8 && (s[0]&0x80) != 0) { - this.s = -1; - if(sh > 0) this[this.t-1] |= ((1<<(this.DB-sh))-1)< 0) self[self.t-1] |= ((1<<(self.DB-sh))-1)< 0) { - if(p < this.DB && (d = this[i]>>p) > 0) { m = true; r = int2char(d); } + if(p < self.DB && (d = self[i]>>p) > 0) { m = true; r = int2char(d); } while(i >= 0) { if(p < k) { - d = (this[i]&((1<>(p+=this.DB-k); + d = (self[i]&((1<>(p+=self.DB-k); } else { - d = (this[i]>>(p-=k))&km; - if(p <= 0) { p += this.DB; --i; } + d = (self[i]>>(p-=k))&km; + if(p <= 0) { p += self.DB; --i; } } if(d > 0) m = true; if(m) r += int2char(d); @@ -252,67 +269,70 @@ function bnpDRShiftTo(n,r) { // (protected) r = this << n function bnpLShiftTo(n,r) { - var bs = n%this.DB; - var cbs = this.DB-bs; + var self = this; + var bs = n%self.DB; + var cbs = self.DB-bs; var bm = (1<= 0; --i) { - r[i+ds+1] = (this[i]>>cbs)|c; - c = (this[i]&bm)<= 0; --i) { + r[i+ds+1] = (self[i]>>cbs)|c; + c = (self[i]&bm)<= 0; --i) r[i] = 0; r[ds] = c; - r.t = this.t+ds+1; - r.s = this.s; + r.t = self.t+ds+1; + r.s = self.s; r.clamp(); } // (protected) r = this >> n function bnpRShiftTo(n,r) { - r.s = this.s; - var ds = Math.floor(n/this.DB); - if(ds >= this.t) { r.t = 0; return; } - var bs = n%this.DB; - var cbs = this.DB-bs; + var self = this; + r.s = self.s; + var ds = Math.floor(n/self.DB); + if(ds >= self.t) { r.t = 0; return; } + var bs = n%self.DB; + var cbs = self.DB-bs; var bm = (1<>bs; - for(var i = ds+1; i < this.t; ++i) { - r[i-ds-1] |= (this[i]&bm)<>bs; + r[0] = self[ds]>>bs; + for(var i = ds+1; i < self.t; ++i) { + r[i-ds-1] |= (self[i]&bm)<>bs; } - if(bs > 0) r[this.t-ds-1] |= (this.s&bm)< 0) r[self.t-ds-1] |= (self.s&bm)<>= this.DB; + c += self[i]-a[i]; + r[i++] = c&self.DM; + c >>= self.DB; } - if(a.t < this.t) { + if(a.t < self.t) { c -= a.s; - while(i < this.t) { - c += this[i]; - r[i++] = c&this.DM; - c >>= this.DB; + while(i < self.t) { + c += self[i]; + r[i++] = c&self.DM; + c >>= self.DB; } - c += this.s; + c += self.s; } else { - c += this.s; + c += self.s; while(i < a.t) { c -= a[i]; - r[i++] = c&this.DM; - c >>= this.DB; + r[i++] = c&self.DM; + c >>= self.DB; } c -= a.s; } r.s = (c<0)?-1:0; - if(c < -1) r[i++] = this.DV+c; + if(c < -1) r[i++] = self.DV+c; else if(c > 0) r[i++] = c; r.t = i; r.clamp(); @@ -351,24 +371,25 @@ function bnpSquareTo(r) { // (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) // r != q, this != m. q or r may be null. function bnpDivRemTo(m,q,r) { + var self = this; var pm = m.abs(); if(pm.t <= 0) return; - var pt = this.abs(); + var pt = self.abs(); if(pt.t < pm.t) { if(q != null) q.fromInt(0); - if(r != null) this.copyTo(r); + if(r != null) self.copyTo(r); return; } if(r == null) r = nbi(); - var y = nbi(), ts = this.s, ms = m.s; - var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus + var y = nbi(), ts = self.s, ms = m.s; + var nsh = self.DB-nbits(pm[pm.t-1]); // normalize modulus if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); } else { pm.copyTo(y); pt.copyTo(r); } var ys = y.t; var y0 = y[ys-1]; if(y0 == 0) return; - var yt = y0*(1<1)?y[ys-2]>>this.F2:0); - var d1 = this.FV/yt, d2 = (1<1)?y[ys-2]>>self.F2:0); + var d1 = self.FV/yt, d2 = (1<= 0) { @@ -380,7 +401,7 @@ function bnpDivRemTo(m,q,r) { while(y.t < ys) y[y.t++] = 0; while(--j >= 0) { // Estimate quotient digit - var qd = (r[--i]==y0)?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2); + var qd = (r[--i]==y0)?self.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2); if((r[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out y.dlShiftTo(j,t); r.subTo(t,r); @@ -529,31 +550,750 @@ function bnModPowInt(e,m) { } // protected -BigInteger.prototype.copyTo = bnpCopyTo; -BigInteger.prototype.fromInt = bnpFromInt; -BigInteger.prototype.fromString = bnpFromString; -BigInteger.prototype.clamp = bnpClamp; -BigInteger.prototype.dlShiftTo = bnpDLShiftTo; -BigInteger.prototype.drShiftTo = bnpDRShiftTo; -BigInteger.prototype.lShiftTo = bnpLShiftTo; -BigInteger.prototype.rShiftTo = bnpRShiftTo; -BigInteger.prototype.subTo = bnpSubTo; -BigInteger.prototype.multiplyTo = bnpMultiplyTo; -BigInteger.prototype.squareTo = bnpSquareTo; -BigInteger.prototype.divRemTo = bnpDivRemTo; -BigInteger.prototype.invDigit = bnpInvDigit; -BigInteger.prototype.isEven = bnpIsEven; -BigInteger.prototype.exp = bnpExp; +proto.copyTo = bnpCopyTo; +proto.fromInt = bnpFromInt; +proto.fromString = bnpFromString; +proto.clamp = bnpClamp; +proto.dlShiftTo = bnpDLShiftTo; +proto.drShiftTo = bnpDRShiftTo; +proto.lShiftTo = bnpLShiftTo; +proto.rShiftTo = bnpRShiftTo; +proto.subTo = bnpSubTo; +proto.multiplyTo = bnpMultiplyTo; +proto.squareTo = bnpSquareTo; +proto.divRemTo = bnpDivRemTo; +proto.invDigit = bnpInvDigit; +proto.isEven = bnpIsEven; +proto.exp = bnpExp; // public -BigInteger.prototype.toString = bnToString; -BigInteger.prototype.negate = bnNegate; -BigInteger.prototype.abs = bnAbs; -BigInteger.prototype.compareTo = bnCompareTo; -BigInteger.prototype.bitLength = bnBitLength; -BigInteger.prototype.mod = bnMod; -BigInteger.prototype.modPowInt = bnModPowInt; +proto.toString = bnToString; +proto.negate = bnNegate; +proto.abs = bnAbs; +proto.compareTo = bnCompareTo; +proto.bitLength = bnBitLength; +proto.mod = bnMod; +proto.modPowInt = bnModPowInt; + +//// jsbn2 + +function nbi() { return new BigInteger(null); } + +// (public) +function bnClone() { var r = nbi(); this.copyTo(r); return r; } + +// (public) return value as integer +function bnIntValue() { + if(this.s < 0) { + if(this.t == 1) return this[0]-this.DV; + else if(this.t == 0) return -1; + } + else if(this.t == 1) return this[0]; + else if(this.t == 0) return 0; + // assumes 16 < DB < 32 + return ((this[1]&((1<<(32-this.DB))-1))<>24; } + +// (public) return value as short (assumes DB>=16) +function bnShortValue() { return (this.t==0)?this.s:(this[0]<<16)>>16; } + +// (protected) return x s.t. r^x < DV +function bnpChunkSize(r) { return Math.floor(Math.LN2*this.DB/Math.log(r)); } + +// (public) 0 if this == 0, 1 if this > 0 +function bnSigNum() { + if(this.s < 0) return -1; + else if(this.t <= 0 || (this.t == 1 && this[0] <= 0)) return 0; + else return 1; +} + +// (protected) convert to radix string +function bnpToRadix(b) { + if(b == null) b = 10; + if(this.signum() == 0 || b < 2 || b > 36) return "0"; + var cs = this.chunkSize(b); + var a = Math.pow(b,cs); + var d = nbv(a), y = nbi(), z = nbi(), r = ""; + this.divRemTo(d,y,z); + while(y.signum() > 0) { + r = (a+z.intValue()).toString(b).substr(1) + r; + y.divRemTo(d,y,z); + } + return z.intValue().toString(b) + r; +} + +// (protected) convert from radix string +function bnpFromRadix(s,b) { + var self = this; + self.fromInt(0); + if(b == null) b = 10; + var cs = self.chunkSize(b); + var d = Math.pow(b,cs), mi = false, j = 0, w = 0; + for(var i = 0; i < s.length; ++i) { + var x = intAt(s,i); + if(x < 0) { + if(s.charAt(i) == "-" && self.signum() == 0) mi = true; + continue; + } + w = b*w+x; + if(++j >= cs) { + self.dMultiply(d); + self.dAddOffset(w,0); + j = 0; + w = 0; + } + } + if(j > 0) { + self.dMultiply(Math.pow(b,j)); + self.dAddOffset(w,0); + } + if(mi) BigInteger.ZERO.subTo(self,self); +} + +// (protected) alternate constructor +function bnpFromNumber(a,b,c) { + var self = this; + if("number" == typeof b) { + // new BigInteger(int,int,RNG) + if(a < 2) self.fromInt(1); + else { + self.fromNumber(a,c); + if(!self.testBit(a-1)) // force MSB set + self.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,self); + if(self.isEven()) self.dAddOffset(1,0); // force odd + while(!self.isProbablePrime(b)) { + self.dAddOffset(2,0); + if(self.bitLength() > a) self.subTo(BigInteger.ONE.shiftLeft(a-1),self); + } + } + } + else { + // new BigInteger(int,RNG) + var x = new Array(), t = a&7; + x.length = (a>>3)+1; + b.nextBytes(x); + if(t > 0) x[0] &= ((1< 0) { + if(p < self.DB && (d = self[i]>>p) != (self.s&self.DM)>>p) + r[k++] = d|(self.s<<(self.DB-p)); + while(i >= 0) { + if(p < 8) { + d = (self[i]&((1<>(p+=self.DB-8); + } + else { + d = (self[i]>>(p-=8))&0xff; + if(p <= 0) { p += self.DB; --i; } + } + if((d&0x80) != 0) d |= -256; + if(k === 0 && (self.s&0x80) != (d&0x80)) ++k; + if(k > 0 || d != self.s) r[k++] = d; + } + } + return r; +} + +function bnEquals(a) { return(this.compareTo(a)==0); } +function bnMin(a) { return(this.compareTo(a)<0)?this:a; } +function bnMax(a) { return(this.compareTo(a)>0)?this:a; } + +// (protected) r = this op a (bitwise) +function bnpBitwiseTo(a,op,r) { + var self = this; + var i, f, m = Math.min(a.t,self.t); + for(i = 0; i < m; ++i) r[i] = op(self[i],a[i]); + if(a.t < self.t) { + f = a.s&self.DM; + for(i = m; i < self.t; ++i) r[i] = op(self[i],f); + r.t = self.t; + } + else { + f = self.s&self.DM; + for(i = m; i < a.t; ++i) r[i] = op(f,a[i]); + r.t = a.t; + } + r.s = op(self.s,a.s); + r.clamp(); +} + +// (public) this & a +function op_and(x,y) { return x&y; } +function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; } + +// (public) this | a +function op_or(x,y) { return x|y; } +function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; } + +// (public) this ^ a +function op_xor(x,y) { return x^y; } +function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; } + +// (public) this & ~a +function op_andnot(x,y) { return x&~y; } +function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; } + +// (public) ~this +function bnNot() { + var r = nbi(); + for(var i = 0; i < this.t; ++i) r[i] = this.DM&~this[i]; + r.t = this.t; + r.s = ~this.s; + return r; +} + +// (public) this << n +function bnShiftLeft(n) { + var r = nbi(); + if(n < 0) this.rShiftTo(-n,r); else this.lShiftTo(n,r); + return r; +} + +// (public) this >> n +function bnShiftRight(n) { + var r = nbi(); + if(n < 0) this.lShiftTo(-n,r); else this.rShiftTo(n,r); + return r; +} + +// return index of lowest 1-bit in x, x < 2^31 +function lbit(x) { + if(x == 0) return -1; + var r = 0; + if((x&0xffff) == 0) { x >>= 16; r += 16; } + if((x&0xff) == 0) { x >>= 8; r += 8; } + if((x&0xf) == 0) { x >>= 4; r += 4; } + if((x&3) == 0) { x >>= 2; r += 2; } + if((x&1) == 0) ++r; + return r; +} + +// (public) returns index of lowest 1-bit (or -1 if none) +function bnGetLowestSetBit() { + for(var i = 0; i < this.t; ++i) + if(this[i] != 0) return i*this.DB+lbit(this[i]); + if(this.s < 0) return this.t*this.DB; + return -1; +} + +// return number of 1 bits in x +function cbit(x) { + var r = 0; + while(x != 0) { x &= x-1; ++r; } + return r; +} + +// (public) return number of set bits +function bnBitCount() { + var r = 0, x = this.s&this.DM; + for(var i = 0; i < this.t; ++i) r += cbit(this[i]^x); + return r; +} + +// (public) true iff nth bit is set +function bnTestBit(n) { + var j = Math.floor(n/this.DB); + if(j >= this.t) return(this.s!=0); + return((this[j]&(1<<(n%this.DB)))!=0); +} + +// (protected) this op (1<>= self.DB; + } + if(a.t < self.t) { + c += a.s; + while(i < self.t) { + c += self[i]; + r[i++] = c&self.DM; + c >>= self.DB; + } + c += self.s; + } + else { + c += self.s; + while(i < a.t) { + c += a[i]; + r[i++] = c&self.DM; + c >>= self.DB; + } + c += a.s; + } + r.s = (c<0)?-1:0; + if(c > 0) r[i++] = c; + else if(c < -1) r[i++] = self.DV+c; + r.t = i; + r.clamp(); +} + +// (public) this + a +function bnAdd(a) { var r = nbi(); this.addTo(a,r); return r; } + +// (public) this - a +function bnSubtract(a) { var r = nbi(); this.subTo(a,r); return r; } + +// (public) this * a +function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; } + +// (public) this^2 +function bnSquare() { var r = nbi(); this.squareTo(r); return r; } + +// (public) this / a +function bnDivide(a) { var r = nbi(); this.divRemTo(a,r,null); return r; } + +// (public) this % a +function bnRemainder(a) { var r = nbi(); this.divRemTo(a,null,r); return r; } + +// (public) [this/a,this%a] +function bnDivideAndRemainder(a) { + var q = nbi(), r = nbi(); + this.divRemTo(a,q,r); + return new Array(q,r); +} + +// (protected) this *= n, this >= 0, 1 < n < DV +function bnpDMultiply(n) { + this[this.t] = this.am(0,n-1,this,0,0,this.t); + ++this.t; + this.clamp(); +} + +// (protected) this += n << w words, this >= 0 +function bnpDAddOffset(n,w) { + if(n == 0) return; + while(this.t <= w) this[this.t++] = 0; + this[w] += n; + while(this[w] >= this.DV) { + this[w] -= this.DV; + if(++w >= this.t) this[this.t++] = 0; + ++this[w]; + } +} + +// A "null" reducer +function NullExp() {} +function nNop(x) { return x; } +function nMulTo(x,y,r) { x.multiplyTo(y,r); } +function nSqrTo(x,r) { x.squareTo(r); } + +NullExp.prototype.convert = nNop; +NullExp.prototype.revert = nNop; +NullExp.prototype.mulTo = nMulTo; +NullExp.prototype.sqrTo = nSqrTo; + +// (public) this^e +function bnPow(e) { return this.exp(e,new NullExp()); } + +// (protected) r = lower n words of "this * a", a.t <= n +// "this" should be the larger one if appropriate. +function bnpMultiplyLowerTo(a,n,r) { + var i = Math.min(this.t+a.t,n); + r.s = 0; // assumes a,this >= 0 + r.t = i; + while(i > 0) r[--i] = 0; + var j; + for(j = r.t-this.t; i < j; ++i) r[i+this.t] = this.am(0,a[i],r,i,0,this.t); + for(j = Math.min(a.t,n); i < j; ++i) this.am(0,a[i],r,i,0,n-i); + r.clamp(); +} + +// (protected) r = "this * a" without lower n words, n > 0 +// "this" should be the larger one if appropriate. +function bnpMultiplyUpperTo(a,n,r) { + --n; + var i = r.t = this.t+a.t-n; + r.s = 0; // assumes a,this >= 0 + while(--i >= 0) r[i] = 0; + for(i = Math.max(n-this.t,0); i < a.t; ++i) + r[this.t+i-n] = this.am(n-i,a[i],r,0,0,this.t+i-n); + r.clamp(); + r.drShiftTo(1,r); +} + +// Barrett modular reduction +function Barrett(m) { + // setup Barrett + this.r2 = nbi(); + this.q3 = nbi(); + BigInteger.ONE.dlShiftTo(2*m.t,this.r2); + this.mu = this.r2.divide(m); + this.m = m; +} + +function barrettConvert(x) { + if(x.s < 0 || x.t > 2*this.m.t) return x.mod(this.m); + else if(x.compareTo(this.m) < 0) return x; + else { var r = nbi(); x.copyTo(r); this.reduce(r); return r; } +} + +function barrettRevert(x) { return x; } + +// x = x mod m (HAC 14.42) +function barrettReduce(x) { + var self = this; + x.drShiftTo(self.m.t-1,self.r2); + if(x.t > self.m.t+1) { x.t = self.m.t+1; x.clamp(); } + self.mu.multiplyUpperTo(self.r2,self.m.t+1,self.q3); + self.m.multiplyLowerTo(self.q3,self.m.t+1,self.r2); + while(x.compareTo(self.r2) < 0) x.dAddOffset(1,self.m.t+1); + x.subTo(self.r2,x); + while(x.compareTo(self.m) >= 0) x.subTo(self.m,x); +} + +// r = x^2 mod m; x != r +function barrettSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +// r = x*y mod m; x,y != r +function barrettMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } + +Barrett.prototype.convert = barrettConvert; +Barrett.prototype.revert = barrettRevert; +Barrett.prototype.reduce = barrettReduce; +Barrett.prototype.mulTo = barrettMulTo; +Barrett.prototype.sqrTo = barrettSqrTo; + +// (public) this^e % m (HAC 14.85) +function bnModPow(e,m) { + var i = e.bitLength(), k, r = nbv(1), z; + if(i <= 0) return r; + else if(i < 18) k = 1; + else if(i < 48) k = 3; + else if(i < 144) k = 4; + else if(i < 768) k = 5; + else k = 6; + if(i < 8) + z = new Classic(m); + else if(m.isEven()) + z = new Barrett(m); + else + z = new Montgomery(m); + + // precomputation + var g = new Array(), n = 3, k1 = k-1, km = (1< 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< 0) { + x.rShiftTo(g,x); + y.rShiftTo(g,y); + } + while(x.signum() > 0) { + if((i = x.getLowestSetBit()) > 0) x.rShiftTo(i,x); + if((i = y.getLowestSetBit()) > 0) y.rShiftTo(i,y); + if(x.compareTo(y) >= 0) { + x.subTo(y,x); + x.rShiftTo(1,x); + } + else { + y.subTo(x,y); + y.rShiftTo(1,y); + } + } + if(g > 0) y.lShiftTo(g,y); + return y; +} + +// (protected) this % n, n < 2^26 +function bnpModInt(n) { + if(n <= 0) return 0; + var d = this.DV%n, r = (this.s<0)?n-1:0; + if(this.t > 0) + if(d == 0) r = this[0]%n; + else for(var i = this.t-1; i >= 0; --i) r = (d*r+this[i])%n; + return r; +} + +// (public) 1/this % m (HAC 14.61) +function bnModInverse(m) { + var ac = m.isEven(); + if((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO; + var u = m.clone(), v = this.clone(); + var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1); + while(u.signum() != 0) { + while(u.isEven()) { + u.rShiftTo(1,u); + if(ac) { + if(!a.isEven() || !b.isEven()) { a.addTo(this,a); b.subTo(m,b); } + a.rShiftTo(1,a); + } + else if(!b.isEven()) b.subTo(m,b); + b.rShiftTo(1,b); + } + while(v.isEven()) { + v.rShiftTo(1,v); + if(ac) { + if(!c.isEven() || !d.isEven()) { c.addTo(this,c); d.subTo(m,d); } + c.rShiftTo(1,c); + } + else if(!d.isEven()) d.subTo(m,d); + d.rShiftTo(1,d); + } + if(u.compareTo(v) >= 0) { + u.subTo(v,u); + if(ac) a.subTo(c,a); + b.subTo(d,b); + } + else { + v.subTo(u,v); + if(ac) c.subTo(a,c); + d.subTo(b,d); + } + } + if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO; + if(d.compareTo(m) >= 0) return d.subtract(m); + if(d.signum() < 0) d.addTo(m,d); else return d; + if(d.signum() < 0) return d.add(m); else return d; +} + +// protected +proto.chunkSize = bnpChunkSize; +proto.toRadix = bnpToRadix; +proto.fromRadix = bnpFromRadix; +proto.fromNumber = bnpFromNumber; +proto.bitwiseTo = bnpBitwiseTo; +proto.changeBit = bnpChangeBit; +proto.addTo = bnpAddTo; +proto.dMultiply = bnpDMultiply; +proto.dAddOffset = bnpDAddOffset; +proto.multiplyLowerTo = bnpMultiplyLowerTo; +proto.multiplyUpperTo = bnpMultiplyUpperTo; +proto.modInt = bnpModInt; + +// public +proto.clone = bnClone; +proto.intValue = bnIntValue; +proto.byteValue = bnByteValue; +proto.shortValue = bnShortValue; +proto.signum = bnSigNum; +proto.toByteArray = bnToByteArray; +proto.equals = bnEquals; +proto.min = bnMin; +proto.max = bnMax; +proto.and = bnAnd; +proto.or = bnOr; +proto.xor = bnXor; +proto.andNot = bnAndNot; +proto.not = bnNot; +proto.shiftLeft = bnShiftLeft; +proto.shiftRight = bnShiftRight; +proto.getLowestSetBit = bnGetLowestSetBit; +proto.bitCount = bnBitCount; +proto.testBit = bnTestBit; +proto.setBit = bnSetBit; +proto.clearBit = bnClearBit; +proto.flipBit = bnFlipBit; +proto.add = bnAdd; +proto.subtract = bnSubtract; +proto.multiply = bnMultiply; +proto.divide = bnDivide; +proto.remainder = bnRemainder; +proto.divideAndRemainder = bnDivideAndRemainder; +proto.modPow = bnModPow; +proto.modInverse = bnModInverse; +proto.pow = bnPow; +proto.gcd = bnGCD; + +// JSBN-specific extension +proto.square = bnSquare; + +// BigInteger interfaces not implemented in jsbn: + +// BigInteger(int signum, byte[] magnitude) +// double doubleValue() +// float floatValue() +// int hashCode() +// long longValue() +// static BigInteger valueOf(long val) // "constants" BigInteger.ZERO = nbv(0); BigInteger.ONE = nbv(1); +BigInteger.valueOf = nbv; + + +/// bitcoinjs addons + +/** + * Turns a byte array into a big integer. + * + * This function will interpret a byte array as a big integer in big + * endian notation and ignore leading zeros. + */ +BigInteger.fromByteArrayUnsigned = function(ba) { + if (!ba.length) { + return new BigInteger.valueOf(0); + } else if (ba[0] & 0x80) { + // Prepend a zero so the BigInteger class doesn't mistake this + // for a negative integer. + return new BigInteger([0].concat(ba)); + } else { + return new BigInteger(ba); + } +}; + +/** + * Parse a signed big integer byte representation. + * + * For details on the format please see BigInteger.toByteArraySigned. + */ +BigInteger.fromByteArraySigned = function(ba) { + // Check for negative value + if (ba[0] & 0x80) { + // Remove sign bit + ba[0] &= 0x7f; + + return BigInteger.fromByteArrayUnsigned(ba).negate(); + } else { + return BigInteger.fromByteArrayUnsigned(ba); + } +}; + +/** + * Returns a byte array representation of the big integer. + * + * This returns the absolute of the contained value in big endian + * form. A value of zero results in an empty array. + */ +BigInteger.prototype.toByteArrayUnsigned = function() { + var ba = this.abs().toByteArray(); + + // Empty array, nothing to do + if (!ba.length) { + return ba; + } + + // remove leading 0 + if (ba[0] === 0) { + ba = ba.slice(1); + } + + // all values must be positive + for (var i=0 ; i 0x00 + * 1 => 0x01 + * -1 => 0x81 + * 127 => 0x7f + * -127 => 0xff + * 128 => 0x0080 + * -128 => 0x8080 + * 255 => 0x00ff + * -255 => 0x80ff + * 16300 => 0x3fac + * -16300 => 0xbfac + * 62300 => 0x00f35c + * -62300 => 0x80f35c +*/ +BigInteger.prototype.toByteArraySigned = function() { + var val = this.toByteArrayUnsigned(); + var neg = this.s < 0; + + // if the first bit is set, we always unshift + // either unshift 0x80 or 0x00 + if (val[0] & 0x80) { + val.unshift((neg) ? 0x80 : 0x00); + } + // if the first bit isn't set, set it if negative + else if (neg) { + val[0] |= 0x80; + } + + return val; +}; + +module.exports = BigInteger; diff --git a/src/jsbn/jsbn2.js b/src/jsbn/jsbn2.js deleted file mode 100644 index 5b2b725..0000000 --- a/src/jsbn/jsbn2.js +++ /dev/null @@ -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))<>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< 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+=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<>= 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< 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< 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) diff --git a/src/jsbn/prng4.js b/src/jsbn/prng4.js index 3034f3f..32bdb5e 100644 --- a/src/jsbn/prng4.js +++ b/src/jsbn/prng4.js @@ -35,11 +35,5 @@ function ARC4next() { Arcfour.prototype.init = ARC4init; Arcfour.prototype.next = ARC4next; -// Plug in your RNG constructor here -function prng_newstate() { - return new Arcfour(); -} +module.exports = Arcfour; -// Pool size must be a multiple of 4 and greater than 32. -// An array of bytes the size of the pool will be passed to init() -var rng_psize = 256; diff --git a/src/jsbn/rng.js b/src/jsbn/rng.js index 03afc3a..bba8fb2 100644 --- a/src/jsbn/rng.js +++ b/src/jsbn/rng.js @@ -1,4 +1,16 @@ // Random number generator - requires a PRNG backend, e.g. prng4.js +// prng4.js - uses Arcfour as a PRNG + +var Arcfour = require('./prng4'); + +// Plug in your RNG constructor here +function prng_newstate() { + return new Arcfour(); +} + +// Pool size must be a multiple of 4 and greater than 32. +// An array of bytes the size of the pool will be passed to init() +var rng_psize = 256; // For best results, put code like // @@ -27,12 +39,15 @@ if(rng_pool == null) { rng_pool = new Array(); rng_pptr = 0; var t; + // TODO(shtylman) use browser crypto if available + /* if(navigator.appName == "Netscape" && navigator.appVersion < "5" && window.crypto) { // Extract entropy (256 bits) from NS4 RNG if available var z = window.crypto.random(32); for(t = 0; t < z.length; ++t) rng_pool[rng_pptr++] = z.charCodeAt(t) & 255; - } + } + */ while(rng_pptr < rng_psize) { // extract some randomness from Math.random() t = Math.floor(65536 * Math.random()); rng_pool[rng_pptr++] = t >>> 8; @@ -66,3 +81,5 @@ function rng_get_bytes(ba) { function SecureRandom() {} SecureRandom.prototype.nextBytes = rng_get_bytes; + +module.exports = SecureRandom; diff --git a/src/jsbn/sec.js b/src/jsbn/sec.js old mode 100755 new mode 100644 index e496571..5a95700 --- a/src/jsbn/sec.js +++ b/src/jsbn/sec.js @@ -2,6 +2,9 @@ // Requires ec.js, jsbn.js, and jsbn2.js +var ECCurveFp = require('./ec'); +var BigInteger = require('./jsbn'); + // ---------------- // X9ECParameters @@ -171,3 +174,5 @@ function getSECCurveByName(name) { if(name == "secp256r1") return secp256r1(); return null; } + +module.exports = getSECCurveByName; diff --git a/src/message.js b/src/message.js index a2dabc0..b370f92 100644 --- a/src/message.js +++ b/src/message.js @@ -1,69 +1,68 @@ -/** - * Implements Bitcoin's feature for signing arbitrary messages. - */ -Bitcoin.Message = (function () { - var Message = {}; +/// Implements Bitcoin's feature for signing arbitrary messages. - Message.magicPrefix = "Bitcoin Signed Message:\n"; +var Crypto = require('./crypto-js/crypto'); +var ecdsa = require('./ecdsa'); +var conv = require('./convert'); +var util = require('./util'); - Message.makeMagicMessage = function (message) { - var magicBytes = Crypto.charenc.UTF8.stringToBytes(Message.magicPrefix); - var messageBytes = Crypto.charenc.UTF8.stringToBytes(message); +var Message = {}; - var buffer = []; - buffer = buffer.concat(Bitcoin.Util.numToVarInt(magicBytes.length)); - buffer = buffer.concat(magicBytes); - buffer = buffer.concat(Bitcoin.Util.numToVarInt(messageBytes.length)); - buffer = buffer.concat(messageBytes); +Message.magicPrefix = "Bitcoin Signed Message:\n"; - return buffer; - }; +Message.makeMagicMessage = function (message) { + var magicBytes = conv.stringToBytes(Message.magicPrefix); + var messageBytes = conv.stringToBytes(message); - Message.getHash = function (message) { - var buffer = Message.makeMagicMessage(message); - return Crypto.SHA256(Crypto.SHA256(buffer, {asBytes: true}), {asBytes: true}); - }; + var buffer = []; + buffer = buffer.concat(util.numToVarInt(magicBytes.length)); + buffer = buffer.concat(magicBytes); + buffer = buffer.concat(util.numToVarInt(messageBytes.length)); + buffer = buffer.concat(messageBytes); - Message.signMessage = function (key, message, compressed) { - var hash = Message.getHash(message); + return buffer; +}; - var sig = key.sign(hash); +Message.getHash = function (message) { + var buffer = Message.makeMagicMessage(message); + return Crypto.SHA256(Crypto.SHA256(buffer, {asBytes: true}), {asBytes: true}); +}; - var obj = Bitcoin.ECDSA.parseSig(sig); +Message.signMessage = function (key, message, compressed) { + var hash = Message.getHash(message); - var address = key.getBitcoinAddress().toString(); - var i = Bitcoin.ECDSA.calcPubkeyRecoveryParam(address, obj.r, obj.s, hash); + var sig = key.sign(hash); - i += 27; - if (compressed) i += 4; + var obj = ecdsa.parseSig(sig); - var rBa = obj.r.toByteArrayUnsigned(); - var sBa = obj.s.toByteArrayUnsigned(); + var i = ecdsa.calcPubkeyRecoveryParam(key, obj.r, obj.s, hash); - // Pad to 32 bytes per value - while (rBa.length < 32) rBa.unshift(0); - while (sBa.length < 32) sBa.unshift(0); + i += 27; + if (compressed) i += 4; - sig = [i].concat(rBa).concat(sBa); + var rBa = obj.r.toByteArrayUnsigned(); + var sBa = obj.s.toByteArrayUnsigned(); - return Crypto.util.bytesToBase64(sig); - }; + // Pad to 32 bytes per value + while (rBa.length < 32) rBa.unshift(0); + while (sBa.length < 32) sBa.unshift(0); - Message.verifyMessage = function (address, sig, message) { - sig = Crypto.util.base64ToBytes(sig); - sig = Bitcoin.ECDSA.parseSigCompact(sig); + sig = [i].concat(rBa).concat(sBa); - var hash = Message.getHash(message); + return conv.bytesToHex(sig); +}; - var isCompressed = !!(sig.i & 4); - var pubKey = Bitcoin.ECDSA.recoverPubKey(sig.r, sig.s, hash, sig.i); +Message.verifyMessage = function (address, sig, message) { + sig = conv.hexToBytes(sig); + sig = ecdsa.parseSigCompact(sig); - pubKey.setCompressed(isCompressed); + var hash = Message.getHash(message); - var expectedAddress = pubKey.getBitcoinAddress().toString(); + var isCompressed = !!(sig.i & 4); + var pubKey = ecdsa.recoverPubKey(sig.r, sig.s, hash, sig.i); - return (address === expectedAddress); - }; + var expectedAddress = pubKey.getBitcoinAddress().toString(); - return Message; -})(); + return (address === expectedAddress); +}; + +module.exports = Message; diff --git a/src/opcode.js b/src/opcode.js index b21da46..0de4dcc 100644 --- a/src/opcode.js +++ b/src/opcode.js @@ -1,154 +1,154 @@ -(function () { - var Opcode = Bitcoin.Opcode = function (num) { - this.code = num; - }; +var Opcode = function (num) { + this.code = num; +}; - Opcode.prototype.toString = function () { - return Opcode.reverseMap[this.code]; - }; +Opcode.prototype.toString = function () { + return Opcode.reverseMap[this.code]; +}; - Opcode.map = { - // push value - OP_0 : 0, - OP_FALSE : 0, - OP_PUSHDATA1 : 76, - OP_PUSHDATA2 : 77, - OP_PUSHDATA4 : 78, - OP_1NEGATE : 79, - OP_RESERVED : 80, - OP_1 : 81, - OP_TRUE : 81, - OP_2 : 82, - OP_3 : 83, - OP_4 : 84, - OP_5 : 85, - OP_6 : 86, - OP_7 : 87, - OP_8 : 88, - OP_9 : 89, - OP_10 : 90, - OP_11 : 91, - OP_12 : 92, - OP_13 : 93, - OP_14 : 94, - OP_15 : 95, - OP_16 : 96, +Opcode.map = { + // push value + OP_0 : 0, + OP_FALSE : 0, + OP_PUSHDATA1 : 76, + OP_PUSHDATA2 : 77, + OP_PUSHDATA4 : 78, + OP_1NEGATE : 79, + OP_RESERVED : 80, + OP_1 : 81, + OP_TRUE : 81, + OP_2 : 82, + OP_3 : 83, + OP_4 : 84, + OP_5 : 85, + OP_6 : 86, + OP_7 : 87, + OP_8 : 88, + OP_9 : 89, + OP_10 : 90, + OP_11 : 91, + OP_12 : 92, + OP_13 : 93, + OP_14 : 94, + OP_15 : 95, + OP_16 : 96, - // control - OP_NOP : 97, - OP_VER : 98, - OP_IF : 99, - OP_NOTIF : 100, - OP_VERIF : 101, - OP_VERNOTIF : 102, - OP_ELSE : 103, - OP_ENDIF : 104, - OP_VERIFY : 105, - OP_RETURN : 106, + // control + OP_NOP : 97, + OP_VER : 98, + OP_IF : 99, + OP_NOTIF : 100, + OP_VERIF : 101, + OP_VERNOTIF : 102, + OP_ELSE : 103, + OP_ENDIF : 104, + OP_VERIFY : 105, + OP_RETURN : 106, - // stack ops - OP_TOALTSTACK : 107, - OP_FROMALTSTACK : 108, - OP_2DROP : 109, - OP_2DUP : 110, - OP_3DUP : 111, - OP_2OVER : 112, - OP_2ROT : 113, - OP_2SWAP : 114, - OP_IFDUP : 115, - OP_DEPTH : 116, - OP_DROP : 117, - OP_DUP : 118, - OP_NIP : 119, - OP_OVER : 120, - OP_PICK : 121, - OP_ROLL : 122, - OP_ROT : 123, - OP_SWAP : 124, - OP_TUCK : 125, + // stack ops + OP_TOALTSTACK : 107, + OP_FROMALTSTACK : 108, + OP_2DROP : 109, + OP_2DUP : 110, + OP_3DUP : 111, + OP_2OVER : 112, + OP_2ROT : 113, + OP_2SWAP : 114, + OP_IFDUP : 115, + OP_DEPTH : 116, + OP_DROP : 117, + OP_DUP : 118, + OP_NIP : 119, + OP_OVER : 120, + OP_PICK : 121, + OP_ROLL : 122, + OP_ROT : 123, + OP_SWAP : 124, + OP_TUCK : 125, - // splice ops - OP_CAT : 126, - OP_SUBSTR : 127, - OP_LEFT : 128, - OP_RIGHT : 129, - OP_SIZE : 130, + // splice ops + OP_CAT : 126, + OP_SUBSTR : 127, + OP_LEFT : 128, + OP_RIGHT : 129, + OP_SIZE : 130, - // bit logic - OP_INVERT : 131, - OP_AND : 132, - OP_OR : 133, - OP_XOR : 134, - OP_EQUAL : 135, - OP_EQUALVERIFY : 136, - OP_RESERVED1 : 137, - OP_RESERVED2 : 138, + // bit logic + OP_INVERT : 131, + OP_AND : 132, + OP_OR : 133, + OP_XOR : 134, + OP_EQUAL : 135, + OP_EQUALVERIFY : 136, + OP_RESERVED1 : 137, + OP_RESERVED2 : 138, - // numeric - OP_1ADD : 139, - OP_1SUB : 140, - OP_2MUL : 141, - OP_2DIV : 142, - OP_NEGATE : 143, - OP_ABS : 144, - OP_NOT : 145, - OP_0NOTEQUAL : 146, + // numeric + OP_1ADD : 139, + OP_1SUB : 140, + OP_2MUL : 141, + OP_2DIV : 142, + OP_NEGATE : 143, + OP_ABS : 144, + OP_NOT : 145, + OP_0NOTEQUAL : 146, - OP_ADD : 147, - OP_SUB : 148, - OP_MUL : 149, - OP_DIV : 150, - OP_MOD : 151, - OP_LSHIFT : 152, - OP_RSHIFT : 153, + OP_ADD : 147, + OP_SUB : 148, + OP_MUL : 149, + OP_DIV : 150, + OP_MOD : 151, + OP_LSHIFT : 152, + OP_RSHIFT : 153, - OP_BOOLAND : 154, - OP_BOOLOR : 155, - OP_NUMEQUAL : 156, - OP_NUMEQUALVERIFY : 157, - OP_NUMNOTEQUAL : 158, - OP_LESSTHAN : 159, - OP_GREATERTHAN : 160, - OP_LESSTHANOREQUAL : 161, - OP_GREATERTHANOREQUAL : 162, - OP_MIN : 163, - OP_MAX : 164, + OP_BOOLAND : 154, + OP_BOOLOR : 155, + OP_NUMEQUAL : 156, + OP_NUMEQUALVERIFY : 157, + OP_NUMNOTEQUAL : 158, + OP_LESSTHAN : 159, + OP_GREATERTHAN : 160, + OP_LESSTHANOREQUAL : 161, + OP_GREATERTHANOREQUAL : 162, + OP_MIN : 163, + OP_MAX : 164, - OP_WITHIN : 165, + OP_WITHIN : 165, - // crypto - OP_RIPEMD160 : 166, - OP_SHA1 : 167, - OP_SHA256 : 168, - OP_HASH160 : 169, - OP_HASH256 : 170, - OP_CODESEPARATOR : 171, - OP_CHECKSIG : 172, - OP_CHECKSIGVERIFY : 173, - OP_CHECKMULTISIG : 174, - OP_CHECKMULTISIGVERIFY : 175, + // crypto + OP_RIPEMD160 : 166, + OP_SHA1 : 167, + OP_SHA256 : 168, + OP_HASH160 : 169, + OP_HASH256 : 170, + OP_CODESEPARATOR : 171, + OP_CHECKSIG : 172, + OP_CHECKSIGVERIFY : 173, + OP_CHECKMULTISIG : 174, + OP_CHECKMULTISIGVERIFY : 175, - // expansion - OP_NOP1 : 176, - OP_NOP2 : 177, - OP_NOP3 : 178, - OP_NOP4 : 179, - OP_NOP5 : 180, - OP_NOP6 : 181, - OP_NOP7 : 182, - OP_NOP8 : 183, - OP_NOP9 : 184, - OP_NOP10 : 185, + // expansion + OP_NOP1 : 176, + OP_NOP2 : 177, + OP_NOP3 : 178, + OP_NOP4 : 179, + OP_NOP5 : 180, + OP_NOP6 : 181, + OP_NOP7 : 182, + OP_NOP8 : 183, + OP_NOP9 : 184, + OP_NOP10 : 185, - // template matching params - OP_PUBKEYHASH : 253, - OP_PUBKEY : 254, - OP_INVALIDOPCODE : 255 - }; + // template matching params + OP_PUBKEYHASH : 253, + OP_PUBKEY : 254, + OP_INVALIDOPCODE : 255 +}; - Opcode.reverseMap = []; +Opcode.reverseMap = []; - for (var i in Opcode.map) { - Opcode.reverseMap[Opcode.map[i]] = i; - } -})(); +for (var i in Opcode.map) { + Opcode.reverseMap[Opcode.map[i]] = i; +} + +module.exports = Opcode; diff --git a/src/script.js b/src/script.js index 71f1ef5..16191fd 100644 --- a/src/script.js +++ b/src/script.js @@ -1,347 +1,382 @@ -(function () { - var Opcode = Bitcoin.Opcode; +var Opcode = require('./opcode'); +var util = require('./util'); +var conv = require('./convert'); +var Address = require('./address'); - // Make opcodes available as pseudo-constants - for (var i in Opcode.map) { - eval("var " + i + " = " + Opcode.map[i] + ";"); +var Script = function (data) { + if (!data) { + this.buffer = []; + } else if ("string" == typeof data) { + this.buffer = conv.hexToBytes(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) { - if (!data) { - this.buffer = []; - } else if ("string" == typeof data) { - this.buffer = Crypto.util.base64ToBytes(data); - } else if (Bitcoin.Util.isArray(data)) { - this.buffer = data; - } else if (data instanceof Script) { - this.buffer = data.buffer; + this.parse(); +}; + +Script.fromPubKey = function(str) { + var script = new Script(); + var s = str.split(" "); + for (var i in s) { + if (Opcode.map.hasOwnProperty(s[i])){ + script.writeOp(Opcode.map[s[i]]); } else { - throw new Error("Invalid script"); + script.writeBytes(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. + * + * Each Script object stores the script in two formats. First as a raw byte + * array and second as an array of "chunks", such as opcodes and pieces of + * data. + * + * This method updates the chunks cache. Normally this is called by the + * constructor and you don't need to worry about it. However, if you change + * the script buffer manually, you should update the chunks using this method. + */ +Script.prototype.parse = function () { + var self = this; + + this.chunks = []; + + // Cursor + var i = 0; + + // Read n bytes and store result as a chunk + function readChunk(n) { + self.chunks.push(self.buffer.slice(i, i + n)); + i += n; }; - /** - * Update the parsed script representation. - * - * Each Script object stores the script in two formats. First as a raw byte - * array and second as an array of "chunks", such as opcodes and pieces of - * data. - * - * This method updates the chunks cache. Normally this is called by the - * constructor and you don't need to worry about it. However, if you change - * the script buffer manually, you should update the chunks using this method. - */ - Script.prototype.parse = function () { - var self = this; - - this.chunks = []; - - // Cursor - var i = 0; - - // Read n bytes and store result as a chunk - function readChunk(n) { - self.chunks.push(self.buffer.slice(i, i + n)); - i += n; - }; - - while (i < this.buffer.length) { - var opcode = this.buffer[i++]; - if (opcode >= 0xF0) { - // Two byte opcode - opcode = (opcode << 8) | this.buffer[i++]; - } - - var len; - if (opcode > 0 && opcode < OP_PUSHDATA1) { - // Read some bytes of data, opcode value is the length of data - readChunk(opcode); - } else if (opcode == OP_PUSHDATA1) { - len = this.buffer[i++]; - readChunk(len); - } else if (opcode == OP_PUSHDATA2) { - len = (this.buffer[i++] << 8) | this.buffer[i++]; - readChunk(len); - } else if (opcode == OP_PUSHDATA4) { - len = (this.buffer[i++] << 24) | - (this.buffer[i++] << 16) | - (this.buffer[i++] << 8) | - this.buffer[i++]; - readChunk(len); - } else { - this.chunks.push(opcode); - } + while (i < this.buffer.length) { + var opcode = this.buffer[i++]; + if (opcode >= 0xF0) { + // Two byte opcode + opcode = (opcode << 8) | this.buffer[i++]; } - }; - /** - * Compare the script to known templates of scriptPubKey. - * - * This method will compare the script to a small number of standard script - * templates and return a string naming the detected type. - * - * Currently supported are: - * Address: - * Paying to a Bitcoin address which is the hash of a pubkey. - * OP_DUP OP_HASH160 [pubKeyHash] OP_EQUALVERIFY OP_CHECKSIG - * - * Pubkey: - * Paying to a public key directly. - * [pubKey] OP_CHECKSIG - * - * Strange: - * Any other script (no template matched). - */ - Script.prototype.getOutType = function () { + var len; + if (opcode > 0 && opcode < Opcode.map.OP_PUSHDATA1) { + // Read some bytes of data, opcode value is the length of data + readChunk(opcode); + } else if (opcode == Opcode.map.OP_PUSHDATA1) { + len = this.buffer[i++]; + readChunk(len); + } else if (opcode == Opcode.map.OP_PUSHDATA2) { + len = (this.buffer[i++] << 8) | this.buffer[i++]; + readChunk(len); + } else if (opcode == Opcode.map.OP_PUSHDATA4) { + len = (this.buffer[i++] << 24) | + (this.buffer[i++] << 16) | + (this.buffer[i++] << 8) | + this.buffer[i++]; + readChunk(len); + } else { + this.chunks.push(opcode); + } + } +}; - if (this.chunks[this.chunks.length-1] == 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] == OP_DUP && - this.chunks[1] == OP_HASH160 && - this.chunks[3] == OP_EQUALVERIFY && - this.chunks[4] == OP_CHECKSIG) { - // Transfer to Bitcoin address - return 'Address'; - } else if (this.chunks.length == 2 && - this.chunks[1] == OP_CHECKSIG) { - // Transfer to IP address - return 'Pubkey'; - } else { - return 'Strange'; - } +/** + * Compare the script to known templates of scriptPubKey. + * + * This method will compare the script to a small number of standard script + * templates and return a string naming the detected type. + * + * Currently supported are: + * Address: + * Paying to a Bitcoin address which is the hash of a pubkey. + * OP_DUP OP_HASH160 [pubKeyHash] OP_EQUALVERIFY OP_CHECKSIG + * + * Pubkey: + * Paying to a public key directly. + * [pubKey] OP_CHECKSIG + * + * Strange: + * Any other script (no template matched). + */ +Script.prototype.getOutType = function () { + if (this.chunks[this.chunks.length-1] == Opcode.map.OP_EQUAL && + this.chunks[0] == Opcode.map.OP_HASH160 && + this.chunks.length == 3) { + // Transfer to M-OF-N + return 'P2SH'; + } + 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 'Pubkey'; + } + else { + return 'Strange'; + } } - /** - * Returns the affected address hash for this output. - * - * For standard transactions, this will return the hash of the pubKey that - * can spend this output. - * - * In the future, for payToScriptHash outputs, this will return the - * scriptHash. Note that non-standard and standard payToScriptHash transactions - * look the same - * - * This method is useful for indexing transactions. - */ - Script.prototype.simpleOutHash = function () - { - switch (this.getOutType()) { - case 'Address': - return this.chunks[2]; - case 'Pubkey': - return Bitcoin.Util.sha256ripe160(this.chunks[0]); - default: - throw new Error("Encountered non-standard scriptPubKey"); - } - }; +/** + * Returns the address corresponding to this output in hash160 form. + * Assumes strange scripts are P2SH + */ +Script.prototype.toScriptHash = function () { + var outType = this.getOutType(); - /** - * Old name for Script#simpleOutHash. - * - * @deprecated - */ - Script.prototype.simpleOutPubKeyHash = Script.prototype.simpleOutHash; + return outType == 'Pubkey' ? this.chunks[2] + : outType == 'P2SH' ? util.sha256ripe160(this.buffer) + : util.sha256ripe160(this.buffer) +}; - /** - * Compare the script to known templates of scriptSig. - * - * This method will compare the script to a small number of standard script - * templates and return a string naming the detected type. - * - * WARNING: Use this method with caution. It merely represents a heuristic - * based on common transaction formats. A non-standard transaction could - * very easily match one of these templates by accident. - * - * Currently supported are: - * Address: - * Paying to a Bitcoin address which is the hash of a pubkey. - * [sig] [pubKey] - * - * Pubkey: - * Paying to a public key directly. - * [sig] - * - * Strange: - * Any other script (no template matched). - */ - Script.prototype.getInType = function () - { - if (this.chunks.length == 1 && - Bitcoin.Util.isArray(this.chunks[0])) { - // Direct IP to IP transactions only have the signature in their scriptSig. - // TODO: We could also check that the length of the data is correct. - return 'Pubkey'; - } else if (this.chunks.length == 2 && - Bitcoin.Util.isArray(this.chunks[0]) && - Bitcoin.Util.isArray(this.chunks[1])) { - return 'Address'; - } else { - return 'Strange'; - } - }; +Script.prototype.toAddress = function() { + var outType = this.getOutType(); + return outType == 'Pubkey' ? new Address(this.chunks[2]) + : outType == 'P2SH' ? new Address(this.chunks[1],5) + : new Address(this.chunks[1],5) +} - /** - * Returns the affected public key for this input. - * - * This currently only works with payToPubKeyHash transactions. It will also - * work in the future for standard payToScriptHash transactions that use a - * single public key. - * - * However for multi-key and other complex transactions, this will only return - * one of the keys or raise an error. Therefore, it is recommended for indexing - * purposes to use Script#simpleInHash or Script#simpleOutHash instead. - * - * @deprecated - */ - Script.prototype.simpleInPubKey = function () - { - switch (this.getInType()) { - case 'Address': - return this.chunks[1]; - 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"); - } - }; +/** + * Compare the script to known templates of scriptSig. + * + * This method will compare the script to a small number of standard script + * templates and return a string naming the detected type. + * + * WARNING: Use this method with caution. It merely represents a heuristic + * based on common transaction formats. A non-standard transaction could + * very easily match one of these templates by accident. + * + * Currently supported are: + * Address: + * Paying to a Bitcoin address which is the hash of a pubkey. + * [sig] [pubKey] + * + * Pubkey: + * Paying to a public key directly. + * [sig] + * + * Multisig: + * Paying to M-of-N public keys. + * + * Strange: + * Any other script (no template matched). + */ +Script.prototype.getInType = function () +{ + if (this.chunks.length == 1 && + 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 && + util.isArray(this.chunks[0]) && + util.isArray(this.chunks[1])) { + return 'Address'; + } else if (this.chunks[0] == Opcode.map.OP_0 && + this.chunks.slice(1).reduce(function(t,chunk,i) { + return t && util.isArray(chunk) + && (chunk[0] == 48 || i == this.chunks.length - 1); + },true)) { + return 'Multisig'; + } else { + return 'Strange'; + } +}; - /** - * Returns the affected address hash for this input. - * - * For standard transactions, this will return the hash of the pubKey that - * can spend this output. - * - * In the future, for standard payToScriptHash inputs, this will return the - * scriptHash. - * - * Note: This function provided for convenience. If you have the corresponding - * scriptPubKey available, you are urged to use Script#simpleOutHash instead - * as it is more reliable for non-standard payToScriptHash transactions. - * - * This method is useful for indexing transactions. - */ - Script.prototype.simpleInHash = function () - { - return Bitcoin.Util.sha256ripe160(this.simpleInPubKey()); - }; +/** + * Returns the affected public key for this input. + * + * This currently only works with payToPubKeyHash transactions. It will also + * work in the future for standard payToScriptHash transactions that use a + * single public key. + * + * However for multi-key and other complex transactions, this will only return + * one of the keys or raise an error. Therefore, it is recommended for indexing + * purposes to use Script#simpleInHash or Script#simpleOutHash instead. + * + * @deprecated + */ +Script.prototype.simpleInPubKey = function () +{ + switch (this.getInType()) { + case 'Address': + return this.chunks[1]; + 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"); + } +}; - /** - * Old name for Script#simpleInHash. - * - * @deprecated - */ - Script.prototype.simpleInPubKeyHash = Script.prototype.simpleInHash; +/** + * Returns the affected address hash for this input. + * + * For standard transactions, this will return the hash of the pubKey that + * can spend this output. + * + * In the future, for standard payToScriptHash inputs, this will return the + * scriptHash. + * + * Note: This function provided for convenience. If you have the corresponding + * scriptPubKey available, you are urged to use Script#simpleOutHash instead + * as it is more reliable for non-standard payToScriptHash transactions. + * + * This method is useful for indexing transactions. + */ +Script.prototype.simpleInHash = function () +{ + return util.sha256ripe160(this.simpleInPubKey()); +}; - /** - * Add an op code to the script. - */ - Script.prototype.writeOp = function (opcode) - { - this.buffer.push(opcode); - this.chunks.push(opcode); - }; +/** + * Old name for Script#simpleInHash. + * + * @deprecated + */ +Script.prototype.simpleInPubKeyHash = Script.prototype.simpleInHash; - /** - * Add a data chunk to the script. - */ - Script.prototype.writeBytes = function (data) - { - if (data.length < OP_PUSHDATA1) { - this.buffer.push(data.length); - } else if (data.length <= 0xff) { - this.buffer.push(OP_PUSHDATA1); - this.buffer.push(data.length); - } else if (data.length <= 0xffff) { - this.buffer.push(OP_PUSHDATA2); - this.buffer.push(data.length & 0xff); - this.buffer.push((data.length >>> 8) & 0xff); - } else { - this.buffer.push(OP_PUSHDATA4); - this.buffer.push(data.length & 0xff); - this.buffer.push((data.length >>> 8) & 0xff); - this.buffer.push((data.length >>> 16) & 0xff); - this.buffer.push((data.length >>> 24) & 0xff); - } - this.buffer = this.buffer.concat(data); - this.chunks.push(data); - }; +/** + * Add an op code to the script. + */ +Script.prototype.writeOp = function (opcode) +{ + this.buffer.push(opcode); + this.chunks.push(opcode); +}; - /** - * 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; - }; +/** + * Add a data chunk to the script. + */ +Script.prototype.writeBytes = function (data) +{ + if (data.length < Opcode.map.OP_PUSHDATA1) { + this.buffer.push(data.length); + } else if (data.length <= 0xff) { + this.buffer.push(Opcode.map.OP_PUSHDATA1); + this.buffer.push(data.length); + } else if (data.length <= 0xffff) { + this.buffer.push(Opcode.map.OP_PUSHDATA2); + this.buffer.push(data.length & 0xff); + this.buffer.push((data.length >>> 8) & 0xff); + } else { + this.buffer.push(Opcode.map.OP_PUSHDATA4); + this.buffer.push(data.length & 0xff); + this.buffer.push((data.length >>> 8) & 0xff); + this.buffer.push((data.length >>> 16) & 0xff); + this.buffer.push((data.length >>> 24) & 0xff); + } + this.buffer = this.buffer.concat(data); + this.chunks.push(data); +}; + +/** + * Create an output for an address + */ +Script.createOutputScript = function (address) +{ + var script = new Script(); + address = new Address(address); + // Standard pay-to-pubkey-hash + if (!address.version) { + 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); + } + // Standard pay-to-script-hash + else { + script.writeOp(Opcode.map.OP_HASH160); + script.writeBytes(address.hash); + script.writeOp(Opcode.map.OP_EQUAL); + } + return script; +}; + +/** + * Extract pubkeys from a multisig script + */ + +Script.prototype.extractPubkeys = function() { + return this.chunks.filter(function(chunk) { + return (chunk[0] == 4 && chunk.length == 65 + || chunk[0] < 4 && chunk.length == 33) + }); +} + +/** + * Create an m-of-n output script + */ +Script.createMultiSigOutputScript = function (m, pubkeys) +{ + var script = new Script(); + + pubkeys = pubkeys.sort(); + script.writeOp(Opcode.map.OP_1 + m - 1); - /** - * Extract bitcoin addresses from an output script - */ - Script.prototype.extractAddresses = function (addresses) - { - switch (this.getOutType()) { - case 'Address': - addresses.push(new Address(this.chunks[2])); - return 1; - case 'Pubkey': - addresses.push(new Address(Util.sha256ripe160(this.chunks[0]))); - return 1; - case 'Multisig': - for (var i = 1; i < this.chunks.length-2; ++i) { - addresses.push(new Address(Util.sha256ripe160(this.chunks[i]))); - } - return this.chunks[0] - OP_1 + 1; - default: - throw new Error("Encountered non-standard scriptPubKey"); - } - }; + for (var i = 0; i < pubkeys.length; ++i) { + script.writeBytes(pubkeys[i]); + } + + script.writeOp(Opcode.map.OP_1 + pubkeys.length - 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]); - } - - 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; +}; - /** - * Create a standard payToPubKeyHash input. - */ - Script.createInputScript = function (signature, pubKey) - { - var script = new Script(); - script.writeBytes(signature); - script.writeBytes(pubKey); - return script; - }; +/** + * Create a multisig input + */ +Script.createMultiSigInputScript = function(signatures, script) +{ + script = new Script(script); + var k = script.chunks[0][0]; + if (signatures.length < k) return false; //Not enough sigs + var inScript = new Script(); + inScript.writeOp(Opcode.map.OP_0); + signatures.map(function(sig) { inScript.writeBytes(sig) }); + inScript.writeBytes(script.buffer); + return inScript; +} - Script.prototype.clone = function () - { - return new Script(this.buffer); - }; -})(); +Script.prototype.clone = function () +{ + return new Script(this.buffer); +}; + +module.exports = Script; diff --git a/src/transaction.js b/src/transaction.js index c06dc87..aaef806 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -1,445 +1,587 @@ -(function () { - var Script = Bitcoin.Script; +var BigInteger = require('./jsbn/jsbn'); +var Script = require('./script'); +var util = require('./util'); +var conv = require('./convert'); +var Crypto = require('./crypto-js/crypto'); +var Wallet = require('./wallet'); +var ECKey = require('./eckey').ECKey; +var ECDSA = require('./ecdsa'); +var Address = require('./address'); - var Transaction = Bitcoin.Transaction = function (doc) { +var Transaction = function (doc) { this.version = 1; this.lock_time = 0; this.ins = []; this.outs = []; this.timestamp = null; this.block = null; - + if (doc) { - if (doc.hash) this.hash = doc.hash; - if (doc.version) this.version = doc.version; - if (doc.lock_time) this.lock_time = doc.lock_time; - if (doc.ins && doc.ins.length) { - for (var i = 0; i < doc.ins.length; i++) { - this.addInput(new TransactionIn(doc.ins[i])); + if (typeof doc == "string" || util.isArray(doc)) { + doc = Transaction.deserialize(doc) } - } - if (doc.outs && doc.outs.length) { - for (var i = 0; i < doc.outs.length; i++) { - this.addOutput(new TransactionOut(doc.outs[i])); + if (doc.hash) this.hash = doc.hash; + if (doc.version) this.version = doc.version; + if (doc.lock_time) this.lock_time = doc.lock_time; + if (doc.ins && doc.ins.length) { + for (var i = 0; i < doc.ins.length; i++) { + this.addInput(new TransactionIn(doc.ins[i])); + } } - } - if (doc.timestamp) this.timestamp = doc.timestamp; - if (doc.block) this.block = doc.block; + 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; - }; +/** + * 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) { +/** + * Create a new txin. + * + * Can be called with any of: + * + * - An existing TransactionOut object + * - A transaction and an index + * - A transaction hash and an index + * - A single string argument of the form txhash:index + * + * Note that this method does not sign the created input. + */ +Transaction.prototype.addInput = function (tx, outIndex) { if (arguments[0] instanceof TransactionIn) { - this.ins.push(arguments[0]); - } else { - this.ins.push(new TransactionIn({ - outpoint: { - hash: tx.hash, - index: outIndex - }, - script: new Bitcoin.Script(), - sequence: 4294967295 - })); + this.ins.push(arguments[0]); + } + else if (arguments[0].length > 65) { + var args = arguments[0].split(':'); + return this.addInput(args[0], args[1]); + } + else { + this.ins.push(new TransactionIn({ + outpoint: { + hash: tx.hash || tx, + index: outIndex + }, + script: new Script(), + sequence: 4294967295 + })); } - }; +}; - /** - * 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) { +/** + * Create a new txout. + * + * Can be called with: + * + * i) An existing TransactionOut object + * ii) An address object or an address and a value + * iii) An address:value string + * + */ +Transaction.prototype.addOutput = function (address, value) { if (arguments[0] instanceof TransactionOut) { - this.outs.push(arguments[0]); - } 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({ + this.outs.push(arguments[0]); + return; + } + if (arguments[0].indexOf(':') >= 0) { + var args = arguments[0].split(':'); + address = args[0]; + value = parseInt(args[1]); + } + 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 () - { +/** + * 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)); + buffer = buffer.concat(util.numToBytes(parseInt(this.version),4)); + 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(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()); + var txin = this.ins[i]; + + // Why do blockchain.info, blockexplorer.com, sx and just about everybody + // else use little-endian hashes? No idea... + buffer = buffer.concat(conv.hexToBytes(txin.outpoint.hash).reverse()); + + buffer = buffer.concat(util.numToBytes(parseInt(txin.outpoint.index),4)); + var scriptBytes = txin.script.buffer; + buffer = buffer.concat(util.numToVarInt(scriptBytes.length)); + buffer = buffer.concat(scriptBytes); + buffer = buffer.concat(util.numToBytes(parseInt(txin.sequence),4)); } - buffer = buffer.concat(Bitcoin.Util.numToVarInt(this.outs.length)); + 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(Bitcoin.Util.numToVarInt(scriptBytes.length)); - buffer = buffer.concat(scriptBytes); + var txout = this.outs[i]; + buffer = buffer.concat(util.numToBytes(txout.value,8)); + var scriptBytes = txout.script.buffer; + buffer = buffer.concat(util.numToVarInt(scriptBytes.length)); + buffer = buffer.concat(scriptBytes); } - buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(this.lock_time)]).reverse()); + buffer = buffer.concat(util.numToBytes(parseInt(this.lock_time),4)); return buffer; - }; +}; - var OP_CODESEPARATOR = 171; +Transaction.prototype.serializeHex = function() { + return conv.bytesToHex(this.serialize()); +} - var SIGHASH_ALL = 1; - var SIGHASH_NONE = 2; - var SIGHASH_SINGLE = 3; - var SIGHASH_ANYONECANPAY = 80; +var OP_CODESEPARATOR = 171; - /** - * 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(); +var SIGHASH_ALL = 1; +var SIGHASH_NONE = 2; +var SIGHASH_SINGLE = 3; +var SIGHASH_ANYONECANPAY = 80; - // 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; - });*/ +/** + * 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(); - // Blank out other inputs' signatures - for (var i = 0; i < txTmp.ins.length; i++) { - txTmp.ins[i].script = new Script(); + // 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(util.numToBytes(parseInt(hashType),4)); + + var hash1 = Crypto.SHA256(buffer, {asBytes: true}); + + return Crypto.SHA256(hash1, {asBytes: true}); +}; + +/** + * Calculate and return the transaction's hash. + * Reverses hash since blockchain.info, blockexplorer.com and others + * use little-endian hashes for some stupid reason + */ +Transaction.prototype.getHash = function () +{ + var buffer = this.serialize(); + return Crypto.SHA256(Crypto.SHA256(buffer, {asBytes: true}), {asBytes: true}).reverse(); +}; + +/** + * 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 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; } - - 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(Crypto.util.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; - } - 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); - - var analysis = {}; - - analysis.impact = impact; - - if (impact.sign > 0 && impact.value.compareTo(BigInteger.ZERO) > 0) { - analysis.type = 'recv'; - analysis.addr = new Bitcoin.Address(firstMeRecvHash); - } else if (allFromMe && allToMe) { - analysis.type = 'self'; - } else if (allFromMe) { - analysis.type = 'sent'; - // TODO: Right now, firstRecvHash is the first output, which - if the - // transaction was not generated by this library could be the - // change address. - analysis.addr = new Bitcoin.Address(firstRecvHash); - } else { - analysis.type = "other"; - } - - return analysis; - }; - - /** - * Get a human-readable version of the data returned by Transaction#analyze. - * - * This is merely a convenience function. Clients should consider implementing - * this themselves based on their UI, I18N, etc. - */ - Transaction.prototype.getDescription = function (wallet) { - var analysis = this.analyze(wallet); - - if (!analysis) return ""; - - switch (analysis.type) { - case 'recv': - return "Received with "+analysis.addr; + 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; - - 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)); + var impact = this.calcImpact(wallet); + + var analysis = {}; + + analysis.impact = impact; + + if (impact.sign > 0 && impact.value > 0) { + analysis.type = 'recv'; + analysis.addr = new Address(firstMeRecvHash); + } else if (allFromMe && allToMe) { + analysis.type = 'self'; + } else if (allFromMe) { + analysis.type = 'sent'; + // TODO: Right now, firstRecvHash is the first output, which - if the + // transaction was not generated by this library could be the + // change address. + analysis.addr = new Address(firstRecvHash); + } else { + analysis.type = "other"; + } + + return analysis; +}; + +/** + * Get a human-readable version of the data returned by Transaction#analyze. + * + * This is merely a convenience function. Clients should consider implementing + * this themselves based on their UI, I18N, etc. + */ +Transaction.prototype.getDescription = function (wallet) { + var analysis = this.analyze(wallet); + + 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 () { + return this.outs.reduce(function(t,o) { return t + o.value },0); +}; + + /** + * 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 Wallet)) return 0; + + // Calculate credit to us from all outputs + var valueOut = this.outs.filter(function(o) { + return wallet.hasHash(conv.bytesToHex(o.script.simpleOutPubKeyHash())); + }) + .reduce(function(t,o) { return t+o.value },0); + + var valueIn = this.ins.filter(function(i) { + return wallet.hasHash(conv.bytesToHex(i.script.simpleInPubKeyHash())) + && wallet.txIndex[i.outpoint.hash]; + }) + .reduce(function(t,i) { + return t + wallet.txIndex[i.outpoint.hash].outs[i.outpoint.index].value + },0); + + if (valueOut > valueIn) { + return { + sign: 1, + value: valueOut - valueIn + }; + } else { + return { + sign: -1, + value: valueIn - valueOut + }; + } +}; + +/** + * Converts a serialized transaction into a transaction object + */ + +Transaction.deserialize = function(buffer) { + if (typeof buffer == "string") { + buffer = conv.hexToBytes(buffer) } - 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)); - } + var pos = 0; + var readAsInt = function(bytes) { + if (bytes == 0) return 0; + pos++; + return buffer[pos-1] + readAsInt(bytes-1) * 256; } - - // Calculate debit to us from all ins - var valueIn = BigInteger.ZERO; - for (var j = 0; j < this.ins.length; j++) { - var txin = this.ins[j]; - var hash = Crypto.util.bytesToBase64(txin.script.simpleInPubKeyHash()); - if (wallet.hasHash(hash)) { - var fromTx = wallet.txIndex[txin.outpoint.hash]; - if (fromTx) { - valueIn = valueIn.add(Bitcoin.Util.valueToBigInt(fromTx.outs[txin.outpoint.index].value)); + var readVarInt = function() { + pos++; + if (buffer[pos-1] < 253) { + return buffer[pos-1]; } - } + return readAsInt(buffer[pos-1] - 251); } - if (valueOut.compareTo(valueIn) >= 0) { - return { - sign: 1, - value: valueOut.subtract(valueIn) - }; - } else { - return { - sign: -1, - value: valueIn.subtract(valueOut) - }; + var readBytes = function(bytes) { + pos += bytes; + return buffer.slice(pos - bytes, pos); } - }; + var readVarString = function() { + var size = readVarInt(); + return readBytes(size); + } + var obj = { + ins: [], + outs: [] + } + obj.version = readAsInt(4); + var ins = readVarInt(); + for (var i = 0; i < ins; i++) { + obj.ins.push({ + outpoint: { + hash: conv.bytesToHex(readBytes(32).reverse()), + index: readAsInt(4) + }, + script: new Script(readVarString()), + sequence: readAsInt(4) + }); + } + var outs = readVarInt(); + for (var i = 0; i < outs; i++) { + obj.outs.push({ + value: util.bytesToNum(readBytes(8)), + script: new Script(readVarString()) + }); + } + obj.locktime = readAsInt(4); + return new Transaction(obj); +} - var TransactionIn = Bitcoin.TransactionIn = function (data) - { - this.outpoint = data.outpoint; - if (data.script instanceof Script) { - this.script = data.script; - } else { - this.script = new Script(data.script); - } - this.sequence = data.sequence; - }; +/** + * Signs a standard output at some index with the given key + */ - TransactionIn.prototype.clone = function () - { - var newTxin = new TransactionIn({ - outpoint: { - hash: this.outpoint.hash, - index: this.outpoint.index - }, - script: this.script.clone(), - sequence: this.sequence +Transaction.prototype.sign = function(index, key, type) { + type = type || SIGHASH_ALL; + key = new ECKey(key); + var pub = key.getPub().export('bytes'), + hash160 = util.sha256ripe160(pub), + script = Script.createOutputScript(new Address(hash160)), + hash = this.hashTransactionForSignature( script, index, type), + sig = key.sign(hash).concat([type]); + this.ins[index].script = Script.createInputScript(sig,pub); +} + +// Takes outputs of the form [{ output: 'txhash:index', address: 'address' },...] +Transaction.prototype.signWithKeys = function(keys, outputs, type) { + type = type || SIGHASH_ALL; + var addrdata = keys.map(function(key) { + key = new ECKey(key); + return { + key: key, + address: key.getBitcoinAddress().toString() + } }); - return newTxin; - }; - - var TransactionOut = Bitcoin.TransactionOut = function (data) - { - if (data.script instanceof Script) { - this.script = data.script; - } else { - this.script = new Script(data.script); + var hmap = {}; + for (var o in outputs) { + hmap[outputs[o].output] = outputs[o]; } - - if (Bitcoin.Util.isArray(data.value)) { - this.value = data.value; - } else if ("string" == typeof data.value) { - var valueHex = (new BigInteger(data.value, 10)).toString(16); - while (valueHex.length < 16) valueHex = "0" + valueHex; - this.value = Crypto.util.hexToBytes(valueHex); + for (var i = 0; i < this.ins.length; i++) { + var outpoint = this.ins[i].outpoint.hash+':'+this.ins[i].outpoint.index, + histItem = hmap[outpoint]; + if (!histItem) continue; + var thisInputAddrdata = addrdata.filter(function(a) { + return a.address == histItem.address; + }); + if (thisInputAddrdata.length == 0) continue; + this.sign(i,thisInputAddrdata[0].key); } - }; +} - TransactionOut.prototype.clone = function () - { - var newTxout = new TransactionOut({ - script: this.script.clone(), - value: this.value.slice(0) +/** + * Signs a P2SH output at some index with the given key + */ + +Transaction.prototype.p2shsign = function(index, script, key, type) { + script = new Script(script); + key = new ECKey(key); + type = type || SIGHASH_ALL; + var hash = this.hashTransactionForSignature(script, index, type), + sig = key.sign(hash).concat([type]); + return sig; +} + +Transaction.prototype.multisign = Transaction.prototype.p2shsign; + +Transaction.prototype.applyMultisigs = function(index, script, sigs, type) { + this.ins[index].script = Script.createMultiSigInputScript(sigs, script); +} + +Transaction.prototype.validateSig = function(index, script, sig, pub) { + script = new Script(script); + var hash = this.hashTransactionForSignature(script,index,1); + return ECDSA.verify(hash, conv.coerceToBytes(sig), + conv.coerceToBytes(pub)); +} + + +var TransactionIn = function (data) { + if (typeof data == "string") + this.outpoint = { hash: data.split(':')[0], index: data.split(':')[1] } + else if (data.outpoint) + this.outpoint = data.outpoint + else + this.outpoint = { hash: data.hash, index: data.index } + + if (data.scriptSig) + this.script = Script.fromScriptSig(data.scriptSig) + else + this.script = new Script(data.script) + + this.sequence = data.sequence || 4294967295; +}; + +TransactionIn.prototype.clone = function () { + return new TransactionIn({ + outpoint: { + hash: this.outpoint.hash, + index: this.outpoint.index + }, + script: this.script.clone(), + sequence: this.sequence }); - return newTxout; - }; -})(); +}; +var TransactionOut = function (data) { + this.script = + data.script instanceof Script ? data.script.clone() + : util.isArray(data.script) ? new Script(data.script) + : typeof data.script == "string" ? new Script(conv.hexToBytes(data.script)) + : data.scriptPubKey ? Script.fromScriptSig(data.scriptPubKey) + : data.address ? Script.createOutputScript(data.address) + : new Script(); + if (this.script.buffer.length > 0) this.address = this.script.toAddress(); + + this.value = + util.isArray(data.value) ? util.bytesToNum(data.value) + : "string" == typeof data.value ? parseInt(data.value) + : data.value instanceof BigInteger ? parseInt(data.value.toString()) + : data.value; +}; + +TransactionOut.prototype.clone = function () +{ + var newTxout = new TransactionOut({ + script: this.script.clone(), + value: this.value + }); + return newTxout; +}; + +module.exports.Transaction = Transaction; +module.exports.TransactionIn = TransactionIn; +module.exports.TransactionOut = TransactionOut; diff --git a/src/txdb.js b/src/txdb.js old mode 100755 new mode 100644 index 73ee835..5baff7c --- a/src/txdb.js +++ b/src/txdb.js @@ -1,3 +1,5 @@ +var Transaction = require('./transaction'); + var TransactionDatabase = function () { this.txs = []; this.txIndex = {}; @@ -16,7 +18,7 @@ TransactionDatabase.prototype.addTransactionNoUpdate = function (tx) { return; } - this.txs.push(new Bitcoin.Transaction(tx)); + this.txs.push(new Transaction(tx)); this.txIndex[tx.hash] = tx; }; diff --git a/src/util.js b/src/util.js index 4c468a0..93cf123 100644 --- a/src/util.js +++ b/src/util.js @@ -1,228 +1,69 @@ -// BigInteger monkey patching -BigInteger.valueOf = nbv; +var Crypto = require('./crypto-js/crypto'); /** - * Returns a byte array representation of the big integer. - * - * This returns the absolute of the contained value in big endian - * form. A value of zero results in an empty array. + * Cross-browser compatibility version of Array.isArray. */ -BigInteger.prototype.toByteArrayUnsigned = function () { - var ba = this.abs().toByteArray(); - if (ba.length) { - if (ba[0] == 0) { - ba = ba.slice(1); - } - return ba.map(function (v) { - return (v < 0) ? v + 256 : v; - }); - } else { - // Empty array, nothing to do - return ba; - } -}; - -/** - * Turns a byte array into a big integer. - * - * This function will interpret a byte array as a big integer in big - * endian notation and ignore leading zeros. - */ -BigInteger.fromByteArrayUnsigned = function (ba) { - if (!ba.length) { - return ba.valueOf(0); - } else if (ba[0] & 0x80) { - // Prepend a zero so the BigInteger class doesn't mistake this - // for a negative integer. - return new BigInteger([0].concat(ba)); - } else { - return new BigInteger(ba); - } -}; - -/** - * Converts big integer to signed byte representation. - * - * The format for this value uses a the most significant bit as a sign - * bit. If the most significant bit is already occupied by the - * absolute value, an extra byte is prepended and the sign bit is set - * there. - * - * Examples: - * - * 0 => 0x00 - * 1 => 0x01 - * -1 => 0x81 - * 127 => 0x7f - * -127 => 0xff - * 128 => 0x0080 - * -128 => 0x8080 - * 255 => 0x00ff - * -255 => 0x80ff - * 16300 => 0x3fac - * -16300 => 0xbfac - * 62300 => 0x00f35c - * -62300 => 0x80f35c - */ -BigInteger.prototype.toByteArraySigned = function () { - var val = this.abs().toByteArrayUnsigned(); - var neg = this.compareTo(BigInteger.ZERO) < 0; - - if (neg) { - if (val[0] & 0x80) { - val.unshift(0x80); - } else { - val[0] |= 0x80; - } - } else { - if (val[0] & 0x80) { - val.unshift(0x00); - } - } - - return val; -}; - -/** - * Parse a signed big integer byte representation. - * - * For details on the format please see BigInteger.toByteArraySigned. - */ -BigInteger.fromByteArraySigned = function (ba) { - // Check for negative value - if (ba[0] & 0x80) { - // Remove sign bit - ba[0] &= 0x7f; - - return BigInteger.fromByteArrayUnsigned(ba).negate(); - } else { - return BigInteger.fromByteArrayUnsigned(ba); - } -}; - -// Console ignore -var names = ["log", "debug", "info", "warn", "error", "assert", "dir", - "dirxml", "group", "groupEnd", "time", "timeEnd", "count", - "trace", "profile", "profileEnd"]; - -if ("undefined" == typeof window.console) window.console = {}; -for (var i = 0; i < names.length; ++i) - if ("undefined" == typeof window.console[names[i]]) - window.console[names[i]] = function() {}; - -// Bitcoin utility functions -Bitcoin.Util = { - /** - * Cross-browser compatibility version of Array.isArray. - */ - isArray: Array.isArray || function(o) - { +exports.isArray = Array.isArray || function(o) { return Object.prototype.toString.call(o) === '[object Array]'; - }, - - /** - * Create an array of a certain length filled with a specific value. - */ - makeFilledArray: function (len, val) - { - var array = []; - var i = 0; - while (i < len) { - array[i++] = val; - } - return array; - }, - - /** - * Turn an integer into a "var_int". - * - * "var_int" is a variable length integer used by Bitcoin's binary format. - * - * Returns a byte array. - */ - numToVarInt: function (i) - { - if (i < 0xfd) { - // unsigned char - return [i]; - } else if (i <= 1<<16) { - // unsigned short (LE) - return [0xfd, i >>> 8, i & 255]; - } else if (i <= 1<<32) { - // unsigned int (LE) - return [0xfe].concat(Crypto.util.wordsToBytes([i])); - } else { - // unsigned long long (LE) - return [0xff].concat(Crypto.util.wordsToBytes([i >>> 32, i])); - } - }, - - /** - * Parse a Bitcoin value byte array, returning a BigInteger. - */ - valueToBigInt: function (valueBuffer) - { - if (valueBuffer instanceof BigInteger) return valueBuffer; - - // Prepend zero byte to prevent interpretation as negative integer - return BigInteger.fromByteArrayUnsigned(valueBuffer); - }, - - /** - * Format a Bitcoin value as a string. - * - * Takes a BigInteger or byte-array and returns that amount of Bitcoins in a - * nice standard formatting. - * - * Examples: - * 12.3555 - * 0.1234 - * 900.99998888 - * 34.00 - */ - formatValue: function (valueBuffer) { - var value = this.valueToBigInt(valueBuffer).toString(); - var integerPart = value.length > 8 ? value.substr(0, value.length-8) : '0'; - var decimalPart = value.length > 8 ? value.substr(value.length-8) : value; - while (decimalPart.length < 8) decimalPart = "0"+decimalPart; - decimalPart = decimalPart.replace(/0*$/, ''); - while (decimalPart.length < 2) decimalPart += "0"; - return integerPart+"."+decimalPart; - }, - - /** - * Parse a floating point string as a Bitcoin value. - * - * Keep in mind that parsing user input is messy. You should always display - * the parsed value back to the user to make sure we understood his input - * correctly. - */ - parseValue: function (valueString) { - // TODO: Detect other number formats (e.g. comma as decimal separator) - var valueComp = valueString.split('.'); - var integralPart = valueComp[0]; - var fractionalPart = valueComp[1] || "0"; - while (fractionalPart.length < 8) fractionalPart += "0"; - fractionalPart = fractionalPart.replace(/^0+/g, ''); - var value = BigInteger.valueOf(parseInt(integralPart)); - value = value.multiply(BigInteger.valueOf(100000000)); - value = value.add(BigInteger.valueOf(parseInt(fractionalPart))); - return value; - }, - - /** - * Calculate RIPEMD160(SHA256(data)). - * - * Takes an arbitrary byte array as inputs and returns the hash as a byte - * array. - */ - sha256ripe160: function (data) { - return Crypto.RIPEMD160(Crypto.SHA256(data, {asBytes: true}), {asBytes: true}); - } -}; - -for (var i in Crypto.util) { - if (Crypto.util.hasOwnProperty(i)) { - Bitcoin.Util[i] = Crypto.util[i]; - } +} + +/** + * Create a byte array representing a number with the given length + */ +exports.numToBytes = function(num, bytes) { + if (bytes === undefined) bytes = 8; + if (bytes === 0) return []; + return [num % 256].concat(module.exports.numToBytes(Math.floor(num / 256), bytes - 1)); +} + +/** + * Convert a byte array to the number that it represents + */ +exports.bytesToNum = function(bytes) { + if (bytes.length === 0) return 0; + return bytes[0] + 256 * module.exports.bytesToNum(bytes.slice(1)); +} + +/** + * Turn an integer into a "var_int". + * + * "var_int" is a variable length integer used by Bitcoin's binary format. + * + * Returns a byte array. + */ +exports.numToVarInt = function(num) { + if (num < 253) return [num]; + if (num < 65536) return [253].concat(exports.numToBytes(num, 2)); + if (num < 4294967296) return [254].concat(exports.numToBytes(num, 4)); + return [253].concat(exports.numToBytes(num, 8)); +} + +exports.bytesToWords = function (bytes) { + var words = []; + for (var i = 0, b = 0; i < bytes.length; i++, b += 8) { + words[b >>> 5] |= bytes[i] << (24 - b % 32); + } + return words; +} + +exports.wordsToBytes = function (words) { + var bytes = []; + for (var b = 0; b < words.length * 32; b += 8) { + bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF); + } + return bytes; +}, + +/** + * Calculate RIPEMD160(SHA256(data)). + * + * Takes an arbitrary byte array as inputs and returns the hash as a byte + * array. + */ +exports.sha256ripe160 = function (data) { + return Crypto.RIPEMD160(Crypto.SHA256(data, {asBytes: true}), {asBytes: true}); +} + +exports.error = function(msg) { + throw new Error(msg); } diff --git a/src/wallet.js b/src/wallet.js old mode 100755 new mode 100644 index 501ec34..a5c955d --- a/src/wallet.js +++ b/src/wallet.js @@ -1,308 +1,182 @@ -Bitcoin.Wallet = (function () { - var Script = Bitcoin.Script, - TransactionIn = Bitcoin.TransactionIn, - TransactionOut = Bitcoin.TransactionOut; +var Script = require('./script'); +var ECKey = require('./eckey').ECKey; +var conv = require('./convert'); +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 BigInteger = require('./jsbn/jsbn'); + +var Transaction = require('./transaction').Transaction; +var TransactionIn = require('./transaction').TransactionIn; +var TransactionOut = require('./transaction').TransactionOut; +var HDWallet = require('./hdwallet.js') + +var SecureRandom = require('./jsbn/rng'); +var rng = new SecureRandom(); + +var Wallet = function (seed) { + + // Stored in a closure to make accidental serialization less likely var keys = []; + var masterkey = null; + var me = this; - // Public hashes of our keys - this.addressHashes = []; + // Addresses + this.addresses = []; - // Transaction data - this.txIndex = {}; - this.unspentOuts = []; + // Transaction output data + this.outputs = {}; - // 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); + // Make a new master key + this.newMasterKey = function(seed) { + if (!seed) { + var seedBytes = new Array(32); + rng.nextBytes(seedBytes); + seed = conv.bytesToString(seedBytes) } - key.setPub(pub); - } + masterkey = new HDWallet(seed); + keys = [] + } + this.newMasterKey(seed) - this.addressHashes.push(key.getBitcoinAddress().getHashBase64()); - }; - - /** - * 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; - }; - - /** - * Get the public keys. - * - * Returns an array of base64-encoded public keys. - */ - this.getPubKeys = function () { - var pubs = []; - - for (var i = 0; i < keys.length; i++) { - pubs.push(Crypto.util.bytesToBase64(keys[i].getPub())); - } - - return pubs; - }; - - /** - * 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 () { - this.addKey(new Bitcoin.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.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; - } - } + // Add a new address + this.generateAddress = function() { + keys.push(masterkey.ckd(keys.length).key) + this.addresses.push(keys[keys.length-1].getBitcoinAddress().toString()) + return this.addresses[this.addresses.length - 1] } - // Remove spent outputs - for (j = 0; j < tx.ins.length; j++) { - var txin = new TransactionIn(tx.ins[j]); - var pubkey = txin.script.simpleInPubKey(); - hash = Crypto.util.bytesToBase64(Bitcoin.Util.sha256ripe160(pubkey)); - for (k = 0; k < this.addressHashes.length; k++) { - if (this.addressHashes[k] === hash) { - for (var l = 0; l < this.unspentOuts.length; l++) { - if (txin.outpoint.hash == this.unspentOuts[l].tx.hash && - txin.outpoint.index == this.unspentOuts[l].index) { - this.unspentOuts.splice(l, 1); + // Processes a transaction object + // If "verified" is true, then we trust the transaction as "final" + this.processTx = function(tx, verified) { + var txhash = conv.bytesToHex(tx.getHash()) + for (var i = 0; i < tx.outs.length; i++) { + if (this.addresses.indexOf(tx.outs[i].address.toString()) >= 0) { + me.outputs[txhash+':'+i] = { + output: txhash+':'+i, + value: tx.outs[i].value, + address: tx.outs[i].address.toString(), + timestamp: new Date().getTime() / 1000, + pending: true + } } - } - break; } - } + for (var i = 0; i < tx.ins.length; i++) { + var op = tx.ins[i].outpoint + var o = me.outputs[op.hash+':'+op.index] + if (o) { + o.spend = txhash+':'+i + o.spendpending = true + o.timestamp = new Date().getTime() / 1000 + } + } + } + // Processes an output from an external source of the form + // { output: txhash:index, value: integer, address: address } + // Excellent compatibility with SX and pybitcointools + this.processOutput = function(o) { + if (!this.outputs[o.output] || this.outputs[o.output].pending) + this.outputs[o.output] = o; } - // 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(Bitcoin.Util.valueToBigInt(txout.value)); + this.processExistingOutputs = function() { + var t = new Date().getTime() / 1000 + for (var o in this.outputs) { + if (o.pending && t > o.timestamp + 1200) + delete this.outputs[o] + if (o.spendpending && t > o.timestamp + 1200) { + o.spendpending = false + o.spend = false + delete o.timestamp + } + } } - return balance; - }; + var peoInterval = setInterval(this.processExistingOutputs, 10000) - 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; + this.getUtxoToPay = function(value) { + var h = [] + for (var out in this.outputs) h.push(this.outputs[out]) + var utxo = h.filter(function(x) { return !x.spend }); + var valuecompare = function(a,b) { return a.value > b.value; } + var high = utxo.filter(function(o) { return o.value >= value; }) + .sort(valuecompare); + if (high.length > 0) return [high[0]]; + utxo.sort(valuecompare); + var totalval = 0; + for (var i = 0; i < utxo.length; i++) { + totalval += utxo[i].value; + if (totalval >= value) return utxo.slice(0,i+1); + } + throw ("Not enough money to send funds including transaction fee. Have: " + + (totalval / 100000000) + ", needed: " + (value / 100000000)); } - if (availableValue.compareTo(txValue) < 0) { - throw new Error('Insufficient funds.'); + this.mkSend = function(to, value, fee) { + var utxo = this.getUtxoToPay(value + fee) + var sum = utxo.reduce(function(t,o) { return t + o.value },0), + remainder = sum - value - fee + if (value < 5430) throw new Error("Amount below dust threshold!") + var unspentOuts = 0; + for (var o in this.outputs) { + if (!this.outputs[o].spend) unspentOuts += 1 + if (unspentOuts >= 5) return + } + var change = this.addresses[this.addresses.length - 1] + var toOut = { address: to, value: value }, + changeOut = { address: change, value: remainder } + halfChangeOut = { address: change, value: Math.floor(remainder/2) }; + + var outs = + remainder < 5430 ? [toOut] + : remainder < 10860 ? [toOut, changeOut] + : unspentOuts == 5 ? [toOut, changeOut] + : [toOut, halfChangeOut, halfChangeOut] + + var tx = new Bitcoin.Transaction({ + ins: utxo.map(function(x) { return x.output }), + outs: outs + }) + this.sign(tx) + return tx } - - var changeValue = availableValue.subtract(txValue); - - var sendTx = new Bitcoin.Transaction(); - - for (i = 0; i < selectedOuts.length; i++) { - sendTx.addInput(selectedOuts[i].tx, selectedOuts[i].index); + this.mkSendToOutputs = function(outputs, changeIndex, fee) { + var value = outputs.reduce(function(t,o) { return t + o.value },0), + utxo = this.getUtxoToPay(value + fee), + sum = utxo.reduce(function(t,p) { return t + o.value },0); + utxo[changeIndex].value += sum - value - fee; + var tx = new Bitcoin.Transaction({ + ins: utxo.map(function(x) { return x.output }), + outs: outputs + }) + this.sign(tx) + return tx } - sendTx.addOutput(address, sendValue); - if (changeValue.compareTo(BigInteger.ZERO) > 0) { - sendTx.addOutput(this.getNextAddress(), changeValue); + this.sign = function(tx) { + tx.ins.map(function(inp,i) { + var inp = inp.outpoint.hash+':'+inp.outpoint.index; + if (me.outputs[inp]) { + var address = me.outputs[inp].address, + ind = me.addresses.indexOf(address); + if (ind >= 0) { + var key = keys[ind] + tx.sign(ind,key) + } + } + }) + return tx; } - var hashType = 1; // SIGHASH_ALL + this.getMasterKey = function() { return masterkey } - 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)); + this.getPrivateKey = function(index) { + if (typeof index == "string") + return keys.filter(function(i,k){ return addresses[i] == index })[0] + else + return keys[index] } - 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; - }; - - return Wallet; -})(); + this.getPrivateKeys = function() { return keys } +}; +module.exports = Wallet; diff --git a/test/address.js b/test/address.js new file mode 100644 index 0000000..2a9fb2c --- /dev/null +++ b/test/address.js @@ -0,0 +1,52 @@ +/* global describe, it */ +var assert = require('assert'); +var Address = require('../src/address.js'); + +describe('Address', function() { + describe('toString', function() { + it('defaults to base58', function() { + var addr = '18fN1QTGWmHWCA9r2dyDH6FbMEyc7XHmQQ'; + assert.equal((new Address(addr)).toString(), addr); + }) + }) + + describe('validate', function() { + it('validates known good addresses', function() { + function validate(addr) { + assert.ok(Address.validate(addr)); + } + + 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'); + }) + + it('does not validate illegal examples', function() { + function invalid(addr) { + assert.ok(!Address.validate(addr)); + } + + invalid(''); + invalid('mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhe'); + + // invalid('1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa', 'testnet'); + // invalid('mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhef', 'prod'); + + // invalid base58 string + invalid('%%@'); + }) + }) +}) diff --git a/test/base58.js b/test/base58.js new file mode 100644 index 0000000..4743450 --- /dev/null +++ b/test/base58.js @@ -0,0 +1,22 @@ +/* global describe, it */ +var assert = require('assert'); +var base58 = require('../').base58; +var conv = require('../').convert; + +describe('base58', function() { + describe('decode', function() { + it('validates known examples', function() { + var enc = '5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'; + var hex = '800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d507a5b8d'; + assert.deepEqual(base58.decode(enc), conv.hexToBytes(hex)); + }) + }) + + describe('encode', function() { + it('handles known examples', function() { + var enc = '5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'; + var hex = '800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d507a5b8d'; + assert.equal(base58.encode(conv.hexToBytes(hex)), enc); + }) + }) +}) diff --git a/test/convert.js b/test/convert.js new file mode 100644 index 0000000..643cde8 --- /dev/null +++ b/test/convert.js @@ -0,0 +1,28 @@ +/* global describe, it */ +var assert = require('assert'); +var convert = require('../src/convert.js'); + +describe('convert', function() { + describe('bytesToHex', function() { + it('handles example 1', function() { + assert.equal(convert.bytesToHex([0, 1, 2, 255]), '000102ff'); + }) + }) + + describe('hexToBytes', function() { + it('handles example 1', function() { + assert.deepEqual(convert.hexToBytes('000102ff'), [0, 1, 2, 255]); + }) + }) + + it('converts from bytes to hex and back', function() { + var bytes = []; + for (var i=0 ; i<256 ; ++i) { + bytes.push(i); + } + + var hex = convert.bytesToHex(bytes); + assert.equal(hex.length, 512); + assert.deepEqual(convert.hexToBytes(hex), bytes); + }) +}) diff --git a/test/ec.js b/test/ec.js new file mode 100644 index 0000000..bc4b979 --- /dev/null +++ b/test/ec.js @@ -0,0 +1,23 @@ +/* global describe, it */ +var assert = require('assert'); +var sec = require('../src/jsbn/sec'); +var ecdsa = require('../').ecdsa; + +var ecparams = sec('secp256k1'); + +describe('ecdsa', function() { + it('handles point ultiplication', 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"); + }) +}) diff --git a/test/eckey.js b/test/eckey.js new file mode 100644 index 0000000..6c53999 --- /dev/null +++ b/test/eckey.js @@ -0,0 +1,43 @@ +/* global describe, it */ +var assert = require('assert'); +var ECKey = require('../src/eckey.js').ECKey; +var convert = require('../src/convert.js'); +var bytesToHex = convert.bytesToHex; +var hexToBytes = convert.hexToBytes; + +describe('ECKey', function() { + describe('constructor (base58 private)', function() { + it('parses case 1', function() { + var priv = '18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725'; + var pub = '0450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b235' + + '22cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6'; + var key = new ECKey(hexToBytes(priv)); + + assert.equal(bytesToHex(key.getPub()['export']('bytes')), pub); + assert.equal(key.compressed, false); + }) + + it('parses case 2', function() { + var priv = '5HwoXVkHoRM8sL2KmNRS217n1g8mPPBomrY7yehCuXC1115WWsh'; + var pub = '044f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0' + + 'f0b704075871aa385b6b1b8ead809ca67454d9683fcf2ba03456d6fe2c4abe2b07f0fbdbb2f1c1'; + var addr = '1MsHWS1BnwMc3tLE8G35UXsS58fKipzB7a'; + var key = new ECKey(priv); + + assert.equal(key.compressed, false); + assert.equal(bytesToHex(key.getPub()['export']('bytes')), pub); + assert.equal(key.getBitcoinAddress().toString(), addr); + }) + + it('parses case 3', function() { + var priv = 'KwntMbt59tTsj8xqpqYqRRWufyjGunvhSyeMo3NTYpFYzZbXJ5Hp'; + var pub = '034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa' + var addr = '1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9'; + var key = new ECKey(priv); + + assert.equal(key.compressed, true); + assert.equal(bytesToHex(key.getPub()['export']('bytes')), pub); + assert.equal(key.getBitcoinAddress().toString(), addr); + }) + }) +}) diff --git a/test/hdwallet.js b/test/hdwallet.js new file mode 100644 index 0000000..974c06e --- /dev/null +++ b/test/hdwallet.js @@ -0,0 +1,217 @@ +/* global describe, it */ +var HDWallet = require('../src/hdwallet.js') +, assert = require('assert') +, convert = require('../src/convert.js') + +var b2h = convert.bytesToHex + +describe('HDWallet', function() { + describe('toBase58', function() { + it('reproduces input', function() { + var input = 'xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5' + , output = HDWallet.fromBase58(input).toBase58(false) + assert.equal(output, input) + + input = 'xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334' + output = HDWallet.fromBase58(input).toBase58(true) + assert.equal(output, input) + }) + + it('fails with priv=true when theres no private key', function() { + var hd = HDWallet.fromBase58('xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon') + try { + hd.toBase58(true) + } catch(e) { + assert(e.message.match(/private key/i)) + return + } + assert.fail() + }) + }) + + describe('ctor', function() { + it('creates from seed', function() { + var seed = 'crazy horse battery staple' + , hd = new HDWallet(seed) + + assert(hd.priv) + assert(hd.pub) + }) + }) + + describe('Test vectors', function() { + it('Test vector 1', function() { + var hd = HDWallet.fromMasterHex('000102030405060708090a0b0c0d0e0f') + + // m + assert.equal(b2h(hd.getIdentifier()), '3442193e1bb70916e914552172cd4e2dbc9df811') + assert.equal(b2h(hd.getFingerprint()), '3442193e') + assert.equal(hd.getBitcoinAddress().toString(), '15mKKb2eos1hWa6tisdPwwDC1a5J1y9nma') + assert.equal(hd.priv.toHex(), 'e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b3501') + assert.equal(hd.priv.toWif(), 'L52XzL2cMkHxqxBXRyEpnPQZGUs3uKiL3R11XbAdHigRzDozKZeW') + assert.equal(hd.pub.toHex(), '0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2') + assert.equal(b2h(hd.chaincode), '873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508') + assert.equal(hd.toHex(false), '0488b21e000000000000000000873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d5080339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2') + assert.equal(hd.toHex(true), '0488ade4000000000000000000873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d50800e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35') + assert.equal(hd.toBase58(false), 'xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8') + assert.equal(hd.toBase58(true), 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi') + + // m/0' + hd = hd.derivePrivate(0) + assert.equal(b2h(hd.getIdentifier()), '5c1bd648ed23aa5fd50ba52b2457c11e9e80a6a7') + assert.equal(b2h(hd.getFingerprint()), '5c1bd648') + assert.equal(hd.getBitcoinAddress().toString(), '19Q2WoS5hSS6T8GjhK8KZLMgmWaq4neXrh') + assert.equal(hd.priv.toHex().slice(0, 64), 'edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea') + assert.equal(hd.priv.toWif(), 'L5BmPijJjrKbiUfG4zbiFKNqkvuJ8usooJmzuD7Z8dkRoTThYnAT') + assert.equal(hd.pub.toHex(), '035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56') + assert.equal(b2h(hd.chaincode), '47fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141') + assert.equal(hd.toHex(false), '0488b21e013442193e8000000047fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56') + assert.equal(hd.toHex(true), '0488ade4013442193e8000000047fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae623614100edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea') + assert.equal(hd.toBase58(false), 'xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw') + assert.equal(hd.toBase58(true), 'xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7') + + // m/0'/1 + hd = hd.derive(1) + assert.equal(b2h(hd.getIdentifier()), 'bef5a2f9a56a94aab12459f72ad9cf8cf19c7bbe') + assert.equal(b2h(hd.getFingerprint()), 'bef5a2f9') + assert.equal(hd.getBitcoinAddress().toString(), '1JQheacLPdM5ySCkrZkV66G2ApAXe1mqLj') + assert.equal(hd.priv.toHex().slice(0, 64), '3c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368') + assert.equal(hd.priv.toWif(), 'KyFAjQ5rgrKvhXvNMtFB5PCSKUYD1yyPEe3xr3T34TZSUHycXtMM') + assert.equal(hd.pub.toHex(), '03501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c') + assert.equal(b2h(hd.chaincode), '2a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c19') + assert.equal(hd.toHex(false), '0488b21e025c1bd648000000012a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c1903501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c') + assert.equal(hd.toHex(true), '0488ade4025c1bd648000000012a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c19003c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368') + assert.equal(hd.toBase58(false), 'xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ') + assert.equal(hd.toBase58(true), 'xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs') + + // m/0'/1/2' + hd = hd.derivePrivate(2) + assert.equal(b2h(hd.getIdentifier()), 'ee7ab90cde56a8c0e2bb086ac49748b8db9dce72') + assert.equal(b2h(hd.getFingerprint()), 'ee7ab90c') + assert.equal(hd.getBitcoinAddress().toString(), '1NjxqbA9aZWnh17q1UW3rB4EPu79wDXj7x') + assert.equal(hd.priv.toHex().slice(0, 64), 'cbce0d719ecf7431d88e6a89fa1483e02e35092af60c042b1df2ff59fa424dca') + assert.equal(hd.priv.toWif(), 'L43t3od1Gh7Lj55Bzjj1xDAgJDcL7YFo2nEcNaMGiyRZS1CidBVU') + assert.equal(hd.pub.toHex(), '0357bfe1e341d01c69fe5654309956cbea516822fba8a601743a012a7896ee8dc2') + assert.equal(b2h(hd.chaincode), '04466b9cc8e161e966409ca52986c584f07e9dc81f735db683c3ff6ec7b1503f') + assert.equal(hd.toHex(false), '0488b21e03bef5a2f98000000204466b9cc8e161e966409ca52986c584f07e9dc81f735db683c3ff6ec7b1503f0357bfe1e341d01c69fe5654309956cbea516822fba8a601743a012a7896ee8dc2') + assert.equal(hd.toHex(true), '0488ade403bef5a2f98000000204466b9cc8e161e966409ca52986c584f07e9dc81f735db683c3ff6ec7b1503f00cbce0d719ecf7431d88e6a89fa1483e02e35092af60c042b1df2ff59fa424dca') + assert.equal(hd.toBase58(false), 'xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5') + assert.equal(hd.toBase58(true), 'xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM') + + // m/0'/1/2'/2 + hd = hd.derive(2) + assert.equal(b2h(hd.getIdentifier()), 'd880d7d893848509a62d8fb74e32148dac68412f') + assert.equal(b2h(hd.getFingerprint()), 'd880d7d8') + assert.equal(hd.getBitcoinAddress().toString(), '1LjmJcdPnDHhNTUgrWyhLGnRDKxQjoxAgt') + assert.equal(hd.priv.toHex().slice(0, 64), '0f479245fb19a38a1954c5c7c0ebab2f9bdfd96a17563ef28a6a4b1a2a764ef4') + assert.equal(hd.priv.toWif(), 'KwjQsVuMjbCP2Zmr3VaFaStav7NvevwjvvkqrWd5Qmh1XVnCteBR') + assert.equal(hd.pub.toHex(), '02e8445082a72f29b75ca48748a914df60622a609cacfce8ed0e35804560741d29') + assert.equal(b2h(hd.chaincode), 'cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd') + assert.equal(hd.toHex(false), '0488b21e04ee7ab90c00000002cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd02e8445082a72f29b75ca48748a914df60622a609cacfce8ed0e35804560741d29') + assert.equal(hd.toHex(true), '0488ade404ee7ab90c00000002cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd000f479245fb19a38a1954c5c7c0ebab2f9bdfd96a17563ef28a6a4b1a2a764ef4') + assert.equal(hd.toBase58(false), 'xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV') + assert.equal(hd.toBase58(true), 'xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334') + + // m/0'/1/2'/2/1000000000 + hd = hd.derive(1000000000) + assert.equal(b2h(hd.getIdentifier()), 'd69aa102255fed74378278c7812701ea641fdf32') + assert.equal(b2h(hd.getFingerprint()), 'd69aa102') + assert.equal(hd.getBitcoinAddress().toString(), '1LZiqrop2HGR4qrH1ULZPyBpU6AUP49Uam') + assert.equal(hd.priv.toHex().slice(0, 64), '471b76e389e528d6de6d816857e012c5455051cad6660850e58372a6c3e6e7c8') + assert.equal(hd.priv.toWif(), 'Kybw8izYevo5xMh1TK7aUr7jHFCxXS1zv8p3oqFz3o2zFbhRXHYs') + assert.equal(hd.pub.toHex(), '022a471424da5e657499d1ff51cb43c47481a03b1e77f951fe64cec9f5a48f7011') + assert.equal(b2h(hd.chaincode), 'c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e') + assert.equal(hd.toHex(false), '0488b21e05d880d7d83b9aca00c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e022a471424da5e657499d1ff51cb43c47481a03b1e77f951fe64cec9f5a48f7011') + assert.equal(hd.toHex(true), '0488ade405d880d7d83b9aca00c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e00471b76e389e528d6de6d816857e012c5455051cad6660850e58372a6c3e6e7c8') + assert.equal(hd.toBase58(false), 'xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy') + assert.equal(hd.toBase58(true), 'xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76') + }) + + it('Test vector 2', function() { + var hd = HDWallet.fromMasterHex('fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542') + + // m + assert.equal(b2h(hd.getIdentifier()), 'bd16bee53961a47d6ad888e29545434a89bdfe95') + assert.equal(b2h(hd.getFingerprint()), 'bd16bee5') + assert.equal(hd.getBitcoinAddress().toString(), '1JEoxevbLLG8cVqeoGKQiAwoWbNYSUyYjg') + assert.equal(hd.priv.toHex().slice(0, 64), '4b03d6fc340455b363f51020ad3ecca4f0850280cf436c70c727923f6db46c3e') + assert.equal(hd.priv.toWif(), 'KyjXhyHF9wTphBkfpxjL8hkDXDUSbE3tKANT94kXSyh6vn6nKaoy') + assert.equal(hd.pub.toHex(), '03cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7') + assert.equal(b2h(hd.chaincode), '60499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd9689') + assert.equal(hd.toHex(false), '0488b21e00000000000000000060499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd968903cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7') + assert.equal(hd.toHex(true), '0488ade400000000000000000060499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd9689004b03d6fc340455b363f51020ad3ecca4f0850280cf436c70c727923f6db46c3e') + assert.equal(hd.toBase58(false), 'xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB') + assert.equal(hd.toBase58(true), 'xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U') + + // m/0 + hd = hd.derive(0) + assert.equal(b2h(hd.getIdentifier()), '5a61ff8eb7aaca3010db97ebda76121610b78096') + assert.equal(b2h(hd.getFingerprint()), '5a61ff8e') + assert.equal(hd.getBitcoinAddress().toString(), '19EuDJdgfRkwCmRzbzVBHZWQG9QNWhftbZ') + assert.equal(hd.priv.toHex().slice(0, 64), 'abe74a98f6c7eabee0428f53798f0ab8aa1bd37873999041703c742f15ac7e1e') + assert.equal(hd.priv.toWif(), 'L2ysLrR6KMSAtx7uPqmYpoTeiRzydXBattRXjXz5GDFPrdfPzKbj') + assert.equal(hd.pub.toHex(), '02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea') + assert.equal(b2h(hd.chaincode), 'f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c') + assert.equal(hd.toHex(false), '0488b21e01bd16bee500000000f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea') + assert.equal(hd.toHex(true), '0488ade401bd16bee500000000f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c00abe74a98f6c7eabee0428f53798f0ab8aa1bd37873999041703c742f15ac7e1e') + assert.equal(hd.toBase58(false), 'xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH') + assert.equal(hd.toBase58(true), 'xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt') + + // m/0/2147483647' + hd = hd.derivePrivate(2147483647) + assert.equal(b2h(hd.getIdentifier()), 'd8ab493736da02f11ed682f88339e720fb0379d1') + assert.equal(b2h(hd.getFingerprint()), 'd8ab4937') + assert.equal(hd.getBitcoinAddress().toString(), '1Lke9bXGhn5VPrBuXgN12uGUphrttUErmk') + assert.equal(hd.priv.toHex().slice(0, 64), '877c779ad9687164e9c2f4f0f4ff0340814392330693ce95a58fe18fd52e6e93') + assert.equal(hd.priv.toWif(), 'L1m5VpbXmMp57P3knskwhoMTLdhAAaXiHvnGLMribbfwzVRpz2Sr') + assert.equal(hd.pub.toHex(), '03c01e7425647bdefa82b12d9bad5e3e6865bee0502694b94ca58b666abc0a5c3b') + assert.equal(b2h(hd.chaincode), 'be17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d9') + assert.equal(hd.toHex(false), '0488b21e025a61ff8effffffffbe17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d903c01e7425647bdefa82b12d9bad5e3e6865bee0502694b94ca58b666abc0a5c3b') + assert.equal(hd.toHex(true), '0488ade4025a61ff8effffffffbe17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d900877c779ad9687164e9c2f4f0f4ff0340814392330693ce95a58fe18fd52e6e93') + assert.equal(hd.toBase58(false), 'xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a') + assert.equal(hd.toBase58(true), 'xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9') + + // m/0/2147483647'/1 + hd = hd.derive(1) + assert.equal(b2h(hd.getIdentifier()), '78412e3a2296a40de124307b6485bd19833e2e34') + assert.equal(b2h(hd.getFingerprint()), '78412e3a') + assert.equal(hd.getBitcoinAddress().toString(), '1BxrAr2pHpeBheusmd6fHDP2tSLAUa3qsW') + assert.equal(hd.priv.toHex().slice(0, 64), '704addf544a06e5ee4bea37098463c23613da32020d604506da8c0518e1da4b7') + assert.equal(hd.priv.toWif(), 'KzyzXnznxSv249b4KuNkBwowaN3akiNeEHy5FWoPCJpStZbEKXN2') + assert.equal(hd.pub.toHex(), '03a7d1d856deb74c508e05031f9895dab54626251b3806e16b4bd12e781a7df5b9') + assert.equal(b2h(hd.chaincode), 'f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb') + assert.equal(hd.toHex(false), '0488b21e03d8ab493700000001f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb03a7d1d856deb74c508e05031f9895dab54626251b3806e16b4bd12e781a7df5b9') + assert.equal(hd.toHex(true), '0488ade403d8ab493700000001f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb00704addf544a06e5ee4bea37098463c23613da32020d604506da8c0518e1da4b7') + assert.equal(hd.toBase58(false), 'xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon') + assert.equal(hd.toBase58(true), 'xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef') + + // m/0/2147483647'/1/2147483646' + hd = hd.derivePrivate(2147483646) + assert.equal(b2h(hd.getIdentifier()), '31a507b815593dfc51ffc7245ae7e5aee304246e') + assert.equal(b2h(hd.getFingerprint()), '31a507b8') + assert.equal(hd.getBitcoinAddress().toString(), '15XVotxCAV7sRx1PSCkQNsGw3W9jT9A94R') + assert.equal(hd.priv.toHex().slice(0, 64), 'f1c7c871a54a804afe328b4c83a1c33b8e5ff48f5087273f04efa83b247d6a2d') + assert.equal(hd.priv.toWif(), 'L5KhaMvPYRW1ZoFmRjUtxxPypQ94m6BcDrPhqArhggdaTbbAFJEF') + assert.equal(hd.pub.toHex(), '02d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0') + assert.equal(b2h(hd.chaincode), '637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e29') + assert.equal(hd.toHex(false), '0488b21e0478412e3afffffffe637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e2902d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0') + assert.equal(hd.toHex(true), '0488ade40478412e3afffffffe637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e2900f1c7c871a54a804afe328b4c83a1c33b8e5ff48f5087273f04efa83b247d6a2d') + assert.equal(hd.toBase58(false), 'xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL') + assert.equal(hd.toBase58(true), 'xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc') + + // Chain m/0/2147483647'/1/2147483646'/2 + hd = hd.derive(2) + assert.equal(b2h(hd.getIdentifier()), '26132fdbe7bf89cbc64cf8dafa3f9f88b8666220') + assert.equal(b2h(hd.getFingerprint()), '26132fdb') + assert.equal(hd.getBitcoinAddress().toString(), '14UKfRV9ZPUp6ZC9PLhqbRtxdihW9em3xt') + assert.equal(hd.priv.toHex().slice(0, 64), 'bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23') + assert.equal(hd.priv.toWif(), 'L3WAYNAZPxx1fr7KCz7GN9nD5qMBnNiqEJNJMU1z9MMaannAt4aK') + assert.equal(hd.pub.toHex(), '024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c') + assert.equal(b2h(hd.chaincode), '9452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271') + assert.equal(hd.toHex(false), '0488b21e0531a507b8000000029452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c') + assert.equal(hd.toHex(true), '0488ade40531a507b8000000029452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed27100bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23') + assert.equal(hd.toBase58(false), 'xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt') + assert.equal(hd.toBase58(true), 'xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j') + }) + }) +}) diff --git a/test/index.html b/test/index.html deleted file mode 100755 index cfa6c67..0000000 --- a/test/index.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - BitcoinJS-lib Test Suite - - - - - - - - - - - - - - - - - - - - - - - - -

BitcoinJS-lib Test Suite

-

-
-

-
    -
    - - diff --git a/test/jsbn.js b/test/jsbn.js new file mode 100644 index 0000000..c2320b3 --- /dev/null +++ b/test/jsbn.js @@ -0,0 +1,28 @@ +/* global describe, it */ +var assert = require('assert'); +var BigInteger = require('../src/jsbn/jsbn.js') +var bytesToHex = require('../src/convert.js').bytesToHex; + +describe('BigInteger', function() { + describe('toByteArraySigned', function() { + it('handles examples', function() { + function hex(num) { + var bytes = BigInteger.valueOf(num).toByteArraySigned(); + var h = bytesToHex(bytes); + return '0x' + h; + } + + 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'); + }) + }) +}) diff --git a/test/message.js b/test/message.js new file mode 100644 index 0000000..553cd13 --- /dev/null +++ b/test/message.js @@ -0,0 +1,34 @@ +/* global describe, it */ +var assert = require('assert'); +var Message = require('../src/message.js'); +var ECKey = require('../src/eckey.js').ECKey; +var hexToBytes = require('../src/convert.js').hexToBytes; + +var priv = '18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725'; +var addr = '16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM'; +var msg = 'foobar'; + +describe('Message', function() { + describe('verify', function() { + it('passes case 1', function() { + var key = new ECKey(hexToBytes(priv)); + assert.equal(key.getBitcoinAddress().toString(), addr); + + var sig = Message.signMessage(key, msg); + assert.ok(Message.verifyMessage(addr, sig, msg)); + + // wrong message + assert.ok(!Message.verifyMessage(addr, sig, 'not foobar')); + + // wrong address + assert.ok(!Message.verifyMessage('1MsHWS1BnwMc3tLE8G35UXsS58fKipzB7a', sig, msg)); + }) + + it('passes case 2', function() { + var priv = '5HwoXVkHoRM8sL2KmNRS217n1g8mPPBomrY7yehCuXC1115WWsh'; + var key = new ECKey(hexToBytes(priv)); + var sig = Message.signMessage(key, msg); + assert.ok(!Message.verifyMessage(addr, sig, msg)); + }) + }) +}) diff --git a/test/misc.js b/test/misc.js new file mode 100644 index 0000000..1ed0b8a --- /dev/null +++ b/test/misc.js @@ -0,0 +1,68 @@ +/* global it */ +var assert = require('assert'); +var bitcoinjs = require('../'); +var sec = require('../src/jsbn/sec'); +var BigInteger = require('../src/jsbn/jsbn.js'); +var Crypto = require('../src/crypto-js/crypto.js'); + +var SecureRandom = require('../src/jsbn/rng'); +var rng = new SecureRandom(); + +var ecparams = sec('secp256k1'); +var ECPointFp = bitcoinjs.ECPointFp; + +it('Keys & Key Management', function () { + var p1 = bitcoinjs.Key().getPub()['export']('bytes'); + assert.equal(p1.length, 65); + + var p1_q = ECPointFp.decodeFrom(ecparams.getCurve(), p1); + assert.ok(p1_q); + assert.ok(p1_q.validate()); + + var p2 = bitcoinjs.convert.hexToBytes( + '0486f356006a38b847bedec1bf47013776925d939d5a35a97a4d1263e550c7f1a' + + 'b5aba44ab74d22892097a0e851addf07ba97e33416df5affaceeb35d5607cd23c'); + + var p2_q = ECPointFp.decodeFrom(ecparams.getCurve(), p2); + assert.ok(p2_q); + assert.ok(p2_q.validate()); +}); + +it('Signing and Verifying', function () { + var s1 = bitcoinjs.Key(); + var sig_a = s1.sign(BigInteger.ZERO); + assert.ok(sig_a, 'Sign null'); + + assert.ok(s1.verify(BigInteger.ZERO, sig_a)); + + var message = new BigInteger(1024, rng).toByteArrayUnsigned(); + var hash = Crypto.SHA256(message, { + asBytes: true + }); + var sig_b = s1.sign(hash); + assert.ok(sig_b, 'Sign random string'); + assert.ok(s1.verify(hash, sig_b)); + + var message2 = bitcoinjs.convert.hexToBytes( + '12dce2c169986b3346827ffb2305cf393984627f5f9722a1b1368e933c8d' + + 'd296653fbe5d7ac031c4962ad0eb1c4298c3b91d244e1116b4a76a130c13' + + '1e7aec7fa70184a71a2e66797052831511b93c6e8d72ae58a1980eaacb66' + + '8a33f50d7cefb96a5dab897b5efcb99cbafb0d777cb83fc9b2115b69c0fa' + + '3d82507b932b84e4'); + + var hash2 = Crypto.SHA256(message2, { + asBytes: true + }); + + var sig_c = bitcoinjs.convert.hexToBytes( + '3044022038d9b8dd5c9fbf330565c1f51d72a59ba869aeb2c2001be959d3' + + '79e861ec71960220a73945f32cf90d03127d2c3410d16cee120fa1a4b4c3' + + 'f273ab082801a95506c4'); + + var s2 = bitcoinjs.convert.hexToBytes( + '045a1594316e433fb91f35ef4874610d22177c3f1a1060f6c1e70a609d51' + + 'b20be5795cd2a5eae0d6b872ba42db95e9afaeea3fbb89e98099575b6828' + + '609a978528'); + + assert.ok(bitcoinjs.ecdsa.verify(hash2, sig_c, s2), 'Verify constant signature'); +}); diff --git a/test/mocha.opts b/test/mocha.opts new file mode 100644 index 0000000..e2bfcc5 --- /dev/null +++ b/test/mocha.opts @@ -0,0 +1 @@ +--ui qunit diff --git a/test/test.js b/test/test.js deleted file mode 100755 index 8d6e21b..0000000 --- a/test/test.js +++ /dev/null @@ -1,112 +0,0 @@ -// -// Testing elliptic curve math -// ----------------------------------------------------------------------------- -module("ec"); - -var ecparams = getSECCurveByName("secp256k1"); -var rng = new SecureRandom(); - -test("Classes", function () { - expect(3); - ok(ECPointFp, "ECPointFp"); - ok(ECFieldElementFp, "ECFieldElementFp"); - ok(ECCurveFp, "ECCurveFp"); -}); - -test("Point multiplication", function () { - expect(5); - - var G = ecparams.getG(); - var n = ecparams.getN(); - - ok(G.multiply(n).isInfinity(), "Gn is infinite"); - - var k = Bitcoin.ECDSA.getBigRandom(n); - var P = G.multiply(k); - ok(!P.isInfinity(), "kG is not infinite"); - ok(P.isOnCurve(), "kG on curve"); - ok(P.multiply(n).isInfinity(), "kGn is infinite"); - - ok(P.validate(), "kG validates as a public key"); -}); - - -// -// Testing ECDSA -// ----------------------------------------------------------------------------- -module("ecdsa"); - -test("Classes", function () { - expect(2); - ok(Bitcoin.ECDSA, "Bitcoin.ECDSA"); - ok(Bitcoin.ECKey, "Bitcoin.ECKey"); -}); - -test("Keys & Key Management", function () { - expect(5); - - var s1 = new Bitcoin.ECKey(); - var p1 = s1.getPub(); - equals(p1.length, 65, "Public key is correct length"); - - var p1_q = ECPointFp.decodeFrom(ecparams.getCurve(), p1); - ok(p1_q, "Decode point from generated bytestring"); - ok(p1_q.validate(), "Is a valid public point"); - - var p2 = Crypto.util.hexToBytes( - "0486f356006a38b847bedec1bf47013776925d939d5a35a97a4d1263e550c7f1a" + - "b5aba44ab74d22892097a0e851addf07ba97e33416df5affaceeb35d5607cd23c" - ); - var p2_q = ECPointFp.decodeFrom(ecparams.getCurve(), p2); - ok(p2_q, "Decode point from constant"); - ok(p2_q.validate(), "Is a valid public point"); -}); - -test("Signing and Verifying", function () { - expect(7); - - var s1 = new Bitcoin.ECKey(); - var sig_a = s1.sign(BigInteger.ZERO); - ok(sig_a, "Sign null"); - equals(sig_a.length, 70, "Signature is correct length"); - ok(s1.verify(BigInteger.ZERO, sig_a)); - - var message = new BigInteger(1024, rng).toByteArrayUnsigned(); - var hash = Crypto.SHA256(message, {asBytes: true}); - var sig_b = s1.sign(hash); - ok(sig_b, "Sign random string"); - equals(sig_b.length, 70, "Signature is correct length"); - ok(s1.verify(hash, sig_b)); - - var message2 = Crypto.util.hexToBytes( - "12dce2c169986b3346827ffb2305cf393984627f5f9722a1b1368e933c8d" + - "d296653fbe5d7ac031c4962ad0eb1c4298c3b91d244e1116b4a76a130c13" + - "1e7aec7fa70184a71a2e66797052831511b93c6e8d72ae58a1980eaacb66" + - "8a33f50d7cefb96a5dab897b5efcb99cbafb0d777cb83fc9b2115b69c0fa" + - "3d82507b932b84e4" - ); - var hash2 = Crypto.SHA256(message2, {asBytes: true}); - var sig_c = Crypto.util.hexToBytes( - "3044022038d9b8dd5c9fbf330565c1f51d72a59ba869aeb2c2001be959d3" + - "79e861ec71960220a73945f32cf90d03127d2c3410d16cee120fa1a4b4c3" + - "f273ab082801a95506c4" - ); - var s2 = Crypto.util.hexToBytes( - "045a1594316e433fb91f35ef4874610d22177c3f1a1060f6c1e70a609d51" + - "b20be5795cd2a5eae0d6b872ba42db95e9afaeea3fbb89e98099575b6828" + - "609a978528" - ); - ok(Bitcoin.ECDSA.verify(hash2, sig_c, s2), "Verify constant signature"); -}); - -// -// Testing Paillier -// ----------------------------------------------------------------------------- -module("paillier"); - -test("Classes", function () { - expect(3); - ok(Bitcoin.Paillier, "Bitcoin.Paillier"); - ok(Bitcoin.Paillier.PublicKey, "Bitcoin.Paillier.PublicKey"); - ok(Bitcoin.Paillier.PrivateKey, "Bitcoin.Paillier.PrivateKey"); -}); diff --git a/vendor/qunit/qunit.css b/vendor/qunit/qunit.css deleted file mode 100755 index c85f36a..0000000 --- a/vendor/qunit/qunit.css +++ /dev/null @@ -1,226 +0,0 @@ -/** - * QUnit - A JavaScript Unit Testing Framework - * - * http://docs.jquery.com/QUnit - * - * Copyright (c) 2011 John Resig, Jörn Zaefferer - * Dual licensed under the MIT (MIT-LICENSE.txt) - * or GPL (GPL-LICENSE.txt) licenses. - */ - -/** Font Family and Sizes */ - -#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { - font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; -} - -#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } -#qunit-tests { font-size: smaller; } - - -/** Resets */ - -#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult { - margin: 0; - padding: 0; -} - - -/** Header */ - -#qunit-header { - padding: 0.5em 0 0.5em 1em; - - color: #8699a4; - background-color: #0d3349; - - font-size: 1.5em; - line-height: 1em; - font-weight: normal; - - border-radius: 15px 15px 0 0; - -moz-border-radius: 15px 15px 0 0; - -webkit-border-top-right-radius: 15px; - -webkit-border-top-left-radius: 15px; -} - -#qunit-header a { - text-decoration: none; - color: #c2ccd1; -} - -#qunit-header a:hover, -#qunit-header a:focus { - color: #fff; -} - -#qunit-banner { - height: 5px; -} - -#qunit-testrunner-toolbar { - padding: 0.5em 0 0.5em 2em; - color: #5E740B; - background-color: #eee; -} - -#qunit-userAgent { - padding: 0.5em 0 0.5em 2.5em; - background-color: #2b81af; - color: #fff; - text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; -} - - -/** Tests: Pass/Fail */ - -#qunit-tests { - list-style-position: inside; -} - -#qunit-tests li { - padding: 0.4em 0.5em 0.4em 2.5em; - border-bottom: 1px solid #fff; - list-style-position: inside; -} - -#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { - display: none; -} - -#qunit-tests li strong { - cursor: pointer; -} - -#qunit-tests li a { - padding: 0.5em; - color: #c2ccd1; - text-decoration: none; -} -#qunit-tests li a:hover, -#qunit-tests li a:focus { - color: #000; -} - -#qunit-tests ol { - margin-top: 0.5em; - padding: 0.5em; - - background-color: #fff; - - border-radius: 15px; - -moz-border-radius: 15px; - -webkit-border-radius: 15px; - - box-shadow: inset 0px 2px 13px #999; - -moz-box-shadow: inset 0px 2px 13px #999; - -webkit-box-shadow: inset 0px 2px 13px #999; -} - -#qunit-tests table { - border-collapse: collapse; - margin-top: .2em; -} - -#qunit-tests th { - text-align: right; - vertical-align: top; - padding: 0 .5em 0 0; -} - -#qunit-tests td { - vertical-align: top; -} - -#qunit-tests pre { - margin: 0; - white-space: pre-wrap; - word-wrap: break-word; -} - -#qunit-tests del { - background-color: #e0f2be; - color: #374e0c; - text-decoration: none; -} - -#qunit-tests ins { - background-color: #ffcaca; - color: #500; - text-decoration: none; -} - -/*** Test Counts */ - -#qunit-tests b.counts { color: black; } -#qunit-tests b.passed { color: #5E740B; } -#qunit-tests b.failed { color: #710909; } - -#qunit-tests li li { - margin: 0.5em; - padding: 0.4em 0.5em 0.4em 0.5em; - background-color: #fff; - border-bottom: none; - list-style-position: inside; -} - -/*** Passing Styles */ - -#qunit-tests li li.pass { - color: #5E740B; - background-color: #fff; - border-left: 26px solid #C6E746; -} - -#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } -#qunit-tests .pass .test-name { color: #366097; } - -#qunit-tests .pass .test-actual, -#qunit-tests .pass .test-expected { color: #999999; } - -#qunit-banner.qunit-pass { background-color: #C6E746; } - -/*** Failing Styles */ - -#qunit-tests li li.fail { - color: #710909; - background-color: #fff; - border-left: 26px solid #EE5757; - white-space: pre; -} - -#qunit-tests > li:last-child { - border-radius: 0 0 15px 15px; - -moz-border-radius: 0 0 15px 15px; - -webkit-border-bottom-right-radius: 15px; - -webkit-border-bottom-left-radius: 15px; -} - -#qunit-tests .fail { color: #000000; background-color: #EE5757; } -#qunit-tests .fail .test-name, -#qunit-tests .fail .module-name { color: #000000; } - -#qunit-tests .fail .test-actual { color: #EE5757; } -#qunit-tests .fail .test-expected { color: green; } - -#qunit-banner.qunit-fail { background-color: #EE5757; } - - -/** Result */ - -#qunit-testresult { - padding: 0.5em 0.5em 0.5em 2.5em; - - color: #2b81af; - background-color: #D2E0E6; - - border-bottom: 1px solid white; -} - -/** Fixture */ - -#qunit-fixture { - position: absolute; - top: -10000px; - left: -10000px; -} diff --git a/vendor/qunit/qunit.js b/vendor/qunit/qunit.js deleted file mode 100755 index a711c82..0000000 --- a/vendor/qunit/qunit.js +++ /dev/null @@ -1,1510 +0,0 @@ -/** - * QUnit - A JavaScript Unit Testing Framework - * - * http://docs.jquery.com/QUnit - * - * Copyright (c) 2011 John Resig, Jörn Zaefferer - * Dual licensed under the MIT (MIT-LICENSE.txt) - * or GPL (GPL-LICENSE.txt) licenses. - */ - -(function(window) { - -var defined = { - setTimeout: typeof window.setTimeout !== "undefined", - sessionStorage: (function() { - try { - return !!sessionStorage.getItem; - } catch(e) { - return false; - } - })() -}; - -var testId = 0; - -var Test = function(name, testName, expected, testEnvironmentArg, async, callback) { - this.name = name; - this.testName = testName; - this.expected = expected; - this.testEnvironmentArg = testEnvironmentArg; - this.async = async; - this.callback = callback; - this.assertions = []; -}; -Test.prototype = { - init: function() { - var tests = id("qunit-tests"); - if (tests) { - var b = document.createElement("strong"); - b.innerHTML = "Running " + this.name; - var li = document.createElement("li"); - li.appendChild( b ); - li.className = "running"; - li.id = this.id = "test-output" + testId++; - tests.appendChild( li ); - } - }, - setup: function() { - if (this.module != config.previousModule) { - if ( config.previousModule ) { - QUnit.moduleDone( { - name: config.previousModule, - failed: config.moduleStats.bad, - passed: config.moduleStats.all - config.moduleStats.bad, - total: config.moduleStats.all - } ); - } - config.previousModule = this.module; - config.moduleStats = { all: 0, bad: 0 }; - QUnit.moduleStart( { - name: this.module - } ); - } - - config.current = this; - this.testEnvironment = extend({ - setup: function() {}, - teardown: function() {} - }, this.moduleTestEnvironment); - if (this.testEnvironmentArg) { - extend(this.testEnvironment, this.testEnvironmentArg); - } - - QUnit.testStart( { - name: this.testName - } ); - - // allow utility functions to access the current test environment - // TODO why?? - QUnit.current_testEnvironment = this.testEnvironment; - - try { - if ( !config.pollution ) { - saveGlobal(); - } - - this.testEnvironment.setup.call(this.testEnvironment); - } catch(e) { - QUnit.ok( false, "Setup failed on " + this.testName + ": " + e.message ); - } - }, - run: function() { - if ( this.async ) { - QUnit.stop(); - } - - if ( config.notrycatch ) { - this.callback.call(this.testEnvironment); - return; - } - try { - this.callback.call(this.testEnvironment); - } catch(e) { - fail("Test " + this.testName + " died, exception and test follows", e, this.callback); - QUnit.ok( false, "Died on test #" + (this.assertions.length + 1) + ": " + e.message + " - " + QUnit.jsDump.parse(e) ); - // else next test will carry the responsibility - saveGlobal(); - - // Restart the tests if they're blocking - if ( config.blocking ) { - start(); - } - } - }, - teardown: function() { - try { - this.testEnvironment.teardown.call(this.testEnvironment); - checkPollution(); - } catch(e) { - QUnit.ok( false, "Teardown failed on " + this.testName + ": " + e.message ); - } - }, - finish: function() { - if ( this.expected && this.expected != this.assertions.length ) { - QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" ); - } - - var good = 0, bad = 0, - tests = id("qunit-tests"); - - config.stats.all += this.assertions.length; - config.moduleStats.all += this.assertions.length; - - if ( tests ) { - var ol = document.createElement("ol"); - - for ( var i = 0; i < this.assertions.length; i++ ) { - var assertion = this.assertions[i]; - - var li = document.createElement("li"); - li.className = assertion.result ? "pass" : "fail"; - li.innerHTML = assertion.message || (assertion.result ? "okay" : "failed"); - ol.appendChild( li ); - - if ( assertion.result ) { - good++; - } else { - bad++; - config.stats.bad++; - config.moduleStats.bad++; - } - } - - // store result when possible - if ( QUnit.config.reorder && defined.sessionStorage ) { - if (bad) { - sessionStorage.setItem("qunit-" + this.module + "-" + this.testName, bad); - } else { - sessionStorage.removeItem("qunit-" + this.module + "-" + this.testName); - } - } - - if (bad == 0) { - ol.style.display = "none"; - } - - var b = document.createElement("strong"); - b.innerHTML = this.name + " (" + bad + ", " + good + ", " + this.assertions.length + ")"; - - var a = document.createElement("a"); - a.innerHTML = "Rerun"; - a.href = QUnit.url({ filter: getText([b]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") }); - - addEvent(b, "click", function() { - var next = b.nextSibling.nextSibling, - display = next.style.display; - next.style.display = display === "none" ? "block" : "none"; - }); - - addEvent(b, "dblclick", function(e) { - var target = e && e.target ? e.target : window.event.srcElement; - if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) { - target = target.parentNode; - } - if ( window.location && target.nodeName.toLowerCase() === "strong" ) { - window.location = QUnit.url({ filter: getText([target]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") }); - } - }); - - var li = id(this.id); - li.className = bad ? "fail" : "pass"; - li.removeChild( li.firstChild ); - li.appendChild( b ); - li.appendChild( a ); - li.appendChild( ol ); - - } else { - for ( var i = 0; i < this.assertions.length; i++ ) { - if ( !this.assertions[i].result ) { - bad++; - config.stats.bad++; - config.moduleStats.bad++; - } - } - } - - try { - QUnit.reset(); - } catch(e) { - fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset); - } - - QUnit.testDone( { - name: this.testName, - failed: bad, - passed: this.assertions.length - bad, - total: this.assertions.length - } ); - }, - - queue: function() { - var test = this; - synchronize(function() { - test.init(); - }); - function run() { - // each of these can by async - synchronize(function() { - test.setup(); - }); - synchronize(function() { - test.run(); - }); - synchronize(function() { - test.teardown(); - }); - synchronize(function() { - test.finish(); - }); - } - // defer when previous test run passed, if storage is available - var bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.module + "-" + this.testName); - if (bad) { - run(); - } else { - synchronize(run); - }; - } - -}; - -var QUnit = { - - // call on start of module test to prepend name to all tests - module: function(name, testEnvironment) { - config.currentModule = name; - config.currentModuleTestEnviroment = testEnvironment; - }, - - asyncTest: function(testName, expected, callback) { - if ( arguments.length === 2 ) { - callback = expected; - expected = 0; - } - - QUnit.test(testName, expected, callback, true); - }, - - test: function(testName, expected, callback, async) { - var name = '' + testName + '', testEnvironmentArg; - - if ( arguments.length === 2 ) { - callback = expected; - expected = null; - } - // is 2nd argument a testEnvironment? - if ( expected && typeof expected === 'object') { - testEnvironmentArg = expected; - expected = null; - } - - if ( config.currentModule ) { - name = '' + config.currentModule + ": " + name; - } - - if ( !validTest(config.currentModule + ": " + testName) ) { - return; - } - - var test = new Test(name, testName, expected, testEnvironmentArg, async, callback); - test.module = config.currentModule; - test.moduleTestEnvironment = config.currentModuleTestEnviroment; - test.queue(); - }, - - /** - * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through. - */ - expect: function(asserts) { - config.current.expected = asserts; - }, - - /** - * Asserts true. - * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" ); - */ - ok: function(a, msg) { - a = !!a; - var details = { - result: a, - message: msg - }; - msg = escapeHtml(msg); - QUnit.log(details); - config.current.assertions.push({ - result: a, - message: msg - }); - }, - - /** - * Checks that the first two arguments are equal, with an optional message. - * Prints out both actual and expected values. - * - * Prefered to ok( actual == expected, message ) - * - * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." ); - * - * @param Object actual - * @param Object expected - * @param String message (optional) - */ - equal: function(actual, expected, message) { - QUnit.push(expected == actual, actual, expected, message); - }, - - notEqual: function(actual, expected, message) { - QUnit.push(expected != actual, actual, expected, message); - }, - - deepEqual: function(actual, expected, message) { - QUnit.push(QUnit.equiv(actual, expected), actual, expected, message); - }, - - notDeepEqual: function(actual, expected, message) { - QUnit.push(!QUnit.equiv(actual, expected), actual, expected, message); - }, - - strictEqual: function(actual, expected, message) { - QUnit.push(expected === actual, actual, expected, message); - }, - - notStrictEqual: function(actual, expected, message) { - QUnit.push(expected !== actual, actual, expected, message); - }, - - raises: function(block, expected, message) { - var actual, ok = false; - - if (typeof expected === 'string') { - message = expected; - expected = null; - } - - try { - block(); - } catch (e) { - actual = e; - } - - if (actual) { - // we don't want to validate thrown error - if (!expected) { - ok = true; - // expected is a regexp - } else if (QUnit.objectType(expected) === "regexp") { - ok = expected.test(actual); - // expected is a constructor - } else if (actual instanceof expected) { - ok = true; - // expected is a validation function which returns true is validation passed - } else if (expected.call({}, actual) === true) { - ok = true; - } - } - - QUnit.ok(ok, message); - }, - - start: function() { - config.semaphore--; - if (config.semaphore > 0) { - // don't start until equal number of stop-calls - return; - } - if (config.semaphore < 0) { - // ignore if start is called more often then stop - config.semaphore = 0; - } - // A slight delay, to avoid any current callbacks - if ( defined.setTimeout ) { - window.setTimeout(function() { - if (config.semaphore > 0) { - return; - } - if ( config.timeout ) { - clearTimeout(config.timeout); - } - - config.blocking = false; - process(); - }, 13); - } else { - config.blocking = false; - process(); - } - }, - - stop: function(timeout) { - config.semaphore++; - config.blocking = true; - - if ( timeout && defined.setTimeout ) { - clearTimeout(config.timeout); - config.timeout = window.setTimeout(function() { - QUnit.ok( false, "Test timed out" ); - QUnit.start(); - }, timeout); - } - } -}; - -// Backwards compatibility, deprecated -QUnit.equals = QUnit.equal; -QUnit.same = QUnit.deepEqual; - -// Maintain internal state -var config = { - // The queue of tests to run - queue: [], - - // block until document ready - blocking: true, - - // when enabled, show only failing tests - // gets persisted through sessionStorage and can be changed in UI via checkbox - hidepassed: false, - - // by default, run previously failed tests first - // very useful in combination with "Hide passed tests" checked - reorder: true, - - // by default, modify document.title when suite is done - altertitle: true, - - urlConfig: ['noglobals', 'notrycatch'] -}; - -// Load paramaters -(function() { - var location = window.location || { search: "", protocol: "file:" }, - params = location.search.slice( 1 ).split( "&" ), - length = params.length, - urlParams = {}, - current; - - if ( params[ 0 ] ) { - for ( var i = 0; i < length; i++ ) { - current = params[ i ].split( "=" ); - current[ 0 ] = decodeURIComponent( current[ 0 ] ); - // allow just a key to turn on a flag, e.g., test.html?noglobals - current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true; - urlParams[ current[ 0 ] ] = current[ 1 ]; - } - } - - QUnit.urlParams = urlParams; - config.filter = urlParams.filter; - - // Figure out if we're running the tests from a server or not - QUnit.isLocal = !!(location.protocol === 'file:'); -})(); - -// Expose the API as global variables, unless an 'exports' -// object exists, in that case we assume we're in CommonJS -if ( typeof exports === "undefined" || typeof require === "undefined" ) { - extend(window, QUnit); - window.QUnit = QUnit; -} else { - extend(exports, QUnit); - exports.QUnit = QUnit; -} - -// define these after exposing globals to keep them in these QUnit namespace only -extend(QUnit, { - config: config, - - // Initialize the configuration options - init: function() { - extend(config, { - stats: { all: 0, bad: 0 }, - moduleStats: { all: 0, bad: 0 }, - started: +new Date, - updateRate: 1000, - blocking: false, - autostart: true, - autorun: false, - filter: "", - queue: [], - semaphore: 0 - }); - - var tests = id( "qunit-tests" ), - banner = id( "qunit-banner" ), - result = id( "qunit-testresult" ); - - if ( tests ) { - tests.innerHTML = ""; - } - - if ( banner ) { - banner.className = ""; - } - - if ( result ) { - result.parentNode.removeChild( result ); - } - - if ( tests ) { - result = document.createElement( "p" ); - result.id = "qunit-testresult"; - result.className = "result"; - tests.parentNode.insertBefore( result, tests ); - result.innerHTML = 'Running...
     '; - } - }, - - /** - * Resets the test setup. Useful for tests that modify the DOM. - * - * If jQuery is available, uses jQuery's html(), otherwise just innerHTML. - */ - reset: function() { - if ( window.jQuery ) { - jQuery( "#qunit-fixture" ).html( config.fixture ); - } else { - var main = id( 'qunit-fixture' ); - if ( main ) { - main.innerHTML = config.fixture; - } - } - }, - - /** - * Trigger an event on an element. - * - * @example triggerEvent( document.body, "click" ); - * - * @param DOMElement elem - * @param String type - */ - triggerEvent: function( elem, type, event ) { - if ( document.createEvent ) { - event = document.createEvent("MouseEvents"); - event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView, - 0, 0, 0, 0, 0, false, false, false, false, 0, null); - elem.dispatchEvent( event ); - - } else if ( elem.fireEvent ) { - elem.fireEvent("on"+type); - } - }, - - // Safe object type checking - is: function( type, obj ) { - return QUnit.objectType( obj ) == type; - }, - - objectType: function( obj ) { - if (typeof obj === "undefined") { - return "undefined"; - - // consider: typeof null === object - } - if (obj === null) { - return "null"; - } - - var type = Object.prototype.toString.call( obj ) - .match(/^\[object\s(.*)\]$/)[1] || ''; - - switch (type) { - case 'Number': - if (isNaN(obj)) { - return "nan"; - } else { - return "number"; - } - case 'String': - case 'Boolean': - case 'Array': - case 'Date': - case 'RegExp': - case 'Function': - return type.toLowerCase(); - } - if (typeof obj === "object") { - return "object"; - } - return undefined; - }, - - push: function(result, actual, expected, message) { - var details = { - result: result, - message: message, - actual: actual, - expected: expected - }; - - message = escapeHtml(message) || (result ? "okay" : "failed"); - message = '' + message + ""; - expected = escapeHtml(QUnit.jsDump.parse(expected)); - actual = escapeHtml(QUnit.jsDump.parse(actual)); - var output = message + ''; - if (actual != expected) { - output += ''; - output += ''; - } - if (!result) { - var source = sourceFromStacktrace(); - if (source) { - details.source = source; - output += ''; - } - } - output += "
    Expected:
    ' + expected + '
    Result:
    ' + actual + '
    Diff:
    ' + QUnit.diff(expected, actual) +'
    Source:
    ' + escapeHtml(source) + '
    "; - - QUnit.log(details); - - config.current.assertions.push({ - result: !!result, - message: output - }); - }, - - url: function( params ) { - params = extend( extend( {}, QUnit.urlParams ), params ); - var querystring = "?", - key; - for ( key in params ) { - querystring += encodeURIComponent( key ) + "=" + - encodeURIComponent( params[ key ] ) + "&"; - } - return window.location.pathname + querystring.slice( 0, -1 ); - }, - - extend: extend, - id: id, - addEvent: addEvent, - - // Logging callbacks; all receive a single argument with the listed properties - // run test/logs.html for any related changes - begin: function() {}, - // done: { failed, passed, total, runtime } - done: function() {}, - // log: { result, actual, expected, message } - log: function() {}, - // testStart: { name } - testStart: function() {}, - // testDone: { name, failed, passed, total } - testDone: function() {}, - // moduleStart: { name } - moduleStart: function() {}, - // moduleDone: { name, failed, passed, total } - moduleDone: function() {} -}); - -if ( typeof document === "undefined" || document.readyState === "complete" ) { - config.autorun = true; -} - -QUnit.load = function() { - QUnit.begin({}); - - // Initialize the config, saving the execution queue - var oldconfig = extend({}, config); - QUnit.init(); - extend(config, oldconfig); - - config.blocking = false; - - var urlConfigHtml = '', len = config.urlConfig.length; - for ( var i = 0, val; i < len, val = config.urlConfig[i]; i++ ) { - config[val] = QUnit.urlParams[val]; - urlConfigHtml += ''; - } - - var userAgent = id("qunit-userAgent"); - if ( userAgent ) { - userAgent.innerHTML = navigator.userAgent; - } - var banner = id("qunit-header"); - if ( banner ) { - banner.innerHTML = ' ' + banner.innerHTML + ' ' + urlConfigHtml; - addEvent( banner, "change", function( event ) { - var params = {}; - params[ event.target.name ] = event.target.checked ? true : undefined; - window.location = QUnit.url( params ); - }); - } - - var toolbar = id("qunit-testrunner-toolbar"); - if ( toolbar ) { - var filter = document.createElement("input"); - filter.type = "checkbox"; - filter.id = "qunit-filter-pass"; - addEvent( filter, "click", function() { - var ol = document.getElementById("qunit-tests"); - if ( filter.checked ) { - ol.className = ol.className + " hidepass"; - } else { - var tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " "; - ol.className = tmp.replace(/ hidepass /, " "); - } - if ( defined.sessionStorage ) { - if (filter.checked) { - sessionStorage.setItem("qunit-filter-passed-tests", "true"); - } else { - sessionStorage.removeItem("qunit-filter-passed-tests"); - } - } - }); - if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem("qunit-filter-passed-tests") ) { - filter.checked = true; - var ol = document.getElementById("qunit-tests"); - ol.className = ol.className + " hidepass"; - } - toolbar.appendChild( filter ); - - var label = document.createElement("label"); - label.setAttribute("for", "qunit-filter-pass"); - label.innerHTML = "Hide passed tests"; - toolbar.appendChild( label ); - } - - var main = id('qunit-fixture'); - if ( main ) { - config.fixture = main.innerHTML; - } - - if (config.autostart) { - QUnit.start(); - } -}; - -addEvent(window, "load", QUnit.load); - -function done() { - config.autorun = true; - - // Log the last module results - if ( config.currentModule ) { - QUnit.moduleDone( { - name: config.currentModule, - failed: config.moduleStats.bad, - passed: config.moduleStats.all - config.moduleStats.bad, - total: config.moduleStats.all - } ); - } - - var banner = id("qunit-banner"), - tests = id("qunit-tests"), - runtime = +new Date - config.started, - passed = config.stats.all - config.stats.bad, - html = [ - 'Tests completed in ', - runtime, - ' milliseconds.
    ', - '', - passed, - ' tests of ', - config.stats.all, - ' passed, ', - config.stats.bad, - ' failed.' - ].join(''); - - if ( banner ) { - banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass"); - } - - if ( tests ) { - id( "qunit-testresult" ).innerHTML = html; - } - - if ( config.altertitle && typeof document !== "undefined" && document.title ) { - // show ✖ for good, ✔ for bad suite result in title - // use escape sequences in case file gets loaded with non-utf-8-charset - document.title = [ - (config.stats.bad ? "\u2716" : "\u2714"), - document.title.replace(/^[\u2714\u2716] /i, "") - ].join(" "); - } - - QUnit.done( { - failed: config.stats.bad, - passed: passed, - total: config.stats.all, - runtime: runtime - } ); -} - -function validTest( name ) { - var filter = config.filter, - run = false; - - if ( !filter ) { - return true; - } - - var not = filter.charAt( 0 ) === "!"; - if ( not ) { - filter = filter.slice( 1 ); - } - - if ( name.indexOf( filter ) !== -1 ) { - return !not; - } - - if ( not ) { - run = true; - } - - return run; -} - -// so far supports only Firefox, Chrome and Opera (buggy) -// could be extended in the future to use something like https://github.com/csnover/TraceKit -function sourceFromStacktrace() { - try { - throw new Error(); - } catch ( e ) { - if (e.stacktrace) { - // Opera - return e.stacktrace.split("\n")[6]; - } else if (e.stack) { - // Firefox, Chrome - return e.stack.split("\n")[4]; - } else if (e.sourceURL) { - // Safari, PhantomJS - // TODO sourceURL points at the 'throw new Error' line above, useless - //return e.sourceURL + ":" + e.line; - } - } -} - -function escapeHtml(s) { - if (!s) { - return ""; - } - s = s + ""; - return s.replace(/[\&"<>\\]/g, function(s) { - switch(s) { - case "&": return "&"; - case "\\": return "\\\\"; - case '"': return '\"'; - case "<": return "<"; - case ">": return ">"; - default: return s; - } - }); -} - -function synchronize( callback ) { - config.queue.push( callback ); - - if ( config.autorun && !config.blocking ) { - process(); - } -} - -function process() { - var start = (new Date()).getTime(); - - while ( config.queue.length && !config.blocking ) { - if ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) { - config.queue.shift()(); - } else { - window.setTimeout( process, 13 ); - break; - } - } - if (!config.blocking && !config.queue.length) { - done(); - } -} - -function saveGlobal() { - config.pollution = []; - - if ( config.noglobals ) { - for ( var key in window ) { - config.pollution.push( key ); - } - } -} - -function checkPollution( name ) { - var old = config.pollution; - saveGlobal(); - - var newGlobals = diff( config.pollution, old ); - if ( newGlobals.length > 0 ) { - ok( false, "Introduced global variable(s): " + newGlobals.join(", ") ); - } - - var deletedGlobals = diff( old, config.pollution ); - if ( deletedGlobals.length > 0 ) { - ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") ); - } -} - -// returns a new Array with the elements that are in a but not in b -function diff( a, b ) { - var result = a.slice(); - for ( var i = 0; i < result.length; i++ ) { - for ( var j = 0; j < b.length; j++ ) { - if ( result[i] === b[j] ) { - result.splice(i, 1); - i--; - break; - } - } - } - return result; -} - -function fail(message, exception, callback) { - if ( typeof console !== "undefined" && console.error && console.warn ) { - console.error(message); - console.error(exception); - console.warn(callback.toString()); - - } else if ( window.opera && opera.postError ) { - opera.postError(message, exception, callback.toString); - } -} - -function extend(a, b) { - for ( var prop in b ) { - if ( b[prop] === undefined ) { - delete a[prop]; - } else { - a[prop] = b[prop]; - } - } - - return a; -} - -function addEvent(elem, type, fn) { - if ( elem.addEventListener ) { - elem.addEventListener( type, fn, false ); - } else if ( elem.attachEvent ) { - elem.attachEvent( "on" + type, fn ); - } else { - fn(); - } -} - -function id(name) { - return !!(typeof document !== "undefined" && document && document.getElementById) && - document.getElementById( name ); -} - -// Test for equality any JavaScript type. -// Discussions and reference: http://philrathe.com/articles/equiv -// Test suites: http://philrathe.com/tests/equiv -// Author: Philippe Rathé -QUnit.equiv = function () { - - var innerEquiv; // the real equiv function - var callers = []; // stack to decide between skip/abort functions - var parents = []; // stack to avoiding loops from circular referencing - - // Call the o related callback with the given arguments. - function bindCallbacks(o, callbacks, args) { - var prop = QUnit.objectType(o); - if (prop) { - if (QUnit.objectType(callbacks[prop]) === "function") { - return callbacks[prop].apply(callbacks, args); - } else { - return callbacks[prop]; // or undefined - } - } - } - - var callbacks = function () { - - // for string, boolean, number and null - function useStrictEquality(b, a) { - if (b instanceof a.constructor || a instanceof b.constructor) { - // to catch short annotaion VS 'new' annotation of a - // declaration - // e.g. var i = 1; - // var j = new Number(1); - return a == b; - } else { - return a === b; - } - } - - return { - "string" : useStrictEquality, - "boolean" : useStrictEquality, - "number" : useStrictEquality, - "null" : useStrictEquality, - "undefined" : useStrictEquality, - - "nan" : function(b) { - return isNaN(b); - }, - - "date" : function(b, a) { - return QUnit.objectType(b) === "date" - && a.valueOf() === b.valueOf(); - }, - - "regexp" : function(b, a) { - return QUnit.objectType(b) === "regexp" - && a.source === b.source && // the regex itself - a.global === b.global && // and its modifers - // (gmi) ... - a.ignoreCase === b.ignoreCase - && a.multiline === b.multiline; - }, - - // - skip when the property is a method of an instance (OOP) - // - abort otherwise, - // initial === would have catch identical references anyway - "function" : function() { - var caller = callers[callers.length - 1]; - return caller !== Object && typeof caller !== "undefined"; - }, - - "array" : function(b, a) { - var i, j, loop; - var len; - - // b could be an object literal here - if (!(QUnit.objectType(b) === "array")) { - return false; - } - - len = a.length; - if (len !== b.length) { // safe and faster - return false; - } - - // track reference to avoid circular references - parents.push(a); - for (i = 0; i < len; i++) { - loop = false; - for (j = 0; j < parents.length; j++) { - if (parents[j] === a[i]) { - loop = true;// dont rewalk array - } - } - if (!loop && !innerEquiv(a[i], b[i])) { - parents.pop(); - return false; - } - } - parents.pop(); - return true; - }, - - "object" : function(b, a) { - var i, j, loop; - var eq = true; // unless we can proove it - var aProperties = [], bProperties = []; // collection of - // strings - - // comparing constructors is more strict than using - // instanceof - if (a.constructor !== b.constructor) { - return false; - } - - // stack constructor before traversing properties - callers.push(a.constructor); - // track reference to avoid circular references - parents.push(a); - - for (i in a) { // be strict: don't ensures hasOwnProperty - // and go deep - loop = false; - for (j = 0; j < parents.length; j++) { - if (parents[j] === a[i]) - loop = true; // don't go down the same path - // twice - } - aProperties.push(i); // collect a's properties - - if (!loop && !innerEquiv(a[i], b[i])) { - eq = false; - break; - } - } - - callers.pop(); // unstack, we are done - parents.pop(); - - for (i in b) { - bProperties.push(i); // collect b's properties - } - - // Ensures identical properties name - return eq - && innerEquiv(aProperties.sort(), bProperties - .sort()); - } - }; - }(); - - innerEquiv = function() { // can take multiple arguments - var args = Array.prototype.slice.apply(arguments); - if (args.length < 2) { - return true; // end transition - } - - return (function(a, b) { - if (a === b) { - return true; // catch the most you can - } else if (a === null || b === null || typeof a === "undefined" - || typeof b === "undefined" - || QUnit.objectType(a) !== QUnit.objectType(b)) { - return false; // don't lose time with error prone cases - } else { - return bindCallbacks(a, callbacks, [ b, a ]); - } - - // apply transition with (1..n) arguments - })(args[0], args[1]) - && arguments.callee.apply(this, args.splice(1, - args.length - 1)); - }; - - return innerEquiv; - -}(); - -/** - * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | - * http://flesler.blogspot.com Licensed under BSD - * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008 - * - * @projectDescription Advanced and extensible data dumping for Javascript. - * @version 1.0.0 - * @author Ariel Flesler - * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html} - */ -QUnit.jsDump = (function() { - function quote( str ) { - return '"' + str.toString().replace(/"/g, '\\"') + '"'; - }; - function literal( o ) { - return o + ''; - }; - function join( pre, arr, post ) { - var s = jsDump.separator(), - base = jsDump.indent(), - inner = jsDump.indent(1); - if ( arr.join ) - arr = arr.join( ',' + s + inner ); - if ( !arr ) - return pre + post; - return [ pre, inner + arr, base + post ].join(s); - }; - function array( arr, stack ) { - var i = arr.length, ret = Array(i); - this.up(); - while ( i-- ) - ret[i] = this.parse( arr[i] , undefined , stack); - this.down(); - return join( '[', ret, ']' ); - }; - - var reName = /^function (\w+)/; - - var jsDump = { - parse:function( obj, type, stack ) { //type is used mostly internally, you can fix a (custom)type in advance - stack = stack || [ ]; - var parser = this.parsers[ type || this.typeOf(obj) ]; - type = typeof parser; - var inStack = inArray(obj, stack); - if (inStack != -1) { - return 'recursion('+(inStack - stack.length)+')'; - } - //else - if (type == 'function') { - stack.push(obj); - var res = parser.call( this, obj, stack ); - stack.pop(); - return res; - } - // else - return (type == 'string') ? parser : this.parsers.error; - }, - typeOf:function( obj ) { - var type; - if ( obj === null ) { - type = "null"; - } else if (typeof obj === "undefined") { - type = "undefined"; - } else if (QUnit.is("RegExp", obj)) { - type = "regexp"; - } else if (QUnit.is("Date", obj)) { - type = "date"; - } else if (QUnit.is("Function", obj)) { - type = "function"; - } else if (typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined") { - type = "window"; - } else if (obj.nodeType === 9) { - type = "document"; - } else if (obj.nodeType) { - type = "node"; - } else if (typeof obj === "object" && typeof obj.length === "number" && obj.length >= 0) { - type = "array"; - } else { - type = typeof obj; - } - return type; - }, - separator:function() { - return this.multiline ? this.HTML ? '
    ' : '\n' : this.HTML ? ' ' : ' '; - }, - indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing - if ( !this.multiline ) - return ''; - var chr = this.indentChar; - if ( this.HTML ) - chr = chr.replace(/\t/g,' ').replace(/ /g,' '); - return Array( this._depth_ + (extra||0) ).join(chr); - }, - up:function( a ) { - this._depth_ += a || 1; - }, - down:function( a ) { - this._depth_ -= a || 1; - }, - setParser:function( name, parser ) { - this.parsers[name] = parser; - }, - // The next 3 are exposed so you can use them - quote:quote, - literal:literal, - join:join, - // - _depth_: 1, - // This is the list of parsers, to modify them, use jsDump.setParser - parsers:{ - window: '[Window]', - document: '[Document]', - error:'[ERROR]', //when no parser is found, shouldn't happen - unknown: '[Unknown]', - 'null':'null', - 'undefined':'undefined', - 'function':function( fn ) { - var ret = 'function', - name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE - if ( name ) - ret += ' ' + name; - ret += '('; - - ret = [ ret, QUnit.jsDump.parse( fn, 'functionArgs' ), '){'].join(''); - return join( ret, QUnit.jsDump.parse(fn,'functionCode'), '}' ); - }, - array: array, - nodelist: array, - arguments: array, - object:function( map, stack ) { - var ret = [ ]; - QUnit.jsDump.up(); - for ( var key in map ) { - var val = map[key]; - ret.push( QUnit.jsDump.parse(key,'key') + ': ' + QUnit.jsDump.parse(val, undefined, stack)); - } - QUnit.jsDump.down(); - return join( '{', ret, '}' ); - }, - node:function( node ) { - var open = QUnit.jsDump.HTML ? '<' : '<', - close = QUnit.jsDump.HTML ? '>' : '>'; - - var tag = node.nodeName.toLowerCase(), - ret = open + tag; - - for ( var a in QUnit.jsDump.DOMAttrs ) { - var val = node[QUnit.jsDump.DOMAttrs[a]]; - if ( val ) - ret += ' ' + a + '=' + QUnit.jsDump.parse( val, 'attribute' ); - } - return ret + close + open + '/' + tag + close; - }, - functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function - var l = fn.length; - if ( !l ) return ''; - - var args = Array(l); - while ( l-- ) - args[l] = String.fromCharCode(97+l);//97 is 'a' - return ' ' + args.join(', ') + ' '; - }, - key:quote, //object calls it internally, the key part of an item in a map - functionCode:'[code]', //function calls it internally, it's the content of the function - attribute:quote, //node calls it internally, it's an html attribute value - string:quote, - date:quote, - regexp:literal, //regex - number:literal, - 'boolean':literal - }, - DOMAttrs:{//attributes to dump from nodes, name=>realName - id:'id', - name:'name', - 'class':'className' - }, - HTML:false,//if true, entities are escaped ( <, >, \t, space and \n ) - indentChar:' ',//indentation unit - multiline:true //if true, items in a collection, are separated by a \n, else just a space. - }; - - return jsDump; -})(); - -// from Sizzle.js -function getText( elems ) { - var ret = "", elem; - - for ( var i = 0; elems[i]; i++ ) { - elem = elems[i]; - - // Get the text from text nodes and CDATA nodes - if ( elem.nodeType === 3 || elem.nodeType === 4 ) { - ret += elem.nodeValue; - - // Traverse everything else, except comment nodes - } else if ( elem.nodeType !== 8 ) { - ret += getText( elem.childNodes ); - } - } - - return ret; -}; - -//from jquery.js -function inArray( elem, array ) { - if ( array.indexOf ) { - return array.indexOf( elem ); - } - - for ( var i = 0, length = array.length; i < length; i++ ) { - if ( array[ i ] === elem ) { - return i; - } - } - - return -1; -} - -/* - * Javascript Diff Algorithm - * By John Resig (http://ejohn.org/) - * Modified by Chu Alan "sprite" - * - * Released under the MIT license. - * - * More Info: - * http://ejohn.org/projects/javascript-diff-algorithm/ - * - * Usage: QUnit.diff(expected, actual) - * - * QUnit.diff("the quick brown fox jumped over", "the quick fox jumps over") == "the quick brown fox jumped jumps over" - */ -QUnit.diff = (function() { - function diff(o, n) { - var ns = {}; - var os = {}; - - for (var i = 0; i < n.length; i++) { - if (ns[n[i]] == null) - ns[n[i]] = { - rows: [], - o: null - }; - ns[n[i]].rows.push(i); - } - - for (var i = 0; i < o.length; i++) { - if (os[o[i]] == null) - os[o[i]] = { - rows: [], - n: null - }; - os[o[i]].rows.push(i); - } - - for (var i in ns) { - if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) { - n[ns[i].rows[0]] = { - text: n[ns[i].rows[0]], - row: os[i].rows[0] - }; - o[os[i].rows[0]] = { - text: o[os[i].rows[0]], - row: ns[i].rows[0] - }; - } - } - - for (var i = 0; i < n.length - 1; i++) { - if (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null && - n[i + 1] == o[n[i].row + 1]) { - n[i + 1] = { - text: n[i + 1], - row: n[i].row + 1 - }; - o[n[i].row + 1] = { - text: o[n[i].row + 1], - row: i + 1 - }; - } - } - - for (var i = n.length - 1; i > 0; i--) { - if (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null && - n[i - 1] == o[n[i].row - 1]) { - n[i - 1] = { - text: n[i - 1], - row: n[i].row - 1 - }; - o[n[i].row - 1] = { - text: o[n[i].row - 1], - row: i - 1 - }; - } - } - - return { - o: o, - n: n - }; - } - - return function(o, n) { - o = o.replace(/\s+$/, ''); - n = n.replace(/\s+$/, ''); - var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/)); - - var str = ""; - - var oSpace = o.match(/\s+/g); - if (oSpace == null) { - oSpace = [" "]; - } - else { - oSpace.push(" "); - } - var nSpace = n.match(/\s+/g); - if (nSpace == null) { - nSpace = [" "]; - } - else { - nSpace.push(" "); - } - - if (out.n.length == 0) { - for (var i = 0; i < out.o.length; i++) { - str += '' + out.o[i] + oSpace[i] + ""; - } - } - else { - if (out.n[0].text == null) { - for (n = 0; n < out.o.length && out.o[n].text == null; n++) { - str += '' + out.o[n] + oSpace[n] + ""; - } - } - - for (var i = 0; i < out.n.length; i++) { - if (out.n[i].text == null) { - str += '' + out.n[i] + nSpace[i] + ""; - } - else { - var pre = ""; - - for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) { - pre += '' + out.o[n] + oSpace[n] + ""; - } - str += " " + out.n[i].text + nSpace[i] + pre; - } - } - } - - return str; - }; -})(); - -})(this);