add walletdb.
This commit is contained in:
parent
6e2eb039ac
commit
b11cdd80af
@ -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');
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
289
lib/bcoin/walletdb.js
Normal 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;
|
||||
Loading…
Reference in New Issue
Block a user