Add testnet support to Address and ECKey, more tests.

Signed-off-by: Michael Gooden <me@michaelgooden.net>
This commit is contained in:
Michael Gooden 2014-02-24 18:31:18 +02:00
parent b3e3f806cb
commit b8215dea60
No known key found for this signature in database
GPG key ID: 909C1E1B489AD6BC
3 changed files with 141 additions and 37 deletions

View file

@ -3,16 +3,6 @@ var Crypto = require('./crypto-js/crypto');
var conv = require('./convert'); var conv = require('./convert');
var util = require('./util'); var util = require('./util');
var address_types = {
prod: 0,
testnet: 111
};
var p2sh_types = {
prod: 5,
testnet: 196
};
var Address = function (bytes, version) { var Address = function (bytes, version) {
if (!(this instanceof Address)) { return new Address(bytes, version); } if (!(this instanceof Address)) { return new Address(bytes, version); }
if (arguments[0] instanceof Address) { if (arguments[0] instanceof Address) {
@ -25,11 +15,11 @@ var Address = function (bytes, version) {
: bytes.length <= 40 ? conv.hexToBytes(bytes) : bytes.length <= 40 ? conv.hexToBytes(bytes)
: util.error('Bad input'); : util.error('Bad input');
this.version = version || this.hash.version || 0; this.version = version || this.hash.version || Address.address_types.prod;
} }
else { else {
this.hash = bytes; this.hash = bytes;
this.version = version || 0; this.version = version || bytes.version || Address.address_types.prod;
} }
}; };
@ -66,4 +56,14 @@ Address.decodeString = function (string) {
return base58.checkDecode(string); return base58.checkDecode(string);
}; };
Address.address_types = {
prod: 0,
testnet: 111
};
Address.p2sh_types = {
prod: 5,
testnet: 196
};
module.exports = Address; module.exports = Address;

View file

@ -11,18 +11,19 @@ var ECPointFp = require('./jsbn/ec').ECPointFp;
var ecparams = sec("secp256k1"); var ecparams = sec("secp256k1");
// input can be nothing, array of bytes, hex string, or base58 string // input can be nothing, array of bytes, hex string, or base58 string
var ECKey = function (input,compressed) { var ECKey = function (input,compressed,version) {
if (!(this instanceof ECKey)) { return new ECKey(input,compressed); } if (!(this instanceof ECKey)) { return new ECKey(input,compressed); }
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);
this.compressed = compressed || false; this.compressed = compressed || false;
this.version = version || Address.address_types.prod;
} }
else this.import(input,compressed) else this.import(input,compressed,version)
}; };
ECKey.prototype.import = function (input,compressed) { ECKey.prototype.import = function (input,compressed,version) {
function has(li,v) { return li.indexOf(v) >= 0 } function has(li,v) { return li.indexOf(v) >= 0 }
function fromBin(x) { return BigInteger.fromByteArrayUnsigned(x) } function fromBin(x) { return BigInteger.fromByteArrayUnsigned(x) }
this.priv = this.priv =
@ -32,7 +33,9 @@ ECKey.prototype.import = function (input,compressed) {
: typeof input != "string" ? null : typeof input != "string" ? null
: input.length == 44 ? fromBin(conv.base64ToBytes(input)) : input.length == 44 ? fromBin(conv.base64ToBytes(input))
: input.length == 51 && input[0] == '5' ? fromBin(base58.checkDecode(input)) : input.length == 51 && input[0] == '5' ? fromBin(base58.checkDecode(input))
: input.length == 51 && input[0] == '9' ? fromBin(base58.checkDecode(input))
: input.length == 52 && has('LK',input[0]) ? fromBin(base58.checkDecode(input).slice(0,32)) : input.length == 52 && has('LK',input[0]) ? fromBin(base58.checkDecode(input).slice(0,32))
: input.length == 52 && input[0] == 'c' ? fromBin(base58.checkDecode(input).slice(0,32))
: has([64,65],input.length) ? fromBin(conv.hexToBytes(input.slice(0,64))) : has([64,65],input.length) ? fromBin(conv.hexToBytes(input.slice(0,64)))
: null : null
@ -44,15 +47,32 @@ ECKey.prototype.import = function (input,compressed) {
: typeof input != "string" ? null : typeof input != "string" ? null
: input.length == 44 ? false : input.length == 44 ? false
: input.length == 51 && input[0] == '5' ? false : input.length == 51 && input[0] == '5' ? false
: input.length == 51 && input[0] == '9' ? false
: input.length == 52 && has('LK',input[0]) ? true : input.length == 52 && has('LK',input[0]) ? true
: input.length == 52 && input[0] == 'c' ? true
: input.length == 64 ? false : input.length == 64 ? false
: input.length == 65 ? true : input.length == 65 ? true
: null : null
this.version =
version !== undefined ? version
: input instanceof ECKey ? input.version
: input instanceof BigInteger ? Address.address_types.prod
: util.isArray(input) ? Address.address_types.prod
: typeof input != "string" ? null
: input.length == 44 ? Address.address_types.prod
: input.length == 51 && input[0] == '5' ? Address.address_types.prod
: input.length == 51 && input[0] == '9' ? Address.address_types.testnet
: input.length == 52 && has('LK',input[0]) ? Address.address_types.prod
: input.length == 52 && input[0] == 'c' ? Address.address_types.testnet
: input.length == 64 ? Address.address_types.prod
: input.length == 65 ? Address.address_types.prod
: null
}; };
ECKey.prototype.getPub = function(compressed) { ECKey.prototype.getPub = function(compressed) {
if (compressed === undefined) compressed = this.compressed if (compressed === undefined) compressed = this.compressed
return ECPubKey(ecparams.getG().multiply(this.priv),compressed) return ECPubKey(ecparams.getG().multiply(this.priv),compressed,this.version)
} }
/** /**
@ -68,7 +88,7 @@ ECKey.prototype.toBin = function() {
} }
ECKey.prototype.toBase58 = function() { ECKey.prototype.toBase58 = function() {
return base58.checkEncode(this.toBytes(), 128) return base58.checkEncode(this.toBytes(), ECKey.version_bytes[this.version])
} }
ECKey.prototype.toWif = ECKey.prototype.toBase58 ECKey.prototype.toWif = ECKey.prototype.toBase58
@ -83,10 +103,14 @@ ECKey.prototype.toBytes = function() {
return bytes return bytes
} }
ECKey.prototype.toBase64 = function() {
return conv.bytesToBase64(this.toBytes())
}
ECKey.prototype.toString = ECKey.prototype.toBase58 ECKey.prototype.toString = ECKey.prototype.toBase58
ECKey.prototype.getBitcoinAddress = function(v) { ECKey.prototype.getBitcoinAddress = function() {
return this.getPub().getBitcoinAddress(v) return this.getPub().getBitcoinAddress(this.version)
} }
ECKey.prototype.add = function(key) { ECKey.prototype.add = function(key) {
@ -97,18 +121,24 @@ ECKey.prototype.multiply = function(key) {
return ECKey(this.priv.multiply(ECKey(key).priv),this.compressed) return ECKey(this.priv.multiply(ECKey(key).priv),this.compressed)
} }
var ECPubKey = function(input,compressed) { ECKey.version_bytes = {
if (!(this instanceof ECPubKey)) { return new ECPubKey(input,compressed); } 0: 128,
111: 239
}
var ECPubKey = function(input,compressed,version) {
if (!(this instanceof ECPubKey)) { return new ECPubKey(input,compressed,version); }
if (!input) { if (!input) {
// Generate new key // Generate new key
var n = ecparams.getN(); var n = ecparams.getN();
this.pub = ecparams.getG().multiply(ecdsa.getBigRandom(n)) this.pub = ecparams.getG().multiply(ecdsa.getBigRandom(n))
this.compressed = compressed || false; this.compressed = compressed || false;
this.version = version || Address.address_types.prod;
} }
else this.import(input,compressed) else this.import(input,compressed,version)
} }
ECPubKey.prototype.import = function(input,compressed) { ECPubKey.prototype.import = function(input,compressed,version) {
var decode = function(x) { return ECPointFp.decodeFrom(ecparams.getCurve(), x) } var decode = function(x) { return ECPointFp.decodeFrom(ecparams.getCurve(), x) }
this.pub = this.pub =
input instanceof ECPointFp ? input input instanceof ECPointFp ? input
@ -119,18 +149,24 @@ ECPubKey.prototype.import = function(input,compressed) {
: ecparams.getG().multiply(ecdsa.getBigRandom(ecparams.getN())) : ecparams.getG().multiply(ecdsa.getBigRandom(ecparams.getN()))
this.compressed = this.compressed =
arguments.length > 1 ? compressed compressed ? compressed
: input instanceof ECPointFp ? input.compressed : input instanceof ECPointFp ? input.compressed
: input instanceof ECPubKey ? input.compressed : input instanceof ECPubKey ? input.compressed
: (this.pub[0] < 4) : (this.pub[0] < 4)
this.version =
version ? version
: input instanceof ECPointFp ? input.version
: input instanceof ECPubKey ? input.version
: Address.address_types.prod
} }
ECPubKey.prototype.add = function(key) { ECPubKey.prototype.add = function(key) {
return ECPubKey(this.pub.add(ECPubKey(key).pub),this.compressed) return ECPubKey(this.pub.add(ECPubKey(key).pub),this.compressed,this.version)
} }
ECPubKey.prototype.multiply = function(key) { ECPubKey.prototype.multiply = function(key) {
return ECPubKey(this.pub.multiply(ECKey(key).priv),this.compressed) return ECPubKey(this.pub.multiply(ECKey(key).priv),this.compressed,this.version)
} }
ECPubKey.prototype['export'] = function(format) { ECPubKey.prototype['export'] = function(format) {
@ -152,7 +188,7 @@ ECPubKey.prototype.toBin = function() {
} }
ECPubKey.prototype.toBase58 = function() { ECPubKey.prototype.toBase58 = function() {
return base58.checkEncode(this.toBytes(), 128) return base58.checkEncode(this.toBytes(), this.version)
} }
ECPubKey.prototype.toWif = ECPubKey.prototype.toBase58 ECPubKey.prototype.toWif = ECPubKey.prototype.toBase58
@ -161,8 +197,8 @@ ECPubKey.prototype.toString = function() {
return this.getBitcoinAddress().toString() return this.getBitcoinAddress().toString()
} }
ECPubKey.prototype.getBitcoinAddress = function(v) { ECPubKey.prototype.getBitcoinAddress = function() {
return new Address(util.sha256ripe160(this.toBytes()), v); return new Address(util.sha256ripe160(this.toBytes()), this.version);
} }
ECKey.prototype.sign = function (hash) { ECKey.prototype.sign = function (hash) {

View file

@ -4,20 +4,33 @@ var ECKey = require('../src/eckey.js').ECKey;
var convert = require('../src/convert.js'); var convert = require('../src/convert.js');
var bytesToHex = convert.bytesToHex; var bytesToHex = convert.bytesToHex;
var hexToBytes = convert.hexToBytes; var hexToBytes = convert.hexToBytes;
var Address = require('../src/address');
describe('ECKey', function() { describe('ECKey', function() {
describe('constructor (base58 private)', function() { describe('constructor (base58 private) on mainnet', function() {
it('parses case 1', function() { it('parses hex', function() {
var priv = '18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725'; var priv = '18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725';
var pub = '0450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b235' + var pub = '0450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b235' +
'22cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6'; '22cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6';
var key = new ECKey(hexToBytes(priv)); var key = new ECKey(priv);
assert.equal(bytesToHex(key.getPub()['export']('bytes')), pub); assert.equal(key.getPub().toHex(), pub);
assert.equal(key.compressed, false); assert.equal(key.compressed, false);
assert.equal(key.version, Address.address_types.prod);
}) })
it('parses case 2', function() { it('parses base64', function() {
var priv = 'VYdB+iv47y5FaUVIPdQInkgATrABeuD1lACUoM4x7tU=';
var pub = '042f43c16c08849fed20a35bb7b1947bbf0923c52d613ee13b5c665a1e10d24b2' +
'8be909a70f5f87c1adb79fbcd1b3f17d20aa91c04fc355112dba2ce9b1cbf013b';
var key = new ECKey(priv);
assert.equal(key.getPub().toHex(), pub);
assert.equal(key.compressed, false);
assert.equal(key.version, Address.address_types.prod);
})
it('parses WIF', function() {
var priv = '5HwoXVkHoRM8sL2KmNRS217n1g8mPPBomrY7yehCuXC1115WWsh'; var priv = '5HwoXVkHoRM8sL2KmNRS217n1g8mPPBomrY7yehCuXC1115WWsh';
var pub = '044f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0' + var pub = '044f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0' +
'f0b704075871aa385b6b1b8ead809ca67454d9683fcf2ba03456d6fe2c4abe2b07f0fbdbb2f1c1'; 'f0b704075871aa385b6b1b8ead809ca67454d9683fcf2ba03456d6fe2c4abe2b07f0fbdbb2f1c1';
@ -25,19 +38,74 @@ describe('ECKey', function() {
var key = new ECKey(priv); var key = new ECKey(priv);
assert.equal(key.compressed, false); assert.equal(key.compressed, false);
assert.equal(bytesToHex(key.getPub()['export']('bytes')), pub); assert.equal(key.getPub().toHex(), pub);
assert.equal(key.getBitcoinAddress().toString(), addr); assert.equal(key.getBitcoinAddress().toString(), addr);
assert.equal(key.version, Address.address_types.prod);
}) })
it('parses case 3', function() { it('parses compressed WIF', function() {
var priv = 'KwntMbt59tTsj8xqpqYqRRWufyjGunvhSyeMo3NTYpFYzZbXJ5Hp'; var priv = 'KwntMbt59tTsj8xqpqYqRRWufyjGunvhSyeMo3NTYpFYzZbXJ5Hp';
var pub = '034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa' var pub = '034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa'
var addr = '1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9'; var addr = '1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9';
var key = new ECKey(priv); var key = new ECKey(priv);
assert.equal(key.compressed, true); assert.equal(key.compressed, true);
assert.equal(bytesToHex(key.getPub()['export']('bytes')), pub); assert.equal(key.getPub().toHex(), pub);
assert.equal(key.getBitcoinAddress().toString(), addr); assert.equal(key.getBitcoinAddress().toString(), addr);
assert.equal(key.version, Address.address_types.prod);
})
})
describe('constructor (base58 private) on testnet', function() {
it('parses hex', function() {
var priv = 'ca48ec9783cf3ad0dfeff1fc254395a2e403cbbc666477b61b45e31d3b8ab458';
var pub = '044b12d9d7c77db68388b6ff7c89046174c871546436806bcd80d07c28ea81199' +
'283fbec990dad6fb98f93f712d50cb874dd717de6a184158d63886dda3090f566';
var key = new ECKey(priv, false, Address.address_types.testnet);
assert.equal(key.getPub().toHex(), pub);
assert.equal(key.compressed, false);
assert.equal(key.version, Address.address_types.testnet);
assert.equal(key.toHex(), priv);
})
it('parses base64', function() {
var priv = 'VYdB+iv47y5FaUVIPdQInkgATrABeuD1lACUoM4x7tU=';
var pub = '042f43c16c08849fed20a35bb7b1947bbf0923c52d613ee13b5c665a1e10d24b2' +
'8be909a70f5f87c1adb79fbcd1b3f17d20aa91c04fc355112dba2ce9b1cbf013b';
var key = new ECKey(priv, false, Address.address_types.testnet);
assert.equal(key.getPub().toHex(), pub);
assert.equal(key.compressed, false);
assert.equal(key.version, Address.address_types.testnet);
assert.equal(key.toBase64(), priv);
})
it('parses WIF', function() {
var priv = '92tb9mjz6q9eKZjYvLsgk87kPrMoh7BGRumSzPeUGhmigtsfrbP';
var pub = '04b70d0bd71417d7903b6c4b451d8cfbd6514cb3557d9363319e5d87377905c4f' +
'3bb6c6e8c1819b6dd95d60aa8da73f0e726a2311545842bf07e78487e8ea2801f';
var addr = 'mmktwdfQ3oaEvyjS5p1TgovfJmgBQLjGa4';
var key = new ECKey(priv);
assert.equal(key.compressed, false);
assert.equal(key.getPub().toHex(), pub);
assert.equal(key.getBitcoinAddress().toString(), addr);
assert.equal(key.version, Address.address_types.testnet);
assert.equal(key.toBase58(), priv);
})
it('parses compressed WIF', function() {
var priv = 'cTLhjiUj2jX9iwUotSiiRLQnXrrq62GK6wuBiJxPNJxP15ERrX8o';
var pub = '03b70d0bd71417d7903b6c4b451d8cfbd6514cb3557d9363319e5d87377905c4f3'
var addr = 'n3nBy5dyx24CLT3kg3ZJwfTeTX5oxxtxNE';
var key = new ECKey(priv);
assert.equal(key.compressed, true);
assert.equal(key.getPub().toHex(), pub);
assert.equal(key.getBitcoinAddress().toString(), addr);
assert.equal(key.version, Address.address_types.testnet);
assert.equal(key.toBase58(), priv);
}) })
}) })
}) })