more wallet work.

This commit is contained in:
Christopher Jeffrey 2016-05-27 14:26:40 -07:00
parent 9c747cde42
commit 3480b8c679
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
5 changed files with 326 additions and 227 deletions

View File

@ -487,10 +487,6 @@ KeyRing.prototype.sign = function sign(tx, key, index, type) {
return total;
};
KeyRing.prototype.__defineGetter__('privateKey', function() {
return this.getPrivateKey();
});
KeyRing.prototype.__defineGetter__('publicKey', function() {
return this.getPublicKey();
});

View File

@ -986,7 +986,7 @@ MTX.prototype.selectCoins = function selectCoins(coins, options) {
if (options.confirmed && coin.height === -1)
continue;
if (options.height != null && coin.coinbase) {
if (options.height >= 0 && coin.coinbase) {
if (options.height + 1 < coin.height + constants.tx.COINBASE_MATURITY)
continue;
}

View File

@ -45,44 +45,6 @@ var BufferWriter = require('./writer');
* (default=account key "address").
*/
function MasterKey(options) {
this.json = options.json;
this.key = options.key || null;
}
MasterKey.prototype.decrypt = function decrypt(passphrase) {
if (this.key)
return this.key;
if (!this.json.encrypted)
return bcoin.hd.fromJSON(this.json);
return bcoin.hd.fromJSON(this.json, passphrase);
};
MasterKey.prototype.toJSON = function toJSON() {
return this.json;
};
MasterKey.fromKey = function fromKey(key) {
return new MasterKey({
key: key,
json: key.toJSON()
});
};
MasterKey.fromJSON = function fromJSON(json) {
var key;
if (!json.encrypted)
key = bcoin.hd.fromJSON(json);
return new MasterKey({
key: key,
json: json
});
};
function Wallet(options) {
var i, key;
@ -129,7 +91,7 @@ function Wallet(options) {
this.m = options.m || 1;
this.n = options.n || 1;
this.cache = new bcoin.lru(20);
this.cache = new bcoin.lru(20, 1);
if (this.n > 1)
this.type = 'multisig';
@ -1114,7 +1076,7 @@ Wallet.prototype.scan = function scan(getByAddress, callback) {
var self = this;
var res = false;
return this._scan({}, getByAddress, function(err, depth, txs) {
return this._scan(getByAddress, function(err, depth, txs) {
if (err)
return callback(err);
@ -1124,42 +1086,13 @@ Wallet.prototype.scan = function scan(getByAddress, callback) {
if (self.setReceiveDepth(depth.receiveDepth + 1))
res = true;
if (self.provider && self.provider.addTX) {
utils.forEachSerial(txs, function(tx, next) {
self.addTX(tx, next);
}, function(err) {
if (err)
return callback(err);
return callback(null, res, txs);
});
return;
}
return callback(null, res, txs);
});
};
/**
* Clone the wallet (used for scanning).
* @returns {Wallet}
*/
Wallet.prototype.clone = function clone() {
var passphrase = this.options.passphrase;
var wallet;
delete this.options.passphrase;
wallet = Wallet.fromJSON(this.toJSON());
this.options.passphrase = passphrase;
return wallet;
};
Wallet.prototype._scan = function _scan(options, getByAddress, callback) {
Wallet.prototype._scan = function _scan(getByAddress, callback) {
var self = this;
var depth = { changeDepth: 0, receiveDepth: 0 };
var wallet = this.clone();
var all = [];
assert(this._initialized);
@ -1170,7 +1103,7 @@ Wallet.prototype._scan = function _scan(options, getByAddress, callback) {
var gap = 0;
(function next() {
var address = wallet.deriveAddress(change, addressIndex++);
var address = self.deriveAddress(change, addressIndex++);
getByAddress(address.getAddress(), function(err, txs) {
var result;
@ -1250,10 +1183,15 @@ Wallet.prototype.scriptInputs = function scriptInputs(tx, index) {
Wallet.prototype.sign = function sign(tx, passphrase, index, type) {
var addresses = this.deriveInputs(tx, index);
var key = this.master.decrypt(passphrase);
var total = 0;
var i, address, key;
try {
key = this.master.decrypt(passphrase);
} catch (e) {
return 0;
}
for (i = 0; i < addresses.length; i++) {
address = addresses[i];
@ -1499,10 +1437,6 @@ Wallet.prototype.getAddress = function getAddress() {
return this.receiveAddress.getAddress();
};
Wallet.prototype.__defineGetter__('privateKey', function() {
return this.getPrivateKey();
});
Wallet.prototype.__defineGetter__('publicKey', function() {
return this.getPublicKey();
});
@ -1681,6 +1615,45 @@ Wallet.isWallet = function isWallet(obj) {
&& obj.deriveAddress === 'function';
};
function MasterKey(options) {
this.json = options.json;
this.key = options.key || null;
}
MasterKey.prototype.decrypt = function decrypt(passphrase) {
if (this.key)
return this.key;
if (!this.json.encrypted)
return bcoin.hd.fromJSON(this.json);
return bcoin.hd.fromJSON(this.json, passphrase);
};
MasterKey.prototype.toJSON = function toJSON() {
return this.json;
};
MasterKey.fromKey = function fromKey(key) {
return new MasterKey({
key: key,
json: key.toJSON()
});
};
MasterKey.fromJSON = function fromJSON(json) {
var key;
if (!json.encrypted)
key = bcoin.hd.fromJSON(json);
return new MasterKey({
key: key,
json: json
});
};
/*
* Expose
*/

View File

@ -252,6 +252,37 @@ WalletDB.prototype.syncOutputDepth = function syncOutputDepth(id, tx, callback)
});
};
/**
* Derive an address.
* @param {WalletID} id
* @param {Boolean} change
* @param {Function} callback
*/
WalletDB.prototype.createAddress = function createAddress(id, change, callback) {
var self = this;
var address;
callback = utils.ensure(callback);
this.get(id, function(err, json) {
if (err)
return callback(err);
if (!wallet)
return callback(new Error('No wallet.'));
address = wallet.createAddress(change);
self.save(wallet, function(err) {
if (err)
return callback(err);
return callback(null, address);
});
});
};
/**
* Add a public account/purpose key to the wallet for multisig.
* @param {WalletID} id
@ -260,7 +291,7 @@ WalletDB.prototype.syncOutputDepth = function syncOutputDepth(id, tx, callback)
* @param {Function} callback
*/
WalletDB.prototype.addKey = function addKey(id, key, callback) {
WalletDB.prototype.modifyKey = function modifyKey(id, key, remove, callback) {
var self = this;
callback = utils.ensure(callback);
@ -273,37 +304,10 @@ WalletDB.prototype.addKey = function addKey(id, key, callback) {
return callback(new Error('No wallet.'));
try {
wallet.addKey(key);
} catch (e) {
return callback(e);
}
self.save(wallet, callback);
});
};
/**
* Remove a public account/purpose key to the wallet for multisig.
* @param {WalletID} id
* @param {HDPublicKey|Base58String} key - Account (bip44) or Purpose
* (bip45) key (can be in base58 form).
* @param {Function} callback
*/
WalletDB.prototype.removeKey = function removeKey(id, key, callback) {
var self = this;
callback = utils.ensure(callback);
this.get(id, function(err, wallet) {
if (err)
return callback(err);
if (!wallet)
return callback(new Error('No wallet.'));
try {
wallet.removeKey(key);
if (!remove)
wallet.addKey(key);
else
wallet.removeKey(key);
} catch (e) {
return callback(e);
}
@ -698,6 +702,41 @@ WalletDB.prototype.provider = function provider() {
return new Provider(this);
};
WalletDB.prototype.register = function register(id, provider) {
if (!this.listeners[id])
this.listeners[id] = [];
if (this.listeners[id].indexOf(provider) !== -1)
this.listeners[id].push(provider);
};
WalletDB.prototype.unregister = function unregister(id, provider) {
var listeners = this.listeners[id];
var i;
if (!listeners)
return;
i = listeners.indexOf(provider);
if (i !== -1)
listeners.splice(i, 1);
if (listeners.length === 0)
delete this.listeners[id];
};
WalletDB.prototype.fire = function fire(id) {
var args = Array.prototype.slice.call(arguments, 1);
var listeners = this.listeners[id];
var i;
if (!listeners)
return;
for (i = 0; i < listeners.length; i++)
listeners.emit.apply(listener, args);
};
/**
* Represents {@link Wallet} Provider. This is what
* allows the {@link Wallet} object to access
@ -959,6 +998,66 @@ Provider.prototype.save = function save(wallet, callback) {
return this.db.save(wallet, callback);
};
/**
* Notify the provider backend that a new address was
* derived (not technically necessary if you're
* implementing a provider).
* @param {Wallet} wallet
* @param {Address} address
*/
Provider.prototype.addKey = function addKey(key, callback) {
return this.db.addKey(this.id, key, false, callback);
};
/**
* Notify the provider backend that a new address was
* derived (not technically necessary if you're
* implementing a provider).
* @param {Wallet} wallet
* @param {Address} address
*/
// Provider.prototype.deriveInputs = function deriveInputs(tx, index, callback) {
// return this.db.deriveInputs(this.id, tx, index, callback);
// };
/**
* Notify the provider backend that a new address was
* derived (not technically necessary if you're
* implementing a provider).
* @param {Wallet} wallet
* @param {Address} address
*/
Provider.prototype.removeKey = function removeKey(key, callback) {
return this.db.addKey(this.id, key, true, callback);
};
/**
* Notify the provider backend that a new address was
* derived (not technically necessary if you're
* implementing a provider).
* @param {Wallet} wallet
* @param {Address} address
*/
Provider.prototype.createReceive = function createReceive(callback) {
return this.db.createAddress(this.id, false, callback);
};
/**
* Notify the provider backend that a new address was
* derived (not technically necessary if you're
* implementing a provider).
* @param {Wallet} wallet
* @param {Address} address
*/
Provider.prototype.createChange = function createChange(callback) {
return this.db.createAddress(this.id, true, callback);
};
/**
* Zap stale transactions.
* @param {Number} now - Current time.

View File

@ -486,134 +486,165 @@ describe('Wallet', function() {
n: 3
};
wdb.create(utils.merge({}, options), function(err, w1) {
assert.ifError(err);
wdb.create(utils.merge({}, options), function(err, w2) {
assert.ifError(err);
wdb.create(utils.merge({}, options), function(err, w3) {
var w1, w2, w3, receive;
utils.serial([
function(next) {
wdb.create(utils.merge({}, options), function(err, w1_) {
assert.ifError(err);
wdb.create({}, function(err, receive) {
w1 = w1_;
next();
});
},
function(next) {
wdb.create(utils.merge({}, options), function(err, w2_) {
assert.ifError(err);
w2 = w2_;
next();
});
},
function(next) {
wdb.create(utils.merge({}, options), function(err, w3_) {
assert.ifError(err);
w3 = w3_;
next();
});
},
function(next) {
wdb.create({}, function(err, receive_) {
assert.ifError(err);
receive = receive_;
next();
});
},
], function(err) {
assert.ifError(err);
w1.addKey(w2);
w1.addKey(w3);
w2.addKey(w1);
w2.addKey(w3);
w3.addKey(w1);
w3.addKey(w2);
utils.serial([
wdb.save.bind(wdb, w1),
wdb.save.bind(wdb, w2),
wdb.save.bind(wdb, w3),
wdb.save.bind(wdb, receive)
], function(err) {
assert.ifError(err);
// w3 = bcoin.wallet.fromJSON(w3.toJSON());
// Our p2sh address
var addr = w1.getAddress();
if (witness)
assert(bcoin.address.parseBase58(addr).type === 'witnessscripthash');
else
assert(bcoin.address.parseBase58(addr).type === 'scripthash');
assert.equal(w1.getAddress(), addr);
assert.equal(w2.getAddress(), addr);
assert.equal(w3.getAddress(), addr);
var paddr = w1.getProgramAddress();
assert.equal(w1.getProgramAddress(), paddr);
assert.equal(w2.getProgramAddress(), paddr);
assert.equal(w3.getProgramAddress(), paddr);
// Add a shared unspent transaction to our wallets
var utx = bcoin.mtx();
if (bullshitNesting)
utx.addOutput({ address: paddr, value: 5460 * 10 });
else
utx.addOutput({ address: addr, value: 5460 * 10 });
utx.addInput(dummyInput);
assert(w1.ownOutput(utx.outputs[0]));
// Simulate a confirmation
utx.ps = 0;
utx.ts = 1;
utx.height = 1;
assert.equal(w1.receiveDepth, 1);
wdb.addTX(utx, function(err) {
assert.ifError(err);
wdb.addTX(utx, function(err) {
assert.ifError(err);
w1.addKey(w2);
w1.addKey(w3);
w2.addKey(w1);
w2.addKey(w3);
w3.addKey(w1);
w3.addKey(w2);
// w3 = bcoin.wallet.fromJSON(w3.toJSON());
// Our p2sh address
var addr = w1.getAddress();
if (witness)
assert(bcoin.address.parseBase58(addr).type === 'witnessscripthash');
else
assert(bcoin.address.parseBase58(addr).type === 'scripthash');
assert.equal(w1.getAddress(), addr);
assert.equal(w2.getAddress(), addr);
assert.equal(w3.getAddress(), addr);
var paddr = w1.getProgramAddress();
assert.equal(w1.getProgramAddress(), paddr);
assert.equal(w2.getProgramAddress(), paddr);
assert.equal(w3.getProgramAddress(), paddr);
// Add a shared unspent transaction to our wallets
var utx = bcoin.mtx();
if (bullshitNesting)
utx.addOutput({ address: paddr, value: 5460 * 10 });
else
utx.addOutput({ address: addr, value: 5460 * 10 });
utx.addInput(dummyInput);
assert(w1.ownOutput(utx.outputs[0]));
// Simulate a confirmation
utx.ps = 0;
utx.ts = 1;
utx.height = 1;
assert.equal(w1.receiveDepth, 1);
wdb.addTX(utx, function(err) {
assert.ifError(err);
wdb.addTX(utx, function(err) {
assert.equal(w1.receiveDepth, 2);
assert.equal(w1.changeDepth, 1);
assert(w1.getAddress() !== addr);
addr = w1.getAddress();
assert.equal(w1.getAddress(), addr);
assert.equal(w2.getAddress(), addr);
assert.equal(w3.getAddress(), addr);
// Create a tx requiring 2 signatures
var send = bcoin.mtx();
send.addOutput({ address: receive.getAddress(), value: 5460 });
assert(!send.verify(null, true, flags));
w1.fill(send, { rate: 10000, round: true }, function(err) {
assert.ifError(err);
wdb.addTX(utx, function(err) {
w1.sign(send);
assert(!send.verify(null, true, flags));
w2.sign(send);
assert(send.verify(null, true, flags));
assert.equal(w1.changeDepth, 1);
var change = w1.changeAddress.getAddress();
assert.equal(w1.changeAddress.getAddress(), change);
assert.equal(w2.changeAddress.getAddress(), change);
assert.equal(w3.changeAddress.getAddress(), change);
// Simulate a confirmation
send.ps = 0;
send.ts = 1;
send.height = 1;
wdb.addTX(send, function(err) {
assert.ifError(err);
assert.equal(w1.receiveDepth, 2);
assert.equal(w1.changeDepth, 1);
assert(w1.getAddress() !== addr);
addr = w1.getAddress();
assert.equal(w1.getAddress(), addr);
assert.equal(w2.getAddress(), addr);
assert.equal(w3.getAddress(), addr);
// Create a tx requiring 2 signatures
var send = bcoin.mtx();
send.addOutput({ address: receive.getAddress(), value: 5460 });
assert(!send.verify(null, true, flags));
w1.fill(send, { rate: 10000, round: true }, function(err) {
wdb.addTX(send, function(err) {
assert.ifError(err);
w1.sign(send);
assert(!send.verify(null, true, flags));
w2.sign(send);
assert(send.verify(null, true, flags));
assert.equal(w1.changeDepth, 1);
var change = w1.changeAddress.getAddress();
assert.equal(w1.changeAddress.getAddress(), change);
assert.equal(w2.changeAddress.getAddress(), change);
assert.equal(w3.changeAddress.getAddress(), change);
// Simulate a confirmation
send.ps = 0;
send.ts = 1;
send.height = 1;
wdb.addTX(send, function(err) {
assert.ifError(err);
wdb.addTX(send, function(err) {
assert.ifError(err);
wdb.addTX(send, function(err) {
assert.ifError(err);
assert.equal(w1.receiveDepth, 2);
assert.equal(w1.changeDepth, 2);
assert.equal(w1.receiveDepth, 2);
assert.equal(w1.changeDepth, 2);
assert(w1.getAddress() === addr);
assert(w1.changeAddress.getAddress() !== change);
change = w1.changeAddress.getAddress();
assert.equal(w1.changeAddress.getAddress(), change);
assert.equal(w2.changeAddress.getAddress(), change);
assert.equal(w3.changeAddress.getAddress(), change);
assert(w1.getAddress() === addr);
assert(w1.changeAddress.getAddress() !== change);
change = w1.changeAddress.getAddress();
assert.equal(w1.changeAddress.getAddress(), change);
assert.equal(w2.changeAddress.getAddress(), change);
assert.equal(w3.changeAddress.getAddress(), change);
if (witness)
send.inputs[0].witness.items[2] = new Buffer([]);
else
send.inputs[0].script.code[2] = 0;
if (witness)
send.inputs[0].witness.items[2] = new Buffer([]);
else
send.inputs[0].script.code[2] = 0;
assert(!send.verify(null, true, flags));
assert.equal(send.getFee(), 10000);
assert(!send.verify(null, true, flags));
assert.equal(send.getFee(), 10000);
w3 = bcoin.wallet.fromJSON(w3.toJSON());
assert.equal(w3.receiveDepth, 2);
assert.equal(w3.changeDepth, 2);
assert.equal(w3.getAddress(), addr);
assert.equal(w3.changeAddress.getAddress(), change);
w3 = bcoin.wallet.fromJSON(w3.toJSON());
assert.equal(w3.receiveDepth, 2);
assert.equal(w3.changeDepth, 2);
assert.equal(w3.getAddress(), addr);
assert.equal(w3.changeAddress.getAddress(), change);
cb();
});
});
cb();
});
});
});