Merge pull request #305 from maraoz/refactor/improve-BIP32
Refactor/improve bip32 and add test
This commit is contained in:
commit
c7218ea2fc
|
@ -26,7 +26,7 @@ module.exports = function(grunt) {
|
||||||
tasks: ['markdown']
|
tasks: ['markdown']
|
||||||
},
|
},
|
||||||
scripts: {
|
scripts: {
|
||||||
files: ['**/*.js', '**/*.html', '!**/node_modules/**', '!browser/bundle.js', '!browser/testdata.js', '!browser/vendor-bundle.js'],
|
files: ['**/*.js', '**/*.html', '!**/node_modules/**', '!browser/bundle.js', '!browser/testdata.js', '!browser/vendor-bundle.js', '!docs/**'],
|
||||||
tasks: ['shell'],
|
tasks: ['shell'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
40
lib/BIP32.js
40
lib/BIP32.js
|
@ -7,8 +7,8 @@ var SecureRandom = imports.SecureRandom || require('./SecureRandom');
|
||||||
var bignum = imports.bignum || require('./Bignum');
|
var bignum = imports.bignum || require('./Bignum');
|
||||||
var networks = require('../networks');
|
var networks = require('../networks');
|
||||||
|
|
||||||
var secp256k1_n = new bignum("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16);
|
var secp256k1_n = new bignum('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141', 16);
|
||||||
var secp256k1_Gx = new bignum("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16);
|
var secp256k1_Gx = new bignum('79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798', 16);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
random new BIP32: new BIP32();
|
random new BIP32: new BIP32();
|
||||||
|
@ -20,9 +20,9 @@ var BIP32 = function(bytes) {
|
||||||
bytes = 'livenet';
|
bytes = 'livenet';
|
||||||
this.version = networks['livenet'].bip32privateVersion;
|
this.version = networks['livenet'].bip32privateVersion;
|
||||||
}
|
}
|
||||||
else if (bytes == 'testnet')
|
else if (bytes == 'testnet') {
|
||||||
this.version = networks['testnet'].bip32privateVersion;
|
this.version = networks['testnet'].bip32privateVersion;
|
||||||
|
}
|
||||||
if (bytes == 'livenet' || bytes == 'testnet') {
|
if (bytes == 'livenet' || bytes == 'testnet') {
|
||||||
this.depth = 0x00;
|
this.depth = 0x00;
|
||||||
this.parentFingerprint = new Buffer([0, 0, 0, 0]);
|
this.parentFingerprint = new Buffer([0, 0, 0, 0]);
|
||||||
|
@ -37,17 +37,17 @@ var BIP32 = function(bytes) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// decode base58
|
// decode base58
|
||||||
if (typeof bytes === "string") {
|
if (typeof bytes === 'string') {
|
||||||
var decoded = base58.decode(bytes);
|
var decoded = base58.decode(bytes);
|
||||||
if (decoded.length != 82)
|
if (decoded.length != 82)
|
||||||
throw new Error("Not enough data");
|
throw new Error('Not enough data, expected 82 and received '+decoded.length);
|
||||||
var checksum = decoded.slice(78, 82);
|
var checksum = decoded.slice(78, 82);
|
||||||
bytes = decoded.slice(0, 78);
|
bytes = decoded.slice(0, 78);
|
||||||
|
|
||||||
var hash = coinUtil.sha256(coinUtil.sha256(bytes));
|
var hash = coinUtil.sha256(coinUtil.sha256(bytes));
|
||||||
|
|
||||||
if (hash[0] != checksum[0] || hash[1] != checksum[1] || hash[2] != checksum[2] || hash[3] != checksum[3]) {
|
if (hash[0] != checksum[0] || hash[1] != checksum[1] || hash[2] != checksum[2] || hash[3] != checksum[3]) {
|
||||||
throw new Error("Invalid checksum");
|
throw new Error('Invalid checksum');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ BIP32.seed = function(bytes, network) {
|
||||||
bytes = new Buffer(bytes, 'hex'); //if not buffer, assume hex
|
bytes = new Buffer(bytes, 'hex'); //if not buffer, assume hex
|
||||||
if (bytes.length < 128/8)
|
if (bytes.length < 128/8)
|
||||||
return false; //need more entropy
|
return false; //need more entropy
|
||||||
var hash = coinUtil.sha512hmac(bytes, new Buffer("Bitcoin seed"));
|
var hash = coinUtil.sha512hmac(bytes, new Buffer('Bitcoin seed'));
|
||||||
|
|
||||||
var bip32 = new BIP32(null);
|
var bip32 = new BIP32(null);
|
||||||
bip32.depth = 0x00;
|
bip32.depth = 0x00;
|
||||||
|
@ -85,7 +85,7 @@ BIP32.seed = function(bytes, network) {
|
||||||
|
|
||||||
BIP32.prototype.initFromBytes = function(bytes) {
|
BIP32.prototype.initFromBytes = function(bytes) {
|
||||||
// Both pub and private extended keys are 78 bytes
|
// Both pub and private extended keys are 78 bytes
|
||||||
if(bytes.length != 78) throw new Error("not enough data");
|
if(bytes.length != 78) throw new Error('not enough data');
|
||||||
|
|
||||||
this.version = u32(bytes.slice(0, 4));
|
this.version = u32(bytes.slice(0, 4));
|
||||||
this.depth = u8(bytes.slice(4, 5));
|
this.depth = u8(bytes.slice(4, 5));
|
||||||
|
@ -116,7 +116,7 @@ BIP32.prototype.initFromBytes = function(bytes) {
|
||||||
this.pubKeyHash = coinUtil.sha256ripe160(this.eckey.public);
|
this.pubKeyHash = coinUtil.sha256ripe160(this.eckey.public);
|
||||||
this.hasPrivateKey = false;
|
this.hasPrivateKey = false;
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Invalid key");
|
throw new Error('Invalid key');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.buildExtendedPublicKey();
|
this.buildExtendedPublicKey();
|
||||||
|
@ -137,7 +137,7 @@ BIP32.prototype.buildExtendedPublicKey = function() {
|
||||||
v = networks['testnet'].bip32publicVersion;
|
v = networks['testnet'].bip32publicVersion;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error("Unknown version");
|
throw new Error('Unknown version');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Version
|
// Version
|
||||||
|
@ -158,15 +158,15 @@ BIP32.prototype.buildExtendedPublicKey = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
BIP32.prototype.extendedPublicKeyString = function(format) {
|
BIP32.prototype.extendedPublicKeyString = function(format) {
|
||||||
if (format === undefined || format === "base58") {
|
if (format === undefined || format === 'base58') {
|
||||||
var hash = coinUtil.sha256(coinUtil.sha256(this.extendedPublicKey));
|
var hash = coinUtil.sha256(coinUtil.sha256(this.extendedPublicKey));
|
||||||
var checksum = hash.slice(0, 4);
|
var checksum = hash.slice(0, 4);
|
||||||
var data = Buffer.concat([this.extendedPublicKey, checksum]);
|
var data = Buffer.concat([this.extendedPublicKey, checksum]);
|
||||||
return base58.encode(data);
|
return base58.encode(data);
|
||||||
} else if (format === "hex") {
|
} else if (format === 'hex') {
|
||||||
return this.extendedPublicKey.toString('hex');;
|
return this.extendedPublicKey.toString('hex');;
|
||||||
} else {
|
} else {
|
||||||
throw new Error("bad format");
|
throw new Error('bad format');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,15 +194,15 @@ BIP32.prototype.buildExtendedPrivateKey = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
BIP32.prototype.extendedPrivateKeyString = function(format) {
|
BIP32.prototype.extendedPrivateKeyString = function(format) {
|
||||||
if (format === undefined || format === "base58") {
|
if (format === undefined || format === 'base58') {
|
||||||
var hash = coinUtil.sha256(coinUtil.sha256(this.extendedPrivateKey));
|
var hash = coinUtil.sha256(coinUtil.sha256(this.extendedPrivateKey));
|
||||||
var checksum = hash.slice(0, 4);
|
var checksum = hash.slice(0, 4);
|
||||||
var data = Buffer.concat([this.extendedPrivateKey, checksum]);
|
var data = Buffer.concat([this.extendedPrivateKey, checksum]);
|
||||||
return base58.encode(data);
|
return base58.encode(data);
|
||||||
} else if (format === "hex") {
|
} else if (format === 'hex') {
|
||||||
return this.extendedPrivateKey.toString('hex');
|
return this.extendedPrivateKey.toString('hex');
|
||||||
} else {
|
} else {
|
||||||
throw new Error("bad format");
|
throw new Error('bad format');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,7 +219,7 @@ BIP32.prototype.derive = function(path) {
|
||||||
var c = e[i];
|
var c = e[i];
|
||||||
|
|
||||||
if (i == 0 ) {
|
if (i == 0 ) {
|
||||||
if (c != 'm') throw new Error("invalid path");
|
if (c != 'm') throw new Error('invalid path');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,7 +250,7 @@ BIP32.prototype.deriveChild = function(i) {
|
||||||
this.version == networks['testnet'].bip32privateVersion );
|
this.version == networks['testnet'].bip32privateVersion );
|
||||||
|
|
||||||
if (usePrivate && (!this.hasPrivateKey || !isPrivate))
|
if (usePrivate && (!this.hasPrivateKey || !isPrivate))
|
||||||
throw new Error("Cannot do private key derivation without private key");
|
throw new Error('Cannot do private key derivation without private key');
|
||||||
|
|
||||||
var ret = null;
|
var ret = null;
|
||||||
if (this.hasPrivateKey) {
|
if (this.hasPrivateKey) {
|
||||||
|
@ -323,7 +323,7 @@ BIP32.prototype.deriveChild = function(i) {
|
||||||
|
|
||||||
function uint(f, size) {
|
function uint(f, size) {
|
||||||
if (f.length < size)
|
if (f.length < size)
|
||||||
throw new Error("not enough data");
|
throw new Error('not enough data');
|
||||||
var n = 0;
|
var n = 0;
|
||||||
for (var i = 0; i < size; i++) {
|
for (var i = 0; i < size; i++) {
|
||||||
n *= 256;
|
n *= 256;
|
||||||
|
|
|
@ -291,7 +291,19 @@ describe('BIP32', function() {
|
||||||
bip32.extendedPrivateKeyString().should.equal(vector2_m_private);
|
bip32.extendedPrivateKeyString().should.equal(vector2_m_private);
|
||||||
bip32.extendedPublicKeyString().should.equal(vector2_m_public);
|
bip32.extendedPublicKeyString().should.equal(vector2_m_public);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('testnet', function() {
|
||||||
|
it('should initialize a new BIP32 correctly from a random BIP32', function() {
|
||||||
|
var b1 = new BIP32('testnet');
|
||||||
|
var b2 = new BIP32(b1.extendedPublicKeyString());
|
||||||
|
b2.extendedPublicKeyString().should.equal(b1.extendedPublicKeyString());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate valid ext pub key for testnet', function() {
|
||||||
|
var b = new BIP32('testnet');
|
||||||
|
b.extendedPublicKeyString().substring(0,4).should.equal('tpub');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue