add spv node. add .bcoin dir. add file storage for wallet.
This commit is contained in:
parent
86a368dddf
commit
6e2eb039ac
10
lib/bcoin.js
10
lib/bcoin.js
@ -20,6 +20,8 @@ if (process.env.BCOIN_DEBUG) {
|
|||||||
bcoin.debug = +bcoin.debug === 1;
|
bcoin.debug = +bcoin.debug === 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bcoin.dir = process.env.HOME + '/.bcoin';
|
||||||
|
|
||||||
if (!bcoin.isBrowser) {
|
if (!bcoin.isBrowser) {
|
||||||
bcoin.fs = require('f' + 's');
|
bcoin.fs = require('f' + 's');
|
||||||
bcoin.crypto = require('cry' + 'pto');
|
bcoin.crypto = require('cry' + 'pto');
|
||||||
@ -27,6 +29,14 @@ if (!bcoin.isBrowser) {
|
|||||||
bcoin.cp = require('child_' + 'process');
|
bcoin.cp = require('child_' + 'process');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bcoin.fs) {
|
||||||
|
try {
|
||||||
|
bcoin.fs.statSync(bcoin.dir, 0o750);
|
||||||
|
} catch (e) {
|
||||||
|
bcoin.fs.mkdirSync(bcoin.dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bcoin.inherits = inherits;
|
bcoin.inherits = inherits;
|
||||||
bcoin.elliptic = elliptic;
|
bcoin.elliptic = elliptic;
|
||||||
bcoin.bn = bn;
|
bcoin.bn = bn;
|
||||||
|
|||||||
@ -31,7 +31,6 @@ function Address(options) {
|
|||||||
options = {};
|
options = {};
|
||||||
|
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.storage = options.storage;
|
|
||||||
this.label = options.label || '';
|
this.label = options.label || '';
|
||||||
this.derived = !!options.derived;
|
this.derived = !!options.derived;
|
||||||
this.addressMap = null;
|
this.addressMap = null;
|
||||||
@ -67,8 +66,6 @@ function Address(options) {
|
|||||||
|
|
||||||
if (options.redeem || options.script)
|
if (options.redeem || options.script)
|
||||||
this.setRedeem(options.redeem || options.script);
|
this.setRedeem(options.redeem || options.script);
|
||||||
|
|
||||||
this.prefix = 'bt/address/' + this.getID() + '/';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inherits(Address, EventEmitter);
|
inherits(Address, EventEmitter);
|
||||||
@ -559,7 +556,7 @@ Address.getType = function getType(addr) {
|
|||||||
return prefix;
|
return prefix;
|
||||||
};
|
};
|
||||||
|
|
||||||
Address.prototype.toJSON = function toJSON(encrypt) {
|
Address.prototype.toJSON = function toJSON(passphrase) {
|
||||||
return {
|
return {
|
||||||
v: 1,
|
v: 1,
|
||||||
name: 'address',
|
name: 'address',
|
||||||
@ -570,7 +567,7 @@ Address.prototype.toJSON = function toJSON(encrypt) {
|
|||||||
index: this.index,
|
index: this.index,
|
||||||
path: this.path,
|
path: this.path,
|
||||||
address: this.getAddress(),
|
address: this.getAddress(),
|
||||||
key: this.key.toJSON(encrypt),
|
key: this.key.toJSON(passphrase),
|
||||||
type: this.type,
|
type: this.type,
|
||||||
redeem: this.redeem ? utils.toHex(this.redeem) : null,
|
redeem: this.redeem ? utils.toHex(this.redeem) : null,
|
||||||
keys: this.keys.map(utils.toBase58),
|
keys: this.keys.map(utils.toBase58),
|
||||||
@ -579,7 +576,7 @@ Address.prototype.toJSON = function toJSON(encrypt) {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
Address.fromJSON = function fromJSON(json, decrypt) {
|
Address.fromJSON = function fromJSON(json, passphrase) {
|
||||||
var w;
|
var w;
|
||||||
|
|
||||||
assert.equal(json.v, 1);
|
assert.equal(json.v, 1);
|
||||||
@ -594,7 +591,7 @@ Address.fromJSON = function fromJSON(json, decrypt) {
|
|||||||
derived: json.derived,
|
derived: json.derived,
|
||||||
index: json.index,
|
index: json.index,
|
||||||
path: json.path,
|
path: json.path,
|
||||||
key: bcoin.keypair.fromJSON(json.key, decrypt),
|
key: bcoin.keypair.fromJSON(json.key, passphrase),
|
||||||
type: json.type,
|
type: json.type,
|
||||||
redeem: json.redeem ? utils.toArray(json.redeem, 'hex') : null,
|
redeem: json.redeem ? utils.toArray(json.redeem, 'hex') : null,
|
||||||
keys: json.keys.map(utils.fromBase58),
|
keys: json.keys.map(utils.fromBase58),
|
||||||
|
|||||||
@ -31,7 +31,7 @@ function BlockDB(options) {
|
|||||||
this.file = options.indexFile;
|
this.file = options.indexFile;
|
||||||
|
|
||||||
if (!this.file)
|
if (!this.file)
|
||||||
this.file = process.env.HOME + '/bcoin-index-' + network.type + '.db';
|
this.file = bcoin.dir + '/index-' + network.type + '.db';
|
||||||
|
|
||||||
this.options = options;
|
this.options = options;
|
||||||
|
|
||||||
@ -1095,7 +1095,7 @@ function BlockData(options) {
|
|||||||
this.file = options.blockFile;
|
this.file = options.blockFile;
|
||||||
|
|
||||||
if (!this.file)
|
if (!this.file)
|
||||||
this.file = process.env.HOME + '/bcoin-block-' + network.type + '.db';
|
this.file = bcoin.dir + '/block-' + network.type + '.db';
|
||||||
|
|
||||||
this.bufferPool = { used: {} };
|
this.bufferPool = { used: {} };
|
||||||
this.size = 0;
|
this.size = 0;
|
||||||
|
|||||||
@ -752,10 +752,14 @@ Chain.prototype._addEntry = function _addEntry(entry, block, callback) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Chain.prototype.resetHeight = function resetHeight(height, force) {
|
Chain.prototype.resetHeight = function resetHeight(height, force) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
if (height === this.db.getSize() - 1)
|
if (height === this.db.getSize() - 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.db.resetHeightSync(height);
|
this.db.resetHeightSync(height, function(entry) {
|
||||||
|
self.emit('remove entry', entry);
|
||||||
|
});
|
||||||
|
|
||||||
// Reset the orphan map completely. There may
|
// Reset the orphan map completely. There may
|
||||||
// have been some orphans on a forked chain we
|
// have been some orphans on a forked chain we
|
||||||
@ -791,6 +795,8 @@ Chain.prototype.resetHeightAsync = function resetHeightAsync(height, callback, f
|
|||||||
self.purgePending();
|
self.purgePending();
|
||||||
|
|
||||||
return done();
|
return done();
|
||||||
|
}, function(entry) {
|
||||||
|
self.emit('remove entry', entry);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -35,7 +35,7 @@ function ChainDB(chain, options) {
|
|||||||
this.file = options.file;
|
this.file = options.file;
|
||||||
|
|
||||||
if (!this.file)
|
if (!this.file)
|
||||||
this.file = process.env.HOME + '/bcoin-chain-' + network.type + '.db';
|
this.file = bcoin.dir + '/chain-' + network.type + '.db';
|
||||||
|
|
||||||
this.heightLookup = {};
|
this.heightLookup = {};
|
||||||
this.queue = {};
|
this.queue = {};
|
||||||
@ -426,7 +426,7 @@ ChainDB.prototype.saveAsync = function saveAsync(entry, callback) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
ChainDB.prototype.resetHeightSync = function resetHeightSync(height) {
|
ChainDB.prototype.resetHeightSync = function resetHeightSync(height, emit) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var osize = this.size;
|
var osize = this.size;
|
||||||
var ohighest = this.highest;
|
var ohighest = this.highest;
|
||||||
@ -453,6 +453,10 @@ ChainDB.prototype.resetHeightSync = function resetHeightSync(height) {
|
|||||||
|
|
||||||
assert(existing);
|
assert(existing);
|
||||||
|
|
||||||
|
// Emit the blocks we remove.
|
||||||
|
if (emit)
|
||||||
|
emit(existing);
|
||||||
|
|
||||||
// Warn of potential race condition
|
// Warn of potential race condition
|
||||||
// (handled with _onFlush).
|
// (handled with _onFlush).
|
||||||
if (this.queue[i])
|
if (this.queue[i])
|
||||||
@ -489,7 +493,7 @@ ChainDB.prototype.resetHeightSync = function resetHeightSync(height) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
ChainDB.prototype.resetHeightAsync = function resetHeightAsync(height, callback) {
|
ChainDB.prototype.resetHeightAsync = function resetHeightAsync(height, callback, emit) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var osize = this.size;
|
var osize = this.size;
|
||||||
var ohighest = this.highest;
|
var ohighest = this.highest;
|
||||||
@ -538,6 +542,10 @@ ChainDB.prototype.resetHeightAsync = function resetHeightAsync(height, callback)
|
|||||||
|
|
||||||
assert(existing);
|
assert(existing);
|
||||||
|
|
||||||
|
// Emit the blocks we remove.
|
||||||
|
if (emit)
|
||||||
|
emit(existing);
|
||||||
|
|
||||||
// Warn of potential race condition
|
// Warn of potential race condition
|
||||||
// (handled with _onFlush).
|
// (handled with _onFlush).
|
||||||
if (self.queue[i])
|
if (self.queue[i])
|
||||||
|
|||||||
@ -837,25 +837,25 @@ HDPrivateKey.prototype.deriveString = function deriveString(path) {
|
|||||||
}, this);
|
}, this);
|
||||||
};
|
};
|
||||||
|
|
||||||
HDPrivateKey.prototype.toJSON = function toJSON(encrypt) {
|
HDPrivateKey.prototype.toJSON = function toJSON(passphrase) {
|
||||||
var json = {
|
var json = {
|
||||||
v: 1,
|
v: 1,
|
||||||
name: 'hdkey',
|
name: 'hdkey',
|
||||||
encrypted: encrypt ? true : false
|
encrypted: passphrase ? true : false
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this instanceof HDPrivateKey) {
|
if (this instanceof HDPrivateKey) {
|
||||||
if (this.seed) {
|
if (this.seed) {
|
||||||
json.mnemonic = encrypt
|
json.mnemonic = passphrase
|
||||||
? encrypt(this.seed.mnemonic)
|
? utils.encrypt(this.seed.mnemonic, passphrase)
|
||||||
: this.seed.mnemonic;
|
: this.seed.mnemonic;
|
||||||
json.passphrase = encrypt
|
json.passphrase = passphrase
|
||||||
? encrypt(this.seed.passphrase)
|
? utils.encrypt(this.seed.passphrase, passphrase)
|
||||||
: this.seed.passphrase;
|
: this.seed.passphrase;
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
json.xprivkey = encrypt
|
json.xprivkey = passphrase
|
||||||
? encrypt(this.xprivkey)
|
? utils.encrypt(this.xprivkey, passphrase)
|
||||||
: this.xprivkey;
|
: this.xprivkey;
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
@ -865,21 +865,21 @@ HDPrivateKey.prototype.toJSON = function toJSON(encrypt) {
|
|||||||
return json;
|
return json;
|
||||||
};
|
};
|
||||||
|
|
||||||
HDPrivateKey.fromJSON = function fromJSON(json, decrypt) {
|
HDPrivateKey.fromJSON = function fromJSON(json, passphrase) {
|
||||||
assert.equal(json.v, 1);
|
assert.equal(json.v, 1);
|
||||||
assert.equal(json.name, 'hdkey');
|
assert.equal(json.name, 'hdkey');
|
||||||
|
|
||||||
if (json.encrypted && !decrypt)
|
if (json.encrypted && !passphrase)
|
||||||
throw new Error('Cannot decrypt address');
|
throw new Error('Cannot decrypt address');
|
||||||
|
|
||||||
if (json.mnemonic) {
|
if (json.mnemonic) {
|
||||||
return new HDPrivateKey({
|
return new HDPrivateKey({
|
||||||
seed: new HDSeed({
|
seed: new HDSeed({
|
||||||
mnemonic: json.encrypted
|
mnemonic: json.encrypted
|
||||||
? decrypt(json.mnemonic)
|
? utils.decrypt(json.mnemonic, passphrase)
|
||||||
: json.mnemonic,
|
: json.mnemonic,
|
||||||
passphrase: json.encrypted
|
passphrase: json.encrypted
|
||||||
? decrypt(json.passphrase)
|
? utils.decrypt(json.passphrase, passphrase)
|
||||||
: json.passphrase
|
: json.passphrase
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@ -888,7 +888,7 @@ HDPrivateKey.fromJSON = function fromJSON(json, decrypt) {
|
|||||||
if (json.xprivkey) {
|
if (json.xprivkey) {
|
||||||
return new HDPrivateKey({
|
return new HDPrivateKey({
|
||||||
xkey: json.encrypted
|
xkey: json.encrypted
|
||||||
? decrypt(json.xprivkey)
|
? utils.decrypt(json.xprivkey, passphrase)
|
||||||
: json.xprivkey
|
: json.xprivkey
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -195,16 +195,16 @@ KeyPair.sign = function sign(msg, key) {
|
|||||||
return bcoin.ecdsa.sign(msg, key.priv);
|
return bcoin.ecdsa.sign(msg, key.priv);
|
||||||
};
|
};
|
||||||
|
|
||||||
KeyPair.prototype.toJSON = function toJSON(encrypt) {
|
KeyPair.prototype.toJSON = function toJSON(passphrase) {
|
||||||
var json = {
|
var json = {
|
||||||
v: 1,
|
v: 1,
|
||||||
name: 'keypair',
|
name: 'keypair',
|
||||||
encrypted: encrypt ? true : false
|
encrypted: passphrase ? true : false
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.pair.priv) {
|
if (this.pair.priv) {
|
||||||
json.privateKey = encrypt
|
json.privateKey = passphrase
|
||||||
? encrypt(this.toSecret())
|
? utils.encrypt(this.toSecret(), passphrase)
|
||||||
: this.toSecret();
|
: this.toSecret();
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
@ -214,19 +214,19 @@ KeyPair.prototype.toJSON = function toJSON(encrypt) {
|
|||||||
return json;
|
return json;
|
||||||
};
|
};
|
||||||
|
|
||||||
KeyPair.fromJSON = function fromJSON(json, decrypt) {
|
KeyPair.fromJSON = function fromJSON(json, passphrase) {
|
||||||
var privateKey, publicKey, compressed;
|
var privateKey, publicKey, compressed;
|
||||||
|
|
||||||
assert.equal(json.v, 1);
|
assert.equal(json.v, 1);
|
||||||
assert.equal(json.name, 'keypair');
|
assert.equal(json.name, 'keypair');
|
||||||
|
|
||||||
if (json.encrypted && !decrypt)
|
if (json.encrypted && !passphrase)
|
||||||
throw new Error('Cannot decrypt address');
|
throw new Error('Cannot decrypt address');
|
||||||
|
|
||||||
if (json.privateKey) {
|
if (json.privateKey) {
|
||||||
privateKey = json.privateKey;
|
privateKey = json.privateKey;
|
||||||
if (json.encrypted)
|
if (json.encrypted)
|
||||||
privateKey = decrypt(privateKey);
|
privateKey = utils.decrypt(privateKey, passphrase);
|
||||||
return KeyPair.fromSecret(privateKey);
|
return KeyPair.fromSecret(privateKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -40,6 +40,9 @@ function Node(options) {
|
|||||||
this.pool = null;
|
this.pool = null;
|
||||||
this.chain = null;
|
this.chain = null;
|
||||||
this.miner = null;
|
this.miner = null;
|
||||||
|
this.wallet = null;
|
||||||
|
|
||||||
|
this.loading = false;
|
||||||
|
|
||||||
Node.global = this;
|
Node.global = this;
|
||||||
|
|
||||||
@ -51,9 +54,14 @@ inherits(Node, EventEmitter);
|
|||||||
Node.prototype._init = function _init() {
|
Node.prototype._init = function _init() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
if (!this.options.pool)
|
if (!this.options.pool)
|
||||||
this.options.pool = {};
|
this.options.pool = {};
|
||||||
|
|
||||||
|
if (!this.options.miner)
|
||||||
|
this.options.miner = {};
|
||||||
|
|
||||||
this.blockdb = new bcoin.blockdb(this.options.blockdb);
|
this.blockdb = new bcoin.blockdb(this.options.blockdb);
|
||||||
this.mempool = new bcoin.mempool(this, this.options.mempool);
|
this.mempool = new bcoin.mempool(this, this.options.mempool);
|
||||||
|
|
||||||
@ -63,6 +71,7 @@ Node.prototype._init = function _init() {
|
|||||||
|
|
||||||
this.pool = new bcoin.pool(this.options.pool);
|
this.pool = new bcoin.pool(this.options.pool);
|
||||||
this.chain = this.pool.chain;
|
this.chain = this.pool.chain;
|
||||||
|
|
||||||
this.miner = new bcoin.miner(this.pool, this.options.miner);
|
this.miner = new bcoin.miner(this.pool, this.options.miner);
|
||||||
|
|
||||||
this.mempool.on('error', function(err) {
|
this.mempool.on('error', function(err) {
|
||||||
@ -73,7 +82,51 @@ Node.prototype._init = function _init() {
|
|||||||
self.emit('error', err);
|
self.emit('error', err);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.pool.startSync();
|
if (!this.options.wallet)
|
||||||
|
this.options.wallet = {};
|
||||||
|
|
||||||
|
if (!this.options.wallet.id)
|
||||||
|
this.options.wallet.id = 'primary';
|
||||||
|
|
||||||
|
bcoin.wallet.load(this.options.wallet, function(err, wallet) {
|
||||||
|
if (err)
|
||||||
|
throw err;
|
||||||
|
|
||||||
|
self.wallet = wallet;
|
||||||
|
|
||||||
|
utils.debug('Loaded wallet with id=%s address=%s',
|
||||||
|
wallet.getID(), wallet.getAddress());
|
||||||
|
|
||||||
|
self.chain.on('block', function(block) {
|
||||||
|
block.txs.forEach(function(tx) {
|
||||||
|
self.wallet.addTX(tx);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
self.mempool.on('tx', function(tx) {
|
||||||
|
self.wallet.addTX(tx);
|
||||||
|
});
|
||||||
|
|
||||||
|
self.miner.address = self.wallet.getAddress();
|
||||||
|
|
||||||
|
// Handle forks
|
||||||
|
self.chain.on('remove entry', function(entry) {
|
||||||
|
self.wallet.tx.getAll().forEach(function(tx) {
|
||||||
|
if (tx.block === entry.hash || tx.height >= entry.height)
|
||||||
|
self.wallet.tx.unconfirm(tx);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
self.pool.addWallet(self.wallet, function(err) {
|
||||||
|
if (err)
|
||||||
|
throw err;
|
||||||
|
|
||||||
|
self.pool.startSync();
|
||||||
|
|
||||||
|
self.loading = false;
|
||||||
|
self.emit('load');
|
||||||
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Node.prototype.getCoin = function getCoin(hash, index, callback) {
|
Node.prototype.getCoin = function getCoin(hash, index, callback) {
|
||||||
|
|||||||
@ -1185,11 +1185,11 @@ Pool.prototype.isWatched = function(tx, bloom) {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
Pool.prototype.addWallet = function addWallet(wallet) {
|
Pool.prototype.addWallet = function addWallet(wallet, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
if (this.loading)
|
if (this.loading)
|
||||||
return this.once('load', this.addWallet.bind(this, wallet));
|
return this.once('load', this.addWallet.bind(this, wallet, callback));
|
||||||
|
|
||||||
if (this.wallets.indexOf(wallet) !== -1)
|
if (this.wallets.indexOf(wallet) !== -1)
|
||||||
return false;
|
return false;
|
||||||
@ -1206,16 +1206,16 @@ Pool.prototype.addWallet = function addWallet(wallet) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!self.options.spv)
|
if (!self.options.spv)
|
||||||
return;
|
return callback();
|
||||||
|
|
||||||
if (self._pendingSearch)
|
if (self._pendingSearch)
|
||||||
return;
|
return callback();
|
||||||
|
|
||||||
self._pendingSearch = true;
|
self._pendingSearch = true;
|
||||||
|
|
||||||
utils.nextTick(function() {
|
utils.nextTick(function() {
|
||||||
self._pendingSearch = false;
|
self._pendingSearch = false;
|
||||||
self.searchWallet();
|
self.searchWallet(callback);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1288,7 +1288,7 @@ Pool.prototype.unwatchWallet = function unwatchWallet(wallet) {
|
|||||||
delete wallet._poolOnRemove;
|
delete wallet._poolOnRemove;
|
||||||
};
|
};
|
||||||
|
|
||||||
Pool.prototype.searchWallet = function(ts, height) {
|
Pool.prototype.searchWallet = function(ts, height, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var wallet;
|
var wallet;
|
||||||
|
|
||||||
@ -1297,7 +1297,8 @@ Pool.prototype.searchWallet = function(ts, height) {
|
|||||||
if (!this.options.spv)
|
if (!this.options.spv)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (ts == null) {
|
if (ts == null || typeof ts === 'function') {
|
||||||
|
callback = ts;
|
||||||
height = this.wallets.reduce(function(height, wallet) {
|
height = this.wallets.reduce(function(height, wallet) {
|
||||||
if (wallet.lastHeight < height)
|
if (wallet.lastHeight < height)
|
||||||
return wallet.lastHeight;
|
return wallet.lastHeight;
|
||||||
@ -1311,6 +1312,7 @@ Pool.prototype.searchWallet = function(ts, height) {
|
|||||||
}, Infinity);
|
}, Infinity);
|
||||||
assert(ts !== Infinity);
|
assert(ts !== Infinity);
|
||||||
} else if (typeof ts !== 'number') {
|
} else if (typeof ts !== 'number') {
|
||||||
|
callback = height;
|
||||||
wallet = ts;
|
wallet = ts;
|
||||||
if (wallet.loading) {
|
if (wallet.loading) {
|
||||||
wallet.once('load', function() {
|
wallet.once('load', function() {
|
||||||
@ -1322,6 +1324,8 @@ Pool.prototype.searchWallet = function(ts, height) {
|
|||||||
height = wallet.lastHeight;
|
height = wallet.lastHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
callback = utils.asyncify(callback);
|
||||||
|
|
||||||
// Always prefer height
|
// Always prefer height
|
||||||
if (height > 0) {
|
if (height > 0) {
|
||||||
// Back one week
|
// Back one week
|
||||||
@ -1329,8 +1333,10 @@ Pool.prototype.searchWallet = function(ts, height) {
|
|||||||
height = this.chain.height - (7 * 24 * 6);
|
height = this.chain.height - (7 * 24 * 6);
|
||||||
|
|
||||||
this.chain.resetHeightAsync(height, function(err) {
|
this.chain.resetHeightAsync(height, function(err) {
|
||||||
if (err)
|
if (err) {
|
||||||
throw err;
|
utils.debug('Failed to reset height: %s', err.stack + '');
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
utils.debug('Wallet height: %s', height);
|
utils.debug('Wallet height: %s', height);
|
||||||
utils.debug(
|
utils.debug(
|
||||||
@ -1338,6 +1344,8 @@ Pool.prototype.searchWallet = function(ts, height) {
|
|||||||
self.chain.height,
|
self.chain.height,
|
||||||
new Date(self.chain.tip.ts * 1000)
|
new Date(self.chain.tip.ts * 1000)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
callback();
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -1347,8 +1355,10 @@ Pool.prototype.searchWallet = function(ts, height) {
|
|||||||
ts = utils.now() - 7 * 24 * 3600;
|
ts = utils.now() - 7 * 24 * 3600;
|
||||||
|
|
||||||
this.chain.resetTimeAsync(ts, function(err) {
|
this.chain.resetTimeAsync(ts, function(err) {
|
||||||
if (err)
|
if (err) {
|
||||||
throw err;
|
utils.debug('Failed to reset time: %s', err.stack + '');
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
utils.debug('Wallet time: %s', new Date(ts * 1000));
|
utils.debug('Wallet time: %s', new Date(ts * 1000));
|
||||||
utils.debug(
|
utils.debug(
|
||||||
@ -1356,6 +1366,8 @@ Pool.prototype.searchWallet = function(ts, height) {
|
|||||||
self.chain.height,
|
self.chain.height,
|
||||||
new Date(self.chain.tip.ts * 1000)
|
new Date(self.chain.tip.ts * 1000)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
callback();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
117
lib/bcoin/spvnode.js
Normal file
117
lib/bcoin/spvnode.js
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
/**
|
||||||
|
* spvnode.js - spv node for bcoin
|
||||||
|
* 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 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SPVNode
|
||||||
|
*/
|
||||||
|
|
||||||
|
function SPVNode(options) {
|
||||||
|
if (!(this instanceof SPVNode))
|
||||||
|
return new SPVNode(options);
|
||||||
|
|
||||||
|
EventEmitter.call(this);
|
||||||
|
|
||||||
|
if (!options)
|
||||||
|
options = {};
|
||||||
|
|
||||||
|
this.options = options;
|
||||||
|
|
||||||
|
if (this.options.debug)
|
||||||
|
bcoin.debug = this.options.debug;
|
||||||
|
|
||||||
|
if (this.options.network)
|
||||||
|
network.set(this.options.network);
|
||||||
|
|
||||||
|
this.pool = null;
|
||||||
|
this.chain = null;
|
||||||
|
this.wallet = null;
|
||||||
|
|
||||||
|
this.loading = false;
|
||||||
|
|
||||||
|
SPVNode.global = this;
|
||||||
|
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
inherits(SPVNode, EventEmitter);
|
||||||
|
|
||||||
|
SPVNode.prototype._init = function _init() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
if (!this.options.pool)
|
||||||
|
this.options.pool = {};
|
||||||
|
|
||||||
|
this.options.pool.spv = true;
|
||||||
|
this.options.pool.preload = this.options.pool.preload !== false;
|
||||||
|
|
||||||
|
this.pool = new bcoin.pool(this.options.pool);
|
||||||
|
this.chain = this.pool.chain;
|
||||||
|
|
||||||
|
this.pool.on('error', function(err) {
|
||||||
|
self.emit('error', err);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.pool.on('tx', function(tx) {
|
||||||
|
self.wallet.addTX(tx);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!this.options.wallet)
|
||||||
|
this.options.wallet = {};
|
||||||
|
|
||||||
|
if (!this.options.wallet.id)
|
||||||
|
this.options.wallet.id = 'primary';
|
||||||
|
|
||||||
|
bcoin.wallet.load(this.options.wallet, function(err, wallet) {
|
||||||
|
if (err)
|
||||||
|
throw err;
|
||||||
|
|
||||||
|
self.wallet = wallet;
|
||||||
|
|
||||||
|
utils.debug('Loaded wallet with id=%s address=%s',
|
||||||
|
wallet.getID(), wallet.getAddress());
|
||||||
|
|
||||||
|
// Handle forks
|
||||||
|
self.chain.on('remove entry', function(entry) {
|
||||||
|
self.wallet.tx.getAll().forEach(function(tx) {
|
||||||
|
if (tx.block === entry.hash || tx.height >= entry.height)
|
||||||
|
self.wallet.tx.unconfirm(tx);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
self.pool.startSync();
|
||||||
|
|
||||||
|
self.loading = false;
|
||||||
|
self.emit('load');
|
||||||
|
return;
|
||||||
|
self.pool.addWallet(this.wallet, function(err) {
|
||||||
|
if (err)
|
||||||
|
throw err;
|
||||||
|
|
||||||
|
self.pool.startSync();
|
||||||
|
|
||||||
|
self.loading = false;
|
||||||
|
self.emit('load');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expose
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = SPVNode;
|
||||||
@ -15,17 +15,15 @@ var EventEmitter = require('events').EventEmitter;
|
|||||||
* TXPool
|
* TXPool
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function TXPool(wallet) {
|
function TXPool(wallet, txs) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
if (!(this instanceof TXPool))
|
if (!(this instanceof TXPool))
|
||||||
return new TXPool(wallet);
|
return new TXPool(wallet, txs);
|
||||||
|
|
||||||
EventEmitter.call(this);
|
EventEmitter.call(this);
|
||||||
|
|
||||||
this._wallet = wallet;
|
this._wallet = wallet;
|
||||||
this._storage = wallet.storage;
|
|
||||||
this._prefix = wallet.prefix + 'tx/';
|
|
||||||
this._all = {};
|
this._all = {};
|
||||||
this._unspent = {};
|
this._unspent = {};
|
||||||
this._orphans = {};
|
this._orphans = {};
|
||||||
@ -37,44 +35,29 @@ function TXPool(wallet) {
|
|||||||
this._received = new bn(0);
|
this._received = new bn(0);
|
||||||
this._balance = new bn(0);
|
this._balance = new bn(0);
|
||||||
|
|
||||||
// Load TXs from storage
|
this._init(txs);
|
||||||
this._init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inherits(TXPool, EventEmitter);
|
inherits(TXPool, EventEmitter);
|
||||||
|
|
||||||
TXPool.prototype._init = function init() {
|
TXPool.prototype._init = function _init(txs) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
if (!this._storage) {
|
if (!txs)
|
||||||
utils.nextTick(function() {
|
|
||||||
self._loaded = true;
|
|
||||||
self.emit('load', self._lastTs, self._lastHeight);
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
var s = this._storage.createReadStream({
|
utils.nextTick(function() {
|
||||||
keys: false,
|
self.populate(txs);
|
||||||
start: this._prefix,
|
|
||||||
end: this._prefix + 'z'
|
|
||||||
});
|
|
||||||
|
|
||||||
s.on('data', function(data) {
|
|
||||||
self.add(bcoin.tx.fromJSON(data), true);
|
|
||||||
});
|
|
||||||
|
|
||||||
s.on('error', function(err) {
|
|
||||||
self.emit('error', err);
|
|
||||||
});
|
|
||||||
|
|
||||||
s.on('end', function() {
|
|
||||||
self._loaded = true;
|
|
||||||
self.emit('load', self._lastTs, self._lastHeight);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
TXPool.prototype.add = function add(tx, noWrite, strict) {
|
TXPool.prototype.populate = function populate(txs) {
|
||||||
|
txs.forEach(function(tx) {
|
||||||
|
this.add(tx, true);
|
||||||
|
}, this);
|
||||||
|
};
|
||||||
|
|
||||||
|
TXPool.prototype.add = function add(tx, noWrite) {
|
||||||
var hash = tx.hash('hex');
|
var hash = tx.hash('hex');
|
||||||
var updated = false;
|
var updated = false;
|
||||||
var i, j, input, output, coin, unspent, index, orphan;
|
var i, j, input, output, coin, unspent, index, orphan;
|
||||||
@ -103,7 +86,7 @@ TXPool.prototype.add = function add(tx, noWrite, strict) {
|
|||||||
var key = hash + '/' + i;
|
var key = hash + '/' + i;
|
||||||
if (this._unspent[key])
|
if (this._unspent[key])
|
||||||
this._unspent[key].height = tx.height;
|
this._unspent[key].height = tx.height;
|
||||||
});
|
}, this);
|
||||||
this._storeTX(hash, tx, noWrite);
|
this._storeTX(hash, tx, noWrite);
|
||||||
this._lastTs = Math.max(tx.ts, this._lastTs);
|
this._lastTs = Math.max(tx.ts, this._lastTs);
|
||||||
this._lastHeight = Math.max(tx.height, this._lastHeight);
|
this._lastHeight = Math.max(tx.height, this._lastHeight);
|
||||||
@ -226,21 +209,18 @@ TXPool.prototype.getCoin = function getCoin(hash, index) {
|
|||||||
TXPool.prototype._storeTX = function _storeTX(hash, tx, noWrite) {
|
TXPool.prototype._storeTX = function _storeTX(hash, tx, noWrite) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
if (!this._storage || noWrite)
|
if (noWrite)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._storage.put(this._prefix + hash, tx.toJSON(), function(err) {
|
this._wallet.saveFile();
|
||||||
if (err)
|
|
||||||
self.emit('error', err);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
TXPool.prototype._removeTX = function _removeTX(tx, noWrite) {
|
TXPool.prototype._removeTX = function _removeTX(tx, noWrite) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var hash = tx.hash('hex');
|
var hash = tx.hash('hex');
|
||||||
var key;
|
var key, i;
|
||||||
|
|
||||||
for (var i = 0; i < tx.outputs.length; i++) {
|
for (i = 0; i < tx.outputs.length; i++) {
|
||||||
key = hash + '/' + i;
|
key = hash + '/' + i;
|
||||||
if (this._unspent[key]) {
|
if (this._unspent[key]) {
|
||||||
delete this._unspent[key];
|
delete this._unspent[key];
|
||||||
@ -250,13 +230,73 @@ TXPool.prototype._removeTX = function _removeTX(tx, noWrite) {
|
|||||||
|
|
||||||
// delete this._all[hash];
|
// delete this._all[hash];
|
||||||
|
|
||||||
if (!this._storage || noWrite)
|
if (noWrite)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._storage.del(this._prefix + tx.hash('hex'), function(err) {
|
this._wallet.saveFile();
|
||||||
if (err)
|
};
|
||||||
self.emit('error', err);
|
|
||||||
});
|
TXPool.prototype.removeTX = function removeTX(hash) {
|
||||||
|
var tx, input, prev, updated;
|
||||||
|
|
||||||
|
if (hash.hash)
|
||||||
|
hash = hash('hex');
|
||||||
|
|
||||||
|
tx = this._all[hash];
|
||||||
|
|
||||||
|
if (!tx)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
this._removeTX(tx, false);
|
||||||
|
|
||||||
|
delete this._all[hash];
|
||||||
|
|
||||||
|
for (i = 0; i < tx.inputs.length; i++) {
|
||||||
|
input = tx.inputs[i];
|
||||||
|
if (!input.output || !this._wallet.ownOutput(input.output))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
this._removeInput(input);
|
||||||
|
|
||||||
|
this._unspent[key] = input.output;
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updated)
|
||||||
|
this.emit('update', this._lastTs, this._lastHeight);
|
||||||
|
};
|
||||||
|
|
||||||
|
TXPool.prototype.unconfirm = function unconfirm(hash) {
|
||||||
|
var tx;
|
||||||
|
|
||||||
|
if (hash.hash)
|
||||||
|
hash = hash('hex');
|
||||||
|
|
||||||
|
tx = this._all[hash];
|
||||||
|
|
||||||
|
if (!tx)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (this._lastHeight >= tx.height)
|
||||||
|
this._lastHeight = tx.height;
|
||||||
|
|
||||||
|
if (this._lastTs >= tx.ts)
|
||||||
|
this._lastTs = tx.ts;
|
||||||
|
|
||||||
|
tx.ps = utils.now();
|
||||||
|
tx.ts = 0;
|
||||||
|
tx.block = null;
|
||||||
|
tx.height = -1;
|
||||||
|
tx.outputs.forEach(function(output, i) {
|
||||||
|
var key = hash + '/' + i;
|
||||||
|
if (this._unspent[key])
|
||||||
|
this._unspent[key].height = -1;
|
||||||
|
}, this);
|
||||||
|
this._storeTX(hash, tx, noWrite);
|
||||||
|
this._lastTs = Math.max(tx.ts, this._lastTs);
|
||||||
|
this._lastHeight = Math.max(tx.height, this._lastHeight);
|
||||||
|
this.emit('update', this._lastTs, this._lastHeight, tx);
|
||||||
|
this.emit('unconfirmed', tx);
|
||||||
};
|
};
|
||||||
|
|
||||||
TXPool.prototype._addOutput = function _addOutput(tx, i, remove) {
|
TXPool.prototype._addOutput = function _addOutput(tx, i, remove) {
|
||||||
@ -426,42 +466,6 @@ TXPool.prototype.unspent = TXPool.prototype.getUnspent;
|
|||||||
TXPool.prototype.pending = TXPool.prototype.getPending;
|
TXPool.prototype.pending = TXPool.prototype.getPending;
|
||||||
TXPool.prototype.balance = TXPool.prototype.getBalance;
|
TXPool.prototype.balance = TXPool.prototype.getBalance;
|
||||||
|
|
||||||
TXPool.prototype.toJSON = function toJSON() {
|
|
||||||
return {
|
|
||||||
v: 1,
|
|
||||||
type: 'tx-pool',
|
|
||||||
txs: Object.keys(this._all).map(function(hash) {
|
|
||||||
return this._all[hash].toJSON();
|
|
||||||
}, this)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
TXPool.prototype.fromJSON = function fromJSON(json) {
|
|
||||||
assert.equal(json.v, 1);
|
|
||||||
assert.equal(json.type, 'tx-pool');
|
|
||||||
|
|
||||||
json.txs.forEach(function(tx) {
|
|
||||||
this.add(bcoin.tx.fromJSON(tx));
|
|
||||||
}, this);
|
|
||||||
};
|
|
||||||
|
|
||||||
TXPool.fromJSON = function fromJSON(wallet, json) {
|
|
||||||
var txPool;
|
|
||||||
|
|
||||||
assert.equal(json.v, 1);
|
|
||||||
assert.equal(json.type, 'tx-pool');
|
|
||||||
|
|
||||||
txPool = new TXPool(wallet);
|
|
||||||
|
|
||||||
utils.nextTick(function() {
|
|
||||||
json.txs.forEach(function(tx) {
|
|
||||||
txPool.add(bcoin.tx.fromJSON(tx));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return txPool;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expose
|
* Expose
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -217,6 +217,57 @@ utils.sha512hmac = function sha512hmac(data, salt) {
|
|||||||
return utils.toArray(result);
|
return utils.toArray(result);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
utils.salt = 'bcoin:';
|
||||||
|
|
||||||
|
utils.encrypt = function encrypt(data, passphrase) {
|
||||||
|
var cipher, out;
|
||||||
|
|
||||||
|
if (!bcoin.crypto)
|
||||||
|
return data;
|
||||||
|
|
||||||
|
if (data[0] === ':')
|
||||||
|
return data;
|
||||||
|
|
||||||
|
if (!passphrase)
|
||||||
|
throw new Error('No passphrase.');
|
||||||
|
|
||||||
|
cipher = bcoin.crypto.createCipher('aes-256-cbc', passphrase);
|
||||||
|
|
||||||
|
out = '';
|
||||||
|
out += cipher.update(utils.salt + data, 'utf8', 'hex');
|
||||||
|
out += cipher.final('hex');
|
||||||
|
|
||||||
|
return ':' + out;
|
||||||
|
};
|
||||||
|
|
||||||
|
utils.decrypt = function decrypt(data, passphrase) {
|
||||||
|
var decipher, out;
|
||||||
|
|
||||||
|
if (!bcoin.crypto)
|
||||||
|
return data;
|
||||||
|
|
||||||
|
if (data[0] !== ':')
|
||||||
|
return data;
|
||||||
|
|
||||||
|
if (!passphrase)
|
||||||
|
throw new Error('No passphrase.');
|
||||||
|
|
||||||
|
data = data.substring(1);
|
||||||
|
|
||||||
|
decipher = bcoin.crypto.createDecipher('aes-256-cbc', passphrase);
|
||||||
|
|
||||||
|
out = '';
|
||||||
|
out += decipher.update(data, 'hex', 'utf8');
|
||||||
|
out += decipher.final('utf8');
|
||||||
|
|
||||||
|
if (out.indexOf(utils.salt) !== 0)
|
||||||
|
throw new Error('Decrypt failed.');
|
||||||
|
|
||||||
|
out = out.substring(utils.salt.length);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
utils.writeAscii = function writeAscii(dst, str, off) {
|
utils.writeAscii = function writeAscii(dst, str, off) {
|
||||||
var i = 0;
|
var i = 0;
|
||||||
var c;
|
var c;
|
||||||
|
|||||||
@ -13,12 +13,14 @@ var utils = bcoin.utils;
|
|||||||
var assert = utils.assert;
|
var assert = utils.assert;
|
||||||
var constants = bcoin.protocol.constants;
|
var constants = bcoin.protocol.constants;
|
||||||
var network = bcoin.protocol.network;
|
var network = bcoin.protocol.network;
|
||||||
|
var fs = bcoin.fs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wallet
|
* Wallet
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function Wallet(options) {
|
function Wallet(options) {
|
||||||
|
var self = this;
|
||||||
var key, receiving;
|
var key, receiving;
|
||||||
|
|
||||||
if (!(this instanceof Wallet))
|
if (!(this instanceof Wallet))
|
||||||
@ -87,7 +89,6 @@ function Wallet(options) {
|
|||||||
: this.master.deriveAccount44(this.accountIndex);
|
: this.master.deriveAccount44(this.accountIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.storage = options.storage;
|
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.lastTs = 0;
|
this.lastTs = 0;
|
||||||
this.lastHeight = 0;
|
this.lastHeight = 0;
|
||||||
@ -131,15 +132,16 @@ Wallet.prototype._init = function _init() {
|
|||||||
assert(!this.receiveAddress.change);
|
assert(!this.receiveAddress.change);
|
||||||
assert(this.changeAddress.change);
|
assert(this.changeAddress.change);
|
||||||
|
|
||||||
this.prefix = 'bt/wallet/' + this.getID() + '/';
|
this.id = this.getID();
|
||||||
|
this.file = options.file;
|
||||||
|
|
||||||
this.tx = options.tx || bcoin.txPool(this);
|
if (!this.file || this.file === true) {
|
||||||
|
this.file = bcoin.dir + '/wallet-'
|
||||||
if (this.tx._loaded) {
|
+ this.id + '-' + network.type + '.json';
|
||||||
this.loading = false;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.tx = new bcoin.txPool(this);
|
||||||
|
|
||||||
// Notify owners about new accepted transactions
|
// Notify owners about new accepted transactions
|
||||||
this.tx.on('update', function(lastTs, lastHeight, tx) {
|
this.tx.on('update', function(lastTs, lastHeight, tx) {
|
||||||
var b = this.getBalance();
|
var b = this.getBalance();
|
||||||
@ -163,16 +165,22 @@ Wallet.prototype._init = function _init() {
|
|||||||
self.emit('confirmed', tx);
|
self.emit('confirmed', tx);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.tx.once('load', function(ts, height) {
|
|
||||||
self.loading = false;
|
|
||||||
self.lastTs = ts;
|
|
||||||
self.lastHeight = height;
|
|
||||||
self.emit('load', ts);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.tx.on('error', function(err) {
|
this.tx.on('error', function(err) {
|
||||||
self.emit('error', err);
|
self.emit('error', err);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (options.txs)
|
||||||
|
this.tx.populate(options.txs);
|
||||||
|
|
||||||
|
this.lastTs = this.tx._lastTs;
|
||||||
|
this.lastHeight = this.tx._lastHeight;
|
||||||
|
|
||||||
|
this.saveFile();
|
||||||
|
|
||||||
|
utils.nextTick(function() {
|
||||||
|
self.loading = false;
|
||||||
|
self.emit('load', self.lastTs);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.addKey = function addKey(key) {
|
Wallet.prototype.addKey = function addKey(key) {
|
||||||
@ -277,6 +285,9 @@ Wallet.prototype._finalizeKeys = function _finalizeKeys(key) {
|
|||||||
// bip45: Purpose key address
|
// bip45: Purpose key address
|
||||||
// bip44: Account key address
|
// bip44: Account key address
|
||||||
Wallet.prototype.getID = function getID() {
|
Wallet.prototype.getID = function getID() {
|
||||||
|
if (this.options.id)
|
||||||
|
return this.options.id;
|
||||||
|
|
||||||
return bcoin.address.compile(this.accountKey.publicKey);
|
return bcoin.address.compile(this.accountKey.publicKey);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -846,11 +857,12 @@ Wallet.prototype.__defineGetter__('address', function() {
|
|||||||
return this.getAddress();
|
return this.getAddress();
|
||||||
});
|
});
|
||||||
|
|
||||||
Wallet.prototype.toJSON = function toJSON(encrypt) {
|
Wallet.prototype.toJSON = function toJSON(noPool) {
|
||||||
return {
|
return {
|
||||||
v: 3,
|
v: 3,
|
||||||
name: 'wallet',
|
name: 'wallet',
|
||||||
network: network.type,
|
network: network.type,
|
||||||
|
id: this.id,
|
||||||
type: this.type,
|
type: this.type,
|
||||||
m: this.m,
|
m: this.m,
|
||||||
n: this.n,
|
n: this.n,
|
||||||
@ -859,17 +871,19 @@ Wallet.prototype.toJSON = function toJSON(encrypt) {
|
|||||||
accountIndex: this.accountIndex,
|
accountIndex: this.accountIndex,
|
||||||
receiveDepth: this.receiveDepth,
|
receiveDepth: this.receiveDepth,
|
||||||
changeDepth: this.changeDepth,
|
changeDepth: this.changeDepth,
|
||||||
master: this.master ? this.master.toJSON(encrypt) : null,
|
master: this.master.toJSON(this.options.passphrase),
|
||||||
addressMap: this.addressMap,
|
addressMap: this.addressMap,
|
||||||
keys: this.keys.map(function(key) {
|
keys: this.keys.map(function(key) {
|
||||||
return key.xpubkey;
|
return key.xpubkey;
|
||||||
}),
|
}),
|
||||||
balance: utils.btc(this.getBalance()),
|
balance: utils.btc(this.getBalance()),
|
||||||
tx: this.tx.toJSON()
|
txs: noPool ? [] : this.tx.getAll().map(function(tx) {
|
||||||
|
return tx.toJSON();
|
||||||
|
})
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.fromJSON = function fromJSON(json, decrypt) {
|
Wallet._fromJSON = function _fromJSON(json, passphrase) {
|
||||||
var wallet;
|
var wallet;
|
||||||
|
|
||||||
assert.equal(json.v, 3);
|
assert.equal(json.v, 3);
|
||||||
@ -878,7 +892,8 @@ Wallet.fromJSON = function fromJSON(json, decrypt) {
|
|||||||
if (json.network)
|
if (json.network)
|
||||||
assert.equal(json.network, network.type);
|
assert.equal(json.network, network.type);
|
||||||
|
|
||||||
wallet = new Wallet({
|
return {
|
||||||
|
id: json.id,
|
||||||
type: json.type,
|
type: json.type,
|
||||||
m: json.m,
|
m: json.m,
|
||||||
n: json.n,
|
n: json.n,
|
||||||
@ -887,16 +902,130 @@ Wallet.fromJSON = function fromJSON(json, decrypt) {
|
|||||||
accountIndex: json.accountIndex,
|
accountIndex: json.accountIndex,
|
||||||
receiveDepth: json.receiveDepth,
|
receiveDepth: json.receiveDepth,
|
||||||
changeDepth: json.changeDepth,
|
changeDepth: json.changeDepth,
|
||||||
master: json.master
|
master: bcoin.hd.fromJSON(json.master, passphrase),
|
||||||
? bcoin.hd.fromJSON(json.master, decrypt)
|
|
||||||
: null,
|
|
||||||
addressMap: json.addressMap,
|
addressMap: json.addressMap,
|
||||||
keys: json.keys
|
keys: json.keys,
|
||||||
|
txs: json.txs.map(function(json) {
|
||||||
|
return bcoin.tx.fromJSON(json);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
Wallet.fromJSON = function fromJSON(json, passphrase) {
|
||||||
|
return new Wallet(Wallet._fromJSON(json, passphrase));
|
||||||
|
};
|
||||||
|
|
||||||
|
Wallet.prototype.saveFile = function saveFile(callback) {
|
||||||
|
callback = utils.asyncify(callback);
|
||||||
|
|
||||||
|
if (!this.options.file)
|
||||||
|
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.tx.fromJSON(json.tx);
|
Wallet._fromFile = function _fromFile(file, passphrase, callback) {
|
||||||
|
if (typeof passphrase === 'function') {
|
||||||
|
callback = passphrase;
|
||||||
|
passphrase = null;
|
||||||
|
}
|
||||||
|
|
||||||
return wallet;
|
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);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user