wallets, now with reference counting.

This commit is contained in:
Christopher Jeffrey 2016-05-29 06:07:47 -07:00
parent ab9301ce7e
commit 390af13b14
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
6 changed files with 632 additions and 800 deletions

View File

@ -145,12 +145,12 @@ ldb.parseOptions = function parseOptions(options) {
*/
ldb.destroy = function destroy(options, callback) {
options = ldb.parseOptions(options);
var backend = ldb.parseOptions(options).db;
if (!options.db.destroy)
if (!backend.destroy)
return utils.nextTick(callback);
options.db.backend.destroy(options.location, callback);
backend.destroy(options.location, callback);
};
/**
@ -160,12 +160,12 @@ ldb.destroy = function destroy(options, callback) {
*/
ldb.repair = function repair(options, callback) {
options = ldb.parseOptions(options);
var backend = ldb.parseOptions(options).db;
if (!options.db.backend.repair)
if (!backend.repair)
return utils.asyncify(callback)(new Error('Cannot repair.'));
options.db.backend.repair(options.location, callback);
backend.repair(options.location, callback);
};
/*

View File

@ -71,15 +71,19 @@ function Wallet(options) {
this.id = options.id || null;
this.master = options.master || null;
this.accountKey = options.accountKey || null;
this.witness = options.witness || false;
this.loaded = false;
this.loading = false;
this.accountKey = options.accountKey || null;
this.accountIndex = options.accountIndex || 0;
this.receiveDepth = options.receiveDepth || 1;
this.changeDepth = options.changeDepth || 1;
this.receiveAddress = null;
this.changeAddress = null;
this.lookahead = options.lookahead != null ? options.lookahead : 5;
this.initialized = false;
this.initialized = options.initialized || false;
this.type = options.type || 'pubkeyhash';
this.compressed = options.compressed !== false;
@ -107,6 +111,9 @@ function Wallet(options) {
if (!this.id)
this.id = this.getID();
if (options.passphrase)
this.master.encrypt(options.passphrase);
// Non-alphanumeric IDs will break leveldb sorting.
assert(/^[a-zA-Z0-9]+$/.test(this.id), 'Wallet IDs must be alphanumeric.');
@ -120,6 +127,80 @@ function Wallet(options) {
utils.inherits(Wallet, EventEmitter);
/**
* Open the wallet, register with the database.
* @param {Function} callback
*/
Wallet.prototype.open = function open(callback) {
var self = this;
callback = utils.ensure(callback);
if (this.loaded)
return utils.nextTick(callback);
if (this.loading)
return this.once('open', callback);
this.loading = true;
try {
this.db.register(this);
} catch (e) {
this.emit('error', err);
return callback(err);
}
this.init(function(err) {
if (err) {
self.emit('error', err);
return callback(err);
}
self.loading = false;
self.loaded = true;
self.emit('open');
return callback();
});
};
/**
* Close the wallet, unregister with the database.
* @method
* @param {Function} callback
*/
Wallet.prototype.close =
Wallet.prototype.destroy = function destroy(callback) {
callback = utils.ensure(callback);
if (!this.loaded)
return utils.nextTick(callback);
assert(!this.loading);
try {
this.db.unregister(this);
} catch (e) {
this.emit('error', err);
return callback(err);
}
this.loaded = false;
return utils.nextTick(callback);
};
/**
* Attempt to intialize the wallet (generating
* the first addresses along with the lookahead
* addresses). Called automatically from the
* walletdb and open().
* @param {Function} callback
*/
Wallet.prototype.init = function init(callback) {
var self = this;
var addresses = [];
@ -499,8 +580,6 @@ Wallet.prototype.deriveAddress = function deriveAddress(change, index) {
address = new bcoin.keyring(options);
this.emit('add address', address);
this.cache.set(data.path, address);
return address;
@ -552,10 +631,7 @@ Wallet.prototype.createPath = function createPath(change, index) {
/**
* Parse a path.
* @param {String} path
* @returns {Object} {
* path: String,
* change: Boolean, index: Number
* }
* @returns {Object} Contains `path`, `change`, and `index`.
*/
Wallet.prototype.parsePath = function parsePath(path) {
@ -678,7 +754,8 @@ Wallet.prototype.fill = function fill(tx, options, callback) {
if (!options)
options = {};
assert(this.initialized);
if (!this.initialized)
return callback(new Error('Cannot use uninitialized wallet.'));
this.getCoins(function(err, coins) {
if (err)
@ -845,8 +922,6 @@ Wallet.prototype.deriveInputs = function deriveInputs(tx, callback) {
*/
Wallet.prototype.getPath = function getPath(address, callback) {
if (!address || typeof address !== 'string')
return callback();
this.db.getPath(this.id, address, callback);
};
@ -863,6 +938,9 @@ Wallet.prototype.getInputPaths = function getInputPaths(tx, callback) {
var i, input, address, path;
if (tx instanceof bcoin.input) {
if (!tx.coin)
return callback(new Error('Not all coins available.'));
return this.getPath(tx.coin.getHash(), function(err, path) {
if (err)
return callback(err);
@ -891,7 +969,7 @@ Wallet.prototype.getInputPaths = function getInputPaths(tx, callback) {
});
}, function(err) {
if (err)
return next(err);
return callback(err);
return callback(null, utils.uniq(paths));
});
};
@ -1487,8 +1565,9 @@ Wallet.prototype.__defineGetter__('address', function() {
Wallet.prototype.inspect = function inspect() {
return {
id: this.id,
type: this.type,
network: this.network.type,
initialized: this.initialized,
type: this.type,
m: this.m,
n: this.n,
keyAddress: this.initialized
@ -1525,6 +1604,7 @@ Wallet.prototype.toJSON = function toJSON() {
name: 'wallet',
network: this.network.type,
id: this.id,
initialized: this.initialized,
type: this.type,
m: this.m,
n: this.n,
@ -1558,6 +1638,7 @@ Wallet.parseJSON = function parseJSON(json) {
return {
network: json.network,
id: json.id,
initialized: json.initialized,
type: json.type,
m: json.m,
n: json.n,
@ -1573,12 +1654,18 @@ Wallet.parseJSON = function parseJSON(json) {
};
};
/**
* Serialize the wallet.
* @returns {Buffer}
*/
Wallet.prototype.toRaw = function toRaw(writer) {
var p = new BufferWriter(writer);
var i;
p.writeU32(this.network.magic);
p.writeVarString(this.id, 'utf8');
p.writeU8(this.initialized ? 1 : 0);
p.writeU8(this.type === 'pubkeyhash' ? 0 : 1);
p.writeU8(this.m);
p.writeU8(this.n);
@ -1599,14 +1686,19 @@ Wallet.prototype.toRaw = function toRaw(writer) {
return p;
};
Wallet.fromRaw = function fromRaw(data) {
return new Wallet(Wallet.parseRaw(data));
};
/**
* Parse a serialized wallet. Return a "naked"
* wallet object, suitable for passing into
* the wallet constructor.
* @param {Buffer} data
* @returns {Object}
*/
Wallet.parseRaw = function parseRaw(data) {
var p = new BufferReader(data);
var network = bcoin.network.fromMagic(p.readU32());
var id = p.readVarString('utf8');
var initialized = p.readU8() === 1;
var type = p.readU8() === 0 ? 'pubkeyhash' : 'multisig';
var m = p.readU8();
var n = p.readU8();
@ -1625,6 +1717,7 @@ Wallet.parseRaw = function parseRaw(data) {
return {
network: network.type,
id: id,
initialized: initialized,
type: type,
m: m,
n: n,
@ -1638,6 +1731,16 @@ Wallet.parseRaw = function parseRaw(data) {
};
};
/**
* Instantiate a wallet from serialized data.
* @param {Buffer} data
* @returns {Wallet}
*/
Wallet.fromRaw = function fromRaw(data) {
return new Wallet(Wallet.parseRaw(data));
};
/**
* Instantiate a Wallet from a
* jsonified wallet object.
@ -1669,7 +1772,7 @@ function MasterKey(options) {
if (!(this instanceof MasterKey))
return new MasterKey(options);
this.encrypted = options.encrypted;
this.encrypted = !!options.encrypted;
this.xprivkey = options.xprivkey;
this.phrase = options.phrase;
this.passphrase = options.passphrase;
@ -1696,6 +1799,8 @@ MasterKey.prototype.encrypt = function encrypt(passphrase) {
if (this.encrypted)
return;
assert(passphrase, 'Passphrase is required.');
this.key = null;
this.encrypted = true;
this.xprivkey = utils.encrypt(this.xprivkey, passphrase);
@ -1812,181 +1917,12 @@ MasterKey.fromJSON = function fromJSON(json) {
MasterKey.isMasterKey = function isMasterKey(obj) {
return obj
&& obj.xprivkey
&& typeof obj.encrypted === 'boolean'
&& typeof obj.decrypt === 'function';
};
/*
* CWallet
*/
function CWallet(id, db) {
var i, key;
if (!(this instanceof CWallet))
return new CWallet(id, db);
EventEmitter.call(this);
this.network = db.network;
this.db = db;
this.id = id;
this.loaded = false;
}
utils.inherits(CWallet, EventEmitter);
CWallet.prototype.open = function open(callback) {
var self = this;
if (this.loaded)
return utils.nextTick(callback);
this.loaded = true;
this.db.register(this.id, this);
return utils.nextTick(callback);
};
CWallet.prototype.close =
CWallet.prototype.destroy = function destroy(callback) {
callback = utils.ensure(callback);
if (!this.db)
return utils.nextTick(callback);
this.db.unregister(this.id, this);
this.db = null;
return utils.nextTick(callback);
};
CWallet.prototype.addKey = function addKey(key, callback) {
this.db.addKey(this.id, key, callback);
};
CWallet.prototype.removeKey = function removeKey(key, callback) {
this.db.removeKey(this.id, key, callback);
};
CWallet.prototype.getID = function getID() {
return this.id;
};
CWallet.prototype.createReceive = function createReceive(callback) {
return this.db.createAddress(this.id, false, callback);
};
CWallet.prototype.createChange = function createChange(callback) {
return this.db.createAddress(this.id, true, callback);
};
CWallet.prototype.createAddress = function createAddress(change, callback) {
return this.db.createAddress(this.id, change, callback);
};
CWallet.prototype.hasAddress = function hasAddress(address, callback) {
this.db.hasAddress(this.id, address, callback);
};
CWallet.prototype.fill = function fill(tx, options, callback) {
this.db.fill(this.id, tx, options, callback);
};
CWallet.prototype.fillCoins = function fillCoins(tx, callback) {
this.db.fillHistory(this.id, tx, callback);
};
CWallet.prototype.getCoin = function getCoin(hash, index, callback) {
this.db.getCoin(hash, index, callback);
};
CWallet.prototype.getTX = function getTX(hash, callback) {
this.db.getTX(hash, callback);
};
CWallet.prototype.createTX = function createTX(options, outputs, callback) {
this.db.createTX(this.id, options, outputs, callback);
};
CWallet.prototype.getPath = function getPath(address, callback) {
if (!address || typeof address !== 'string')
return callback();
this.db.getPath(this.id, address, callback);
};
CWallet.prototype.getRedeem = function getRedeem(hash, callback) {
this.db.getRedeem(this.id, hash, callback);
};
CWallet.prototype.zap = function zap(now, age, callback) {
return this.db.zap(this.id, now, age, callback);
};
CWallet.prototype.scriptInputs = function scriptInputs(tx, callback) {
this.db.scriptInputs(this.id, tx, callback);
};
CWallet.prototype.sign = function sign(tx, options, callback) {
this.db.sign(this.id, tx, options, callback);
};
CWallet.prototype.addTX = function addTX(tx, callback) {
return this.db.addTX(tx, callback);
};
CWallet.prototype.getHistory = function getHistory(callback) {
return this.db.getHistory(this.id, callback);
};
CWallet.prototype.getCoins = function getCoins(callback) {
return this.db.getCoins(this.id, callback);
};
CWallet.prototype.getUnconfirmed = function getUnconfirmed(callback) {
return this.db.getUnconfirmed(this.id, callback);
};
CWallet.prototype.getBalance = function getBalance(callback) {
return this.db.getBalance(this.id, callback);
};
CWallet.prototype.getLastTime = function getLastTime(callback) {
return this.db.getLastTime(this.id, callback);
};
CWallet.prototype.getLast = function getLast(limit, callback) {
return this.db.getLast(this.id, limit, callback);
};
CWallet.prototype.getTimeRange = function getTimeRange(options, callback) {
return this.db.getTimeRange(this.id, options, callback);
};
CWallet.prototype.getReceiveAddress = function getReceiveAddress(callback) {
return this.db.getReceiveAddress(this.id, callback);
};
CWallet.prototype.getInfo = function getInfo(callback) {
return this.db.getInfo(this.id, callback);
};
CWallet.prototype.inspect = function inspect() {
return '<CWallet id=' + this.id + '>';
};
CWallet.isCWallet = function isCWallet(obj) {
return obj
&& obj.db
&& obj.id
&& obj.getInfo === 'function';
};
/*
* Expose
*/
module.exports = Wallet;
module.exports.CWallet = CWallet;
bcoin.cwallet = CWallet;

View File

@ -16,7 +16,6 @@ var bcoin = require('./env');
var EventEmitter = require('events').EventEmitter;
var utils = require('./utils');
var assert = utils.assert;
var DUMMY = new Buffer([0]);
var BufferReader = require('./reader');
var BufferWriter = require('./writer');
@ -116,19 +115,6 @@ WalletDB.prototype._init = function _init() {
useFilter: true
});
this.db.open(function(err) {
if (err)
return self.emit('error', err);
self.tx._loadFilter(function(err) {
if (err)
return self.emit('error', err);
self.emit('open');
self.loaded = true;
});
});
this.tx.on('error', function(err) {
self.emit('error', err);
});
@ -164,7 +150,7 @@ WalletDB.prototype._init = function _init() {
utils.forEachSerial(map.output, function(id, next) {
if (self.listeners('balance').length === 0
&& !self.hasListener(id, ' balance')) {
&& !self.hasListener(id, 'balance')) {
return next();
}
@ -186,6 +172,19 @@ WalletDB.prototype._init = function _init() {
self.emit('balances', balances, map);
});
});
this.db.open(function(err) {
if (err)
return self.emit('error', err);
self.tx._loadFilter(function(err) {
if (err)
return self.emit('error', err);
self.emit('open');
self.loaded = true;
});
});
};
WalletDB.prototype.sync = function sync(tx, map, callback) {
@ -220,173 +219,80 @@ WalletDB.prototype.destroy = function destroy(callback) {
};
/**
* Helper function to get a wallet.
* @private
* Register a wallet with the walletdb.
* @param {WalletID} id
* @param {Function} errback
* @param {Function} callback
* @param {Wallet} wallet
*/
WalletDB.prototype.fetchWallet = function fetchWallet(id, errback, callback) {
var self = this;
WalletDB.prototype.register = function register(wallet) {
var id = wallet.id;
callback = utils.ensure(callback);
if (!this.watchers[id])
this.watchers[id] = { wallet: wallet, refs: 0 };
this.get(id, function(err, _, wallet) {
if (err)
return errback(err);
// Should never happen, and if it does, I will cry.
assert(this.watchers[id].wallet === wallet, 'I\'m crying.');
if (!wallet)
return errback(new Error('No wallet.'));
callback(wallet);
});
};
WalletDB.prototype.syncOutputDepth = function syncOutputDepth(id, tx, callback) {
this.fetchWallet(id, callback, function(wallet) {
wallet.syncOutputDepth(tx, callback);
});
};
WalletDB.prototype.createAddress = function createAddress(id, change, callback) {
this.fetchWallet(id, callback, function(wallet) {
wallet.createAddress(change, callback);
});
};
WalletDB.prototype.getReceiveAddress = function getReceiveAddress(id, callback) {
this.fetchWallet(id, callback, function(wallet) {
callback(null, wallet.receiveAddress);
});
};
WalletDB.prototype.getChangeAddress = function getChangeAddress(id, callback) {
this.fetchWallet(id, callback, function(wallet) {
callback(null, wallet.changeAddress);
});
};
WalletDB.prototype.fill = function fill(id, tx, options, callback) {
this.fetchWallet(id, callback, function(wallet) {
wallet.fill(tx, options, callback);
});
};
WalletDB.prototype.scriptInputs = function scriptInputs(id, tx, callback) {
this.fetchWallet(id, callback, function(wallet) {
wallet.scriptInputs(tx, callback);
});
};
WalletDB.prototype.sign = function sign(id, tx, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
if (typeof options === 'string' || Buffer.isBuffer(options))
options = { passphrase: options };
this.fetchWallet(id, callback, function(wallet) {
wallet.sign(tx, options, callback);
});
};
WalletDB.prototype.createTX = function createTX(id, options, outputs, callback) {
this.fetchWallet(id, callback, function(wallet) {
wallet.createTX(options, outputs, callback);
});
};
WalletDB.prototype.addKey = function addKey(id, key, callback) {
this.fetchWallet(id, callback, function(wallet) {
wallet.addKey(key, callback);
});
};
WalletDB.prototype.removeKey = function removeKey(id, key, callback) {
this.fetchWallet(id, callback, function(wallet) {
wallet.removeKey(key, callback);
});
};
WalletDB.prototype.getInfo = function getInfo(id, callback) {
this.fetchWallet(id, callback, function(wallet) {
callback(null, wallet);
});
};
WalletDB.prototype.getRedeem = function getRedeem(id, hash, callback) {
this.fetchWallet(id, callback, function(wallet) {
wallet.getRedeem(hash, callback);
});
// We do some reference counting here
// because we're thug like that (police
// have a fit when your papers legit).
this.watchers[id].refs++;
};
/**
* Save a "naked" (non-instantiated) wallet. Will
* also index the address table.
* @param {WalletID}
* @param {Object} json - "Naked" wallet.
* @param {Function} callback - Returns [Error, Object].
* Unregister a wallet with the walletdb.
* @param {WalletID} id
* @param {Wallet} wallet
*/
WalletDB.prototype.saveJSON = function saveJSON(id, json, callback) {
this.db.put('w/' + id, json, callback);
WalletDB.prototype.unregister = function unregister(wallet) {
var id = wallet.id;
var watcher = this.watchers[id];
var i;
if (!watcher)
return;
assert(watcher.wallet === wallet);
assert(watcher.refs !== 0, '`wallet.destroy()` called twice!');
if (--watcher.refs === 0)
delete this.watchers[id];
};
/**
* Remove wallet from the database.
* Fire an event for a registered wallet.
* @param {WalletID} id
* @param {Function} callback - Returns [Error, Object].
* @param {...Object} args
*/
WalletDB.prototype.removeJSON = function removeJSON(id, callback) {
var self = this;
WalletDB.prototype.fire = function fire(id) {
var args = Array.prototype.slice.call(arguments, 1);
var watcher = this.watchers[id];
callback = utils.ensure(callback);
if (!watcher)
return;
this.getJSON(id, function(err, json) {
if (err)
return callback(err);
self.db.del('w/' + id, function(err) {
if (err && err.type !== 'NotFoundError')
return callback(err);
return callback(null, json);
});
});
watcher.wallet.emit.apply(watcher.wallet, args);
};
/**
* Retrieve object from the database.
* @private
* Test for a listener on a registered wallet.
* @param {WalletID} id
* @param {Function} callback - Returns [Error, Object(nakedWallet)].
* @param {String} event
* @returns {Boolean}
*/
WalletDB.prototype.getJSON = function getJSON(id, callback) {
callback = utils.ensure(callback);
WalletDB.prototype.hasListener = function hasListener(id, event) {
var watcher = this.watchers[id];
if (!id)
return callback();
if (!watcher)
return false;
this.db.get('w/' + id, function(err, json) {
if (err && err.type === 'NotFoundError')
return callback();
if (watcher.wallet.listeners(event).length !== 0)
return true;
if (err)
return callback(err);
try {
json = bcoin.wallet.parseRaw(json);
} catch (e) {
return callback(e);
}
return callback(null, json);
});
return false;
};
/**
@ -397,30 +303,39 @@ WalletDB.prototype.getJSON = function getJSON(id, callback) {
WalletDB.prototype.get = function get(id, callback) {
var self = this;
var wallet;
callback = utils.ensure(callback);
if (!id)
return callback();
return this.getJSON(id, function(err, json) {
var wallet;
if (this.watchers[id]) {
this.watchers[id].refs++;
return callback(null, this.watchers[id].wallet);
}
this.db.get('w/' + id, function(err, data) {
if (err && err.type === 'NotFoundError')
return callback();
if (err)
return callback(err);
if (!json)
if (!data)
return callback();
try {
json.db = self;
wallet = new bcoin.wallet(json);
data = bcoin.wallet.parseRaw(data);
data.db = self;
wallet = new bcoin.wallet(data);
} catch (e) {
return callback(e);
}
wallet.init(function(err) {
wallet.open(function(err) {
if (err)
return callback(err);
return callback(null, new bcoin.cwallet(wallet.id, self), wallet);
return callback(null, wallet);
});
});
};
@ -432,13 +347,7 @@ WalletDB.prototype.get = function get(id, callback) {
*/
WalletDB.prototype.save = function save(wallet, callback) {
var self = this;
if (Array.isArray(wallet)) {
return utils.forEachSerial(wallet, function(wallet, next) {
self.save(wallet, next);
}, callback);
}
this.saveJSON(wallet.id, wallet.toRaw(), callback);
this.db.put('w/' + wallet.id, wallet.toRaw(), callback);
};
/**
@ -448,13 +357,12 @@ WalletDB.prototype.save = function save(wallet, callback) {
*/
WalletDB.prototype.remove = function remove(id, callback) {
var self = this;
if (Array.isArray(wallet)) {
return utils.forEachSerial(id, function(id, next) {
self.remove(id, next);
}, callback);
}
return this.removeJSON(id, callback);
this.db.del('w/' + id, function(err) {
if (err && err.type !== 'NotFoundError')
return callback(err);
return callback();
});
};
/**
@ -477,6 +385,8 @@ WalletDB.prototype.create = function create(options, callback) {
if (exists)
return callback(new Error('Wallet already exists.'));
options = utils.merge({}, options);
if (self.network.witness)
options.witness = options.witness !== false;
@ -484,11 +394,11 @@ WalletDB.prototype.create = function create(options, callback) {
options.db = self;
wallet = new bcoin.wallet(options);
wallet.init(function(err) {
wallet.open(function(err) {
if (err)
return callback(err);
return callback(null, new bcoin.cwallet(wallet.id, self), wallet);
return callback(null, wallet);
});
});
};
@ -515,12 +425,12 @@ WalletDB.prototype.has = function has(id, callback) {
WalletDB.prototype.ensure = function ensure(options, callback) {
var self = this;
return this.get(options.id, function(err, cwallet, wallet) {
return this.get(options.id, function(err, wallet) {
if (err)
return callback(err);
if (cwallet)
return callback(null, cwallet, wallet);
if (wallet)
return callback(null, wallet);
self.create(options, callback);
});
@ -611,6 +521,9 @@ WalletDB.prototype.hasAddress = function hasAddress(id, address, callback) {
*/
WalletDB.prototype.getAddress = function getAddress(address, callback) {
if (!address)
return callback();
this.db.fetch('W/' + address, parsePaths, callback);
};
@ -778,78 +691,115 @@ WalletDB.prototype.removeBlock = function removeBlock(block, callback) {
};
/**
* Register an event emitter with the walletdb.
* Helper function to get a wallet.
* @private
* @param {WalletID} id
* @param {EventEmitter} watcher
* @param {Function} callback
* @param {Function} handler
*/
WalletDB.prototype.register = function register(id, watcher) {
if (!this.watchers[id])
this.watchers[id] = [];
WalletDB.prototype.fetchWallet = function fetchWallet(id, callback, handler) {
var self = this;
if (this.watchers[id].indexOf(watcher) === -1)
this.watchers[id].push(watcher);
this.get(id, function(err, wallet) {
if (err)
return callback(err);
if (!wallet)
return callback(new Error('No wallet.'));
handler(wallet, function(err, result) {
// Kill the reference.
self.unregister(wallet);
if (err)
return callback(err);
callback(null, result);
});
});
};
/**
* Unregister an event emitter with the walletdb.
* @param {WalletID} id
* @param {EventEmitter} watcher
*/
WalletDB.prototype.unregister = function unregister(id, watcher) {
var watchers = this.watchers[id];
var i;
if (!watchers)
return;
i = watchers.indexOf(watcher);
if (i !== -1)
watchers.splice(i, 1);
if (watchers.length === 0)
delete this.watchers[id];
WalletDB.prototype.syncOutputDepth = function syncOutputDepth(id, tx, callback) {
this.fetchWallet(id, callback, function(wallet, callback) {
wallet.syncOutputDepth(tx, callback);
});
};
/**
* Fire an event for all registered event emitters.
* @param {WalletID} id
* @param {...Object} args
*/
WalletDB.prototype.fire = function fire(id) {
var args = Array.prototype.slice.call(arguments, 1);
var watchers = this.watchers[id];
var i;
if (!watchers)
return;
for (i = 0; i < watchers.length; i++)
watchers[i].emit.apply(watchers[i], args);
WalletDB.prototype.createAddress = function createAddress(id, change, callback) {
this.fetchWallet(id, callback, function(wallet, callback) {
wallet.createAddress(change, callback);
});
};
/**
* Test for a listener on a registered event emitter.
* @param {WalletID} id
* @param {String} event
* @returns {Boolean}
*/
WalletDB.prototype.getReceiveAddress = function getReceiveAddress(id, callback) {
this.fetchWallet(id, callback, function(wallet, callback) {
callback(null, wallet.receiveAddress);
});
};
WalletDB.prototype.hasListener = function hasListener(id, event) {
var watchers = this.watchers[id];
var i;
WalletDB.prototype.getChangeAddress = function getChangeAddress(id, callback) {
this.fetchWallet(id, callback, function(wallet, callback) {
callback(null, wallet.changeAddress);
});
};
if (!watchers)
return false;
WalletDB.prototype.fill = function fill(id, tx, options, callback) {
this.fetchWallet(id, callback, function(wallet, callback) {
wallet.fill(tx, options, callback);
});
};
for (i = 0; i < watchers.length; i++) {
if (watchers[i].listeners(event).length !== 0)
return true;
WalletDB.prototype.scriptInputs = function scriptInputs(id, tx, callback) {
this.fetchWallet(id, callback, function(wallet, callback) {
wallet.scriptInputs(tx, callback);
});
};
WalletDB.prototype.sign = function sign(id, tx, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
return false;
this.fetchWallet(id, callback, function(wallet, callback) {
wallet.sign(tx, options, callback);
});
};
WalletDB.prototype.createTX = function createTX(id, options, outputs, callback) {
if (typeof outputs === 'function') {
callback = outputs;
outputs = null;
}
this.fetchWallet(id, callback, function(wallet, callback) {
wallet.createTX(options, outputs, callback);
});
};
WalletDB.prototype.addKey = function addKey(id, key, callback) {
this.fetchWallet(id, callback, function(wallet, callback) {
wallet.addKey(key, callback);
});
};
WalletDB.prototype.removeKey = function removeKey(id, key, callback) {
this.fetchWallet(id, callback, function(wallet, callback) {
wallet.removeKey(key, callback);
});
};
WalletDB.prototype.getInfo = function getInfo(id, callback) {
this.fetchWallet(id, callback, function(wallet, callback) {
callback(null, wallet);
});
};
WalletDB.prototype.getRedeem = function getRedeem(id, hash, callback) {
this.fetchWallet(id, callback, function(wallet, callback) {
wallet.getRedeem(hash, callback);
});
};
/*

View File

@ -17,8 +17,7 @@ describe('Chain', function() {
walletdb = new bcoin.walletdb({ name: 'chain-test-wdb', db: 'memory' });
wallet = new bcoin.wallet({ db: walletdb });
miner = new bcoin.miner({
chain: chain,
address: wallet.getAddress()
chain: chain
});
chain.on('error', function() {});
@ -73,8 +72,12 @@ describe('Chain', function() {
it('should open chain and miner', function(cb) {
miner.open(function(err) {
assert.ifError(cb);
wallet.init(cb);
assert.ifError(err);
wallet.open(function(err) {
assert.ifError(err);
miner.address = wallet.getAddress();
cb();
});
});
});

View File

@ -19,18 +19,24 @@ describe('Mempool', function() {
db: 'memory'
});
var w;
mempool.on('error', function() {});
it('should open mempool', function(cb) {
mempool.open(cb);
});
it('should handle incoming orphans and TXs', function(cb) {
var w = new bcoin.wallet();
it('should open wallet', function(cb) {
w = new bcoin.wallet();
w.open(cb);
});
it('should handle incoming orphans and TXs', function(cb) {
var kp = bcoin.hd.generate();
// Coinbase
var t1 = bcoin.mtx().addOutput(w, 50000).addOutput(w, 10000); // 10000 instead of 1000
var prev = new bcoin.script([w.publicKey, opcodes.OP_CHECKSIG]);
var prev = new bcoin.script([kp.publicKey, opcodes.OP_CHECKSIG]);
var dummyInput = {
prevout: {
hash: constants.ONE_HASH.toString('hex'),
@ -49,84 +55,96 @@ describe('Mempool', function() {
sequence: 0xffffffff
};
t1.addInput(dummyInput);
t1.inputs[0].script = new bcoin.script([t1.createSignature(0, prev, w.privateKey, 'all', 0)]),
t1.inputs[0].script = new bcoin.script([t1.createSignature(0, prev, kp.privateKey, 'all', 0)]),
// balance: 51000
w.sign(t1);
var t2 = bcoin.mtx().addInput(t1, 0) // 50000
.addOutput(w, 20000)
.addOutput(w, 20000);
// balance: 49000
w.sign(t2);
var t3 = bcoin.mtx().addInput(t1, 1) // 10000
.addInput(t2, 0) // 20000
.addOutput(w, 23000);
// balance: 47000
w.sign(t3);
var t4 = bcoin.mtx().addInput(t2, 1) // 24000
.addInput(t3, 0) // 23000
.addOutput(w, 11000)
.addOutput(w, 11000);
// balance: 22000
w.sign(t4);
var f1 = bcoin.mtx().addInput(t4, 1) // 11000
.addOutput(new bcoin.wallet(), 9000);
// balance: 11000
w.sign(f1);
var fake = bcoin.mtx().addInput(t1, 1) // 1000 (already redeemed)
.addOutput(w, 6000); // 6000 instead of 500
// Script inputs but do not sign
w.scriptInputs(fake);
// Fake signature
fake.inputs[0].script.code[0] = new Buffer([0,0,0,0,0,0,0,0,0]);
// balance: 11000
[t2, t3, t4, f1, fake].forEach(function(tx) {
tx.inputs.forEach(function(input) {
delete input.coin;
});
});
// Just for debugging
t1.hint = 't1';
t2.hint = 't2';
t3.hint = 't3';
t4.hint = 't4';
f1.hint = 'f1';
fake.hint = 'fake';
mempool.addTX(fake, function(err) {
w.sign(t1, function(err, total) {
assert.ifError(err);
mempool.addTX(t4, function(err) {
var t2 = bcoin.mtx().addInput(t1, 0) // 50000
.addOutput(w, 20000)
.addOutput(w, 20000);
// balance: 49000
w.sign(t2, function(err, total) {
assert.ifError(err);
mempool.getBalance(function(err, balance) {
var t3 = bcoin.mtx().addInput(t1, 1) // 10000
.addInput(t2, 0) // 20000
.addOutput(w, 23000);
// balance: 47000
w.sign(t3, function(err, total) {
assert.ifError(err);
assert.equal(balance.total, 0);
mempool.addTX(t1, function(err) {
var t4 = bcoin.mtx().addInput(t2, 1) // 24000
.addInput(t3, 0) // 23000
.addOutput(w, 11000)
.addOutput(w, 11000);
// balance: 22000
w.sign(t4, function(err, total) {
assert.ifError(err);
mempool.getBalance(function(err, balance) {
var f1 = bcoin.mtx().addInput(t4, 1) // 11000
.addOutput(bcoin.address.fromData(new Buffer([])).toBase58(), 9000);
// balance: 11000
w.sign(f1, function(err, total) {
assert.ifError(err);
assert.equal(balance.total, 60000);
mempool.addTX(t2, function(err) {
var fake = bcoin.mtx().addInput(t1, 1) // 1000 (already redeemed)
.addOutput(w, 6000); // 6000 instead of 500
// Script inputs but do not sign
w.scriptInputs(fake, function(err) {
assert.ifError(err);
mempool.getBalance(function(err, balance) {
// Fake signature
fake.inputs[0].script.code[0] = new Buffer([0,0,0,0,0,0,0,0,0]);
// balance: 11000
[t2, t3, t4, f1, fake].forEach(function(tx) {
tx.inputs.forEach(function(input) {
delete input.coin;
});
});
// Just for debugging
t1.hint = 't1';
t2.hint = 't2';
t3.hint = 't3';
t4.hint = 't4';
f1.hint = 'f1';
fake.hint = 'fake';
mempool.addTX(fake, function(err) {
assert.ifError(err);
assert.equal(balance.total, 50000);
mempool.addTX(t3, function(err) {
mempool.addTX(t4, function(err) {
assert.ifError(err);
mempool.getBalance(function(err, balance) {
assert.ifError(err);
assert.equal(balance.total, 22000);
mempool.addTX(f1, function(err) {
assert.equal(balance.total, 0);
mempool.addTX(t1, function(err) {
assert.ifError(err);
mempool.getBalance(function(err, balance) {
assert.ifError(err);
assert.equal(balance.total, 20000);
mempool.getHistory(function(err, txs) {
assert(txs.some(function(tx) {
return tx.hash('hex') === f1.hash('hex');
}));
assert.equal(balance.total, 60000);
mempool.addTX(t2, function(err) {
assert.ifError(err);
mempool.getBalance(function(err, balance) {
assert.ifError(err);
assert.equal(balance.total, 50000);
mempool.addTX(t3, function(err) {
assert.ifError(err);
mempool.getBalance(function(err, balance) {
assert.ifError(err);
assert.equal(balance.total, 22000);
mempool.addTX(f1, function(err) {
assert.ifError(err);
mempool.getBalance(function(err, balance) {
assert.ifError(err);
assert.equal(balance.total, 20000);
mempool.getHistory(function(err, txs) {
assert(txs.some(function(tx) {
return tx.hash('hex') === f1.hash('hex');
}));
cb();
cb();
});
});
});
});
});
});
});
});
});
@ -142,11 +160,10 @@ describe('Mempool', function() {
});
it('should handle locktime', function(cb) {
var w = new bcoin.wallet();
var kp = bcoin.hd.generate();
// Coinbase
var t1 = bcoin.mtx().addOutput(w, 50000).addOutput(w, 10000); // 10000 instead of 1000
var prev = new bcoin.script([w.publicKey, opcodes.OP_CHECKSIG]);
var prev = new bcoin.script([kp.publicKey, opcodes.OP_CHECKSIG]);
var prevHash = bcoin.ec.random(32).toString('hex');
var dummyInput = {
prevout: {
@ -168,7 +185,7 @@ describe('Mempool', function() {
t1.addInput(dummyInput);
t1.setLocktime(200);
chain.tip.height = 200;
t1.inputs[0].script = new bcoin.script([t1.createSignature(0, prev, w.privateKey, 'all', 0)]),
t1.inputs[0].script = new bcoin.script([t1.createSignature(0, prev, kp.privateKey, 'all', 0)]),
mempool.addTX(t1, function(err) {
chain.tip.height = 0;
assert.ifError(err);
@ -177,11 +194,10 @@ describe('Mempool', function() {
});
it('should handle invalid locktime', function(cb) {
var w = new bcoin.wallet();
var kp = bcoin.hd.generate();
// Coinbase
var t1 = bcoin.mtx().addOutput(w, 50000).addOutput(w, 10000); // 10000 instead of 1000
var prev = new bcoin.script([w.publicKey, opcodes.OP_CHECKSIG]);
var prev = new bcoin.script([kp.publicKey, opcodes.OP_CHECKSIG]);
var prevHash = bcoin.ec.random(32).toString('hex');
var dummyInput = {
prevout: {
@ -203,7 +219,7 @@ describe('Mempool', function() {
t1.addInput(dummyInput);
t1.setLocktime(200);
chain.tip.height = 200 - 1;
t1.inputs[0].script = new bcoin.script([t1.createSignature(0, prev, w.privateKey, 'all', 0)]),
t1.inputs[0].script = new bcoin.script([t1.createSignature(0, prev, kp.privateKey, 'all', 0)]),
mempool.addTX(t1, function(err) {
chain.tip.height = 0;
assert(err);

View File

@ -82,40 +82,37 @@ describe('Wallet', function() {
if (witness)
flags |= bcoin.protocol.constants.flags.VERIFY_WITNESS;
wdb.create({ witness: witness }, function(err, w, w2) {
wdb.create({ witness: witness }, function(err, w) {
assert.ifError(err);
w.getReceiveAddress(function(err, a) {
if (witness)
assert(bcoin.address.parseBase58(a.getAddress()).type === 'witnesspubkeyhash');
else
assert(bcoin.address.parseBase58(a.getAddress()).type === 'pubkeyhash');
if (witness)
assert(bcoin.address.parseBase58(w.getAddress()).type === 'witnesspubkeyhash');
else
assert(bcoin.address.parseBase58(w.getAddress()).type === 'pubkeyhash');
// Input transcation
var src = bcoin.mtx({
outputs: [{
value: 5460 * 2,
address: bullshitNesting
? a.getProgramAddress()
: a.getAddress()
}, {
value: 5460 * 2,
address: bcoin.address.fromData(new Buffer([])).toBase58()
}]
});
// Input transcation
var src = bcoin.mtx({
outputs: [{
value: 5460 * 2,
address: bullshitNesting
? w.getProgramAddress()
: w.getAddress()
}, {
value: 5460 * 2,
address: bcoin.address.fromData(new Buffer([])).toBase58()
}]
});
src.addInput(dummyInput);
src.addInput(dummyInput);
var tx = bcoin.mtx()
.addInput(src, 0)
.addOutput(a.getAddress(), 5460);
var tx = bcoin.mtx()
.addInput(src, 0)
.addOutput(w.getAddress(), 5460);
w.sign(tx, function(err) {
assert.ifError(err);
utils.print(tx);
assert(tx.verify(null, true, flags));
cb();
});
w.sign(tx, function(err) {
assert.ifError(err);
assert(tx.verify(null, true, flags));
cb();
});
});
}
@ -174,107 +171,109 @@ describe('Wallet', function() {
var dw, di;
it('should have TX pool and be serializable', function(cb) {
wdb.create({}, function(err, w) {
assert.ifError(err);
w.getReceiveAddress(function(err, aw) {
assert.ifError(err);
wdb.create({}, function(err, f) {
assert.ifError(err);
f.getReceiveAddress(function(err, af) {
assert.ifError(err);
dw = w;
// Coinbase
var t1 = bcoin.mtx().addOutput(aw, 50000).addOutput(aw, 1000);
var t1 = bcoin.mtx().addOutput(w, 50000).addOutput(w, 1000);
t1.addInput(dummyInput);
// balance: 51000
// w.sign(t1);
w.sign(t1, function(err) {
assert.ifError(err);
var t2 = bcoin.mtx().addInput(t1, 0) // 50000
.addOutput(aw, 24000)
.addOutput(aw, 24000);
di = t2.inputs[0];
// balance: 49000
// w.sign(t2);
w.sign(t2, function(err) {
assert.ifError(err);
var t3 = bcoin.mtx().addInput(t1, 1) // 1000
.addInput(t2, 0) // 24000
.addOutput(aw, 23000);
// balance: 47000
// w.sign(t3);
w.sign(t3, function(err) {
assert.ifError(err);
var t4 = bcoin.mtx().addInput(t2, 1) // 24000
.addInput(t3, 0) // 23000
.addOutput(aw, 11000)
.addOutput(aw, 11000);
// balance: 22000
// w.sign(t4);
w.sign(t4, function(err) {
assert.ifError(err);
var f1 = bcoin.mtx().addInput(t4, 1) // 11000
.addOutput(af, 10000);
// balance: 11000
// w.sign(f1);
w.sign(f1, function(err) {
assert.ifError(err);
var fake = bcoin.mtx().addInput(t1, 1) // 1000 (already redeemed)
.addOutput(aw, 500);
// Script inputs but do not sign
// w.scriptInputs(fake);
w.scriptInputs(fake, function(err) {
assert.ifError(err);
// Fake signature
fake.inputs[0].script.code[0] = new Buffer([0,0,0,0,0,0,0,0,0]);
// balance: 11000
// Just for debugging
t1.hint = 't1';
t2.hint = 't2';
t3.hint = 't3';
t4.hint = 't4';
f1.hint = 'f1';
fake.hint = 'fake';
// Fake TX should temporarly change output
wdb.addTX(fake, function(err) {
assert.ifError(err);
wdb.addTX(t4, function(err) {
var t2 = bcoin.mtx().addInput(t1, 0) // 50000
.addOutput(w, 24000)
.addOutput(w, 24000);
di = t2.inputs[0];
// balance: 49000
// w.sign(t2);
w.sign(t2, function(err) {
assert.ifError(err);
w.getBalance(function(err, balance) {
var t3 = bcoin.mtx().addInput(t1, 1) // 1000
.addInput(t2, 0) // 24000
.addOutput(w, 23000);
// balance: 47000
// w.sign(t3);
w.sign(t3, function(err) {
assert.ifError(err);
assert.equal(balance.total, 22500);
wdb.addTX(t1, function(err) {
w.getBalance(function(err, balance) {
var t4 = bcoin.mtx().addInput(t2, 1) // 24000
.addInput(t3, 0) // 23000
.addOutput(w, 11000)
.addOutput(w, 11000);
// balance: 22000
// w.sign(t4);
w.sign(t4, function(err) {
assert.ifError(err);
var f1 = bcoin.mtx().addInput(t4, 1) // 11000
.addOutput(f, 10000);
// balance: 11000
// w.sign(f1);
w.sign(f1, function(err) {
assert.ifError(err);
assert.equal(balance.total, 73000);
wdb.addTX(t2, function(err) {
var fake = bcoin.mtx().addInput(t1, 1) // 1000 (already redeemed)
.addOutput(w, 500);
// Script inputs but do not sign
// w.scriptInputs(fake);
w.scriptInputs(fake, function(err) {
assert.ifError(err);
w.getBalance(function(err, balance) {
// Fake signature
fake.inputs[0].script.code[0] = new Buffer([0,0,0,0,0,0,0,0,0]);
// balance: 11000
// Just for debugging
t1.hint = 't1';
t2.hint = 't2';
t3.hint = 't3';
t4.hint = 't4';
f1.hint = 'f1';
fake.hint = 'fake';
// Fake TX should temporarly change output
wdb.addTX(fake, function(err) {
assert.ifError(err);
assert.equal(balance.total, 47000);
wdb.addTX(t3, function(err) {
wdb.addTX(t4, function(err) {
assert.ifError(err);
w.getBalance(function(err, balance) {
assert.ifError(err);
assert.equal(balance.total, 22000);
wdb.addTX(f1, function(err) {
assert.ifError(err);
assert.equal(balance.total, 22500);
wdb.addTX(t1, function(err) {
w.getBalance(function(err, balance) {
assert.ifError(err);
assert.equal(balance.total, 11000);
w.getHistory(function(err, txs) {
assert(txs.some(function(tx) {
return tx.hash('hex') === f1.hash('hex');
}));
assert.equal(balance.total, 73000);
wdb.addTX(t2, function(err) {
assert.ifError(err);
w.getBalance(function(err, balance) {
assert.ifError(err);
assert.equal(balance.total, 47000);
wdb.addTX(t3, function(err) {
assert.ifError(err);
w.getBalance(function(err, balance) {
assert.ifError(err);
assert.equal(balance.total, 22000);
wdb.addTX(f1, function(err) {
assert.ifError(err);
w.getBalance(function(err, balance) {
assert.ifError(err);
assert.equal(balance.total, 11000);
w.getHistory(function(err, txs) {
assert(txs.some(function(tx) {
return tx.hash('hex') === f1.hash('hex');
}));
//var w2 = bcoin.wallet.fromJSON(w.toJSON());
// assert.equal(w2.getBalance(), 11000);
// assert(w2.getHistory().some(function(tx) {
// return tx.hash('hex') === f1.hash('hex');
// }));
cb();
//var w2 = bcoin.wallet.fromJSON(w.toJSON());
// assert.equal(w2.getBalance(), 11000);
// assert(w2.getHistory().some(function(tx) {
// return tx.hash('hex') === f1.hash('hex');
// }));
cb();
});
});
});
});
});
});
});
});
});
@ -285,17 +284,9 @@ describe('Wallet', function() {
});
});
});
});
});
});
});
});
});
});
});
});
});
});
});
});
@ -314,20 +305,16 @@ describe('Wallet', function() {
it('should fill tx with inputs', function(cb) {
wdb.create({}, function(err, w1) {
assert.ifError(err);
w1.getReceiveAddress(function(err, aw1) {
assert.ifError(err);
wdb.create({}, function(err, w2) {
assert.ifError(err);
w2.getReceiveAddress(function(err, aw2) {
assert.ifError(err);
// Coinbase
var t1 = bcoin.mtx()
.addOutput(aw1, 5460)
.addOutput(aw1, 5460)
.addOutput(aw1, 5460)
.addOutput(aw1, 5460);
.addOutput(w1, 5460)
.addOutput(w1, 5460)
.addOutput(w1, 5460)
.addOutput(w1, 5460);
t1.addInput(dummyInput);
@ -336,7 +323,7 @@ describe('Wallet', function() {
assert.ifError(err);
// Create new transaction
var t2 = bcoin.mtx().addOutput(aw2, 5460);
var t2 = bcoin.mtx().addOutput(w2, 5460);
w1.fill(t2, { rate: 10000, round: true }, function(err) {
assert.ifError(err);
w1.sign(t2, function(err) {
@ -351,7 +338,7 @@ describe('Wallet', function() {
assert.equal(t2.getFee(), 10920);
// Create new transaction
var t3 = bcoin.mtx().addOutput(aw2, 15000);
var t3 = bcoin.mtx().addOutput(w2, 15000);
w1.fill(t3, { rate: 10000, round: true }, function(err) {
assert(err);
assert.equal(err.requiredFunds, 25000);
@ -360,28 +347,22 @@ describe('Wallet', function() {
});
});
});
});
});
});
});
});
it('should fill tx with inputs with accurate fee', function(cb) {
wdb.create({ master: KEY1 }, function(err, w1) {
assert.ifError(err);
w1.getReceiveAddress(function(err, aw1) {
assert.ifError(err);
wdb.create({ master: KEY2 }, function(err, w2) {
assert.ifError(err);
w2.getReceiveAddress(function(err, aw2) {
assert.ifError(err);
// Coinbase
var t1 = bcoin.mtx()
.addOutput(aw1, 5460)
.addOutput(aw1, 5460)
.addOutput(aw1, 5460)
.addOutput(aw1, 5460);
.addOutput(w1, 5460)
.addOutput(w1, 5460)
.addOutput(w1, 5460)
.addOutput(w1, 5460);
t1.addInput(dummyInput);
@ -390,73 +371,71 @@ describe('Wallet', function() {
assert.ifError(err);
// Create new transaction
var t2 = bcoin.mtx().addOutput(aw2, 5460);
var t2 = bcoin.mtx().addOutput(w2, 5460);
w1.fill(t2, { rate: 10000 }, function(err) {
assert.ifError(err);
w1.sign(t2, function(err) {
assert.ifError(err);
assert(t2.verify());
assert.equal(t2.getInputValue(), 16380);
// Should now have a change output:
assert.equal(t2.getOutputValue(), 11130);
assert.equal(t2.getFee(), 5250);
assert.equal(t2.getCost(), 2084);
assert.equal(t2.getBaseSize(), 521);
assert.equal(t2.getSize(), 521);
assert.equal(t2.getVirtualSize(), 521);
// Create new transaction
wdb.addTX(t2, function(err) {
assert.ifError(err);
var t3 = bcoin.mtx().addOutput(aw2, 15000);
w1.fill(t3, { rate: 10000 }, function(err) {
assert(err);
cb();
assert(t2.verify());
assert.equal(t2.getInputValue(), 16380);
// Should now have a change output:
assert.equal(t2.getOutputValue(), 11130);
assert.equal(t2.getFee(), 5250);
assert.equal(t2.getCost(), 2084);
assert.equal(t2.getBaseSize(), 521);
assert.equal(t2.getSize(), 521);
assert.equal(t2.getVirtualSize(), 521);
var balance;
w2.once('balance', function(b) {
balance = b;
});
// Create new transaction
wdb.addTX(t2, function(err) {
assert.ifError(err);
var t3 = bcoin.mtx().addOutput(w2, 15000);
w1.fill(t3, { rate: 10000 }, function(err) {
assert(err);
assert(balance.total === 5460);
cb();
});
});
});
});
});
});
});
});
});
});
});
it('should sign multiple inputs using different keys', function(cb) {
wdb.create({}, function(err, w1) {
assert.ifError(err);
w1.getReceiveAddress(function(err, aw1) {
assert.ifError(err);
wdb.create({}, function(err, w2) {
assert.ifError(err);
w2.getReceiveAddress(function(err, aw2) {
assert.ifError(err);
wdb.create({}, function(err, to) {
assert.ifError(err);
to.getReceiveAddress(function(err, ato) {
assert.ifError(err);
// Coinbase
var t1 = bcoin.mtx()
.addOutput(aw1, 5460)
.addOutput(aw1, 5460)
.addOutput(aw1, 5460)
.addOutput(aw1, 5460);
.addOutput(w1, 5460)
.addOutput(w1, 5460)
.addOutput(w1, 5460)
.addOutput(w1, 5460);
t1.addInput(dummyInput);
// Fake TX should temporarly change output
// Coinbase
var t2 = bcoin.mtx()
.addOutput(aw2, 5460)
.addOutput(aw2, 5460)
.addOutput(aw2, 5460)
.addOutput(aw2, 5460);
.addOutput(w2, 5460)
.addOutput(w2, 5460)
.addOutput(w2, 5460)
.addOutput(w2, 5460);
t2.addInput(dummyInput);
// Fake TX should temporarly change output
@ -468,7 +447,7 @@ describe('Wallet', function() {
// Create our tx with an output
var tx = bcoin.mtx();
tx.addOutput(ato, 5460);
tx.addOutput(to, 5460);
var cost = tx.getOutputValue();
var total = cost * constants.tx.MIN_FEE;
@ -479,7 +458,7 @@ describe('Wallet', function() {
assert.ifError(err);
// Add dummy output (for `left`) to calculate maximum TX size
tx.addOutput(aw1, 0);
tx.addOutput(w1, 0);
// Add our unspent inputs to sign
tx.addInput(coins1[0]);
@ -534,9 +513,6 @@ describe('Wallet', function() {
});
});
});
});
});
});
});
function multisig(witness, bullshitNesting, cb) {
@ -556,43 +532,6 @@ describe('Wallet', function() {
var w1, w2, w3, receive;
function getInfo(callback) {
var info = { change: {} };
utils.serial([
function(next) {
w1.getInfo(function(err, info_) {
assert.ifError(err);
info.w1 = info_;
next();
});
},
function(next) {
w2.getInfo(function(err, info_) {
assert.ifError(err);
info.w2 = info_;
next();
});
},
function(next) {
w3.getInfo(function(err, info_) {
assert.ifError(err);
info.w3 = info_;
next();
});
},
function(next) {
receive.getInfo(function(err, info_) {
assert.ifError(err);
info.receive = info_;
next();
});
}
], function(err) {
assert.ifError(err);
return callback(null, info);
});
}
utils.serial([
function(next) {
wdb.create(utils.merge({}, options), function(err, w1_) {
@ -625,38 +564,34 @@ describe('Wallet', function() {
], function(err) {
assert.ifError(err);
getInfo(function(err, a) {
assert.ifError(err);
utils.serial([
w1.addKey.bind(w1, a.w2.accountKey),
w1.addKey.bind(w1, a.w3.accountKey),
w2.addKey.bind(w2, a.w1.accountKey),
w2.addKey.bind(w2, a.w3.accountKey),
w3.addKey.bind(w3, a.w1.accountKey),
w3.addKey.bind(w3, a.w2.accountKey)
w1.addKey.bind(w1, w2.accountKey),
w1.addKey.bind(w1, w3.accountKey),
w2.addKey.bind(w2, w1.accountKey),
w2.addKey.bind(w2, w3.accountKey),
w3.addKey.bind(w3, w1.accountKey),
w3.addKey.bind(w3, w2.accountKey)
], function(err) {
assert.ifError(err);
getInfo(function(err, a) {
assert.ifError(err);
// w3 = bcoin.wallet.fromJSON(w3.toJSON());
// Our p2sh address
var addr = a.w1.getAddress();
var addr = w1.getAddress();
if (witness)
assert(bcoin.address.parseBase58(addr).type === 'witnessscripthash');
else
assert(bcoin.address.parseBase58(addr).type === 'scripthash');
assert.equal(a.w1.getAddress(), addr);
assert.equal(a.w2.getAddress(), addr);
assert.equal(a.w3.getAddress(), addr);
assert.equal(w1.getAddress(), addr);
assert.equal(w2.getAddress(), addr);
assert.equal(w3.getAddress(), addr);
var paddr = a.w1.getProgramAddress();
assert.equal(a.w1.getProgramAddress(), paddr);
assert.equal(a.w2.getProgramAddress(), paddr);
assert.equal(a.w3.getProgramAddress(), paddr);
var paddr = w1.getProgramAddress();
assert.equal(w1.getProgramAddress(), paddr);
assert.equal(w2.getProgramAddress(), paddr);
assert.equal(w3.getProgramAddress(), paddr);
// Add a shared unspent transaction to our wallets
var utx = bcoin.mtx();
@ -672,7 +607,7 @@ describe('Wallet', function() {
utx.ts = 1;
utx.height = 1;
assert.equal(a.w1.receiveDepth, 1);
assert.equal(w1.receiveDepth, 1);
wdb.addTX(utx, function(err) {
assert.ifError(err);
@ -681,91 +616,83 @@ describe('Wallet', function() {
wdb.addTX(utx, function(err) {
assert.ifError(err);
getInfo(function(err, a) {
assert.ifError(err);
assert.equal(a.w1.receiveDepth, 2);
assert.equal(a.w1.changeDepth, 1);
assert.equal(w1.receiveDepth, 2);
assert.equal(w1.changeDepth, 1);
assert(a.w1.getAddress() !== addr);
addr = a.w1.getAddress();
assert.equal(a.w1.getAddress(), addr);
assert.equal(a.w2.getAddress(), addr);
assert.equal(a.w3.getAddress(), addr);
assert(w1.getAddress() !== addr);
addr = w1.getAddress();
assert.equal(w1.getAddress(), addr);
assert.equal(w2.getAddress(), addr);
assert.equal(w3.getAddress(), addr);
// Create a tx requiring 2 signatures
var send = bcoin.mtx();
send.addOutput({ address: a.receive.getAddress(), value: 5460 });
send.addOutput({ address: receive.getAddress(), value: 5460 });
assert(!send.verify(null, true, flags));
w1.fill(send, { rate: 10000, round: true }, function(err) {
assert.ifError(err);
w1.sign(send, function(err) {
assert.ifError(err);
assert(!send.verify(null, true, flags));
w2.sign(send, function(err) {
assert.ifError(err);
assert(send.verify(null, true, flags));
assert.equal(a.w1.changeDepth, 1);
var change = a.w1.changeAddress.getAddress();
assert.equal(a.w1.changeAddress.getAddress(), change);
assert.equal(a.w2.changeAddress.getAddress(), change);
assert.equal(a.w3.changeAddress.getAddress(), change);
// Simulate a confirmation
send.ps = 0;
send.ts = 1;
send.height = 1;
wdb.addTX(send, function(err) {
assert.ifError(err);
wdb.addTX(send, function(err) {
assert(!send.verify(null, true, flags));
w2.sign(send, function(err) {
assert.ifError(err);
assert(send.verify(null, true, flags));
assert.equal(w1.changeDepth, 1);
var change = w1.changeAddress.getAddress();
assert.equal(w1.changeAddress.getAddress(), change);
assert.equal(w2.changeAddress.getAddress(), change);
assert.equal(w3.changeAddress.getAddress(), change);
// Simulate a confirmation
send.ps = 0;
send.ts = 1;
send.height = 1;
wdb.addTX(send, function(err) {
assert.ifError(err);
getInfo(function(err, a) {
wdb.addTX(send, function(err) {
assert.ifError(err);
wdb.addTX(send, function(err) {
assert.ifError(err);
assert.equal(a.w1.receiveDepth, 2);
assert.equal(a.w1.changeDepth, 2);
assert.equal(w1.receiveDepth, 2);
assert.equal(w1.changeDepth, 2);
assert(a.w1.getAddress() === addr);
assert(a.w1.changeAddress.getAddress() !== change);
change = a.w1.changeAddress.getAddress();
assert.equal(a.w1.changeAddress.getAddress(), change);
assert.equal(a.w2.changeAddress.getAddress(), change);
assert.equal(a.w3.changeAddress.getAddress(), change);
assert(w1.getAddress() === addr);
assert(w1.changeAddress.getAddress() !== change);
change = w1.changeAddress.getAddress();
assert.equal(w1.changeAddress.getAddress(), change);
assert.equal(w2.changeAddress.getAddress(), change);
assert.equal(w3.changeAddress.getAddress(), change);
if (witness)
send.inputs[0].witness.items[2] = new Buffer([]);
else
send.inputs[0].script.code[2] = 0;
if (witness)
send.inputs[0].witness.items[2] = new Buffer([]);
else
send.inputs[0].script.code[2] = 0;
assert(!send.verify(null, true, flags));
assert.equal(send.getFee(), 10000);
assert(!send.verify(null, true, flags));
assert.equal(send.getFee(), 10000);
// w3 = bcoin.wallet.fromJSON(w3.toJSON());
// assert.equal(a.w3.receiveDepth, 2);
// assert.equal(a.w3.changeDepth, 2);
//assert.equal(a.w3.getAddress(), addr);
//assert.equal(a.w3.changeAddress.getAddress(), change);
// w3 = bcoin.wallet.fromJSON(w3.toJSON());
// assert.equal(w3.receiveDepth, 2);
// assert.equal(w3.changeDepth, 2);
//assert.equal(w3.getAddress(), addr);
//assert.equal(w3.changeAddress.getAddress(), change);
cb();
cb();
});
});
});
});
});
});
});
});
});
});
});
});
});
});
});
});
}