bitcoinjs-lib
Advanced tools
Comparing version 0.1.0 to 0.1.3
{ | ||
"name": "bitcoinjs-lib", | ||
"version": "0.1.0", | ||
"version": "0.1.3", | ||
"description": "Client-side Bitcoin JavaScript library", | ||
@@ -5,0 +5,0 @@ |
@@ -13,1 +13,20 @@ # bitcoinjs-lib | ||
Prototype software, use at your own peril. | ||
# License | ||
This library is free and open-source software released under the MIT | ||
license. | ||
# Copyright | ||
BitcoinJS (c) 2011-2012 Stefan Thomas | ||
Released under MIT license | ||
http://bitcoinjs.org/ | ||
JSBN (c) 2003-2005 Tom Wu | ||
Released under BSD license | ||
http://www-cs-students.stanford.edu/~tjw/jsbn/ | ||
CryptoJS (c) 2009–2012 by Jeff Mott | ||
Released under New BSD license | ||
http://code.google.com/p/crypto-js/ |
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; | ||
}; | ||
/** | ||
* Serialize this object as a standard Bitcoin address. | ||
* | ||
* Returns the address as a base58-encoded string in the standardized format. | ||
*/ | ||
Bitcoin.Address.prototype.toString = function () { | ||
// Get a copy of the hash | ||
var hash = this.hash.slice(0); | ||
// 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); | ||
}; | ||
/** | ||
* Parse a Bitcoin address contained in a string. | ||
*/ | ||
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; | ||
}; |
(function (Bitcoin) { | ||
Bitcoin.Base58 = { | ||
alphabet: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz", | ||
base: BigInteger.valueOf(58), | ||
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 = []; | ||
/** | ||
* 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; | ||
}, | ||
return chars.join(''); | ||
}, | ||
/** | ||
* 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) { | ||
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))); | ||
// 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; | ||
} | ||
}; | ||
@@ -65,0 +68,0 @@ var B58 = Bitcoin.Base58; |
@@ -14,14 +14,14 @@ (function (exports) { | ||
{ | ||
// 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); | ||
// 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}; | ||
return {priv: privateKey, pubkey: publicPoint}; | ||
}; | ||
@@ -31,27 +31,27 @@ | ||
{ | ||
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; | ||
}; | ||
@@ -68,14 +68,14 @@ | ||
{ | ||
// 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([]); | ||
} | ||
@@ -86,3 +86,3 @@ txTmp.ins[inIndex].script = Crypto.util.bytesToBase64(scriptCode); | ||
if ((hashType & 0x1f) == SIGHASH_NONE) { | ||
txTmp.outs = []; | ||
txTmp.outs = []; | ||
@@ -102,14 +102,14 @@ // Let the others update at will | ||
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); | ||
}; | ||
@@ -119,15 +119,15 @@ | ||
{ | ||
// 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])); | ||
} | ||
@@ -137,25 +137,25 @@ }; | ||
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==" | ||
}] | ||
}; | ||
@@ -165,19 +165,19 @@ | ||
$(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"))); | ||
}); | ||
// | ||
*/ |
502
src/ecdsa.js
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); | ||
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; | ||
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 (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.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(); | ||
} | ||
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 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); | ||
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); | ||
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(); | ||
@@ -112,10 +117,10 @@ | ||
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); | ||
} | ||
} | ||
@@ -138,2 +143,12 @@ | ||
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 () { | ||
@@ -174,139 +189,290 @@ var n = this.curve.getQ(); | ||
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(); | ||
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(); | ||
var P_OVER_FOUR = null; | ||
for (var i = m - 1; i >= 0; --i) { | ||
R = R.twice2D(); | ||
function implShamirsTrick(P, k, Q, l) | ||
{ | ||
var m = Math.max(k.bitLength(), l.bitLength()); | ||
var Z = P.add2D(Q); | ||
var R = P.curve.getInfinity(); | ||
R.z = BigInteger.ONE; | ||
for (var i = m - 1; i >= 0; --i) { | ||
R = R.twice2D(); | ||
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); | ||
} | ||
} | ||
} | ||
R.z = BigInteger.ONE; | ||
return R; | ||
}; | ||
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 ECDSA = { | ||
getBigRandom: function (limit) { | ||
return new BigInteger(limit.bitLength(), rng) | ||
.mod(limit.subtract(BigInteger.ONE)) | ||
.add(BigInteger.ONE) | ||
; | ||
}, | ||
sign: function (hash, priv) { | ||
var d = priv; | ||
var n = ecparams.getN(); | ||
var e = BigInteger.fromByteArrayUnsigned(hash); | ||
return R; | ||
}; | ||
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 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 s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n); | ||
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.toByteArrayUnsigned(); | ||
var sBa = s.toByteArrayUnsigned(); | ||
var rBa = r.toByteArraySigned(); | ||
var sBa = s.toByteArraySigned(); | ||
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) { | ||
var obj = ECDSA.parseSig(sig); | ||
var r = obj.r; | ||
var s = obj.s; | ||
/** | ||
* Parses a byte array containing a DER-encoded signature. | ||
* | ||
* This function will return an object of the form: | ||
* | ||
* { | ||
* r: BigInteger, | ||
* s: BigInteger | ||
* } | ||
*/ | ||
parseSig: function (sig) { | ||
var cursor; | ||
if (sig[0] != 0x30) | ||
throw new Error("Signature not a valid DERSequence"); | ||
var n = ecparams.getN(); | ||
var e = BigInteger.fromByteArrayUnsigned(hash); | ||
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]); | ||
if (r.compareTo(BigInteger.ONE) < 0 || | ||
r.compareTo(n) >= 0) | ||
return false; | ||
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]); | ||
if (s.compareTo(BigInteger.ONE) < 0 || | ||
s.compareTo(n) >= 0) | ||
return false; | ||
cursor += 2+sig[cursor+1]; | ||
var c = s.modInverse(n); | ||
//if (cursor != sig.length) | ||
// throw new Error("Extra bytes in signature"); | ||
var u1 = e.multiply(c).mod(n); | ||
var u2 = r.multiply(c).mod(n); | ||
var r = BigInteger.fromByteArrayUnsigned(rBa); | ||
var s = BigInteger.fromByteArrayUnsigned(sBa); | ||
var G = ecparams.getG(); | ||
var Q = ECPointFp.decodeFrom(ecparams.getCurve(), pubkey); | ||
return {r: r, s: s}; | ||
}, | ||
var point = implShamirsTrick(G, u1, Q, u2); | ||
parseSigCompact: function (sig) { | ||
if (sig.length !== 65) { | ||
throw "Signature has the wrong length"; | ||
} | ||
var v = point.x.toBigInteger().mod(n); | ||
// 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"; | ||
} | ||
return v.equals(r); | ||
}, | ||
var n = ecparams.getN(); | ||
var r = BigInteger.fromByteArrayUnsigned(sig.slice(1, 33)).mod(n); | ||
var s = BigInteger.fromByteArrayUnsigned(sig.slice(33, 65)).mod(n); | ||
parseSig: function (sig) { | ||
var cursor; | ||
if (sig[0] != 0x30) | ||
throw new Error("Signature not a valid DERSequence"); | ||
return {r: r, s: s, i: i}; | ||
}, | ||
cursor = 2; | ||
if (sig[cursor] != 0x02) | ||
throw new Error("First element in signature must be a DERInteger");; | ||
var rBa = sig.slice(cursor+2, cursor+2+sig[cursor+1]); | ||
/** | ||
* 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; | ||
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]); | ||
// The less significant bit specifies whether the y coordinate | ||
// of the compressed point is even or not. | ||
var isYEven = i & 1; | ||
cursor += 2+sig[cursor+1]; | ||
// The more significant bit specifies whether we should use the | ||
// first or second candidate key. | ||
var isSecondKey = i >> 1; | ||
//if (cursor != sig.length) | ||
// throw new Error("Extra bytes in signature"); | ||
var n = ecparams.getN(); | ||
var G = ecparams.getG(); | ||
var curve = ecparams.getCurve(); | ||
var p = curve.getQ(); | ||
var a = curve.getA().toBigInteger(); | ||
var b = curve.getB().toBigInteger(); | ||
var r = BigInteger.fromByteArrayUnsigned(rBa); | ||
var s = BigInteger.fromByteArrayUnsigned(sBa); | ||
// 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)); | ||
} | ||
return {r: r, s: s}; | ||
// 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; | ||
return ECDSA; | ||
})(); |
163
src/eckey.js
Bitcoin.ECKey = (function () { | ||
var ECDSA = Bitcoin.ECDSA; | ||
var ecparams = getSECCurveByName("secp256k1"); | ||
var rng = new SecureRandom(); | ||
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)); | ||
} | ||
}; | ||
var ECKey = function (input) { | ||
if (!input) { | ||
// Generate new key | ||
var n = ecparams.getN(); | ||
this.priv = ECDSA.getBigRandom(n); | ||
} else if (input instanceof BigInteger) { | ||
// Input is a private key value | ||
this.priv = input; | ||
} else if (Bitcoin.Util.isArray(input)) { | ||
// Prepend zero byte to prevent interpretation as negative integer | ||
this.priv = BigInteger.fromByteArrayUnsigned(input); | ||
} else if ("string" == typeof input) { | ||
if (input.length == 51 && input[0] == '5') { | ||
// Base58 encoded private key | ||
this.priv = BigInteger.fromByteArrayUnsigned(ECKey.decodeString(input)); | ||
} else { | ||
// Prepend zero byte to prevent interpretation as negative integer | ||
this.priv = BigInteger.fromByteArrayUnsigned(Crypto.util.base64ToBytes(input)); | ||
} | ||
} | ||
this.compressed = !!ECKey.compressByDefault; | ||
}; | ||
ECKey.prototype.getPub = function () { | ||
if (this.pub) return this.pub; | ||
/** | ||
* Whether public keys should be returned compressed by default. | ||
*/ | ||
ECKey.compressByDefault = false; | ||
return this.pub = ecparams.getG().multiply(this.priv).getEncoded(); | ||
}; | ||
/** | ||
* Set whether the public key should be returned compressed or not. | ||
*/ | ||
ECKey.prototype.setCompressed = function (v) { | ||
this.compressed = !!v; | ||
}; | ||
ECKey.prototype.getPubKeyHash = function () { | ||
if (this.pubKeyHash) return this.pubKeyHash; | ||
/** | ||
* Return public key in DER encoding. | ||
*/ | ||
ECKey.prototype.getPub = function () { | ||
return this.getPubPoint().getEncoded(this.compressed); | ||
}; | ||
return this.pubKeyHash = Bitcoin.Util.sha256ripe160(this.getPub()); | ||
}; | ||
/** | ||
* Return public point as ECPoint object. | ||
*/ | ||
ECKey.prototype.getPubPoint = function () { | ||
if (!this.pub) this.pub = ecparams.getG().multiply(this.priv); | ||
ECKey.prototype.getBitcoinAddress = function () { | ||
var hash = this.getPubKeyHash(); | ||
var addr = new Bitcoin.Address(hash); | ||
return addr; | ||
}; | ||
return this.pub; | ||
}; | ||
/** | ||
* Get the pubKeyHash for this key. | ||
* | ||
* This is calculated as RIPE160(SHA256([encoded pubkey])) and returned as | ||
* a byte array. | ||
*/ | ||
ECKey.prototype.getPubKeyHash = function () { | ||
if (this.pubKeyHash) return this.pubKeyHash; | ||
return this.pubKeyHash = Bitcoin.Util.sha256ripe160(this.getPub()); | ||
}; | ||
ECKey.prototype.getBitcoinAddress = function () { | ||
var hash = this.getPubKeyHash(); | ||
var addr = new Bitcoin.Address(hash); | ||
return addr; | ||
}; | ||
ECKey.prototype.getExportedPrivateKey = function () { | ||
var hash = this.priv.toByteArrayUnsigned(); | ||
while (hash.length < 32) hash.unshift(0); | ||
hash.unshift(0x80); | ||
var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true}); | ||
var bytes = hash.concat(checksum.slice(0,4)); | ||
return Bitcoin.Base58.encode(bytes); | ||
}; | ||
ECKey.prototype.setPub = function (pub) { | ||
this.pub = pub; | ||
this.pub = ECPointFp.decodeFrom(ecparams.getCurve(), 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; | ||
/** | ||
* Parse an exported private key contained in a string. | ||
*/ | ||
ECKey.decodeString = function (string) { | ||
var bytes = Bitcoin.Base58.decode(string); | ||
var hash = bytes.slice(0, 33); | ||
var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true}); | ||
if (checksum[0] != bytes[33] || | ||
checksum[1] != bytes[34] || | ||
checksum[2] != bytes[35] || | ||
checksum[3] != bytes[36]) { | ||
throw "Checksum validation failed!"; | ||
} | ||
var version = hash.shift(); | ||
if (version != 0x80) { | ||
throw "Version "+version+" not supported!"; | ||
} | ||
return hash; | ||
}; | ||
return ECKey; | ||
})(); |
@@ -10,8 +10,8 @@ (function (exports, Bitcoin, io, $, global) { | ||
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 = []; | ||
}; | ||
@@ -26,3 +26,3 @@ | ||
ExitNode.prototype.connect = function (wallet) { | ||
this.wallet = wallet; | ||
this.wallet = wallet; | ||
@@ -33,13 +33,13 @@ // Workaround for socket.io not properly allowing disconnecting and reconnecting | ||
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(); | ||
@@ -57,13 +57,13 @@ this.socket = null; | ||
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; | ||
@@ -74,17 +74,17 @@ 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("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}); | ||
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 | ||
// Pass on the newly downloaded transactions | ||
self.trigger('txData', { | ||
@@ -95,11 +95,11 @@ confirmed: true, | ||
// 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', { | ||
@@ -109,4 +109,4 @@ confirmed: false, | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}; | ||
@@ -116,12 +116,12 @@ | ||
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 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 request messages | ||
} else if ("undefined" !== typeof data.method) { | ||
// Create an event | ||
this.trigger(data.method, data.params[0]); | ||
} | ||
} | ||
}; | ||
@@ -131,3 +131,3 @@ | ||
ExitNode.prototype.handleDisconnect = function () { | ||
// TODO: Attempt reconnect (unless disconnect was intended) | ||
// TODO: Attempt reconnect (unless disconnect was intended) | ||
}; | ||
@@ -134,0 +134,0 @@ |
@@ -35,5 +35,2 @@ // Basic Javascript Elliptic Curve implementation | ||
function feFpSubtract(b) { | ||
/*console.log("b.y (int): ", Crypto.util.bytesToHex(this.x.toByteArrayUnsigned())); | ||
console.log("this.y (int): ", Crypto.util.bytesToHex(b.toBigInteger().toByteArrayUnsigned())); | ||
console.log("b.y-this.y (premod): ", Crypto.util.bytesToHex(this.x.subtract(b.toBigInteger()).toByteArrayUnsigned()));*/ | ||
return new ECFieldElementFp(this.q, this.x.subtract(b.toBigInteger()).mod(this.q)); | ||
@@ -51,3 +48,2 @@ } | ||
function feFpDivide(b) { | ||
//console.log("x: ", Crypto.util.bytesToHex(this.x.toByteArrayUnsigned())); | ||
return new ECFieldElementFp(this.q, this.x.multiply(b.toBigInteger().modInverse(this.q)).mod(this.q)); | ||
@@ -130,3 +126,2 @@ } | ||
if(BigInteger.ZERO.equals(v)) { | ||
@@ -133,0 +128,0 @@ if(BigInteger.ZERO.equals(u)) { |
@@ -215,3 +215,3 @@ // Copyright (c) 2005 Tom Wu | ||
r = i-a.t; | ||
if(r != 0) return r; | ||
if(r != 0) return (this.s<0)?-r:r; | ||
while(--i >= 0) if((r=this[i]-a[i]) != 0) return r; | ||
@@ -218,0 +218,0 @@ return 0; |
(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; | ||
} | ||
})(); |
@@ -0,3 +1,9 @@ | ||
/** | ||
* Implement the Paillier cryptosystem in JavaScript. | ||
* | ||
* Paillier is useful for multiparty calculation. It is not currently part of any | ||
* BitcoinJS-lib distribution, but it is included here for experimental use. | ||
*/ | ||
Bitcoin.Paillier = (function () { | ||
var rng = new SecureRandom(); | ||
var rng = new SecureRandom(); | ||
var TWO = BigInteger.valueOf(2); | ||
@@ -4,0 +10,0 @@ |
(function () { | ||
var Opcode = Bitcoin.Opcode; | ||
var Opcode = Bitcoin.Opcode; | ||
// Make opcodes available as pseudo-constants | ||
for (var i in Opcode.map) { | ||
eval("var " + i + " = " + Opcode.map[i] + ";"); | ||
} | ||
// 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"); | ||
} | ||
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(); | ||
}; | ||
this.parse(); | ||
}; | ||
Script.prototype.parse = function () { | ||
var self = this; | ||
/** | ||
* Update the parsed script representation. | ||
* | ||
* Each Script object stores the script in two formats. First as a raw byte | ||
* array and second as an array of "chunks", such as opcodes and pieces of | ||
* data. | ||
* | ||
* This method updates the chunks cache. Normally this is called by the | ||
* constructor and you don't need to worry about it. However, if you change | ||
* the script buffer manually, you should update the chunks using this method. | ||
*/ | ||
Script.prototype.parse = function () { | ||
var self = this; | ||
this.chunks = []; | ||
this.chunks = []; | ||
// Cursor | ||
var i = 0; | ||
// Cursor | ||
var i = 0; | ||
// Read n bytes and store result as a chunk | ||
function readChunk(n) { | ||
self.chunks.push(self.buffer.slice(i, i + n)); | ||
i += n; | ||
}; | ||
// Read n bytes and store result as a chunk | ||
function readChunk(n) { | ||
self.chunks.push(self.buffer.slice(i, i + n)); | ||
i += n; | ||
}; | ||
while (i < this.buffer.length) { | ||
var opcode = this.buffer[i++]; | ||
if (opcode >= 0xF0) { | ||
// Two byte opcode | ||
opcode = (opcode << 8) | this.buffer[i++]; | ||
} | ||
while (i < this.buffer.length) { | ||
var opcode = this.buffer[i++]; | ||
if (opcode >= 0xF0) { | ||
// Two byte opcode | ||
opcode = (opcode << 8) | this.buffer[i++]; | ||
} | ||
var len; | ||
if (opcode > 0 && opcode < OP_PUSHDATA1) { | ||
// Read some bytes of data, opcode value is the length of data | ||
readChunk(opcode); | ||
} else if (opcode == OP_PUSHDATA1) { | ||
len = this.buffer[i++]; | ||
readChunk(len); | ||
} else if (opcode == OP_PUSHDATA2) { | ||
len = (this.buffer[i++] << 8) | this.buffer[i++]; | ||
readChunk(len); | ||
} else if (opcode == OP_PUSHDATA4) { | ||
len = (this.buffer[i++] << 24) | | ||
(this.buffer[i++] << 16) | | ||
(this.buffer[i++] << 8) | | ||
this.buffer[i++]; | ||
readChunk(len); | ||
} else { | ||
this.chunks.push(opcode); | ||
} | ||
} | ||
}; | ||
var len; | ||
if (opcode > 0 && opcode < 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) { | ||
/** | ||
* 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 () { | ||
// Transfer to Bitcoin address | ||
return 'Address'; | ||
} else if (this.chunks.length == 2 && | ||
this.chunks[1] == OP_CHECKSIG) { | ||
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'; | ||
} | ||
} | ||
// Transfer to IP 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"); | ||
} | ||
}; | ||
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"); | ||
} | ||
}; | ||
/** | ||
* Old name for Script#simpleOutHash. | ||
* | ||
* @deprecated | ||
*/ | ||
Script.prototype.simpleOutPubKeyHash = Script.prototype.simpleOutHash; | ||
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"); | ||
} | ||
}; | ||
/** | ||
* 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.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"); | ||
} | ||
}; | ||
/** | ||
* 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"); | ||
} | ||
}; | ||
Script.prototype.simpleInPubKeyHash = function () | ||
{ | ||
return Bitcoin.Util.sha256ripe160(this.simpleInPubKey()); | ||
}; | ||
/** | ||
* Returns the affected address hash for this input. | ||
* | ||
* For standard transactions, this will return the hash of the pubKey that | ||
* can spend this output. | ||
* | ||
* In the future, for standard payToScriptHash inputs, this will return the | ||
* scriptHash. | ||
* | ||
* Note: This function provided for convenience. If you have the corresponding | ||
* scriptPubKey available, you are urged to use Script#simpleOutHash instead | ||
* as it is more reliable for non-standard payToScriptHash transactions. | ||
* | ||
* This method is useful for indexing transactions. | ||
*/ | ||
Script.prototype.simpleInHash = function () | ||
{ | ||
return Bitcoin.Util.sha256ripe160(this.simpleInPubKey()); | ||
}; | ||
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; | ||
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); | ||
}; | ||
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 < 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.createInputScript = function (signature, pubKey) | ||
{ | ||
var script = new Script(); | ||
script.writeBytes(signature); | ||
script.writeBytes(pubKey); | ||
return script; | ||
}; | ||
/** | ||
* Create a standard payToPubKeyHash output. | ||
*/ | ||
Script.createOutputScript = function (address) | ||
{ | ||
var script = new Script(); | ||
script.writeOp(OP_DUP); | ||
script.writeOp(OP_HASH160); | ||
script.writeBytes(address.hash); | ||
script.writeOp(OP_EQUALVERIFY); | ||
script.writeOp(OP_CHECKSIG); | ||
return script; | ||
}; | ||
/** | ||
* Extract bitcoin addresses from an output script | ||
*/ | ||
Script.prototype.extractAddresses = function (addresses) | ||
{ | ||
switch (this.getOutType()) { | ||
case 'Address': | ||
addresses.push(new Address(this.chunks[2])); | ||
return 1; | ||
case 'Pubkey': | ||
addresses.push(new Address(Util.sha256ripe160(this.chunks[0]))); | ||
return 1; | ||
case 'Multisig': | ||
for (var i = 1; i < this.chunks.length-2; ++i) { | ||
addresses.push(new Address(Util.sha256ripe160(this.chunks[i]))); | ||
} | ||
return this.chunks[0] - OP_1 + 1; | ||
default: | ||
throw new Error("Encountered non-standard scriptPubKey"); | ||
} | ||
}; | ||
Script.prototype.clone = function () | ||
{ | ||
return new Script(this.buffer); | ||
}; | ||
/** | ||
* 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(OP_CHECKMULTISIG); | ||
return script; | ||
}; | ||
/** | ||
* Create a standard payToPubKeyHash input. | ||
*/ | ||
Script.createInputScript = function (signature, pubKey) | ||
{ | ||
var script = new Script(); | ||
script.writeBytes(signature); | ||
script.writeBytes(pubKey); | ||
return script; | ||
}; | ||
Script.prototype.clone = function () | ||
{ | ||
return new Script(this.buffer); | ||
}; | ||
})(); |
(function () { | ||
var Script = Bitcoin.Script; | ||
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; | ||
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; | ||
} | ||
}; | ||
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; | ||
}; | ||
/** | ||
* 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; | ||
}; | ||
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 | ||
})); | ||
} | ||
}; | ||
/** | ||
* Create a new txin. | ||
* | ||
* Can be called with an existing TransactionIn object to add it to the | ||
* transaction. Or it can be called with a Transaction object and an integer | ||
* output index, in which case a new TransactionIn object pointing to the | ||
* referenced output will be created. | ||
* | ||
* Note that this method does not sign the created input. | ||
*/ | ||
Transaction.prototype.addInput = function (tx, outIndex) { | ||
if (arguments[0] instanceof TransactionIn) { | ||
this.ins.push(arguments[0]); | ||
} else { | ||
this.ins.push(new TransactionIn({ | ||
outpoint: { | ||
hash: tx.hash, | ||
index: outIndex | ||
}, | ||
script: new Bitcoin.Script(), | ||
sequence: 4294967295 | ||
})); | ||
} | ||
}; | ||
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 | ||
} | ||
/** | ||
* Create a new txout. | ||
* | ||
* Can be called with an existing TransactionOut object to add it to the | ||
* transaction. Or it can be called with an Address object and a BigInteger | ||
* for the amount, in which case a new TransactionOut object with those | ||
* values will be created. | ||
*/ | ||
Transaction.prototype.addOutput = function (address, value) { | ||
if (arguments[0] instanceof TransactionOut) { | ||
this.outs.push(arguments[0]); | ||
} else { | ||
if (value instanceof BigInteger) { | ||
value = value.toByteArrayUnsigned().reverse(); | ||
while (value.length < 8) value.push(0); | ||
} else if (Bitcoin.Util.isArray(value)) { | ||
// Nothing to do | ||
} | ||
this.outs.push(new TransactionOut({ | ||
value: value, | ||
script: Script.createOutputScript(address) | ||
})); | ||
} | ||
}; | ||
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()); | ||
/** | ||
* Serialize this transaction. | ||
* | ||
* Returns the transaction as a byte array in the standard Bitcoin binary | ||
* format. This method is byte-perfect, i.e. the resulting byte array can | ||
* be hashed to get the transaction's standard Bitcoin hash. | ||
*/ | ||
Transaction.prototype.serialize = function () | ||
{ | ||
var buffer = []; | ||
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(this.version)]).reverse()); | ||
buffer = buffer.concat(Bitcoin.Util.numToVarInt(this.ins.length)); | ||
for (var i = 0; i < this.ins.length; i++) { | ||
var txin = this.ins[i]; | ||
buffer = buffer.concat(Crypto.util.base64ToBytes(txin.outpoint.hash)); | ||
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(txin.outpoint.index)]).reverse()); | ||
var scriptBytes = txin.script.buffer; | ||
buffer = buffer.concat(Bitcoin.Util.numToVarInt(scriptBytes.length)); | ||
buffer = buffer.concat(scriptBytes); | ||
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(txin.sequence)]).reverse()); | ||
} | ||
buffer = buffer.concat(Bitcoin.Util.numToVarInt(this.outs.length)); | ||
for (var i = 0; i < this.outs.length; i++) { | ||
var txout = this.outs[i]; | ||
buffer = buffer.concat(txout.value); | ||
var scriptBytes = txout.script.buffer; | ||
buffer = buffer.concat(Bitcoin.Util.numToVarInt(scriptBytes.length)); | ||
buffer = buffer.concat(scriptBytes); | ||
} | ||
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(this.lock_time)]).reverse()); | ||
return buffer; | ||
}; | ||
return buffer; | ||
}; | ||
var OP_CODESEPARATOR = 171; | ||
var OP_CODESEPARATOR = 171; | ||
var SIGHASH_ALL = 1; | ||
var SIGHASH_NONE = 2; | ||
var SIGHASH_SINGLE = 3; | ||
var SIGHASH_ANYONECANPAY = 80; | ||
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(); | ||
/** | ||
* Hash transaction for signing a specific input. | ||
* | ||
* Bitcoin uses a different hash for each signed transaction input. This | ||
* method copies the transaction, makes the necessary changes based on the | ||
* hashType, serializes and finally hashes the result. This hash can then be | ||
* used to sign the transaction input in question. | ||
*/ | ||
Transaction.prototype.hashTransactionForSignature = | ||
function (connectedScript, inIndex, hashType) | ||
{ | ||
var txTmp = this.clone(); | ||
// In case concatenating two scripts ends up with two codeseparators, | ||
// or an extra one at the end, this prevents all those possible incompatibilities. | ||
/*scriptCode = scriptCode.filter(function (val) { | ||
return val !== OP_CODESEPARATOR; | ||
});*/ | ||
// 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(); | ||
} | ||
// 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; | ||
txTmp.ins[inIndex].script = connectedScript; | ||
// Blank out some of the outputs | ||
if ((hashType & 0x1f) == SIGHASH_NONE) { | ||
txTmp.outs = []; | ||
// 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 | ||
} | ||
// 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]]; | ||
} | ||
// 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)); | ||
var hash1 = Crypto.SHA256(buffer, {asBytes: true}); | ||
var hash1 = Crypto.SHA256(buffer, {asBytes: true}); | ||
return Crypto.SHA256(hash1, {asBytes: true}); | ||
}; | ||
console.log("sha256_1: ", Crypto.util.bytesToHex(hash1)); | ||
/** | ||
* 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}); | ||
}; | ||
return Crypto.SHA256(hash1, {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; | ||
}; | ||
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; | ||
}; | ||
/** | ||
* 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; | ||
Transaction.prototype.analyze = function (wallet) { | ||
if (!(wallet instanceof Bitcoin.Wallet)) return null; | ||
var allFromMe = true, | ||
allToMe = true, | ||
firstRecvHash = null, | ||
firstMeRecvHash = null, | ||
firstSendHash = 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; | ||
} | ||
} | ||
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 impact = this.calcImpact(wallet); | ||
@@ -213,13 +277,16 @@ var analysis = {}; | ||
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'; | ||
// 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"; | ||
} | ||
} else { | ||
analysis.type = "other"; | ||
} | ||
@@ -229,3 +296,9 @@ return analysis; | ||
Transaction.prototype.getDescription = function (wallet) { | ||
/** | ||
* 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); | ||
@@ -241,7 +314,7 @@ | ||
case 'sent': | ||
return "Payment to "+analysis.addr; | ||
return "Payment to "+analysis.addr; | ||
break; | ||
case 'self': | ||
return "Payment to yourself"; | ||
return "Payment to yourself"; | ||
break; | ||
@@ -253,114 +326,124 @@ | ||
} | ||
}; | ||
}; | ||
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; | ||
}; | ||
/** | ||
* Get the total amount of a transaction's outputs. | ||
*/ | ||
Transaction.prototype.getTotalOutValue = function () { | ||
var totalValue = BigInteger.ZERO; | ||
for (var j = 0; j < this.outs.length; j++) { | ||
var txout = this.outs[j]; | ||
totalValue = totalValue.add(Bitcoin.Util.valueToBigInt(txout.value)); | ||
} | ||
return totalValue; | ||
}; | ||
/** | ||
* 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; | ||
/** | ||
* Old name for Transaction#getTotalOutValue. | ||
* | ||
* @deprecated | ||
*/ | ||
Transaction.prototype.getTotalValue = Transaction.prototype.getTotalOutValue; | ||
// 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)); | ||
} | ||
} | ||
/** | ||
* 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 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) | ||
}; | ||
} | ||
}; | ||
// 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 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; | ||
}; | ||
// 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) | ||
}; | ||
} | ||
}; | ||
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 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; | ||
}; | ||
var TransactionOut = Bitcoin.TransactionOut = function (data) | ||
{ | ||
if (data.script instanceof Script) { | ||
this.script = data.script; | ||
} else { | ||
this.script = new Script(data.script); | ||
} | ||
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; | ||
}; | ||
if (Bitcoin.Util.isArray(data.value)) { | ||
this.value = data.value; | ||
} else if ("string" == typeof data.value) { | ||
var valueHex = (new BigInteger(data.value, 10)).toString(16); | ||
while (valueHex.length < 16) valueHex = "0" + valueHex; | ||
this.value = Crypto.util.hexToBytes(valueHex); | ||
} | ||
}; | ||
var TransactionOut = Bitcoin.TransactionOut = function (data) | ||
{ | ||
if (data.script instanceof Script) { | ||
this.script = data.script; | ||
} else { | ||
this.script = new Script(data.script); | ||
} | ||
TransactionOut.prototype.clone = function () | ||
{ | ||
var newTxout = new TransactionOut({ | ||
script: this.script.clone(), | ||
value: this.value.slice(0) | ||
}); | ||
return newTxout; | ||
}; | ||
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; | ||
}; | ||
})(); | ||
var TransactionDatabase = function () { | ||
this.txs = []; | ||
this.txIndex = {}; | ||
this.txs = []; | ||
this.txIndex = {}; | ||
}; | ||
@@ -9,49 +9,49 @@ | ||
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; | ||
}; | ||
@@ -58,0 +58,0 @@ |
279
src/util.js
// BigInteger monkey patching | ||
BigInteger.valueOf = nbv; | ||
/** | ||
* 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.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.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); | ||
} | ||
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"]; | ||
"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; | ||
/** | ||
* Cross-browser compatibility version of Array.isArray. | ||
*/ | ||
isArray: Array.isArray || function(o) | ||
{ | ||
return Object.prototype.toString.call(o) === '[object Array]'; | ||
}, | ||
// 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; | ||
}, | ||
/** | ||
* 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) { | ||
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))); | ||
// 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; | ||
}, | ||
sha256ripe160: function (data) { | ||
return Crypto.RIPEMD160(Crypto.SHA256(data, {asBytes: true}), {asBytes: true}); | ||
} | ||
/** | ||
* 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}); | ||
} | ||
}; | ||
@@ -98,0 +223,0 @@ |
Bitcoin.Wallet = (function () { | ||
var Script = Bitcoin.Script, | ||
TransactionIn = Bitcoin.TransactionIn, | ||
TransactionOut = Bitcoin.TransactionOut; | ||
var Script = Bitcoin.Script, | ||
TransactionIn = Bitcoin.TransactionIn, | ||
TransactionOut = Bitcoin.TransactionOut; | ||
var Wallet = function () { | ||
// Keychain | ||
var keys = []; | ||
this.addressHashes = []; | ||
var Wallet = function () { | ||
// Keychain | ||
// | ||
// The keychain is stored as a var in this closure to make accidental | ||
// serialization less likely. | ||
// | ||
// Any functions accessing this value therefore have to be defined in | ||
// the closure of this constructor. | ||
var keys = []; | ||
// Transaction data | ||
this.txIndex = {}; | ||
this.unspentOuts = []; | ||
// Public hashes of our keys | ||
this.addressHashes = []; | ||
// Other fields | ||
this.addressPointer = 0; | ||
// Transaction data | ||
this.txIndex = {}; | ||
this.unspentOuts = []; | ||
this.addKey = function (key, pub) { | ||
if (!(key instanceof Bitcoin.ECKey)) { | ||
key = new Bitcoin.ECKey(key); | ||
} | ||
keys.push(key); | ||
// 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) { | ||
@@ -28,9 +43,12 @@ if ("string" === typeof pub) { | ||
} | ||
key.pub = pub; | ||
key.setPub(pub); | ||
} | ||
this.addressHashes.push(key.getBitcoinAddress().getHashBase64()); | ||
}; | ||
this.addressHashes.push(key.getBitcoinAddress().getHashBase64()); | ||
}; | ||
this.addKeys = function (keys, pubs) { | ||
/** | ||
* Add multiple keys at once. | ||
*/ | ||
this.addKeys = function (keys, pubs) { | ||
if ("string" === typeof keys) { | ||
@@ -42,38 +60,54 @@ keys = keys.split(','); | ||
} | ||
console.log(pubs); | ||
var i; | ||
if (Array.isArray(pubs) && keys.length == pubs.length) { | ||
for (var i = 0; i < keys.length; i++) { | ||
this.addKey(keys[i], pubs[i]); | ||
for (i = 0; i < keys.length; i++) { | ||
this.addKey(keys[i], pubs[i]); | ||
} | ||
} else { | ||
for (var i = 0; i < keys.length; i++) { | ||
this.addKey(keys[i]); | ||
for (i = 0; i < keys.length; i++) { | ||
this.addKey(keys[i]); | ||
} | ||
} | ||
}; | ||
}; | ||
this.getKeys = function () { | ||
var serializedWallet = []; | ||
/** | ||
* 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')); | ||
} | ||
for (var i = 0; i < keys.length; i++) { | ||
serializedWallet.push(keys[i].toString('base64')); | ||
} | ||
return serializedWallet; | ||
}; | ||
return serializedWallet; | ||
}; | ||
/** | ||
* Get the public keys. | ||
* | ||
* Returns an array of base64-encoded public keys. | ||
*/ | ||
this.getPubKeys = function () { | ||
var pubs = []; | ||
var pubs = []; | ||
for (var i = 0; i < keys.length; i++) { | ||
pubs.push(Crypto.util.bytesToBase64(keys[i].getPub())); | ||
} | ||
for (var i = 0; i < keys.length; i++) { | ||
pubs.push(Crypto.util.bytesToBase64(keys[i].getPub())); | ||
} | ||
return pubs; | ||
return pubs; | ||
}; | ||
this.clear = function () { | ||
keys = []; | ||
}; | ||
/** | ||
* Delete all keys. | ||
*/ | ||
this.clear = function () { | ||
keys = []; | ||
}; | ||
/** | ||
* Return the number of keys in this wallet. | ||
*/ | ||
this.getLength = function () { | ||
@@ -83,110 +117,140 @@ return keys.length; | ||
this.getAllAddresses = function () { | ||
var addresses = []; | ||
for (var i = 0; i < keys.length; i++) { | ||
addresses.push(keys[i].getBitcoinAddress()); | ||
} | ||
return addresses; | ||
}; | ||
/** | ||
* 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; | ||
} | ||
}; | ||
this.getCurAddress = function () { | ||
if (keys[this.addressPointer]) { | ||
return keys[this.addressPointer].getBitcoinAddress(); | ||
} else { | ||
return null; | ||
} | ||
}; | ||
this.getNextAddress = function () { | ||
if (keys.length) { | ||
// TODO: Create new addresses if we run out | ||
this.addressPointer = (this.addressPointer + 1) % keys.length; | ||
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(); | ||
}; | ||
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"); | ||
}; | ||
/** | ||
* 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"); | ||
}; | ||
this.getPubKeyFromHash = function (pubKeyHash) { | ||
pubKeyHash = Crypto.util.bytesToBase64(pubKeyHash); | ||
for (var i = 0; i < this.addressHashes.length; i++) { | ||
if (this.addressHashes[i] == pubKeyHash) { | ||
console.log(Crypto.util.bytesToBase64(Bitcoin.Util.sha256ripe160(keys[i].getPub())), pubKeyHash); | ||
return keys[i].getPub(); | ||
} | ||
} | ||
throw new Error("Hash unknown"); | ||
}; | ||
}; | ||
/** | ||
* 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()); | ||
}; | ||
Wallet.prototype.generateAddress = function () { | ||
this.addKey(new Bitcoin.ECKey()); | ||
}; | ||
Wallet.prototype.process = function (tx) { | ||
if (this.txIndex[tx.hash]) return; | ||
/** | ||
* 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; | ||
// Gather outputs | ||
for (var j = 0; j < tx.outs.length; j++) { | ||
var txout = new TransactionOut(tx.outs[j]); | ||
var hash = Crypto.util.bytesToBase64(txout.script.simpleOutPubKeyHash()); | ||
for (var k = 0; k < this.addressHashes.length; k++) { | ||
if (this.addressHashes[k] === hash) { | ||
this.unspentOuts.push({tx: tx, index: j, out: txout}); | ||
break; | ||
} | ||
} | ||
} | ||
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 (var j = 0; j < tx.ins.length; j++) { | ||
var txin = new TransactionIn(tx.ins[j]); | ||
var pubkey = txin.script.simpleInPubKey(); | ||
var hash = Crypto.util.bytesToBase64(Bitcoin.Util.sha256ripe160(pubkey)); | ||
for (var 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; | ||
} | ||
} | ||
} | ||
// 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; | ||
}; | ||
// 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.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 = []; | ||
Wallet.prototype.createSend = function (address, sendValue, feeValue) { | ||
var selectedOuts = []; | ||
var txValue = sendValue.add(feeValue); | ||
var availableValue = BigInteger.ZERO; | ||
for (var i = 0; i < this.unspentOuts.length; i++) { | ||
selectedOuts.push(this.unspentOuts[i]); | ||
availableValue = availableValue.add(Bitcoin.Util.valueToBigInt(this.unspentOuts[i].out.value)); | ||
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) break; | ||
} | ||
@@ -197,37 +261,32 @@ if (availableValue.compareTo(txValue) < 0) { | ||
console.log(selectedOuts); | ||
var changeValue = availableValue.subtract(txValue); | ||
var changeValue = availableValue.subtract(txValue); | ||
var sendTx = new Bitcoin.Transaction(); | ||
var sendTx = new Bitcoin.Transaction(); | ||
for (var i = 0; i < selectedOuts.length; i++) { | ||
sendTx.addInput(selectedOuts[i].tx, selectedOuts[i].index); | ||
} | ||
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); | ||
} | ||
sendTx.addOutput(address, sendValue); | ||
if (changeValue.compareTo(BigInteger.ZERO) > 0) { | ||
sendTx.addOutput(this.getNextAddress(), changeValue); | ||
} | ||
var hashType = 1; // SIGHASH_ALL | ||
var hashType = 1; // SIGHASH_ALL | ||
for (var 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); | ||
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)); | ||
// Append hash type | ||
signature.push(parseInt(hashType, 10)); | ||
sendTx.ins[i].script = Script.createInputScript(signature, this.getPubKeyFromHash(pubKeyHash)); | ||
} | ||
sendTx.ins[i].script = Script.createInputScript(signature, this.getPubKeyFromHash(pubKeyHash)); | ||
} | ||
console.log(sendTx); | ||
return sendTx; | ||
}; | ||
console.log("pubkey: "+Crypto.util.bytesToHex(this.getPubKeyFromHash(pubKeyHash))); | ||
return sendTx; | ||
}; | ||
Wallet.prototype.clearTransactions = function () { | ||
@@ -238,17 +297,17 @@ this.txIndex = {}; | ||
/** | ||
* 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); | ||
/** | ||
* 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; | ||
}; | ||
// 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; | ||
return Wallet; | ||
})(); | ||
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
289344
40
6693
32
3