Merge remote-tracking branch 'olalonde/electrum'

This commit is contained in:
Ryan X. Charles 2014-04-22 09:20:42 -03:00
commit 7f7d076c7f
7 changed files with 147 additions and 0 deletions

View File

@ -49,6 +49,7 @@ requireWhenAccessed('Wallet', './lib/Wallet');
requireWhenAccessed('WalletKey', './lib/WalletKey'); requireWhenAccessed('WalletKey', './lib/WalletKey');
requireWhenAccessed('PeerManager', './lib/PeerManager'); requireWhenAccessed('PeerManager', './lib/PeerManager');
requireWhenAccessed('Message', './lib/Message'); requireWhenAccessed('Message', './lib/Message');
requireWhenAccessed('Electrum', './lib/Electrum');
module.exports.Buffer = Buffer; module.exports.Buffer = Buffer;
if (typeof process.versions === 'undefined') { if (typeof process.versions === 'undefined') {

View File

@ -30,6 +30,7 @@ var modules = [
'lib/Bloom', 'lib/Bloom',
'lib/Connection', 'lib/Connection',
'lib/Deserialize', 'lib/Deserialize',
'lib/Electrum',
'lib/Message', 'lib/Message',
'lib/Opcode', 'lib/Opcode',
'lib/Peer', 'lib/Peer',

11
examples/ElectrumMPK.js Normal file
View File

@ -0,0 +1,11 @@
var Electrum = require('../lib/Electrum');
var Address = require('../lib/Address');
var mpk = '92eea4d2f5263651db9e3222caded1fd4c89772f79a7c03fb6afc00e9d2c9d2ed9b86c2c95fc1171e49163079dacb7f048b3c509a27a490e1df9e7128362d468';
mpk = new Electrum(mpk);
var key0 = mpk.generatePubKey(0);
var addr0 = Address.fromPubKey(key0);
console.log(addr0.as('base58'));

54
lib/Electrum.js Normal file
View File

@ -0,0 +1,54 @@
var Key = require('./Key'),
Point = require('./Point'),
twoSha256 = require('../util').twoSha256,
buffertools = require('buffertools'),
bignum = require('bignum');
/**
* Pre-BIP32 Electrum public key derivation (electrum <2.0)
*
* For now, this class can only understands master public keys.
* It doesn't support derivation from a private master key (TODO).
*
* @example examples/ElectrumMPK.js
*/
function Electrum (master_public_key) {
this.mpk = new Buffer(master_public_key, 'hex');
}
Electrum.prototype.getSequence = function (for_change, n) {
var mode = for_change ? 1 : 0;
var buf = Buffer.concat([ new Buffer(n + ':' + mode + ':', 'utf8'), this.mpk ]);
return bignum.fromBuffer(twoSha256(buf));
};
Electrum.prototype.generatePubKey = function (n, for_change) {
var x = bignum.fromBuffer(this.mpk.slice(0, 32), { size: 32 });
var y = bignum.fromBuffer(this.mpk.slice(32, 64), { size: 32 });
var mpk_pt = new Point(x, y);
var sequence = this.getSequence(for_change, n);
var sequence_key = new Key();
sequence_key.private = sequence.toBuffer();
sequence_key.regenerateSync();
var sequence_pt = Point.fromKey(sequence_key);
pt = Point.add(mpk_pt, sequence_pt);
var xbuf = pt.x.toBuffer({ size: 32 });
var ybuf = pt.y.toBuffer({ size: 32 });
var prefix = new Buffer([0x04]);
var key = new Key();
key.compressed = false;
key.public = Buffer.concat([prefix, xbuf, ybuf]);
return key.public;
};
Electrum.prototype.generateChangePubKey = function (sequence) {
return this.generatePubKey(sequence, true);
};
module.exports = Electrum;

View File

@ -317,6 +317,7 @@ Key::SetPublic(Local<String> property, Local<Value> value, const AccessorInfo& i
if (!ret) { if (!ret) {
// TODO: Error // TODO: Error
VException("Invalid public key.");
return; return;
} }

View File

@ -22,6 +22,7 @@
<script src="test.Bloom.js"></script> <script src="test.Bloom.js"></script>
<script src="test.Connection.js"></script> <script src="test.Connection.js"></script>
<script src="test.EncodedData.js"></script> <script src="test.EncodedData.js"></script>
<script src="test.Electrum.js"></script>
<script src="test.Key.js"></script> <script src="test.Key.js"></script>
<script src="test.main.js"></script> <script src="test.main.js"></script>
<script src="test.Message.js"></script> <script src="test.Message.js"></script>

78
test/test.Electrum.js Normal file
View File

@ -0,0 +1,78 @@
'use strict';
var chai = chai || require('chai');
var bitcore = bitcore || require('../bitcore');
var should = chai.should();
var Electrum = bitcore.Electrum;
var Address = bitcore.Address;
var mpk = '92eea4d2f5263651db9e3222caded1fd4c89772f79a7c03fb6afc00e9d2c9d2ed9b86c2c95fc1171e49163079dacb7f048b3c509a27a490e1df9e7128362d468';
/**
* The hard coded values (expected public key / address hash) were retrieved
* by creating an Electrum wallet and looking which addresses were generated for
* master public key mpk. Not very rigorous but it will help catch regressions.
*/
describe('Electrum', function() {
it('should initialze the main object', function() {
should.exist(Electrum);
});
it('should be able to create instance', function() {
var elec = new Electrum(mpk);
should.exist(elec);
});
it('should be able to call generatePubKey', function() {
var elec = new Electrum(mpk);
(function () {
elec.generatePubKey(0);
}).should.not.throw(Error);
});
it('should be able to call generateChangePubKey', function() {
var elec = new Electrum(mpk);
(function () {
elec.generateChangePubKey(0);
}).should.not.throw(Error);
});
it('should generate correct public key at sequence 0', function() {
var elec = new Electrum(mpk);
var pubkey = elec.generatePubKey(0);
var addr = Address.fromPubKey(pubkey);
addr.as('base58').should.equal('15Ur7LV4hZFvFYQHkB12g1mdnKuHyHBDiW');
});
it('should generate correct (change) public keys at sequence 0,1,2', function() {
var expected_values = {
receiving: [
'15Ur7LV4hZFvFYQHkB12g1mdnKuHyHBDiW',
'19K48MhyXK4qChGCUJzAHxLFjbQRg9tb9F',
'1EfoxVmRVzYf1a1WELv2qMvEEpu2u5pXsy'
],
change: [
'138EGyTTyXFuqWBr8Jd7qmPbnfJUFmmuQg',
'12H7HZhtn7aNpTySXysKXiyxULybMktukh',
'1KMEUhmmiSdjX9fsv8aCAiYnVZnTTjqx7W'
]
};
var elec = new Electrum(mpk);
var pubkey, addr, addr_change;
for (var i = 0; i < expected_values.receiving.length; i++) {
//receiving
pubkey = elec.generatePubKey(i);
addr = Address.fromPubKey(pubkey);
addr.as('base58').should.equal(expected_values.receiving[i]);
//change
pubkey = elec.generateChangePubKey(i);
addr = Address.fromPubKey(pubkey);
console.log('change');
addr.as('base58').should.equal(expected_values.change[i]);
}
});
});