add support for Dark Wallet-style addresses

These functions are prefixed DW which stands for Dark Wallet. The code for the
Dark Wallet address format can be found here:

https://github.com/darkwallet/darkwallet/blob/develop/js/util/stealth.js

Note that I deliberately support only the simplest possible format, which is
where there is only one payload pubkey and the prefix is blank. I should now go
back and replace my old toString, fromString, toBuffer, fromBuffer functions
with these Dark Wallet versions, since they are much more well-thought out than
mine.
This commit is contained in:
Ryan X. Charles 2014-09-22 18:23:10 -07:00
parent 9b8ce05b15
commit 96df77429f
2 changed files with 116 additions and 0 deletions

View File

@ -2,6 +2,8 @@ var Stealthkey = require('./stealthkey');
var Base58check = require('../base58check');
var Pubkey = require('../pubkey');
var KDF = require('../kdf');
var BufferWriter = require('../bufferwriter');
var BufferReader = require('../bufferreader');
var StealthAddress = function StealthAddress(addrstr) {
if (!(this instanceof StealthAddress))
@ -20,6 +22,9 @@ var StealthAddress = function StealthAddress(addrstr) {
}
};
StealthAddress.mainver = 42;
StealthAddress.testver = 43;
StealthAddress.prototype.set = function(obj) {
this.payloadPubkey = obj.payloadPubkey || this.payloadPubkey;
this.scanPubkey = obj.scanPubkey || this.scanPubkey;
@ -56,6 +61,29 @@ StealthAddress.prototype.fromBuffer = function(buf) {
return this;
};
StealthAddress.prototype.fromDWBuffer = function(buf) {
var parsed = StealthAddress.parseDWBuffer(buf);
if ((parsed.version !== StealthAddress.mainver) && (parsed.version !== StealthAddress.testver))
throw new Error('Invalid version');
if (parsed.options !== 0)
throw new Error('Invalid options');
if (!parsed.scanPubkey)
throw new Error('Invalid scanPubkey');
if (parsed.payloadPubkeys.length !== 1)
throw new Error('Must have exactly one payloadPubkey');
if (parsed.nSigs !== 1)
throw new Error('Must require exactly one signature');
if (parsed.prefix.toString() !== "")
throw new Error('Only blank prefixes supported');
this.scanPubkey = parsed.scanPubkey;
this.payloadPubkey = parsed.payloadPubkeys[0];
return this;
};
StealthAddress.prototype.fromDWString = function(str) {
return this.fromDWBuffer(Base58check(str).toBuffer());
};
StealthAddress.prototype.fromString = function(str) {
var buf = Base58check.decode(str);
this.fromBuffer(buf);
@ -86,6 +114,27 @@ StealthAddress.prototype.toBuffer = function() {
return Buffer.concat([pBuf, sBuf]);
};
StealthAddress.prototype.toDWBuffer = function(networkstr) {
if (networkstr === 'testnet')
var version = StealthAddress.testver;
else
var version = StealthAddress.mainver;
var bw = new BufferWriter();
bw.writeUInt8(version);
bw.writeUInt8(0); //options
bw.write(this.scanPubkey.toDER(true));
bw.writeUInt8(1); //number of payload keys - we only support 1 (not multisig)
bw.write(this.payloadPubkey.toDER(true));
bw.writeUInt8(1); //number of signatures - we only support 1 (not multisig)
bw.writeUInt8(0); //prefix length - we do not support prefix yet
var buf = bw.concat();
return buf;
};
StealthAddress.prototype.toDWString = function(networkstr) {
return Base58check(this.toDWBuffer(networkstr)).toString();
};
StealthAddress.prototype.toString = function() {
var buf = this.toBuffer();
var b58 = Base58check.encode(buf);
@ -93,4 +142,20 @@ StealthAddress.prototype.toString = function() {
return b58;
};
StealthAddress.parseDWBuffer = function(buf) {
var br = new BufferReader(buf);
var parsed = {};
parsed.version = br.readUInt8();
parsed.options = br.readUInt8();
parsed.scanPubkey = Pubkey().fromBuffer(br.read(33));
parsed.nPayloadPubkeys = br.readUInt8();
parsed.payloadPubkeys = [];
for (var i = 0; i < parsed.nPayloadPubkeys; i++)
parsed.payloadPubkeys.push(Pubkey().fromBuffer(br.read(33)));
parsed.nSigs = br.readUInt8();
parsed.nPrefix = br.readUInt8();
parsed.prefix = br.read(parsed.nPrefix / 8);
return parsed;
};
module.exports = StealthAddress;

View File

@ -26,6 +26,7 @@ describe('StealthAddress', function() {
senderKeypair.privkey2pubkey();
var addressString = '9dDbC9FzZ74r8njQkXD6W27gtrxLiWaeFPHxeo1fynQRXPicqxVt7u95ozbwoVVMXyrzaHKN9owsteg63FgwDfrxWx82SAW';
var dwhex = '2a0002697763d7e9becb0c180083738c32c05b0e2fee26d6278020c06bbb04d5f66b32010362408459041e0473298af3824dbabe4d2b7f846825ed4d1c2e2c670c07cb275d0100';
it('should make a new stealth address', function() {
var sa = new StealthAddress();
@ -69,6 +70,22 @@ describe('StealthAddress', function() {
});
describe('#fromDWBuffer', function() {
it('should parse this DW buffer', function() {
StealthAddress().fromDWBuffer(new Buffer(dwhex, 'hex')).toDWBuffer().toString('hex').should.equal(dwhex);
});
});
describe('#fromDWString', function() {
it('should parse this DW buffer', function() {
StealthAddress().fromDWString(Base58check(new Buffer(dwhex, 'hex')).toString()).toDWBuffer().toString('hex').should.equal(dwhex);
});
});
describe('#fromString', function() {
it('should give a stealthkey address with the right pubkeys', function() {
@ -126,6 +143,24 @@ describe('StealthAddress', function() {
});
describe('#toDWBuffer', function() {
it('should return this known address buffer', function() {
var buf = Base58check.decode(addressString);
StealthAddress().fromBuffer(buf).toDWBuffer().toString('hex').should.equal(dwhex);
});
});
describe('#toDWString', function() {
it('should return this known address buffer', function() {
var buf = Base58check.decode(addressString);
StealthAddress().fromBuffer(buf).toDWString().should.equal(Base58check(new Buffer(dwhex, 'hex')).toString());
});
});
describe('#toString', function() {
it('should return this known address string', function() {
@ -134,4 +169,20 @@ describe('StealthAddress', function() {
});
describe('@parseDWBuffer', function() {
it('should parse this known DW buffer', function() {
var buf = new Buffer(dwhex, 'hex');
var parsed = StealthAddress.parseDWBuffer(buf);
parsed.version.should.equal(42);
parsed.options.should.equal(0);
parsed.scanPubkey.toString().should.equal('02697763d7e9becb0c180083738c32c05b0e2fee26d6278020c06bbb04d5f66b32');
parsed.nPayloadPubkeys.should.equal(1);
parsed.payloadPubkeys[0].toString().should.equal('0362408459041e0473298af3824dbabe4d2b7f846825ed4d1c2e2c670c07cb275d');
parsed.nSigs.should.equal(1);
parsed.prefix.toString().should.equal('');
});
});
});