From 1a7fc9d063f864058809d06ef4542af40be3558f Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Wed, 11 Jan 2012 02:40:45 +0100 Subject: [PATCH] Fixed indents. --- src/address.js | 54 ++-- src/base58.js | 108 ++++---- src/bitcoin.js | 200 +++++++-------- src/ecdsa.js | 350 ++++++++++++------------- src/eckey.js | 104 ++++---- src/exit/client.js | 106 ++++---- src/opcode.js | 274 ++++++++++---------- src/paillier.js | 2 +- src/script.js | 362 +++++++++++++------------- src/transaction.js | 626 ++++++++++++++++++++++----------------------- src/txdb.js | 60 ++--- src/util.js | 154 +++++------ src/wallet.js | 438 +++++++++++++++---------------- 13 files changed, 1419 insertions(+), 1419 deletions(-) diff --git a/src/address.js b/src/address.js index 8e99694..0941f58 100644 --- a/src/address.js +++ b/src/address.js @@ -1,49 +1,49 @@ Bitcoin.Address = function (bytes) { - if ("string" == typeof bytes) { - bytes = Bitcoin.Address.decodeString(bytes); - } - this.hash = bytes; + if ("string" == typeof bytes) { + bytes = Bitcoin.Address.decodeString(bytes); + } + this.hash = bytes; - this.version = 0x00; + this.version = 0x00; }; Bitcoin.Address.prototype.toString = function () { - // Get a copy of the hash - var hash = this.hash.slice(0); + // Get a copy of the hash + var hash = this.hash.slice(0); - // Version - hash.unshift(this.version); + // Version + hash.unshift(this.version); - var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true}); + var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true}); - var bytes = hash.concat(checksum.slice(0,4)); + var bytes = hash.concat(checksum.slice(0,4)); - return Bitcoin.Base58.encode(bytes); + return Bitcoin.Base58.encode(bytes); }; Bitcoin.Address.prototype.getHashBase64 = function () { - return Crypto.util.bytesToBase64(this.hash); + return Crypto.util.bytesToBase64(this.hash); }; Bitcoin.Address.decodeString = function (string) { - var bytes = Bitcoin.Base58.decode(string); + var bytes = Bitcoin.Base58.decode(string); - var hash = bytes.slice(0, 21); + var hash = bytes.slice(0, 21); - var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true}); + 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!"; - } + 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(); + var version = hash.shift(); - if (version != 0) { - throw "Version "+version+" not supported!"; - } + if (version != 0) { + throw "Version "+version+" not supported!"; + } - return hash; + return hash; }; diff --git a/src/base58.js b/src/base58.js index f0c9dfc..b028623 100644 --- a/src/base58.js +++ b/src/base58.js @@ -1,66 +1,66 @@ (function (Bitcoin) { - Bitcoin.Base58 = { - alphabet: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz", - base: BigInteger.valueOf(58), + Bitcoin.Base58 = { + alphabet: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz", + 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 = []; + /** + * 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 = []; - 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()]); + 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()]); - // Convert leading zeros too. - for (var i = 0; i < input.length; i++) { - if (input[i] == 0x00) { - chars.unshift(B58.alphabet[0]); - } else break; - } + // Convert leading zeros too. + for (var i = 0; i < input.length; i++) { + if (input[i] == 0x00) { + chars.unshift(B58.alphabet[0]); + } else break; + } - s = chars.join(''); - return s; - }, + s = chars.join(''); + return s; + }, - /** - * 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) { - bi = BigInteger.valueOf(0); - var leadingZerosNum = 0; - for (var i = input.length - 1; i >= 0; i--) { - var alphaIndex = B58.alphabet.indexOf(input[i]); - bi = bi.add(BigInteger.valueOf(alphaIndex) - .multiply(B58.base.pow(input.length - 1 -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) { + bi = BigInteger.valueOf(0); + var leadingZerosNum = 0; + for (var i = input.length - 1; i >= 0; i--) { + var alphaIndex = B58.alphabet.indexOf(input[i]); + bi = bi.add(BigInteger.valueOf(alphaIndex) + .multiply(B58.base.pow(input.length - 1 -i))); - // This counts leading zero bytes - if (input[i] == "1") leadingZerosNum++; - else leadingZerosNum = 0; - } - var bytes = bi.toByteArrayUnsigned(); + // 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); + // Add leading zeros + while (leadingZerosNum-- > 0) bytes.unshift(0); - return bytes; - } - }; + return bytes; + } + }; var B58 = Bitcoin.Base58; })( diff --git a/src/bitcoin.js b/src/bitcoin.js index cefb268..e14d36f 100755 --- a/src/bitcoin.js +++ b/src/bitcoin.js @@ -11,47 +11,47 @@ /* 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}; + // 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 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; @@ -63,24 +63,24 @@ function hashTransactionForSignature(scriptCode, tx, inIndex, hashType) { - // TODO: We need to actually deep copy here - var txTmp = tx; + // TODO: We need to actually deep copy here + var txTmp = tx; - // In case concatenating two scripts ends up with two codeseparators, + // 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; - }); - + 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[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 = []; + txTmp.outs = []; // Let the others update at will for (var i = 0; i < txTmp.ins.length; i++) @@ -95,81 +95,81 @@ txTmp.ins = [txTmp.ins[inIndex]]; } - var buffer = serializeTransaction(txTmp); - - buffer.concat(Crypto.util.wordsToBytes([parseInt(hashType)])); + var buffer = serializeTransaction(txTmp); + + buffer.concat(Crypto.util.wordsToBytes([parseInt(hashType)])); console.log(buffer); - - return Crypto.SHA256(Crypto.SHA256(buffer, {asBytes: true}), {asBytes: true}); + + return Crypto.SHA256(Crypto.SHA256(buffer, {asBytes: true}), {asBytes: true}); }; function verifyTransactionSignature(tx) { - var hash = hashTransactionForSignature([], tx, 0, 0); - return Crypto.util.bytesToHex(hash); + 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])); + // 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])); + // 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==" - }] + "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)); + 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(key.verify(message, out)); - //console.log(Bitcoin.ECDSA.verify(message, Crypto.util.hexToBytes("3046022100dffbc26774fc841bbe1c1362fd643609c6e42dcb274763476d87af2c0597e89e022100c59e3c13b96b316cae9fa0ab0260612c7a133a6fe2b3445b6bf80b3123bf274d"), Crypto.util.hexToBytes("0401de173aa944eacf7e44e5073baca93fb34fe4b7897a1c82c92dfdc8a1f75ef58cd1b06e8052096980cb6e1ad6d3df143c34b3d7394bae2782a4df570554c2fb"))); + //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"))); + //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/ecdsa.js b/src/ecdsa.js index 1a2edcd..e986a48 100644 --- a/src/ecdsa.js +++ b/src/ecdsa.js @@ -1,104 +1,104 @@ function integerToBytes(i, len) { - var bytes = i.toByteArrayUnsigned(); + var bytes = i.toByteArrayUnsigned(); - if (len < bytes.length) { - bytes = bytes.slice(bytes.length-len); - } else while (len > bytes.length) { - bytes.unshift(0); - } + if (len < bytes.length) { + bytes = bytes.slice(bytes.length-len); + } else while (len > bytes.length) { + bytes.unshift(0); + } - return bytes; + return bytes; }; ECFieldElementFp.prototype.getByteLength = function () { - return Math.floor((this.toBigInteger().bitLength() + 7) / 8); + return Math.floor((this.toBigInteger().bitLength() + 7) / 8); }; ECPointFp.prototype.getEncoded = function (compressed) { - var x = this.getX().toBigInteger(); - var y = this.getY().toBigInteger(); + 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.testBit(0)) { - enc.unshift(0x02); - } else { - enc.unshift(0x03); - } + var enc = integerToBytes(x, 32); + + if (compressed) { + if (y.testBit(0)) { + enc.unshift(0x02); + } else { + enc.unshift(0x03); + } // TODO: Implement - } else { - enc.unshift(0x04); - enc = enc.concat(integerToBytes(y, 32)); - } - return enc; + } else { + enc.unshift(0x04); + enc = enc.concat(integerToBytes(y, 32)); + } + return enc; }; ECPointFp.decodeFrom = function (curve, enc) { - var type = enc[0]; + 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); + // 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); + // 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); + // 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)); + // 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); + 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(); - } + 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 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); + 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); + return new ECPointFp(this.curve, x3, y3); }; ECPointFp.prototype.multiply2D = function (k) { - if(this.isInfinity()) return this; + if(this.isInfinity()) return this; if(k.signum() == 0) return this.curve.getInfinity(); var e = k; @@ -109,14 +109,14 @@ ECPointFp.prototype.multiply2D = function (k) { var i; for (i = h.bitLength() - 2; i > 0; --i) { - R = R.twice(); + R = R.twice(); - var hBit = h.testBit(i); - var eBit = e.testBit(i); + var hBit = h.testBit(i); + var eBit = e.testBit(i); - if (hBit != eBit) { - R = R.add2D(hBit ? this : neg); - } + if (hBit != eBit) { + R = R.add2D(hBit ? this : neg); + } } return R; @@ -169,141 +169,141 @@ ECPointFp.prototype.validate = function () { }; function dmp(v) { - if (!(v instanceof BigInteger)) v = v.toBigInteger(); - return Crypto.util.bytesToHex(v.toByteArrayUnsigned()); + if (!(v instanceof BigInteger)) v = v.toBigInteger(); + return Crypto.util.bytesToHex(v.toByteArrayUnsigned()); }; Bitcoin.ECDSA = (function () { - var ecparams = getSECCurveByName("secp256k1"); - var rng = new SecureRandom(); - - function implShamirsTrick(P, k, Q, l) - { - var m = Math.max(k.bitLength(), l.bitLength()); - var Z = P.add2D(Q); - var R = P.curve.getInfinity(); - - for (var i = m - 1; i >= 0; --i) { - R = R.twice2D(); - - R.z = BigInteger.ONE; - - if (k.testBit(i)) { - if (l.testBit(i)) { - R = R.add2D(Z); - } else { - R = R.add2D(P); - } - } else { - if (l.testBit(i)) { - R = R.add2D(Q); - } - } - } - - return R; - }; - - 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); - - 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); + var ecparams = getSECCurveByName("secp256k1"); + var rng = new SecureRandom(); + + function implShamirsTrick(P, k, Q, l) + { + var m = Math.max(k.bitLength(), l.bitLength()); + var Z = P.add2D(Q); + var R = P.curve.getInfinity(); + + for (var i = m - 1; i >= 0; --i) { + R = R.twice2D(); + + R.z = BigInteger.ONE; + + if (k.testBit(i)) { + if (l.testBit(i)) { + R = R.add2D(Z); + } else { + R = R.add2D(P); + } + } else { + if (l.testBit(i)) { + R = R.add2D(Q); + } + } + } + + return R; + }; + + 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); + + 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); - }, + }, serializeSig: function (r, s) { - var rBa = r.toByteArrayUnsigned(); - var sBa = s.toByteArrayUnsigned(); + var rBa = r.toByteArrayUnsigned(); + var sBa = s.toByteArrayUnsigned(); - var sequence = []; - sequence.push(0x02); // INTEGER - sequence.push(rBa.length); - sequence = sequence.concat(rBa); + 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.push(0x02); // INTEGER + sequence.push(sBa.length); + sequence = sequence.concat(sBa); - sequence.unshift(sequence.length); - sequence.unshift(0x30) // SEQUENCE + sequence.unshift(sequence.length); + sequence.unshift(0x30) // SEQUENCE - return sequence; + return sequence; }, - verify: function (hash, sig, pubkey) { + verify: function (hash, sig, pubkey) { var obj = ECDSA.parseSig(sig); var r = obj.r; var s = obj.s; - var n = ecparams.getN(); - var e = BigInteger.fromByteArrayUnsigned(hash); + var n = ecparams.getN(); + var e = BigInteger.fromByteArrayUnsigned(hash); - if (r.compareTo(BigInteger.ONE) < 0 || - r.compareTo(n) >= 0) - return false; + if (r.compareTo(BigInteger.ONE) < 0 || + r.compareTo(n) >= 0) + return false; - if (s.compareTo(BigInteger.ONE) < 0 || - s.compareTo(n) >= 0) - return false; + if (s.compareTo(BigInteger.ONE) < 0 || + s.compareTo(n) >= 0) + return false; - var c = s.modInverse(n); + var c = s.modInverse(n); - var u1 = e.multiply(c).mod(n); - var u2 = r.multiply(c).mod(n); + var u1 = e.multiply(c).mod(n); + var u2 = r.multiply(c).mod(n); - var G = ecparams.getG(); - var Q = ECPointFp.decodeFrom(ecparams.getCurve(), pubkey); + var G = ecparams.getG(); + var Q = ECPointFp.decodeFrom(ecparams.getCurve(), pubkey); - var point = implShamirsTrick(G, u1, Q, u2); + var point = implShamirsTrick(G, u1, Q, u2); - var v = point.x.toBigInteger().mod(n); + var v = point.x.toBigInteger().mod(n); - return v.equals(r); - }, + return v.equals(r); + }, parseSig: function (sig) { - var cursor; - if (sig[0] != 0x30) - throw new Error("Signature not a valid DERSequence"); + 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; + 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 (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]; + cursor += 2+sig[cursor+1]; - //if (cursor != sig.length) - // throw new Error("Extra bytes in signature"); + //if (cursor != sig.length) + // throw new Error("Extra bytes in signature"); - var r = BigInteger.fromByteArrayUnsigned(rBa); - var s = BigInteger.fromByteArrayUnsigned(sBa); + var r = BigInteger.fromByteArrayUnsigned(rBa); + var s = BigInteger.fromByteArrayUnsigned(sBa); return {r: r, s: s}; } - }; + }; - return ECDSA; + return ECDSA; })(); diff --git a/src/eckey.js b/src/eckey.js index 10c1494..d287b51 100644 --- a/src/eckey.js +++ b/src/eckey.js @@ -1,62 +1,62 @@ Bitcoin.ECKey = (function () { - var ECDSA = Bitcoin.ECDSA; - var ecparams = getSECCurveByName("secp256k1"); - var rng = new SecureRandom(); - - var ECKey = function (input) { - if (!input) { - // Generate new key - var n = ecparams.getN(); - this.priv = ECDSA.getBigRandom(n); - } else if (input instanceof BigInteger) { - // Input is a private key value - this.priv = input; - } else if (Bitcoin.Util.isArray(input)) { - // Prepend zero byte to prevent interpretation as negative integer - this.priv = BigInteger.fromByteArrayUnsigned(input); - } else if ("string" == typeof input) { - // Prepend zero byte to prevent interpretation as negative integer - this.priv = BigInteger.fromByteArrayUnsigned(Crypto.util.base64ToBytes(input)); - } - }; - - ECKey.prototype.getPub = function () { - if (this.pub) return this.pub; - - return this.pub = ecparams.getG().multiply(this.priv).getEncoded(); - }; - - ECKey.prototype.getPubKeyHash = function () { - if (this.pubKeyHash) return this.pubKeyHash; - - return this.pubKeyHash = Bitcoin.Util.sha256ripe160(this.getPub()); - }; - - ECKey.prototype.getBitcoinAddress = function () { - var hash = this.getPubKeyHash(); - var addr = new Bitcoin.Address(hash); - return addr; - }; + var ECDSA = Bitcoin.ECDSA; + var ecparams = getSECCurveByName("secp256k1"); + var rng = new SecureRandom(); + + var ECKey = function (input) { + if (!input) { + // Generate new key + var n = ecparams.getN(); + this.priv = ECDSA.getBigRandom(n); + } else if (input instanceof BigInteger) { + // Input is a private key value + this.priv = input; + } else if (Bitcoin.Util.isArray(input)) { + // Prepend zero byte to prevent interpretation as negative integer + this.priv = BigInteger.fromByteArrayUnsigned(input); + } else if ("string" == typeof input) { + // Prepend zero byte to prevent interpretation as negative integer + this.priv = BigInteger.fromByteArrayUnsigned(Crypto.util.base64ToBytes(input)); + } + }; + + ECKey.prototype.getPub = function () { + if (this.pub) return this.pub; + + return this.pub = ecparams.getG().multiply(this.priv).getEncoded(); + }; + + ECKey.prototype.getPubKeyHash = function () { + if (this.pubKeyHash) return this.pubKeyHash; + + return this.pubKeyHash = Bitcoin.Util.sha256ripe160(this.getPub()); + }; + + ECKey.prototype.getBitcoinAddress = function () { + var hash = this.getPubKeyHash(); + var addr = new Bitcoin.Address(hash); + return addr; + }; ECKey.prototype.setPub = function (pub) { this.pub = pub; }; - 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.toString = function (format) { + if (format === "base64") { + return Crypto.util.bytesToBase64(this.priv.toByteArrayUnsigned()); + } else { + return Crypto.util.bytesToHex(this.priv.toByteArrayUnsigned()); + } + }; - ECKey.prototype.sign = function (hash) { - return ECDSA.sign(hash, this.priv); - }; + ECKey.prototype.sign = function (hash) { + return ECDSA.sign(hash, this.priv); + }; - ECKey.prototype.verify = function (hash, sig) { - return ECDSA.verify(hash, sig, this.getPub()); - }; + ECKey.prototype.verify = function (hash, sig) { + return ECDSA.verify(hash, sig, this.getPub()); + }; - return ECKey; + return ECKey; })(); diff --git a/src/exit/client.js b/src/exit/client.js index 03bc5a7..86d9f55 100644 --- a/src/exit/client.js +++ b/src/exit/client.js @@ -7,12 +7,12 @@ exports.ExitNode = ExitNode; function ExitNode(host, port, secure) { - this.setUri(host, port, secure); + this.setUri(host, port, secure); - this.unique = 1; + this.unique = 1; this.connected = false; - this.callbacks = []; + this.callbacks = []; }; Bitcoin.EventEmitter.augment(ExitNode); @@ -22,23 +22,23 @@ }; ExitNode.prototype.connect = function (wallet) { - this.wallet = wallet; + this.wallet = wallet; // Workaround for socket.io not properly allowing disconnecting and reconnecting delete io.sockets[this.uri]; io.j = []; - this.socket = io.connect(this.uri); - this.socket.on('connect', $.proxy(this.handleConnect, this)); - this.socket.on('error', function () { - console.log('error, test'); - }); - this.socket.on('message', $.proxy(this.handleMessage, this)); - this.socket.on('disconnect', $.proxy(this.handleDisconnect, this)); + this.socket = io.connect(this.uri); + this.socket.on('connect', $.proxy(this.handleConnect, this)); + this.socket.on('error', function () { + console.log('error, test'); + }); + this.socket.on('message', $.proxy(this.handleMessage, this)); + this.socket.on('disconnect', $.proxy(this.handleDisconnect, this)); }; ExitNode.prototype.disconnect = function () { - if (this.socket) { + if (this.socket) { this.socket.disconnect(); this.socket = null; this.connected = false; @@ -51,76 +51,76 @@ * Make RPC call. */ ExitNode.prototype.call = function (method, argObj, callback) { - this.socket.send($.toJSON({ - "method": method, - "params": [argObj], - "id": this.unique - })); - if (callback) this.callbacks[this.unique] = callback; - this.unique++; + this.socket.send($.toJSON({ + "method": method, + "params": [argObj], + "id": this.unique + })); + if (callback) this.callbacks[this.unique] = callback; + this.unique++; }; ExitNode.prototype.handleConnect = function () { - var self = this; + var self = this; this.connected = true; }; ExitNode.prototype.listen = function (addrs) { - self.call("pubkeysRegister", { - keys: addrs.join(',') - }, function (err, result) { - if (err) { - console.error("Could not register public keys"); - return; - } - - self.call("pubkeysListen", { - handle: result.handle - }, function (err, result) { - // Communicate the block height - self.trigger('blockInit', {height: result.height}); - - // Pass on the newly downloaded transactions + self.call("pubkeysRegister", { + keys: addrs.join(',') + }, function (err, result) { + if (err) { + console.error("Could not register public keys"); + return; + } + + self.call("pubkeysListen", { + handle: result.handle + }, function (err, result) { + // Communicate the block height + self.trigger('blockInit', {height: result.height}); + + // Pass on the newly downloaded transactions self.trigger('txData', { confirmed: true, txs: result.txs }); - // TODO: Download more transactions + // TODO: Download more transactions self.trigger('connectStatus', {status: 'ok'}); - }); + }); - self.call("pubkeysUnconfirmed", { - handle: result.handle - }, function (err, result) { - // Pass on the newly downloaded transactions + self.call("pubkeysUnconfirmed", { + handle: result.handle + }, function (err, result) { + // Pass on the newly downloaded transactions self.trigger('txData', { confirmed: false, txs: result.txs }); - }); - }); + }); + }); }; ExitNode.prototype.handleMessage = function (data) { - // Handle JSON-RPC result messages - if ("undefined" !== typeof data.result && - "function" == typeof this.callbacks[data.id]) { - this.callbacks[data.id](data.error, data.result); - - // Handle JSON-RPC request messages - } else if ("undefined" !== typeof data.method) { - // Create an event + // Handle JSON-RPC result messages + if ("undefined" !== typeof data.result && + "function" == typeof this.callbacks[data.id]) { + this.callbacks[data.id](data.error, data.result); + + // Handle JSON-RPC request messages + } else if ("undefined" !== typeof data.method) { + // Create an event this.trigger(data.method, data.params[0]); - } + } }; ExitNode.prototype.handleDisconnect = function () { - // TODO: Attempt reconnect (unless disconnect was intended) + // TODO: Attempt reconnect (unless disconnect was intended) }; ExitNode.prototype.query = function (api, params, jsonp, callback) { diff --git a/src/opcode.js b/src/opcode.js index b7b2bce..4850d17 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 = Bitcoin.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; + } })(); diff --git a/src/paillier.js b/src/paillier.js index 32b1735..ed0ccc9 100644 --- a/src/paillier.js +++ b/src/paillier.js @@ -1,5 +1,5 @@ Bitcoin.Paillier = (function () { - var rng = new SecureRandom(); + var rng = new SecureRandom(); var TWO = BigInteger.valueOf(2); var Paillier = { diff --git a/src/script.js b/src/script.js index d9e68b7..317dc18 100644 --- a/src/script.js +++ b/src/script.js @@ -1,183 +1,183 @@ (function () { - var Opcode = Bitcoin.Opcode; - - // Make opcodes available as pseudo-constants - for (var i in Opcode.map) { - eval("var " + i + " = " + Opcode.map[i] + ";"); - } - - 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; - } else { - throw new Error("Invalid script"); - } - - this.parse(); - }; - - 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); - } - } - }; - - Script.prototype.getOutType = function () - { - 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'; - } - }; - - Script.prototype.simpleOutPubKeyHash = 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"); - } - }; - - Script.prototype.getInType = function () - { - if (this.chunks.length == 1) { - // Direct IP to IP transactions only have the public key in their scriptSig. - return 'Pubkey'; - } else if (this.chunks.length == 2 && - Bitcoin.Util.isArray(this.chunks[0]) && - Bitcoin.Util.isArray(this.chunks[1])) { - return 'Address'; - } else { - console.log(this.chunks); - throw new Error("Encountered non-standard scriptSig"); - } - }; - - Script.prototype.simpleInPubKey = function () - { - switch (this.getInType()) { - case 'Address': - return this.chunks[1]; - case 'Pubkey': - return this.chunks[0]; - default: - throw new Error("Encountered non-standard scriptSig"); - } - }; - - Script.prototype.simpleInPubKeyHash = function () - { - return Bitcoin.Util.sha256ripe160(this.simpleInPubKey()); - }; - - Script.prototype.writeOp = function (opcode) - { - this.buffer.push(opcode); - this.chunks.push(opcode); - }; - - 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); - }; - - 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; - }; - - Script.createInputScript = function (signature, pubKey) - { - var script = new Script(); - script.writeBytes(signature); - script.writeBytes(pubKey); - return script; - }; - - Script.prototype.clone = function () - { - return new Script(this.buffer); - }; + var Opcode = Bitcoin.Opcode; + + // Make opcodes available as pseudo-constants + for (var i in Opcode.map) { + eval("var " + i + " = " + Opcode.map[i] + ";"); + } + + 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; + } else { + throw new Error("Invalid script"); + } + + this.parse(); + }; + + 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); + } + } + }; + + Script.prototype.getOutType = function () + { + 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'; + } + }; + + Script.prototype.simpleOutPubKeyHash = 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"); + } + }; + + Script.prototype.getInType = function () + { + if (this.chunks.length == 1) { + // Direct IP to IP transactions only have the public key in their scriptSig. + return 'Pubkey'; + } else if (this.chunks.length == 2 && + Bitcoin.Util.isArray(this.chunks[0]) && + Bitcoin.Util.isArray(this.chunks[1])) { + return 'Address'; + } else { + console.log(this.chunks); + throw new Error("Encountered non-standard scriptSig"); + } + }; + + Script.prototype.simpleInPubKey = function () + { + switch (this.getInType()) { + case 'Address': + return this.chunks[1]; + case 'Pubkey': + return this.chunks[0]; + default: + throw new Error("Encountered non-standard scriptSig"); + } + }; + + Script.prototype.simpleInPubKeyHash = function () + { + return Bitcoin.Util.sha256ripe160(this.simpleInPubKey()); + }; + + Script.prototype.writeOp = function (opcode) + { + this.buffer.push(opcode); + this.chunks.push(opcode); + }; + + 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); + }; + + 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; + }; + + Script.createInputScript = function (signature, pubKey) + { + var script = new Script(); + script.writeBytes(signature); + script.writeBytes(pubKey); + return script; + }; + + Script.prototype.clone = function () + { + return new Script(this.buffer); + }; })(); diff --git a/src/transaction.js b/src/transaction.js index 8e8ba28..ff89a6e 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -1,231 +1,231 @@ (function () { - var Script = Bitcoin.Script; - - var Transaction = Bitcoin.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 (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; - } - }; - - Transaction.objectify = function (txs) { - var objs = []; - for (var i = 0; i < txs.length; i++) { - objs.push(new Transaction(txs[i])); - } - return objs; - }; - - 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 - })); - } - }; - - Transaction.prototype.addOutput = function (address, value) { - if (arguments[0] instanceof TransactionOut) { - this.outs.push(arguments[0]); - } else { - if (value instanceof BigInteger) { - value = value.toByteArrayUnsigned().reverse(); - while (value.length < 8) value.push(0); - } else if (Bitcoin.Util.isArray(value)) { - // Nothing to do - } - - this.outs.push(new TransactionOut({ - value: value, - script: Script.createOutputScript(address) - })); - } - }; - - Transaction.prototype.serialize = function () - { - var buffer = []; - buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(this.version)]).reverse()); - buffer = buffer.concat(Bitcoin.Util.numToVarInt(this.ins.length)); - for (var i = 0; i < this.ins.length; i++) { - var txin = this.ins[i]; - buffer = buffer.concat(Crypto.util.base64ToBytes(txin.outpoint.hash)); - buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(txin.outpoint.index)]).reverse()); - var scriptBytes = txin.script.buffer; - buffer = buffer.concat(Bitcoin.Util.numToVarInt(scriptBytes.length)); - buffer = buffer.concat(scriptBytes); - buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(txin.sequence)]).reverse()); - } - buffer = buffer.concat(Bitcoin.Util.numToVarInt(this.outs.length)); - for (var i = 0; i < this.outs.length; i++) { - var txout = this.outs[i]; - buffer = buffer.concat(txout.value); - var scriptBytes = txout.script.buffer; - buffer = buffer.concat(Bitcoin.Util.numToVarInt(scriptBytes.length)); - buffer = buffer.concat(scriptBytes); - } - buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(this.lock_time)]).reverse()); - - return buffer; - }; - - var OP_CODESEPARATOR = 171; - - var SIGHASH_ALL = 1; - var SIGHASH_NONE = 2; - var SIGHASH_SINGLE = 3; - var SIGHASH_ANYONECANPAY = 80; - - Transaction.prototype.hashTransactionForSignature = function (connectedScript, inIndex, hashType) - { - var txTmp = this.clone(); - - // In case concatenating two scripts ends up with two codeseparators, - // or an extra one at the end, this prevents all those possible incompatibilities. - /*scriptCode = scriptCode.filter(function (val) { - return val !== OP_CODESEPARATOR; - });*/ - - // Blank out other inputs' signatures - for (var i = 0; i < txTmp.ins.length; i++) { - txTmp.ins[i].script = new Script(); - } - - txTmp.ins[inIndex].script = connectedScript; - - // Blank out some of the outputs - if ((hashType & 0x1f) == SIGHASH_NONE) { - txTmp.outs = []; - - // Let the others update at will - for (var i = 0; i < txTmp.ins.length; i++) - if (i != inIndex) - txTmp.ins[i].sequence = 0; - } else if ((hashType & 0x1f) == SIGHASH_SINGLE) { - // TODO: Implement - } - - // Blank out other inputs completely, not recommended for open transactions - if (hashType & SIGHASH_ANYONECANPAY) { - txTmp.ins = [txTmp.ins[inIndex]]; - } + var Script = Bitcoin.Script; + + var Transaction = Bitcoin.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 (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; + } + }; + + Transaction.objectify = function (txs) { + var objs = []; + for (var i = 0; i < txs.length; i++) { + objs.push(new Transaction(txs[i])); + } + return objs; + }; + + 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 + })); + } + }; + + Transaction.prototype.addOutput = function (address, value) { + if (arguments[0] instanceof TransactionOut) { + this.outs.push(arguments[0]); + } else { + if (value instanceof BigInteger) { + value = value.toByteArrayUnsigned().reverse(); + while (value.length < 8) value.push(0); + } else if (Bitcoin.Util.isArray(value)) { + // Nothing to do + } + + this.outs.push(new TransactionOut({ + value: value, + script: Script.createOutputScript(address) + })); + } + }; + + Transaction.prototype.serialize = function () + { + var buffer = []; + buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(this.version)]).reverse()); + buffer = buffer.concat(Bitcoin.Util.numToVarInt(this.ins.length)); + for (var i = 0; i < this.ins.length; i++) { + var txin = this.ins[i]; + buffer = buffer.concat(Crypto.util.base64ToBytes(txin.outpoint.hash)); + buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(txin.outpoint.index)]).reverse()); + var scriptBytes = txin.script.buffer; + buffer = buffer.concat(Bitcoin.Util.numToVarInt(scriptBytes.length)); + buffer = buffer.concat(scriptBytes); + buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(txin.sequence)]).reverse()); + } + buffer = buffer.concat(Bitcoin.Util.numToVarInt(this.outs.length)); + for (var i = 0; i < this.outs.length; i++) { + var txout = this.outs[i]; + buffer = buffer.concat(txout.value); + var scriptBytes = txout.script.buffer; + buffer = buffer.concat(Bitcoin.Util.numToVarInt(scriptBytes.length)); + buffer = buffer.concat(scriptBytes); + } + buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(this.lock_time)]).reverse()); + + return buffer; + }; + + var OP_CODESEPARATOR = 171; + + var SIGHASH_ALL = 1; + var SIGHASH_NONE = 2; + var SIGHASH_SINGLE = 3; + var SIGHASH_ANYONECANPAY = 80; + + Transaction.prototype.hashTransactionForSignature = function (connectedScript, inIndex, hashType) + { + var txTmp = this.clone(); + + // In case concatenating two scripts ends up with two codeseparators, + // or an extra one at the end, this prevents all those possible incompatibilities. + /*scriptCode = scriptCode.filter(function (val) { + return val !== OP_CODESEPARATOR; + });*/ + + // Blank out other inputs' signatures + for (var i = 0; i < txTmp.ins.length; i++) { + txTmp.ins[i].script = new Script(); + } + + txTmp.ins[inIndex].script = connectedScript; + + // Blank out some of the outputs + if ((hashType & 0x1f) == SIGHASH_NONE) { + txTmp.outs = []; + + // Let the others update at will + for (var i = 0; i < txTmp.ins.length; i++) + if (i != inIndex) + txTmp.ins[i].sequence = 0; + } else if ((hashType & 0x1f) == SIGHASH_SINGLE) { + // TODO: Implement + } + + // Blank out other inputs completely, not recommended for open transactions + if (hashType & SIGHASH_ANYONECANPAY) { + txTmp.ins = [txTmp.ins[inIndex]]; + } console.log(txTmp); - var buffer = txTmp.serialize(); + var buffer = txTmp.serialize(); - buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(hashType)]).reverse()); + buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(hashType)]).reverse()); - console.log("signtx: "+Crypto.util.bytesToHex(buffer)); + console.log("signtx: "+Crypto.util.bytesToHex(buffer)); - var hash1 = Crypto.SHA256(buffer, {asBytes: true}); + var hash1 = Crypto.SHA256(buffer, {asBytes: true}); - console.log("sha256_1: ", Crypto.util.bytesToHex(hash1)); + console.log("sha256_1: ", Crypto.util.bytesToHex(hash1)); - return Crypto.SHA256(hash1, {asBytes: true}); - }; + return Crypto.SHA256(hash1, {asBytes: true}); + }; - Transaction.prototype.getHash = function () - { - var buffer = this.serialize(); - return Crypto.SHA256(Crypto.SHA256(buffer, {asBytes: true}), {asBytes: true}); - }; + Transaction.prototype.getHash = function () + { + var buffer = this.serialize(); + return Crypto.SHA256(Crypto.SHA256(buffer, {asBytes: true}), {asBytes: true}); + }; - 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; - }; + 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. */ - 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); + 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'; + if (impact.sign > 0 && impact.value.compareTo(BigInteger.ZERO) > 0) { + analysis.type = 'recv'; analysis.addr = new Bitcoin.Address(firstMeRecvHash); - } else if (allFromMe && allToMe) { + } else if (allFromMe && allToMe) { analysis.type = 'self'; - } else if (allFromMe) { + } else if (allFromMe) { analysis.type = 'sent'; analysis.addr = new Bitcoin.Address(firstRecvHash); - } else { - analysis.type = "other"; - } + } else { + analysis.type = "other"; + } return analysis; }; - Transaction.prototype.getDescription = function (wallet) { + Transaction.prototype.getDescription = function (wallet) { var analysis = this.analyze(wallet); if (!analysis) return ""; @@ -236,127 +236,127 @@ break; case 'sent': - return "Payment to "+analysis.addr; + return "Payment to "+analysis.addr; break; case 'self': - return "Payment to yourself"; + return "Payment to yourself"; break; case 'other': default: return ""; } - }; - - Transaction.prototype.getTotalValue = function () { - var totalValue = BigInteger.ZERO; - for (var j = 0; j < this.outs.length; j++) { - var txout = this.outs[j]; - totalValue = totalValue.add(Bitcoin.Util.valueToBigInt(txout.value)); - } - return totalValue; - }; - - /** - * Calculates the impact a transaction has on this wallet. - * - * Based on the its public keys, the wallet will calculate the - * credit or debit of this transaction. - * - * It will return an object with two properties: - * - sign: 1 or -1 depending on sign of the calculated impact. - * - value: amount of calculated impact - * - * @returns Object Impact on wallet - */ - Transaction.prototype.calcImpact = function (wallet) { - if (!(wallet instanceof Bitcoin.Wallet)) return BigInteger.ZERO; - - // Calculate credit to us from all outputs - var valueOut = BigInteger.ZERO; - for (var j = 0; j < this.outs.length; j++) { - var txout = this.outs[j]; - var hash = Crypto.util.bytesToBase64(txout.script.simpleOutPubKeyHash()); - if (wallet.hasHash(hash)) { - valueOut = valueOut.add(Bitcoin.Util.valueToBigInt(txout.value)); - } - } - - // Calculate debit to us from all ins - var valueIn = BigInteger.ZERO; - for (var j = 0; j < this.ins.length; j++) { - var txin = this.ins[j]; - var hash = Crypto.util.bytesToBase64(txin.script.simpleInPubKeyHash()); - if (wallet.hasHash(hash)) { - var fromTx = wallet.txIndex[txin.outpoint.hash]; - if (fromTx) { - valueIn = valueIn.add(Bitcoin.Util.valueToBigInt(fromTx.outs[txin.outpoint.index].value)); - } - } - } - if (valueOut.compareTo(valueIn) >= 0) { - return { - sign: 1, - value: valueOut.subtract(valueIn) - }; - } else { - return { - sign: -1, - value: valueIn.subtract(valueOut) - }; - } - }; - - 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; - }; - - TransactionIn.prototype.clone = function () - { - var newTxin = new TransactionIn({ - outpoint: { - hash: this.outpoint.hash, - index: this.outpoint.index - }, - script: this.script.clone(), - sequence: this.sequence - }); - return newTxin; - }; - - var TransactionOut = Bitcoin.TransactionOut = function (data) - { - if (data.script instanceof Script) { - this.script = data.script; - } else { - this.script = new Script(data.script); - } - - 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); - } - }; - - TransactionOut.prototype.clone = function () - { - var newTxout = new TransactionOut({ - script: this.script.clone(), - value: this.value.slice(0) - }); - return newTxout; - }; + }; + + Transaction.prototype.getTotalValue = function () { + var totalValue = BigInteger.ZERO; + for (var j = 0; j < this.outs.length; j++) { + var txout = this.outs[j]; + totalValue = totalValue.add(Bitcoin.Util.valueToBigInt(txout.value)); + } + return totalValue; + }; + + /** + * Calculates the impact a transaction has on this wallet. + * + * Based on the its public keys, the wallet will calculate the + * credit or debit of this transaction. + * + * It will return an object with two properties: + * - sign: 1 or -1 depending on sign of the calculated impact. + * - value: amount of calculated impact + * + * @returns Object Impact on wallet + */ + Transaction.prototype.calcImpact = function (wallet) { + if (!(wallet instanceof Bitcoin.Wallet)) return BigInteger.ZERO; + + // Calculate credit to us from all outputs + var valueOut = BigInteger.ZERO; + for (var j = 0; j < this.outs.length; j++) { + var txout = this.outs[j]; + var hash = Crypto.util.bytesToBase64(txout.script.simpleOutPubKeyHash()); + if (wallet.hasHash(hash)) { + valueOut = valueOut.add(Bitcoin.Util.valueToBigInt(txout.value)); + } + } + + // Calculate debit to us from all ins + var valueIn = BigInteger.ZERO; + for (var j = 0; j < this.ins.length; j++) { + var txin = this.ins[j]; + var hash = Crypto.util.bytesToBase64(txin.script.simpleInPubKeyHash()); + if (wallet.hasHash(hash)) { + var fromTx = wallet.txIndex[txin.outpoint.hash]; + if (fromTx) { + valueIn = valueIn.add(Bitcoin.Util.valueToBigInt(fromTx.outs[txin.outpoint.index].value)); + } + } + } + if (valueOut.compareTo(valueIn) >= 0) { + return { + sign: 1, + value: valueOut.subtract(valueIn) + }; + } else { + return { + sign: -1, + value: valueIn.subtract(valueOut) + }; + } + }; + + 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; + }; + + TransactionIn.prototype.clone = function () + { + var newTxin = new TransactionIn({ + outpoint: { + hash: this.outpoint.hash, + index: this.outpoint.index + }, + script: this.script.clone(), + sequence: this.sequence + }); + return newTxin; + }; + + var TransactionOut = Bitcoin.TransactionOut = function (data) + { + if (data.script instanceof Script) { + this.script = data.script; + } else { + this.script = new Script(data.script); + } + + 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); + } + }; + + TransactionOut.prototype.clone = function () + { + var newTxout = new TransactionOut({ + script: this.script.clone(), + value: this.value.slice(0) + }); + return newTxout; + }; })(); diff --git a/src/txdb.js b/src/txdb.js index bb5feae..73ee835 100755 --- a/src/txdb.js +++ b/src/txdb.js @@ -1,58 +1,58 @@ var TransactionDatabase = function () { - this.txs = []; - this.txIndex = {}; + this.txs = []; + this.txIndex = {}; }; EventEmitter.augment(TransactionDatabase.prototype); TransactionDatabase.prototype.addTransaction = function (tx) { - this.addTransactionNoUpdate(tx); - $(this).trigger('update'); + this.addTransactionNoUpdate(tx); + $(this).trigger('update'); }; TransactionDatabase.prototype.addTransactionNoUpdate = function (tx) { - // Return if transaction is already known - if (this.txIndex[tx.hash]) { - return; - } + // Return if transaction is already known + if (this.txIndex[tx.hash]) { + return; + } - this.txs.push(new Bitcoin.Transaction(tx)); - this.txIndex[tx.hash] = tx; + this.txs.push(new Bitcoin.Transaction(tx)); + this.txIndex[tx.hash] = tx; }; TransactionDatabase.prototype.removeTransaction = function (hash) { - this.removeTransactionNoUpdate(hash); - $(this).trigger('update'); + this.removeTransactionNoUpdate(hash); + $(this).trigger('update'); }; TransactionDatabase.prototype.removeTransactionNoUpdate = function (hash) { - var tx = this.txIndex[hash]; + var tx = this.txIndex[hash]; - if (!tx) { - // If the tx is not in the index, we don't actually waste our - // time looping through the array. - return; - } + if (!tx) { + // If the tx is not in the index, we don't actually waste our + // time looping through the array. + return; + } - for (var i = 0, l = this.txs.length; i < l; i++) { - if (this.txs[i].hash == hash) { - this.txs.splice(i, 1); - break; - } - } + for (var i = 0, l = this.txs.length; i < l; i++) { + if (this.txs[i].hash == hash) { + this.txs.splice(i, 1); + break; + } + } - delete this.txIndex[hash]; + delete this.txIndex[hash]; }; TransactionDatabase.prototype.loadTransactions = function (txs) { - for (var i = 0; i < txs.length; i++) { - this.addTransactionNoUpdate(txs[i]); - } - $(this).trigger('update'); + for (var i = 0; i < txs.length; i++) { + this.addTransactionNoUpdate(txs[i]); + } + $(this).trigger('update'); }; TransactionDatabase.prototype.getTransactions = function () { - return this.txs; + return this.txs; }; TransactionDatabase.prototype.clear = function () { diff --git a/src/util.js b/src/util.js index f033cd3..14bf9cb 100644 --- a/src/util.js +++ b/src/util.js @@ -1,99 +1,99 @@ // BigInteger monkey patching BigInteger.valueOf = nbv; BigInteger.prototype.toByteArrayUnsigned = function () { - var ba = this.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; - } + var ba = this.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; + } }; 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); - } + 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); + } }; // Console ignore var names = ["log", "debug", "info", "warn", "error", "assert", "dir", - "dirxml", "group", "groupEnd", "time", "timeEnd", "count", - "trace", "profile", "profileEnd"]; + "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() {}; + if ("undefined" == typeof window.console[names[i]]) + window.console[names[i]] = function() {}; // Bitcoin utility functions Bitcoin.Util = { - isArray: Array.isArray || function(o) { - return Object.prototype.toString.call(o) === '[object Array]'; - }, - makeFilledArray: function (len, val) { - var array = []; - var i = 0; - while (i < len) { - array[i++] = val; - } - return array; - }, - numToVarInt: function (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])); - } - }, - valueToBigInt: function (valueBuffer) { - if (valueBuffer instanceof BigInteger) return valueBuffer; + isArray: Array.isArray || function(o) { + return Object.prototype.toString.call(o) === '[object Array]'; + }, + makeFilledArray: function (len, val) { + var array = []; + var i = 0; + while (i < len) { + array[i++] = val; + } + return array; + }, + numToVarInt: function (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])); + } + }, + valueToBigInt: function (valueBuffer) { + if (valueBuffer instanceof BigInteger) return valueBuffer; - // Prepend zero byte to prevent interpretation as negative integer - return BigInteger.fromByteArrayUnsigned(valueBuffer); - }, - 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; - }, + // Prepend zero byte to prevent interpretation as negative integer + return BigInteger.fromByteArrayUnsigned(valueBuffer); + }, + 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; + }, parseValue: function (valueString) { - 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))); + 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; }, - sha256ripe160: function (data) { - return Crypto.RIPEMD160(Crypto.SHA256(data, {asBytes: true}), {asBytes: true}); - } + sha256ripe160: function (data) { + return Crypto.RIPEMD160(Crypto.SHA256(data, {asBytes: true}), {asBytes: true}); + } }; for (var i in Crypto.util) { diff --git a/src/wallet.js b/src/wallet.js index 28f3c1f..457ebf1 100755 --- a/src/wallet.js +++ b/src/wallet.js @@ -1,245 +1,245 @@ Bitcoin.Wallet = (function () { - var Script = Bitcoin.Script, - TransactionIn = Bitcoin.TransactionIn, - TransactionOut = Bitcoin.TransactionOut; - - var Wallet = function () { - // Keychain - var keys = []; - this.addressHashes = []; - - // Transaction data - this.txIndex = {}; - this.unspentOuts = []; - - // Other fields - this.addressPointer = 0; - - this.addKey = function (key, pub) { - if (!(key instanceof Bitcoin.ECKey)) { - key = new Bitcoin.ECKey(key); - } - keys.push(key); - - if (pub) { - if ("string" === typeof pub) { - pub = Crypto.util.base64ToBytes(pub); - } - key.pub = pub; - } - - this.addressHashes.push(key.getBitcoinAddress().getHashBase64()); - }; - - 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]); - } - } - }; - - this.getKeys = function () { - var serializedWallet = []; - - for (var i = 0; i < keys.length; i++) { - serializedWallet.push(keys[i].toString('base64')); - } - - return serializedWallet; - }; - - this.getPubKeys = function () { - var pubs = []; - - for (var i = 0; i < keys.length; i++) { - pubs.push(Crypto.util.bytesToBase64(keys[i].getPub())); - } - - return pubs; - }; - - this.clear = function () { - keys = []; - }; - - this.getLength = function () { - return keys.length; - }; - - 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; - } - }; - - this.getNextAddress = function () { - this.addressPointer++; - if(!keys[this.addressPointer]) { - this.generateAddress(); - } - return keys[this.addressPointer].getBitcoinAddress(); - }; - - 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"); - }; - - 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()); - }; - - 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; - } - } - } - - // Remove spent outputs - for (j = 0; j < tx.ins.length; j++) { - var txin = new TransactionIn(tx.ins[j]); - var pubkey = txin.script.simpleInPubKey(); - hash = Crypto.util.bytesToBase64(Bitcoin.Util.sha256ripe160(pubkey)); - for (k = 0; k < this.addressHashes.length; k++) { - if (this.addressHashes[k] === hash) { - for (var l = 0; l < this.unspentOuts.length; l++) { - if (txin.outpoint.hash == this.unspentOuts[l].tx.hash && - txin.outpoint.index == this.unspentOuts[l].index) { - this.unspentOuts.splice(l, 1); - } - } - break; - } - } + var Script = Bitcoin.Script, + TransactionIn = Bitcoin.TransactionIn, + TransactionOut = Bitcoin.TransactionOut; + + var Wallet = function () { + // Keychain + var keys = []; + this.addressHashes = []; + + // Transaction data + this.txIndex = {}; + this.unspentOuts = []; + + // Other fields + this.addressPointer = 0; + + this.addKey = function (key, pub) { + if (!(key instanceof Bitcoin.ECKey)) { + key = new Bitcoin.ECKey(key); + } + keys.push(key); + + if (pub) { + if ("string" === typeof pub) { + pub = Crypto.util.base64ToBytes(pub); } + key.pub = pub; + } - // Index transaction - this.txIndex[tx.hash] = tx; + this.addressHashes.push(key.getBitcoinAddress().getHashBase64()); }; - 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.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]); } - return balance; - }; - - Wallet.prototype.createSend = function (address, sendValue, feeValue) { - var selectedOuts = []; - var txValue = sendValue.add(feeValue); - var availableValue = BigInteger.ZERO; - var i; - for (i = 0; i < this.unspentOuts.length; i++) { - selectedOuts.push(this.unspentOuts[i]); - availableValue = availableValue.add(Bitcoin.Util.valueToBigInt(this.unspentOuts[i].out.value)); - - if (availableValue.compareTo(txValue) >= 0) break; + } else { + for (i = 0; i < keys.length; i++) { + this.addKey(keys[i]); } + } + }; - if (availableValue.compareTo(txValue) < 0) { - throw new Error('Insufficient funds.'); - } - - - var changeValue = availableValue.subtract(txValue); + this.getKeys = function () { + var serializedWallet = []; - var sendTx = new Bitcoin.Transaction(); + for (var i = 0; i < keys.length; i++) { + serializedWallet.push(keys[i].toString('base64')); + } - for (i = 0; i < selectedOuts.length; i++) { - sendTx.addInput(selectedOuts[i].tx, selectedOuts[i].index); - } + return serializedWallet; + }; - sendTx.addOutput(address, sendValue); - if (changeValue.compareTo(BigInteger.ZERO) > 0) { - sendTx.addOutput(this.getNextAddress(), changeValue); - } + this.getPubKeys = function () { + var pubs = []; - var hashType = 1; // SIGHASH_ALL + for (var i = 0; i < keys.length; i++) { + pubs.push(Crypto.util.bytesToBase64(keys[i].getPub())); + } - for (i = 0; i < sendTx.ins.length; i++) { - var hash = sendTx.hashTransactionForSignature(selectedOuts[i].out.script, i, hashType); - var pubKeyHash = selectedOuts[i].out.script.simpleOutPubKeyHash(); - var signature = this.signWithKey(pubKeyHash, hash); + return pubs; + }; - // Append hash type - signature.push(parseInt(hashType, 10)); + this.clear = function () { + keys = []; + }; - sendTx.ins[i].script = Script.createInputScript(signature, this.getPubKeyFromHash(pubKeyHash)); - } + this.getLength = function () { + return keys.length; + }; - return sendTx; + this.getAllAddresses = function () { + var addresses = []; + for (var i = 0; i < keys.length; i++) { + addresses.push(keys[i].getBitcoinAddress()); + } + return addresses; }; - Wallet.prototype.clearTransactions = function () { - this.txIndex = {}; - this.unspentOuts = []; + this.getCurAddress = function () { + if (keys[this.addressPointer]) { + return keys[this.addressPointer].getBitcoinAddress(); + } else { + return null; + } }; - /** - * Check to see if a pubKeyHash belongs to this wallet. - */ - Wallet.prototype.hasHash = function (hash) { - if (Bitcoin.Util.isArray(hash)) hash = Crypto.util.bytesToBase64(hash); + this.getNextAddress = function () { + this.addressPointer++; + if(!keys[this.addressPointer]) { + this.generateAddress(); + } + return keys[this.addressPointer].getBitcoinAddress(); + }; - // TODO: Just create an object with base64 hashes as keys for faster lookup - for (var k = 0; k < this.addressHashes.length; k++) { - if (this.addressHashes[k] === hash) return true; + this.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); } - return false; + } + throw new Error("Missing key for signature"); }; - return 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()); + }; + + 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; + } + } + } + + // Remove spent outputs + for (j = 0; j < tx.ins.length; j++) { + var txin = new TransactionIn(tx.ins[j]); + var pubkey = txin.script.simpleInPubKey(); + hash = Crypto.util.bytesToBase64(Bitcoin.Util.sha256ripe160(pubkey)); + for (k = 0; k < this.addressHashes.length; k++) { + if (this.addressHashes[k] === hash) { + for (var l = 0; l < this.unspentOuts.length; l++) { + if (txin.outpoint.hash == this.unspentOuts[l].tx.hash && + txin.outpoint.index == this.unspentOuts[l].index) { + this.unspentOuts.splice(l, 1); + } + } + break; + } + } + } + + // 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)); + } + return balance; + }; + + Wallet.prototype.createSend = function (address, sendValue, feeValue) { + var selectedOuts = []; + var txValue = sendValue.add(feeValue); + var availableValue = BigInteger.ZERO; + var i; + for (i = 0; i < this.unspentOuts.length; i++) { + selectedOuts.push(this.unspentOuts[i]); + availableValue = availableValue.add(Bitcoin.Util.valueToBigInt(this.unspentOuts[i].out.value)); + + if (availableValue.compareTo(txValue) >= 0) break; + } + + if (availableValue.compareTo(txValue) < 0) { + throw new Error('Insufficient funds.'); + } + + + var changeValue = availableValue.subtract(txValue); + + var sendTx = new Bitcoin.Transaction(); + + for (i = 0; i < selectedOuts.length; i++) { + sendTx.addInput(selectedOuts[i].tx, selectedOuts[i].index); + } + + sendTx.addOutput(address, sendValue); + if (changeValue.compareTo(BigInteger.ZERO) > 0) { + sendTx.addOutput(this.getNextAddress(), changeValue); + } + + var hashType = 1; // SIGHASH_ALL + + for (i = 0; i < sendTx.ins.length; i++) { + var hash = sendTx.hashTransactionForSignature(selectedOuts[i].out.script, i, hashType); + var pubKeyHash = selectedOuts[i].out.script.simpleOutPubKeyHash(); + var signature = this.signWithKey(pubKeyHash, hash); + + // Append hash type + signature.push(parseInt(hashType, 10)); + + sendTx.ins[i].script = Script.createInputScript(signature, this.getPubKeyFromHash(pubKeyHash)); + } + + return sendTx; + }; + + Wallet.prototype.clearTransactions = function () { + this.txIndex = {}; + this.unspentOuts = []; + }; + + /** + * Check to see if a pubKeyHash belongs to this wallet. + */ + Wallet.prototype.hasHash = function (hash) { + if (Bitcoin.Util.isArray(hash)) hash = Crypto.util.bytesToBase64(hash); + + // TODO: Just create an object with base64 hashes as keys for faster lookup + for (var k = 0; k < this.addressHashes.length; k++) { + if (this.addressHashes[k] === hash) return true; + } + return false; + }; + + return Wallet; })();