wallet: classify.
This commit is contained in:
parent
9240d2f827
commit
f313ca166d
@ -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
@ -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
@ -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
|
||||
*/
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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
|
||||
*/
|
||||
|
||||
@ -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
|
||||
*/
|
||||
|
||||
5261
lib/wallet/txdb.js
5261
lib/wallet/txdb.js
File diff suppressed because it is too large
Load Diff
4129
lib/wallet/wallet.js
4129
lib/wallet/wallet.js
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user