wallet: use separate branch for nested addrs.
This commit is contained in:
parent
19c8959c1a
commit
960393a53f
9
bin/cli
9
bin/cli
@ -88,6 +88,12 @@ CLI.prototype.createAddress = co(function* createAddress() {
|
||||
this.log(addr);
|
||||
});
|
||||
|
||||
CLI.prototype.createNested = co(function* createNested() {
|
||||
var account = this.argv[0];
|
||||
var addr = yield this.wallet.createNested(account);
|
||||
this.log(addr);
|
||||
});
|
||||
|
||||
CLI.prototype.getAccounts = co(function* getAccounts() {
|
||||
var accounts = yield this.wallet.getAccounts();
|
||||
this.log(accounts);
|
||||
@ -343,6 +349,8 @@ CLI.prototype.handleWallet = co(function* handleWallet() {
|
||||
return yield this.getAccount();
|
||||
case 'address':
|
||||
return yield this.createAddress();
|
||||
case 'nested':
|
||||
return yield this.createNested();
|
||||
case 'retoken':
|
||||
return yield this.retoken();
|
||||
case 'sign':
|
||||
@ -370,6 +378,7 @@ CLI.prototype.handleWallet = co(function* handleWallet() {
|
||||
this.log(' $ account create [account-name]: Create account.');
|
||||
this.log(' $ account get [account-name]: Get account details.');
|
||||
this.log(' $ address: Derive new address.');
|
||||
this.log(' $ nested: Derive new nested address.');
|
||||
this.log(' $ retoken: Create new api key.');
|
||||
this.log(' $ send [address] [value]: Send transaction.');
|
||||
this.log(' $ mktx [address] [value]: Create transaction.');
|
||||
|
||||
@ -756,6 +756,27 @@ HTTPClient.prototype.createAddress = function createAddress(id, options) {
|
||||
return this._post(path, options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create address.
|
||||
* @param {WalletID} id
|
||||
* @param {Object} options
|
||||
* @returns {Promise} - Returns Array.
|
||||
*/
|
||||
|
||||
HTTPClient.prototype.createNested = function createNested(id, options) {
|
||||
var path;
|
||||
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
if (typeof options === 'string')
|
||||
options = { account: options };
|
||||
|
||||
path = '/wallet/' + id + '/nested';
|
||||
|
||||
return this._post(path, options);
|
||||
};
|
||||
|
||||
/*
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
@ -714,6 +714,13 @@ HTTPServer.prototype._init = function _init() {
|
||||
send(200, address.toJSON());
|
||||
}));
|
||||
|
||||
// Create nested address
|
||||
this.post('/wallet/:id/nested', con(function *(req, res, send, next) {
|
||||
var account = req.options.account;
|
||||
var address = yield req.wallet.createNested(account);
|
||||
send(200, address.toJSON());
|
||||
}));
|
||||
|
||||
// Wallet Balance
|
||||
this.get('/wallet/:id/balance', con(function *(req, res, send, next) {
|
||||
var account = req.options.account;
|
||||
|
||||
@ -281,6 +281,14 @@ HTTPWallet.prototype.createAddress = function createAddress(account) {
|
||||
return this.client.createAddress(this.id, account);
|
||||
};
|
||||
|
||||
/**
|
||||
* @see Wallet#createAddress
|
||||
*/
|
||||
|
||||
HTTPWallet.prototype.createNested = function createNested(account) {
|
||||
return this.client.createNested(this.id, account);
|
||||
};
|
||||
|
||||
/**
|
||||
* @see Wallet#setPassphrase
|
||||
*/
|
||||
|
||||
@ -37,6 +37,7 @@ function KeyRing(options, network) {
|
||||
|
||||
this.network = bcoin.network.get();
|
||||
this.witness = false;
|
||||
this.nested = false;
|
||||
this.publicKey = constants.ZERO_KEY;
|
||||
this.privateKey = null;
|
||||
this.script = null;
|
||||
@ -82,6 +83,11 @@ KeyRing.prototype.fromOptions = function fromOptions(options, network) {
|
||||
this.witness = options.witness;
|
||||
}
|
||||
|
||||
if (options.nested != null) {
|
||||
assert(typeof options.nested === 'boolean');
|
||||
this.nested = options.nested;
|
||||
}
|
||||
|
||||
if (script)
|
||||
return this.fromScript(key, script, compressed, network);
|
||||
|
||||
@ -572,6 +578,8 @@ KeyRing.prototype.compile = function compile(hash, type, version) {
|
||||
*/
|
||||
|
||||
KeyRing.prototype.getHash = function getHash(enc) {
|
||||
if (this.nested)
|
||||
return this.getNestedHash(enc);
|
||||
if (this.script)
|
||||
return this.getScriptHash(enc);
|
||||
return this.getKeyHash(enc);
|
||||
@ -584,6 +592,8 @@ KeyRing.prototype.getHash = function getHash(enc) {
|
||||
*/
|
||||
|
||||
KeyRing.prototype.getAddress = function getAddress(enc) {
|
||||
if (this.nested)
|
||||
return this.getNestedAddress(enc);
|
||||
if (this.script)
|
||||
return this.getScriptAddress(enc);
|
||||
return this.getKeyAddress(enc);
|
||||
@ -704,14 +714,14 @@ KeyRing.prototype.verify = function verify(msg, sig) {
|
||||
* @returns {ScriptType}
|
||||
*/
|
||||
|
||||
KeyRing.prototype.getType = function getType() {
|
||||
if (this.program)
|
||||
return this.program.getType();
|
||||
KeyRing.prototype.getVersion = function getVersion() {
|
||||
if (!this.witness)
|
||||
return -1;
|
||||
|
||||
if (this.script)
|
||||
return this.script.getType();
|
||||
if (this.nested)
|
||||
return -1;
|
||||
|
||||
return scriptTypes.PUBKEYHASH;
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -719,14 +729,19 @@ KeyRing.prototype.getType = function getType() {
|
||||
* @returns {ScriptType}
|
||||
*/
|
||||
|
||||
KeyRing.prototype.getAddressType = function getAddressType() {
|
||||
KeyRing.prototype.getType = function getType() {
|
||||
if (this.nested)
|
||||
return scriptTypes.SCRIPTHASH;
|
||||
|
||||
if (this.witness) {
|
||||
if (this.script)
|
||||
return scriptTypes.WITNESSSCRIPTHASH;
|
||||
return scriptTypes.WITNESSPUBKEYHASH;
|
||||
}
|
||||
|
||||
if (this.script)
|
||||
return scriptTypes.SCRIPTHASH;
|
||||
|
||||
return scriptTypes.PUBKEYHASH;
|
||||
};
|
||||
|
||||
@ -738,6 +753,10 @@ KeyRing.prototype.__defineGetter__('type', function() {
|
||||
return this.getType();
|
||||
});
|
||||
|
||||
KeyRing.prototype.__defineGetter__('version', function() {
|
||||
return this.getVersion();
|
||||
});
|
||||
|
||||
KeyRing.prototype.__defineGetter__('scriptHash', function() {
|
||||
return this.getScriptHash();
|
||||
});
|
||||
@ -800,11 +819,12 @@ KeyRing.prototype.toJSON = function toJSON() {
|
||||
return {
|
||||
network: this.network.type,
|
||||
witness: this.witness,
|
||||
nested: this.nested,
|
||||
publicKey: this.publicKey.toString('hex'),
|
||||
script: this.script ? this.script.toRaw().toString('hex') : null,
|
||||
program: this.program ? this.program.toRaw().toString('hex') : null,
|
||||
type: constants.scriptTypesByVal[this.type].toLowerCase(),
|
||||
address: this.getAddress('base58'),
|
||||
nestedAddress: this.getNestedAddress('base58')
|
||||
address: this.getAddress('base58')
|
||||
};
|
||||
};
|
||||
|
||||
@ -818,11 +838,13 @@ KeyRing.prototype.fromJSON = function fromJSON(json) {
|
||||
assert(json);
|
||||
assert(typeof json.network === 'string');
|
||||
assert(typeof json.witness === 'boolean');
|
||||
assert(typeof json.nested === 'boolean');
|
||||
assert(typeof json.publicKey === 'string');
|
||||
assert(!json.script || typeof json.script === 'string');
|
||||
|
||||
this.nework = bcoin.network.get(json.network);
|
||||
this.witness = json.witness;
|
||||
this.nested = json.nested;
|
||||
this.publicKey = new Buffer(json.publicKey, 'hex');
|
||||
|
||||
if (json.script)
|
||||
@ -850,6 +872,7 @@ KeyRing.prototype.toRaw = function toRaw(writer) {
|
||||
var p = new BufferWriter(writer);
|
||||
|
||||
p.writeU8(this.witness ? 1 : 0);
|
||||
p.writeU8(this.nested ? 1 : 0);
|
||||
|
||||
if (this.privateKey) {
|
||||
p.writeVarBytes(this.privateKey);
|
||||
@ -881,6 +904,7 @@ KeyRing.prototype.fromRaw = function fromRaw(data, network) {
|
||||
|
||||
this.network = bcoin.network.get(network);
|
||||
this.witness = p.readU8() === 1;
|
||||
this.nested = p.readU8() === 1;
|
||||
|
||||
key = p.readVarBytes();
|
||||
|
||||
|
||||
@ -2344,7 +2344,9 @@ utils.isName = function isName(key) {
|
||||
if (typeof key !== 'string')
|
||||
return false;
|
||||
|
||||
// Maximum worst case size: 80 bytes
|
||||
if (!/^[\-\._0-9A-Za-z]+$/.test(key))
|
||||
return false;
|
||||
|
||||
return key.length >= 1 && key.length <= 40;
|
||||
};
|
||||
|
||||
|
||||
@ -16,6 +16,7 @@ var BufferWriter = require('../utils/writer');
|
||||
var Path = require('./path');
|
||||
var Script = require('../script/script');
|
||||
var WalletKey = require('./walletkey');
|
||||
var HD = require('../hd/hd');
|
||||
|
||||
/**
|
||||
* Represents a BIP44 Account belonging to a {@link Wallet}.
|
||||
@ -53,6 +54,7 @@ function Account(db, options) {
|
||||
|
||||
this.receive = null;
|
||||
this.change = null;
|
||||
this.nested = null;
|
||||
|
||||
this.wid = 0;
|
||||
this.id = null;
|
||||
@ -62,6 +64,7 @@ function Account(db, options) {
|
||||
this.accountIndex = 0;
|
||||
this.receiveDepth = 0;
|
||||
this.changeDepth = 0;
|
||||
this.nestedDepth = 0;
|
||||
this.type = Account.types.PUBKEYHASH;
|
||||
this.m = 1;
|
||||
this.n = 1;
|
||||
@ -105,7 +108,7 @@ Account.prototype.fromOptions = function fromOptions(options) {
|
||||
assert(options, 'Options are required.');
|
||||
assert(utils.isNumber(options.wid));
|
||||
assert(utils.isName(options.id), 'Bad Wallet ID.');
|
||||
assert(bcoin.hd.isHD(options.accountKey), 'Account key is required.');
|
||||
assert(HD.isHD(options.accountKey), 'Account key is required.');
|
||||
assert(utils.isNumber(options.accountIndex), 'Account index is required.');
|
||||
|
||||
this.wid = options.wid;
|
||||
@ -138,6 +141,11 @@ Account.prototype.fromOptions = function fromOptions(options) {
|
||||
this.changeDepth = options.changeDepth;
|
||||
}
|
||||
|
||||
if (options.nestedDepth != null) {
|
||||
assert(utils.isNumber(options.nestedDepth));
|
||||
this.nestedDepth = options.nestedDepth;
|
||||
}
|
||||
|
||||
if (options.type != null) {
|
||||
if (typeof options.type === 'string') {
|
||||
this.type = Account.types[options.type.toUpperCase()];
|
||||
@ -218,9 +226,10 @@ Account.prototype.init = co(function* init() {
|
||||
|
||||
assert(this.receiveDepth === 0);
|
||||
assert(this.changeDepth === 0);
|
||||
assert(this.nestedDepth === 0);
|
||||
|
||||
this.initialized = true;
|
||||
yield this.setDepth(1, 1);
|
||||
yield this.setDepth(1, 1, 1);
|
||||
});
|
||||
|
||||
/**
|
||||
@ -235,6 +244,9 @@ Account.prototype.open = function open() {
|
||||
this.receive = this.deriveReceive(this.receiveDepth - 1);
|
||||
this.change = this.deriveChange(this.changeDepth - 1);
|
||||
|
||||
if (this.witness)
|
||||
this.nested = this.deriveReceive(this.nestedDepth - 1);
|
||||
|
||||
return Promise.resolve(null);
|
||||
};
|
||||
|
||||
@ -249,10 +261,10 @@ Account.prototype.open = function open() {
|
||||
Account.prototype.pushKey = function pushKey(key) {
|
||||
var index;
|
||||
|
||||
if (bcoin.hd.isExtended(key))
|
||||
key = bcoin.hd.fromBase58(key);
|
||||
if (HD.isExtended(key))
|
||||
key = HD.fromBase58(key);
|
||||
|
||||
if (!bcoin.hd.isPublic(key))
|
||||
if (!HD.isPublic(key))
|
||||
throw new Error('Must add HD keys to wallet.');
|
||||
|
||||
if (!key.isAccount44())
|
||||
@ -283,10 +295,10 @@ Account.prototype.pushKey = function pushKey(key) {
|
||||
*/
|
||||
|
||||
Account.prototype.spliceKey = function spliceKey(key) {
|
||||
if (bcoin.hd.isExtended(key))
|
||||
key = bcoin.hd.fromBase58(key);
|
||||
if (HD.isExtended(key))
|
||||
key = HD.fromBase58(key);
|
||||
|
||||
if (!bcoin.hd.isHDPublicKey(key))
|
||||
if (!HD.isHDPublicKey(key))
|
||||
throw new Error('Must add HD keys to wallet.');
|
||||
|
||||
if (!key.isAccount44())
|
||||
@ -379,7 +391,7 @@ Account.prototype.removeKey = function removeKey(key) {
|
||||
*/
|
||||
|
||||
Account.prototype.createReceive = function createReceive() {
|
||||
return this.createKey(false);
|
||||
return this.createKey(0);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -388,7 +400,16 @@ Account.prototype.createReceive = function createReceive() {
|
||||
*/
|
||||
|
||||
Account.prototype.createChange = function createChange() {
|
||||
return this.createKey(true);
|
||||
return this.createKey(1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new change address (increments receiveDepth).
|
||||
* @returns {WalletKey}
|
||||
*/
|
||||
|
||||
Account.prototype.createNested = function createNested() {
|
||||
return this.createKey(2);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -397,23 +418,36 @@ Account.prototype.createChange = function createChange() {
|
||||
* @returns {Promise} - Returns {@link WalletKey}.
|
||||
*/
|
||||
|
||||
Account.prototype.createKey = co(function* createKey(change) {
|
||||
Account.prototype.createKey = co(function* createKey(branch) {
|
||||
var ring, lookahead;
|
||||
|
||||
if (change) {
|
||||
ring = this.deriveChange(this.changeDepth);
|
||||
lookahead = this.deriveChange(this.changeDepth + this.lookahead);
|
||||
yield this.saveKey(ring);
|
||||
yield this.saveKey(lookahead);
|
||||
this.changeDepth++;
|
||||
this.change = ring;
|
||||
} else {
|
||||
ring = this.deriveReceive(this.receiveDepth);
|
||||
lookahead = this.deriveReceive(this.receiveDepth + this.lookahead);
|
||||
yield this.saveKey(ring);
|
||||
yield this.saveKey(lookahead);
|
||||
this.receiveDepth++;
|
||||
this.receive = ring;
|
||||
switch (branch) {
|
||||
case 0:
|
||||
ring = this.deriveReceive(this.receiveDepth);
|
||||
lookahead = this.deriveReceive(this.receiveDepth + this.lookahead);
|
||||
yield this.saveKey(ring);
|
||||
yield this.saveKey(lookahead);
|
||||
this.receiveDepth++;
|
||||
this.receive = ring;
|
||||
break;
|
||||
case 1:
|
||||
ring = this.deriveChange(this.changeDepth);
|
||||
lookahead = this.deriveChange(this.changeDepth + this.lookahead);
|
||||
yield this.saveKey(ring);
|
||||
yield this.saveKey(lookahead);
|
||||
this.changeDepth++;
|
||||
this.change = ring;
|
||||
break;
|
||||
case 2:
|
||||
ring = this.deriveNested(this.nestedDepth);
|
||||
lookahead = this.deriveNested(this.nestedDepth + this.lookahead);
|
||||
yield this.saveKey(ring);
|
||||
yield this.saveKey(lookahead);
|
||||
this.nestedDepth++;
|
||||
this.nested = ring;
|
||||
break;
|
||||
default:
|
||||
throw new Error('Bad branch: ' + branch);
|
||||
}
|
||||
|
||||
this.save();
|
||||
@ -428,7 +462,7 @@ Account.prototype.createKey = co(function* createKey(change) {
|
||||
*/
|
||||
|
||||
Account.prototype.deriveReceive = function deriveReceive(index, master) {
|
||||
return this.deriveKey(false, index, master);
|
||||
return this.deriveKey(0, index, master);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -438,7 +472,20 @@ Account.prototype.deriveReceive = function deriveReceive(index, master) {
|
||||
*/
|
||||
|
||||
Account.prototype.deriveChange = function deriveChange(index, master) {
|
||||
return this.deriveKey(true, index, master);
|
||||
return this.deriveKey(1, index, master);
|
||||
};
|
||||
|
||||
/**
|
||||
* Derive a nested address at `index`. Do not increment depth.
|
||||
* @param {Number} index
|
||||
* @returns {WalletKey}
|
||||
*/
|
||||
|
||||
Account.prototype.deriveNested = function deriveNested(index, master) {
|
||||
if (!this.witness)
|
||||
throw new Error('Cannot derive nested on non-witness account.');
|
||||
|
||||
return this.deriveKey(2, index, master);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -485,7 +532,7 @@ Account.prototype.deriveKey = function deriveKey(branch, index, master) {
|
||||
var keys = [];
|
||||
var i, key, shared, ring, hash;
|
||||
|
||||
branch = +branch;
|
||||
assert(typeof branch === 'number');
|
||||
|
||||
if (master && master.key) {
|
||||
key = master.key.deriveAccount44(this.accountIndex);
|
||||
@ -516,22 +563,6 @@ Account.prototype.deriveKey = function deriveKey(branch, index, master) {
|
||||
return ring;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get address type.
|
||||
* @returns {ScriptType}
|
||||
*/
|
||||
|
||||
Account.prototype.getAddressType = function getAddressType() {
|
||||
if (this.witness) {
|
||||
if (this.type === Account.types.MULTISIG)
|
||||
return Script.types.WITNESSSCRIPTHASH;
|
||||
return Script.types.WITNESSPUBKEYHASH;
|
||||
}
|
||||
if (this.type === Account.types.MULTISIG)
|
||||
return Script.types.SCRIPTHASH;
|
||||
return Script.types.PUBKEYHASH;
|
||||
};
|
||||
|
||||
/**
|
||||
* Save the account to the database. Necessary
|
||||
* when address depth and keys change.
|
||||
@ -570,9 +601,9 @@ Account.prototype.savePath = function savePath(path) {
|
||||
* @returns {Promise} - Returns {@link WalletKey}, {@link WalletKey}.
|
||||
*/
|
||||
|
||||
Account.prototype.setDepth = co(function* setDepth(receiveDepth, changeDepth) {
|
||||
Account.prototype.setDepth = co(function* setDepth(receiveDepth, changeDepth, nestedDepth) {
|
||||
var i = -1;
|
||||
var receive, change, lookahead;
|
||||
var receive, change, nested, lookahead;
|
||||
|
||||
if (receiveDepth > this.receiveDepth) {
|
||||
for (i = this.receiveDepth; i < receiveDepth; i++) {
|
||||
@ -604,12 +635,27 @@ Account.prototype.setDepth = co(function* setDepth(receiveDepth, changeDepth) {
|
||||
this.changeDepth = changeDepth;
|
||||
}
|
||||
|
||||
if (this.witness && nestedDepth > this.nestedDepth) {
|
||||
for (i = this.nestedDepth; i < nestedDepth; i++) {
|
||||
nested = this.deriveNested(i);
|
||||
yield this.saveKey(nested);
|
||||
}
|
||||
|
||||
for (i = nestedDepth; i < nestedDepth + this.lookahead; i++) {
|
||||
lookahead = this.deriveNested(i);
|
||||
yield this.saveKey(lookahead);
|
||||
}
|
||||
|
||||
this.nested = nested;
|
||||
this.nestedDepth = nestedDepth;
|
||||
}
|
||||
|
||||
if (i === -1)
|
||||
return;
|
||||
|
||||
this.save();
|
||||
|
||||
return receive;
|
||||
return receive || nested;
|
||||
});
|
||||
|
||||
/**
|
||||
@ -629,13 +675,14 @@ Account.prototype.inspect = function inspect() {
|
||||
address: this.initialized
|
||||
? this.receive.getAddress()
|
||||
: null,
|
||||
nestedAddress: this.initialized
|
||||
? this.receive.getNestedAddress()
|
||||
nestedAddress: this.initialized && this.nested
|
||||
? this.nested.getAddress()
|
||||
: null,
|
||||
witness: this.witness,
|
||||
accountIndex: this.accountIndex,
|
||||
receiveDepth: this.receiveDepth,
|
||||
changeDepth: this.changeDepth,
|
||||
nestedDepth: this.nestedDepth,
|
||||
accountKey: this.accountKey.xpubkey,
|
||||
keys: this.keys.map(function(key) {
|
||||
return key.xpubkey;
|
||||
@ -662,11 +709,12 @@ Account.prototype.toJSON = function toJSON() {
|
||||
accountIndex: this.accountIndex,
|
||||
receiveDepth: this.receiveDepth,
|
||||
changeDepth: this.changeDepth,
|
||||
nestedDepth: this.nestedDepth,
|
||||
receiveAddress: this.receive
|
||||
? this.receive.getAddress('base58')
|
||||
: null,
|
||||
nestedAddress: this.receive
|
||||
? this.receive.getNestedAddress('base58')
|
||||
nestedAddress: this.nested
|
||||
? this.nested.getAddress('base58')
|
||||
: null,
|
||||
changeAddress: this.change
|
||||
? this.change.getAddress('base58')
|
||||
@ -699,6 +747,7 @@ Account.prototype.fromJSON = function fromJSON(json) {
|
||||
assert(utils.isNumber(json.accountIndex));
|
||||
assert(utils.isNumber(json.receiveDepth));
|
||||
assert(utils.isNumber(json.changeDepth));
|
||||
assert(utils.isNumber(json.nestedDepth));
|
||||
assert(Array.isArray(json.keys));
|
||||
|
||||
this.wid = json.wid;
|
||||
@ -711,12 +760,13 @@ Account.prototype.fromJSON = function fromJSON(json) {
|
||||
this.accountIndex = json.accountIndex;
|
||||
this.receiveDepth = json.receiveDepth;
|
||||
this.changeDepth = json.changeDepth;
|
||||
this.accountKey = bcoin.hd.fromBase58(json.accountKey);
|
||||
this.nestedDepth = json.nestedDepth;
|
||||
this.accountKey = HD.fromBase58(json.accountKey);
|
||||
|
||||
assert(this.type != null);
|
||||
|
||||
for (i = 0; i < json.keys.length; i++) {
|
||||
key = bcoin.hd.fromBase58(json.keys[i]);
|
||||
key = HD.fromBase58(json.keys[i]);
|
||||
this.pushKey(key);
|
||||
}
|
||||
|
||||
@ -733,7 +783,7 @@ Account.prototype.toRaw = function toRaw(writer) {
|
||||
var i, key;
|
||||
|
||||
p.writeU32(this.network.magic);
|
||||
p.writeVarString(this.name, 'utf8');
|
||||
p.writeVarString(this.name, 'ascii');
|
||||
p.writeU8(this.initialized ? 1 : 0);
|
||||
p.writeU8(this.type);
|
||||
p.writeU8(this.m);
|
||||
@ -742,6 +792,7 @@ Account.prototype.toRaw = function toRaw(writer) {
|
||||
p.writeU32(this.accountIndex);
|
||||
p.writeU32(this.receiveDepth);
|
||||
p.writeU32(this.changeDepth);
|
||||
p.writeU32(this.nestedDepth);
|
||||
p.writeBytes(this.accountKey.toRaw());
|
||||
p.writeU8(this.keys.length);
|
||||
|
||||
@ -768,7 +819,7 @@ Account.prototype.fromRaw = function fromRaw(data) {
|
||||
var i, count, key;
|
||||
|
||||
this.network = bcoin.network.fromMagic(p.readU32());
|
||||
this.name = p.readVarString('utf8');
|
||||
this.name = p.readVarString('ascii');
|
||||
this.initialized = p.readU8() === 1;
|
||||
this.type = p.readU8();
|
||||
this.m = p.readU8();
|
||||
@ -777,14 +828,15 @@ Account.prototype.fromRaw = function fromRaw(data) {
|
||||
this.accountIndex = p.readU32();
|
||||
this.receiveDepth = p.readU32();
|
||||
this.changeDepth = p.readU32();
|
||||
this.accountKey = bcoin.hd.fromRaw(p.readBytes(82));
|
||||
this.nestedDepth = p.readU32();
|
||||
this.accountKey = HD.fromRaw(p.readBytes(82));
|
||||
|
||||
assert(Account.typesByVal[this.type]);
|
||||
|
||||
count = p.readU8();
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
key = bcoin.hd.fromRaw(p.readBytes(82));
|
||||
key = HD.fromRaw(p.readBytes(82));
|
||||
this.pushKey(key);
|
||||
}
|
||||
|
||||
|
||||
@ -9,10 +9,11 @@
|
||||
var bcoin = require('../env');
|
||||
var utils = require('../utils/utils');
|
||||
var assert = utils.assert;
|
||||
var constants = bcoin.constants;
|
||||
var constants = require('../protocol/constants');
|
||||
var BufferReader = require('../utils/reader');
|
||||
var BufferWriter = require('../utils/writer');
|
||||
var Address = require('../primitives/address');
|
||||
var Script = require('../script/script');
|
||||
|
||||
/**
|
||||
* Path
|
||||
@ -43,7 +44,7 @@ function Path() {
|
||||
this.data = null;
|
||||
|
||||
// Currently unused.
|
||||
this.type = bcoin.script.types.PUBKEYHASH;
|
||||
this.type = Script.types.PUBKEYHASH;
|
||||
this.version = -1;
|
||||
this.hash = null; // Passed in by caller.
|
||||
}
|
||||
|
||||
@ -7,15 +7,19 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var bcoin = require('../env');
|
||||
var utils = require('../utils/utils');
|
||||
var Locker = require('../utils/locker');
|
||||
var LRU = require('../utils/lru');
|
||||
var spawn = require('../utils/spawn');
|
||||
var co = spawn.co;
|
||||
var assert = bcoin.utils.assert;
|
||||
var constants = bcoin.constants;
|
||||
var DUMMY = new Buffer([0]);
|
||||
var assert = utils.assert;
|
||||
var constants = require('../protocol/constants');
|
||||
var BufferReader = require('../utils/reader');
|
||||
var BufferWriter = require('../utils/writer');
|
||||
var TX = require('../primitives/tx');
|
||||
var Coin = require('../primitives/coin');
|
||||
var Outpoint = require('../primitives/outpoint');
|
||||
var DUMMY = new Buffer([0]);
|
||||
|
||||
/*
|
||||
* Database Layout:
|
||||
@ -211,8 +215,8 @@ function TXDB(wallet) {
|
||||
this.options = wallet.db.options;
|
||||
this.locked = {};
|
||||
|
||||
this.locker = new bcoin.locker();
|
||||
this.coinCache = new bcoin.lru(10000);
|
||||
this.locker = new Locker();
|
||||
this.coinCache = new LRU(10000);
|
||||
|
||||
this.current = null;
|
||||
this.balance = null;
|
||||
@ -446,7 +450,7 @@ TXDB.prototype.getOrphans = co(function* getOrphans(hash, index) {
|
||||
inputs = [];
|
||||
|
||||
while (p.left())
|
||||
inputs.push(bcoin.outpoint.fromRaw(p));
|
||||
inputs.push(Outpoint.fromRaw(p));
|
||||
|
||||
for (i = 0; i < inputs.length; i++) {
|
||||
input = inputs[i];
|
||||
@ -558,7 +562,7 @@ TXDB.prototype.resolveOrphans = co(function* resolveOrphans(tx, index) {
|
||||
|
||||
this.del(layout.o(hash, index));
|
||||
|
||||
coin = bcoin.coin.fromTX(tx, index);
|
||||
coin = Coin.fromTX(tx, index);
|
||||
|
||||
// Add input to orphan
|
||||
for (i = 0; i < orphans.length; i++) {
|
||||
@ -677,7 +681,7 @@ TXDB.prototype._add = co(function* add(tx, info) {
|
||||
key = prevout.hash + prevout.index;
|
||||
|
||||
// s[outpoint-key] -> [spender-hash]|[spender-input-index]
|
||||
spender = bcoin.outpoint.fromTX(tx, i).toRaw();
|
||||
spender = Outpoint.fromTX(tx, i).toRaw();
|
||||
this.put(layout.s(prevout.hash, prevout.index), spender);
|
||||
|
||||
// Add orphan, if no parent transaction is yet known
|
||||
@ -722,7 +726,7 @@ TXDB.prototype._add = co(function* add(tx, info) {
|
||||
if (orphans)
|
||||
continue;
|
||||
|
||||
coin = bcoin.coin.fromTX(tx, i);
|
||||
coin = Coin.fromTX(tx, i);
|
||||
this.balance.add(coin);
|
||||
coin = coin.toRaw();
|
||||
|
||||
@ -873,7 +877,7 @@ TXDB.prototype.isSpent = co(function* isSpent(hash, index) {
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
return bcoin.outpoint.fromRaw(data);
|
||||
return Outpoint.fromRaw(data);
|
||||
});
|
||||
|
||||
/**
|
||||
@ -1103,7 +1107,7 @@ TXDB.prototype.__remove = co(function* remove(tx, info) {
|
||||
if (!path)
|
||||
continue;
|
||||
|
||||
coin = bcoin.coin.fromTX(tx, i);
|
||||
coin = Coin.fromTX(tx, i);
|
||||
|
||||
this.balance.sub(coin);
|
||||
|
||||
@ -1321,7 +1325,7 @@ TXDB.prototype.getLocked = function getLocked() {
|
||||
key = keys[i];
|
||||
hash = key.slice(0, 64);
|
||||
index = +key.slice(64);
|
||||
outpoint = new bcoin.outpoint(hash, index);
|
||||
outpoint = new Outpoint(hash, index);
|
||||
outpoints.push(outpoint);
|
||||
}
|
||||
|
||||
@ -1395,10 +1399,10 @@ TXDB.prototype.getOutpoints = function getOutpoints(account) {
|
||||
parse: function(key) {
|
||||
if (account != null) {
|
||||
key = layout.Cc(key);
|
||||
return new bcoin.outpoint(key[1], key[2]);
|
||||
return new Outpoint(key[1], key[2]);
|
||||
}
|
||||
key = layout.cc(key);
|
||||
return new bcoin.outpoint(key[0], key[1]);
|
||||
return new Outpoint(key[0], key[1]);
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -1563,7 +1567,7 @@ TXDB.prototype.getHistory = function getHistory(account) {
|
||||
return this.values({
|
||||
gte: layout.t(constants.NULL_HASH),
|
||||
lte: layout.t(constants.HIGH_HASH),
|
||||
parse: bcoin.tx.fromExtended
|
||||
parse: TX.fromExtended
|
||||
});
|
||||
};
|
||||
|
||||
@ -1638,7 +1642,7 @@ TXDB.prototype.getCoins = function getCoins(account) {
|
||||
var parts = layout.cc(key);
|
||||
var hash = parts[0];
|
||||
var index = parts[1];
|
||||
var coin = bcoin.coin.fromRaw(value);
|
||||
var coin = Coin.fromRaw(value);
|
||||
coin.hash = hash;
|
||||
coin.index = index;
|
||||
key = hash + index;
|
||||
@ -1691,7 +1695,7 @@ TXDB.prototype.fillHistory = function fillHistory(tx) {
|
||||
lte: layout.d(hash, 0xffffffff),
|
||||
parse: function(key, value) {
|
||||
var index = layout.dd(key)[1];
|
||||
var coin = bcoin.coin.fromRaw(value);
|
||||
var coin = Coin.fromRaw(value);
|
||||
var input = tx.inputs[index];
|
||||
coin.hash = input.prevout.hash;
|
||||
coin.index = input.prevout.index;
|
||||
@ -1740,7 +1744,7 @@ TXDB.prototype.getTX = co(function* getTX(hash) {
|
||||
if (!tx)
|
||||
return;
|
||||
|
||||
return bcoin.tx.fromExtended(tx);
|
||||
return TX.fromExtended(tx);
|
||||
});
|
||||
|
||||
/**
|
||||
@ -1817,7 +1821,7 @@ TXDB.prototype.getCoin = co(function* getCoin(hash, index) {
|
||||
var coin;
|
||||
|
||||
if (data) {
|
||||
coin = bcoin.coin.fromRaw(data);
|
||||
coin = Coin.fromRaw(data);
|
||||
coin.hash = hash;
|
||||
coin.index = index;
|
||||
return coin;
|
||||
@ -1828,7 +1832,7 @@ TXDB.prototype.getCoin = co(function* getCoin(hash, index) {
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
coin = bcoin.coin.fromRaw(data);
|
||||
coin = Coin.fromRaw(data);
|
||||
coin.hash = hash;
|
||||
coin.index = index;
|
||||
|
||||
@ -1851,7 +1855,7 @@ TXDB.prototype.getSpentCoin = co(function* getSpentCoin(spent, prevout) {
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
coin = bcoin.coin.fromRaw(data);
|
||||
coin = Coin.fromRaw(data);
|
||||
coin.hash = prevout.hash;
|
||||
coin.index = prevout.index;
|
||||
|
||||
@ -1866,7 +1870,7 @@ TXDB.prototype.getSpentCoin = co(function* getSpentCoin(spent, prevout) {
|
||||
*/
|
||||
|
||||
TXDB.prototype.updateSpentCoin = co(function* updateSpentCoin(tx, i) {
|
||||
var prevout = bcoin.outpoint.fromTX(tx, i);
|
||||
var prevout = Outpoint.fromTX(tx, i);
|
||||
var spent = yield this.isSpent(prevout.hash, prevout.index);
|
||||
var coin;
|
||||
|
||||
@ -2002,7 +2006,7 @@ TXDB.prototype._zap = co(function* zap(account, age) {
|
||||
|
||||
txs = yield this.getRange(account, {
|
||||
start: 0,
|
||||
end: bcoin.now() - age
|
||||
end: utils.now() - age
|
||||
});
|
||||
|
||||
for (i = 0; i < txs.length; i++) {
|
||||
|
||||
@ -9,8 +9,9 @@
|
||||
|
||||
var bcoin = require('../env');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var constants = bcoin.constants;
|
||||
var constants = require('../protocol/constants');
|
||||
var utils = require('../utils/utils');
|
||||
var Locker = require('../utils/locker');
|
||||
var spawn = require('../utils/spawn');
|
||||
var co = spawn.co;
|
||||
var crypto = require('../crypto/crypto');
|
||||
@ -20,7 +21,12 @@ var BufferWriter = require('../utils/writer');
|
||||
var TXDB = require('./txdb');
|
||||
var Path = require('./path');
|
||||
var Address = require('../primitives/address');
|
||||
var MTX = require('../primitives/mtx');
|
||||
var WalletKey = require('./walletkey');
|
||||
var HD = require('../hd/hd');
|
||||
var Account = require('./account');
|
||||
var Input = require('../primitives/input');
|
||||
var Output = require('../primitives/output');
|
||||
|
||||
/**
|
||||
* BIP44 Wallet
|
||||
@ -58,8 +64,8 @@ function Wallet(db, options) {
|
||||
this.db = db;
|
||||
this.network = db.network;
|
||||
this.logger = db.logger;
|
||||
this.writeLock = new bcoin.locker();
|
||||
this.fundLock = new bcoin.locker();
|
||||
this.writeLock = new Locker();
|
||||
this.fundLock = new Locker();
|
||||
|
||||
this.wid = 0;
|
||||
this.id = null;
|
||||
@ -89,12 +95,12 @@ Wallet.prototype.fromOptions = function fromOptions(options) {
|
||||
var id, token;
|
||||
|
||||
if (!master)
|
||||
master = bcoin.hd.fromMnemonic(null, this.network);
|
||||
master = HD.fromMnemonic(null, this.network);
|
||||
|
||||
if (!bcoin.hd.isHD(master) && !MasterKey.isMasterKey(master))
|
||||
master = bcoin.hd.from(master, this.network);
|
||||
if (!HD.isHD(master) && !MasterKey.isMasterKey(master))
|
||||
master = HD.from(master, this.network);
|
||||
|
||||
if (bcoin.hd.isHD(master))
|
||||
if (HD.isHD(master))
|
||||
master = MasterKey.fromKey(master);
|
||||
|
||||
assert(MasterKey.isMasterKey(master));
|
||||
@ -681,7 +687,7 @@ Wallet.prototype.hasAccount = function hasAccount(account) {
|
||||
*/
|
||||
|
||||
Wallet.prototype.createReceive = function createReceive(account) {
|
||||
return this.createKey(account, false);
|
||||
return this.createKey(account, 0);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -691,7 +697,17 @@ Wallet.prototype.createReceive = function createReceive(account) {
|
||||
*/
|
||||
|
||||
Wallet.prototype.createChange = function createChange(account) {
|
||||
return this.createKey(account, true);
|
||||
return this.createKey(account, 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new nested address (increments receiveDepth).
|
||||
* @param {(Number|String)?} account
|
||||
* @returns {Promise} - Returns {@link WalletKey}.
|
||||
*/
|
||||
|
||||
Wallet.prototype.createNested = function createNested(account) {
|
||||
return this.createKey(account, 2);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -893,7 +909,7 @@ Wallet.prototype._importKey = co(function* importKey(account, ring, passphrase)
|
||||
if (!account)
|
||||
throw new Error('Account not found.');
|
||||
|
||||
if (account.type !== bcoin.account.types.PUBKEYHASH)
|
||||
if (account.type !== Account.types.PUBKEYHASH)
|
||||
throw new Error('Cannot import into non-pkh account.');
|
||||
|
||||
yield this.unlock(passphrase);
|
||||
@ -949,7 +965,7 @@ Wallet.prototype.importAddress = co(function* importAddress(account, address) {
|
||||
Wallet.prototype._importAddress = co(function* importAddress(account, address) {
|
||||
var exists, path;
|
||||
|
||||
if (account instanceof Address) {
|
||||
if (!address) {
|
||||
address = account;
|
||||
account = null;
|
||||
}
|
||||
@ -957,7 +973,7 @@ Wallet.prototype._importAddress = co(function* importAddress(account, address) {
|
||||
if (account == null)
|
||||
account = 0;
|
||||
|
||||
exists = yield this.getPath(address.getHash('hex'));
|
||||
exists = yield this.getPath(address);
|
||||
|
||||
if (exists)
|
||||
throw new Error('Address already exists.');
|
||||
@ -967,7 +983,7 @@ Wallet.prototype._importAddress = co(function* importAddress(account, address) {
|
||||
if (!account)
|
||||
throw new Error('Account not found.');
|
||||
|
||||
if (account.type !== bcoin.account.types.PUBKEYHASH)
|
||||
if (account.type !== Account.types.PUBKEYHASH)
|
||||
throw new Error('Cannot import into non-pkh account.');
|
||||
|
||||
path = Path.fromAddress(account, address);
|
||||
@ -1093,7 +1109,7 @@ Wallet.prototype.createTX = co(function* createTX(options, force) {
|
||||
throw new Error('No outputs.');
|
||||
|
||||
// Create mutable tx
|
||||
tx = bcoin.mtx();
|
||||
tx = new MTX();
|
||||
|
||||
// Add the outputs
|
||||
for (i = 0; i < outputs.length; i++)
|
||||
@ -1255,7 +1271,7 @@ Wallet.prototype.getInputPaths = co(function* getInputPaths(tx) {
|
||||
var hashes = [];
|
||||
var i, hash, path;
|
||||
|
||||
if (tx instanceof bcoin.input) {
|
||||
if (tx instanceof Input) {
|
||||
if (!tx.coin)
|
||||
throw new Error('Not all coins available.');
|
||||
|
||||
@ -1293,7 +1309,7 @@ Wallet.prototype.getOutputPaths = co(function* getOutputPaths(tx) {
|
||||
var hashes = [];
|
||||
var i, hash, path;
|
||||
|
||||
if (tx instanceof bcoin.output) {
|
||||
if (tx instanceof Output) {
|
||||
hash = tx.getHash('hex');
|
||||
if (hash)
|
||||
hashes.push(hash);
|
||||
@ -1337,10 +1353,10 @@ Wallet.prototype.syncOutputDepth = co(function* syncOutputDepth(info) {
|
||||
*/
|
||||
|
||||
Wallet.prototype._syncOutputDepth = co(function* syncOutputDepth(info) {
|
||||
var receive = [];
|
||||
var derived = [];
|
||||
var accounts = {};
|
||||
var i, j, path, paths, account;
|
||||
var receiveDepth, changeDepth, ring;
|
||||
var receive, change, nested, ring;
|
||||
|
||||
this.start();
|
||||
|
||||
@ -1361,43 +1377,52 @@ Wallet.prototype._syncOutputDepth = co(function* syncOutputDepth(info) {
|
||||
for (i = 0; i < accounts.length; i++) {
|
||||
paths = accounts[i];
|
||||
account = paths[0].account;
|
||||
receiveDepth = -1;
|
||||
changeDepth = -1;
|
||||
receive = -1;
|
||||
change = -1;
|
||||
nested = -1;
|
||||
|
||||
for (j = 0; j < paths.length; j++) {
|
||||
path = paths[j];
|
||||
|
||||
if (path.branch) {
|
||||
if (path.index > changeDepth)
|
||||
changeDepth = path.index;
|
||||
} else {
|
||||
if (path.index > receiveDepth)
|
||||
receiveDepth = path.index;
|
||||
switch (path.branch) {
|
||||
case 0:
|
||||
if (path.index > receive)
|
||||
receive = path.index;
|
||||
break;
|
||||
case 1:
|
||||
if (path.index > change)
|
||||
change = path.index;
|
||||
break;
|
||||
case 2:
|
||||
if (path.index > nested)
|
||||
nested = path.index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
receiveDepth += 2;
|
||||
changeDepth += 2;
|
||||
receive += 2;
|
||||
change += 2;
|
||||
nested += 2;
|
||||
|
||||
account = yield this.getAccount(account);
|
||||
|
||||
if (!account)
|
||||
continue;
|
||||
|
||||
ring = yield account.setDepth(receiveDepth, changeDepth);
|
||||
ring = yield account.setDepth(receive, change, nested);
|
||||
|
||||
if (ring)
|
||||
receive.push(ring);
|
||||
derived.push(ring);
|
||||
}
|
||||
|
||||
yield this.commit();
|
||||
|
||||
if (receive.length > 0) {
|
||||
this.db.emit('address', this.id, receive);
|
||||
this.emit('address', receive);
|
||||
if (derived.length > 0) {
|
||||
this.db.emit('address', this.id, derived);
|
||||
this.emit('address', derived);
|
||||
}
|
||||
|
||||
return receive;
|
||||
return derived;
|
||||
});
|
||||
|
||||
/**
|
||||
@ -1773,9 +1798,9 @@ Wallet.prototype.getProgram = function getProgram() {
|
||||
*/
|
||||
|
||||
Wallet.prototype.getNestedHash = function getNestedHash(enc) {
|
||||
if (!this.receive)
|
||||
if (!this.nested)
|
||||
return;
|
||||
return this.receive.getNestedHash(enc);
|
||||
return this.nested.getHash(enc);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1786,9 +1811,9 @@ Wallet.prototype.getNestedHash = function getNestedHash(enc) {
|
||||
*/
|
||||
|
||||
Wallet.prototype.getNestedAddress = function getNestedAddress(enc) {
|
||||
if (!this.receive)
|
||||
if (!this.nested)
|
||||
return;
|
||||
return this.receive.getNestedAddress(enc);
|
||||
return this.nested.getAddress(enc);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1903,6 +1928,12 @@ Wallet.prototype.__defineGetter__('changeDepth', function() {
|
||||
return this.account.changeDepth;
|
||||
});
|
||||
|
||||
Wallet.prototype.__defineGetter__('nestedDepth', function() {
|
||||
if (!this.account)
|
||||
return -1;
|
||||
return this.account.nestedDepth;
|
||||
});
|
||||
|
||||
Wallet.prototype.__defineGetter__('accountKey', function() {
|
||||
if (!this.account)
|
||||
return;
|
||||
@ -1921,6 +1952,12 @@ Wallet.prototype.__defineGetter__('change', function() {
|
||||
return this.account.change;
|
||||
});
|
||||
|
||||
Wallet.prototype.__defineGetter__('nested', function() {
|
||||
if (!this.account)
|
||||
return;
|
||||
return this.account.nested;
|
||||
});
|
||||
|
||||
/**
|
||||
* Convert the wallet to a more inspection-friendly object.
|
||||
* @returns {Object}
|
||||
@ -1997,7 +2034,7 @@ Wallet.prototype.toRaw = function toRaw(writer) {
|
||||
|
||||
p.writeU32(this.network.magic);
|
||||
p.writeU32(this.wid);
|
||||
p.writeVarString(this.id, 'utf8');
|
||||
p.writeVarString(this.id, 'ascii');
|
||||
p.writeU8(this.initialized ? 1 : 0);
|
||||
p.writeU32(this.accountDepth);
|
||||
p.writeBytes(this.token);
|
||||
@ -2020,7 +2057,7 @@ Wallet.prototype.fromRaw = function fromRaw(data) {
|
||||
var p = new BufferReader(data);
|
||||
this.network = bcoin.network.fromMagic(p.readU32());
|
||||
this.wid = p.readU32();
|
||||
this.id = p.readVarString('utf8');
|
||||
this.id = p.readVarString('ascii');
|
||||
this.initialized = p.readU8() === 1;
|
||||
this.accountDepth = p.readU32();
|
||||
this.token = p.readBytes(32);
|
||||
@ -2083,7 +2120,7 @@ function MasterKey(options) {
|
||||
this.timer = null;
|
||||
this.until = 0;
|
||||
this._destroy = this.destroy.bind(this);
|
||||
this.locker = new bcoin.locker(this);
|
||||
this.locker = new Locker(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2111,7 +2148,7 @@ MasterKey.prototype.fromOptions = function fromOptions(options) {
|
||||
}
|
||||
|
||||
if (options.key) {
|
||||
assert(bcoin.hd.isHD(options.key));
|
||||
assert(HD.isHD(options.key));
|
||||
this.key = options.key;
|
||||
}
|
||||
|
||||
@ -2167,7 +2204,7 @@ MasterKey.prototype._unlock = co(function* _unlock(passphrase, timeout) {
|
||||
key = yield crypto.derive(passphrase);
|
||||
data = crypto.decipher(this.ciphertext, key, this.iv);
|
||||
|
||||
this.key = bcoin.hd.fromExtended(data);
|
||||
this.key = HD.fromExtended(data);
|
||||
|
||||
this.start(timeout);
|
||||
|
||||
@ -2305,7 +2342,7 @@ MasterKey.prototype._decrypt = co(function* decrypt(passphrase) {
|
||||
|
||||
data = yield crypto.decrypt(this.ciphertext, passphrase, this.iv);
|
||||
|
||||
this.key = bcoin.hd.fromExtended(data);
|
||||
this.key = HD.fromExtended(data);
|
||||
this.encrypted = false;
|
||||
this.iv = null;
|
||||
this.ciphertext = null;
|
||||
@ -2418,7 +2455,7 @@ MasterKey.prototype.fromRaw = function fromRaw(raw) {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.key = bcoin.hd.fromExtended(p.readVarBytes());
|
||||
this.key = HD.fromExtended(p.readVarBytes());
|
||||
|
||||
return this;
|
||||
};
|
||||
@ -2503,7 +2540,7 @@ MasterKey.prototype.fromJSON = function fromJSON(json) {
|
||||
this.iv = new Buffer(json.iv, 'hex');
|
||||
this.ciphertext = new Buffer(json.ciphertext, 'hex');
|
||||
} else {
|
||||
this.key = bcoin.hd.fromJSON(json.key);
|
||||
this.key = HD.fromJSON(json.key);
|
||||
}
|
||||
|
||||
return this;
|
||||
|
||||
@ -11,15 +11,20 @@ var bcoin = require('../env');
|
||||
var AsyncObject = require('../utils/async');
|
||||
var utils = require('../utils/utils');
|
||||
var spawn = require('../utils/spawn');
|
||||
var Locker = require('../utils/locker');
|
||||
var LRU = require('../utils/lru');
|
||||
var co = spawn.co;
|
||||
var crypto = require('../crypto/crypto');
|
||||
var assert = utils.assert;
|
||||
var constants = bcoin.constants;
|
||||
var constants = require('../protocol/constants');
|
||||
var BufferReader = require('../utils/reader');
|
||||
var BufferWriter = require('../utils/writer');
|
||||
var Path = require('./path');
|
||||
var Script = require('../script/script');
|
||||
var MAX_POINT = String.fromCharCode(0xdbff, 0xdfff); // U+10FFFF
|
||||
var Wallet = require('./wallet');
|
||||
var Account = require('./account');
|
||||
var ldb = require('../db/ldb');
|
||||
var Bloom = require('../utils/bloom');
|
||||
|
||||
/*
|
||||
* Database Layout:
|
||||
@ -65,15 +70,15 @@ var layout = {
|
||||
return key.readUInt32BE(1, true);
|
||||
},
|
||||
l: function(id) {
|
||||
var len = Buffer.byteLength(id, 'utf8');
|
||||
var len = Buffer.byteLength(id, 'ascii');
|
||||
var key = new Buffer(1 + len);
|
||||
key[0] = 0x6c;
|
||||
if (len > 0)
|
||||
key.write(id, 1, 'utf8');
|
||||
key.write(id, 1, 'ascii');
|
||||
return key;
|
||||
},
|
||||
ll: function(key) {
|
||||
return key.toString('utf8', 1);
|
||||
return key.toString('ascii', 1);
|
||||
},
|
||||
a: function a(wid, index) {
|
||||
var key = new Buffer(9);
|
||||
@ -83,16 +88,16 @@ var layout = {
|
||||
return key;
|
||||
},
|
||||
i: function i(wid, name) {
|
||||
var len = Buffer.byteLength(name, 'utf8');
|
||||
var len = Buffer.byteLength(name, 'ascii');
|
||||
var key = new Buffer(5 + len);
|
||||
key[0] = 0x69;
|
||||
key.writeUInt32BE(wid, 1, true);
|
||||
if (len > 0)
|
||||
key.write(name, 5, 'utf8');
|
||||
key.write(name, 5, 'ascii');
|
||||
return key;
|
||||
},
|
||||
ii: function ii(key) {
|
||||
return [key.readUInt32BE(1, true), key.toString('utf8', 5)];
|
||||
return [key.readUInt32BE(1, true), key.toString('ascii', 5)];
|
||||
},
|
||||
R: new Buffer([0x52]),
|
||||
b: function b(hash) {
|
||||
@ -147,15 +152,15 @@ function WalletDB(options) {
|
||||
|
||||
// We need one read lock for `get` and `create`.
|
||||
// It will hold locks specific to wallet ids.
|
||||
this.readLock = new bcoin.locker.mapped();
|
||||
this.writeLock = new bcoin.locker();
|
||||
this.txLock = new bcoin.locker();
|
||||
this.readLock = new Locker.mapped();
|
||||
this.writeLock = new Locker();
|
||||
this.txLock = new Locker();
|
||||
|
||||
this.widCache = new bcoin.lru(10000);
|
||||
this.indexCache = new bcoin.lru(10000);
|
||||
this.accountCache = new bcoin.lru(10000);
|
||||
this.pathCache = new bcoin.lru(100000);
|
||||
this.pathMapCache = new bcoin.lru(100000);
|
||||
this.widCache = new LRU(10000);
|
||||
this.indexCache = new LRU(10000);
|
||||
this.accountCache = new LRU(10000);
|
||||
this.pathCache = new LRU(100000);
|
||||
this.pathMapCache = new LRU(100000);
|
||||
|
||||
// Try to optimize for up to 1m addresses.
|
||||
// We use a regular bloom filter here
|
||||
@ -164,10 +169,10 @@ function WalletDB(options) {
|
||||
// degrades.
|
||||
// Memory used: 1.7mb
|
||||
this.filter = this.options.useFilter !== false
|
||||
? bcoin.bloom.fromRate(1000000, 0.001, -1)
|
||||
? Bloom.fromRate(1000000, 0.001, -1)
|
||||
: null;
|
||||
|
||||
this.db = bcoin.ldb({
|
||||
this.db = ldb({
|
||||
location: this.options.location,
|
||||
db: this.options.db,
|
||||
maxOpenFiles: this.options.maxFiles,
|
||||
@ -489,7 +494,7 @@ WalletDB.prototype._get = co(function* get(wid) {
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
wallet = bcoin.wallet.fromRaw(this, data);
|
||||
wallet = Wallet.fromRaw(this, data);
|
||||
|
||||
this.register(wallet);
|
||||
|
||||
@ -679,7 +684,7 @@ WalletDB.prototype._create = co(function* create(options) {
|
||||
if (exists)
|
||||
throw new Error('Wallet already exists.');
|
||||
|
||||
wallet = bcoin.wallet.fromOptions(this, options);
|
||||
wallet = Wallet.fromOptions(this, options);
|
||||
wallet.wid = this.depth++;
|
||||
|
||||
this.register(wallet);
|
||||
@ -760,7 +765,7 @@ WalletDB.prototype._getAccount = co(function* getAccount(wid, index) {
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
account = bcoin.account.fromRaw(this, data);
|
||||
account = Account.fromRaw(this, data);
|
||||
|
||||
this.accountCache.set(key, account);
|
||||
|
||||
@ -778,8 +783,8 @@ WalletDB.prototype.getAccounts = co(function* getAccounts(wid) {
|
||||
var i, items, item, name, index, accounts;
|
||||
|
||||
items = yield this.db.range({
|
||||
gte: layout.i(wid, ''),
|
||||
lte: layout.i(wid, MAX_POINT)
|
||||
gte: layout.i(wid, '\x00'),
|
||||
lte: layout.i(wid, '\xff')
|
||||
});
|
||||
|
||||
for (i = 0; i < items.length; i++) {
|
||||
@ -888,7 +893,7 @@ WalletDB.prototype.createAccount = co(function* createAccount(options) {
|
||||
if (exists)
|
||||
throw new Error('Account already exists.');
|
||||
|
||||
account = bcoin.account.fromOptions(this, options);
|
||||
account = Account.fromOptions(this, options);
|
||||
|
||||
yield account.init();
|
||||
|
||||
@ -959,14 +964,9 @@ WalletDB.prototype.getWalletsByHash = co(function* getWalletsByHash(hash) {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletDB.prototype.saveKey = co(function* saveKey(wid, ring) {
|
||||
yield this.savePath(wid, ring.toPath());
|
||||
|
||||
if (!ring.witness)
|
||||
return;
|
||||
|
||||
yield this.savePath(wid, ring.toNestedPath());
|
||||
});
|
||||
WalletDB.prototype.saveKey = function saveKey(wid, ring) {
|
||||
return this.savePath(wid, ring.toPath());
|
||||
};
|
||||
|
||||
/**
|
||||
* Save a path to the path map.
|
||||
@ -1143,8 +1143,8 @@ WalletDB.prototype.getWalletPaths = co(function* getWalletPaths(wid) {
|
||||
|
||||
WalletDB.prototype.getWallets = function getWallets() {
|
||||
return this.db.keys({
|
||||
gte: layout.l(''),
|
||||
lte: layout.l(MAX_POINT),
|
||||
gte: layout.l('\x00'),
|
||||
lte: layout.l('\xff'),
|
||||
parse: layout.ll
|
||||
});
|
||||
};
|
||||
@ -1255,7 +1255,7 @@ WalletDB.prototype.resend = co(function* resend() {
|
||||
if (!data)
|
||||
continue;
|
||||
|
||||
tx = bcoin.tx.fromExtended(data);
|
||||
tx = TX.fromExtended(data);
|
||||
|
||||
this.emit('send', tx);
|
||||
}
|
||||
@ -1852,7 +1852,7 @@ Details.prototype._insert = function _insert(vector, target, table) {
|
||||
io = vector[i];
|
||||
member = new DetailsMember();
|
||||
|
||||
if (io instanceof bcoin.input)
|
||||
if (io.prevout)
|
||||
member.value = io.coin ? io.coin.value : 0;
|
||||
else
|
||||
member.value = io.value;
|
||||
|
||||
@ -129,8 +129,10 @@ WalletKey.prototype.toJSON = function toJSON() {
|
||||
return {
|
||||
network: this.network.type,
|
||||
witness: this.witness,
|
||||
nested: this.nested,
|
||||
publicKey: this.publicKey.toString('hex'),
|
||||
script: this.script ? this.script.toRaw().toString('hex') : null,
|
||||
program: this.program ? this.program.toRaw().toString('hex') : null,
|
||||
type: constants.scriptTypesByVal[this.type].toLowerCase(),
|
||||
wid: this.wid,
|
||||
id: this.id,
|
||||
@ -138,8 +140,7 @@ WalletKey.prototype.toJSON = function toJSON() {
|
||||
account: this.account,
|
||||
branch: this.branch,
|
||||
index: this.index,
|
||||
address: this.getAddress('base58'),
|
||||
nestedAddress: this.getNestedAddress('base58')
|
||||
address: this.getAddress('base58')
|
||||
};
|
||||
};
|
||||
|
||||
@ -178,6 +179,7 @@ WalletKey.prototype.fromHD = function fromHD(account, key, branch, index) {
|
||||
this.branch = branch;
|
||||
this.index = index;
|
||||
this.witness = account.witness;
|
||||
this.nested = branch === 2;
|
||||
|
||||
if (key.privateKey)
|
||||
return this.fromPrivate(key.privateKey, key.network);
|
||||
@ -266,7 +268,7 @@ WalletKey.isWalletKey = function isWalletKey(obj) {
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
WalletKey.prototype.toPath = function toPath(nested) {
|
||||
WalletKey.prototype.toPath = function toPath() {
|
||||
var path = new Path();
|
||||
|
||||
path.id = this.id;
|
||||
@ -286,30 +288,13 @@ WalletKey.prototype.toPath = function toPath(nested) {
|
||||
|
||||
path.keyType = this.keyType;
|
||||
|
||||
if (nested) {
|
||||
assert(this.witness);
|
||||
path.version = -1;
|
||||
path.type = Script.types.SCRIPTHASH;
|
||||
path.hash = this.getNestedHash('hex');
|
||||
} else {
|
||||
path.version = this.witness ? 0 : -1;
|
||||
path.type = this.getAddressType();
|
||||
path.hash = this.getHash('hex');
|
||||
}
|
||||
path.version = this.getVersion();
|
||||
path.type = this.getType();
|
||||
path.hash = this.getHash('hex');
|
||||
|
||||
return path;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether an object is a WalletKey.
|
||||
* @param {Object} obj
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
WalletKey.prototype.toNestedPath = function toNestedPath() {
|
||||
return this.toPath(true);
|
||||
};
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
@ -507,6 +507,9 @@ describe('Wallet', function() {
|
||||
var flags = bcoin.constants.flags.STANDARD_VERIFY_FLAGS;
|
||||
var options, w1, w2, w3, receive, b58, addr, paddr, utx, send, change;
|
||||
|
||||
var rec = bullshitNesting ? 'nested' : 'receive';
|
||||
var depth = bullshitNesting ? 'nestedDepth' : 'receiveDepth';
|
||||
|
||||
if (witness)
|
||||
flags |= bcoin.constants.flags.VERIFY_WITNESS;
|
||||
|
||||
@ -531,17 +534,21 @@ describe('Wallet', function() {
|
||||
yield w3.addKey(w2.accountKey);
|
||||
|
||||
// Our p2sh address
|
||||
b58 = w1.getAddress('base58');
|
||||
b58 = w1[rec].getAddress('base58');
|
||||
addr = bcoin.address.fromBase58(b58);
|
||||
|
||||
if (witness)
|
||||
assert.equal(addr.type, scriptTypes.WITNESSSCRIPTHASH);
|
||||
else
|
||||
if (witness) {
|
||||
if (bullshitNesting)
|
||||
assert.equal(addr.type, scriptTypes.SCRIPTHASH);
|
||||
else
|
||||
assert.equal(addr.type, scriptTypes.WITNESSSCRIPTHASH);
|
||||
} else {
|
||||
assert.equal(addr.type, scriptTypes.SCRIPTHASH);
|
||||
}
|
||||
|
||||
assert.equal(w1.getAddress('base58'), b58);
|
||||
assert.equal(w2.getAddress('base58'), b58);
|
||||
assert.equal(w3.getAddress('base58'), b58);
|
||||
assert.equal(w1[rec].getAddress('base58'), b58);
|
||||
assert.equal(w2[rec].getAddress('base58'), b58);
|
||||
assert.equal(w3[rec].getAddress('base58'), b58);
|
||||
|
||||
paddr = w1.getNestedAddress('base58');
|
||||
assert.equal(w1.getNestedAddress('base58'), paddr);
|
||||
@ -563,20 +570,21 @@ describe('Wallet', function() {
|
||||
utx.ts = 1;
|
||||
utx.height = 1;
|
||||
|
||||
assert.equal(w1.receiveDepth, 1);
|
||||
assert.equal(w1[depth], 1);
|
||||
|
||||
yield walletdb.addTX(utx);
|
||||
yield walletdb.addTX(utx);
|
||||
yield walletdb.addTX(utx);
|
||||
|
||||
assert.equal(w1.receiveDepth, 2);
|
||||
assert.equal(w1[depth], 2);
|
||||
|
||||
assert.equal(w1.changeDepth, 1);
|
||||
|
||||
assert(w1.getAddress('base58') !== b58);
|
||||
b58 = w1.getAddress('base58');
|
||||
assert.equal(w1.getAddress('base58'), b58);
|
||||
assert.equal(w2.getAddress('base58'), b58);
|
||||
assert.equal(w3.getAddress('base58'), b58);
|
||||
assert(w1[rec].getAddress('base58') !== b58);
|
||||
b58 = w1[rec].getAddress('base58');
|
||||
assert.equal(w1[rec].getAddress('base58'), b58);
|
||||
assert.equal(w2[rec].getAddress('base58'), b58);
|
||||
assert.equal(w3[rec].getAddress('base58'), b58);
|
||||
|
||||
// Create a tx requiring 2 signatures
|
||||
send = bcoin.mtx();
|
||||
@ -609,10 +617,10 @@ describe('Wallet', function() {
|
||||
yield walletdb.addTX(send);
|
||||
yield walletdb.addTX(send);
|
||||
|
||||
assert.equal(w1.receiveDepth, 2);
|
||||
assert.equal(w1[depth], 2);
|
||||
assert.equal(w1.changeDepth, 2);
|
||||
|
||||
assert(w1.getAddress('base58') === b58);
|
||||
assert(w1[rec].getAddress('base58') === b58);
|
||||
assert(w1.change.getAddress('base58') !== change);
|
||||
change = w1.change.getAddress('base58');
|
||||
assert.equal(w1.change.getAddress('base58'), change);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user