Skip to content

Commit

Permalink
hd keys. address pruning. txpool balance.
Browse files Browse the repository at this point in the history
  • Loading branch information
chjj committed Feb 5, 2016
1 parent b9149a0 commit b835fff
Show file tree
Hide file tree
Showing 6 changed files with 271 additions and 63 deletions.
8 changes: 8 additions & 0 deletions lib/bcoin/address.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ function Address(options) {

inherits(Address, EventEmitter);

Address.prototype.__defineGetter__('balance', function() {
return this.getBalance();
});

Address.prototype.getBalance = function getBalance() {
return this._wallet.tx.getAddressBalance(this.getAddress());
};

Address.prototype.setRedeem = function setRedeem(redeem) {
var old = this.getScriptAddress();

Expand Down
52 changes: 47 additions & 5 deletions lib/bcoin/hd.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ function HDSeed(options) {

HDSeed.prototype.createSeed = function createSeed(passphrase) {
this.passphrase = passphrase || '';
return pbkdf2(this.mnemonic, 'mnemonic' + passphrase, 2048, 64);
return pbkdf2(this.mnemonic, 'mnemonic' + this.passphrase, 2048, 64);
};

HDSeed._mnemonic = function _mnemonic(entropy) {
Expand Down Expand Up @@ -634,11 +634,16 @@ HDPrivateKey.prototype._build = function _build(data) {
};

HDPrivateKey.prototype.derive = function derive(index, hardened) {
var data, hash, leftPart, chainCode, privateKey;
var cached, data, hash, leftPart, chainCode, privateKey;

if (typeof index === 'string')
return this.deriveString(index);

cached = cache.get(this.xprivkey, index);

if (cached)
return cached;

hardened = index >= constants.hd.hardened ? true : hardened;
if (index < constants.hd.hardened && hardened)
index += constants.hd.hardened;
Expand All @@ -656,7 +661,7 @@ HDPrivateKey.prototype.derive = function derive(index, hardened) {
.mod(ec.curve.n)
.toArray('be', 32);

return new HDPrivateKey({
var child = new HDPrivateKey({
version: this.version,
depth: new bn(this.depth).toNumber() + 1,
parentFingerPrint: this.fingerPrint,
Expand All @@ -665,6 +670,10 @@ HDPrivateKey.prototype.derive = function derive(index, hardened) {
privateKey: privateKey,
checksum: null
});

cache.set(this.xprivkey, index, child);

return child;
};

// https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
Expand Down Expand Up @@ -928,11 +937,16 @@ HDPublicKey.prototype._build = function _build(data) {
};

HDPublicKey.prototype.derive = function derive(index, hardened) {
var data, hash, leftPart, chainCode, pair, point, publicKey;
var cached, data, hash, leftPart, chainCode, pair, point, publicKey;

if (typeof index === 'string')
return this.deriveString(index);

cached = cache.get(this.xpubkey, index);

if (cached)
return cached;

if (index >= constants.hd.hardened || hardened)
throw new Error('invalid index');

Expand All @@ -948,7 +962,7 @@ HDPublicKey.prototype.derive = function derive(index, hardened) {
point = ec.curve.g.mul(leftPart).add(pair.pub);
publicKey = bcoin.ecdsa.keyPair({ pub: point }).getPublic(true, 'array');

return new HDPublicKey({
var child = new HDPublicKey({
version: this.version,
depth: new bn(this.depth).toNumber() + 1,
parentFingerPrint: this.fingerPrint,
Expand All @@ -957,6 +971,10 @@ HDPublicKey.prototype.derive = function derive(index, hardened) {
publicKey: publicKey,
checksum: null
});

cache.set(this.xpubkey, index, child);

return child;
};

HDPublicKey.isValidPath = function isValidPath(arg) {
Expand Down Expand Up @@ -1123,6 +1141,30 @@ function pbkdf2(key, salt, iterations, dkLen) {
return DK;
}

var cache = {
data: {},
count: 0
};

cache.set = function(key, index, value) {
key = key + '/' + index;

if (this.count > 200) {
this.data = {};
this.count = 0;
}

if (this.data[key] === undefined)
this.count++;

this.data[key] = value;
};

cache.get = function(key, index) {
key = key + '/' + index;
return this.data[key];
};

/**
* Expose
*/
Expand Down
159 changes: 147 additions & 12 deletions lib/bcoin/tx-pool.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ var EventEmitter = require('events').EventEmitter;
*/

function TXPool(wallet) {
var self = this;

if (!(this instanceof TXPool))
return new TXPool(wallet);

Expand All @@ -29,6 +31,20 @@ function TXPool(wallet) {
this._orphans = {};
this._lastTs = 0;
this._loaded = false;
this._addresses = {};
this._sent = new bn(0);
this._received = new bn(0);
this._balance = new bn(0);

this._wallet.on('remove address', function(address) {
address = self._addresses[address.getAddress()];
if (address) {
self._balance.isub(address.balance);
self._sent.isub(address.sent);
self._received.isub(address.received);
delete self._addresses[address];
}
});

// Load TXs from storage
this._init();
Expand Down Expand Up @@ -120,6 +136,8 @@ TXPool.prototype.add = function add(tx, noWrite, strict) {
if (!tx.verify(index))
return;

this._addInput(tx, index);

delete this._unspent[key];
updated = true;
continue;
Expand Down Expand Up @@ -167,6 +185,7 @@ TXPool.prototype.add = function add(tx, noWrite, strict) {
this._removeTX(orphan.tx, noWrite);
return false;
}
this._addInput(orphan.tx, index);
return true;
}

Expand All @@ -178,6 +197,8 @@ TXPool.prototype.add = function add(tx, noWrite, strict) {
if (!this._wallet.ownOutput(tx, i))
continue;

this._addOutput(tx, i);

key = hash + '/' + i;
orphans = this._orphans[key];

Expand Down Expand Up @@ -220,9 +241,15 @@ TXPool.prototype._storeTX = function _storeTX(hash, tx, noWrite) {

TXPool.prototype._removeTX = function _removeTX(tx, noWrite) {
var self = this;
var key;

for (var i = 0; i < tx.outputs.length; i++)
delete this._unspent[tx.hash('hex') + '/' + i];
for (var i = 0; i < tx.outputs.length; i++) {
key = tx.hash('hex') + '/' + i;
if (this._unspent[key]) {
delete this._unspent[key];
this._removeOutput(tx, i);
}
}

if (!this._storage || noWrite)
return;
Expand All @@ -246,21 +273,123 @@ TXPool.prototype.prune = function prune(pruneOrphans) {
this._orphans = {};
};

TXPool.prototype.getAll = function getAll() {
TXPool.prototype.getAll = function getAll(address) {
if (!address)
address = this._wallet;

return Object.keys(this._all).map(function(key) {
return this._all[key];
}, this).filter(function(tx) {
return this._wallet.ownOutput(tx)
|| this._wallet.ownInput(tx);
}, this);
return address.ownOutput(tx)
|| address.ownInput(tx);
});
};

TXPool.prototype._addOutput = function _addOutput(tx, i, remove) {
var i, data, address, addr, output;

output = tx.outputs[i];
data = bcoin.script.getOutputData(output.script);

if (!this._wallet.ownOutput(tx, i))
return;

if (data.scriptAddress)
data.addresses = [data.scriptAddress];

for (i = 0; i < data.addresses.length; i++) {
addr = data.addresses[i];
if (!this._addresses[addr]) {
this._addresses[addr] = {
received: new bn(0),
sent: new bn(0),
balance: new bn(0)
};
}
if (!remove) {
this._addresses[addr].balance.iadd(output.value);
this._addresses[addr].received.iadd(output.value);
} else {
this._addresses[addr].balance.isub(output.value);
this._addresses[addr].received.isub(output.value);
}
}

if (!remove) {
this._balance.iadd(output.value);
this._received.iadd(output.value);
} else {
this._balance.isub(output.value);
this._received.isub(output.value);
}
};

TXPool.prototype._removeOutput = function _removeOutput(tx, i) {
return this._addOutput(tx, i, true);
};

TXPool.prototype._addInput = function _addInput(tx, i, remove) {
var i, input, prev, data, address, addr, output;

input = tx.inputs[i];
assert(input.prevout.tx);

if (!this._wallet.ownOutput(input.prevout.tx, input.prevout.index))
return;

prev = input.prevout.tx.outputs[input.prevout.index];
data = bcoin.script.getInputData(input.script, prev.script);

if (data.scriptAddress)
data.addresses = [data.scriptAddress];

for (i = 0; i < data.addresses.length; i++) {
addr = data.addresses[i];
if (!this._addresses[addr]) {
this._addresses[addr] = {
received: new bn(0),
sent: new bn(0),
balance: new bn(0)
};
}
if (!remove) {
this._addresses[addr].balance.isub(prev.value);
this._addresses[addr].sent.iadd(prev.value);
} else {
this._addresses[addr].balance.iadd(prev.value);
this._addresses[addr].sent.isub(prev.value);
}
}

if (!remove) {
this._balance.isub(prev.value);
this._sent.iadd(prev.value);
} else {
this._balance.iadd(prev.value);
this._sent.isub(prev.value);
}
};

TXPool.prototype._removeInput = function _removeInput(tx, i) {
return this._addInput(tx, i, true);
};

TXPool.prototype.getAddressBalance = function getAddressBalance(address) {
if (this._addresses[address])
return this._addresses[address].balance.clone();

return new bn(0);
};

TXPool.prototype.getUnspent = function getUnspent() {
TXPool.prototype.getUnspent = function getUnspent(address) {
if (!address)
address = this._wallet;

return Object.keys(this._unspent).map(function(key) {
return this._unspent[key];
}, this).filter(function(item) {
return this._wallet.ownOutput(item.tx, item.index);
}, this);
return address.ownOutput(item.tx, item.index);
});
};

TXPool.prototype.getPending = function getPending() {
Expand All @@ -271,9 +400,9 @@ TXPool.prototype.getPending = function getPending() {
});
};

TXPool.prototype.getBalance = function getBalance() {
TXPool.prototype.getBalance = function getBalance(address) {
var acc = new bn(0);
var unspent = this.getUnspent();
var unspent = this.getUnspent(address);
if (unspent.length === 0)
return acc;

Expand All @@ -282,6 +411,12 @@ TXPool.prototype.getBalance = function getBalance() {
}, acc);
};

TXPool.prototype.getBalance = function getBalance(address) {
if (address)
return this.getAddressBalance(address);
return this._balance.clone();
};

// Legacy
TXPool.prototype.all = TXPool.prototype.getAll;
TXPool.prototype.unspent = TXPool.prototype.getUnspent;
Expand Down Expand Up @@ -321,7 +456,7 @@ TXPool.fromJSON = function fromJSON(wallet, json) {
});
});

return tx;
return txPool;
};

/**
Expand Down
Loading

0 comments on commit b835fff

Please sign in to comment.