Fixes interfaces

This commit is contained in:
Esteban Ordano 2014-11-26 17:55:34 -03:00
parent 5728c30371
commit ad74b549a1
9 changed files with 159 additions and 77 deletions

View File

@ -20,9 +20,9 @@ bitcore.encoding.Varint = require('./lib/encoding/varint');
// main bitcoin library
bitcore.Address = require('./lib/address');
bitcore.BIP32 = require('./lib/bip32');
bitcore.Block = require('./lib/block');
bitcore.Blockheader = require('./lib/blockheader');
bitcore.HDPrivateKey = require('./lib/hdprivkey.js');
bitcore.Networks = require('./lib/networks');
bitcore.Opcode = require('./lib/opcode');
bitcore.PrivateKey = require('./lib/privatekey');

View File

@ -1,10 +1,16 @@
'use strict';
var _ = require('lodash');
var bs58 = require('bs58');
var buffer = require('buffer');
var ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'.split('');
var Base58 = function Base58(obj) {
if (!(this instanceof Base58))
/* jshint maxcomplexity: 8 */
if (!(this instanceof Base58)) {
return new Base58(obj);
}
if (Buffer.isBuffer(obj)) {
var buf = obj;
this.fromBuffer(buf);
@ -16,20 +22,29 @@ var Base58 = function Base58(obj) {
}
};
Base58.validCharacters = function validCharacters(chars) {
if (buffer.Buffer.isBuffer(chars)) {
chars = chars.toString();
}
return _.all(_.map(chars, function(char) { return _.contains(ALPHABET, char); }));
};
Base58.prototype.set = function(obj) {
this.buf = obj.buf || this.buf || undefined;
return this;
};
Base58.encode = function(buf) {
if (!Buffer.isBuffer(buf))
if (!buffer.Buffer.isBuffer(buf)) {
throw new Error('Input should be a buffer');
}
return bs58.encode(buf);
};
Base58.decode = function(str) {
if (typeof str !== 'string')
if (typeof str !== 'string') {
throw new Error('Input should be a string');
}
return new Buffer(bs58.decode(str));
};

View File

@ -1,6 +1,8 @@
'use strict';
var _ = require('lodash');
var base58 = require('./base58');
var buffer = require('buffer');
var sha256sha256 = require('../crypto/hash').sha256sha256;
var Base58Check = function Base58Check(obj) {
@ -22,6 +24,20 @@ Base58Check.prototype.set = function(obj) {
return this;
};
Base58Check.validChecksum = function validChecksum(data, checksum) {
if (_.isString(data)) {
data = new buffer.Buffer(base58.decode(data));
}
if (_.isString(checksum)) {
checksum = new buffer.Buffer(base58.decode(checksum));
}
if (!checksum) {
checksum = data.slice(-4);
data = data.slice(0, -4);
}
return Base58Check.checksum(data).toString('hex') === checksum.toString('hex');
};
Base58Check.decode = function(s) {
if (typeof s !== 'string')
throw new Error('Input must be a string');

View File

@ -5,9 +5,9 @@ var BN = require('./crypto/bn');
var Base58 = require('./encoding/base58');
var Base58Check = require('./encoding/base58check');
var Hash = require('./crypto/hash');
var Network = require('./network');
var Network = require('./networks');
var Point = require('./crypto/point');
var PrivateKey = require('./privkey');
var PrivateKey = require('./privatekey');
var Random = require('./crypto/random');
var assert = require('assert');
@ -15,7 +15,7 @@ var buffer = require('buffer');
var util = require('./util');
var MINIMUM_ENTROPY_BITS = 128;
var BITS_TO_BYTES = 128;
var BITS_TO_BYTES = 1/8;
var MAXIMUM_ENTROPY_BITS = 512;
@ -24,21 +24,21 @@ function HDPrivateKey(arg) {
if (arg instanceof HDPrivateKey) {
return arg;
}
if (!this instanceof HDPrivateKey) {
if (!(this instanceof HDPrivateKey)) {
return new HDPrivateKey(arg);
}
if (arg) {
if (_.isString(arg) || buffer.Buffer.isBuffer(arg)) {
if (HDPrivateKey.isValidSerialized(arg)) {
this._buildFromSerialized(arg);
} else if (util.isValidJson(arg)) {
this._buildFromJson(arg);
} else {
throw new Error(HDPrivateKey.Errors.UnrecognizedArgument);
throw new Error(HDPrivateKey.getSerializedError(arg));
}
} else {
if (_.isObject(arg)) {
this._buildFromObject(arg);
} else if (util.isValidJson(arg)) {
this._buildFromJson(arg);
} else {
throw new Error(HDPrivateKey.Errors.UnrecognizedArgument);
}
@ -70,11 +70,12 @@ HDPrivateKey.prototype._deriveWithNumber = function deriveWithNumber(index, hard
} else {
data = buffer.Buffer.concat([this.publicKey.toBuffer(), indexBuffer]);
}
var hash = Hash.sha512hmac(data, this.chainCode);
var hash = Hash.sha512hmac(data, this._buffers.chainCode);
var leftPart = BN().fromBuffer(hash.slice(0, 32), {size: 32});
var chainCode = hash.slice(32, 64);
var privateKey = leftPart.add(this.privateKey.toBigNumber()).mod(Point.getN());
var privateKey = leftPart.add(this.privateKey.toBigNumber()).mod(Point.getN()).toBuffer({size: 32});
console.log(privateKey);
return new HDPrivateKey({
network: this.network,
@ -134,13 +135,12 @@ HDPrivateKey.getSerializedError = function getSerializedError(data, network) {
if (!(_.isString(data) || buffer.Buffer.isBuffer(data))) {
return HDPrivateKey.Errors.InvalidArgument;
}
if (_.isString(data)) {
data = new buffer.Buffer(data);
}
if (!Base58.validCharacters(data)) {
return HDPrivateKey.Errors.InvalidB58Char;
}
if (!Base58Check.validChecksum(data)) {
try {
data = Base58Check.decode(data);
} catch (e) {
return HDPrivateKey.Errors.InvalidB58Checksum;
}
if (data.length !== 78) {
@ -175,12 +175,12 @@ HDPrivateKey.prototype._buildFromObject = function buildFromObject(arg) {
// TODO: Type validation
var buffers = {
version: util.integerAsBuffer(Network.get(arg.network).xprivkey),
depth: util.integerAsBuffer(arg.depth),
depth: util.integerAsSingleByteBuffer(arg.depth),
parentFingerPrint: util.integerAsBuffer(arg.parentFingerPrint),
childIndex: util.integerAsBuffer(arg.childIndex),
chainCode: util.integerAsBuffer(arg.chainCode),
privateKey: util.hexToBuffer(arg.privateKey),
checksum: util.integerAsBuffer(arg.checksum)
chainCode: _.isString(arg.chainCode) ? util.hexToBuffer(arg.chainCode) : arg.chainCode,
privateKey: _.isString(arg.privateKey) ? util.hexToBuffer(arg.privateKey) : arg.privateKey,
checksum: arg.checksum && arg.checksum.length ? util.integerAsBuffer(arg.checksum) : undefined
};
return this._buildFromBuffers(buffers);
};
@ -196,7 +196,7 @@ HDPrivateKey.prototype._buildFromSerialized = function buildFromSerialized(arg)
chainCode: decoded.slice(HDPrivateKey.ChainCodeStart, HDPrivateKey.ChainCodeEnd),
privateKey: decoded.slice(HDPrivateKey.PrivateKeyStart, HDPrivateKey.PrivateKeyEnd),
checksum: decoded.slice(HDPrivateKey.ChecksumStart, HDPrivateKey.ChecksumEnd),
xprivkey: decoded.toString()
xprivkey: arg
};
return this._buildFromBuffers(buffers);
};
@ -227,8 +227,8 @@ HDPrivateKey.fromSeed = function fromSeed(hexa, network) {
depth: 0,
parentFingerPrint: 0,
childIndex: 0,
chainCode: hash.slice(32, 64),
privateKey: hash.slice(0, 32)
privateKey: hash.slice(0, 32),
chainCode: hash.slice(32, 64)
});
};
@ -249,16 +249,21 @@ HDPrivateKey.fromSeed = function fromSeed(hexa, network) {
* @return {HDPrivateKey} this
*/
HDPrivateKey.prototype._buildFromBuffers = function buildFromBuffers(arg) {
/* jshint maxcomplexity: 8 */
console.log(arg.privateKey);
HDPrivateKey._validateBufferArguments(arg);
this._buffers = arg;
console.log(arg.privateKey);
var sequence = [
arg.version, arg.depth, arg.parentFingerPrint, arg.childIndex, arg.chainCode,
util.emptyBuffer(1), arg.privateKey,
util.emptyBuffer(1), arg.privateKey
];
if (!arg.checksum) {
arg.checksum = Base58Check.checksum(sequence);
console.log(arg.privateKey);
console.log(sequence);
if (!arg.checksum || !arg.checksum.length) {
arg.checksum = Base58Check.checksum(buffer.Buffer.concat(sequence));
} else {
if (arg.checksum.toString() !== sequence.toString()) {
throw new Error(HDPrivateKey.Errors.InvalidB58Checksum);
@ -275,29 +280,32 @@ HDPrivateKey.prototype._buildFromBuffers = function buildFromBuffers(arg) {
// TODO:
// * Instantiate associated HDPublicKey
this.network = Network.get(util.integerFromBuffer(arg.version));
this.privateKey = new PrivateKey(arg.privateKey);
this.publicKey = this.privateKey.publicKey;
this.fingerPrint = Base58Check.checksum(this.publicKey._value);
this.fingerPrint = Base58Check.checksum(util.hexToBuffer(this.publicKey.toString()));
return this;
};
HDPrivateKey._validateBufferArguments = function validateBufferArguments(arg) {
var checkBuffer = function(name, size) {
var buffer = arg[name];
assert(buffer.Buffer.isBuffer(buffer), name + ' argument is not a buffer');
var buff = arg[name];
assert(buffer.Buffer.isBuffer(buff), name + ' argument is not a buffer');
assert(
buffer.length === size,
name + ' has not the expected size: found ' + buffer.length + ', expected ' + size
buff.length === size,
name + ' has not the expected size: found ' + buff.length + ', expected ' + size
);
};
checkBuffer('version', HDPrivateKey.VersionSize);
checkBuffer('depth', HDPrivateKey.DepthLength);
checkBuffer('depth', HDPrivateKey.DepthSize);
checkBuffer('parentFingerPrint', HDPrivateKey.ParentFingerPrintSize);
checkBuffer('childIndex', HDPrivateKey.ChildIndexSize);
checkBuffer('chainCode', HDPrivateKey.ChainCodeSize);
checkBuffer('privateKey', HDPrivateKey.PrivateKeySize);
checkBuffer('checksum', HDPrivateKey.CheckSumSize);
if (arg.checksum && arg.checksum.length) {
checkBuffer('checksum', HDPrivateKey.CheckSumSize);
}
};
HDPrivateKey.prototype.toString = function toString() {
@ -330,22 +338,31 @@ HDPrivateKey.Hardened = 0x80000000;
HDPrivateKey.RootElementAlias = ['m', 'M', 'm\'', 'M\''];
HDPrivateKey.VersionSize = 4;
HDPrivateKey.DepthLength = 4;
HDPrivateKey.DepthSize = 1;
HDPrivateKey.ParentFingerPrintSize = 4;
HDPrivateKey.ChildIndexSize = 4;
HDPrivateKey.ChainCodeSize = 32;
HDPrivateKey.PrivateKeySize = 32;
HDPrivateKey.CheckSumSize = 4;
HDPrivateKey.VersionStart = 0;
HDPrivateKey.VersionEnd = HDPrivateKey.DepthStart = 4;
HDPrivateKey.DepthEnd = HDPrivateKey.ParentFingerPrintStart = 8;
HDPrivateKey.ParentFingerPrintEnd = HDPrivateKey.ChildIndexStart = 12;
HDPrivateKey.ChildIndexEnd = HDPrivateKey.ChainCodeStart = 16;
HDPrivateKey.ChainCodeEnd = 32;
HDPrivateKey.PrivateKeyStart = 33;
HDPrivateKey.PrivateKeyEnd = HDPrivateKey.ChecksumStart = 65;
HDPrivateKey.ChecksumEnd = 69;
HDPrivateKey.SerializedByteSize = 82;
HDPrivateKey.VersionStart = 0;
HDPrivateKey.VersionEnd = HDPrivateKey.VersionStart + HDPrivateKey.VersionSize;
HDPrivateKey.DepthStart = HDPrivateKey.VersionEnd;
HDPrivateKey.DepthEnd = HDPrivateKey.DepthStart + HDPrivateKey.DepthSize;
HDPrivateKey.ParentFingerPrintStart = HDPrivateKey.DepthEnd;
HDPrivateKey.ParentFingerPrintEnd = HDPrivateKey.ParentFingerPrintStart + HDPrivateKey.ParentFingerPrintSize;
HDPrivateKey.ChildIndexStart = HDPrivateKey.ParentFingerPrintEnd;
HDPrivateKey.ChildIndexEnd = HDPrivateKey.ChildIndexStart + HDPrivateKey.ChildIndexSize;
HDPrivateKey.ChainCodeStart = HDPrivateKey.ChildIndexEnd;
HDPrivateKey.ChainCodeEnd = HDPrivateKey.ChainCodeStart + HDPrivateKey.ChainCodeSize;
HDPrivateKey.PrivateKeyStart = HDPrivateKey.ChainCodeEnd + 1;
HDPrivateKey.PrivateKeyEnd = HDPrivateKey.PrivateKeyStart + HDPrivateKey.PrivateKeySize;
HDPrivateKey.ChecksumStart = HDPrivateKey.PrivateKeyEnd;
HDPrivateKey.ChecksumEnd = HDPrivateKey.ChecksumStart + HDPrivateKey.CheckSumSize;
assert(HDPrivateKey.ChecksumEnd === HDPrivateKey.SerializedByteSize);
HDPrivateKey.Errors = {};
HDPrivateKey.Errors.InvalidArgument = 'Invalid argument, expected string or Buffer';

View File

@ -8,6 +8,12 @@ var base58check = require('./encoding/base58check');
var Address = require('./address');
var PublicKey = require('./publickey');
var assert = require('assert');
var COMPRESSED_LENGTH = 34;
var UNCOMPRESSED_LENGTH = 33;
var RAW_LENGTH = 32;
/**
*
* Instantiate a PrivateKey from a BN, Buffer and WIF.
@ -38,9 +44,10 @@ var PrivateKey = function PrivateKey(data, network, compressed) {
return new PrivateKey(data, network, compressed);
}
network = network || 'livenet';
var info = {
compressed: typeof(compressed) !== 'undefined' ? compressed : true,
network: network || 'mainnet'
network: network
};
// detect type of data
@ -60,9 +67,6 @@ var PrivateKey = function PrivateKey(data, network, compressed) {
if (!info.bn.lt(Point.getN())) {
throw new TypeError('Number must be less than N');
}
if (typeof(networks[info.network]) === 'undefined') {
throw new TypeError('Must specify the network ("mainnet" or "testnet")');
}
if (typeof(info.compressed) !== 'boolean') {
throw new TypeError('Must specify whether the corresponding public key is compressed or not (true or false)');
}
@ -70,6 +74,7 @@ var PrivateKey = function PrivateKey(data, network, compressed) {
this.bn = info.bn;
this.compressed = info.compressed;
this.network = info.network;
this.publicKey = this.toPublicKey();
return this;
@ -104,37 +109,29 @@ PrivateKey._getRandomBN = function(){
* @private
*/
PrivateKey._transformBuffer = function(buf, network, compressed) {
/* jshint maxcomplexity: 8 */
var info = {};
if (buf.length === 1 + 32 + 1 && buf[1 + 32 + 1 - 1] === 1) {
info.compressed = false;
if (buf.length === COMPRESSED_LENGTH && buf[COMPRESSED_LENGTH-1] === 1) {
info.compressed = true;
} else if (buf.length === 1 + 32) {
info.compressed = false;
assert(buf[0] === networks.get(network).privatekey, 'Network version mismatch');
} else if (buf.length === RAW_LENGTH || buf.length === UNCOMPRESSED_LENGTH) {
if (buf.length === UNCOMPRESSED_LENGTH) {
assert(buf[0] === networks.get(network).privatekey, 'Network version mismatch');
buf = buf.slice(1, RAW_LENGTH);
}
} else {
throw new Error('Length of buffer must be 33 (uncompressed) or 34 (compressed)');
}
if (buf[0] === networks.mainnet.privatekey) {
info.network = 'mainnet';
} else if (buf[0] === networks.testnet.privatekey) {
info.network = 'testnet';
} else {
throw new Error('Invalid network');
}
if (network && info.network !== network){
throw TypeError('Private key network mismatch');
throw new Error('Length of buffer must be 32 to 34 (plain, uncompressed, or compressed)');
}
if (typeof(compressed) !== 'undefined' && info.compressed !== compressed){
throw TypeError('Private key compression mismatch');
}
info.bn = BN.fromBuffer(buf.slice(1, 32 + 1));
info.bn = BN.fromBuffer(buf);
return info;
};
/**

View File

@ -1,6 +1,15 @@
'use strict';
var _ = require('lodash');
var buffer = require('buffer');
var assert = require('assert');
var isHexa = function isHexa(value) {
if (!_.isString(value)) {
return false;
}
return /^[0-9a-fA-F]+$/.test(value);
};
module.exports = {
isValidJson: function isValidJson(arg) {
@ -16,6 +25,10 @@ module.exports = {
for (var i = 0; i < bytes; i++) {
result.write('\0', i);
}
return result;
},
integerAsSingleByteBuffer: function integerAsSingleByteBuffer(integer) {
return new Buffer([integer & 0xff]);
},
integerAsBuffer: function integerAsBuffer(integer) {
var bytes = [];
@ -25,16 +38,17 @@ module.exports = {
bytes.push(integer & 0xff);
return new Buffer(bytes);
},
isHexa: function isHexa(value) {
if (!_.isString(value)) {
return false;
}
return /^[0-9a-fA-F]+$/.test(value);
},
isHexa: isHexa,
isHexaString: isHexa,
integerFromBuffer: function integerFromBuffer(buffer) {
return buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
},
bufferToHex: function bufferToHex(buffer) {
return buffer.toString('hex');
},
hexToBuffer: function hexToBuffer(string) {
assert(isHexa(string));
return new buffer.Buffer(string, 'hex');
}
};

View File

@ -2,10 +2,11 @@
var should = require('chai').should();
var bitcore = require('../..');
var buffer = require('buffer');
var Base58 = bitcore.encoding.Base58;
describe('Base58', function() {
var buf = new Buffer([0, 1, 2, 3, 253, 254, 255]);
var buf = new buffer.Buffer([0, 1, 2, 3, 253, 254, 255]);
var enc = '1W7N4RuG';
it('should make an instance with "new"', function() {
@ -13,6 +14,21 @@ describe('Base58', function() {
should.exist(b58);
});
it('validates characters with no false negatives', function() {
Base58.validCharacters(
'123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
).should.equal(true);
});
it('validates characters from buffer', function() {
Base58.validCharacters(
new buffer.Buffer('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz')
).should.equal(true);
});
it('some characters are invalid (no false positives)', function() {
Base58.validCharacters('!@#%^$&*()\\').should.equal(false);
});
it('should make an instance without "new"', function() {
var b58 = Base58();
should.exist(b58);
@ -27,7 +43,7 @@ describe('Base58', function() {
it('should set a blank buffer', function() {
Base58().set({
buf: new Buffer([])
buf: new buffer.Buffer([])
});
});

View File

@ -14,6 +14,13 @@ describe('Base58Check', function() {
should.exist(b58);
});
it('can validate a serialized string', function() {
var address = '3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy';
Base58Check.validChecksum(address).should.equal(true);
address = address + 'a';
Base58Check.validChecksum(address).should.equal(false);
});
it('should make an instance without "new"', function() {
var b58 = Base58Check();
should.exist(b58);

View File

@ -15,7 +15,7 @@ var bitcore = require('..');
var HDPrivateKey = bitcore.HDPrivateKey;
var HDPublicKey = bitcore.HDPublicKey;
describe('BIP32 compliance', function() {
describe.only('BIP32 compliance', function() {
it('should initialize test vector 1 from the extended public key', function() {
new HDPublicKey(vector1_m_public).xpubkey.should.equal(vector1_m_public);