Ryan X. Charles
11 years ago
7 changed files with 147 additions and 0 deletions
@ -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')); |
@ -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; |
@ -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]); |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
Loading…
Reference in new issue