account: hd watchonly support.

This commit is contained in:
Christopher Jeffrey 2016-10-02 19:39:35 -07:00
parent 57bc9bf4b0
commit 7b3134d782
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
3 changed files with 56 additions and 8 deletions

View File

@ -69,6 +69,7 @@ function Account(db, options) {
this.n = 1;
this.keys = [];
this.initialized = false;
this.watchOnly = false;
if (options)
this.fromOptions(options);
@ -171,6 +172,11 @@ Account.prototype.fromOptions = function fromOptions(options) {
this.initialized = options.initialized;
}
if (options.watchOnly != null) {
assert(typeof options.watchOnly === 'boolean');
this.watchOnly = options.watchOnly;
}
if (this.n > 1)
this.type = Account.types.MULTISIG;
@ -533,7 +539,7 @@ Account.prototype.deriveKey = function deriveKey(branch, index, master) {
assert(typeof branch === 'number');
if (master && master.key) {
if (master && master.key && !this.watchOnly) {
key = master.key.deriveAccount44(this.accountIndex);
key = key.derive(branch).derive(index);
} else {
@ -668,6 +674,7 @@ Account.prototype.inspect = function inspect() {
name: this.name,
network: this.network,
initialized: this.initialized,
watchOnly: this.watchOnly,
type: Account.typesByVal[this.type].toLowerCase(),
m: this.m,
n: this.n,
@ -701,6 +708,7 @@ Account.prototype.toJSON = function toJSON() {
wid: this.wid,
name: this.name,
initialized: this.initialized,
watchOnly: this.watchOnly,
type: Account.typesByVal[this.type].toLowerCase(),
m: this.m,
n: this.n,
@ -739,6 +747,7 @@ Account.prototype.fromJSON = function fromJSON(json) {
assert(utils.isName(json.id), 'Bad wallet ID.');
assert(utils.isName(json.name), 'Bad account name.');
assert(typeof json.initialized === 'boolean');
assert(typeof json.watchOnly === 'boolean');
assert(typeof json.type === 'string');
assert(utils.isNumber(json.m));
assert(utils.isNumber(json.n));
@ -752,6 +761,7 @@ Account.prototype.fromJSON = function fromJSON(json) {
this.wid = json.wid;
this.name = json.name;
this.initialized = json.initialized;
this.watchOnly = json.watchOnly;
this.type = Account.types[json.type.toUpperCase()];
this.m = json.m;
this.n = json.n;
@ -784,6 +794,7 @@ Account.prototype.toRaw = function toRaw(writer) {
p.writeU32(this.network.magic);
p.writeVarString(this.name, 'ascii');
p.writeU8(this.initialized ? 1 : 0);
p.writeU8(this.watchOnly ? 1 : 0);
p.writeU8(this.type);
p.writeU8(this.m);
p.writeU8(this.n);
@ -820,6 +831,7 @@ Account.prototype.fromRaw = function fromRaw(data) {
this.network = Network.fromMagic(p.readU32());
this.name = p.readVarString('ascii');
this.initialized = p.readU8() === 1;
this.watchOnly = p.readU8() === 1;
this.type = p.readU8();
this.m = p.readU8();
this.n = p.readU8();

View File

@ -96,11 +96,10 @@ Wallet.prototype.fromOptions = function fromOptions(options) {
if (!master)
master = HD.fromMnemonic(null, this.network);
if (!HD.isHD(master) && !MasterKey.isMasterKey(master))
if (!HD.isHD(master))
master = HD.from(master, this.network);
if (HD.isHD(master))
master = MasterKey.fromKey(master);
master = MasterKey.fromKey(master);
assert(MasterKey.isMasterKey(master));
@ -633,6 +632,7 @@ Wallet.prototype._createAccount = co(function* createAccount(options) {
var passphrase = options.passphrase;
var timeout = options.timeout;
var name = options.name;
var watchOnly = options.watchOnly === true;
var key, master, account;
if (typeof options.account === 'string')
@ -643,7 +643,13 @@ Wallet.prototype._createAccount = co(function* createAccount(options) {
master = yield this.unlock(passphrase, timeout);
key = master.deriveAccount44(this.accountDepth);
if (watchOnly && options.accountKey) {
key = options.accountKey;
if (!HD.isHD(key))
key = HD.from(key, this.network);
} else {
key = master.deriveAccount44(this.accountDepth);
}
options = {
network: this.network,
@ -656,7 +662,8 @@ Wallet.prototype._createAccount = co(function* createAccount(options) {
type: options.type,
keys: options.keys,
m: options.m,
n: options.n
n: options.n,
watchOnly: watchOnly
};
this.start();
@ -981,6 +988,9 @@ Wallet.prototype._importKey = co(function* importKey(account, ring, passphrase)
if (account.type !== Account.types.PUBKEYHASH)
throw new Error('Cannot import into non-pkh account.');
if (!ring.privateKey && !account.watchOnly)
throw new Error('Cannot import pubkey into non-watchonly account.');
yield this.unlock(passphrase);
ring = WalletKey.fromRing(account, ring);
@ -1055,6 +1065,9 @@ Wallet.prototype._importAddress = co(function* importAddress(account, address) {
if (account.type !== Account.types.PUBKEYHASH)
throw new Error('Cannot import into non-pkh account.');
if (!account.watchOnly)
throw new Error('Cannot import address into non-watchonly account.');
path = Path.fromAddress(account, address);
this.start();

View File

@ -900,7 +900,7 @@ describe('Wallet', function() {
assert.equal(balance.total, 21840);
}));
it('should import key', cob(function *() {
it('should import privkey', cob(function *() {
var key = bcoin.keyring.generate();
var w = yield walletdb.create({ passphrase: 'test' });
var options, k, t1, t2, tx;
@ -943,16 +943,39 @@ describe('Wallet', function() {
ekey = key;
}));
it('should import pubkey', cob(function *() {
var priv = bcoin.keyring.generate();
var key = new bcoin.keyring(priv.publicKey);
var w = yield walletdb.create();
var options, k, t1, t2, tx;
yield w.createAccount({ name: 'watchonly', watchOnly: true });
yield w.importKey('watchonly', key);
k = yield w.getPath(key.getHash('hex'));
assert.equal(k.hash, key.getHash('hex'));
k = yield w.getKey(key.getHash('hex'));
assert(k);
}));
it('should import address', cob(function *() {
var key = bcoin.keyring.generate();
var w = yield walletdb.create();
var options, k, t1, t2, tx;
yield w.importAddress('default', key.getAddress());
yield w.createAccount({ name: 'watchonly', watchOnly: true });
yield w.importAddress('watchonly', key.getAddress());
k = yield w.getPath(key.getHash('hex'));
assert.equal(k.hash, key.getHash('hex'));
k = yield w.getKey(key.getHash('hex'));
assert(!k);
}));
it('should get details', cob(function *() {