refactor.

This commit is contained in:
Christopher Jeffrey 2016-05-28 12:02:39 -07:00
parent 14088e5b0c
commit 1002c222a6
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
9 changed files with 352 additions and 389 deletions

View File

@ -116,7 +116,6 @@ function Environment(options) {
this.options = options;
this._ensured = false;
this._debug = null;
this.isBrowser = utils.isBrowser;
@ -211,11 +210,8 @@ Environment.prototype.set = function set(options) {
options.prefix = options.prefix
|| process.env.BCOIN_PREFIX;
if (!options.prefix) {
if (!options.prefix)
options.prefix = utils.HOME + '/.bcoin';
if (options.network !== 'main')
options.prefix += '/' + options.network;
}
if (!options.db)
options.db = process.env.BCOIN_DB;
@ -244,8 +240,12 @@ Environment.prototype.set = function set(options) {
if (options.workerTime == null && process.env.BCOIN_WORKER_TIMEOUT != null)
options.workerTimeout = +process.env.BCOIN_WORKER_TIMEOUT;
if (options.debugFile && typeof options.debugFile !== 'string')
options.debugFile = options.prefix + '/debug.log';
if (options.debugFile && typeof options.debugFile !== 'string') {
options.debugFile = options.prefix;
if (options.network !== 'main')
options.debugFile += '/' + options.network;
options.debugFile += '/debug.log';
}
this.prefix = options.prefix;
this.networkType = options.network;
@ -272,23 +272,26 @@ Environment.prototype.set = function set(options) {
};
/**
* Ensure the `prefix`.
* @private
* Ensure a directory.
* @param {String} path
* @param {Boolean?} dirname
*/
Environment.prototype.ensurePrefix = function ensurePrefix() {
Environment.prototype.mkdir = function mkdir(path, dirname) {
if (this.isBrowser)
return;
if (this._ensured)
path = normalize(path, dirname);
if (!mkdir.paths)
mkdir.paths = {};
if (mkdir.paths[path])
return;
this._ensured = true;
mkdir.paths[path] = true;
mkdirp(this.prefix);
if (this.profile)
mkdirp(this.prefix + '/profiler');
return mkdirp(path);
};
/**
@ -369,7 +372,7 @@ Environment.prototype.write = function write(msg) {
return;
if (!this._debug) {
this.ensurePrefix();
this.mkdir(this.debugFile, true);
this._debug = fs.createWriteStream(this.debugFile, { flags: 'a' });
}
@ -385,6 +388,25 @@ Environment.prototype.now = function now() {
return this.time.now();
};
/**
* Normalize a path.
* @param {String} path
* @param {Boolean?} dirname
*/
function normalize(path, dirname) {
var parts;
path = path.replace(/\\/g, '/');
path = path.replace(/\/+$/, '');
parts = path.split(/\/+/);
if (dirname)
parts.pop();
return parts.join('/');
}
/**
* Create a full directory structure.
* @param {String} path

View File

@ -456,9 +456,9 @@ function HDPrivateKey(options) {
this.publicKey = ec.publicKeyCreate(options.privateKey, true);
this.fingerPrint = null;
this.mnemonic = options.mnemonic;
this.mnemonic = options.mnemonic || null;
this._xprivkey = options.xprivkey;
this._xprivkey = options.xprivkey || null;
this.hdPrivateKey = this;
this._hdPublicKey = null;
@ -692,6 +692,24 @@ HDPrivateKey.prototype.derivePath = function derivePath(path) {
return key;
};
/**
* Compare a key against an object.
* @param {Object} obj
* @returns {Boolean}
*/
HDPrivateKey.prototype.equal = function equal(obj) {
if (!HDPrivateKey.isHDPrivateKey(obj))
return false;
return this.network === obj.network
&& this.depth === obj.depth
&& utils.equal(this.parentFingerPrint, obj.parentFingerPrint)
&& this.childIndex === obj.childIndex
&& utils.equal(this.chainCode, obj.chainCode)
&& utils.equal(this.privateKey, obj.privateKey);
};
/**
* Create an hd private key from a seed.
* @param {Buffer|Mnemonic|Object} options - A seed,
@ -1021,8 +1039,8 @@ HDPrivateKey.fromJSON = function fromJSON(json, passphrase) {
HDPrivateKey.isHDPrivateKey = function isHDPrivateKey(obj) {
return obj
&& obj.hdPublicKey
&& obj.hdPublicKey !== obj
&& obj.privateKey
&& Buffer.isBuffer(obj.chainCode)
&& typeof obj.derive === 'function';
};
@ -1219,6 +1237,24 @@ HDPublicKey.prototype.derivePath = function derivePath(path) {
return key;
};
/**
* Compare a key against an object.
* @param {Object} obj
* @returns {Boolean}
*/
HDPublicKey.prototype.equal = function equal(obj) {
if (!HDPublicKey.isHDPublicKey(obj))
return false;
return this.network === obj.network
&& this.depth === obj.depth
&& utils.equal(this.parentFingerPrint, obj.parentFingerPrint)
&& this.childIndex === obj.childIndex
&& utils.equal(this.chainCode, obj.chainCode)
&& utils.equal(this.publicKey, obj.publicKey);
};
/**
* Convert key to a more json-friendly object.
* @method
@ -1344,8 +1380,8 @@ HDPublicKey.fromBase58 = function fromBase58(xkey) {
HDPublicKey.isHDPublicKey = function isHDPublicKey(obj) {
return obj
&& obj.hdPublicKey
&& obj.hdPublicKey === obj
&& !obj.privateKey
&& Buffer.isBuffer(obj.chainCode)
&& typeof obj.derive === 'function';
};

View File

@ -10,6 +10,7 @@
var bcoin = require('./env');
var LowlevelUp = require('./lowlevelup');
var utils = bcoin.utils;
var assert = utils.assert;
var db = {};
/**
@ -28,16 +29,13 @@ var db = {};
*/
function ldb(options) {
var file = ldb.getLocation(options);
options = ldb.parseOptions(options);
if (!db[file]) {
if (!options)
options = {};
db[file] = new LowlevelUp(file, {
keyEncoding: 'ascii',
valueEncoding: 'binary',
if (!db[options.location]) {
if (options.backend !== 'bst' && !bcoin.isBrowser)
bcoin.mkdir(options.location, true);
db[options.location] = new LowlevelUp(options.location, {
// LevelDB and others
createIfMissing: true,
errorIfExists: false,
@ -54,11 +52,11 @@ function ldb(options) {
mapSize: options.mapSize || 300 * (1024 << 20),
writeMap: options.writeMap || false,
db: ldb.getBackend(options.db)
db: options.db
});
}
return db[file];
return db[options.location];
}
/**
@ -67,7 +65,7 @@ function ldb(options) {
* @returns {Object}
*/
ldb.getName = function getName(db) {
ldb.getBackend = function getBackend(db) {
var name, ext;
if (!db)
@ -106,38 +104,38 @@ ldb.getName = function getName(db) {
};
/**
* Get database location based on options.
* Parse options.
* @param {Object} options
* @returns {String} Path.
* @returns {Object}
*/
ldb.getLocation = function getLocation(options) {
var backend = ldb.getName(options.db);
ldb.parseOptions = function parseOptions(options) {
var network = bcoin.network.get(options.network);
var backend = ldb.getBackend(options.db);
var location = options.location;
var db;
if (options.location)
return options.location;
return bcoin.prefix + '/' + options.name + '.' + backend.ext;
};
/**
* Require database backend module.
* @param {String} db
* @returns {Object} Module.
*/
ldb.getBackend = function getBackend(db) {
var backend = ldb.getName(db);
if (!location) {
assert(typeof options.name === 'string', 'Name or location required.');
location = bcoin.prefix;
if (network.type !== 'main')
location += '/' + network.type;
location += '/' + options.name + '.' + backend.ext;
}
if (backend.name === 'bst')
return require('./bst');
db = require('./bst');
else if (bcoin.isBrowser)
db = require('level-js');
else
db = require(options.db);
if (bcoin.isBrowser)
return require('level-js');
bcoin.ensurePrefix();
return require(backend.name);
return utils.merge({}, options, {
backend: backend.name,
ext: backend.ext,
location: location,
db: db
});
};
/**
@ -147,13 +145,12 @@ ldb.getBackend = function getBackend(db) {
*/
ldb.destroy = function destroy(options, callback) {
var file = ldb.getLocation(options);
var backend = ldb.getBackend(options.db);
options = ldb.parseOptions(options);
if (!backend.destroy)
if (!options.db.destroy)
return utils.nextTick(callback);
backend.destroy(file, callback);
options.db.backend.destroy(options.location, callback);
};
/**
@ -163,13 +160,12 @@ ldb.destroy = function destroy(options, callback) {
*/
ldb.repair = function repair(options, callback) {
var file = ldb.getLocation(options);
var backend = ldb.getBackend(options.db);
options = ldb.parseOptions(options);
if (!backend.repair)
if (!options.db.backend.repair)
return utils.asyncify(callback)(new Error('Cannot repair.'));
backend.repair(file, callback);
options.db.backend.repair(options.location, callback);
};
/*

View File

@ -767,6 +767,24 @@ MTX.prototype.maxSize = function maxSize(options, force) {
if (options.wallet)
wallet = options.wallet;
function getRedeem(vector, hash) {
var redeem = vector.getRedeem();
var address;
if (redeem)
return redeem;
if (!wallet)
return;
address = wallet.receiveAddress;
if (address.program && hash.length === 20)
return address.program;
return address.script;
}
// Calculate the size, minus the input scripts.
total = this.getBaseSize();
@ -800,10 +818,7 @@ MTX.prototype.maxSize = function maxSize(options, force) {
// here since it will be ignored by
// the isMultisig clause.
// OP_PUSHDATA2 [redeem]
redeem = wallet
? wallet.getRedeem(prev.code[1])
: input.script.getRedeem();
redeem = getRedeem(input.script, prev.code[1]);
if (redeem) {
prev = redeem;
sz = prev.getSize();
@ -834,10 +849,7 @@ MTX.prototype.maxSize = function maxSize(options, force) {
hadWitness = true;
if (prev.isWitnessScripthash()) {
redeem = wallet
? wallet.getRedeem(prev.code[1])
: input.witness.getRedeem();
redeem = getRedeem(input.witness, prev.code[1]);
if (redeem) {
prev = redeem;
sz = prev.getSize();

View File

@ -23,7 +23,6 @@ function ensure() {
if (bcoin.profile && !bcoin.isBrowser) {
v8profiler = require('v8-' + 'profiler');
fs = require('f' + 's');
bcoin.ensurePrefix();
}
}
@ -109,6 +108,8 @@ Profile.prototype.save = function save(callback) {
+ self.name
+ '.cpuprofile';
bcoin.mkdir(file, true);
fs.writeFile(file, result, callback);
});
};
@ -202,6 +203,8 @@ Snapshot.prototype.save = function save(callback) {
+ self.name
+ '.heapsnapshot';
bcoin.mkdir(file, true);
fs.writeFile(file, result, callback);
});
};

View File

@ -58,15 +58,18 @@ function Wallet(options) {
this.options = options;
this.network = bcoin.network.get(options.network);
this.db = options.db || bcoin.walletdb({ name: 'wtmp', db: 'memory' });
this.db = options.db;
if (!this.db)
this.db = new bcoin.walletdb({ name: 'wtmp', db: 'memory' });
if (!options.master)
options.master = bcoin.hd.fromMnemonic(null, this.network);
if (!(options.master instanceof bcoin.hd) && !(options.master instanceof MasterKey))
if (!bcoin.hd.isHD(options.master) && !MasterKey.isMasterKey(options.master))
options.master = bcoin.hd.fromAny(options.master, this.network);
if (!(options.master instanceof MasterKey))
if (!MasterKey.isMasterKey(options.master))
options.master = MasterKey.fromKey(options.master);
this.id = options.id || null;
@ -81,8 +84,7 @@ function Wallet(options) {
this.copayBIP45 = options.copayBIP45 || false;
this.lookahead = options.lookahead != null ? options.lookahead : 5;
this.cosignerIndex = -1;
this.saved = options.saved || false;
this.initialized = options.initialized || false;
this.initialized = false;
this.type = options.type || 'pubkeyhash';
this.derivation = options.derivation || 'bip44';
@ -111,6 +113,8 @@ function Wallet(options) {
key = key.derivePurpose45().hdPublicKey;
else if (this.derivation === 'bip44')
key = key.deriveAccount44(this.accountIndex).hdPublicKey;
else
assert(false);
this.accountKey = key;
}
@ -136,20 +140,20 @@ Wallet.prototype.init = function init(callback) {
var addresses = [];
var i;
this.saved = true;
// Waiting for more keys.
if (this.keys.length !== this.n) {
assert(!this.initialized);
return this.save(callback);
return this.db.open(function(err) {
if (err)
return callback(err);
self.save(callback);
});
}
if (this.initialized) {
assert(this.saved);
this.receiveAddress = this.deriveReceive(this.receiveDepth - 1);
this.changeAddress = this.deriveChange(this.changeDepth - 1);
//return callback();
return this.save(callback);
return this.db.open(callback);
}
this.initialized = true;
@ -172,74 +176,17 @@ Wallet.prototype.init = function init(callback) {
addresses.push(this.receiveAddress);
addresses.push(this.changeAddress);
return this.saveAddress(addresses, function(err) {
return this.db.open(function(err) {
if (err)
return callback(err);
return self.save(callback);
});
};
/**
* Open the wallet, wait for the database to load.
* @param {Function} callback
*/
Wallet.prototype.open = function open(callback) {
var self = this;
if (this.loaded)
return utils.nextTick(callback);
if (this._loading)
return this.once('open', callback);
this._loading = true;
this.db.register(this.id, this);
this.db.open(function(err) {
if (err)
return self.emit('error', err);
if (0)
if (self.saved) {
utils.nextTick(function() {
self.loaded = true;
self.emit('open');
callback();
});
return;
}
self.init(function(err) {
if (err) {
self.emit('error', err);
return self.saveAddress(addresses, function(err) {
if (err)
return callback(err);
}
self.loaded = true;
self.emit('open');
callback();
return self.save(callback);
});
});
};
/**
* Close the wallet, wait for the database to close.
* @method
* @param {Function} callback
*/
Wallet.prototype.close =
Wallet.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;
};
/**
* Add a public account/purpose key to the wallet for multisig.
* @param {HDPublicKey|Base58String} key - Account (bip44) or
@ -248,14 +195,17 @@ Wallet.prototype.destroy = function destroy(callback) {
*/
Wallet.prototype._addKey = function addKey(key) {
var result = false;
var index, i;
assert(key, 'Key required.');
if (Array.isArray(key)) {
for (i = 0; i < key.length; i++)
this._addKey(key[i]);
return;
for (i = 0; i < key.length; i++) {
if (this._addKey(key[i]))
result = true;
}
return result;
}
if (key instanceof bcoin.wallet) {
@ -269,25 +219,26 @@ Wallet.prototype._addKey = function addKey(key) {
if (key.hdPublicKey)
key = key.hdPublicKey;
assert(key instanceof bcoin.hd, 'Must add HD keys to wallet.');
if (!bcoin.hd.isHD(key))
throw new Error('Must add HD keys to wallet.');
if (this.derivation === 'bip44') {
if (!key || !key.isAccount44())
if (!key.isAccount44())
throw new Error('Must add HD account keys to BIP44 wallet.');
} else if (this.derivation === 'bip45') {
if (!key || !key.isPurpose45())
if (!key.isPurpose45())
throw new Error('Must add HD purpose keys to BIP45 wallet.');
}
for (i = 0; i < this.keys.length; i++) {
if (this.keys[i].xpubkey === key.xpubkey) {
if (this.keys[i].equal(key)) {
index = i;
break;
}
}
if (index != null)
return;
return false;
assert(!this._keysFinalized);
@ -295,11 +246,13 @@ Wallet.prototype._addKey = function addKey(key) {
if (this.keys.length === this.n)
this._finalizeKeys();
return true;
};
Wallet.prototype.addKey = function addKey(key, callback) {
this._addKey(key);
this.init(callback);
if (this._addKey(key))
this.init(callback);
};
/**
@ -310,14 +263,17 @@ Wallet.prototype.addKey = function addKey(key, callback) {
*/
Wallet.prototype._removeKey = function removeKey(key) {
var result = false;
var index, i;
assert(!this._keysFinalized);
if (Array.isArray(key)) {
for (i = 0; i < key.length; i++)
this._removeKey(key[i]);
return;
for (i = 0; i < key.length; i++) {
if (this._removeKey(key[i]))
result = true;
}
return result;
}
assert(key, 'Key required.');
@ -333,27 +289,30 @@ Wallet.prototype._removeKey = function removeKey(key) {
if (key.hdPublicKey)
key = key.hdPublicKey;
assert(key instanceof bcoin.hd, 'Must add HD keys to wallet.');
if (!bcoin.hd.isHD(key))
throw new Error('Must add HD keys to wallet.');
if (this.derivation === 'bip44') {
if (!key || !key.isAccount44())
if (!key.isAccount44())
throw new Error('Must add HD account keys to BIP44 wallet.');
} else if (this.derivation === 'bip45') {
if (!key || !key.isPurpose45())
if (!key.isPurpose45())
throw new Error('Must add HD purpose keys to BIP45 wallet.');
}
for (i = 0; i < this.keys.length; i++) {
if (this.keys[i].xpubkey === key.xpubkey) {
if (this.keys[i].equal(key)) {
index = i;
break;
}
}
if (index == null)
return;
return false;
this.keys.splice(index, 1);
return true;
};
Wallet.prototype.removeKey = function removeKey(key, callback) {
@ -362,8 +321,8 @@ Wallet.prototype.removeKey = function removeKey(key, callback) {
if (this.keys.length === this.n)
return callback(new Error('Cannot remove the fucking key now.'));
this._removeKey(key);
this.save(callback);
if (this._removeKey(key))
this.save(callback);
};
Wallet.prototype._finalizeKeys = function _finalizeKeys() {
@ -375,7 +334,7 @@ Wallet.prototype._finalizeKeys = function _finalizeKeys() {
this.keys = utils.sortHDKeys(this.keys);
for (i = 0; i < this.keys.length; i++) {
if (this.keys[i].xpubkey === this.accountKey.xpubkey) {
if (this.keys[i].equal(this.accountKey)) {
this.cosignerIndex = i;
break;
}
@ -754,7 +713,7 @@ Wallet.prototype.fill = function fill(tx, options, callback) {
rate: options.rate != null
? options.rate
: self.network.getRate(),
//wallet: self,
wallet: self,
m: self.m,
n: self.n
});
@ -835,7 +794,8 @@ Wallet.prototype.createTX = function createTX(options, outputs, callback) {
try {
tx.addOutput(outputs[i]);
} catch (e) {
return utils.asyncify(callback)(e);
callback = utils.asyncify(callback);
return callback(e);
}
}
@ -860,10 +820,15 @@ Wallet.prototype.createTX = function createTX(options, outputs, callback) {
if (!tx.checkInputs(height))
return callback(new Error('CheckInputs failed.'));
if (!self.scriptInputs(tx))
return callback(new Error('scriptInputs failed.'));
self.scriptInputs(tx, function(err, total) {
if (err)
return callback(err);
return callback(null, tx);
if (total === 0)
return callback(new Error('scriptInputs failed.'));
return callback(null, tx);
});
});
};
@ -1233,7 +1198,7 @@ Wallet.prototype.scriptInputs = function scriptInputs(tx, callback) {
Wallet.prototype.sign = function sign(tx, options, callback) {
var self = this;
var total = 0;
var i, address, key;
var i, address, key, master;
if (Array.isArray(tx)) {
utils.forEachSerial(tx, function(tx, next) {
@ -1255,24 +1220,27 @@ Wallet.prototype.sign = function sign(tx, options, callback) {
return callback(err);
try {
var master = self.master.decrypt(options.passphrase);
master = self.master.decrypt(options.passphrase);
} catch (e) {
return callback(null, 0);
}
for (i = 0; i < addresses.length; i++) {
address = addresses[i];
key = master;
if (self.derivation === 'bip44') {
key = key.deriveAccount44(self.accountIndex);
assert.equal(key.xpubkey, self.accountKey.xpubkey);
} else if (self.derivation === 'bip45')
} else if (self.derivation === 'bip45') {
key = key.derivePurpose45();
else
assert.equal(key.xpubkey, self.accountKey.xpubkey);
} else {
assert(false);
}
key = key.derive(address.path);
assert(utils.equal(key.getPublicKey(), address.key));
total += address.sign(tx, key, options.index, options.type);
@ -1590,8 +1558,6 @@ Wallet.prototype.toJSON = function toJSON() {
type: this.type,
m: this.m,
n: this.n,
initialized: this.initialized,
saved: this.saved,
witness: this.witness,
derivation: this.derivation,
copayBIP45: this.copayBIP45,
@ -1627,8 +1593,6 @@ Wallet.parseJSON = function parseJSON(json) {
type: json.type,
m: json.m,
n: json.n,
initialized: json.initialized,
saved: json.saved,
witness: json.witness,
derivation: json.derivation,
copayBIP45: json.copayBIP45,
@ -1704,26 +1668,18 @@ MasterKey.fromJSON = function fromJSON(json) {
});
};
MasterKey.isMasterKey = function isMasterKey(obj) {
return obj
&& obj.json
&& typeof obj.decrypt === 'function';
};
/*
* Expose
*/
module.exports = Wallet;
/*!
* wallet.js - wallet object for bcoin
* Copyright (c) 2014-2015, Fedor Indutny (MIT License)
* Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
* https://github.com/indutny/bcoin
*/
var bcoin = require('./env');
var EventEmitter = require('events').EventEmitter;
var utils = require('./utils');
var assert = utils.assert;
var constants = bcoin.protocol.constants;
var BufferWriter = require('./writer');
/**
* HD BIP-44/45 wallet
* @exports Wallet
@ -1764,7 +1720,7 @@ function CWallet(id, db) {
EventEmitter.call(this);
this.network = db.network;
this.db = db || bcoin.walletdb({ name: 'wtmp', db: 'memory' });
this.db = db;
this.id = id;
}
@ -1963,28 +1919,7 @@ CWallet.prototype.getPath = function getPath(address, callback) {
*/
CWallet.prototype.getRedeem = function getRedeem(hash, callback) {
var self = this;
var address;
if (typeof hash === 'string')
hash = new Buffer(hash, 'hex');
this.getPath(hash.toString('hex'), function(err, path) {
if (err)
return callback(err);
if (!path)
return callback();
address = self.deriveAddress(path);
if (address.program && hash.length === 20) {
if (utils.equal(hash, address.programHash))
return callback(null, address.program);
}
return callback(null, address.script);
});
;
};
/**
@ -2109,119 +2044,21 @@ CWallet.prototype.getReceiveAddress = function getReceiveAddress(callback) {
return this.db.getReceiveAddress(this.id, callback);
};
CWallet.prototype.getInfo = function getInfo(callback) {
return this.db.get(this.id, function(err, cwallet, wallet) {
if (err)
return callback(err);
return callback(null, wallet);
});
};
/**
* Convert the wallet to a more inspection-friendly object.
* @returns {Object}
*/
CWallet.prototype.inspect = function inspect() {
return {
id: this.id,
type: this.type,
network: this.network.type,
m: this.m,
n: this.n,
keyAddress: this.initialized
? this.keyAddress
: null,
scriptAddress: this.initialized
? this.scriptAddress
: null,
programAddress: this.initialized
? this.programAddress
: null,
witness: this.witness,
derivation: this.derivation,
copayBIP45: this.copayBIP45,
accountIndex: this.accountIndex,
receiveDepth: this.receiveDepth,
changeDepth: this.changeDepth,
master: this.master.toJSON(),
accountKey: this.accountKey.xpubkey,
keys: this.keys.map(function(key) {
return key.xpubkey;
})
};
};
/**
* Convert the wallet to an object suitable for
* serialization. Will automatically encrypt the
* master key based on the `passphrase` option.
* @returns {Object}
*/
CWallet.prototype.toJSON = function toJSON() {
return {
v: 3,
name: 'wallet',
network: this.network.type,
id: this.id,
type: this.type,
m: this.m,
n: this.n,
initialized: this.initialized,
saved: this.saved,
witness: this.witness,
derivation: this.derivation,
copayBIP45: this.copayBIP45,
accountIndex: this.accountIndex,
receiveDepth: this.receiveDepth,
changeDepth: this.changeDepth,
master: this.master.toJSON(),
accountKey: this.accountKey.xpubkey,
keys: this.keys.map(function(key) {
return key.xpubkey;
})
};
};
/**
* Handle a deserialized JSON wallet object.
* @returns {Object} A "naked" wallet (a
* plain javascript object which is suitable
* for passing to the Wallet constructor).
* @param {Object} json
* @param {String?} passphrase
* @returns {Object}
* @throws Error on bad decrypt
*/
CWallet.parseJSON = function parseJSON(json) {
assert.equal(json.v, 3);
assert.equal(json.name, 'wallet');
return {
network: json.network,
id: json.id,
type: json.type,
m: json.m,
n: json.n,
initialized: json.initialized,
saved: json.saved,
witness: json.witness,
derivation: json.derivation,
copayBIP45: json.copayBIP45,
accountIndex: json.accountIndex,
receiveDepth: json.receiveDepth,
changeDepth: json.changeDepth,
master: MasterKey.fromJSON(json.master),
accountKey: bcoin.hd.fromBase58(json.accountKey),
keys: json.keys.map(function(key) {
return bcoin.hd.fromBase58(key);
})
};
};
/**
* Instantiate a Wallet from a
* jsonified wallet object.
* @param {Object} json - The jsonified wallet object.
* @returns {Wallet}
*/
CWallet.fromJSON = function fromJSON(json) {
return new CWallet(CWallet.parseJSON(json));
return '<CWallet id=' + this.id + '>';
};
/**
@ -2232,8 +2069,9 @@ CWallet.fromJSON = function fromJSON(json) {
CWallet.isCWallet = function isCWallet(obj) {
return obj
&& typeof obj.receiveDepth === 'number'
&& obj.deriveAddress === 'function';
&& obj.db
&& obj.id
&& obj.getInfo === 'function';
};
/*

View File

@ -403,7 +403,7 @@ WalletDB.prototype.get = function get(id, callback) {
return callback(e);
}
wallet.open(function(err) {
wallet.init(function(err) {
if (err)
return callback(err);
@ -474,7 +474,7 @@ WalletDB.prototype.create = function create(options, callback) {
options.db = self;
wallet = new bcoin.wallet(options);
wallet.open(function(err) {
wallet.init(function(err) {
if (err)
return callback(err);
@ -514,7 +514,7 @@ WalletDB.prototype.ensure = function ensure(options, callback) {
return callback(e);
}
wallet.open(function(err) {
wallet.init(function(err) {
if (err)
return callback(err);

View File

@ -8,13 +8,14 @@ var opcodes = constants.opcodes;
constants.tx.COINBASE_MATURITY = 0;
describe('Chain', function() {
var chain, wallet, miner;
var chain, wallet, miner, walletdb;
var competingTip, oldTip, ch1, ch2, cb1, cb2;
this.timeout(5000);
chain = new bcoin.chain({ name: 'chain-test', db: 'memory' });
wallet = new bcoin.wallet();
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()
@ -40,13 +41,16 @@ describe('Chain', function() {
value: utils.satoshi('25.0')
});
redeemer.addOutput({
address: wallet.createAddress().getAddress(),
address: wallet.deriveAddress(false, 100).getAddress(),
value: utils.satoshi('5.0')
});
redeemer.addInput(tx, 0);
redeemer.setLocktime(chain.height);
wallet.sign(redeemer);
attempt.addTX(redeemer);
return wallet.sign(redeemer, function(err) {
assert.ifError(err);
attempt.addTX(redeemer);
callback(null, attempt.mineSync());
});
}
callback(null, attempt.mineSync());
});
@ -68,7 +72,10 @@ describe('Chain', function() {
}
it('should open chain and miner', function(cb) {
miner.open(cb);
miner.open(function(err) {
assert.ifError(cb);
wallet.init(cb);
});
});
it('should mine a block', function(cb) {

View File

@ -60,7 +60,7 @@ describe('Wallet', function() {
it('should generate new key and address', function() {
var w = bcoin.wallet();
w.open(function(err) {
w.init(function(err) {
assert.ifError(err);
var addr = w.getAddress();
assert(addr);
@ -139,7 +139,7 @@ describe('Wallet', function() {
m: 1,
n: 2
});
w.open(function(err) {
w.init(function(err) {
assert.ifError(err);
var k2 = bcoin.hd.fromMnemonic().deriveAccount44(0).hdPublicKey;
w.addKey(k2, function(err) {
@ -556,6 +556,43 @@ 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_) {
@ -584,38 +621,42 @@ describe('Wallet', function() {
receive = receive_;
next();
});
},
}
], function(err) {
assert.ifError(err);
getInfo(function(err, a) {
assert.ifError(err);
utils.serial([
w1.addKey.bind(w1, w2),
w1.addKey.bind(w1, w3),
w2.addKey.bind(w2, w1),
w2.addKey.bind(w2, w3),
w3.addKey.bind(w3, w1),
w3.addKey.bind(w3, w2)
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)
], function(err) {
assert.ifError(err);
getInfo(function(err, a) {
assert.ifError(err);
// w3 = bcoin.wallet.fromJSON(w3.toJSON());
// Our p2sh address
var addr = w1.getAddress();
var addr = a.w1.getAddress();
if (witness)
assert(bcoin.address.parseBase58(addr).type === 'witnessscripthash');
else
assert(bcoin.address.parseBase58(addr).type === 'scripthash');
assert.equal(w1.getAddress(), addr);
assert.equal(w2.getAddress(), addr);
assert.equal(w3.getAddress(), addr);
assert.equal(a.w1.getAddress(), addr);
assert.equal(a.w2.getAddress(), addr);
assert.equal(a.w3.getAddress(), addr);
var paddr = w1.getProgramAddress();
assert.equal(w1.getProgramAddress(), paddr);
assert.equal(w2.getProgramAddress(), paddr);
assert.equal(w3.getProgramAddress(), paddr);
var paddr = a.w1.getProgramAddress();
assert.equal(a.w1.getProgramAddress(), paddr);
assert.equal(a.w2.getProgramAddress(), paddr);
assert.equal(a.w3.getProgramAddress(), paddr);
// Add a shared unspent transaction to our wallets
var utx = bcoin.mtx();
@ -631,7 +672,7 @@ describe('Wallet', function() {
utx.ts = 1;
utx.height = 1;
assert.equal(w1.receiveDepth, 1);
assert.equal(a.w1.receiveDepth, 1);
wdb.addTX(utx, function(err) {
assert.ifError(err);
@ -640,18 +681,20 @@ describe('Wallet', function() {
wdb.addTX(utx, function(err) {
assert.ifError(err);
assert.equal(w1.receiveDepth, 2);
assert.equal(w1.changeDepth, 1);
getInfo(function(err, a) {
assert.ifError(err);
assert.equal(a.w1.receiveDepth, 2);
assert.equal(a.w1.changeDepth, 1);
assert(w1.getAddress() !== addr);
addr = w1.getAddress();
assert.equal(w1.getAddress(), addr);
assert.equal(w2.getAddress(), addr);
assert.equal(w3.getAddress(), addr);
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);
// Create a tx requiring 2 signatures
var send = bcoin.mtx();
send.addOutput({ address: receive.getAddress(), value: 5460 });
send.addOutput({ address: a.receive.getAddress(), value: 5460 });
assert(!send.verify(null, true, flags));
w1.fill(send, { rate: 10000, round: true }, function(err) {
assert.ifError(err);
@ -665,11 +708,11 @@ describe('Wallet', function() {
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);
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;
@ -682,32 +725,35 @@ describe('Wallet', function() {
assert.ifError(err);
wdb.addTX(send, function(err) {
assert.ifError(err);
getInfo(function(err, a) {
assert.ifError(err);
assert.equal(w1.receiveDepth, 2);
assert.equal(w1.changeDepth, 2);
assert.equal(a.w1.receiveDepth, 2);
assert.equal(a.w1.changeDepth, 2);
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);
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);
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(w3.receiveDepth, 2);
assert.equal(w3.changeDepth, 2);
//assert.equal(w3.getAddress(), addr);
//assert.equal(w3.changeAddress.getAddress(), change);
// 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);
cb();
cb();
});
});
});
});
@ -715,8 +761,11 @@ describe('Wallet', function() {
});
});
});
});
});
});
});
});
});
});
}