walletdb: custom addresses.
This commit is contained in:
parent
2b3c6622d3
commit
eecae63cf3
@ -3555,7 +3555,7 @@ RPC.prototype.listunspent = function listunspent(args, callback) {
|
||||
txid: utils.revHex(coin.hash),
|
||||
vout: coin.index,
|
||||
address: address ? address.toBase58(self.network) : null,
|
||||
account: ring ? ring.name : undefined,
|
||||
account: ring ? ring.path.name : undefined,
|
||||
redeemScript: ring && ring.script
|
||||
? ring.script.toJSON()
|
||||
: undefined,
|
||||
|
||||
@ -36,13 +36,7 @@ function KeyRing(options, network) {
|
||||
this.publicKey = null;
|
||||
this.privateKey = null;
|
||||
this.script = null;
|
||||
|
||||
this.wid = 0;
|
||||
this.id = null;
|
||||
this.name = null;
|
||||
this.account = 0;
|
||||
this.change = 0;
|
||||
this.index = 0;
|
||||
this.path = null;
|
||||
|
||||
this._keyHash = null;
|
||||
this._keyAddress = null;
|
||||
@ -351,13 +345,6 @@ KeyRing.prototype.fromAccount = function fromAccount(account, key, keys, change,
|
||||
|
||||
this.witness = account.witness;
|
||||
|
||||
this.wid = account.wid;
|
||||
this.id = account.id;
|
||||
this.name = account.name;
|
||||
this.account = account.accountIndex;
|
||||
this.change = change;
|
||||
this.index = index;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
@ -818,12 +805,12 @@ KeyRing.prototype.toJSON = function toJSON() {
|
||||
key: this.publicKey.toString('hex'),
|
||||
script: this.script ? this.script.toRaw().toString('hex') : null,
|
||||
type: constants.scriptTypesByVal[this.type].toLowerCase(),
|
||||
wid: this.wid,
|
||||
id: this.id,
|
||||
name: this.name,
|
||||
account: this.account,
|
||||
change: this.change,
|
||||
index: this.index,
|
||||
wid: this.path ? this.path.wid : undefined,
|
||||
id: this.path ? this.path.id : undefined,
|
||||
name: this.path ? this.path.name : undefined,
|
||||
account: this.path ? this.path.account : undefined,
|
||||
change: this.path ? this.path.change : undefined,
|
||||
index: this.path ? this.path.index : undefined,
|
||||
address: this.getAddress('base58'),
|
||||
programAddress: this.getProgramAddress('base58')
|
||||
};
|
||||
@ -884,14 +871,12 @@ KeyRing.prototype.toRaw = function toRaw(writer) {
|
||||
var p = new BufferWriter(writer);
|
||||
var i;
|
||||
|
||||
p.writeU32(this.network.magic);
|
||||
p.writeU8(this.witness ? 1 : 0);
|
||||
p.writeVarBytes(this.publicKey);
|
||||
|
||||
if (this.privateKey)
|
||||
p.writeVarBytes(this.privateKey);
|
||||
else
|
||||
p.writeVarint(0);
|
||||
p.writeVarBytes(this.publicKey);
|
||||
|
||||
if (this.script)
|
||||
p.writeVarBytes(this.script.toRaw());
|
||||
@ -912,16 +897,18 @@ KeyRing.prototype.toRaw = function toRaw(writer) {
|
||||
|
||||
KeyRing.prototype.fromRaw = function fromRaw(data) {
|
||||
var p = new BufferReader(data);
|
||||
var i, count;
|
||||
var i, count, key;
|
||||
|
||||
this.network = bcoin.network.fromMagic(p.readU32());
|
||||
this.witness = p.readU8() === 1;
|
||||
this.publicKey = p.readVarBytes();
|
||||
|
||||
this.privateKey = p.readVarBytes();
|
||||
key = p.readVarBytes();
|
||||
|
||||
if (this.privateKey.length === 0)
|
||||
this.privateKey = null;
|
||||
if (key.length === 32) {
|
||||
this.privateKey = key;
|
||||
this.publicKey = bcoin.ec.publicKeyCreate(key, true);
|
||||
} else {
|
||||
this.publicKey = key;
|
||||
}
|
||||
|
||||
this.script = p.readVarBytes();
|
||||
|
||||
|
||||
@ -628,6 +628,46 @@ 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
|
||||
@ -1049,7 +1089,7 @@ Wallet.prototype.deriveInputs = function deriveInputs(tx, master, callback) {
|
||||
if (!account)
|
||||
return next();
|
||||
|
||||
ring = account.deriveAddress(path.change, path.index, master);
|
||||
ring = account.derivePath(path, master);
|
||||
rings.push(ring);
|
||||
|
||||
next();
|
||||
@ -1091,7 +1131,7 @@ Wallet.prototype.getKeyring = function getKeyring(address, callback) {
|
||||
if (!account)
|
||||
return callback();
|
||||
|
||||
ring = account.deriveAddress(path.change, path.index, self.master.key);
|
||||
ring = account.derivePath(path, self.master.key);
|
||||
|
||||
callback(null, ring);
|
||||
});
|
||||
@ -1347,7 +1387,7 @@ Wallet.prototype.getRedeem = function getRedeem(hash, callback) {
|
||||
if (!account)
|
||||
return callback();
|
||||
|
||||
ring = account.deriveAddress(path.change, path.index);
|
||||
ring = account.derivePath(path);
|
||||
|
||||
if (ring.program && hash.length === 20) {
|
||||
if (utils.equal(hash, ring.programHash))
|
||||
@ -2500,6 +2540,27 @@ Account.prototype.deriveChange = function deriveChange(index, master) {
|
||||
return this.deriveAddress(true, index, master);
|
||||
};
|
||||
|
||||
Account.prototype.derivePath = function derivePath(path, master) {
|
||||
var ring, script;
|
||||
|
||||
// Imported key.
|
||||
if (path.index === -1) {
|
||||
assert(path.imported);
|
||||
assert(this.n === 1);
|
||||
ring = bcoin.keyring.fromRaw(path.imported);
|
||||
ring.path = path;
|
||||
return ring;
|
||||
}
|
||||
|
||||
// Custom redeem script.
|
||||
if (path.script)
|
||||
script = new bcoin.script(path.script);
|
||||
|
||||
ring = this.deriveAddress(path.change, path.index, master, script);
|
||||
|
||||
return ring;
|
||||
};
|
||||
|
||||
/**
|
||||
* Derive an address at `index`. Do not increment depth.
|
||||
* @param {Boolean} change - Whether the address on the change branch.
|
||||
@ -2507,7 +2568,7 @@ Account.prototype.deriveChange = function deriveChange(index, master) {
|
||||
* @returns {KeyRing}
|
||||
*/
|
||||
|
||||
Account.prototype.deriveAddress = function deriveAddress(change, index, master) {
|
||||
Account.prototype.deriveAddress = function deriveAddress(change, index, master, script) {
|
||||
var keys = [];
|
||||
var i, key, shared, ring;
|
||||
|
||||
@ -2520,19 +2581,27 @@ Account.prototype.deriveAddress = function deriveAddress(change, index, master)
|
||||
key = this.accountKey.derive(change).derive(index);
|
||||
}
|
||||
|
||||
keys.push(key.publicKey);
|
||||
if (script) {
|
||||
// Custom redeem script.
|
||||
assert(this.n === 1);
|
||||
ring = bcoin.keyring.fromScript(key.publicKey, script, this.network);
|
||||
} else {
|
||||
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 = bcoin.keyring.fromAccount(this, key, keys, change, index);
|
||||
}
|
||||
|
||||
ring = bcoin.keyring.fromAccount(this, key, keys, change, index);
|
||||
|
||||
if (master)
|
||||
ring.privateKey = key.privateKey;
|
||||
|
||||
ring.path = bcoin.walletdb.Path.fromAccount(this, ring, change, index);
|
||||
|
||||
return ring;
|
||||
};
|
||||
|
||||
|
||||
@ -946,7 +946,7 @@ WalletDB.prototype.saveAddress = function saveAddress(wid, rings, callback) {
|
||||
|
||||
for (i = 0; i < rings.length; i++) {
|
||||
ring = rings[i];
|
||||
path = Path.fromKeyRing(ring);
|
||||
path = ring.path;
|
||||
|
||||
items.push([ring.getAddress(), path]);
|
||||
|
||||
@ -1594,6 +1594,9 @@ function Path() {
|
||||
this.change = 0;
|
||||
this.index = 0;
|
||||
|
||||
this.imported = null;
|
||||
this.script = null;
|
||||
|
||||
// Currently unused.
|
||||
this.type = bcoin.script.types.PUBKEYHASH;
|
||||
this.version = -1;
|
||||
@ -1615,8 +1618,24 @@ Path.prototype.fromRaw = function fromRaw(data) {
|
||||
this.wid = p.readU32();
|
||||
this.name = p.readVarString('utf8');
|
||||
this.account = p.readU32();
|
||||
this.change = p.readU32();
|
||||
this.index = p.readU32();
|
||||
|
||||
switch (p.readU8()) {
|
||||
case 0:
|
||||
this.change = p.readU32();
|
||||
this.index = p.readU32();
|
||||
if (p.left() > 0)
|
||||
this.script = p.readVarBytes();
|
||||
break;
|
||||
case 1:
|
||||
this.imported = p.readVarBytes();
|
||||
this.change = -1;
|
||||
this.index = -1;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
this.version = p.readU8();
|
||||
this.type = p.readU8();
|
||||
|
||||
@ -1647,8 +1666,19 @@ Path.prototype.toRaw = function toRaw(writer) {
|
||||
p.writeU32(this.wid);
|
||||
p.writeVarString(this.name, 'utf8');
|
||||
p.writeU32(this.account);
|
||||
p.writeU32(this.change);
|
||||
p.writeU32(this.index);
|
||||
|
||||
if (this.index !== -1) {
|
||||
p.writeU8(0);
|
||||
p.writeU32(this.change);
|
||||
p.writeU32(this.index);
|
||||
if (this.script)
|
||||
p.writeVarBytes(this.script);
|
||||
} else {
|
||||
assert(this.imported);
|
||||
p.writeU8(1);
|
||||
p.writeVarBytes(this.imported);
|
||||
}
|
||||
|
||||
p.writeU8(this.version === -1 ? 0xff : this.version);
|
||||
p.writeU8(this.type);
|
||||
|
||||
@ -1681,6 +1711,26 @@ Path.prototype.fromKeyRing = function fromKeyRing(ring) {
|
||||
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;
|
||||
|
||||
this.version = ring.witness ? 0 : -1;
|
||||
this.type = ring.getType();
|
||||
|
||||
this.id = account.id;
|
||||
this.hash = ring.getHash('hex');
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Path.fromAccount = function fromAccount(account, ring, change, index) {
|
||||
return new Path().fromAccount(account, ring, change, index);
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate path from keyring.
|
||||
* @param {WalletID} wid
|
||||
|
||||
@ -1063,6 +1063,58 @@ describe('Wallet', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should import key', function(cb) {
|
||||
var key = bcoin.keyring.generate();
|
||||
walletdb.create(function(err, w1) {
|
||||
assert.ifError(err);
|
||||
w1.importKey(key, 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
|
||||
var t1 = bcoin.mtx()
|
||||
.addOutput(key.getAddress(), 5460)
|
||||
.addOutput(key.getAddress(), 5460)
|
||||
.addOutput(key.getAddress(), 5460)
|
||||
.addOutput(key.getAddress(), 5460);
|
||||
|
||||
t1.addInput(dummyInput);
|
||||
t1 = t1.toTX();
|
||||
|
||||
walletdb.addTX(t1, function(err) {
|
||||
assert.ifError(err);
|
||||
|
||||
w1.getTX(t1.hash('hex'), function(err, tx) {
|
||||
assert.ifError(err);
|
||||
assert(tx);
|
||||
assert.equal(t1.hash('hex'), tx.hash('hex'));
|
||||
|
||||
var options = {
|
||||
rate: 10000,
|
||||
round: true,
|
||||
outputs: [{ address: w1.getAddress(), value: 7000 }]
|
||||
};
|
||||
|
||||
// Create new transaction
|
||||
w1.createTX(options, function(err, t2) {
|
||||
assert.ifError(err);
|
||||
w1.sign(t2, function(err) {
|
||||
assert.ifError(err);
|
||||
assert(t2.verify());
|
||||
assert(t2.inputs[0].prevout.hash === tx.hash('hex'));
|
||||
cb();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should cleanup', function(cb) {
|
||||
walletdb.dump(function(err, records) {
|
||||
assert.ifError(err);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user