wallet: classify.

This commit is contained in:
Christopher Jeffrey 2017-11-16 20:11:17 -08:00
parent 9240d2f827
commit f313ca166d
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
13 changed files with 9296 additions and 9345 deletions

View File

@ -908,7 +908,7 @@ class ChainDB {
return this.db.values({
gte: layout.e(encoding.ZERO_HASH),
lte: layout.e(encoding.MAX_HASH),
parse: value => ChainEntry.fromRaw(value)
parse: data => ChainEntry.fromRaw(data)
});
}

File diff suppressed because it is too large Load Diff

View File

@ -26,14 +26,16 @@ const HDPrivateKey = require('../hd/private');
const HDPublicKey = require('../hd/public');
const common = require('./common');
/**
* HTTP
* @alias module:wallet.HTTP
*/
class HTTP extends Server {
/**
* HTTP
* @alias module:wallet.HTTP
* Create an http server.
* @constructor
* @param {Object} options
* @see HTTPBase
* @emits HTTP#socket
*/
constructor(options) {

File diff suppressed because it is too large Load Diff

View File

@ -10,159 +10,160 @@ const assert = require('assert');
const EventEmitter = require('events');
/**
* NullClient
* Null Client
* Sort of a fake local client for separation of concerns.
* @alias module:node.NullClient
* @constructor
*/
function NullClient(wdb) {
if (!(this instanceof NullClient))
return new NullClient(wdb);
class NullClient extends EventEmitter {
/**
* Create a client.
* @constructor
*/
EventEmitter.call(this);
constructor(wdb) {
super();
this.wdb = wdb;
this.network = wdb.network;
this.opened = false;
this.wdb = wdb;
this.network = wdb.network;
this.opened = false;
}
/**
* Open the client.
* @returns {Promise}
*/
async open(options) {
assert(!this.opened, 'NullClient is already open.');
this.opened = true;
setImmediate(() => this.emit('connect'));
}
/**
* Close the client.
* @returns {Promise}
*/
async close() {
assert(this.opened, 'NullClient is not open.');
this.opened = false;
setImmediate(() => this.emit('disconnect'));
}
/**
* Add a listener.
* @param {String} type
* @param {Function} handler
*/
bind(type, handler) {
return this.on(type, handler);
}
/**
* Add a listener.
* @param {String} type
* @param {Function} handler
*/
hook(type, handler) {
return this.on(type, handler);
}
/**
* Get chain tip.
* @returns {Promise}
*/
async getTip() {
const {hash, height, time} = this.network.genesis;
return { hash, height, time };
}
/**
* Get chain entry.
* @param {Hash} hash
* @returns {Promise}
*/
async getEntry(hash) {
return { hash, height: 0, time: 0 };
}
/**
* Send a transaction. Do not wait for promise.
* @param {TX} tx
* @returns {Promise}
*/
async send(tx) {
this.wdb.emit('send', tx);
}
/**
* Set bloom filter.
* @param {Bloom} filter
* @returns {Promise}
*/
async setFilter(filter) {
this.wdb.emit('set filter', filter);
}
/**
* Add data to filter.
* @param {Buffer} data
* @returns {Promise}
*/
async addFilter(data) {
this.wdb.emit('add filter', data);
}
/**
* Reset filter.
* @returns {Promise}
*/
async resetFilter() {
this.wdb.emit('reset filter');
}
/**
* Esimate smart fee.
* @param {Number?} blocks
* @returns {Promise}
*/
async estimateFee(blocks) {
return this.network.feeRate;
}
/**
* Get hash range.
* @param {Number} start
* @param {Number} end
* @returns {Promise}
*/
async getHashes(start = -1, end = -1) {
return [this.network.genesis.hash];
}
/**
* Rescan for any missed transactions.
* @param {Number|Hash} start - Start block.
* @param {Bloom} filter
* @param {Function} iter - Iterator.
* @returns {Promise}
*/
async rescan(start) {
;
}
}
Object.setPrototypeOf(NullClient.prototype, EventEmitter.prototype);
/**
* Open the client.
* @returns {Promise}
*/
NullClient.prototype.open = async function open(options) {
assert(!this.opened, 'NullClient is already open.');
this.opened = true;
setImmediate(() => this.emit('connect'));
};
/**
* Close the client.
* @returns {Promise}
*/
NullClient.prototype.close = async function close() {
assert(this.opened, 'NullClient is not open.');
this.opened = false;
setImmediate(() => this.emit('disconnect'));
};
/**
* Add a listener.
* @param {String} type
* @param {Function} handler
*/
NullClient.prototype.bind = function bind(type, handler) {
return this.on(type, handler);
};
/**
* Add a listener.
* @param {String} type
* @param {Function} handler
*/
NullClient.prototype.hook = function hook(type, handler) {
return this.on(type, handler);
};
/**
* Get chain tip.
* @returns {Promise}
*/
NullClient.prototype.getTip = async function getTip() {
const {hash, height, time} = this.network.genesis;
return { hash, height, time };
};
/**
* Get chain entry.
* @param {Hash} hash
* @returns {Promise}
*/
NullClient.prototype.getEntry = async function getEntry(hash) {
return { hash, height: 0, time: 0 };
};
/**
* Send a transaction. Do not wait for promise.
* @param {TX} tx
* @returns {Promise}
*/
NullClient.prototype.send = async function send(tx) {
this.wdb.emit('send', tx);
};
/**
* Set bloom filter.
* @param {Bloom} filter
* @returns {Promise}
*/
NullClient.prototype.setFilter = async function setFilter(filter) {
this.wdb.emit('set filter', filter);
};
/**
* Add data to filter.
* @param {Buffer} data
* @returns {Promise}
*/
NullClient.prototype.addFilter = async function addFilter(data) {
this.wdb.emit('add filter', data);
};
/**
* Reset filter.
* @returns {Promise}
*/
NullClient.prototype.resetFilter = async function resetFilter() {
this.wdb.emit('reset filter');
};
/**
* Esimate smart fee.
* @param {Number?} blocks
* @returns {Promise}
*/
NullClient.prototype.estimateFee = async function estimateFee(blocks) {
return this.network.feeRate;
};
/**
* Get hash range.
* @param {Number} start
* @param {Number} end
* @returns {Promise}
*/
NullClient.prototype.getHashes = async function getHashes(start = -1, end = -1) {
return [this.network.genesis.hash];
};
/**
* Rescan for any missed transactions.
* @param {Number|Hash} start - Start block.
* @param {Bloom} filter
* @param {Function} iter - Iterator.
* @returns {Promise}
*/
NullClient.prototype.rescan = async function rescan(start) {
;
};
/*
* Expose
*/

View File

@ -14,36 +14,283 @@ const {encoding} = bio;
/**
* Path
* @alias module:wallet.Path
* @constructor
* @property {WalletID} wid
* @property {String} name - Account name.
* @property {Number} account - Account index.
* @property {Number} branch - Branch index.
* @property {Number} index - Address index.
* @property {Address|null} address
*/
function Path(options) {
if (!(this instanceof Path))
return new Path(options);
class Path {
/**
* Create a path.
* @constructor
* @param {Object?} options
*/
this.keyType = Path.types.HD;
constructor(options) {
this.keyType = Path.types.HD;
this.name = null; // Passed in by caller.
this.account = 0;
this.branch = -1;
this.index = -1;
this.name = null; // Passed in by caller.
this.account = 0;
this.branch = -1;
this.index = -1;
this.encrypted = false;
this.data = null;
this.encrypted = false;
this.data = null;
// Currently unused.
this.type = Address.types.PUBKEYHASH;
this.version = -1;
this.hash = null; // Passed in by caller.
// Currently unused.
this.type = Address.types.PUBKEYHASH;
this.version = -1;
this.hash = null; // Passed in by caller.
if (options)
this.fromOptions(options);
if (options)
this.fromOptions(options);
}
/**
* Instantiate path from options object.
* @private
* @param {Object} options
* @returns {Path}
*/
fromOptions(options) {
this.keyType = options.keyType;
this.name = options.name;
this.account = options.account;
this.branch = options.branch;
this.index = options.index;
this.encrypted = options.encrypted;
this.data = options.data;
this.type = options.type;
this.version = options.version;
this.hash = options.hash;
return this;
}
/**
* Instantiate path from options object.
* @param {Object} options
* @returns {Path}
*/
static fromOptions(options) {
return new this().fromOptions(options);
}
/**
* Clone the path object.
* @returns {Path}
*/
clone() {
const path = new this.constructor();
path.keyType = this.keyType;
path.name = this.name;
path.account = this.account;
path.branch = this.branch;
path.index = this.index;
path.encrypted = this.encrypted;
path.data = this.data;
path.type = this.type;
path.version = this.version;
path.hash = this.hash;
return path;
}
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
*/
fromRaw(data) {
const br = bio.read(data);
this.account = br.readU32();
this.keyType = br.readU8();
switch (this.keyType) {
case Path.types.HD:
this.branch = br.readU32();
this.index = br.readU32();
break;
case Path.types.KEY:
this.encrypted = br.readU8() === 1;
this.data = br.readVarBytes();
break;
case Path.types.ADDRESS:
// Hash will be passed in by caller.
break;
default:
assert(false);
break;
}
this.version = br.readI8();
this.type = br.readU8();
if (this.type === 129 || this.type === 130)
this.type = 4;
return this;
}
/**
* Instantiate path from serialized data.
* @param {Buffer} data
* @returns {Path}
*/
static fromRaw(data) {
return new this().fromRaw(data);
}
/**
* Calculate serialization size.
* @returns {Number}
*/
getSize() {
let size = 0;
size += 5;
switch (this.keyType) {
case Path.types.HD:
size += 8;
break;
case Path.types.KEY:
size += 1;
size += encoding.sizeVarBytes(this.data);
break;
}
size += 2;
return size;
}
/**
* Serialize path.
* @returns {Buffer}
*/
toRaw() {
const size = this.getSize();
const bw = bio.write(size);
bw.writeU32(this.account);
bw.writeU8(this.keyType);
switch (this.keyType) {
case Path.types.HD:
assert(!this.data);
assert(this.index !== -1);
bw.writeU32(this.branch);
bw.writeU32(this.index);
break;
case Path.types.KEY:
assert(this.data);
assert(this.index === -1);
bw.writeU8(this.encrypted ? 1 : 0);
bw.writeVarBytes(this.data);
break;
case Path.types.ADDRESS:
assert(!this.data);
assert(this.index === -1);
break;
default:
assert(false);
break;
}
bw.writeI8(this.version);
bw.writeU8(this.type);
return bw.render();
}
/**
* Inject properties from address.
* @private
* @param {Account} account
* @param {Address} address
*/
fromAddress(account, address) {
this.keyType = Path.types.ADDRESS;
this.name = account.name;
this.account = account.accountIndex;
this.version = address.version;
this.type = address.type;
this.hash = address.getHash('hex');
return this;
}
/**
* Instantiate path from address.
* @param {Account} account
* @param {Address} address
* @returns {Path}
*/
static fromAddress(account, address) {
return new this().fromAddress(account, address);
}
/**
* Convert path object to string derivation path.
* @returns {String}
*/
toPath() {
if (this.keyType !== Path.types.HD)
return null;
return `m/${this.account}'/${this.branch}/${this.index}`;
}
/**
* Convert path object to an address (currently unused).
* @returns {Address}
*/
toAddress() {
return Address.fromHash(this.hash, this.type, this.version);
}
/**
* Convert path to a json-friendly object.
* @returns {Object}
*/
toJSON() {
return {
name: this.name,
account: this.account,
change: this.branch === 1,
derivation: this.toPath()
};
}
/**
* Inspect the path.
* @returns {String}
*/
inspect() {
return `<Path: ${this.name}:${this.toPath()}>`;
}
}
/**
@ -59,249 +306,16 @@ Path.types = {
};
/**
* Instantiate path from options object.
* @private
* @param {Object} options
* @returns {Path}
* Path types.
* @enum {Number}
* @default
*/
Path.prototype.fromOptions = function fromOptions(options) {
this.keyType = options.keyType;
this.name = options.name;
this.account = options.account;
this.branch = options.branch;
this.index = options.index;
this.encrypted = options.encrypted;
this.data = options.data;
this.type = options.type;
this.version = options.version;
this.hash = options.hash;
return this;
};
/**
* Instantiate path from options object.
* @param {Object} options
* @returns {Path}
*/
Path.fromOptions = function fromOptions(options) {
return new Path().fromOptions(options);
};
/**
* Clone the path object.
* @returns {Path}
*/
Path.prototype.clone = function clone() {
const path = new Path();
path.keyType = this.keyType;
path.name = this.name;
path.account = this.account;
path.branch = this.branch;
path.index = this.index;
path.encrypted = this.encrypted;
path.data = this.data;
path.type = this.type;
path.version = this.version;
path.hash = this.hash;
return path;
};
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
*/
Path.prototype.fromRaw = function fromRaw(data) {
const br = bio.read(data);
this.account = br.readU32();
this.keyType = br.readU8();
switch (this.keyType) {
case Path.types.HD:
this.branch = br.readU32();
this.index = br.readU32();
break;
case Path.types.KEY:
this.encrypted = br.readU8() === 1;
this.data = br.readVarBytes();
break;
case Path.types.ADDRESS:
// Hash will be passed in by caller.
break;
default:
assert(false);
break;
}
this.version = br.readI8();
this.type = br.readU8();
if (this.type === 129 || this.type === 130)
this.type = 4;
return this;
};
/**
* Instantiate path from serialized data.
* @param {Buffer} data
* @returns {Path}
*/
Path.fromRaw = function fromRaw(data) {
return new Path().fromRaw(data);
};
/**
* Calculate serialization size.
* @returns {Number}
*/
Path.prototype.getSize = function getSize() {
let size = 0;
size += 5;
switch (this.keyType) {
case Path.types.HD:
size += 8;
break;
case Path.types.KEY:
size += 1;
size += encoding.sizeVarBytes(this.data);
break;
}
size += 2;
return size;
};
/**
* Serialize path.
* @returns {Buffer}
*/
Path.prototype.toRaw = function toRaw() {
const size = this.getSize();
const bw = bio.write(size);
bw.writeU32(this.account);
bw.writeU8(this.keyType);
switch (this.keyType) {
case Path.types.HD:
assert(!this.data);
assert(this.index !== -1);
bw.writeU32(this.branch);
bw.writeU32(this.index);
break;
case Path.types.KEY:
assert(this.data);
assert(this.index === -1);
bw.writeU8(this.encrypted ? 1 : 0);
bw.writeVarBytes(this.data);
break;
case Path.types.ADDRESS:
assert(!this.data);
assert(this.index === -1);
break;
default:
assert(false);
break;
}
bw.writeI8(this.version);
bw.writeU8(this.type);
return bw.render();
};
/**
* Inject properties from address.
* @private
* @param {Account} account
* @param {Address} address
*/
Path.prototype.fromAddress = function fromAddress(account, address) {
this.keyType = Path.types.ADDRESS;
this.name = account.name;
this.account = account.accountIndex;
this.version = address.version;
this.type = address.type;
this.hash = address.getHash('hex');
return this;
};
/**
* Instantiate path from address.
* @param {Account} account
* @param {Address} address
* @returns {Path}
*/
Path.fromAddress = function fromAddress(account, address) {
return new Path().fromAddress(account, address);
};
/**
* Convert path object to string derivation path.
* @returns {String}
*/
Path.prototype.toPath = function toPath() {
if (this.keyType !== Path.types.HD)
return null;
return `m/${this.account}'/${this.branch}/${this.index}`;
};
/**
* Convert path object to an address (currently unused).
* @returns {Address}
*/
Path.prototype.toAddress = function toAddress() {
return Address.fromHash(this.hash, this.type, this.version);
};
/**
* Convert path to a json-friendly object.
* @returns {Object}
*/
Path.prototype.toJSON = function toJSON() {
return {
name: this.name,
account: this.account,
change: this.branch === 1,
derivation: this.toPath()
};
};
/**
* Inspect the path.
* @returns {String}
*/
Path.prototype.inspect = function inspect() {
return `<Path: ${this.name}:${this.toPath()}>`;
};
Path.typesByVal = [
'HD',
'KEY',
'ADDRESS'
];
/**
* Expose

View File

@ -20,71 +20,75 @@ const plugin = exports;
/**
* Plugin
* @constructor
* @param {Node} node
* @extends EventEmitter
*/
function Plugin(node) {
if (!(this instanceof Plugin))
return new Plugin(node);
class Plugin extends EventEmitter {
/**
* Create a plugin.
* @constructor
* @param {Node} node
*/
const config = node.config;
constructor(node) {
super();
this.network = node.network;
this.logger = node.logger;
const config = node.config;
this.client = new NodeClient(node);
this.plugin = true;
this.network = node.network;
this.logger = node.logger;
this.wdb = new WalletDB({
network: node.network,
logger: node.logger,
workers: node.workers,
client: this.client,
prefix: config.prefix,
db: config.str(['wallet-db', 'db']),
maxFiles: config.uint('wallet-max-files'),
cacheSize: config.mb('wallet-cache-size'),
witness: config.bool('wallet-witness'),
checkpoints: config.bool('wallet-checkpoints'),
startHeight: config.uint('wallet-start-height'),
wipeNoReally: config.bool('wallet-wipe-no-really'),
spv: node.spv
});
this.client = new NodeClient(node);
this.plugin = true;
this.rpc = new RPC(this);
this.wdb = new WalletDB({
network: node.network,
logger: node.logger,
workers: node.workers,
client: this.client,
prefix: config.prefix,
db: config.str(['wallet-db', 'db']),
maxFiles: config.uint('wallet-max-files'),
cacheSize: config.mb('wallet-cache-size'),
witness: config.bool('wallet-witness'),
checkpoints: config.bool('wallet-checkpoints'),
startHeight: config.uint('wallet-start-height'),
wipeNoReally: config.bool('wallet-wipe-no-really'),
spv: node.spv
});
this.http = new HTTP({
network: node.network,
logger: node.logger,
node: this,
apiKey: config.str(['wallet-api-key', 'api-key']),
walletAuth: config.bool('wallet-auth'),
noAuth: config.bool(['wallet-no-auth', 'no-auth'])
});
this.rpc = new RPC(this);
this.http.attach('/wallet', node.http);
this.http = new HTTP({
network: node.network,
logger: node.logger,
node: this,
apiKey: config.str(['wallet-api-key', 'api-key']),
walletAuth: config.bool('wallet-auth'),
noAuth: config.bool(['wallet-no-auth', 'no-auth'])
});
this.init();
this.http.attach('/wallet', node.http);
this.init();
}
init() {
this.wdb.on('error', err => this.emit('error', err));
this.http.on('error', err => this.emit('error', err));
}
async open() {
await this.wdb.open();
this.rpc.wallet = this.wdb.primary;
}
async close() {
this.rpc.wallet = null;
await this.wdb.close();
}
}
Object.setPrototypeOf(Plugin.prototype, EventEmitter.prototype);
Plugin.prototype.init = function init() {
this.wdb.on('error', err => this.emit('error', err));
this.http.on('error', err => this.emit('error', err));
};
Plugin.prototype.open = async function open() {
await this.wdb.open();
this.rpc.wallet = this.wdb.primary;
};
Plugin.prototype.close = async function close() {
this.rpc.wallet = this.wdb.primary;
await this.wdb.close();
};
/**
* Plugin name.
* @const {String}

View File

@ -18,464 +18,479 @@ const {encoding} = bio;
/**
* Chain State
* @constructor
*/
function ChainState() {
if (!(this instanceof ChainState))
return new ChainState();
class ChainState {
/**
* Create a chain state.
* @constructor
*/
this.startHeight = 0;
this.startHash = encoding.NULL_HASH;
this.height = 0;
this.marked = false;
constructor() {
this.startHeight = 0;
this.startHash = encoding.NULL_HASH;
this.height = 0;
this.marked = false;
}
/**
* Clone the state.
* @returns {ChainState}
*/
clone() {
const state = new ChainState();
state.startHeight = this.startHeight;
state.startHash = this.startHash;
state.height = this.height;
state.marked = this.marked;
return state;
}
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
*/
fromRaw(data) {
const br = bio.read(data);
this.startHeight = br.readU32();
this.startHash = br.readHash('hex');
this.height = br.readU32();
this.marked = br.readU8() === 1;
return this;
}
/**
* Instantiate chain state from serialized data.
* @param {Hash} hash
* @param {Buffer} data
* @returns {ChainState}
*/
static fromRaw(data) {
return new this().fromRaw(data);
}
/**
* Serialize the chain state.
* @returns {Buffer}
*/
toRaw() {
const bw = bio.write(41);
bw.writeU32(this.startHeight);
bw.writeHash(this.startHash);
bw.writeU32(this.height);
bw.writeU8(this.marked ? 1 : 0);
return bw.render();
}
}
/**
* Clone the state.
* @returns {ChainState}
*/
ChainState.prototype.clone = function clone() {
const state = new ChainState();
state.startHeight = this.startHeight;
state.startHash = this.startHash;
state.height = this.height;
state.marked = this.marked;
return state;
};
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
*/
ChainState.prototype.fromRaw = function fromRaw(data) {
const br = bio.read(data);
this.startHeight = br.readU32();
this.startHash = br.readHash('hex');
this.height = br.readU32();
this.marked = br.readU8() === 1;
return this;
};
/**
* Instantiate chain state from serialized data.
* @param {Hash} hash
* @param {Buffer} data
* @returns {ChainState}
*/
ChainState.fromRaw = function fromRaw(data) {
return new ChainState().fromRaw(data);
};
/**
* Serialize the chain state.
* @returns {Buffer}
*/
ChainState.prototype.toRaw = function toRaw() {
const bw = bio.write(41);
bw.writeU32(this.startHeight);
bw.writeHash(this.startHash);
bw.writeU32(this.height);
bw.writeU8(this.marked ? 1 : 0);
return bw.render();
};
/**
* Block Meta
* @constructor
* @param {Hash} hash
* @param {Number} height
* @param {Number} time
*/
function BlockMeta(hash, height, time) {
if (!(this instanceof BlockMeta))
return new BlockMeta(hash, height, time);
class BlockMeta {
/**
* Create block meta.
* @constructor
* @param {Hash} hash
* @param {Number} height
* @param {Number} time
*/
this.hash = hash || encoding.NULL_HASH;
this.height = height != null ? height : -1;
this.time = time || 0;
}
/**
* Clone the block.
* @returns {BlockMeta}
*/
BlockMeta.prototype.clone = function clone() {
return new BlockMeta(this.hash, this.height, this.time);
};
/**
* Get block meta hash as a buffer.
* @returns {Buffer}
*/
BlockMeta.prototype.toHash = function toHash() {
return Buffer.from(this.hash, 'hex');
};
/**
* Instantiate block meta from chain entry.
* @private
* @param {ChainEntry} entry
*/
BlockMeta.prototype.fromEntry = function fromEntry(entry) {
this.hash = entry.hash;
this.height = entry.height;
this.time = entry.time;
return this;
};
/**
* Instantiate block meta from json object.
* @private
* @param {Object} json
*/
BlockMeta.prototype.fromJSON = function fromJSON(json) {
this.hash = encoding.revHex(json.hash);
this.height = json.height;
this.time = json.time;
return this;
};
/**
* Instantiate block meta from serialized tip data.
* @private
* @param {Buffer} data
*/
BlockMeta.prototype.fromRaw = function fromRaw(data) {
const br = bio.read(data);
this.hash = br.readHash('hex');
this.height = br.readU32();
this.time = br.readU32();
return this;
};
/**
* Instantiate block meta from chain entry.
* @param {ChainEntry} entry
* @returns {BlockMeta}
*/
BlockMeta.fromEntry = function fromEntry(entry) {
return new BlockMeta().fromEntry(entry);
};
/**
* Instantiate block meta from json object.
* @param {Object} json
* @returns {BlockMeta}
*/
BlockMeta.fromJSON = function fromJSON(json) {
return new BlockMeta().fromJSON(json);
};
/**
* Instantiate block meta from serialized data.
* @param {Hash} hash
* @param {Buffer} data
* @returns {BlockMeta}
*/
BlockMeta.fromRaw = function fromRaw(data) {
return new BlockMeta().fromRaw(data);
};
/**
* Serialize the block meta.
* @returns {Buffer}
*/
BlockMeta.prototype.toRaw = function toRaw() {
const bw = bio.write(42);
bw.writeHash(this.hash);
bw.writeU32(this.height);
bw.writeU32(this.time);
return bw.render();
};
/**
* Convert the block meta to a more json-friendly object.
* @returns {Object}
*/
BlockMeta.prototype.toJSON = function toJSON() {
return {
hash: encoding.revHex(this.hash),
height: this.height,
time: this.time
};
};
/**
* TXRecord
* @constructor
* @param {TX} tx
* @param {BlockMeta?} block
*/
function TXRecord(tx, block) {
if (!(this instanceof TXRecord))
return new TXRecord(tx, block);
this.tx = null;
this.hash = null;
this.mtime = util.now();
this.height = -1;
this.block = null;
this.index = -1;
this.time = 0;
if (tx)
this.fromTX(tx, block);
}
/**
* Inject properties from tx and block.
* @private
* @param {TX} tx
* @param {Block?} block
* @returns {TXRecord}
*/
TXRecord.prototype.fromTX = function fromTX(tx, block) {
this.tx = tx;
this.hash = tx.hash('hex');
if (block)
this.setBlock(block);
return this;
};
/**
* Instantiate tx record from tx and block.
* @param {TX} tx
* @param {Block?} block
* @returns {TXRecord}
*/
TXRecord.fromTX = function fromTX(tx, block) {
return new TXRecord().fromTX(tx, block);
};
/**
* Set block data (confirm).
* @param {BlockMeta} block
*/
TXRecord.prototype.setBlock = function setBlock(block) {
this.height = block.height;
this.block = block.hash;
this.time = block.time;
};
/**
* Unset block (unconfirm).
*/
TXRecord.prototype.unsetBlock = function unsetBlock() {
this.height = -1;
this.block = null;
this.time = 0;
};
/**
* Convert tx record to a block meta.
* @returns {BlockMeta}
*/
TXRecord.prototype.getBlock = function getBlock() {
if (this.height === -1)
return null;
return new BlockMeta(this.block, this.height, this.time);
};
/**
* Calculate current number of transaction confirmations.
* @param {Number} height - Current chain height.
* @returns {Number} confirmations
*/
TXRecord.prototype.getDepth = function getDepth(height) {
assert(typeof height === 'number', 'Must pass in height.');
if (this.height === -1)
return 0;
if (height < this.height)
return 0;
return height - this.height + 1;
};
/**
* Get serialization size.
* @returns {Number}
*/
TXRecord.prototype.getSize = function getSize() {
let size = 0;
size += this.tx.getSize();
size += 4;
if (this.block) {
size += 1;
size += 32;
size += 4 * 3;
} else {
size += 1;
constructor(hash, height, time) {
this.hash = hash || encoding.NULL_HASH;
this.height = height != null ? height : -1;
this.time = time || 0;
}
return size;
};
/**
* Clone the block.
* @returns {BlockMeta}
*/
/**
* Serialize a transaction to "extended format".
* @returns {Buffer}
*/
TXRecord.prototype.toRaw = function toRaw() {
const size = this.getSize();
const bw = bio.write(size);
let index = this.index;
this.tx.toWriter(bw);
bw.writeU32(this.mtime);
if (this.block) {
if (index === -1)
index = 0x7fffffff;
bw.writeU8(1);
bw.writeHash(this.block);
bw.writeU32(this.height);
bw.writeU32(this.time);
bw.writeU32(index);
} else {
bw.writeU8(0);
clone() {
return new this.constructor(this.hash, this.height, this.time);
}
return bw.render();
};
/**
* Get block meta hash as a buffer.
* @returns {Buffer}
*/
/**
* Inject properties from "extended" format.
* @private
* @param {Buffer} data
*/
toHash() {
return Buffer.from(this.hash, 'hex');
}
TXRecord.prototype.fromRaw = function fromRaw(data) {
const br = bio.read(data);
/**
* Instantiate block meta from chain entry.
* @private
* @param {ChainEntry} entry
*/
this.tx = new TX();
this.tx.fromReader(br);
fromEntry(entry) {
this.hash = entry.hash;
this.height = entry.height;
this.time = entry.time;
return this;
}
this.hash = this.tx.hash('hex');
this.mtime = br.readU32();
/**
* Instantiate block meta from json object.
* @private
* @param {Object} json
*/
if (br.readU8() === 1) {
this.block = br.readHash('hex');
fromJSON(json) {
this.hash = encoding.revHex(json.hash);
this.height = json.height;
this.time = json.time;
return this;
}
/**
* Instantiate block meta from serialized tip data.
* @private
* @param {Buffer} data
*/
fromRaw(data) {
const br = bio.read(data);
this.hash = br.readHash('hex');
this.height = br.readU32();
this.time = br.readU32();
this.index = br.readU32();
if (this.index === 0x7fffffff)
this.index = -1;
return this;
}
return this;
};
/**
* Instantiate block meta from chain entry.
* @param {ChainEntry} entry
* @returns {BlockMeta}
*/
static fromEntry(entry) {
return new this().fromEntry(entry);
}
/**
* Instantiate block meta from json object.
* @param {Object} json
* @returns {BlockMeta}
*/
static fromJSON(json) {
return new this().fromJSON(json);
}
/**
* Instantiate block meta from serialized data.
* @param {Hash} hash
* @param {Buffer} data
* @returns {BlockMeta}
*/
static fromRaw(data) {
return new this().fromRaw(data);
}
/**
* Serialize the block meta.
* @returns {Buffer}
*/
toRaw() {
const bw = bio.write(42);
bw.writeHash(this.hash);
bw.writeU32(this.height);
bw.writeU32(this.time);
return bw.render();
}
/**
* Convert the block meta to a more json-friendly object.
* @returns {Object}
*/
toJSON() {
return {
hash: encoding.revHex(this.hash),
height: this.height,
time: this.time
};
}
}
/**
* Instantiate a transaction from a buffer
* in "extended" serialization format.
* @param {Buffer} data
* @returns {TX}
* TX Record
*/
TXRecord.fromRaw = function fromRaw(data) {
return new TXRecord().fromRaw(data);
};
class TXRecord {
/**
* Create tx record.
* @constructor
* @param {TX} tx
* @param {BlockMeta?} block
*/
constructor(tx, block) {
this.tx = null;
this.hash = null;
this.mtime = util.now();
this.height = -1;
this.block = null;
this.index = -1;
this.time = 0;
if (tx)
this.fromTX(tx, block);
}
/**
* Inject properties from tx and block.
* @private
* @param {TX} tx
* @param {Block?} block
* @returns {TXRecord}
*/
fromTX(tx, block) {
this.tx = tx;
this.hash = tx.hash('hex');
if (block)
this.setBlock(block);
return this;
}
/**
* Instantiate tx record from tx and block.
* @param {TX} tx
* @param {Block?} block
* @returns {TXRecord}
*/
static fromTX(tx, block) {
return new this().fromTX(tx, block);
}
/**
* Set block data (confirm).
* @param {BlockMeta} block
*/
setBlock(block) {
this.height = block.height;
this.block = block.hash;
this.time = block.time;
}
/**
* Unset block (unconfirm).
*/
unsetBlock() {
this.height = -1;
this.block = null;
this.time = 0;
}
/**
* Convert tx record to a block meta.
* @returns {BlockMeta}
*/
getBlock() {
if (this.height === -1)
return null;
return new BlockMeta(this.block, this.height, this.time);
}
/**
* Calculate current number of transaction confirmations.
* @param {Number} height - Current chain height.
* @returns {Number} confirmations
*/
getDepth(height) {
assert(typeof height === 'number', 'Must pass in height.');
if (this.height === -1)
return 0;
if (height < this.height)
return 0;
return height - this.height + 1;
}
/**
* Get serialization size.
* @returns {Number}
*/
getSize() {
let size = 0;
size += this.tx.getSize();
size += 4;
if (this.block) {
size += 1;
size += 32;
size += 4 * 3;
} else {
size += 1;
}
return size;
}
/**
* Serialize a transaction to "extended format".
* @returns {Buffer}
*/
toRaw() {
const size = this.getSize();
const bw = bio.write(size);
let index = this.index;
this.tx.toWriter(bw);
bw.writeU32(this.mtime);
if (this.block) {
if (index === -1)
index = 0x7fffffff;
bw.writeU8(1);
bw.writeHash(this.block);
bw.writeU32(this.height);
bw.writeU32(this.time);
bw.writeU32(index);
} else {
bw.writeU8(0);
}
return bw.render();
}
/**
* Inject properties from "extended" format.
* @private
* @param {Buffer} data
*/
fromRaw(data) {
const br = bio.read(data);
this.tx = new TX();
this.tx.fromReader(br);
this.hash = this.tx.hash('hex');
this.mtime = br.readU32();
if (br.readU8() === 1) {
this.block = br.readHash('hex');
this.height = br.readU32();
this.time = br.readU32();
this.index = br.readU32();
if (this.index === 0x7fffffff)
this.index = -1;
}
return this;
}
/**
* Instantiate a transaction from a buffer
* in "extended" serialization format.
* @param {Buffer} data
* @returns {TX}
*/
static fromRaw(data) {
return new this().fromRaw(data);
}
}
/**
* Map Record
* @constructor
*/
function MapRecord() {
this.wids = new Set();
class MapRecord {
/**
* Create map record.
* @constructor
*/
constructor() {
this.wids = new Set();
}
add(wid) {
if (this.wids.has(wid))
return false;
this.wids.add(wid);
return true;
}
remove(wid) {
return this.wids.delete(wid);
}
toWriter(bw) {
bw.writeU32(this.wids.size);
for (const wid of this.wids)
bw.writeU32(wid);
return bw;
}
getSize() {
return 4 + this.wids.size * 4;
}
toRaw() {
const size = this.getSize();
return this.toWriter(bio.write(size)).render();
}
fromReader(br) {
const count = br.readU32();
for (let i = 0; i < count; i++)
this.wids.add(br.readU32());
return this;
}
fromRaw(data) {
return this.fromReader(bio.read(data));
}
static fromReader(br) {
return new this().fromReader(br);
}
static fromRaw(data) {
return new this().fromRaw(data);
}
}
MapRecord.prototype.add = function add(wid) {
if (this.wids.has(wid))
return false;
this.wids.add(wid);
return true;
};
MapRecord.prototype.remove = function remove(wid) {
return this.wids.delete(wid);
};
MapRecord.prototype.toWriter = function toWriter(bw) {
bw.writeU32(this.wids.size);
for (const wid of this.wids)
bw.writeU32(wid);
return bw;
};
MapRecord.prototype.getSize = function getSize() {
return 4 + this.wids.size * 4;
};
MapRecord.prototype.toRaw = function toRaw() {
const size = this.getSize();
return this.toWriter(bio.write(size)).render();
};
MapRecord.prototype.fromReader = function fromReader(br) {
const count = br.readU32();
for (let i = 0; i < count; i++)
this.wids.add(br.readU32());
return this;
};
MapRecord.prototype.fromRaw = function fromRaw(data) {
return this.fromReader(bio.read(data));
};
MapRecord.fromReader = function fromReader(br) {
return new MapRecord().fromReader(br);
};
MapRecord.fromRaw = function fromRaw(data) {
return new MapRecord().fromRaw(data);
};
/*
* Expose
*/

View File

@ -16,121 +16,123 @@ const RPC = require('./rpc');
/**
* Wallet Node
* @extends Node
* @constructor
*/
function WalletNode(options) {
if (!(this instanceof WalletNode))
return new WalletNode(options);
class WalletNode extends Node {
/**
* Create a wallet node.
* @constructor
* @param {Object?} options
*/
Node.call(this, 'bcoin', 'wallet.conf', 'wallet.log', options);
constructor(options) {
super('bcoin', 'wallet.conf', 'wallet.log', options);
this.opened = false;
this.opened = false;
this.client = new Client({
network: this.network,
url: this.config.str('node-url'),
host: this.config.str('node-host'),
port: this.config.str('node-port', this.network.rpcPort),
ssl: this.config.str('node-ssl'),
apiKey: this.config.str('node-api-key')
});
this.client = new Client({
network: this.network,
url: this.config.str('node-url'),
host: this.config.str('node-host'),
port: this.config.str('node-port', this.network.rpcPort),
ssl: this.config.str('node-ssl'),
apiKey: this.config.str('node-api-key')
});
this.wdb = new WalletDB({
network: this.network,
logger: this.logger,
workers: this.workers,
client: this.client,
prefix: this.config.prefix,
db: this.config.str('db'),
maxFiles: this.config.uint('max-files'),
cacheSize: this.config.mb('cache-size'),
witness: this.config.bool('witness'),
checkpoints: this.config.bool('checkpoints'),
startHeight: this.config.uint('start-height'),
wipeNoReally: this.config.bool('wipe-no-really'),
spv: this.config.bool('spv')
});
this.wdb = new WalletDB({
network: this.network,
logger: this.logger,
workers: this.workers,
client: this.client,
prefix: this.config.prefix,
db: this.config.str('db'),
maxFiles: this.config.uint('max-files'),
cacheSize: this.config.mb('cache-size'),
witness: this.config.bool('witness'),
checkpoints: this.config.bool('checkpoints'),
startHeight: this.config.uint('start-height'),
wipeNoReally: this.config.bool('wipe-no-really'),
spv: this.config.bool('spv')
});
this.rpc = new RPC(this);
this.rpc = new RPC(this);
this.http = new HTTP({
network: this.network,
logger: this.logger,
node: this,
prefix: this.config.prefix,
ssl: this.config.bool('ssl'),
keyFile: this.config.path('ssl-key'),
certFile: this.config.path('ssl-cert'),
host: this.config.str('http-host'),
port: this.config.uint('http-port'),
apiKey: this.config.str('api-key'),
noAuth: this.config.bool('no-auth'),
walletAuth: this.config.bool('wallet-auth')
});
this.http = new HTTP({
network: this.network,
logger: this.logger,
node: this,
prefix: this.config.prefix,
ssl: this.config.bool('ssl'),
keyFile: this.config.path('ssl-key'),
certFile: this.config.path('ssl-cert'),
host: this.config.str('http-host'),
port: this.config.uint('http-port'),
apiKey: this.config.str('api-key'),
noAuth: this.config.bool('no-auth'),
walletAuth: this.config.bool('wallet-auth')
});
this.init();
this.init();
}
/**
* Initialize the node.
* @private
*/
init() {
this.wdb.on('error', err => this.error(err));
this.http.on('error', err => this.error(err));
this.loadPlugins();
}
/**
* Open the node and all its child objects,
* wait for the database to load.
* @alias WalletNode#open
* @returns {Promise}
*/
async open() {
assert(!this.opened, 'WalletNode is already open.');
this.opened = true;
await this.handlePreopen();
await this.wdb.open();
this.rpc.wallet = this.wdb.primary;
await this.openPlugins();
await this.http.open();
await this.handleOpen();
this.logger.info('Wallet node is loaded.');
}
/**
* Close the node, wait for the database to close.
* @alias WalletNode#close
* @returns {Promise}
*/
async close() {
assert(this.opened, 'WalletNode is not open.');
this.opened = false;
await this.handlePreclose();
await this.http.close();
await this.closePlugins();
this.rpc.wallet = null;
await this.wdb.close();
await this.handleClose();
}
}
Object.setPrototypeOf(WalletNode.prototype, Node.prototype);
/**
* Initialize the node.
* @private
*/
WalletNode.prototype.init = function init() {
this.wdb.on('error', err => this.error(err));
this.http.on('error', err => this.error(err));
this.loadPlugins();
};
/**
* Open the node and all its child objects,
* wait for the database to load.
* @alias WalletNode#open
* @returns {Promise}
*/
WalletNode.prototype.open = async function open() {
assert(!this.opened, 'WalletNode is already open.');
this.opened = true;
await this.handlePreopen();
await this.wdb.open();
this.rpc.wallet = this.wdb.primary;
await this.openPlugins();
await this.http.open();
await this.handleOpen();
this.logger.info('Wallet node is loaded.');
};
/**
* Close the node, wait for the database to close.
* @alias WalletNode#close
* @returns {Promise}
*/
WalletNode.prototype.close = async function close() {
assert(this.opened, 'WalletNode is not open.');
this.opened = false;
await this.handlePreclose();
await this.http.close();
await this.closePlugins();
this.rpc.wallet = null;
await this.wdb.close();
await this.handleClose();
};
/*
* Expose
*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -36,79 +36,6 @@ class WalletKey extends KeyRing {
this.index = -1;
}
/**
* Instantiate key ring from options.
* @param {Object} options
* @returns {WalletKey}
*/
static fromOptions(options) {
return new this().fromOptions(options);
}
/**
* Instantiate wallet key from a private key.
* @param {Buffer} key
* @param {Boolean?} compressed
* @returns {WalletKey}
*/
static fromPrivate(key, compressed) {
return new this().fromPrivate(key, compressed);
}
/**
* Generate a wallet key.
* @param {Boolean?} compressed
* @returns {WalletKey}
*/
static generate(compressed) {
return new this().generate(compressed);
}
/**
* Instantiate wallet key from a public key.
* @param {Buffer} publicKey
* @returns {WalletKey}
*/
static fromPublic(key) {
return new this().fromPublic(key);
}
/**
* Instantiate wallet key from a public key.
* @param {Buffer} publicKey
* @returns {WalletKey}
*/
static fromKey(key, compressed) {
return new this().fromKey(key, compressed);
}
/**
* Instantiate wallet key from script.
* @param {Buffer} key
* @param {Script} script
* @returns {WalletKey}
*/
static fromScript(key, script, compressed) {
return new this().fromScript(key, script, compressed);
}
/**
* Instantiate a wallet key from a serialized CBitcoinSecret.
* @param {Base58String} secret
* @param {Network?} network
* @returns {WalletKey}
*/
static fromSecret(data, network) {
return new this().fromSecret(data, network);
}
/**
* Convert an WalletKey to a more json-friendly object.
* @returns {Object}
@ -130,26 +57,6 @@ class WalletKey extends KeyRing {
};
}
/**
* Instantiate an WalletKey from a jsonified transaction object.
* @param {Object} json - The jsonified transaction object.
* @returns {WalletKey}
*/
static fromJSON(json) {
return new this().fromJSON(json);
}
/**
* Instantiate a wallet key from serialized data.
* @param {Buffer} data
* @returns {WalletKey}
*/
static fromRaw(data) {
return new this().fromRaw(data);
}
/**
* Inject properties from hd key.
* @private