more refactoring.

This commit is contained in:
Christopher Jeffrey 2016-04-04 23:53:05 -07:00
parent d423e33e03
commit 86312d9241
2 changed files with 110 additions and 83 deletions

View File

@ -539,8 +539,8 @@ MTX.prototype.signInput = function signInput(index, addr, type) {
return signatures === m;
};
MTX.prototype.isSigned = function isSigned(m) {
var i, input, prev, vector, len, j;
MTX.prototype.isSigned = function isSigned() {
var i, input, prev, vector, m, len, j;
var total = 0;
for (i = 0; i < this.inputs.length; i++) {
@ -567,10 +567,7 @@ MTX.prototype.isSigned = function isSigned(m) {
// If the output script is a witness program,
// we have to switch the vector to the witness
// and potentially alter the length. Note that
// witnesses are stack items, so the `dummy`
// _has_ to be an empty buffer (what OP_0
// pushes onto the stack).
// and potentially alter the length.
if (prev.isWitnessScripthash()) {
prev = input.witness.getRedeem();
vector = input.witness.items;
@ -602,13 +599,7 @@ MTX.prototype.isSigned = function isSigned(m) {
if (len - 1 !== m)
return false;
} else {
for (j = 0; j < vector.length; j++) {
if (Script.isSignatureEncoding(vector[j]))
total++;
}
if (total !== m)
return false;
return false;
}
}
@ -684,15 +675,25 @@ MTX.prototype.scriptOutput = function scriptOutput(index, options) {
};
MTX.prototype.isScripted = function isScripted() {
if (this.inputs.length === 0)
return false;
var i, input;
if (this.outputs.length === 0)
return false;
return this.inputs.every(function(input) {
return input.script.code.length > 0 || input.witness.items.length > 0;
});
if (this.inputs.length === 0)
return false;
for (i = 0; i < this.inputs.length; i++) {
input = this.inputs[i];
if (input.script.code.length === 0)
return false;
if (input.witness.items.length === 0)
return false;
}
return true;
};
MTX.prototype.maxSize = function maxSize(options, force) {
@ -855,25 +856,20 @@ MTX.prototype.maxSize = function maxSize(options, force) {
};
MTX.prototype.selectCoins = function selectCoins(coins, options) {
var chosen, lastAdded, tx, outputValue;
var i, size, change, fee, tryFree, minValue;
var chosen = [];
var index = 0;
var tx = this.clone();
var outputValue = tx.getOutputValue();
var tryFree, i, size, change, fee, minValue;
if (!options || typeof options !== 'object') {
options = {
changeAddress: arguments[1],
fee: arguments[2]
};
}
if (!options)
options = {};
tx = this.clone();
tx.inputs.length = 0;
chosen = [];
lastAdded = 0;
outputValue = tx.getOutputValue();
tryFree = options.free;
// Null the inputs if there are any.
tx.inputs.length = 0;
if (options.confirmed) {
coins = coins.filter(function(coin) {
return coin.height !== -1;
@ -895,7 +891,7 @@ MTX.prototype.selectCoins = function selectCoins(coins, options) {
}
function total() {
if (options.subtractFee)
if (options.subtractFee != null)
return outputValue;
return outputValue.add(fee);
}
@ -905,15 +901,13 @@ MTX.prototype.selectCoins = function selectCoins(coins, options) {
}
function addCoins() {
var i, index;
for (i = lastAdded; i < coins.length; i++) {
while (index < coins.length) {
// Add new inputs until TX will have enough
// funds to cover both minimum post cost
// and fee.
tx.addInput(coins[i]);
chosen.push(coins[i]);
lastAdded++;
tx.addInput(coins[index]);
chosen.push(coins[index]);
index++;
if (options.selection === 'all')
continue;
@ -939,6 +933,9 @@ MTX.prototype.selectCoins = function selectCoins(coins, options) {
// calculate maximum TX size.
tx.addOutput({
address: options.changeAddress,
// In case we don't have a change address,
// use a fake p2pkh output to gauge size.
keyHash: constants.zeroHash.slice(0, 20),
value: new bn(0)
});
@ -961,10 +958,10 @@ MTX.prototype.selectCoins = function selectCoins(coins, options) {
else
fee = tx.getMaxFee(size);
// Failed to get enough funds, add more inputs.
// Failed to get enough funds, add more coins.
if (!isFull())
addCoins();
} while (!isFull() && lastAdded < coins.length);
} while (!isFull() && index < coins.length);
}
if (!isFull()) {
@ -1012,50 +1009,37 @@ MTX.prototype.selectCoins = function selectCoins(coins, options) {
};
MTX.prototype.fill = function fill(coins, options) {
var self = this;
var result, err;
var result, i;
assert(this.ts === 0, 'Cannot modify a confirmed tx.');
assert(this.inputs.length === 0, 'TX is already filled.');
if (!options || typeof options !== 'object') {
options = {
changeAddress: arguments[1],
fee: arguments[2]
};
}
assert(coins);
assert(options, '`options` are required.');
assert(options.changeAddress, '`changeAddress` is required.');
// Select necessary coins.
result = this.selectCoins(coins, options);
result.coins.forEach(function(coin) {
self.addInput(coin);
});
// Add coins to transaction.
for (i = 0; i < result.coins.length; i++)
this.addInput(result.coins[i]);
if (result.change.cmpn(constants.tx.dustThreshold) < 0) {
// Do nothing. Change is added to fee.
assert.equal(
this.getFee().toNumber(),
result.fee.add(result.change).toNumber()
);
assert(this.getFee().cmp(result.fee.add(result.change)) === 0);
this.changeIndex = -1;
} else {
// Add a change output.
this.addOutput({
address: options.changeAddress,
value: result.change
});
this.changeIndex = this.outputs.length - 1;
assert.equal(this.getFee().toNumber(), result.fee.toNumber());
assert(this.getFee().cmp(result.fee) === 0);
}
return result;
};
// https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki
MTX.prototype.sortMembers = function sortMembers() {
var changeOutput;

View File

@ -202,6 +202,8 @@ Wallet.prototype.addKey = function addKey(key) {
if (key instanceof bcoin.hd.privateKey)
key = key.hdPublicKey;
assert(key instanceof bcoin.hd, 'Must add HD keys to wallet.');
if (this.derivation === 'bip44') {
if (!key || !key.isAccount44())
throw new Error('Must add HD account keys to BIP44 wallet.');
@ -246,6 +248,8 @@ Wallet.prototype.removeKey = function removeKey(key) {
if (key instanceof bcoin.hd.privateKey)
key = key.hdPublicKey;
assert(key instanceof bcoin.hd, 'Must add HD keys to wallet.');
if (this.derivation === 'bip44') {
if (!key || !key.isAccount44())
throw new Error('Must add HD account keys to BIP44 wallet.');
@ -616,6 +620,10 @@ Wallet.prototype.createTX = function createTX(options, outputs, callback) {
});
};
Wallet.prototype.deriveInput = function deriveInput(tx, i) {
return this.deriveInputs(tx.inputs[i])[0];
};
Wallet.prototype.deriveInputs = function deriveInputs(tx) {
var paths = this.getInputPaths(tx);
var addresses = [];
@ -731,7 +739,7 @@ Wallet.prototype.getRedeem = function getRedeem(hash, prefix) {
else if (hash.length === 32)
prefix = 'witnessscripthash';
else
assert(false, 'Unknown hash length.');
return;
}
addr = bcoin.address.compileHash(hash, prefix);
@ -759,8 +767,9 @@ Wallet.prototype.zap = function zap(now, age, callback) {
Wallet.prototype.scan = function scan(txByAddress, callback) {
var self = this;
var res = false;
var i;
return this._scan({}, txByAddress, function(err, depth) {
return this._scan({}, txByAddress, function(err, depth, txs) {
if (err)
return callback(err);
@ -770,25 +779,50 @@ Wallet.prototype.scan = function scan(txByAddress, callback) {
if (self.setReceiveDepth(depth.receiveDepth + 1))
res = true;
return callback(null, res);
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);
});
};
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, txByAddress, callback) {
var self = this;
var depth = { changeDepth: 0, receiveDepth: 0 };
var wallet = this.clone();
var all = [];
assert(this._initialized);
return (function chainCheck(change) {
(function chainCheck(change) {
var addressIndex = 0;
var total = 0;
var gap = 0;
return (function next() {
var address = self.deriveAddress(change, addressIndex++);
(function next() {
var address = wallet.deriveAddress(change, addressIndex++);
return txByAddress(address.getAddress(), function(err, txs) {
txByAddress(address.getAddress(), function(err, txs) {
var result;
if (err)
@ -805,7 +839,7 @@ Wallet.prototype._scan = function _scan(options, txByAddress, callback) {
result = false;
if (Array.isArray(txs) && (txs[0] instanceof bcoin.tx))
txs.forEach(function(tx) { self.addTX(tx); });
all = all.concat(txs);
}
if (result) {
@ -827,7 +861,7 @@ Wallet.prototype._scan = function _scan(options, txByAddress, callback) {
if (change === false)
return chainCheck(true);
return callback(null, depth);
return callback(null, depth, all);
});
})();
})(false);
@ -835,26 +869,35 @@ Wallet.prototype._scan = function _scan(options, txByAddress, callback) {
Wallet.prototype.scriptInputs = function scriptInputs(tx, index) {
var addresses = this.deriveInputs(tx);
var total = 0;
var i;
return addresses.reduce(function(total, address) {
return total + address.scriptInputs(tx, index);
}, 0);
for (i = 0; i < addresses.length; i++)
total += addresses[i].scriptInputs(tx, index);
return total;
};
Wallet.prototype.signInputs = function signInputs(tx, type, index) {
var addresses = this.deriveInputs(tx);
var total = 0;
var i;
return addresses.reduce(function(total, address) {
return total + address.signInputs(tx, type, index);
}, 0);
for (i = 0; i < addresses.length; i++)
total += addresses[i].signInputs(tx, type, index);
return total;
};
Wallet.prototype.sign = function sign(tx, type, index) {
var addresses = this.deriveInputs(tx);
var total = 0;
var i;
return addresses.reduce(function(total, address) {
return total + address.sign(tx, type, index);
}, 0);
for (i = 0; i < addresses.length; i++)
total += addresses[i].sign(tx, type, index);
return total;
};
Wallet.prototype.addTX = function addTX(tx, callback) {