wallet: key importing.
This commit is contained in:
parent
eecae63cf3
commit
4b008540e0
@ -266,13 +266,10 @@ crypto.decrypt = function decrypt(data, passphrase, iv, callback) {
|
||||
try {
|
||||
data = crypto.decipher(data, key, iv);
|
||||
} catch (e) {
|
||||
key.fill(0);
|
||||
return callback(e);
|
||||
}
|
||||
|
||||
key.fill(0);
|
||||
|
||||
return callback(null, data);
|
||||
return callback(null, data, key);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -326,42 +326,6 @@ KeyRing.fromSecret = function fromSecret(data) {
|
||||
return new KeyRing().fromSecret(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject properties from account object.
|
||||
* @private
|
||||
* @param {Account} account
|
||||
* @param {Buffer} key
|
||||
* @param {Buffer[]} keys
|
||||
* @param {Number} change
|
||||
* @param {Number} index
|
||||
*/
|
||||
|
||||
KeyRing.prototype.fromAccount = function fromAccount(account, key, keys, change, index) {
|
||||
this.network = account.network;
|
||||
this.publicKey = key.publicKey;
|
||||
|
||||
if (account.n > 1)
|
||||
this.script = bcoin.script.fromMultisig(account.m, account.n, keys);
|
||||
|
||||
this.witness = account.witness;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate key ring from an account.
|
||||
* @param {Account} account
|
||||
* @param {Buffer} key
|
||||
* @param {Buffer[]} keys
|
||||
* @param {Number} change
|
||||
* @param {Number} index
|
||||
* @returns {KeyRing}
|
||||
*/
|
||||
|
||||
KeyRing.fromAccount = function fromAccount(account, key, keys, change, index) {
|
||||
return new KeyRing().fromAccount(account, key, keys, change, index);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get public key.
|
||||
* @param {String?} enc - `"hex"` or `null`.
|
||||
|
||||
@ -628,46 +628,6 @@ Wallet.prototype.createReceive = function createReceive(account, callback) {
|
||||
return this.createAddress(account, false, callback);
|
||||
};
|
||||
|
||||
Wallet.prototype.importKey = function importKey(account, ring, callback) {
|
||||
var self = this;
|
||||
|
||||
if (typeof ring === 'function') {
|
||||
callback = ring;
|
||||
ring = account;
|
||||
account = 0;
|
||||
}
|
||||
|
||||
callback = this._lockWrite(importKey, [account, ring, callback]);
|
||||
|
||||
if (!callback)
|
||||
return;
|
||||
|
||||
this.getAccount(account, function(err, account) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (!account)
|
||||
return callback(new Error('Account not found.'));
|
||||
|
||||
self.start();
|
||||
|
||||
ring.path = bcoin.walletdb.Path.fromAccount(account, ring, -1, -1);
|
||||
ring.path.imported = ring.toRaw();
|
||||
|
||||
account.saveAddress([ring], function(err) {
|
||||
if (err) {
|
||||
self.drop();
|
||||
return callback(err);
|
||||
}
|
||||
self.commit(function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
callback();
|
||||
});
|
||||
});
|
||||
}, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new change address (increments receiveDepth).
|
||||
* @param {(Number|String)?} account
|
||||
@ -831,6 +791,71 @@ Wallet.prototype.getPaths = function getPaths(account, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Import a keyring (will not exist on derivation chain).
|
||||
* Rescanning must be invoked manually.
|
||||
* @param {(String|Number)?} account
|
||||
* @param {KeyRing} ring
|
||||
* @param {(String|Buffer)?} passphrase
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Wallet.prototype.importKey = function importKey(account, ring, passphrase, callback) {
|
||||
var self = this;
|
||||
var raw, path;
|
||||
|
||||
if (typeof passphrase === 'function') {
|
||||
callback = passphrase;
|
||||
passphrase = null;
|
||||
}
|
||||
|
||||
if (typeof ring === 'function') {
|
||||
callback = ring;
|
||||
ring = account;
|
||||
account = 0;
|
||||
}
|
||||
|
||||
callback = this._lockWrite(importKey, [account, ring, callback]);
|
||||
|
||||
if (!callback)
|
||||
return;
|
||||
|
||||
this.getAccount(account, function(err, account) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (!account)
|
||||
return callback(new Error('Account not found.'));
|
||||
|
||||
self.unlock(passphrase, null, function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
raw = ring.toRaw();
|
||||
path = bcoin.path.fromAccount(account, ring);
|
||||
|
||||
if (self.master.encrypted) {
|
||||
raw = self.master.encipher(raw, path.hash);
|
||||
assert(raw);
|
||||
path.encrypted = true;
|
||||
}
|
||||
|
||||
path.imported = raw;
|
||||
ring.path = path;
|
||||
|
||||
self.start();
|
||||
|
||||
account.saveAddress([ring], function(err) {
|
||||
if (err) {
|
||||
self.drop();
|
||||
return callback(err);
|
||||
}
|
||||
self.commit(callback);
|
||||
});
|
||||
}, true);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Fill a transaction with inputs, estimate
|
||||
* transaction size, calculate fee, and add a change output.
|
||||
@ -1067,16 +1092,11 @@ Wallet.prototype.resend = function resend(callback) {
|
||||
* @param {Function} callback - Returns [Error, {@link KeyRing}[]].
|
||||
*/
|
||||
|
||||
Wallet.prototype.deriveInputs = function deriveInputs(tx, master, callback) {
|
||||
Wallet.prototype.deriveInputs = function deriveInputs(tx, callback) {
|
||||
var self = this;
|
||||
var rings = [];
|
||||
var ring;
|
||||
|
||||
if (typeof master === 'function') {
|
||||
callback = master;
|
||||
master = null;
|
||||
}
|
||||
|
||||
this.getInputPaths(tx, function(err, paths) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
@ -1089,8 +1109,10 @@ Wallet.prototype.deriveInputs = function deriveInputs(tx, master, callback) {
|
||||
if (!account)
|
||||
return next();
|
||||
|
||||
ring = account.derivePath(path, master);
|
||||
rings.push(ring);
|
||||
ring = account.derivePath(path, self.master);
|
||||
|
||||
if (ring)
|
||||
rings.push(ring);
|
||||
|
||||
next();
|
||||
});
|
||||
@ -1131,7 +1153,7 @@ Wallet.prototype.getKeyring = function getKeyring(address, callback) {
|
||||
if (!account)
|
||||
return callback();
|
||||
|
||||
ring = account.derivePath(path, self.master.key);
|
||||
ring = account.derivePath(path, self.master);
|
||||
|
||||
callback(null, ring);
|
||||
});
|
||||
@ -1387,7 +1409,10 @@ Wallet.prototype.getRedeem = function getRedeem(hash, callback) {
|
||||
if (!account)
|
||||
return callback();
|
||||
|
||||
ring = account.derivePath(path);
|
||||
ring = account.derivePath(path, self.master);
|
||||
|
||||
if (!ring)
|
||||
return callback();
|
||||
|
||||
if (ring.program && hash.length === 20) {
|
||||
if (utils.equal(hash, ring.programHash))
|
||||
@ -1453,7 +1478,7 @@ Wallet.prototype.sign = function sign(tx, options, callback) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
self.deriveInputs(tx, master, function(err, rings) {
|
||||
self.deriveInputs(tx, function(err, rings) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
@ -2540,15 +2565,32 @@ Account.prototype.deriveChange = function deriveChange(index, master) {
|
||||
return this.deriveAddress(true, index, master);
|
||||
};
|
||||
|
||||
/**
|
||||
* Derive an address from `path` object.
|
||||
* @param {Path} path
|
||||
* @param {MasterKey} master
|
||||
* @returns {KeyRing}
|
||||
*/
|
||||
|
||||
Account.prototype.derivePath = function derivePath(path, master) {
|
||||
var ring, script;
|
||||
var ring, script, raw;
|
||||
|
||||
// Imported key.
|
||||
if (path.index === -1) {
|
||||
assert(path.imported);
|
||||
assert(this.n === 1);
|
||||
ring = bcoin.keyring.fromRaw(path.imported);
|
||||
|
||||
raw = path.imported;
|
||||
|
||||
if (path.encrypted)
|
||||
raw = master.decipher(raw, path.hash);
|
||||
|
||||
if (!raw)
|
||||
return;
|
||||
|
||||
ring = bcoin.keyring.fromRaw(raw);
|
||||
ring.path = path;
|
||||
|
||||
return ring;
|
||||
}
|
||||
|
||||
@ -2574,33 +2616,43 @@ Account.prototype.deriveAddress = function deriveAddress(change, index, master,
|
||||
|
||||
change = +change;
|
||||
|
||||
if (master) {
|
||||
key = master.deriveAccount44(this.accountIndex);
|
||||
if (master && master.key) {
|
||||
key = master.key.deriveAccount44(this.accountIndex);
|
||||
key = key.derive(change).derive(index);
|
||||
} else {
|
||||
key = this.accountKey.derive(change).derive(index);
|
||||
}
|
||||
|
||||
ring = bcoin.keyring.fromPublic(key.publicKey, this.network);
|
||||
ring.witness = this.witness;
|
||||
|
||||
if (script) {
|
||||
// Custom redeem script.
|
||||
assert(this.n === 1);
|
||||
ring = bcoin.keyring.fromScript(key.publicKey, script, this.network);
|
||||
assert(this.type === Account.types.PUBKEYHASH);
|
||||
ring.script = script;
|
||||
} else {
|
||||
keys.push(key.publicKey);
|
||||
switch (this.type) {
|
||||
case Account.types.PUBKEYHASH:
|
||||
break;
|
||||
case Account.types.MULTISIG:
|
||||
keys.push(key.publicKey);
|
||||
|
||||
for (i = 0; i < this.keys.length; i++) {
|
||||
shared = this.keys[i];
|
||||
shared = shared.derive(change).derive(index);
|
||||
keys.push(shared.publicKey);
|
||||
for (i = 0; i < this.keys.length; i++) {
|
||||
shared = this.keys[i];
|
||||
shared = shared.derive(change).derive(index);
|
||||
keys.push(shared.publicKey);
|
||||
}
|
||||
|
||||
ring.script = bcoin.script.fromMultisig(this.m, this.n, keys);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
ring = bcoin.keyring.fromAccount(this, key, keys, change, index);
|
||||
}
|
||||
|
||||
if (master)
|
||||
if (key.privateKey)
|
||||
ring.privateKey = key.privateKey;
|
||||
|
||||
ring.path = bcoin.walletdb.Path.fromAccount(this, ring, change, index);
|
||||
ring.path = bcoin.path.fromAccount(this, ring, change, index);
|
||||
|
||||
return ring;
|
||||
};
|
||||
@ -2908,6 +2960,7 @@ function MasterKey(options) {
|
||||
this.ciphertext = null;
|
||||
this.key = null;
|
||||
|
||||
this.aesKey = null;
|
||||
this.timer = null;
|
||||
this.until = 0;
|
||||
this._destroy = this.destroy.bind(this);
|
||||
@ -2989,7 +3042,7 @@ MasterKey.prototype.unlock = function unlock(passphrase, timeout, callback) {
|
||||
|
||||
assert(this.encrypted);
|
||||
|
||||
utils.decrypt(this.ciphertext, passphrase, this.iv, function(err, data) {
|
||||
utils.decrypt(this.ciphertext, passphrase, this.iv, function(err, data, key) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
@ -3001,6 +3054,8 @@ MasterKey.prototype.unlock = function unlock(passphrase, timeout, callback) {
|
||||
|
||||
self.start(timeout);
|
||||
|
||||
self.aesKey = key;
|
||||
|
||||
callback(null, self.key);
|
||||
});
|
||||
};
|
||||
@ -3037,6 +3092,26 @@ MasterKey.prototype.stop = function stop() {
|
||||
}
|
||||
};
|
||||
|
||||
MasterKey.prototype.encipher = function encipher(data, iv) {
|
||||
if (!this.aesKey)
|
||||
return;
|
||||
|
||||
if (typeof iv === 'string')
|
||||
iv = new Buffer(iv, 'hex');
|
||||
|
||||
return utils.encipher(data, this.aesKey, iv.slice(0, 16));
|
||||
};
|
||||
|
||||
MasterKey.prototype.decipher = function decipher(data, iv) {
|
||||
if (!this.aesKey)
|
||||
return;
|
||||
|
||||
if (typeof iv === 'string')
|
||||
iv = new Buffer(iv, 'hex');
|
||||
|
||||
return utils.decipher(data, this.aesKey, iv.slice(0, 16));
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy the key by zeroing the
|
||||
* privateKey and chainCode. Stop
|
||||
@ -3056,6 +3131,11 @@ MasterKey.prototype.destroy = function destroy() {
|
||||
this.key.destroy(true);
|
||||
this.key = null;
|
||||
}
|
||||
|
||||
if (this.aesKey) {
|
||||
this.aesKey.fill(0);
|
||||
this.aesKey = null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -1591,9 +1591,10 @@ function Path() {
|
||||
this.wid = null;
|
||||
this.name = null;
|
||||
this.account = 0;
|
||||
this.change = 0;
|
||||
this.index = 0;
|
||||
this.change = -1;
|
||||
this.index = -1;
|
||||
|
||||
this.encrypted = false;
|
||||
this.imported = null;
|
||||
this.script = null;
|
||||
|
||||
@ -1627,6 +1628,7 @@ Path.prototype.fromRaw = function fromRaw(data) {
|
||||
this.script = p.readVarBytes();
|
||||
break;
|
||||
case 1:
|
||||
this.encrypted = p.readU8() === 1;
|
||||
this.imported = p.readVarBytes();
|
||||
this.change = -1;
|
||||
this.index = -1;
|
||||
@ -1676,6 +1678,7 @@ Path.prototype.toRaw = function toRaw(writer) {
|
||||
} else {
|
||||
assert(this.imported);
|
||||
p.writeU8(1);
|
||||
p.writeU8(this.encrypted ? 1 : 0);
|
||||
p.writeVarBytes(this.imported);
|
||||
}
|
||||
|
||||
@ -1689,34 +1692,22 @@ Path.prototype.toRaw = function toRaw(writer) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject properties from keyring.
|
||||
* Inject properties from account.
|
||||
* @private
|
||||
* @param {WalletID} wid
|
||||
* @param {KeyRing} ring
|
||||
*/
|
||||
|
||||
Path.prototype.fromKeyRing = function fromKeyRing(ring) {
|
||||
this.wid = ring.wid;
|
||||
this.name = ring.name;
|
||||
this.account = ring.account;
|
||||
this.change = ring.change;
|
||||
this.index = ring.index;
|
||||
|
||||
this.version = ring.witness ? 0 : -1;
|
||||
this.type = ring.getType();
|
||||
|
||||
this.id = ring.id;
|
||||
this.hash = ring.getHash('hex');
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Path.prototype.fromAccount = function fromAccount(account, ring, change, index) {
|
||||
this.wid = account.wid;
|
||||
this.name = account.name;
|
||||
this.account = account.accountIndex;
|
||||
this.change = change;
|
||||
this.index = index;
|
||||
|
||||
if (change != null)
|
||||
this.change = change;
|
||||
|
||||
if (index != null)
|
||||
this.index = index;
|
||||
|
||||
this.version = ring.witness ? 0 : -1;
|
||||
this.type = ring.getType();
|
||||
|
||||
@ -1065,13 +1065,14 @@ describe('Wallet', function() {
|
||||
|
||||
it('should import key', function(cb) {
|
||||
var key = bcoin.keyring.generate();
|
||||
walletdb.create(function(err, w1) {
|
||||
walletdb.create({ passphrase: 'test' }, function(err, w1) {
|
||||
assert.ifError(err);
|
||||
w1.importKey(key, function(err) {
|
||||
w1.importKey('default', key, 'test', function(err) {
|
||||
assert.ifError(err);
|
||||
w1.getKeyring(key.getHash('hex'), function(err, k) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
assert.equal(k.getHash('hex'), key.getHash('hex'));
|
||||
|
||||
// Coinbase
|
||||
|
||||
Loading…
Reference in New Issue
Block a user