diff --git a/index.js b/index.js index 9a711a5c9..7371c59f6 100644 --- a/index.js +++ b/index.js @@ -15,9 +15,9 @@ privsec.buffer = Buffer; privsec.constants = require('./lib/constants'); privsec.hash = require('./lib/hash'); privsec.point = require('./lib/point'); +privsec.privkey = require('./lib/privkey'); //privsec.key = require('lib/key'); -//privsec.privkey = require('lib/privkey'); //privsec.pubkey = require('lib/pubkey'); //privsec.script = require('lib/script'); //privsec.scriptexec = require('lib/scriptexec'); diff --git a/lib/privkey.js b/lib/privkey.js new file mode 100644 index 000000000..ecb74931c --- /dev/null +++ b/lib/privkey.js @@ -0,0 +1,77 @@ +var bn = require('./bn'); +var point = require('./point'); +var constants = require('./constants'); +var base58check = require('./base58check'); + +var Privkey = function(n, network, compressed) { + if (typeof n === 'undefined') + return; + this.setNumber(n); + this.setNetwork(network); + this.setCompressed(compressed); +}; + +Privkey.prototype.setNumber = function(n) { + if (!n.lt(point.getN())) + throw new Error('privkey: Number must be less than N'); + this.n = n; +}; + +Privkey.prototype.setNetwork = function(network) { + if (typeof constants[network] === undefined) + throw new Error('privkey: Must specify the network ("mainnet" or "testnet")'); + this.network = network; +}; + +Privkey.prototype.setCompressed = function(compressed) { + if (typeof compressed !== 'boolean') + throw new Error('privkey: Must specify whether the corresponding public key is compressed or not (true or false)'); + this.compressed = compressed; +}; + +Privkey.prototype.toWIF = function() { + this.setNetwork(this.network); + this.setCompressed(this.compressed); + + var network = this.network; + var compressed = this.compressed; + + var privbuf = this.n.toBuffer({size: 32}); + var buf; + if (compressed) + buf = Buffer.concat([new Buffer([constants[network].privkey]), this.n.toBuffer({size: 32}), new Buffer([0x01])]); + else + buf = Buffer.concat([new Buffer([constants[network].privkey]), this.n.toBuffer({size: 32})]); + + return base58check.encode(buf); +}; + +Privkey.prototype.fromWIF = function(str) { + var buf = base58check.decode(str); + + if (buf.length === 1 + 32 + 1 && buf[1 + 32 + 1 - 1] == 1) + this.compressed = true; + else if (buf.length === 1 + 32) + this.compressed = false; + else + throw new Error('privkey: Length of buffer must be 33 (uncompressed) or 34 (compressed)'); + + if (buf[0] === constants.mainnet.privkey) + this.network = 'mainnet'; + else if (buf[0] === constants.testnet.privkey) + this.network = 'testnet'; + else + throw new Error('privkey: Invalid network'); + + this.n = bn.fromBuffer(buf.slice(1, 32 + 1)); +}; + +Privkey.prototype.toString = function() { + return this.toWIF(); +}; + +Privkey.prototype.fromString = function(str) { + this.fromWIF(str); +}; + +module.exports = Privkey; diff --git a/test/test.privkey.js b/test/test.privkey.js new file mode 100644 index 000000000..db271f9df --- /dev/null +++ b/test/test.privkey.js @@ -0,0 +1,74 @@ +var Privkey = require('../lib/privkey'); +var base58check = require('../lib/base58check'); +var bn = require('../lib/bn'); +var should = require('chai').should(); + +describe('#privkey', function() { + var hex = '96c132224121b509b7d0a16245e957d9192609c5637c6228311287b1be21627a'; + var buf = new Buffer(hex, 'hex'); + var enctestnet = 'cSdkPxkAjA4HDr5VHgsebAPDEh9Gyub4HK8UJr2DFGGqKKy4K5sG'; + var enctu = '92jJzK4tbURm1C7udQXxeCBvXHoHJstDXRxAMouPG1k1XUaXdsu'; + var encmainnet = 'L2Gkw3kKJ6N24QcDuH4XDqt9cTqsKTVNDGz1CRZhk9cq4auDUbJy'; + var encmu = '5JxgQaFM1FMd38cd14e3mbdxsdSa9iM2BV6DHBYsvGzxkTNQ7Un'; + + it('should create an empty private key', function() { + var privkey = new Privkey(); + should.exist(privkey); + }); + + it('should create a mainnet private key', function() { + var privkey = new Privkey(bn.fromBuffer(buf), 'mainnet', true); + privkey.toString().should.equal(encmainnet); + }); + + it('should create an uncompressed testnet private key', function() { + var privkey = new Privkey(bn.fromBuffer(buf), 'testnet', false); + privkey.toString().should.equal(enctu); + }); + + it('should create an uncompressed mainnet private key', function() { + var privkey = new Privkey(bn.fromBuffer(buf), 'mainnet', false); + privkey.toString().should.equal(encmu); + }); + + describe('#fromWIF', function() { + + it('should parse this compressed testnet address correctly', function() { + var privkey = new Privkey(); + privkey.fromWIF(encmainnet); + privkey.toWIF().should.equal(encmainnet); + }); + + }); + + describe('#toWIF', function() { + + it('should parse this compressed testnet address correctly', function() { + var privkey = new Privkey(); + privkey.fromWIF(enctestnet); + privkey.toWIF().should.equal(enctestnet); + }); + + }); + + describe('#fromString', function() { + + it('should parse this uncompressed testnet address correctly', function() { + var privkey = new Privkey(); + privkey.fromString(enctu); + privkey.toWIF().should.equal(enctu); + }); + + }); + + describe('#toString', function() { + + it('should parse this uncompressed mainnet address correctly', function() { + var privkey = new Privkey(); + privkey.fromString(encmu); + privkey.toString().should.equal(encmu); + }); + + }); + +});