add walletdb.

This commit is contained in:
Christopher Jeffrey 2016-02-20 17:54:51 -08:00
parent 6e2eb039ac
commit b11cdd80af
7 changed files with 321 additions and 128 deletions

View File

@ -62,10 +62,11 @@ bcoin.input = require('./bcoin/input');
bcoin.output = require('./bcoin/output');
bcoin.coin = require('./bcoin/coin');
bcoin.tx = require('./bcoin/tx');
bcoin.txPool = require('./bcoin/tx-pool');
bcoin.txpool = require('./bcoin/tx-pool');
bcoin.block = require('./bcoin/block');
bcoin.ramdisk = require('./bcoin/ramdisk');
bcoin.blockdb = require('./bcoin/blockdb');
bcoin.spvnode = require('./bcoin/spvnode');
bcoin.node = require('./bcoin/node');
bcoin.chainblock = require('./bcoin/chainblock');
bcoin.chaindb = require('./bcoin/chaindb');
@ -73,6 +74,7 @@ bcoin.chain = require('./bcoin/chain');
bcoin.mempool = require('./bcoin/mempool');
bcoin.keypair = require('./bcoin/keypair');
bcoin.address = require('./bcoin/address');
bcoin.walletdb = require('./bcoin/walletdb');
bcoin.wallet = require('./bcoin/wallet');
bcoin.peer = require('./bcoin/peer');
bcoin.pool = require('./bcoin/pool');

View File

@ -74,6 +74,8 @@ Node.prototype._init = function _init() {
this.miner = new bcoin.miner(this.pool, this.options.miner);
this.walletdb = new bcoin.walletdb(this.options.walletdb);
this.mempool.on('error', function(err) {
self.emit('error', err);
});
@ -88,7 +90,10 @@ Node.prototype._init = function _init() {
if (!this.options.wallet.id)
this.options.wallet.id = 'primary';
bcoin.wallet.load(this.options.wallet, function(err, wallet) {
if (!this.options.wallet.passphrase)
this.options.wallet.passphrase = 'node';
this.walletdb.create(this.options.wallet, function(err, wallet) {
if (err)
throw err;

View File

@ -62,6 +62,8 @@ SPVNode.prototype._init = function _init() {
this.pool = new bcoin.pool(this.options.pool);
this.chain = this.pool.chain;
this.walletdb = new bcoin.walletdb(this.options.walletdb);
this.pool.on('error', function(err) {
self.emit('error', err);
});
@ -76,7 +78,10 @@ SPVNode.prototype._init = function _init() {
if (!this.options.wallet.id)
this.options.wallet.id = 'primary';
bcoin.wallet.load(this.options.wallet, function(err, wallet) {
if (!this.options.wallet.passphrase)
this.options.wallet.passphrase = 'node';
this.walletdb.create(this.options.wallet, function(err, wallet) {
if (err)
throw err;
@ -93,11 +98,6 @@ SPVNode.prototype._init = function _init() {
});
});
self.pool.startSync();
self.loading = false;
self.emit('load');
return;
self.pool.addWallet(this.wallet, function(err) {
if (err)
throw err;

View File

@ -212,7 +212,10 @@ TXPool.prototype._storeTX = function _storeTX(hash, tx, noWrite) {
if (noWrite)
return;
this._wallet.saveFile();
this._wallet.save(function(err) {
if (err)
self.emit('error', err);
});
};
TXPool.prototype._removeTX = function _removeTX(tx, noWrite) {
@ -233,7 +236,10 @@ TXPool.prototype._removeTX = function _removeTX(tx, noWrite) {
if (noWrite)
return;
this._wallet.saveFile();
this._wallet.save(function(err) {
if (err)
self.emit('error', err);
});
};
TXPool.prototype.removeTX = function removeTX(hash) {

View File

@ -1463,7 +1463,7 @@ TX.prototype.hasPrevout = function hasPrevout() {
TX.prototype.fillPrevout = function fillPrevout(txs, unspent) {
var inputs;
if (txs instanceof bcoin.txPool) {
if (txs instanceof bcoin.txpool) {
unspent = txs._unspent;
txs = txs._all;
} else if (txs instanceof bcoin.wallet) {

View File

@ -46,6 +46,7 @@ function Wallet(options) {
options.master = bcoin.hd.privateKey();
this.options = options;
this.db = options.db || new bcoin.walletdb({ type: 'file' });
this.addresses = [];
this.master = options.master || null;
this.addressMap = options.addressMap || {};
@ -133,14 +134,7 @@ Wallet.prototype._init = function _init() {
assert(this.changeAddress.change);
this.id = this.getID();
this.file = options.file;
if (!this.file || this.file === true) {
this.file = bcoin.dir + '/wallet-'
+ this.id + '-' + network.type + '.json';
}
this.tx = new bcoin.txPool(this);
this.tx = new bcoin.txpool(this);
// Notify owners about new accepted transactions
this.tx.on('update', function(lastTs, lastHeight, tx) {
@ -175,9 +169,10 @@ Wallet.prototype._init = function _init() {
this.lastTs = this.tx._lastTs;
this.lastHeight = this.tx._lastHeight;
this.saveFile();
this.save(function(err) {
if (err)
throw err;
utils.nextTick(function() {
self.loading = false;
self.emit('load', self.lastTs);
});
@ -915,117 +910,13 @@ Wallet.fromJSON = function fromJSON(json, passphrase) {
return new Wallet(Wallet._fromJSON(json, passphrase));
};
Wallet.prototype.saveFile = function saveFile(callback) {
Wallet.prototype.save = function save(callback) {
callback = utils.asyncify(callback);
if (!this.options.file)
if (!this.options.store && !this.options.db)
return callback();
return this.toFile(this.file, this.options.passphrase, callback);
};
Wallet.prototype.toFile = function toFile(file, callback) {
var json, options;
if (typeof file === 'function') {
callback = file;
file = null;
}
if (!file)
file = this.file;
callback = utils.asyncify(callback);
if (!bcoin.fs)
return callback();
json = JSON.stringify(this.toJSON(this.options.passphrase), null, 2);
options = {
encoding: 'utf8',
mode: 0o600
};
fs.writeFile(file, json, options, function(err) {
if (err)
return callback(err);
return callback(null, file);
});
};
Wallet._fromFile = function _fromFile(file, passphrase, callback) {
if (typeof passphrase === 'function') {
callback = passphrase;
passphrase = null;
}
callback = utils.asyncify(callback);
if (!bcoin.fs)
return callback();
if (!file)
return callback();
fs.readFile(file, 'utf8', function(err, json) {
var options;
if (err && err.code === 'ENOENT')
return callback();
if (err)
return callback(err);
try {
options = Wallet._fromJSON(JSON.parse(json));
} catch (e) {
return callback(e);
}
return callback(null, options);
});
};
Wallet.fromFile = function fromFile(file, passphrase, callback) {
if (typeof passphrase === 'function') {
callback = passphrase;
passphrase = null;
}
return Wallet._fromFile(file, passphrase, function(err, options) {
if (err)
return callback(err);
if (!options)
return callback();
return callback(null, new Wallet(options));
});
};
Wallet.load = function load(options, callback) {
var file;
if (options.id) {
file = bcoin.dir + '/wallet-'
+ options.id + '-' + network.type + '.json';
options.file = true;
}
if (typeof options.file === 'string')
file = options.file;
return Wallet.fromFile(file, options.passphrase, function(err, wallet) {
if (err)
return callback(err);
if (!wallet)
wallet = new Wallet(options);
return callback(null, wallet);
});
return this.db.save(this.id, this, callback);
};
/**

289
lib/bcoin/walletdb.js Normal file
View File

@ -0,0 +1,289 @@
/**
* walletdb.js - storage for wallets
* Copyright (c) 2014-2015, Fedor Indutny (MIT License)
* https://github.com/indutny/bcoin
*/
var inherits = require('inherits');
var EventEmitter = require('events').EventEmitter;
var bcoin = require('../bcoin');
var levelup = require('levelup');
var bn = require('bn.js');
var constants = bcoin.protocol.constants;
var network = bcoin.protocol.network;
var utils = bcoin.utils;
var assert = utils.assert;
var fs = bcoin.fs;
/**
* WalletDB
*/
function WalletDB(options) {
if (!(this instanceof WalletDB))
return new WalletDB(options);
if (WalletDB.global)
return WalletDB.global;
if (!options)
options = {};
EventEmitter.call(this);
this.options = options;
this.file = options.file;
if (!this.file)
this.file = bcoin.dir + '/wallet-' + network.type + '.db';
if (!this.dir)
this.dir = bcoin.dir + '/wallet-' + network.type;
if (!this.type)
this.type = 'file';
WalletDB.global = this;
this._init();
}
inherits(WalletDB, EventEmitter);
WalletDB._db = {};
WalletDB.prototype._init = function _init() {
if (this.type === 'file' && !bcoin.fs) {
this.type = 'leveldb';
utils.debug('`fs` module not available. Falling back to leveldb.');
}
if (this.type === 'file') {
if (bcoin.fs) {
try {
bcoin.fs.statSync(this.dir, 0o750);
} catch (e) {
bcoin.fs.mkdirSync(this.dir);
}
}
if (+process.env.BCOIN_FRESH === 1) {
try {
bcoin.fs.readdirSync(this.dir).forEach(function(file) {
bcoin.fs.unlinkSync(this.dir + '/' + file);
}, this);
} catch (e) {
;
}
}
return;
}
if (this.type === 'leveldb') {
if (!WalletDB._db[this.file]) {
WalletDB._db[this.file] = new levelup(this.file, {
keyEncoding: 'ascii',
valueEncoding: 'json',
createIfMissing: true,
errorIfExists: false,
compression: true,
cacheSize: 1 * 1024 * 1024,
writeBufferSize: 1 * 1024 * 1024,
// blockSize: 4 * 1024,
maxOpenFiles: 1024,
// blockRestartInterval: 16,
db: bcoin.isBrowser
? require('memdown')
: require('level' + 'down')
});
}
this.db = WalletDB._db[this.file];
return;
}
throw new Error('Unknown storage type: ' + this.type);
};
WalletDB.prototype.save = function save(id, json, callback) {
callback = utils.asyncify(callback);
if (this.type === 'leveldb')
return this.saveDB(id, json, callback);
if (this.type === 'file')
return this.saveFile(id, json, callback);
throw new Error('Unknown storage type: ' + this.type);
};
WalletDB.prototype.saveDB = function saveFile(id, json, callback) {
var key;
key = 'w/' + id;
if (json instanceof bcoin.wallet) {
json.store = true;
json.db = this;
json = json.toJSON(this.options.noPool);
}
callback = utils.asyncify(callback);
this.db.put(key, json, callback);
};
WalletDB.prototype.saveFile = function saveFile(id, json, callback) {
var file, options;
file = this.dir + '/' + id + '.json';
if (json instanceof bcoin.wallet) {
json.store = true;
json.db = this;
json = json.toJSON(this.options.noPool);
}
callback = utils.asyncify(callback);
if (!bcoin.fs)
return callback();
json = JSON.stringify(json, null, 2);
options = {
encoding: 'utf8',
mode: 0o600
};
fs.writeFile(file, json, options, function(err) {
if (err)
return callback(err);
return callback(null, file);
});
};
WalletDB.prototype.getJSON = function getJSON(id, passphrase, callback) {
if (typeof passphrase === 'function') {
callback = passphrase;
passphrase = null;
}
callback = utils.asyncify(callback);
if (id instanceof bcoin.wallet) {
id = wallet.id;
json.store = true;
json.db = this;
}
if (this.type === 'leveldb')
return this.getDB(id, passphrase, callback);
if (this.type === 'file')
return this.getFile(id, passphrase, callback);
throw new Error('Unknown storage type: ' + this.type);
};
WalletDB.prototype.getFile = function getFile(id, passphrase, callback) {
var self = this;
var file;
callback = utils.asyncify(callback);
if (!bcoin.fs)
return callback();
if (!id)
return callback();
file = this.dir + '/' + id + '.json';
fs.readFile(file, 'utf8', function(err, json) {
var options;
if (err && err.code === 'ENOENT')
return callback();
if (err)
return callback(err);
try {
options = bcoin.wallet._fromJSON(JSON.parse(json), passphrase);
} catch (e) {
return callback(e);
}
options.store = true;
options.db = self;
return callback(null, options);
});
};
WalletDB.prototype.getDB = function getDB(id, passphrase, callback) {
var self = this;
var key;
callback = utils.asyncify(callback);
key = 'w/' + id;
this.db.get(key, function(err, json) {
var options;
if (err && err.type === 'NotFoundError')
return callback();
if (err)
return callback(err);
try {
options = bcoin.wallet._fromJSON(JSON.parse(json), passphrase);
} catch (e) {
return callback(e);
}
options.store = true;
options.db = self;
return callback(null, options);
});
};
WalletDB.prototype.get = function get(id, passphrase, callback) {
callback = utils.asyncify(callback);
return this.getJSON(id, passphrase, function(err, options) {
if (err)
return callback(err);
if (!options)
return callback();
return callback(null, new bcoin.wallet(options));
});
};
WalletDB.prototype.create = function create(options, callback) {
var self = this;
callback = utils.asyncify(callback);
return this.getJSON(options.id, options.passphrase, function(err, opt) {
if (err)
return callback(err);
if (!opt) {
options.store = true;
options.db = self;
return callback(null, new bcoin.wallet(options));
}
return callback(null, new bcoin.wallet(opt));
});
};
/**
* Expose
*/
module.exports = WalletDB;