Fixed indents.
This commit is contained in:
parent
a5f9afde5a
commit
1a7fc9d063
13 changed files with 1343 additions and 1343 deletions
|
@ -1,49 +1,49 @@
|
||||||
Bitcoin.Address = function (bytes) {
|
Bitcoin.Address = function (bytes) {
|
||||||
if ("string" == typeof bytes) {
|
if ("string" == typeof bytes) {
|
||||||
bytes = Bitcoin.Address.decodeString(bytes);
|
bytes = Bitcoin.Address.decodeString(bytes);
|
||||||
}
|
}
|
||||||
this.hash = bytes;
|
this.hash = bytes;
|
||||||
|
|
||||||
this.version = 0x00;
|
this.version = 0x00;
|
||||||
};
|
};
|
||||||
|
|
||||||
Bitcoin.Address.prototype.toString = function () {
|
Bitcoin.Address.prototype.toString = function () {
|
||||||
// Get a copy of the hash
|
// Get a copy of the hash
|
||||||
var hash = this.hash.slice(0);
|
var hash = this.hash.slice(0);
|
||||||
|
|
||||||
// Version
|
// Version
|
||||||
hash.unshift(this.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 () {
|
Bitcoin.Address.prototype.getHashBase64 = function () {
|
||||||
return Crypto.util.bytesToBase64(this.hash);
|
return Crypto.util.bytesToBase64(this.hash);
|
||||||
};
|
};
|
||||||
|
|
||||||
Bitcoin.Address.decodeString = function (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] ||
|
if (checksum[0] != bytes[21] ||
|
||||||
checksum[1] != bytes[22] ||
|
checksum[1] != bytes[22] ||
|
||||||
checksum[2] != bytes[23] ||
|
checksum[2] != bytes[23] ||
|
||||||
checksum[3] != bytes[24]) {
|
checksum[3] != bytes[24]) {
|
||||||
throw "Checksum validation failed!";
|
throw "Checksum validation failed!";
|
||||||
}
|
}
|
||||||
|
|
||||||
var version = hash.shift();
|
var version = hash.shift();
|
||||||
|
|
||||||
if (version != 0) {
|
if (version != 0) {
|
||||||
throw "Version "+version+" not supported!";
|
throw "Version "+version+" not supported!";
|
||||||
}
|
}
|
||||||
|
|
||||||
return hash;
|
return hash;
|
||||||
};
|
};
|
||||||
|
|
108
src/base58.js
108
src/base58.js
|
@ -1,66 +1,66 @@
|
||||||
(function (Bitcoin) {
|
(function (Bitcoin) {
|
||||||
Bitcoin.Base58 = {
|
Bitcoin.Base58 = {
|
||||||
alphabet: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",
|
alphabet: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",
|
||||||
base: BigInteger.valueOf(58),
|
base: BigInteger.valueOf(58),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a byte array to a base58-encoded string.
|
* Convert a byte array to a base58-encoded string.
|
||||||
*
|
*
|
||||||
* Written by Mike Hearn for BitcoinJ.
|
* Written by Mike Hearn for BitcoinJ.
|
||||||
* Copyright (c) 2011 Google Inc.
|
* Copyright (c) 2011 Google Inc.
|
||||||
*
|
*
|
||||||
* Ported to JavaScript by Stefan Thomas.
|
* Ported to JavaScript by Stefan Thomas.
|
||||||
*/
|
*/
|
||||||
encode: function (input) {
|
encode: function (input) {
|
||||||
var bi = BigInteger.fromByteArrayUnsigned(input);
|
var bi = BigInteger.fromByteArrayUnsigned(input);
|
||||||
var chars = [];
|
var chars = [];
|
||||||
|
|
||||||
while (bi.compareTo(B58.base) >= 0) {
|
while (bi.compareTo(B58.base) >= 0) {
|
||||||
var mod = bi.mod(B58.base);
|
var mod = bi.mod(B58.base);
|
||||||
chars.unshift(B58.alphabet[mod.intValue()]);
|
chars.unshift(B58.alphabet[mod.intValue()]);
|
||||||
bi = bi.subtract(mod).divide(B58.base);
|
bi = bi.subtract(mod).divide(B58.base);
|
||||||
}
|
}
|
||||||
chars.unshift(B58.alphabet[bi.intValue()]);
|
chars.unshift(B58.alphabet[bi.intValue()]);
|
||||||
|
|
||||||
// Convert leading zeros too.
|
// Convert leading zeros too.
|
||||||
for (var i = 0; i < input.length; i++) {
|
for (var i = 0; i < input.length; i++) {
|
||||||
if (input[i] == 0x00) {
|
if (input[i] == 0x00) {
|
||||||
chars.unshift(B58.alphabet[0]);
|
chars.unshift(B58.alphabet[0]);
|
||||||
} else break;
|
} else break;
|
||||||
}
|
}
|
||||||
|
|
||||||
s = chars.join('');
|
s = chars.join('');
|
||||||
return s;
|
return s;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a base58-encoded string to a byte array.
|
* Convert a base58-encoded string to a byte array.
|
||||||
*
|
*
|
||||||
* Written by Mike Hearn for BitcoinJ.
|
* Written by Mike Hearn for BitcoinJ.
|
||||||
* Copyright (c) 2011 Google Inc.
|
* Copyright (c) 2011 Google Inc.
|
||||||
*
|
*
|
||||||
* Ported to JavaScript by Stefan Thomas.
|
* Ported to JavaScript by Stefan Thomas.
|
||||||
*/
|
*/
|
||||||
decode: function (input) {
|
decode: function (input) {
|
||||||
bi = BigInteger.valueOf(0);
|
bi = BigInteger.valueOf(0);
|
||||||
var leadingZerosNum = 0;
|
var leadingZerosNum = 0;
|
||||||
for (var i = input.length - 1; i >= 0; i--) {
|
for (var i = input.length - 1; i >= 0; i--) {
|
||||||
var alphaIndex = B58.alphabet.indexOf(input[i]);
|
var alphaIndex = B58.alphabet.indexOf(input[i]);
|
||||||
bi = bi.add(BigInteger.valueOf(alphaIndex)
|
bi = bi.add(BigInteger.valueOf(alphaIndex)
|
||||||
.multiply(B58.base.pow(input.length - 1 -i)));
|
.multiply(B58.base.pow(input.length - 1 -i)));
|
||||||
|
|
||||||
// This counts leading zero bytes
|
// This counts leading zero bytes
|
||||||
if (input[i] == "1") leadingZerosNum++;
|
if (input[i] == "1") leadingZerosNum++;
|
||||||
else leadingZerosNum = 0;
|
else leadingZerosNum = 0;
|
||||||
}
|
}
|
||||||
var bytes = bi.toByteArrayUnsigned();
|
var bytes = bi.toByteArrayUnsigned();
|
||||||
|
|
||||||
// Add leading zeros
|
// Add leading zeros
|
||||||
while (leadingZerosNum-- > 0) bytes.unshift(0);
|
while (leadingZerosNum-- > 0) bytes.unshift(0);
|
||||||
|
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var B58 = Bitcoin.Base58;
|
var B58 = Bitcoin.Base58;
|
||||||
})(
|
})(
|
||||||
|
|
186
src/bitcoin.js
186
src/bitcoin.js
|
@ -11,47 +11,47 @@
|
||||||
/*
|
/*
|
||||||
function makeKeypair()
|
function makeKeypair()
|
||||||
{
|
{
|
||||||
// Generate private key
|
// Generate private key
|
||||||
var n = ecparams.getN();
|
var n = ecparams.getN();
|
||||||
var n1 = n.subtract(BigInteger.ONE);
|
var n1 = n.subtract(BigInteger.ONE);
|
||||||
var r = new BigInteger(n.bitLength(), rng);
|
var r = new BigInteger(n.bitLength(), rng);
|
||||||
|
|
||||||
var privateKey = r.mod(n1).add(BigInteger.ONE);
|
var privateKey = r.mod(n1).add(BigInteger.ONE);
|
||||||
|
|
||||||
// Generate public key
|
// Generate public key
|
||||||
var G = ecparams.getG();
|
var G = ecparams.getG();
|
||||||
var publicPoint = G.multiply(privateKey);
|
var publicPoint = G.multiply(privateKey);
|
||||||
|
|
||||||
return {priv: privateKey, pubkey: publicPoint};
|
return {priv: privateKey, pubkey: publicPoint};
|
||||||
};
|
};
|
||||||
|
|
||||||
function serializeTransaction(tx)
|
function serializeTransaction(tx)
|
||||||
{
|
{
|
||||||
var buffer = [];
|
var buffer = [];
|
||||||
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(tx.version)]));
|
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(tx.version)]));
|
||||||
buffer = buffer.concat(numToVarInt(tx.ins.length));
|
buffer = buffer.concat(numToVarInt(tx.ins.length));
|
||||||
for (var i = 0; i < tx.ins.length; i++) {
|
for (var i = 0; i < tx.ins.length; i++) {
|
||||||
var txin = tx.ins[i];
|
var txin = tx.ins[i];
|
||||||
buffer = buffer.concat(Crypto.util.base64ToBytes(txin.outpoint.hash));
|
buffer = buffer.concat(Crypto.util.base64ToBytes(txin.outpoint.hash));
|
||||||
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(txin.index)]));
|
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(txin.index)]));
|
||||||
var scriptBytes = Crypto.util.base64ToBytes(txin.script);
|
var scriptBytes = Crypto.util.base64ToBytes(txin.script);
|
||||||
buffer = buffer.concat(numToVarInt(scriptBytes.length));
|
buffer = buffer.concat(numToVarInt(scriptBytes.length));
|
||||||
buffer = buffer.concat(scriptBytes);
|
buffer = buffer.concat(scriptBytes);
|
||||||
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(txin.sequence)]));
|
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(txin.sequence)]));
|
||||||
}
|
}
|
||||||
buffer = buffer.concat(numToVarInt(tx.outs.length));
|
buffer = buffer.concat(numToVarInt(tx.outs.length));
|
||||||
for (var i = 0; i < tx.outs.length; i++) {
|
for (var i = 0; i < tx.outs.length; i++) {
|
||||||
var txout = tx.outs[i];
|
var txout = tx.outs[i];
|
||||||
var valueHex = (new BigInteger(txout.value, 10)).toString(16);
|
var valueHex = (new BigInteger(txout.value, 10)).toString(16);
|
||||||
while (valueHex.length < 16) valueHex = "0" + valueHex;
|
while (valueHex.length < 16) valueHex = "0" + valueHex;
|
||||||
buffer = buffer.concat(Crypto.util.hexToBytes(valueHex));
|
buffer = buffer.concat(Crypto.util.hexToBytes(valueHex));
|
||||||
var scriptBytes = Crypto.util.base64ToBytes(txout.script);
|
var scriptBytes = Crypto.util.base64ToBytes(txout.script);
|
||||||
buffer = buffer.concat(numToVarInt(scriptBytes.length));
|
buffer = buffer.concat(numToVarInt(scriptBytes.length));
|
||||||
buffer = buffer.concat(scriptBytes);
|
buffer = buffer.concat(scriptBytes);
|
||||||
}
|
}
|
||||||
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(tx.lock_time)]));
|
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(tx.lock_time)]));
|
||||||
|
|
||||||
return buffer;
|
return buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
var OP_CODESEPARATOR = 171;
|
var OP_CODESEPARATOR = 171;
|
||||||
|
@ -63,24 +63,24 @@
|
||||||
|
|
||||||
function hashTransactionForSignature(scriptCode, tx, inIndex, hashType)
|
function hashTransactionForSignature(scriptCode, tx, inIndex, hashType)
|
||||||
{
|
{
|
||||||
// TODO: We need to actually deep copy here
|
// TODO: We need to actually deep copy here
|
||||||
var txTmp = tx;
|
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.
|
// or an extra one at the end, this prevents all those possible incompatibilities.
|
||||||
scriptCode = scriptCode.filter(function (val) {
|
scriptCode = scriptCode.filter(function (val) {
|
||||||
return val !== OP_CODESEPARATOR;
|
return val !== OP_CODESEPARATOR;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Blank out other inputs' signatures
|
// Blank out other inputs' signatures
|
||||||
for (var i = 0; i < txTmp.ins.length; i++) {
|
for (var i = 0; i < txTmp.ins.length; i++) {
|
||||||
txTmp.ins[i].script = Crypto.util.bytesToBase64([]);
|
txTmp.ins[i].script = Crypto.util.bytesToBase64([]);
|
||||||
}
|
}
|
||||||
txTmp.ins[inIndex].script = Crypto.util.bytesToBase64(scriptCode);
|
txTmp.ins[inIndex].script = Crypto.util.bytesToBase64(scriptCode);
|
||||||
|
|
||||||
// Blank out some of the outputs
|
// Blank out some of the outputs
|
||||||
if ((hashType & 0x1f) == SIGHASH_NONE) {
|
if ((hashType & 0x1f) == SIGHASH_NONE) {
|
||||||
txTmp.outs = [];
|
txTmp.outs = [];
|
||||||
|
|
||||||
// Let the others update at will
|
// Let the others update at will
|
||||||
for (var i = 0; i < txTmp.ins.length; i++)
|
for (var i = 0; i < txTmp.ins.length; i++)
|
||||||
|
@ -95,81 +95,81 @@
|
||||||
txTmp.ins = [txTmp.ins[inIndex]];
|
txTmp.ins = [txTmp.ins[inIndex]];
|
||||||
}
|
}
|
||||||
|
|
||||||
var buffer = serializeTransaction(txTmp);
|
var buffer = serializeTransaction(txTmp);
|
||||||
|
|
||||||
buffer.concat(Crypto.util.wordsToBytes([parseInt(hashType)]));
|
buffer.concat(Crypto.util.wordsToBytes([parseInt(hashType)]));
|
||||||
|
|
||||||
console.log(buffer);
|
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) {
|
function verifyTransactionSignature(tx) {
|
||||||
var hash = hashTransactionForSignature([], tx, 0, 0);
|
var hash = hashTransactionForSignature([], tx, 0, 0);
|
||||||
return Crypto.util.bytesToHex(hash);
|
return Crypto.util.bytesToHex(hash);
|
||||||
};
|
};
|
||||||
|
|
||||||
function numToVarInt(i)
|
function numToVarInt(i)
|
||||||
{
|
{
|
||||||
// TODO: THIS IS TOTALLY UNTESTED!
|
// TODO: THIS IS TOTALLY UNTESTED!
|
||||||
if (i < 0xfd) {
|
if (i < 0xfd) {
|
||||||
// unsigned char
|
// unsigned char
|
||||||
return [i];
|
return [i];
|
||||||
} else if (i <= 1<<16) {
|
} else if (i <= 1<<16) {
|
||||||
// unsigned short (LE)
|
// unsigned short (LE)
|
||||||
return [0xfd, i >>> 8, i & 255];
|
return [0xfd, i >>> 8, i & 255];
|
||||||
} else if (i <= 1<<32) {
|
} else if (i <= 1<<32) {
|
||||||
// unsigned int (LE)
|
// unsigned int (LE)
|
||||||
return [0xfe].concat(Crypto.util.wordsToBytes([i]));
|
return [0xfe].concat(Crypto.util.wordsToBytes([i]));
|
||||||
} else {
|
} else {
|
||||||
// unsigned long long (LE)
|
// unsigned long long (LE)
|
||||||
return [0xff].concat(Crypto.util.wordsToBytes([i >>> 32, i]));
|
return [0xff].concat(Crypto.util.wordsToBytes([i >>> 32, i]));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var testTx = {
|
var testTx = {
|
||||||
"version":"1",
|
"version":"1",
|
||||||
"lock_time":"0",
|
"lock_time":"0",
|
||||||
"block": {
|
"block": {
|
||||||
"hash":"N/A",
|
"hash":"N/A",
|
||||||
"height":115806
|
"height":115806
|
||||||
},
|
},
|
||||||
"index":6,
|
"index":6,
|
||||||
"hash":"WUFzjKubG1kqfJWMb4qZdlhU2F3l5NGXN7AUg8Jwl14=",
|
"hash":"WUFzjKubG1kqfJWMb4qZdlhU2F3l5NGXN7AUg8Jwl14=",
|
||||||
"ins":[{
|
"ins":[{
|
||||||
"outpoint":{
|
"outpoint":{
|
||||||
"hash":"nqcbMM1oRhfLdZga11q7x0CpUMujm+vtxHXO9V0gnwE=",
|
"hash":"nqcbMM1oRhfLdZga11q7x0CpUMujm+vtxHXO9V0gnwE=",
|
||||||
"index":0
|
"index":0
|
||||||
},
|
},
|
||||||
"script":"RzBEAiB2XXkx1pca9SlfCmCGNUVf+h2sAFBttcxG1VnypIcvEgIgXrOp7LSdYBYp3nPsQAz8BOLD3K4pAlXfZImP1rkzk2EBQQRi7NcODzNfnVqLtG79Axp5UF6EhFIhCmzqKqssfKpfCIOmzCuXEeDFUFvFzeGLJx5N+wp2qRS1TqYezGD3yERk",
|
"script":"RzBEAiB2XXkx1pca9SlfCmCGNUVf+h2sAFBttcxG1VnypIcvEgIgXrOp7LSdYBYp3nPsQAz8BOLD3K4pAlXfZImP1rkzk2EBQQRi7NcODzNfnVqLtG79Axp5UF6EhFIhCmzqKqssfKpfCIOmzCuXEeDFUFvFzeGLJx5N+wp2qRS1TqYezGD3yERk",
|
||||||
"sequence":4294967295
|
"sequence":4294967295
|
||||||
}],
|
}],
|
||||||
"outs":[{
|
"outs":[{
|
||||||
"value":"3000000000",
|
"value":"3000000000",
|
||||||
"script":"dqkUBLZwqhAPRVgZvwI8MN5gLHbU8NOIrA=="
|
"script":"dqkUBLZwqhAPRVgZvwI8MN5gLHbU8NOIrA=="
|
||||||
},{
|
},{
|
||||||
"value":"25937000000",
|
"value":"25937000000",
|
||||||
"script":"dqkUQ82gJ0O5vOBg6yK5/yorLLV5zLKIrA=="
|
"script":"dqkUQ82gJ0O5vOBg6yK5/yorLLV5zLKIrA=="
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
|
|
||||||
TODO: Make this stuff into test cases ;)
|
TODO: Make this stuff into test cases ;)
|
||||||
$(function () {
|
$(function () {
|
||||||
var key = new Bitcoin.ECKey(Crypto.util.hexToBytes("5c0b98e524ad188ddef35dc6abba13c34a351a05409e5d285403718b93336a4a"));
|
var key = new Bitcoin.ECKey(Crypto.util.hexToBytes("5c0b98e524ad188ddef35dc6abba13c34a351a05409e5d285403718b93336a4a"));
|
||||||
key = new Bitcoin.ECKey(Crypto.util.hexToBytes("180cb41c7c600be951b5d3d0a7334acc7506173875834f7a6c4c786a28fcbb19"));
|
key = new Bitcoin.ECKey(Crypto.util.hexToBytes("180cb41c7c600be951b5d3d0a7334acc7506173875834f7a6c4c786a28fcbb19"));
|
||||||
//console.log(key.getBitcoinAddress().toString());
|
//console.log(key.getBitcoinAddress().toString());
|
||||||
//var message = Crypto.util.hexToBytes("2aec28d323ee7b06a799d540d224b351161fe48967174ca5e43164e86137da11");
|
//var message = Crypto.util.hexToBytes("2aec28d323ee7b06a799d540d224b351161fe48967174ca5e43164e86137da11");
|
||||||
//message = [0];
|
//message = [0];
|
||||||
//var out = key.sign(message);
|
//var out = key.sign(message);
|
||||||
//console.log("pubkey: "+Crypto.util.bytesToHex(key.getPub()));
|
//console.log("pubkey: "+Crypto.util.bytesToHex(key.getPub()));
|
||||||
//console.log("sig: "+Crypto.util.bytesToHex(out));
|
//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("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("c2c75bb77d7a5acddceb1d45ceef58e7451fd0d3abc9d4c16df7848eefafe00d"), Crypto.util.hexToBytes("3045022100ff9362dadcbf1f6ef954bc8eb27144bbb4f49abd32be1eb04c311151dcf4bcf802205112c2ca6a25aefb8be98bf460c5a9056c01253f31e118d80b81ec9604e3201a01"),Crypto.util.hexToBytes("04fe62ce7892ec209310c176ef7f06565865e286e8699e884603657efa9aa51086785099d544d4e04f1f7b4b065205c1783fade8daf4ba1e0d1962292e8eb722cd")));
|
||||||
});
|
});
|
||||||
//
|
//
|
||||||
*/
|
*/
|
||||||
|
|
336
src/ecdsa.js
336
src/ecdsa.js
|
@ -1,104 +1,104 @@
|
||||||
function integerToBytes(i, len) {
|
function integerToBytes(i, len) {
|
||||||
var bytes = i.toByteArrayUnsigned();
|
var bytes = i.toByteArrayUnsigned();
|
||||||
|
|
||||||
if (len < bytes.length) {
|
if (len < bytes.length) {
|
||||||
bytes = bytes.slice(bytes.length-len);
|
bytes = bytes.slice(bytes.length-len);
|
||||||
} else while (len > bytes.length) {
|
} else while (len > bytes.length) {
|
||||||
bytes.unshift(0);
|
bytes.unshift(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return bytes;
|
return bytes;
|
||||||
};
|
};
|
||||||
|
|
||||||
ECFieldElementFp.prototype.getByteLength = function () {
|
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) {
|
ECPointFp.prototype.getEncoded = function (compressed) {
|
||||||
var x = this.getX().toBigInteger();
|
var x = this.getX().toBigInteger();
|
||||||
var y = this.getY().toBigInteger();
|
var y = this.getY().toBigInteger();
|
||||||
|
|
||||||
// Get value as a 32-byte Buffer
|
// Get value as a 32-byte Buffer
|
||||||
// Fixed length based on a patch by bitaddress.org and Casascius
|
// Fixed length based on a patch by bitaddress.org and Casascius
|
||||||
var enc = integerToBytes(x, 32);
|
var enc = integerToBytes(x, 32);
|
||||||
|
|
||||||
if (compressed) {
|
if (compressed) {
|
||||||
if (y.testBit(0)) {
|
if (y.testBit(0)) {
|
||||||
enc.unshift(0x02);
|
enc.unshift(0x02);
|
||||||
} else {
|
} else {
|
||||||
enc.unshift(0x03);
|
enc.unshift(0x03);
|
||||||
}
|
}
|
||||||
// TODO: Implement
|
// TODO: Implement
|
||||||
} else {
|
} else {
|
||||||
enc.unshift(0x04);
|
enc.unshift(0x04);
|
||||||
enc = enc.concat(integerToBytes(y, 32));
|
enc = enc.concat(integerToBytes(y, 32));
|
||||||
}
|
}
|
||||||
return enc;
|
return enc;
|
||||||
};
|
};
|
||||||
|
|
||||||
ECPointFp.decodeFrom = function (curve, enc) {
|
ECPointFp.decodeFrom = function (curve, enc) {
|
||||||
var type = enc[0];
|
var type = enc[0];
|
||||||
var dataLen = enc.length-1;
|
var dataLen = enc.length-1;
|
||||||
|
|
||||||
// Extract x and y as byte arrays
|
// Extract x and y as byte arrays
|
||||||
var xBa = enc.slice(1, 1 + dataLen/2);
|
var xBa = enc.slice(1, 1 + dataLen/2);
|
||||||
var yBa = enc.slice(1 + dataLen/2, 1 + dataLen);
|
var yBa = enc.slice(1 + dataLen/2, 1 + dataLen);
|
||||||
|
|
||||||
// Prepend zero byte to prevent interpretation as negative integer
|
// Prepend zero byte to prevent interpretation as negative integer
|
||||||
xBa.unshift(0);
|
xBa.unshift(0);
|
||||||
yBa.unshift(0);
|
yBa.unshift(0);
|
||||||
|
|
||||||
// Convert to BigIntegers
|
// Convert to BigIntegers
|
||||||
var x = new BigInteger(xBa);
|
var x = new BigInteger(xBa);
|
||||||
var y = new BigInteger(yBa);
|
var y = new BigInteger(yBa);
|
||||||
|
|
||||||
// Return point
|
// Return point
|
||||||
return new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y));
|
return new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y));
|
||||||
};
|
};
|
||||||
|
|
||||||
ECPointFp.prototype.add2D = function (b) {
|
ECPointFp.prototype.add2D = function (b) {
|
||||||
if(this.isInfinity()) return b;
|
if(this.isInfinity()) return b;
|
||||||
if(b.isInfinity()) return this;
|
if(b.isInfinity()) return this;
|
||||||
|
|
||||||
if (this.x.equals(b.x)) {
|
if (this.x.equals(b.x)) {
|
||||||
if (this.y.equals(b.y)) {
|
if (this.y.equals(b.y)) {
|
||||||
// this = b, i.e. this must be doubled
|
// this = b, i.e. this must be doubled
|
||||||
return this.twice();
|
return this.twice();
|
||||||
}
|
}
|
||||||
// this = -b, i.e. the result is the point at infinity
|
// this = -b, i.e. the result is the point at infinity
|
||||||
return this.curve.getInfinity();
|
return this.curve.getInfinity();
|
||||||
}
|
}
|
||||||
|
|
||||||
var x_x = b.x.subtract(this.x);
|
var x_x = b.x.subtract(this.x);
|
||||||
var y_y = b.y.subtract(this.y);
|
var y_y = b.y.subtract(this.y);
|
||||||
var gamma = y_y.divide(x_x);
|
var gamma = y_y.divide(x_x);
|
||||||
|
|
||||||
var x3 = gamma.square().subtract(this.x).subtract(b.x);
|
var x3 = gamma.square().subtract(this.x).subtract(b.x);
|
||||||
var y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y);
|
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 () {
|
ECPointFp.prototype.twice2D = function () {
|
||||||
if (this.isInfinity()) return this;
|
if (this.isInfinity()) return this;
|
||||||
if (this.y.toBigInteger().signum() == 0) {
|
if (this.y.toBigInteger().signum() == 0) {
|
||||||
// if y1 == 0, then (x1, y1) == (x1, -y1)
|
// if y1 == 0, then (x1, y1) == (x1, -y1)
|
||||||
// and hence this = -this and thus 2(x1, y1) == infinity
|
// and hence this = -this and thus 2(x1, y1) == infinity
|
||||||
return this.curve.getInfinity();
|
return this.curve.getInfinity();
|
||||||
}
|
}
|
||||||
|
|
||||||
var TWO = this.curve.fromBigInteger(BigInteger.valueOf(2));
|
var TWO = this.curve.fromBigInteger(BigInteger.valueOf(2));
|
||||||
var THREE = this.curve.fromBigInteger(BigInteger.valueOf(3));
|
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 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 x3 = gamma.square().subtract(this.x.multiply(TWO));
|
||||||
var y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y);
|
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) {
|
ECPointFp.prototype.multiply2D = function (k) {
|
||||||
if(this.isInfinity()) return this;
|
if(this.isInfinity()) return this;
|
||||||
if(k.signum() == 0) return this.curve.getInfinity();
|
if(k.signum() == 0) return this.curve.getInfinity();
|
||||||
|
|
||||||
var e = k;
|
var e = k;
|
||||||
|
@ -109,14 +109,14 @@ ECPointFp.prototype.multiply2D = function (k) {
|
||||||
|
|
||||||
var i;
|
var i;
|
||||||
for (i = h.bitLength() - 2; i > 0; --i) {
|
for (i = h.bitLength() - 2; i > 0; --i) {
|
||||||
R = R.twice();
|
R = R.twice();
|
||||||
|
|
||||||
var hBit = h.testBit(i);
|
var hBit = h.testBit(i);
|
||||||
var eBit = e.testBit(i);
|
var eBit = e.testBit(i);
|
||||||
|
|
||||||
if (hBit != eBit) {
|
if (hBit != eBit) {
|
||||||
R = R.add2D(hBit ? this : neg);
|
R = R.add2D(hBit ? this : neg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return R;
|
return R;
|
||||||
|
@ -169,141 +169,141 @@ ECPointFp.prototype.validate = function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
function dmp(v) {
|
function dmp(v) {
|
||||||
if (!(v instanceof BigInteger)) v = v.toBigInteger();
|
if (!(v instanceof BigInteger)) v = v.toBigInteger();
|
||||||
return Crypto.util.bytesToHex(v.toByteArrayUnsigned());
|
return Crypto.util.bytesToHex(v.toByteArrayUnsigned());
|
||||||
};
|
};
|
||||||
|
|
||||||
Bitcoin.ECDSA = (function () {
|
Bitcoin.ECDSA = (function () {
|
||||||
var ecparams = getSECCurveByName("secp256k1");
|
var ecparams = getSECCurveByName("secp256k1");
|
||||||
var rng = new SecureRandom();
|
var rng = new SecureRandom();
|
||||||
|
|
||||||
function implShamirsTrick(P, k, Q, l)
|
function implShamirsTrick(P, k, Q, l)
|
||||||
{
|
{
|
||||||
var m = Math.max(k.bitLength(), l.bitLength());
|
var m = Math.max(k.bitLength(), l.bitLength());
|
||||||
var Z = P.add2D(Q);
|
var Z = P.add2D(Q);
|
||||||
var R = P.curve.getInfinity();
|
var R = P.curve.getInfinity();
|
||||||
|
|
||||||
for (var i = m - 1; i >= 0; --i) {
|
for (var i = m - 1; i >= 0; --i) {
|
||||||
R = R.twice2D();
|
R = R.twice2D();
|
||||||
|
|
||||||
R.z = BigInteger.ONE;
|
R.z = BigInteger.ONE;
|
||||||
|
|
||||||
if (k.testBit(i)) {
|
if (k.testBit(i)) {
|
||||||
if (l.testBit(i)) {
|
if (l.testBit(i)) {
|
||||||
R = R.add2D(Z);
|
R = R.add2D(Z);
|
||||||
} else {
|
} else {
|
||||||
R = R.add2D(P);
|
R = R.add2D(P);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (l.testBit(i)) {
|
if (l.testBit(i)) {
|
||||||
R = R.add2D(Q);
|
R = R.add2D(Q);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return R;
|
return R;
|
||||||
};
|
};
|
||||||
|
|
||||||
var ECDSA = {
|
var ECDSA = {
|
||||||
getBigRandom: function (limit) {
|
getBigRandom: function (limit) {
|
||||||
return new BigInteger(limit.bitLength(), rng)
|
return new BigInteger(limit.bitLength(), rng)
|
||||||
.mod(limit.subtract(BigInteger.ONE))
|
.mod(limit.subtract(BigInteger.ONE))
|
||||||
.add(BigInteger.ONE)
|
.add(BigInteger.ONE)
|
||||||
;
|
;
|
||||||
},
|
},
|
||||||
sign: function (hash, priv) {
|
sign: function (hash, priv) {
|
||||||
var d = priv;
|
var d = priv;
|
||||||
var n = ecparams.getN();
|
var n = ecparams.getN();
|
||||||
var e = BigInteger.fromByteArrayUnsigned(hash);
|
var e = BigInteger.fromByteArrayUnsigned(hash);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
var k = ECDSA.getBigRandom(n);
|
var k = ECDSA.getBigRandom(n);
|
||||||
var G = ecparams.getG();
|
var G = ecparams.getG();
|
||||||
var Q = G.multiply(k);
|
var Q = G.multiply(k);
|
||||||
var r = Q.getX().toBigInteger().mod(n);
|
var r = Q.getX().toBigInteger().mod(n);
|
||||||
} while (r.compareTo(BigInteger.ZERO) <= 0);
|
} while (r.compareTo(BigInteger.ZERO) <= 0);
|
||||||
|
|
||||||
var s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n);
|
var s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n);
|
||||||
|
|
||||||
return ECDSA.serializeSig(r, s);
|
return ECDSA.serializeSig(r, s);
|
||||||
},
|
|
||||||
|
|
||||||
serializeSig: function (r, s) {
|
|
||||||
var rBa = r.toByteArrayUnsigned();
|
|
||||||
var sBa = s.toByteArrayUnsigned();
|
|
||||||
|
|
||||||
var sequence = [];
|
|
||||||
sequence.push(0x02); // INTEGER
|
|
||||||
sequence.push(rBa.length);
|
|
||||||
sequence = sequence.concat(rBa);
|
|
||||||
|
|
||||||
sequence.push(0x02); // INTEGER
|
|
||||||
sequence.push(sBa.length);
|
|
||||||
sequence = sequence.concat(sBa);
|
|
||||||
|
|
||||||
sequence.unshift(sequence.length);
|
|
||||||
sequence.unshift(0x30) // SEQUENCE
|
|
||||||
|
|
||||||
return sequence;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
verify: function (hash, sig, pubkey) {
|
serializeSig: function (r, s) {
|
||||||
|
var rBa = r.toByteArrayUnsigned();
|
||||||
|
var sBa = s.toByteArrayUnsigned();
|
||||||
|
|
||||||
|
var sequence = [];
|
||||||
|
sequence.push(0x02); // INTEGER
|
||||||
|
sequence.push(rBa.length);
|
||||||
|
sequence = sequence.concat(rBa);
|
||||||
|
|
||||||
|
sequence.push(0x02); // INTEGER
|
||||||
|
sequence.push(sBa.length);
|
||||||
|
sequence = sequence.concat(sBa);
|
||||||
|
|
||||||
|
sequence.unshift(sequence.length);
|
||||||
|
sequence.unshift(0x30) // SEQUENCE
|
||||||
|
|
||||||
|
return sequence;
|
||||||
|
},
|
||||||
|
|
||||||
|
verify: function (hash, sig, pubkey) {
|
||||||
var obj = ECDSA.parseSig(sig);
|
var obj = ECDSA.parseSig(sig);
|
||||||
var r = obj.r;
|
var r = obj.r;
|
||||||
var s = obj.s;
|
var s = obj.s;
|
||||||
|
|
||||||
var n = ecparams.getN();
|
var n = ecparams.getN();
|
||||||
var e = BigInteger.fromByteArrayUnsigned(hash);
|
var e = BigInteger.fromByteArrayUnsigned(hash);
|
||||||
|
|
||||||
if (r.compareTo(BigInteger.ONE) < 0 ||
|
if (r.compareTo(BigInteger.ONE) < 0 ||
|
||||||
r.compareTo(n) >= 0)
|
r.compareTo(n) >= 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (s.compareTo(BigInteger.ONE) < 0 ||
|
if (s.compareTo(BigInteger.ONE) < 0 ||
|
||||||
s.compareTo(n) >= 0)
|
s.compareTo(n) >= 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var c = s.modInverse(n);
|
var c = s.modInverse(n);
|
||||||
|
|
||||||
var u1 = e.multiply(c).mod(n);
|
var u1 = e.multiply(c).mod(n);
|
||||||
var u2 = r.multiply(c).mod(n);
|
var u2 = r.multiply(c).mod(n);
|
||||||
|
|
||||||
var G = ecparams.getG();
|
var G = ecparams.getG();
|
||||||
var Q = ECPointFp.decodeFrom(ecparams.getCurve(), pubkey);
|
var Q = ECPointFp.decodeFrom(ecparams.getCurve(), pubkey);
|
||||||
|
|
||||||
var point = implShamirsTrick(G, u1, Q, u2);
|
var point = implShamirsTrick(G, u1, Q, u2);
|
||||||
|
|
||||||
var v = point.x.toBigInteger().mod(n);
|
var v = point.x.toBigInteger().mod(n);
|
||||||
|
|
||||||
return v.equals(r);
|
return v.equals(r);
|
||||||
},
|
},
|
||||||
|
|
||||||
parseSig: function (sig) {
|
parseSig: function (sig) {
|
||||||
var cursor;
|
var cursor;
|
||||||
if (sig[0] != 0x30)
|
if (sig[0] != 0x30)
|
||||||
throw new Error("Signature not a valid DERSequence");
|
throw new Error("Signature not a valid DERSequence");
|
||||||
|
|
||||||
cursor = 2;
|
cursor = 2;
|
||||||
if (sig[cursor] != 0x02)
|
if (sig[cursor] != 0x02)
|
||||||
throw new Error("First element in signature must be a DERInteger");;
|
throw new Error("First element in signature must be a DERInteger");;
|
||||||
var rBa = sig.slice(cursor+2, cursor+2+sig[cursor+1]);
|
var rBa = sig.slice(cursor+2, cursor+2+sig[cursor+1]);
|
||||||
|
|
||||||
cursor += 2+sig[cursor+1];
|
cursor += 2+sig[cursor+1];
|
||||||
if (sig[cursor] != 0x02)
|
if (sig[cursor] != 0x02)
|
||||||
throw new Error("Second element in signature must be a DERInteger");
|
throw new Error("Second element in signature must be a DERInteger");
|
||||||
var sBa = sig.slice(cursor+2, cursor+2+sig[cursor+1]);
|
var sBa = sig.slice(cursor+2, cursor+2+sig[cursor+1]);
|
||||||
|
|
||||||
cursor += 2+sig[cursor+1];
|
cursor += 2+sig[cursor+1];
|
||||||
|
|
||||||
//if (cursor != sig.length)
|
//if (cursor != sig.length)
|
||||||
// throw new Error("Extra bytes in signature");
|
// throw new Error("Extra bytes in signature");
|
||||||
|
|
||||||
var r = BigInteger.fromByteArrayUnsigned(rBa);
|
var r = BigInteger.fromByteArrayUnsigned(rBa);
|
||||||
var s = BigInteger.fromByteArrayUnsigned(sBa);
|
var s = BigInteger.fromByteArrayUnsigned(sBa);
|
||||||
|
|
||||||
return {r: r, s: s};
|
return {r: r, s: s};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return ECDSA;
|
return ECDSA;
|
||||||
})();
|
})();
|
||||||
|
|
92
src/eckey.js
92
src/eckey.js
|
@ -1,62 +1,62 @@
|
||||||
Bitcoin.ECKey = (function () {
|
Bitcoin.ECKey = (function () {
|
||||||
var ECDSA = Bitcoin.ECDSA;
|
var ECDSA = Bitcoin.ECDSA;
|
||||||
var ecparams = getSECCurveByName("secp256k1");
|
var ecparams = getSECCurveByName("secp256k1");
|
||||||
var rng = new SecureRandom();
|
var rng = new SecureRandom();
|
||||||
|
|
||||||
var ECKey = function (input) {
|
var ECKey = function (input) {
|
||||||
if (!input) {
|
if (!input) {
|
||||||
// Generate new key
|
// Generate new key
|
||||||
var n = ecparams.getN();
|
var n = ecparams.getN();
|
||||||
this.priv = ECDSA.getBigRandom(n);
|
this.priv = ECDSA.getBigRandom(n);
|
||||||
} else if (input instanceof BigInteger) {
|
} else if (input instanceof BigInteger) {
|
||||||
// Input is a private key value
|
// Input is a private key value
|
||||||
this.priv = input;
|
this.priv = input;
|
||||||
} else if (Bitcoin.Util.isArray(input)) {
|
} else if (Bitcoin.Util.isArray(input)) {
|
||||||
// Prepend zero byte to prevent interpretation as negative integer
|
// Prepend zero byte to prevent interpretation as negative integer
|
||||||
this.priv = BigInteger.fromByteArrayUnsigned(input);
|
this.priv = BigInteger.fromByteArrayUnsigned(input);
|
||||||
} else if ("string" == typeof input) {
|
} else if ("string" == typeof input) {
|
||||||
// Prepend zero byte to prevent interpretation as negative integer
|
// Prepend zero byte to prevent interpretation as negative integer
|
||||||
this.priv = BigInteger.fromByteArrayUnsigned(Crypto.util.base64ToBytes(input));
|
this.priv = BigInteger.fromByteArrayUnsigned(Crypto.util.base64ToBytes(input));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ECKey.prototype.getPub = function () {
|
ECKey.prototype.getPub = function () {
|
||||||
if (this.pub) return this.pub;
|
if (this.pub) return this.pub;
|
||||||
|
|
||||||
return this.pub = ecparams.getG().multiply(this.priv).getEncoded();
|
return this.pub = ecparams.getG().multiply(this.priv).getEncoded();
|
||||||
};
|
};
|
||||||
|
|
||||||
ECKey.prototype.getPubKeyHash = function () {
|
ECKey.prototype.getPubKeyHash = function () {
|
||||||
if (this.pubKeyHash) return this.pubKeyHash;
|
if (this.pubKeyHash) return this.pubKeyHash;
|
||||||
|
|
||||||
return this.pubKeyHash = Bitcoin.Util.sha256ripe160(this.getPub());
|
return this.pubKeyHash = Bitcoin.Util.sha256ripe160(this.getPub());
|
||||||
};
|
};
|
||||||
|
|
||||||
ECKey.prototype.getBitcoinAddress = function () {
|
ECKey.prototype.getBitcoinAddress = function () {
|
||||||
var hash = this.getPubKeyHash();
|
var hash = this.getPubKeyHash();
|
||||||
var addr = new Bitcoin.Address(hash);
|
var addr = new Bitcoin.Address(hash);
|
||||||
return addr;
|
return addr;
|
||||||
};
|
};
|
||||||
|
|
||||||
ECKey.prototype.setPub = function (pub) {
|
ECKey.prototype.setPub = function (pub) {
|
||||||
this.pub = pub;
|
this.pub = pub;
|
||||||
};
|
};
|
||||||
|
|
||||||
ECKey.prototype.toString = function (format) {
|
ECKey.prototype.toString = function (format) {
|
||||||
if (format === "base64") {
|
if (format === "base64") {
|
||||||
return Crypto.util.bytesToBase64(this.priv.toByteArrayUnsigned());
|
return Crypto.util.bytesToBase64(this.priv.toByteArrayUnsigned());
|
||||||
} else {
|
} else {
|
||||||
return Crypto.util.bytesToHex(this.priv.toByteArrayUnsigned());
|
return Crypto.util.bytesToHex(this.priv.toByteArrayUnsigned());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ECKey.prototype.sign = function (hash) {
|
ECKey.prototype.sign = function (hash) {
|
||||||
return ECDSA.sign(hash, this.priv);
|
return ECDSA.sign(hash, this.priv);
|
||||||
};
|
};
|
||||||
|
|
||||||
ECKey.prototype.verify = function (hash, sig) {
|
ECKey.prototype.verify = function (hash, sig) {
|
||||||
return ECDSA.verify(hash, sig, this.getPub());
|
return ECDSA.verify(hash, sig, this.getPub());
|
||||||
};
|
};
|
||||||
|
|
||||||
return ECKey;
|
return ECKey;
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -7,12 +7,12 @@
|
||||||
exports.ExitNode = ExitNode;
|
exports.ExitNode = ExitNode;
|
||||||
|
|
||||||
function ExitNode(host, port, secure) {
|
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.connected = false;
|
||||||
|
|
||||||
this.callbacks = [];
|
this.callbacks = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
Bitcoin.EventEmitter.augment(ExitNode);
|
Bitcoin.EventEmitter.augment(ExitNode);
|
||||||
|
@ -22,23 +22,23 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
ExitNode.prototype.connect = function (wallet) {
|
ExitNode.prototype.connect = function (wallet) {
|
||||||
this.wallet = wallet;
|
this.wallet = wallet;
|
||||||
|
|
||||||
// Workaround for socket.io not properly allowing disconnecting and reconnecting
|
// Workaround for socket.io not properly allowing disconnecting and reconnecting
|
||||||
delete io.sockets[this.uri];
|
delete io.sockets[this.uri];
|
||||||
io.j = [];
|
io.j = [];
|
||||||
|
|
||||||
this.socket = io.connect(this.uri);
|
this.socket = io.connect(this.uri);
|
||||||
this.socket.on('connect', $.proxy(this.handleConnect, this));
|
this.socket.on('connect', $.proxy(this.handleConnect, this));
|
||||||
this.socket.on('error', function () {
|
this.socket.on('error', function () {
|
||||||
console.log('error, test');
|
console.log('error, test');
|
||||||
});
|
});
|
||||||
this.socket.on('message', $.proxy(this.handleMessage, this));
|
this.socket.on('message', $.proxy(this.handleMessage, this));
|
||||||
this.socket.on('disconnect', $.proxy(this.handleDisconnect, this));
|
this.socket.on('disconnect', $.proxy(this.handleDisconnect, this));
|
||||||
};
|
};
|
||||||
|
|
||||||
ExitNode.prototype.disconnect = function () {
|
ExitNode.prototype.disconnect = function () {
|
||||||
if (this.socket) {
|
if (this.socket) {
|
||||||
this.socket.disconnect();
|
this.socket.disconnect();
|
||||||
this.socket = null;
|
this.socket = null;
|
||||||
this.connected = false;
|
this.connected = false;
|
||||||
|
@ -51,76 +51,76 @@
|
||||||
* Make RPC call.
|
* Make RPC call.
|
||||||
*/
|
*/
|
||||||
ExitNode.prototype.call = function (method, argObj, callback) {
|
ExitNode.prototype.call = function (method, argObj, callback) {
|
||||||
this.socket.send($.toJSON({
|
this.socket.send($.toJSON({
|
||||||
"method": method,
|
"method": method,
|
||||||
"params": [argObj],
|
"params": [argObj],
|
||||||
"id": this.unique
|
"id": this.unique
|
||||||
}));
|
}));
|
||||||
if (callback) this.callbacks[this.unique] = callback;
|
if (callback) this.callbacks[this.unique] = callback;
|
||||||
this.unique++;
|
this.unique++;
|
||||||
};
|
};
|
||||||
|
|
||||||
ExitNode.prototype.handleConnect = function () {
|
ExitNode.prototype.handleConnect = function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
this.connected = true;
|
this.connected = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
ExitNode.prototype.listen = function (addrs) {
|
ExitNode.prototype.listen = function (addrs) {
|
||||||
self.call("pubkeysRegister", {
|
self.call("pubkeysRegister", {
|
||||||
keys: addrs.join(',')
|
keys: addrs.join(',')
|
||||||
}, function (err, result) {
|
}, function (err, result) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error("Could not register public keys");
|
console.error("Could not register public keys");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.call("pubkeysListen", {
|
self.call("pubkeysListen", {
|
||||||
handle: result.handle
|
handle: result.handle
|
||||||
}, function (err, result) {
|
}, function (err, result) {
|
||||||
// Communicate the block height
|
// Communicate the block height
|
||||||
self.trigger('blockInit', {height: result.height});
|
self.trigger('blockInit', {height: result.height});
|
||||||
|
|
||||||
// Pass on the newly downloaded transactions
|
// Pass on the newly downloaded transactions
|
||||||
self.trigger('txData', {
|
self.trigger('txData', {
|
||||||
confirmed: true,
|
confirmed: true,
|
||||||
txs: result.txs
|
txs: result.txs
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: Download more transactions
|
// TODO: Download more transactions
|
||||||
|
|
||||||
self.trigger('connectStatus', {status: 'ok'});
|
self.trigger('connectStatus', {status: 'ok'});
|
||||||
});
|
});
|
||||||
|
|
||||||
self.call("pubkeysUnconfirmed", {
|
self.call("pubkeysUnconfirmed", {
|
||||||
handle: result.handle
|
handle: result.handle
|
||||||
}, function (err, result) {
|
}, function (err, result) {
|
||||||
// Pass on the newly downloaded transactions
|
// Pass on the newly downloaded transactions
|
||||||
self.trigger('txData', {
|
self.trigger('txData', {
|
||||||
confirmed: false,
|
confirmed: false,
|
||||||
txs: result.txs
|
txs: result.txs
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
ExitNode.prototype.handleMessage = function (data) {
|
ExitNode.prototype.handleMessage = function (data) {
|
||||||
// Handle JSON-RPC result messages
|
// Handle JSON-RPC result messages
|
||||||
if ("undefined" !== typeof data.result &&
|
if ("undefined" !== typeof data.result &&
|
||||||
"function" == typeof this.callbacks[data.id]) {
|
"function" == typeof this.callbacks[data.id]) {
|
||||||
this.callbacks[data.id](data.error, data.result);
|
this.callbacks[data.id](data.error, data.result);
|
||||||
|
|
||||||
// Handle JSON-RPC request messages
|
// Handle JSON-RPC request messages
|
||||||
} else if ("undefined" !== typeof data.method) {
|
} else if ("undefined" !== typeof data.method) {
|
||||||
// Create an event
|
// Create an event
|
||||||
this.trigger(data.method, data.params[0]);
|
this.trigger(data.method, data.params[0]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
ExitNode.prototype.handleDisconnect = function () {
|
ExitNode.prototype.handleDisconnect = function () {
|
||||||
// TODO: Attempt reconnect (unless disconnect was intended)
|
// TODO: Attempt reconnect (unless disconnect was intended)
|
||||||
};
|
};
|
||||||
|
|
||||||
ExitNode.prototype.query = function (api, params, jsonp, callback) {
|
ExitNode.prototype.query = function (api, params, jsonp, callback) {
|
||||||
|
|
274
src/opcode.js
274
src/opcode.js
|
@ -1,154 +1,154 @@
|
||||||
(function () {
|
(function () {
|
||||||
var Opcode = Bitcoin.Opcode = function (num) {
|
var Opcode = Bitcoin.Opcode = function (num) {
|
||||||
this.code = num;
|
this.code = num;
|
||||||
};
|
};
|
||||||
|
|
||||||
Opcode.prototype.toString = function () {
|
Opcode.prototype.toString = function () {
|
||||||
return Opcode.reverseMap[this.code];
|
return Opcode.reverseMap[this.code];
|
||||||
};
|
};
|
||||||
|
|
||||||
Opcode.map = {
|
Opcode.map = {
|
||||||
// push value
|
// push value
|
||||||
OP_0 : 0,
|
OP_0 : 0,
|
||||||
OP_FALSE : 0,
|
OP_FALSE : 0,
|
||||||
OP_PUSHDATA1 : 76,
|
OP_PUSHDATA1 : 76,
|
||||||
OP_PUSHDATA2 : 77,
|
OP_PUSHDATA2 : 77,
|
||||||
OP_PUSHDATA4 : 78,
|
OP_PUSHDATA4 : 78,
|
||||||
OP_1NEGATE : 79,
|
OP_1NEGATE : 79,
|
||||||
OP_RESERVED : 80,
|
OP_RESERVED : 80,
|
||||||
OP_1 : 81,
|
OP_1 : 81,
|
||||||
OP_TRUE : 81,
|
OP_TRUE : 81,
|
||||||
OP_2 : 82,
|
OP_2 : 82,
|
||||||
OP_3 : 83,
|
OP_3 : 83,
|
||||||
OP_4 : 84,
|
OP_4 : 84,
|
||||||
OP_5 : 85,
|
OP_5 : 85,
|
||||||
OP_6 : 86,
|
OP_6 : 86,
|
||||||
OP_7 : 87,
|
OP_7 : 87,
|
||||||
OP_8 : 88,
|
OP_8 : 88,
|
||||||
OP_9 : 89,
|
OP_9 : 89,
|
||||||
OP_10 : 90,
|
OP_10 : 90,
|
||||||
OP_11 : 91,
|
OP_11 : 91,
|
||||||
OP_12 : 92,
|
OP_12 : 92,
|
||||||
OP_13 : 93,
|
OP_13 : 93,
|
||||||
OP_14 : 94,
|
OP_14 : 94,
|
||||||
OP_15 : 95,
|
OP_15 : 95,
|
||||||
OP_16 : 96,
|
OP_16 : 96,
|
||||||
|
|
||||||
// control
|
// control
|
||||||
OP_NOP : 97,
|
OP_NOP : 97,
|
||||||
OP_VER : 98,
|
OP_VER : 98,
|
||||||
OP_IF : 99,
|
OP_IF : 99,
|
||||||
OP_NOTIF : 100,
|
OP_NOTIF : 100,
|
||||||
OP_VERIF : 101,
|
OP_VERIF : 101,
|
||||||
OP_VERNOTIF : 102,
|
OP_VERNOTIF : 102,
|
||||||
OP_ELSE : 103,
|
OP_ELSE : 103,
|
||||||
OP_ENDIF : 104,
|
OP_ENDIF : 104,
|
||||||
OP_VERIFY : 105,
|
OP_VERIFY : 105,
|
||||||
OP_RETURN : 106,
|
OP_RETURN : 106,
|
||||||
|
|
||||||
// stack ops
|
// stack ops
|
||||||
OP_TOALTSTACK : 107,
|
OP_TOALTSTACK : 107,
|
||||||
OP_FROMALTSTACK : 108,
|
OP_FROMALTSTACK : 108,
|
||||||
OP_2DROP : 109,
|
OP_2DROP : 109,
|
||||||
OP_2DUP : 110,
|
OP_2DUP : 110,
|
||||||
OP_3DUP : 111,
|
OP_3DUP : 111,
|
||||||
OP_2OVER : 112,
|
OP_2OVER : 112,
|
||||||
OP_2ROT : 113,
|
OP_2ROT : 113,
|
||||||
OP_2SWAP : 114,
|
OP_2SWAP : 114,
|
||||||
OP_IFDUP : 115,
|
OP_IFDUP : 115,
|
||||||
OP_DEPTH : 116,
|
OP_DEPTH : 116,
|
||||||
OP_DROP : 117,
|
OP_DROP : 117,
|
||||||
OP_DUP : 118,
|
OP_DUP : 118,
|
||||||
OP_NIP : 119,
|
OP_NIP : 119,
|
||||||
OP_OVER : 120,
|
OP_OVER : 120,
|
||||||
OP_PICK : 121,
|
OP_PICK : 121,
|
||||||
OP_ROLL : 122,
|
OP_ROLL : 122,
|
||||||
OP_ROT : 123,
|
OP_ROT : 123,
|
||||||
OP_SWAP : 124,
|
OP_SWAP : 124,
|
||||||
OP_TUCK : 125,
|
OP_TUCK : 125,
|
||||||
|
|
||||||
// splice ops
|
// splice ops
|
||||||
OP_CAT : 126,
|
OP_CAT : 126,
|
||||||
OP_SUBSTR : 127,
|
OP_SUBSTR : 127,
|
||||||
OP_LEFT : 128,
|
OP_LEFT : 128,
|
||||||
OP_RIGHT : 129,
|
OP_RIGHT : 129,
|
||||||
OP_SIZE : 130,
|
OP_SIZE : 130,
|
||||||
|
|
||||||
// bit logic
|
// bit logic
|
||||||
OP_INVERT : 131,
|
OP_INVERT : 131,
|
||||||
OP_AND : 132,
|
OP_AND : 132,
|
||||||
OP_OR : 133,
|
OP_OR : 133,
|
||||||
OP_XOR : 134,
|
OP_XOR : 134,
|
||||||
OP_EQUAL : 135,
|
OP_EQUAL : 135,
|
||||||
OP_EQUALVERIFY : 136,
|
OP_EQUALVERIFY : 136,
|
||||||
OP_RESERVED1 : 137,
|
OP_RESERVED1 : 137,
|
||||||
OP_RESERVED2 : 138,
|
OP_RESERVED2 : 138,
|
||||||
|
|
||||||
// numeric
|
// numeric
|
||||||
OP_1ADD : 139,
|
OP_1ADD : 139,
|
||||||
OP_1SUB : 140,
|
OP_1SUB : 140,
|
||||||
OP_2MUL : 141,
|
OP_2MUL : 141,
|
||||||
OP_2DIV : 142,
|
OP_2DIV : 142,
|
||||||
OP_NEGATE : 143,
|
OP_NEGATE : 143,
|
||||||
OP_ABS : 144,
|
OP_ABS : 144,
|
||||||
OP_NOT : 145,
|
OP_NOT : 145,
|
||||||
OP_0NOTEQUAL : 146,
|
OP_0NOTEQUAL : 146,
|
||||||
|
|
||||||
OP_ADD : 147,
|
OP_ADD : 147,
|
||||||
OP_SUB : 148,
|
OP_SUB : 148,
|
||||||
OP_MUL : 149,
|
OP_MUL : 149,
|
||||||
OP_DIV : 150,
|
OP_DIV : 150,
|
||||||
OP_MOD : 151,
|
OP_MOD : 151,
|
||||||
OP_LSHIFT : 152,
|
OP_LSHIFT : 152,
|
||||||
OP_RSHIFT : 153,
|
OP_RSHIFT : 153,
|
||||||
|
|
||||||
OP_BOOLAND : 154,
|
OP_BOOLAND : 154,
|
||||||
OP_BOOLOR : 155,
|
OP_BOOLOR : 155,
|
||||||
OP_NUMEQUAL : 156,
|
OP_NUMEQUAL : 156,
|
||||||
OP_NUMEQUALVERIFY : 157,
|
OP_NUMEQUALVERIFY : 157,
|
||||||
OP_NUMNOTEQUAL : 158,
|
OP_NUMNOTEQUAL : 158,
|
||||||
OP_LESSTHAN : 159,
|
OP_LESSTHAN : 159,
|
||||||
OP_GREATERTHAN : 160,
|
OP_GREATERTHAN : 160,
|
||||||
OP_LESSTHANOREQUAL : 161,
|
OP_LESSTHANOREQUAL : 161,
|
||||||
OP_GREATERTHANOREQUAL : 162,
|
OP_GREATERTHANOREQUAL : 162,
|
||||||
OP_MIN : 163,
|
OP_MIN : 163,
|
||||||
OP_MAX : 164,
|
OP_MAX : 164,
|
||||||
|
|
||||||
OP_WITHIN : 165,
|
OP_WITHIN : 165,
|
||||||
|
|
||||||
// crypto
|
// crypto
|
||||||
OP_RIPEMD160 : 166,
|
OP_RIPEMD160 : 166,
|
||||||
OP_SHA1 : 167,
|
OP_SHA1 : 167,
|
||||||
OP_SHA256 : 168,
|
OP_SHA256 : 168,
|
||||||
OP_HASH160 : 169,
|
OP_HASH160 : 169,
|
||||||
OP_HASH256 : 170,
|
OP_HASH256 : 170,
|
||||||
OP_CODESEPARATOR : 171,
|
OP_CODESEPARATOR : 171,
|
||||||
OP_CHECKSIG : 172,
|
OP_CHECKSIG : 172,
|
||||||
OP_CHECKSIGVERIFY : 173,
|
OP_CHECKSIGVERIFY : 173,
|
||||||
OP_CHECKMULTISIG : 174,
|
OP_CHECKMULTISIG : 174,
|
||||||
OP_CHECKMULTISIGVERIFY : 175,
|
OP_CHECKMULTISIGVERIFY : 175,
|
||||||
|
|
||||||
// expansion
|
// expansion
|
||||||
OP_NOP1 : 176,
|
OP_NOP1 : 176,
|
||||||
OP_NOP2 : 177,
|
OP_NOP2 : 177,
|
||||||
OP_NOP3 : 178,
|
OP_NOP3 : 178,
|
||||||
OP_NOP4 : 179,
|
OP_NOP4 : 179,
|
||||||
OP_NOP5 : 180,
|
OP_NOP5 : 180,
|
||||||
OP_NOP6 : 181,
|
OP_NOP6 : 181,
|
||||||
OP_NOP7 : 182,
|
OP_NOP7 : 182,
|
||||||
OP_NOP8 : 183,
|
OP_NOP8 : 183,
|
||||||
OP_NOP9 : 184,
|
OP_NOP9 : 184,
|
||||||
OP_NOP10 : 185,
|
OP_NOP10 : 185,
|
||||||
|
|
||||||
// template matching params
|
// template matching params
|
||||||
OP_PUBKEYHASH : 253,
|
OP_PUBKEYHASH : 253,
|
||||||
OP_PUBKEY : 254,
|
OP_PUBKEY : 254,
|
||||||
OP_INVALIDOPCODE : 255,
|
OP_INVALIDOPCODE : 255,
|
||||||
};
|
};
|
||||||
|
|
||||||
Opcode.reverseMap = [];
|
Opcode.reverseMap = [];
|
||||||
|
|
||||||
for (var i in Opcode.map) {
|
for (var i in Opcode.map) {
|
||||||
Opcode.reverseMap[Opcode.map[i]] = i;
|
Opcode.reverseMap[Opcode.map[i]] = i;
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
Bitcoin.Paillier = (function () {
|
Bitcoin.Paillier = (function () {
|
||||||
var rng = new SecureRandom();
|
var rng = new SecureRandom();
|
||||||
var TWO = BigInteger.valueOf(2);
|
var TWO = BigInteger.valueOf(2);
|
||||||
|
|
||||||
var Paillier = {
|
var Paillier = {
|
||||||
|
|
320
src/script.js
320
src/script.js
|
@ -1,183 +1,183 @@
|
||||||
(function () {
|
(function () {
|
||||||
var Opcode = Bitcoin.Opcode;
|
var Opcode = Bitcoin.Opcode;
|
||||||
|
|
||||||
// Make opcodes available as pseudo-constants
|
// Make opcodes available as pseudo-constants
|
||||||
for (var i in Opcode.map) {
|
for (var i in Opcode.map) {
|
||||||
eval("var " + i + " = " + Opcode.map[i] + ";");
|
eval("var " + i + " = " + Opcode.map[i] + ";");
|
||||||
}
|
}
|
||||||
|
|
||||||
var Script = Bitcoin.Script = function (data) {
|
var Script = Bitcoin.Script = function (data) {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
this.buffer = [];
|
this.buffer = [];
|
||||||
} else if ("string" == typeof data) {
|
} else if ("string" == typeof data) {
|
||||||
this.buffer = Crypto.util.base64ToBytes(data);
|
this.buffer = Crypto.util.base64ToBytes(data);
|
||||||
} else if (Bitcoin.Util.isArray(data)) {
|
} else if (Bitcoin.Util.isArray(data)) {
|
||||||
this.buffer = data;
|
this.buffer = data;
|
||||||
} else if (data instanceof Script) {
|
} else if (data instanceof Script) {
|
||||||
this.buffer = data.buffer;
|
this.buffer = data.buffer;
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Invalid script");
|
throw new Error("Invalid script");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.parse();
|
this.parse();
|
||||||
};
|
};
|
||||||
|
|
||||||
Script.prototype.parse = function () {
|
Script.prototype.parse = function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
this.chunks = [];
|
this.chunks = [];
|
||||||
|
|
||||||
// Cursor
|
// Cursor
|
||||||
var i = 0;
|
var i = 0;
|
||||||
|
|
||||||
// Read n bytes and store result as a chunk
|
// Read n bytes and store result as a chunk
|
||||||
function readChunk(n) {
|
function readChunk(n) {
|
||||||
self.chunks.push(self.buffer.slice(i, i + n));
|
self.chunks.push(self.buffer.slice(i, i + n));
|
||||||
i += n;
|
i += n;
|
||||||
};
|
};
|
||||||
|
|
||||||
while (i < this.buffer.length) {
|
while (i < this.buffer.length) {
|
||||||
var opcode = this.buffer[i++];
|
var opcode = this.buffer[i++];
|
||||||
if (opcode >= 0xF0) {
|
if (opcode >= 0xF0) {
|
||||||
// Two byte opcode
|
// Two byte opcode
|
||||||
opcode = (opcode << 8) | this.buffer[i++];
|
opcode = (opcode << 8) | this.buffer[i++];
|
||||||
}
|
}
|
||||||
|
|
||||||
var len;
|
var len;
|
||||||
if (opcode > 0 && opcode < OP_PUSHDATA1) {
|
if (opcode > 0 && opcode < OP_PUSHDATA1) {
|
||||||
// Read some bytes of data, opcode value is the length of data
|
// Read some bytes of data, opcode value is the length of data
|
||||||
readChunk(opcode);
|
readChunk(opcode);
|
||||||
} else if (opcode == OP_PUSHDATA1) {
|
} else if (opcode == OP_PUSHDATA1) {
|
||||||
len = this.buffer[i++];
|
len = this.buffer[i++];
|
||||||
readChunk(len);
|
readChunk(len);
|
||||||
} else if (opcode == OP_PUSHDATA2) {
|
} else if (opcode == OP_PUSHDATA2) {
|
||||||
len = (this.buffer[i++] << 8) | this.buffer[i++];
|
len = (this.buffer[i++] << 8) | this.buffer[i++];
|
||||||
readChunk(len);
|
readChunk(len);
|
||||||
} else if (opcode == OP_PUSHDATA4) {
|
} else if (opcode == OP_PUSHDATA4) {
|
||||||
len = (this.buffer[i++] << 24) |
|
len = (this.buffer[i++] << 24) |
|
||||||
(this.buffer[i++] << 16) |
|
(this.buffer[i++] << 16) |
|
||||||
(this.buffer[i++] << 8) |
|
(this.buffer[i++] << 8) |
|
||||||
this.buffer[i++];
|
this.buffer[i++];
|
||||||
readChunk(len);
|
readChunk(len);
|
||||||
} else {
|
} else {
|
||||||
this.chunks.push(opcode);
|
this.chunks.push(opcode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Script.prototype.getOutType = function ()
|
Script.prototype.getOutType = function ()
|
||||||
{
|
{
|
||||||
if (this.chunks.length == 5 &&
|
if (this.chunks.length == 5 &&
|
||||||
this.chunks[0] == OP_DUP &&
|
this.chunks[0] == OP_DUP &&
|
||||||
this.chunks[1] == OP_HASH160 &&
|
this.chunks[1] == OP_HASH160 &&
|
||||||
this.chunks[3] == OP_EQUALVERIFY &&
|
this.chunks[3] == OP_EQUALVERIFY &&
|
||||||
this.chunks[4] == OP_CHECKSIG) {
|
this.chunks[4] == OP_CHECKSIG) {
|
||||||
|
|
||||||
// Transfer to Bitcoin address
|
// Transfer to Bitcoin address
|
||||||
return 'Address';
|
return 'Address';
|
||||||
} else if (this.chunks.length == 2 &&
|
} else if (this.chunks.length == 2 &&
|
||||||
this.chunks[1] == OP_CHECKSIG) {
|
this.chunks[1] == OP_CHECKSIG) {
|
||||||
|
|
||||||
// Transfer to IP address
|
// Transfer to IP address
|
||||||
return 'Pubkey';
|
return 'Pubkey';
|
||||||
} else {
|
} else {
|
||||||
return 'Strange';
|
return 'Strange';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Script.prototype.simpleOutPubKeyHash = function ()
|
Script.prototype.simpleOutPubKeyHash = function ()
|
||||||
{
|
{
|
||||||
switch (this.getOutType()) {
|
switch (this.getOutType()) {
|
||||||
case 'Address':
|
case 'Address':
|
||||||
return this.chunks[2];
|
return this.chunks[2];
|
||||||
case 'Pubkey':
|
case 'Pubkey':
|
||||||
return Bitcoin.Util.sha256ripe160(this.chunks[0]);
|
return Bitcoin.Util.sha256ripe160(this.chunks[0]);
|
||||||
default:
|
default:
|
||||||
throw new Error("Encountered non-standard scriptPubKey");
|
throw new Error("Encountered non-standard scriptPubKey");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Script.prototype.getInType = function ()
|
Script.prototype.getInType = function ()
|
||||||
{
|
{
|
||||||
if (this.chunks.length == 1) {
|
if (this.chunks.length == 1) {
|
||||||
// Direct IP to IP transactions only have the public key in their scriptSig.
|
// Direct IP to IP transactions only have the public key in their scriptSig.
|
||||||
return 'Pubkey';
|
return 'Pubkey';
|
||||||
} else if (this.chunks.length == 2 &&
|
} else if (this.chunks.length == 2 &&
|
||||||
Bitcoin.Util.isArray(this.chunks[0]) &&
|
Bitcoin.Util.isArray(this.chunks[0]) &&
|
||||||
Bitcoin.Util.isArray(this.chunks[1])) {
|
Bitcoin.Util.isArray(this.chunks[1])) {
|
||||||
return 'Address';
|
return 'Address';
|
||||||
} else {
|
} else {
|
||||||
console.log(this.chunks);
|
console.log(this.chunks);
|
||||||
throw new Error("Encountered non-standard scriptSig");
|
throw new Error("Encountered non-standard scriptSig");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Script.prototype.simpleInPubKey = function ()
|
Script.prototype.simpleInPubKey = function ()
|
||||||
{
|
{
|
||||||
switch (this.getInType()) {
|
switch (this.getInType()) {
|
||||||
case 'Address':
|
case 'Address':
|
||||||
return this.chunks[1];
|
return this.chunks[1];
|
||||||
case 'Pubkey':
|
case 'Pubkey':
|
||||||
return this.chunks[0];
|
return this.chunks[0];
|
||||||
default:
|
default:
|
||||||
throw new Error("Encountered non-standard scriptSig");
|
throw new Error("Encountered non-standard scriptSig");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Script.prototype.simpleInPubKeyHash = function ()
|
Script.prototype.simpleInPubKeyHash = function ()
|
||||||
{
|
{
|
||||||
return Bitcoin.Util.sha256ripe160(this.simpleInPubKey());
|
return Bitcoin.Util.sha256ripe160(this.simpleInPubKey());
|
||||||
};
|
};
|
||||||
|
|
||||||
Script.prototype.writeOp = function (opcode)
|
Script.prototype.writeOp = function (opcode)
|
||||||
{
|
{
|
||||||
this.buffer.push(opcode);
|
this.buffer.push(opcode);
|
||||||
this.chunks.push(opcode);
|
this.chunks.push(opcode);
|
||||||
};
|
};
|
||||||
|
|
||||||
Script.prototype.writeBytes = function (data)
|
Script.prototype.writeBytes = function (data)
|
||||||
{
|
{
|
||||||
if (data.length < OP_PUSHDATA1) {
|
if (data.length < OP_PUSHDATA1) {
|
||||||
this.buffer.push(data.length);
|
this.buffer.push(data.length);
|
||||||
} else if (data.length <= 0xff) {
|
} else if (data.length <= 0xff) {
|
||||||
this.buffer.push(OP_PUSHDATA1);
|
this.buffer.push(OP_PUSHDATA1);
|
||||||
this.buffer.push(data.length);
|
this.buffer.push(data.length);
|
||||||
} else if (data.length <= 0xffff) {
|
} else if (data.length <= 0xffff) {
|
||||||
this.buffer.push(OP_PUSHDATA2);
|
this.buffer.push(OP_PUSHDATA2);
|
||||||
this.buffer.push(data.length & 0xff);
|
this.buffer.push(data.length & 0xff);
|
||||||
this.buffer.push((data.length >>> 8) & 0xff);
|
this.buffer.push((data.length >>> 8) & 0xff);
|
||||||
} else {
|
} else {
|
||||||
this.buffer.push(OP_PUSHDATA4);
|
this.buffer.push(OP_PUSHDATA4);
|
||||||
this.buffer.push(data.length & 0xff);
|
this.buffer.push(data.length & 0xff);
|
||||||
this.buffer.push((data.length >>> 8) & 0xff);
|
this.buffer.push((data.length >>> 8) & 0xff);
|
||||||
this.buffer.push((data.length >>> 16) & 0xff);
|
this.buffer.push((data.length >>> 16) & 0xff);
|
||||||
this.buffer.push((data.length >>> 24) & 0xff);
|
this.buffer.push((data.length >>> 24) & 0xff);
|
||||||
}
|
}
|
||||||
this.buffer = this.buffer.concat(data);
|
this.buffer = this.buffer.concat(data);
|
||||||
this.chunks.push(data);
|
this.chunks.push(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
Script.createOutputScript = function (address)
|
Script.createOutputScript = function (address)
|
||||||
{
|
{
|
||||||
var script = new Script();
|
var script = new Script();
|
||||||
script.writeOp(OP_DUP);
|
script.writeOp(OP_DUP);
|
||||||
script.writeOp(OP_HASH160);
|
script.writeOp(OP_HASH160);
|
||||||
script.writeBytes(address.hash);
|
script.writeBytes(address.hash);
|
||||||
script.writeOp(OP_EQUALVERIFY);
|
script.writeOp(OP_EQUALVERIFY);
|
||||||
script.writeOp(OP_CHECKSIG);
|
script.writeOp(OP_CHECKSIG);
|
||||||
return script;
|
return script;
|
||||||
};
|
};
|
||||||
|
|
||||||
Script.createInputScript = function (signature, pubKey)
|
Script.createInputScript = function (signature, pubKey)
|
||||||
{
|
{
|
||||||
var script = new Script();
|
var script = new Script();
|
||||||
script.writeBytes(signature);
|
script.writeBytes(signature);
|
||||||
script.writeBytes(pubKey);
|
script.writeBytes(pubKey);
|
||||||
return script;
|
return script;
|
||||||
};
|
};
|
||||||
|
|
||||||
Script.prototype.clone = function ()
|
Script.prototype.clone = function ()
|
||||||
{
|
{
|
||||||
return new Script(this.buffer);
|
return new Script(this.buffer);
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -1,231 +1,231 @@
|
||||||
(function () {
|
(function () {
|
||||||
var Script = Bitcoin.Script;
|
var Script = Bitcoin.Script;
|
||||||
|
|
||||||
var Transaction = Bitcoin.Transaction = function (doc) {
|
var Transaction = Bitcoin.Transaction = function (doc) {
|
||||||
this.version = 1;
|
this.version = 1;
|
||||||
this.lock_time = 0;
|
this.lock_time = 0;
|
||||||
this.ins = [];
|
this.ins = [];
|
||||||
this.outs = [];
|
this.outs = [];
|
||||||
this.timestamp = null;
|
this.timestamp = null;
|
||||||
this.block = null;
|
this.block = null;
|
||||||
|
|
||||||
if (doc) {
|
if (doc) {
|
||||||
if (doc.hash) this.hash = doc.hash;
|
if (doc.hash) this.hash = doc.hash;
|
||||||
if (doc.version) this.version = doc.version;
|
if (doc.version) this.version = doc.version;
|
||||||
if (doc.lock_time) this.lock_time = doc.lock_time;
|
if (doc.lock_time) this.lock_time = doc.lock_time;
|
||||||
if (doc.ins && doc.ins.length) {
|
if (doc.ins && doc.ins.length) {
|
||||||
for (var i = 0; i < doc.ins.length; i++) {
|
for (var i = 0; i < doc.ins.length; i++) {
|
||||||
this.addInput(new TransactionIn(doc.ins[i]));
|
this.addInput(new TransactionIn(doc.ins[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (doc.outs && doc.outs.length) {
|
if (doc.outs && doc.outs.length) {
|
||||||
for (var i = 0; i < doc.outs.length; i++) {
|
for (var i = 0; i < doc.outs.length; i++) {
|
||||||
this.addOutput(new TransactionOut(doc.outs[i]));
|
this.addOutput(new TransactionOut(doc.outs[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (doc.timestamp) this.timestamp = doc.timestamp;
|
if (doc.timestamp) this.timestamp = doc.timestamp;
|
||||||
if (doc.block) this.block = doc.block;
|
if (doc.block) this.block = doc.block;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Transaction.objectify = function (txs) {
|
Transaction.objectify = function (txs) {
|
||||||
var objs = [];
|
var objs = [];
|
||||||
for (var i = 0; i < txs.length; i++) {
|
for (var i = 0; i < txs.length; i++) {
|
||||||
objs.push(new Transaction(txs[i]));
|
objs.push(new Transaction(txs[i]));
|
||||||
}
|
}
|
||||||
return objs;
|
return objs;
|
||||||
};
|
};
|
||||||
|
|
||||||
Transaction.prototype.addInput = function (tx, outIndex) {
|
Transaction.prototype.addInput = function (tx, outIndex) {
|
||||||
if (arguments[0] instanceof TransactionIn) {
|
if (arguments[0] instanceof TransactionIn) {
|
||||||
this.ins.push(arguments[0]);
|
this.ins.push(arguments[0]);
|
||||||
} else {
|
} else {
|
||||||
this.ins.push(new TransactionIn({
|
this.ins.push(new TransactionIn({
|
||||||
outpoint: {
|
outpoint: {
|
||||||
hash: tx.hash,
|
hash: tx.hash,
|
||||||
index: outIndex
|
index: outIndex
|
||||||
},
|
},
|
||||||
script: new Bitcoin.Script(),
|
script: new Bitcoin.Script(),
|
||||||
sequence: 4294967295
|
sequence: 4294967295
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Transaction.prototype.addOutput = function (address, value) {
|
Transaction.prototype.addOutput = function (address, value) {
|
||||||
if (arguments[0] instanceof TransactionOut) {
|
if (arguments[0] instanceof TransactionOut) {
|
||||||
this.outs.push(arguments[0]);
|
this.outs.push(arguments[0]);
|
||||||
} else {
|
} else {
|
||||||
if (value instanceof BigInteger) {
|
if (value instanceof BigInteger) {
|
||||||
value = value.toByteArrayUnsigned().reverse();
|
value = value.toByteArrayUnsigned().reverse();
|
||||||
while (value.length < 8) value.push(0);
|
while (value.length < 8) value.push(0);
|
||||||
} else if (Bitcoin.Util.isArray(value)) {
|
} else if (Bitcoin.Util.isArray(value)) {
|
||||||
// Nothing to do
|
// Nothing to do
|
||||||
}
|
}
|
||||||
|
|
||||||
this.outs.push(new TransactionOut({
|
this.outs.push(new TransactionOut({
|
||||||
value: value,
|
value: value,
|
||||||
script: Script.createOutputScript(address)
|
script: Script.createOutputScript(address)
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Transaction.prototype.serialize = function ()
|
Transaction.prototype.serialize = function ()
|
||||||
{
|
{
|
||||||
var buffer = [];
|
var buffer = [];
|
||||||
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(this.version)]).reverse());
|
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(this.version)]).reverse());
|
||||||
buffer = buffer.concat(Bitcoin.Util.numToVarInt(this.ins.length));
|
buffer = buffer.concat(Bitcoin.Util.numToVarInt(this.ins.length));
|
||||||
for (var i = 0; i < this.ins.length; i++) {
|
for (var i = 0; i < this.ins.length; i++) {
|
||||||
var txin = this.ins[i];
|
var txin = this.ins[i];
|
||||||
buffer = buffer.concat(Crypto.util.base64ToBytes(txin.outpoint.hash));
|
buffer = buffer.concat(Crypto.util.base64ToBytes(txin.outpoint.hash));
|
||||||
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(txin.outpoint.index)]).reverse());
|
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(txin.outpoint.index)]).reverse());
|
||||||
var scriptBytes = txin.script.buffer;
|
var scriptBytes = txin.script.buffer;
|
||||||
buffer = buffer.concat(Bitcoin.Util.numToVarInt(scriptBytes.length));
|
buffer = buffer.concat(Bitcoin.Util.numToVarInt(scriptBytes.length));
|
||||||
buffer = buffer.concat(scriptBytes);
|
buffer = buffer.concat(scriptBytes);
|
||||||
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(txin.sequence)]).reverse());
|
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(txin.sequence)]).reverse());
|
||||||
}
|
}
|
||||||
buffer = buffer.concat(Bitcoin.Util.numToVarInt(this.outs.length));
|
buffer = buffer.concat(Bitcoin.Util.numToVarInt(this.outs.length));
|
||||||
for (var i = 0; i < this.outs.length; i++) {
|
for (var i = 0; i < this.outs.length; i++) {
|
||||||
var txout = this.outs[i];
|
var txout = this.outs[i];
|
||||||
buffer = buffer.concat(txout.value);
|
buffer = buffer.concat(txout.value);
|
||||||
var scriptBytes = txout.script.buffer;
|
var scriptBytes = txout.script.buffer;
|
||||||
buffer = buffer.concat(Bitcoin.Util.numToVarInt(scriptBytes.length));
|
buffer = buffer.concat(Bitcoin.Util.numToVarInt(scriptBytes.length));
|
||||||
buffer = buffer.concat(scriptBytes);
|
buffer = buffer.concat(scriptBytes);
|
||||||
}
|
}
|
||||||
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(this.lock_time)]).reverse());
|
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_ALL = 1;
|
||||||
var SIGHASH_NONE = 2;
|
var SIGHASH_NONE = 2;
|
||||||
var SIGHASH_SINGLE = 3;
|
var SIGHASH_SINGLE = 3;
|
||||||
var SIGHASH_ANYONECANPAY = 80;
|
var SIGHASH_ANYONECANPAY = 80;
|
||||||
|
|
||||||
Transaction.prototype.hashTransactionForSignature = function (connectedScript, inIndex, hashType)
|
Transaction.prototype.hashTransactionForSignature = function (connectedScript, inIndex, hashType)
|
||||||
{
|
{
|
||||||
var txTmp = this.clone();
|
var txTmp = this.clone();
|
||||||
|
|
||||||
// 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.
|
// or an extra one at the end, this prevents all those possible incompatibilities.
|
||||||
/*scriptCode = scriptCode.filter(function (val) {
|
/*scriptCode = scriptCode.filter(function (val) {
|
||||||
return val !== OP_CODESEPARATOR;
|
return val !== OP_CODESEPARATOR;
|
||||||
});*/
|
});*/
|
||||||
|
|
||||||
// Blank out other inputs' signatures
|
// Blank out other inputs' signatures
|
||||||
for (var i = 0; i < txTmp.ins.length; i++) {
|
for (var i = 0; i < txTmp.ins.length; i++) {
|
||||||
txTmp.ins[i].script = new Script();
|
txTmp.ins[i].script = new Script();
|
||||||
}
|
}
|
||||||
|
|
||||||
txTmp.ins[inIndex].script = connectedScript;
|
txTmp.ins[inIndex].script = connectedScript;
|
||||||
|
|
||||||
// Blank out some of the outputs
|
// Blank out some of the outputs
|
||||||
if ((hashType & 0x1f) == SIGHASH_NONE) {
|
if ((hashType & 0x1f) == SIGHASH_NONE) {
|
||||||
txTmp.outs = [];
|
txTmp.outs = [];
|
||||||
|
|
||||||
// Let the others update at will
|
// Let the others update at will
|
||||||
for (var i = 0; i < txTmp.ins.length; i++)
|
for (var i = 0; i < txTmp.ins.length; i++)
|
||||||
if (i != inIndex)
|
if (i != inIndex)
|
||||||
txTmp.ins[i].sequence = 0;
|
txTmp.ins[i].sequence = 0;
|
||||||
} else if ((hashType & 0x1f) == SIGHASH_SINGLE) {
|
} else if ((hashType & 0x1f) == SIGHASH_SINGLE) {
|
||||||
// TODO: Implement
|
// TODO: Implement
|
||||||
}
|
}
|
||||||
|
|
||||||
// Blank out other inputs completely, not recommended for open transactions
|
// Blank out other inputs completely, not recommended for open transactions
|
||||||
if (hashType & SIGHASH_ANYONECANPAY) {
|
if (hashType & SIGHASH_ANYONECANPAY) {
|
||||||
txTmp.ins = [txTmp.ins[inIndex]];
|
txTmp.ins = [txTmp.ins[inIndex]];
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(txTmp);
|
console.log(txTmp);
|
||||||
var buffer = txTmp.serialize();
|
var buffer = txTmp.serialize();
|
||||||
|
|
||||||
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(hashType)]).reverse());
|
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(hashType)]).reverse());
|
||||||
|
|
||||||
console.log("signtx: "+Crypto.util.bytesToHex(buffer));
|
console.log("signtx: "+Crypto.util.bytesToHex(buffer));
|
||||||
|
|
||||||
var hash1 = Crypto.SHA256(buffer, {asBytes: true});
|
var hash1 = Crypto.SHA256(buffer, {asBytes: true});
|
||||||
|
|
||||||
console.log("sha256_1: ", Crypto.util.bytesToHex(hash1));
|
console.log("sha256_1: ", Crypto.util.bytesToHex(hash1));
|
||||||
|
|
||||||
return Crypto.SHA256(hash1, {asBytes: true});
|
return Crypto.SHA256(hash1, {asBytes: true});
|
||||||
};
|
};
|
||||||
|
|
||||||
Transaction.prototype.getHash = function ()
|
Transaction.prototype.getHash = function ()
|
||||||
{
|
{
|
||||||
var buffer = this.serialize();
|
var buffer = this.serialize();
|
||||||
return Crypto.SHA256(Crypto.SHA256(buffer, {asBytes: true}), {asBytes: true});
|
return Crypto.SHA256(Crypto.SHA256(buffer, {asBytes: true}), {asBytes: true});
|
||||||
};
|
};
|
||||||
|
|
||||||
Transaction.prototype.clone = function ()
|
Transaction.prototype.clone = function ()
|
||||||
{
|
{
|
||||||
var newTx = new Transaction();
|
var newTx = new Transaction();
|
||||||
newTx.version = this.version;
|
newTx.version = this.version;
|
||||||
newTx.lock_time = this.lock_time;
|
newTx.lock_time = this.lock_time;
|
||||||
for (var i = 0; i < this.ins.length; i++) {
|
for (var i = 0; i < this.ins.length; i++) {
|
||||||
var txin = this.ins[i].clone();
|
var txin = this.ins[i].clone();
|
||||||
newTx.addInput(txin);
|
newTx.addInput(txin);
|
||||||
}
|
}
|
||||||
for (var i = 0; i < this.outs.length; i++) {
|
for (var i = 0; i < this.outs.length; i++) {
|
||||||
var txout = this.outs[i].clone();
|
var txout = this.outs[i].clone();
|
||||||
newTx.addOutput(txout);
|
newTx.addOutput(txout);
|
||||||
}
|
}
|
||||||
return newTx;
|
return newTx;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Analyze how this transaction affects a wallet.
|
* Analyze how this transaction affects a wallet.
|
||||||
*/
|
*/
|
||||||
Transaction.prototype.analyze = function (wallet) {
|
Transaction.prototype.analyze = function (wallet) {
|
||||||
if (!(wallet instanceof Bitcoin.Wallet)) return null;
|
if (!(wallet instanceof Bitcoin.Wallet)) return null;
|
||||||
|
|
||||||
var allFromMe = true,
|
var allFromMe = true,
|
||||||
allToMe = true,
|
allToMe = true,
|
||||||
firstRecvHash = null,
|
firstRecvHash = null,
|
||||||
firstMeRecvHash = null,
|
firstMeRecvHash = null,
|
||||||
firstSendHash = null;
|
firstSendHash = null;
|
||||||
|
|
||||||
for (var i = this.outs.length-1; i >= 0; i--) {
|
for (var i = this.outs.length-1; i >= 0; i--) {
|
||||||
var txout = this.outs[i];
|
var txout = this.outs[i];
|
||||||
var hash = txout.script.simpleOutPubKeyHash();
|
var hash = txout.script.simpleOutPubKeyHash();
|
||||||
if (!wallet.hasHash(hash)) {
|
if (!wallet.hasHash(hash)) {
|
||||||
allToMe = false;
|
allToMe = false;
|
||||||
} else {
|
} else {
|
||||||
firstMeRecvHash = hash;
|
firstMeRecvHash = hash;
|
||||||
}
|
}
|
||||||
firstRecvHash = hash;
|
firstRecvHash = hash;
|
||||||
}
|
}
|
||||||
for (var i = this.ins.length-1; i >= 0; i--) {
|
for (var i = this.ins.length-1; i >= 0; i--) {
|
||||||
var txin = this.ins[i];
|
var txin = this.ins[i];
|
||||||
firstSendHash = txin.script.simpleInPubKeyHash();
|
firstSendHash = txin.script.simpleInPubKeyHash();
|
||||||
if (!wallet.hasHash(firstSendHash)) {
|
if (!wallet.hasHash(firstSendHash)) {
|
||||||
allFromMe = false;
|
allFromMe = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var impact = this.calcImpact(wallet);
|
var impact = this.calcImpact(wallet);
|
||||||
|
|
||||||
var analysis = {};
|
var analysis = {};
|
||||||
|
|
||||||
analysis.impact = impact;
|
analysis.impact = impact;
|
||||||
|
|
||||||
if (impact.sign > 0 && impact.value.compareTo(BigInteger.ZERO) > 0) {
|
if (impact.sign > 0 && impact.value.compareTo(BigInteger.ZERO) > 0) {
|
||||||
analysis.type = 'recv';
|
analysis.type = 'recv';
|
||||||
analysis.addr = new Bitcoin.Address(firstMeRecvHash);
|
analysis.addr = new Bitcoin.Address(firstMeRecvHash);
|
||||||
} else if (allFromMe && allToMe) {
|
} else if (allFromMe && allToMe) {
|
||||||
analysis.type = 'self';
|
analysis.type = 'self';
|
||||||
} else if (allFromMe) {
|
} else if (allFromMe) {
|
||||||
analysis.type = 'sent';
|
analysis.type = 'sent';
|
||||||
analysis.addr = new Bitcoin.Address(firstRecvHash);
|
analysis.addr = new Bitcoin.Address(firstRecvHash);
|
||||||
} else {
|
} else {
|
||||||
analysis.type = "other";
|
analysis.type = "other";
|
||||||
}
|
}
|
||||||
|
|
||||||
return analysis;
|
return analysis;
|
||||||
};
|
};
|
||||||
|
|
||||||
Transaction.prototype.getDescription = function (wallet) {
|
Transaction.prototype.getDescription = function (wallet) {
|
||||||
var analysis = this.analyze(wallet);
|
var analysis = this.analyze(wallet);
|
||||||
|
|
||||||
if (!analysis) return "";
|
if (!analysis) return "";
|
||||||
|
@ -236,127 +236,127 @@
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'sent':
|
case 'sent':
|
||||||
return "Payment to "+analysis.addr;
|
return "Payment to "+analysis.addr;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'self':
|
case 'self':
|
||||||
return "Payment to yourself";
|
return "Payment to yourself";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'other':
|
case 'other':
|
||||||
default:
|
default:
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Transaction.prototype.getTotalValue = function () {
|
Transaction.prototype.getTotalValue = function () {
|
||||||
var totalValue = BigInteger.ZERO;
|
var totalValue = BigInteger.ZERO;
|
||||||
for (var j = 0; j < this.outs.length; j++) {
|
for (var j = 0; j < this.outs.length; j++) {
|
||||||
var txout = this.outs[j];
|
var txout = this.outs[j];
|
||||||
totalValue = totalValue.add(Bitcoin.Util.valueToBigInt(txout.value));
|
totalValue = totalValue.add(Bitcoin.Util.valueToBigInt(txout.value));
|
||||||
}
|
}
|
||||||
return totalValue;
|
return totalValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the impact a transaction has on this wallet.
|
* Calculates the impact a transaction has on this wallet.
|
||||||
*
|
*
|
||||||
* Based on the its public keys, the wallet will calculate the
|
* Based on the its public keys, the wallet will calculate the
|
||||||
* credit or debit of this transaction.
|
* credit or debit of this transaction.
|
||||||
*
|
*
|
||||||
* It will return an object with two properties:
|
* It will return an object with two properties:
|
||||||
* - sign: 1 or -1 depending on sign of the calculated impact.
|
* - sign: 1 or -1 depending on sign of the calculated impact.
|
||||||
* - value: amount of calculated impact
|
* - value: amount of calculated impact
|
||||||
*
|
*
|
||||||
* @returns Object Impact on wallet
|
* @returns Object Impact on wallet
|
||||||
*/
|
*/
|
||||||
Transaction.prototype.calcImpact = function (wallet) {
|
Transaction.prototype.calcImpact = function (wallet) {
|
||||||
if (!(wallet instanceof Bitcoin.Wallet)) return BigInteger.ZERO;
|
if (!(wallet instanceof Bitcoin.Wallet)) return BigInteger.ZERO;
|
||||||
|
|
||||||
// Calculate credit to us from all outputs
|
// Calculate credit to us from all outputs
|
||||||
var valueOut = BigInteger.ZERO;
|
var valueOut = BigInteger.ZERO;
|
||||||
for (var j = 0; j < this.outs.length; j++) {
|
for (var j = 0; j < this.outs.length; j++) {
|
||||||
var txout = this.outs[j];
|
var txout = this.outs[j];
|
||||||
var hash = Crypto.util.bytesToBase64(txout.script.simpleOutPubKeyHash());
|
var hash = Crypto.util.bytesToBase64(txout.script.simpleOutPubKeyHash());
|
||||||
if (wallet.hasHash(hash)) {
|
if (wallet.hasHash(hash)) {
|
||||||
valueOut = valueOut.add(Bitcoin.Util.valueToBigInt(txout.value));
|
valueOut = valueOut.add(Bitcoin.Util.valueToBigInt(txout.value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate debit to us from all ins
|
// Calculate debit to us from all ins
|
||||||
var valueIn = BigInteger.ZERO;
|
var valueIn = BigInteger.ZERO;
|
||||||
for (var j = 0; j < this.ins.length; j++) {
|
for (var j = 0; j < this.ins.length; j++) {
|
||||||
var txin = this.ins[j];
|
var txin = this.ins[j];
|
||||||
var hash = Crypto.util.bytesToBase64(txin.script.simpleInPubKeyHash());
|
var hash = Crypto.util.bytesToBase64(txin.script.simpleInPubKeyHash());
|
||||||
if (wallet.hasHash(hash)) {
|
if (wallet.hasHash(hash)) {
|
||||||
var fromTx = wallet.txIndex[txin.outpoint.hash];
|
var fromTx = wallet.txIndex[txin.outpoint.hash];
|
||||||
if (fromTx) {
|
if (fromTx) {
|
||||||
valueIn = valueIn.add(Bitcoin.Util.valueToBigInt(fromTx.outs[txin.outpoint.index].value));
|
valueIn = valueIn.add(Bitcoin.Util.valueToBigInt(fromTx.outs[txin.outpoint.index].value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (valueOut.compareTo(valueIn) >= 0) {
|
if (valueOut.compareTo(valueIn) >= 0) {
|
||||||
return {
|
return {
|
||||||
sign: 1,
|
sign: 1,
|
||||||
value: valueOut.subtract(valueIn)
|
value: valueOut.subtract(valueIn)
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
sign: -1,
|
sign: -1,
|
||||||
value: valueIn.subtract(valueOut)
|
value: valueIn.subtract(valueOut)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var TransactionIn = Bitcoin.TransactionIn = function (data)
|
var TransactionIn = Bitcoin.TransactionIn = function (data)
|
||||||
{
|
{
|
||||||
this.outpoint = data.outpoint;
|
this.outpoint = data.outpoint;
|
||||||
if (data.script instanceof Script) {
|
if (data.script instanceof Script) {
|
||||||
this.script = data.script;
|
this.script = data.script;
|
||||||
} else {
|
} else {
|
||||||
this.script = new Script(data.script);
|
this.script = new Script(data.script);
|
||||||
}
|
}
|
||||||
this.sequence = data.sequence;
|
this.sequence = data.sequence;
|
||||||
};
|
};
|
||||||
|
|
||||||
TransactionIn.prototype.clone = function ()
|
TransactionIn.prototype.clone = function ()
|
||||||
{
|
{
|
||||||
var newTxin = new TransactionIn({
|
var newTxin = new TransactionIn({
|
||||||
outpoint: {
|
outpoint: {
|
||||||
hash: this.outpoint.hash,
|
hash: this.outpoint.hash,
|
||||||
index: this.outpoint.index
|
index: this.outpoint.index
|
||||||
},
|
},
|
||||||
script: this.script.clone(),
|
script: this.script.clone(),
|
||||||
sequence: this.sequence
|
sequence: this.sequence
|
||||||
});
|
});
|
||||||
return newTxin;
|
return newTxin;
|
||||||
};
|
};
|
||||||
|
|
||||||
var TransactionOut = Bitcoin.TransactionOut = function (data)
|
var TransactionOut = Bitcoin.TransactionOut = function (data)
|
||||||
{
|
{
|
||||||
if (data.script instanceof Script) {
|
if (data.script instanceof Script) {
|
||||||
this.script = data.script;
|
this.script = data.script;
|
||||||
} else {
|
} else {
|
||||||
this.script = new Script(data.script);
|
this.script = new Script(data.script);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Bitcoin.Util.isArray(data.value)) {
|
if (Bitcoin.Util.isArray(data.value)) {
|
||||||
this.value = data.value;
|
this.value = data.value;
|
||||||
} else if ("string" == typeof data.value) {
|
} else if ("string" == typeof data.value) {
|
||||||
var valueHex = (new BigInteger(data.value, 10)).toString(16);
|
var valueHex = (new BigInteger(data.value, 10)).toString(16);
|
||||||
while (valueHex.length < 16) valueHex = "0" + valueHex;
|
while (valueHex.length < 16) valueHex = "0" + valueHex;
|
||||||
this.value = Crypto.util.hexToBytes(valueHex);
|
this.value = Crypto.util.hexToBytes(valueHex);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TransactionOut.prototype.clone = function ()
|
TransactionOut.prototype.clone = function ()
|
||||||
{
|
{
|
||||||
var newTxout = new TransactionOut({
|
var newTxout = new TransactionOut({
|
||||||
script: this.script.clone(),
|
script: this.script.clone(),
|
||||||
value: this.value.slice(0)
|
value: this.value.slice(0)
|
||||||
});
|
});
|
||||||
return newTxout;
|
return newTxout;
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
|
60
src/txdb.js
60
src/txdb.js
|
@ -1,58 +1,58 @@
|
||||||
var TransactionDatabase = function () {
|
var TransactionDatabase = function () {
|
||||||
this.txs = [];
|
this.txs = [];
|
||||||
this.txIndex = {};
|
this.txIndex = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
EventEmitter.augment(TransactionDatabase.prototype);
|
EventEmitter.augment(TransactionDatabase.prototype);
|
||||||
|
|
||||||
TransactionDatabase.prototype.addTransaction = function (tx) {
|
TransactionDatabase.prototype.addTransaction = function (tx) {
|
||||||
this.addTransactionNoUpdate(tx);
|
this.addTransactionNoUpdate(tx);
|
||||||
$(this).trigger('update');
|
$(this).trigger('update');
|
||||||
};
|
};
|
||||||
|
|
||||||
TransactionDatabase.prototype.addTransactionNoUpdate = function (tx) {
|
TransactionDatabase.prototype.addTransactionNoUpdate = function (tx) {
|
||||||
// Return if transaction is already known
|
// Return if transaction is already known
|
||||||
if (this.txIndex[tx.hash]) {
|
if (this.txIndex[tx.hash]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.txs.push(new Bitcoin.Transaction(tx));
|
this.txs.push(new Bitcoin.Transaction(tx));
|
||||||
this.txIndex[tx.hash] = tx;
|
this.txIndex[tx.hash] = tx;
|
||||||
};
|
};
|
||||||
|
|
||||||
TransactionDatabase.prototype.removeTransaction = function (hash) {
|
TransactionDatabase.prototype.removeTransaction = function (hash) {
|
||||||
this.removeTransactionNoUpdate(hash);
|
this.removeTransactionNoUpdate(hash);
|
||||||
$(this).trigger('update');
|
$(this).trigger('update');
|
||||||
};
|
};
|
||||||
|
|
||||||
TransactionDatabase.prototype.removeTransactionNoUpdate = function (hash) {
|
TransactionDatabase.prototype.removeTransactionNoUpdate = function (hash) {
|
||||||
var tx = this.txIndex[hash];
|
var tx = this.txIndex[hash];
|
||||||
|
|
||||||
if (!tx) {
|
if (!tx) {
|
||||||
// If the tx is not in the index, we don't actually waste our
|
// If the tx is not in the index, we don't actually waste our
|
||||||
// time looping through the array.
|
// time looping through the array.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0, l = this.txs.length; i < l; i++) {
|
for (var i = 0, l = this.txs.length; i < l; i++) {
|
||||||
if (this.txs[i].hash == hash) {
|
if (this.txs[i].hash == hash) {
|
||||||
this.txs.splice(i, 1);
|
this.txs.splice(i, 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete this.txIndex[hash];
|
delete this.txIndex[hash];
|
||||||
};
|
};
|
||||||
|
|
||||||
TransactionDatabase.prototype.loadTransactions = function (txs) {
|
TransactionDatabase.prototype.loadTransactions = function (txs) {
|
||||||
for (var i = 0; i < txs.length; i++) {
|
for (var i = 0; i < txs.length; i++) {
|
||||||
this.addTransactionNoUpdate(txs[i]);
|
this.addTransactionNoUpdate(txs[i]);
|
||||||
}
|
}
|
||||||
$(this).trigger('update');
|
$(this).trigger('update');
|
||||||
};
|
};
|
||||||
|
|
||||||
TransactionDatabase.prototype.getTransactions = function () {
|
TransactionDatabase.prototype.getTransactions = function () {
|
||||||
return this.txs;
|
return this.txs;
|
||||||
};
|
};
|
||||||
|
|
||||||
TransactionDatabase.prototype.clear = function () {
|
TransactionDatabase.prototype.clear = function () {
|
||||||
|
|
154
src/util.js
154
src/util.js
|
@ -1,99 +1,99 @@
|
||||||
// BigInteger monkey patching
|
// BigInteger monkey patching
|
||||||
BigInteger.valueOf = nbv;
|
BigInteger.valueOf = nbv;
|
||||||
BigInteger.prototype.toByteArrayUnsigned = function () {
|
BigInteger.prototype.toByteArrayUnsigned = function () {
|
||||||
var ba = this.toByteArray();
|
var ba = this.toByteArray();
|
||||||
if (ba.length) {
|
if (ba.length) {
|
||||||
if (ba[0] == 0) {
|
if (ba[0] == 0) {
|
||||||
ba = ba.slice(1);
|
ba = ba.slice(1);
|
||||||
}
|
}
|
||||||
return ba.map(function (v) {
|
return ba.map(function (v) {
|
||||||
return (v < 0) ? v + 256 : v;
|
return (v < 0) ? v + 256 : v;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Empty array, nothing to do
|
// Empty array, nothing to do
|
||||||
return ba;
|
return ba;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
BigInteger.fromByteArrayUnsigned = function (ba) {
|
BigInteger.fromByteArrayUnsigned = function (ba) {
|
||||||
if (!ba.length) {
|
if (!ba.length) {
|
||||||
return ba.valueOf(0);
|
return ba.valueOf(0);
|
||||||
} else if (ba[0] & 0x80) {
|
} else if (ba[0] & 0x80) {
|
||||||
// Prepend a zero so the BigInteger class doesn't mistake this
|
// Prepend a zero so the BigInteger class doesn't mistake this
|
||||||
// for a negative integer.
|
// for a negative integer.
|
||||||
return new BigInteger([0].concat(ba));
|
return new BigInteger([0].concat(ba));
|
||||||
} else {
|
} else {
|
||||||
return new BigInteger(ba);
|
return new BigInteger(ba);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Console ignore
|
// Console ignore
|
||||||
var names = ["log", "debug", "info", "warn", "error", "assert", "dir",
|
var names = ["log", "debug", "info", "warn", "error", "assert", "dir",
|
||||||
"dirxml", "group", "groupEnd", "time", "timeEnd", "count",
|
"dirxml", "group", "groupEnd", "time", "timeEnd", "count",
|
||||||
"trace", "profile", "profileEnd"];
|
"trace", "profile", "profileEnd"];
|
||||||
|
|
||||||
if ("undefined" == typeof window.console) window.console = {};
|
if ("undefined" == typeof window.console) window.console = {};
|
||||||
for (var i = 0; i < names.length; ++i)
|
for (var i = 0; i < names.length; ++i)
|
||||||
if ("undefined" == typeof window.console[names[i]])
|
if ("undefined" == typeof window.console[names[i]])
|
||||||
window.console[names[i]] = function() {};
|
window.console[names[i]] = function() {};
|
||||||
|
|
||||||
// Bitcoin utility functions
|
// Bitcoin utility functions
|
||||||
Bitcoin.Util = {
|
Bitcoin.Util = {
|
||||||
isArray: Array.isArray || function(o) {
|
isArray: Array.isArray || function(o) {
|
||||||
return Object.prototype.toString.call(o) === '[object Array]';
|
return Object.prototype.toString.call(o) === '[object Array]';
|
||||||
},
|
},
|
||||||
makeFilledArray: function (len, val) {
|
makeFilledArray: function (len, val) {
|
||||||
var array = [];
|
var array = [];
|
||||||
var i = 0;
|
var i = 0;
|
||||||
while (i < len) {
|
while (i < len) {
|
||||||
array[i++] = val;
|
array[i++] = val;
|
||||||
}
|
}
|
||||||
return array;
|
return array;
|
||||||
},
|
},
|
||||||
numToVarInt: function (i) {
|
numToVarInt: function (i) {
|
||||||
// TODO: THIS IS TOTALLY UNTESTED!
|
// TODO: THIS IS TOTALLY UNTESTED!
|
||||||
if (i < 0xfd) {
|
if (i < 0xfd) {
|
||||||
// unsigned char
|
// unsigned char
|
||||||
return [i];
|
return [i];
|
||||||
} else if (i <= 1<<16) {
|
} else if (i <= 1<<16) {
|
||||||
// unsigned short (LE)
|
// unsigned short (LE)
|
||||||
return [0xfd, i >>> 8, i & 255];
|
return [0xfd, i >>> 8, i & 255];
|
||||||
} else if (i <= 1<<32) {
|
} else if (i <= 1<<32) {
|
||||||
// unsigned int (LE)
|
// unsigned int (LE)
|
||||||
return [0xfe].concat(Crypto.util.wordsToBytes([i]));
|
return [0xfe].concat(Crypto.util.wordsToBytes([i]));
|
||||||
} else {
|
} else {
|
||||||
// unsigned long long (LE)
|
// unsigned long long (LE)
|
||||||
return [0xff].concat(Crypto.util.wordsToBytes([i >>> 32, i]));
|
return [0xff].concat(Crypto.util.wordsToBytes([i >>> 32, i]));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
valueToBigInt: function (valueBuffer) {
|
valueToBigInt: function (valueBuffer) {
|
||||||
if (valueBuffer instanceof BigInteger) return valueBuffer;
|
if (valueBuffer instanceof BigInteger) return valueBuffer;
|
||||||
|
|
||||||
// Prepend zero byte to prevent interpretation as negative integer
|
// Prepend zero byte to prevent interpretation as negative integer
|
||||||
return BigInteger.fromByteArrayUnsigned(valueBuffer);
|
return BigInteger.fromByteArrayUnsigned(valueBuffer);
|
||||||
},
|
},
|
||||||
formatValue: function (valueBuffer) {
|
formatValue: function (valueBuffer) {
|
||||||
var value = this.valueToBigInt(valueBuffer).toString();
|
var value = this.valueToBigInt(valueBuffer).toString();
|
||||||
var integerPart = value.length > 8 ? value.substr(0, value.length-8) : '0';
|
var integerPart = value.length > 8 ? value.substr(0, value.length-8) : '0';
|
||||||
var decimalPart = value.length > 8 ? value.substr(value.length-8) : value;
|
var decimalPart = value.length > 8 ? value.substr(value.length-8) : value;
|
||||||
while (decimalPart.length < 8) decimalPart = "0"+decimalPart;
|
while (decimalPart.length < 8) decimalPart = "0"+decimalPart;
|
||||||
decimalPart = decimalPart.replace(/0*$/, '');
|
decimalPart = decimalPart.replace(/0*$/, '');
|
||||||
while (decimalPart.length < 2) decimalPart += "0";
|
while (decimalPart.length < 2) decimalPart += "0";
|
||||||
return integerPart+"."+decimalPart;
|
return integerPart+"."+decimalPart;
|
||||||
},
|
},
|
||||||
parseValue: function (valueString) {
|
parseValue: function (valueString) {
|
||||||
var valueComp = valueString.split('.');
|
var valueComp = valueString.split('.');
|
||||||
var integralPart = valueComp[0];
|
var integralPart = valueComp[0];
|
||||||
var fractionalPart = valueComp[1] || "0";
|
var fractionalPart = valueComp[1] || "0";
|
||||||
while (fractionalPart.length < 8) fractionalPart += "0";
|
while (fractionalPart.length < 8) fractionalPart += "0";
|
||||||
fractionalPart = fractionalPart.replace(/^0+/g, '');
|
fractionalPart = fractionalPart.replace(/^0+/g, '');
|
||||||
var value = BigInteger.valueOf(parseInt(integralPart));
|
var value = BigInteger.valueOf(parseInt(integralPart));
|
||||||
value = value.multiply(BigInteger.valueOf(100000000));
|
value = value.multiply(BigInteger.valueOf(100000000));
|
||||||
value = value.add(BigInteger.valueOf(parseInt(fractionalPart)));
|
value = value.add(BigInteger.valueOf(parseInt(fractionalPart)));
|
||||||
return value;
|
return value;
|
||||||
},
|
},
|
||||||
sha256ripe160: function (data) {
|
sha256ripe160: function (data) {
|
||||||
return Crypto.RIPEMD160(Crypto.SHA256(data, {asBytes: true}), {asBytes: true});
|
return Crypto.RIPEMD160(Crypto.SHA256(data, {asBytes: true}), {asBytes: true});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (var i in Crypto.util) {
|
for (var i in Crypto.util) {
|
||||||
|
|
420
src/wallet.js
420
src/wallet.js
|
@ -1,245 +1,245 @@
|
||||||
Bitcoin.Wallet = (function () {
|
Bitcoin.Wallet = (function () {
|
||||||
var Script = Bitcoin.Script,
|
var Script = Bitcoin.Script,
|
||||||
TransactionIn = Bitcoin.TransactionIn,
|
TransactionIn = Bitcoin.TransactionIn,
|
||||||
TransactionOut = Bitcoin.TransactionOut;
|
TransactionOut = Bitcoin.TransactionOut;
|
||||||
|
|
||||||
var Wallet = function () {
|
var Wallet = function () {
|
||||||
// Keychain
|
// Keychain
|
||||||
var keys = [];
|
var keys = [];
|
||||||
this.addressHashes = [];
|
this.addressHashes = [];
|
||||||
|
|
||||||
// Transaction data
|
// Transaction data
|
||||||
this.txIndex = {};
|
this.txIndex = {};
|
||||||
this.unspentOuts = [];
|
this.unspentOuts = [];
|
||||||
|
|
||||||
// Other fields
|
// Other fields
|
||||||
this.addressPointer = 0;
|
this.addressPointer = 0;
|
||||||
|
|
||||||
this.addKey = function (key, pub) {
|
this.addKey = function (key, pub) {
|
||||||
if (!(key instanceof Bitcoin.ECKey)) {
|
if (!(key instanceof Bitcoin.ECKey)) {
|
||||||
key = new Bitcoin.ECKey(key);
|
key = new Bitcoin.ECKey(key);
|
||||||
}
|
}
|
||||||
keys.push(key);
|
keys.push(key);
|
||||||
|
|
||||||
if (pub) {
|
if (pub) {
|
||||||
if ("string" === typeof pub) {
|
if ("string" === typeof pub) {
|
||||||
pub = Crypto.util.base64ToBytes(pub);
|
pub = Crypto.util.base64ToBytes(pub);
|
||||||
}
|
}
|
||||||
key.pub = pub;
|
key.pub = pub;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.addressHashes.push(key.getBitcoinAddress().getHashBase64());
|
this.addressHashes.push(key.getBitcoinAddress().getHashBase64());
|
||||||
};
|
|
||||||
|
|
||||||
this.addKeys = function (keys, pubs) {
|
|
||||||
if ("string" === typeof keys) {
|
|
||||||
keys = keys.split(',');
|
|
||||||
}
|
|
||||||
if ("string" === typeof pubs) {
|
|
||||||
pubs = pubs.split(',');
|
|
||||||
}
|
|
||||||
var i;
|
|
||||||
if (Array.isArray(pubs) && keys.length == pubs.length) {
|
|
||||||
for (i = 0; i < keys.length; i++) {
|
|
||||||
this.addKey(keys[i], pubs[i]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (i = 0; i < keys.length; i++) {
|
|
||||||
this.addKey(keys[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getKeys = function () {
|
|
||||||
var serializedWallet = [];
|
|
||||||
|
|
||||||
for (var i = 0; i < keys.length; i++) {
|
|
||||||
serializedWallet.push(keys[i].toString('base64'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return serializedWallet;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getPubKeys = function () {
|
|
||||||
var pubs = [];
|
|
||||||
|
|
||||||
for (var i = 0; i < keys.length; i++) {
|
|
||||||
pubs.push(Crypto.util.bytesToBase64(keys[i].getPub()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return pubs;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.clear = function () {
|
|
||||||
keys = [];
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getLength = function () {
|
|
||||||
return keys.length;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getAllAddresses = function () {
|
|
||||||
var addresses = [];
|
|
||||||
for (var i = 0; i < keys.length; i++) {
|
|
||||||
addresses.push(keys[i].getBitcoinAddress());
|
|
||||||
}
|
|
||||||
return addresses;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getCurAddress = function () {
|
|
||||||
if (keys[this.addressPointer]) {
|
|
||||||
return keys[this.addressPointer].getBitcoinAddress();
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getNextAddress = function () {
|
|
||||||
this.addressPointer++;
|
|
||||||
if(!keys[this.addressPointer]) {
|
|
||||||
this.generateAddress();
|
|
||||||
}
|
|
||||||
return keys[this.addressPointer].getBitcoinAddress();
|
|
||||||
};
|
|
||||||
|
|
||||||
this.signWithKey = function (pubKeyHash, hash) {
|
|
||||||
pubKeyHash = Crypto.util.bytesToBase64(pubKeyHash);
|
|
||||||
for (var i = 0; i < this.addressHashes.length; i++) {
|
|
||||||
if (this.addressHashes[i] == pubKeyHash) {
|
|
||||||
return keys[i].sign(hash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new Error("Missing key for signature");
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getPubKeyFromHash = function (pubKeyHash) {
|
|
||||||
pubKeyHash = Crypto.util.bytesToBase64(pubKeyHash);
|
|
||||||
for (var i = 0; i < this.addressHashes.length; i++) {
|
|
||||||
if (this.addressHashes[i] == pubKeyHash) {
|
|
||||||
return keys[i].getPub();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new Error("Hash unknown");
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.generateAddress = function () {
|
this.addKeys = function (keys, pubs) {
|
||||||
this.addKey(new Bitcoin.ECKey());
|
if ("string" === typeof keys) {
|
||||||
|
keys = keys.split(',');
|
||||||
|
}
|
||||||
|
if ("string" === typeof pubs) {
|
||||||
|
pubs = pubs.split(',');
|
||||||
|
}
|
||||||
|
var i;
|
||||||
|
if (Array.isArray(pubs) && keys.length == pubs.length) {
|
||||||
|
for (i = 0; i < keys.length; i++) {
|
||||||
|
this.addKey(keys[i], pubs[i]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (i = 0; i < keys.length; i++) {
|
||||||
|
this.addKey(keys[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.process = function (tx) {
|
this.getKeys = function () {
|
||||||
if (this.txIndex[tx.hash]) return;
|
var serializedWallet = [];
|
||||||
|
|
||||||
var j;
|
for (var i = 0; i < keys.length; i++) {
|
||||||
var k;
|
serializedWallet.push(keys[i].toString('base64'));
|
||||||
var hash;
|
}
|
||||||
// Gather outputs
|
|
||||||
for (j = 0; j < tx.outs.length; j++) {
|
return serializedWallet;
|
||||||
var txout = new TransactionOut(tx.outs[j]);
|
};
|
||||||
hash = Crypto.util.bytesToBase64(txout.script.simpleOutPubKeyHash());
|
|
||||||
for (k = 0; k < this.addressHashes.length; k++) {
|
this.getPubKeys = function () {
|
||||||
if (this.addressHashes[k] === hash) {
|
var pubs = [];
|
||||||
this.unspentOuts.push({tx: tx, index: j, out: txout});
|
|
||||||
break;
|
for (var i = 0; i < keys.length; i++) {
|
||||||
}
|
pubs.push(Crypto.util.bytesToBase64(keys[i].getPub()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return pubs;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.clear = function () {
|
||||||
|
keys = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getLength = function () {
|
||||||
|
return keys.length;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getAllAddresses = function () {
|
||||||
|
var addresses = [];
|
||||||
|
for (var i = 0; i < keys.length; i++) {
|
||||||
|
addresses.push(keys[i].getBitcoinAddress());
|
||||||
|
}
|
||||||
|
return addresses;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getCurAddress = function () {
|
||||||
|
if (keys[this.addressPointer]) {
|
||||||
|
return keys[this.addressPointer].getBitcoinAddress();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getNextAddress = function () {
|
||||||
|
this.addressPointer++;
|
||||||
|
if(!keys[this.addressPointer]) {
|
||||||
|
this.generateAddress();
|
||||||
|
}
|
||||||
|
return keys[this.addressPointer].getBitcoinAddress();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.signWithKey = function (pubKeyHash, hash) {
|
||||||
|
pubKeyHash = Crypto.util.bytesToBase64(pubKeyHash);
|
||||||
|
for (var i = 0; i < this.addressHashes.length; i++) {
|
||||||
|
if (this.addressHashes[i] == pubKeyHash) {
|
||||||
|
return keys[i].sign(hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error("Missing key for signature");
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getPubKeyFromHash = function (pubKeyHash) {
|
||||||
|
pubKeyHash = Crypto.util.bytesToBase64(pubKeyHash);
|
||||||
|
for (var i = 0; i < this.addressHashes.length; i++) {
|
||||||
|
if (this.addressHashes[i] == pubKeyHash) {
|
||||||
|
return keys[i].getPub();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error("Hash unknown");
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
Wallet.prototype.generateAddress = function () {
|
||||||
|
this.addKey(new Bitcoin.ECKey());
|
||||||
|
};
|
||||||
|
|
||||||
|
Wallet.prototype.process = function (tx) {
|
||||||
|
if (this.txIndex[tx.hash]) return;
|
||||||
|
|
||||||
|
var j;
|
||||||
|
var k;
|
||||||
|
var hash;
|
||||||
|
// Gather outputs
|
||||||
|
for (j = 0; j < tx.outs.length; j++) {
|
||||||
|
var txout = new TransactionOut(tx.outs[j]);
|
||||||
|
hash = Crypto.util.bytesToBase64(txout.script.simpleOutPubKeyHash());
|
||||||
|
for (k = 0; k < this.addressHashes.length; k++) {
|
||||||
|
if (this.addressHashes[k] === hash) {
|
||||||
|
this.unspentOuts.push({tx: tx, index: j, out: txout});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove spent outputs
|
||||||
|
for (j = 0; j < tx.ins.length; j++) {
|
||||||
|
var txin = new TransactionIn(tx.ins[j]);
|
||||||
|
var pubkey = txin.script.simpleInPubKey();
|
||||||
|
hash = Crypto.util.bytesToBase64(Bitcoin.Util.sha256ripe160(pubkey));
|
||||||
|
for (k = 0; k < this.addressHashes.length; k++) {
|
||||||
|
if (this.addressHashes[k] === hash) {
|
||||||
|
for (var l = 0; l < this.unspentOuts.length; l++) {
|
||||||
|
if (txin.outpoint.hash == this.unspentOuts[l].tx.hash &&
|
||||||
|
txin.outpoint.index == this.unspentOuts[l].index) {
|
||||||
|
this.unspentOuts.splice(l, 1);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Remove spent outputs
|
// Index transaction
|
||||||
for (j = 0; j < tx.ins.length; j++) {
|
this.txIndex[tx.hash] = tx;
|
||||||
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
|
Wallet.prototype.getBalance = function () {
|
||||||
this.txIndex[tx.hash] = tx;
|
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 () {
|
Wallet.prototype.createSend = function (address, sendValue, feeValue) {
|
||||||
var balance = BigInteger.valueOf(0);
|
var selectedOuts = [];
|
||||||
for (var i = 0; i < this.unspentOuts.length; i++) {
|
var txValue = sendValue.add(feeValue);
|
||||||
var txout = this.unspentOuts[i].out;
|
var availableValue = BigInteger.ZERO;
|
||||||
balance = balance.add(Bitcoin.Util.valueToBigInt(txout.value));
|
var i;
|
||||||
}
|
for (i = 0; i < this.unspentOuts.length; i++) {
|
||||||
return balance;
|
selectedOuts.push(this.unspentOuts[i]);
|
||||||
};
|
availableValue = availableValue.add(Bitcoin.Util.valueToBigInt(this.unspentOuts[i].out.value));
|
||||||
|
|
||||||
Wallet.prototype.createSend = function (address, sendValue, feeValue) {
|
if (availableValue.compareTo(txValue) >= 0) break;
|
||||||
var selectedOuts = [];
|
}
|
||||||
var txValue = sendValue.add(feeValue);
|
|
||||||
var availableValue = BigInteger.ZERO;
|
|
||||||
var i;
|
|
||||||
for (i = 0; i < this.unspentOuts.length; i++) {
|
|
||||||
selectedOuts.push(this.unspentOuts[i]);
|
|
||||||
availableValue = availableValue.add(Bitcoin.Util.valueToBigInt(this.unspentOuts[i].out.value));
|
|
||||||
|
|
||||||
if (availableValue.compareTo(txValue) >= 0) break;
|
if (availableValue.compareTo(txValue) < 0) {
|
||||||
}
|
throw new Error('Insufficient funds.');
|
||||||
|
}
|
||||||
if (availableValue.compareTo(txValue) < 0) {
|
|
||||||
throw new Error('Insufficient funds.');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var changeValue = availableValue.subtract(txValue);
|
var changeValue = availableValue.subtract(txValue);
|
||||||
|
|
||||||
var sendTx = new Bitcoin.Transaction();
|
var sendTx = new Bitcoin.Transaction();
|
||||||
|
|
||||||
for (i = 0; i < selectedOuts.length; i++) {
|
for (i = 0; i < selectedOuts.length; i++) {
|
||||||
sendTx.addInput(selectedOuts[i].tx, selectedOuts[i].index);
|
sendTx.addInput(selectedOuts[i].tx, selectedOuts[i].index);
|
||||||
}
|
}
|
||||||
|
|
||||||
sendTx.addOutput(address, sendValue);
|
sendTx.addOutput(address, sendValue);
|
||||||
if (changeValue.compareTo(BigInteger.ZERO) > 0) {
|
if (changeValue.compareTo(BigInteger.ZERO) > 0) {
|
||||||
sendTx.addOutput(this.getNextAddress(), changeValue);
|
sendTx.addOutput(this.getNextAddress(), changeValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
var hashType = 1; // SIGHASH_ALL
|
var hashType = 1; // SIGHASH_ALL
|
||||||
|
|
||||||
for (i = 0; i < sendTx.ins.length; i++) {
|
for (i = 0; i < sendTx.ins.length; i++) {
|
||||||
var hash = sendTx.hashTransactionForSignature(selectedOuts[i].out.script, i, hashType);
|
var hash = sendTx.hashTransactionForSignature(selectedOuts[i].out.script, i, hashType);
|
||||||
var pubKeyHash = selectedOuts[i].out.script.simpleOutPubKeyHash();
|
var pubKeyHash = selectedOuts[i].out.script.simpleOutPubKeyHash();
|
||||||
var signature = this.signWithKey(pubKeyHash, hash);
|
var signature = this.signWithKey(pubKeyHash, hash);
|
||||||
|
|
||||||
// Append hash type
|
// Append hash type
|
||||||
signature.push(parseInt(hashType, 10));
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
return sendTx;
|
return sendTx;
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.clearTransactions = function () {
|
Wallet.prototype.clearTransactions = function () {
|
||||||
this.txIndex = {};
|
this.txIndex = {};
|
||||||
this.unspentOuts = [];
|
this.unspentOuts = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check to see if a pubKeyHash belongs to this wallet.
|
* Check to see if a pubKeyHash belongs to this wallet.
|
||||||
*/
|
*/
|
||||||
Wallet.prototype.hasHash = function (hash) {
|
Wallet.prototype.hasHash = function (hash) {
|
||||||
if (Bitcoin.Util.isArray(hash)) hash = Crypto.util.bytesToBase64(hash);
|
if (Bitcoin.Util.isArray(hash)) hash = Crypto.util.bytesToBase64(hash);
|
||||||
|
|
||||||
// TODO: Just create an object with base64 hashes as keys for faster lookup
|
// TODO: Just create an object with base64 hashes as keys for faster lookup
|
||||||
for (var k = 0; k < this.addressHashes.length; k++) {
|
for (var k = 0; k < this.addressHashes.length; k++) {
|
||||||
if (this.addressHashes[k] === hash) return true;
|
if (this.addressHashes[k] === hash) return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
return Wallet;
|
return Wallet;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue