wallet: move watchOnly flag to wallet for safety.
This commit is contained in:
parent
25946b8909
commit
2097450b42
@ -211,7 +211,7 @@ Account.fromOptions = function fromOptions(db, options) {
|
|||||||
* @const {Number}
|
* @const {Number}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Account.MAX_LOOKAHEAD = 5;
|
Account.MAX_LOOKAHEAD = 10;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt to intialize the account (generating
|
* Attempt to intialize the account (generating
|
||||||
@ -794,7 +794,6 @@ Account.prototype.toRaw = function toRaw(writer) {
|
|||||||
p.writeU32(this.network.magic);
|
p.writeU32(this.network.magic);
|
||||||
p.writeVarString(this.name, 'ascii');
|
p.writeVarString(this.name, 'ascii');
|
||||||
p.writeU8(this.initialized ? 1 : 0);
|
p.writeU8(this.initialized ? 1 : 0);
|
||||||
p.writeU8(this.watchOnly ? 1 : 0);
|
|
||||||
p.writeU8(this.type);
|
p.writeU8(this.type);
|
||||||
p.writeU8(this.m);
|
p.writeU8(this.m);
|
||||||
p.writeU8(this.n);
|
p.writeU8(this.n);
|
||||||
@ -831,7 +830,6 @@ Account.prototype.fromRaw = function fromRaw(data) {
|
|||||||
this.network = Network.fromMagic(p.readU32());
|
this.network = Network.fromMagic(p.readU32());
|
||||||
this.name = p.readVarString('ascii');
|
this.name = p.readVarString('ascii');
|
||||||
this.initialized = p.readU8() === 1;
|
this.initialized = p.readU8() === 1;
|
||||||
this.watchOnly = p.readU8() === 1;
|
|
||||||
this.type = p.readU8();
|
this.type = p.readU8();
|
||||||
this.m = p.readU8();
|
this.m = p.readU8();
|
||||||
this.n = p.readU8();
|
this.n = p.readU8();
|
||||||
|
|||||||
@ -71,6 +71,7 @@ function Wallet(db, options) {
|
|||||||
this.id = null;
|
this.id = null;
|
||||||
this.master = null;
|
this.master = null;
|
||||||
this.initialized = false;
|
this.initialized = false;
|
||||||
|
this.watchOnly = false;
|
||||||
this.accountDepth = 0;
|
this.accountDepth = 0;
|
||||||
this.token = constants.ZERO_HASH;
|
this.token = constants.ZERO_HASH;
|
||||||
this.tokenDepth = 0;
|
this.tokenDepth = 0;
|
||||||
@ -111,6 +112,11 @@ Wallet.prototype.fromOptions = function fromOptions(options) {
|
|||||||
this.initialized = options.initialized;
|
this.initialized = options.initialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.watchOnly != null) {
|
||||||
|
assert(typeof options.watchOnly === 'boolean');
|
||||||
|
this.watchOnly = options.watchOnly;
|
||||||
|
}
|
||||||
|
|
||||||
if (options.accountDepth != null) {
|
if (options.accountDepth != null) {
|
||||||
assert(utils.isNumber(options.accountDepth));
|
assert(utils.isNumber(options.accountDepth));
|
||||||
this.accountDepth = options.accountDepth;
|
this.accountDepth = options.accountDepth;
|
||||||
@ -633,7 +639,6 @@ Wallet.prototype._createAccount = co(function* createAccount(options) {
|
|||||||
var passphrase = options.passphrase;
|
var passphrase = options.passphrase;
|
||||||
var timeout = options.timeout;
|
var timeout = options.timeout;
|
||||||
var name = options.name;
|
var name = options.name;
|
||||||
var watchOnly = options.watchOnly === true;
|
|
||||||
var key, master, account;
|
var key, master, account;
|
||||||
|
|
||||||
if (typeof options.account === 'string')
|
if (typeof options.account === 'string')
|
||||||
@ -644,7 +649,7 @@ Wallet.prototype._createAccount = co(function* createAccount(options) {
|
|||||||
|
|
||||||
master = yield this.unlock(passphrase, timeout);
|
master = yield this.unlock(passphrase, timeout);
|
||||||
|
|
||||||
if (watchOnly && options.accountKey) {
|
if (this.watchOnly && options.accountKey) {
|
||||||
key = options.accountKey;
|
key = options.accountKey;
|
||||||
if (!HD.isHD(key))
|
if (!HD.isHD(key))
|
||||||
key = HD.from(key, this.network);
|
key = HD.from(key, this.network);
|
||||||
@ -664,7 +669,7 @@ Wallet.prototype._createAccount = co(function* createAccount(options) {
|
|||||||
keys: options.keys,
|
keys: options.keys,
|
||||||
m: options.m,
|
m: options.m,
|
||||||
n: options.n,
|
n: options.n,
|
||||||
watchOnly: watchOnly
|
watchOnly: this.watchOnly
|
||||||
};
|
};
|
||||||
|
|
||||||
this.start();
|
this.start();
|
||||||
@ -742,6 +747,7 @@ Wallet.prototype.getAccount = co(function* getAccount(account) {
|
|||||||
|
|
||||||
account.wid = this.wid;
|
account.wid = this.wid;
|
||||||
account.id = this.id;
|
account.id = this.id;
|
||||||
|
account.watchOnly = this.watchOnly;
|
||||||
|
|
||||||
return account;
|
return account;
|
||||||
});
|
});
|
||||||
@ -976,6 +982,14 @@ Wallet.prototype._importKey = co(function* importKey(account, ring, passphrase)
|
|||||||
if (account == null)
|
if (account == null)
|
||||||
account = 0;
|
account = 0;
|
||||||
|
|
||||||
|
if (!this.watchOnly) {
|
||||||
|
if (!ring.privateKey)
|
||||||
|
throw new Error('Cannot import pubkey into non watch-only wallet.');
|
||||||
|
} else {
|
||||||
|
if (ring.privateKey)
|
||||||
|
throw new Error('Cannot import privkey into watch-only wallet.');
|
||||||
|
}
|
||||||
|
|
||||||
exists = yield this.getPath(ring.getHash('hex'));
|
exists = yield this.getPath(ring.getHash('hex'));
|
||||||
|
|
||||||
if (exists)
|
if (exists)
|
||||||
@ -989,9 +1003,6 @@ Wallet.prototype._importKey = co(function* importKey(account, ring, passphrase)
|
|||||||
if (account.type !== Account.types.PUBKEYHASH)
|
if (account.type !== Account.types.PUBKEYHASH)
|
||||||
throw new Error('Cannot import into non-pkh account.');
|
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);
|
yield this.unlock(passphrase);
|
||||||
|
|
||||||
ring = WalletKey.fromRing(account, ring);
|
ring = WalletKey.fromRing(account, ring);
|
||||||
@ -1053,6 +1064,9 @@ Wallet.prototype._importAddress = co(function* importAddress(account, address) {
|
|||||||
if (account == null)
|
if (account == null)
|
||||||
account = 0;
|
account = 0;
|
||||||
|
|
||||||
|
if (!this.watchOnly)
|
||||||
|
throw new Error('Cannot import address into non watch-only wallet.');
|
||||||
|
|
||||||
exists = yield this.getPath(address);
|
exists = yield this.getPath(address);
|
||||||
|
|
||||||
if (exists)
|
if (exists)
|
||||||
@ -1066,9 +1080,6 @@ Wallet.prototype._importAddress = co(function* importAddress(account, address) {
|
|||||||
if (account.type !== Account.types.PUBKEYHASH)
|
if (account.type !== Account.types.PUBKEYHASH)
|
||||||
throw new Error('Cannot import into non-pkh account.');
|
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);
|
path = Path.fromAddress(account, address);
|
||||||
|
|
||||||
this.start();
|
this.start();
|
||||||
@ -1132,6 +1143,9 @@ Wallet.prototype._fund = co(function* fund(tx, options) {
|
|||||||
if (!this.initialized)
|
if (!this.initialized)
|
||||||
throw new Error('Wallet is not initialized.');
|
throw new Error('Wallet is not initialized.');
|
||||||
|
|
||||||
|
if (this.watchOnly)
|
||||||
|
throw new Error('Cannot fund from watch-only wallet.');
|
||||||
|
|
||||||
if (options.account != null) {
|
if (options.account != null) {
|
||||||
account = yield this.getAccount(options.account);
|
account = yield this.getAccount(options.account);
|
||||||
if (!account)
|
if (!account)
|
||||||
@ -1595,6 +1609,9 @@ Wallet.prototype.sign = co(function* sign(tx, options) {
|
|||||||
if (typeof options === 'string' || Buffer.isBuffer(options))
|
if (typeof options === 'string' || Buffer.isBuffer(options))
|
||||||
options = { passphrase: options };
|
options = { passphrase: options };
|
||||||
|
|
||||||
|
if (this.watchOnly)
|
||||||
|
throw new Error('Cannot sign from a watch-only wallet.');
|
||||||
|
|
||||||
yield this.unlock(options.passphrase, options.timeout);
|
yield this.unlock(options.passphrase, options.timeout);
|
||||||
|
|
||||||
rings = yield this.deriveInputs(tx);
|
rings = yield this.deriveInputs(tx);
|
||||||
@ -2073,6 +2090,7 @@ Wallet.prototype.toJSON = function toJSON() {
|
|||||||
wid: this.wid,
|
wid: this.wid,
|
||||||
id: this.id,
|
id: this.id,
|
||||||
initialized: this.initialized,
|
initialized: this.initialized,
|
||||||
|
watchOnly: this.watchOnly,
|
||||||
accountDepth: this.accountDepth,
|
accountDepth: this.accountDepth,
|
||||||
token: this.token.toString('hex'),
|
token: this.token.toString('hex'),
|
||||||
tokenDepth: this.tokenDepth,
|
tokenDepth: this.tokenDepth,
|
||||||
@ -2090,6 +2108,7 @@ Wallet.prototype.toJSON = function toJSON() {
|
|||||||
Wallet.prototype.fromJSON = function fromJSON(json) {
|
Wallet.prototype.fromJSON = function fromJSON(json) {
|
||||||
assert(utils.isNumber(json.wid));
|
assert(utils.isNumber(json.wid));
|
||||||
assert(typeof json.initialized === 'boolean');
|
assert(typeof json.initialized === 'boolean');
|
||||||
|
assert(typeof json.watchOnly === 'boolean');
|
||||||
assert(utils.isName(json.id), 'Bad wallet ID.');
|
assert(utils.isName(json.id), 'Bad wallet ID.');
|
||||||
assert(utils.isNumber(json.accountDepth));
|
assert(utils.isNumber(json.accountDepth));
|
||||||
assert(typeof json.token === 'string');
|
assert(typeof json.token === 'string');
|
||||||
@ -2100,6 +2119,7 @@ Wallet.prototype.fromJSON = function fromJSON(json) {
|
|||||||
this.wid = json.wid;
|
this.wid = json.wid;
|
||||||
this.id = json.id;
|
this.id = json.id;
|
||||||
this.initialized = json.initialized;
|
this.initialized = json.initialized;
|
||||||
|
this.watchOnly = json.watchOnly;
|
||||||
this.accountDepth = json.accountDepth;
|
this.accountDepth = json.accountDepth;
|
||||||
this.token = new Buffer(json.token, 'hex');
|
this.token = new Buffer(json.token, 'hex');
|
||||||
this.master = MasterKey.fromJSON(json.master);
|
this.master = MasterKey.fromJSON(json.master);
|
||||||
@ -2119,6 +2139,7 @@ Wallet.prototype.toRaw = function toRaw(writer) {
|
|||||||
p.writeU32(this.wid);
|
p.writeU32(this.wid);
|
||||||
p.writeVarString(this.id, 'ascii');
|
p.writeVarString(this.id, 'ascii');
|
||||||
p.writeU8(this.initialized ? 1 : 0);
|
p.writeU8(this.initialized ? 1 : 0);
|
||||||
|
p.writeU8(this.watchOnly ? 1 : 0);
|
||||||
p.writeU32(this.accountDepth);
|
p.writeU32(this.accountDepth);
|
||||||
p.writeBytes(this.token);
|
p.writeBytes(this.token);
|
||||||
p.writeU32(this.tokenDepth);
|
p.writeU32(this.tokenDepth);
|
||||||
@ -2142,6 +2163,7 @@ Wallet.prototype.fromRaw = function fromRaw(data) {
|
|||||||
this.wid = p.readU32();
|
this.wid = p.readU32();
|
||||||
this.id = p.readVarString('ascii');
|
this.id = p.readVarString('ascii');
|
||||||
this.initialized = p.readU8() === 1;
|
this.initialized = p.readU8() === 1;
|
||||||
|
this.watchOnly = p.readU8() === 1;
|
||||||
this.accountDepth = p.readU32();
|
this.accountDepth = p.readU32();
|
||||||
this.token = p.readBytes(32);
|
this.token = p.readBytes(32);
|
||||||
this.tokenDepth = p.readU32();
|
this.tokenDepth = p.readU32();
|
||||||
|
|||||||
@ -946,12 +946,10 @@ describe('Wallet', function() {
|
|||||||
it('should import pubkey', cob(function *() {
|
it('should import pubkey', cob(function *() {
|
||||||
var priv = bcoin.keyring.generate();
|
var priv = bcoin.keyring.generate();
|
||||||
var key = new bcoin.keyring(priv.publicKey);
|
var key = new bcoin.keyring(priv.publicKey);
|
||||||
var w = yield walletdb.create();
|
var w = yield walletdb.create({ watchOnly: true });
|
||||||
var options, k, t1, t2, tx;
|
var options, k, t1, t2, tx;
|
||||||
|
|
||||||
yield w.createAccount({ name: 'watchonly', watchOnly: true });
|
yield w.importKey('default', key);
|
||||||
|
|
||||||
yield w.importKey('watchonly', key);
|
|
||||||
|
|
||||||
k = yield w.getPath(key.getHash('hex'));
|
k = yield w.getPath(key.getHash('hex'));
|
||||||
|
|
||||||
@ -963,12 +961,10 @@ describe('Wallet', function() {
|
|||||||
|
|
||||||
it('should import address', cob(function *() {
|
it('should import address', cob(function *() {
|
||||||
var key = bcoin.keyring.generate();
|
var key = bcoin.keyring.generate();
|
||||||
var w = yield walletdb.create();
|
var w = yield walletdb.create({ watchOnly: true });
|
||||||
var options, k, t1, t2, tx;
|
var options, k, t1, t2, tx;
|
||||||
|
|
||||||
yield w.createAccount({ name: 'watchonly', watchOnly: true });
|
yield w.importAddress('default', key.getAddress());
|
||||||
|
|
||||||
yield w.importAddress('watchonly', key.getAddress());
|
|
||||||
|
|
||||||
k = yield w.getPath(key.getHash('hex'));
|
k = yield w.getPath(key.getHash('hex'));
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user