refactor tx.

This commit is contained in:
Christopher Jeffrey 2016-02-05 22:46:04 -08:00
parent 7de73c670c
commit 18f0a55779
7 changed files with 220 additions and 192 deletions

View File

@ -7,6 +7,7 @@
var bn = require('bn.js');
var bcoin = require('../bcoin');
var utils = bcoin.utils;
var assert = utils.assert;
var constants = bcoin.protocol.constants;
/**
@ -68,10 +69,8 @@ Input.prototype.__defineGetter__('data', function() {
data = Input.getData(this);
if (!this.tx || this.tx.ps === 0) {
if (this.script.length && this.prevout.tx)
utils.hidden(this, '_data', data);
}
if (this.script.length && this.output)
utils.hidden(this, '_data', data);
return data;
});
@ -96,8 +95,12 @@ Input.prototype.__defineGetter__('hash', function() {
return this.data.scriptHash || this.hashes[0];
});
Input.prototype.__defineGetter__('id', function() {
return this.address || this.getID();
});
Input.prototype.__defineGetter__('address', function() {
return this.data.scriptAddress || this.addresses[0] || this.getID();
return this.data.scriptAddress || this.addresses[0];
});
Input.prototype.__defineGetter__('signatures', function() {
@ -158,7 +161,7 @@ Input.prototype.__defineGetter__('output', function() {
Input.prototype.__defineGetter__('value', function() {
if (!this.output)
return new bn(0);
return;
return this.output.value;
});
@ -221,10 +224,9 @@ Input.prototype.__defineGetter__('scriptaddr', function() {
// }
Input.getData = function getData(input) {
var def, data, output;
var def, data;
if (!input || !input.script)
return;
assert(input instanceof Input);
def = {
side: 'input',
@ -233,12 +235,10 @@ Input.getData = function getData(input) {
seq: input.seq
};
if (input.prevout) {
def.prev = input.prevout.hash;
def.index = input.prevout.index;
}
def.prev = input.prevout.hash;
def.index = input.prevout.index;
if (input.prevout && +input.prevout.hash === 0) {
if (+input.prevout.hash === 0) {
data = bcoin.script.getCoinbaseData(input.script);
return utils.merge(def, data, {
type: 'coinbase',
@ -246,18 +246,40 @@ Input.getData = function getData(input) {
});
}
if (input.prevout && input.prevout.tx) {
output = input.prevout.tx.outputs[input.prevout.index];
if (output) {
data = bcoin.script.getInputData(input.script, output.script);
data.value = output.value;
return utils.merge(def, data);
}
if (input.output) {
data = bcoin.script.getInputData(input.script, input.output.script);
data.value = input.output.value;
return utils.merge(def, data);
}
return utils.merge(def, bcoin.script.getInputData(input.script));
};
Input.prototype.getData = function getData() {
return Input.getData(this);
};
Input.prototype.getAddresses = function getAddresses() {
return this.getData().addresses;
};
Input.prototype.getScriptAddress = function getScriptAddress() {
return this.getData().scriptAddress;
};
Input.prototype.getKeyAddress = function getKeyAddress() {
return this.getData().addresses[0];
};
Input.prototype.getAddress = function getAddress() {
var data = this.getData();
if (data.scriptAddress)
return data.scriptAddress;
return data.addresses[0];
};
Input.prototype.isFinal = function isFinal() {
return this.sequence === 0xffffffff;
};
@ -271,9 +293,9 @@ Input.prototype.getID = function getID() {
Input.prototype.getLocktime = function getLocktime() {
var output, redeem, lock, type;
assert(this.prevout.tx);
assert(this.output);
output = this.prevout.tx.outputs[this.prevout.index];
output = this.output;
redeem = output.script;
if (bcoin.script.isScripthash(redeem))
@ -349,8 +371,8 @@ Input.prototype.inspect = function inspect() {
text: this.text,
locktime: this.locktime,
value: utils.btc(output.value),
script: bcoin.script.format(this.script)[0],
redeem: this.redeem ? bcoin.script.format(this.redeem)[0] : null,
script: bcoin.script.format(this.script),
redeem: this.redeem ? bcoin.script.format(this.redeem) : null,
seq: this.seq,
output: output
};

View File

@ -48,10 +48,8 @@ Output.prototype.__defineGetter__('data', function() {
data = Output.getData(this);
if (!this.tx || this.tx.ps === 0) {
if (this.script.length && this.value.cmpn(0) > 0)
utils.hidden(this, '_data', data);
}
if (this.script.length)
utils.hidden(this, '_data', data);
return data;
});
@ -72,8 +70,12 @@ Output.prototype.__defineGetter__('hash', function() {
return this.data.scriptHash || this.hashes[0];
});
Output.prototype.__defineGetter__('id', function() {
return this.address || this.getID();
});
Output.prototype.__defineGetter__('address', function() {
return this.data.scriptAddress || this.addresses[0] || this.getID();
return this.data.scriptAddress || this.addresses[0];
});
Output.prototype.__defineGetter__('signatures', function() {
@ -178,8 +180,7 @@ Output.prototype.__defineGetter__('scriptaddr', function() {
Output.getData = function getData(output) {
var def;
if (!output || !output.script)
return;
assert(output instanceof Output);
def = {
side: 'output',
@ -190,6 +191,31 @@ Output.getData = function getData(output) {
return utils.merge(def, bcoin.script.getOutputData(output.script));
};
Output.prototype.getData = function getData() {
return Output.getData(this);
};
Output.prototype.getAddresses = function getAddresses() {
return this.getData().addresses;
};
Output.prototype.getScriptAddress = function getScriptAddress() {
return this.getData().scriptAddress;
};
Output.prototype.getKeyAddress = function getKeyAddress() {
return this.getData().addresses[0];
};
Output.prototype.getAddress = function getAddress() {
var data = this.getData();
if (data.scriptAddress)
return data.scriptAddress;
return data.addresses[0];
};
Output.prototype.getID = function getID() {
var data = bcoin.script.encode(this.script);
var hash = utils.toHex(utils.ripesha(data));
@ -221,7 +247,7 @@ Output.prototype.inspect = function inspect() {
text: this.text,
locktime: this.locktime,
value: utils.btc(this.value),
script: bcoin.script.format(this.script)[0]
script: bcoin.script.format(this.script)
};
};

View File

@ -999,7 +999,7 @@ Pool.prototype.unwatch = function unwatch(id) {
// See "Filter matching algorithm":
// https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki
Pool.prototype.isWatched = function(tx, bloom) {
var i, input, output, prev;
var i, input, output;
if (!bloom)
bloom = this.bloom;
@ -1039,9 +1039,8 @@ Pool.prototype.isWatched = function(tx, bloom) {
return true;
// Test the prev_out script
if (input.prevout.tx) {
prev = input.prevout.tx.outputs[input.prevout.index];
if (testScript(prev.script))
if (input.output) {
if (testScript(input.output.script))
return true;
}

View File

@ -278,6 +278,20 @@ script.getSubscript = function getSubscript(s, lastSep) {
return res;
};
script.concat = function concat(scripts) {
var s = [];
var i;
s.push(scripts[0]);
for (i = 1; i < scripts.length; i++) {
s.push('codeseparator');
s = s.concat(scripts[i]);
}
return s;
};
script.checksig = function checksig(msg, sig, key) {
if (key.getPublic)
key = key.getPublic();
@ -2067,26 +2081,20 @@ script.isLowDER = function isLowDER(sig) {
return true;
};
script.format = function format(input, output) {
script.format = function format(s) {
var scripts = [];
var prev, redeem;
if (Array.isArray(input)) {
scripts.push(input);
} else if (Array.isArray(output)) {
scripts.push(output);
} else if (input) {
scripts.push(input.script);
if (input.prevout.tx && input.prevout.tx.outputs[input.prevout.index]) {
prev = input.prevout.tx.outputs[input.prevout.index].script;
scripts.push(prev);
if (script.isScripthash(prev)) {
redeem = script.decode(input.script[input.script.length - 1]);
scripts.push(redeem);
}
if (Array.isArray(s)) {
scripts.push(s);
} else if (s instanceof bcoin.input) {
scripts.push(s.script);
if (s.output) {
scripts.push(s.output.script);
if (script.isScripthash(s.output.script))
scripts.push(script.getRedeem(s.script));
}
} else if (output) {
scripts.push(output.script);
} else if (s instanceof bcoin.output) {
scripts.push(s.script);
}
scripts = scripts.map(function(script) {
@ -2102,7 +2110,7 @@ script.format = function format(input, output) {
}).join(' ');
});
return scripts;
return script.concat(scripts);
};
script.isPushOnly = function isPushOnly(s) {
@ -2111,8 +2119,6 @@ script.isPushOnly = function isPushOnly(s) {
op = s[i];
if (Array.isArray(op) || op === '1negate' || (op >= 1 && op <= 16))
continue;
if (constants.opcodes[op] == null)
return false;
return false;
}
return true;

View File

@ -286,33 +286,28 @@ TXPool.prototype.getAll = function getAll(address) {
};
TXPool.prototype._addOutput = function _addOutput(tx, i, remove) {
var i, data, address, addr, output;
output = tx.outputs[i];
data = bcoin.script.getOutputData(output.script);
var output = tx.outputs[i];
var address;
if (!this._wallet.ownOutput(tx, i))
return;
if (data.scriptAddress)
data.addresses = [data.scriptAddress];
address = output.getAddress();
for (i = 0; i < data.addresses.length; i++) {
addr = data.addresses[i];
if (!this._addresses[addr]) {
this._addresses[addr] = {
received: new bn(0),
sent: new bn(0),
balance: new bn(0)
};
}
if (!remove) {
this._addresses[addr].balance.iadd(output.value);
this._addresses[addr].received.iadd(output.value);
} else {
this._addresses[addr].balance.isub(output.value);
this._addresses[addr].received.isub(output.value);
}
if (!this._addresses[address]) {
this._addresses[address] = {
received: new bn(0),
sent: new bn(0),
balance: new bn(0)
};
}
if (!remove) {
this._addresses[address].balance.iadd(output.value);
this._addresses[address].received.iadd(output.value);
} else {
this._addresses[address].balance.isub(output.value);
this._addresses[address].received.isub(output.value);
}
if (!remove) {
@ -329,36 +324,31 @@ TXPool.prototype._removeOutput = function _removeOutput(tx, i) {
};
TXPool.prototype._addInput = function _addInput(tx, i, remove) {
var i, input, prev, data, address, addr, output;
input = tx.inputs[i];
assert(input.prevout.tx);
var input = tx.inputs[i];
var prev, address;
if (!this._wallet.ownOutput(input.prevout.tx, input.prevout.index))
return;
prev = input.prevout.tx.outputs[input.prevout.index];
data = bcoin.script.getInputData(input.script, prev.script);
assert(input.output);
if (data.scriptAddress)
data.addresses = [data.scriptAddress];
prev = input.output;
address = prev.getAddress();
for (i = 0; i < data.addresses.length; i++) {
addr = data.addresses[i];
if (!this._addresses[addr]) {
this._addresses[addr] = {
received: new bn(0),
sent: new bn(0),
balance: new bn(0)
};
}
if (!remove) {
this._addresses[addr].balance.isub(prev.value);
this._addresses[addr].sent.iadd(prev.value);
} else {
this._addresses[addr].balance.iadd(prev.value);
this._addresses[addr].sent.isub(prev.value);
}
if (!this._addresses[address]) {
this._addresses[address] = {
received: new bn(0),
sent: new bn(0),
balance: new bn(0)
};
}
if (!remove) {
this._addresses[address].balance.isub(prev.value);
this._addresses[address].sent.iadd(prev.value);
} else {
this._addresses[address].balance.iadd(prev.value);
this._addresses[address].sent.isub(prev.value);
}
if (!remove) {

View File

@ -206,16 +206,16 @@ TX.prototype.scriptInput = function scriptInput(index, pub, redeem) {
// return;
// We should have previous outputs by now.
assert(input.prevout.tx);
assert(input.output);
// Get the previous output's subscript
s = input.prevout.tx.getSubscript(input.prevout.index);
s = input.output.script;
// P2SH
if (bcoin.script.isScripthash(s)) {
if (!redeem)
return false;
s = bcoin.script.getSubscript(bcoin.script.decode(redeem));
s = bcoin.script.decode(redeem);
} else {
redeem = null;
}
@ -316,17 +316,15 @@ TX.prototype.createSignature = function createSignature(index, key, type) {
assert(input);
// We should have previous outputs by now.
assert(input.prevout.tx);
assert(input.output);
// Get the previous output's subscript
s = input.prevout.tx.getSubscript(input.prevout.index);
s = input.output.script;
// We need to grab the redeem script when
// signing p2sh transactions.
if (bcoin.script.isScripthash(s)) {
s = bcoin.script.decode(input.script[input.script.length - 1]);
s = bcoin.script.getSubscript(s);
}
if (bcoin.script.isScripthash(s))
s = bcoin.script.getRedeem(input.script);
// Get the hash of the current tx, minus the other
// inputs, plus the sighash type.
@ -357,13 +355,13 @@ TX.prototype.signInput = function signInput(index, key, type) {
assert(input);
// We should have previous outputs by now.
assert(input.prevout.tx);
assert(input.output);
// Create our signature.
signature = this.createSignature(index, key, type);
// Get the previous output's subscript
s = input.prevout.tx.getSubscript(input.prevout.index);
s = input.output.script;
// Script length, needed for multisig
len = input.script.length;
@ -371,8 +369,7 @@ TX.prototype.signInput = function signInput(index, key, type) {
// We need to grab the redeem script when
// signing p2sh transactions.
if (bcoin.script.isScripthash(s)) {
s = bcoin.script.decode(input.script[input.script.length - 1]);
s = bcoin.script.getSubscript(s);
s = bcoin.script.getRedeem(input.script);
// Decrement `len` to avoid the redeem script
len--;
}
@ -571,10 +568,10 @@ TX.prototype.isSigned = function isSigned(index, required) {
// We can't check for signatures unless
// we have the previous output.
assert(input.prevout.tx);
assert(input.output);
// Get the prevout's subscript
s = input.prevout.tx.getSubscript(input.prevout.index);
s = input.output.script;
// Script length, needed for multisig
len = input.script.length;
@ -642,7 +639,7 @@ TX.prototype.isSigned = function isSigned(index, required) {
TX.prototype.addOutput = function addOutput(obj, value) {
var options, output;
if (obj.getAddress)
if ((obj instanceof bcoin.wallet) || (obj instanceof bcoin.address))
obj = obj.getAddress();
if (typeof obj === 'string') {
@ -770,11 +767,9 @@ TX.prototype.signatureHash = function signatureHash(index, s, type) {
if (!Array.isArray(s)) {
type = s;
s = this.inputs[index].prevout.tx.getSubscript(this.inputs[index].prevout.index);
if (bcoin.script.isScripthash(s)) {
s = this.inputs[index].script[this.inputs[index.script.length - 1]];
s = bcoin.script.getSubscript(bcoin.script.decode(s));
}
s = this.inputs[index].output.script;
if (bcoin.script.isScripthash(s))
s = bcoin.script.getRedeem(this.inputs[index].script);
}
if (typeof index !== 'number')
@ -886,31 +881,17 @@ TX.prototype.verify = function verify(index, force, flags) {
assert(this.inputs[index]);
return this.inputs.every(function(input, i) {
var output;
if (index != null && i !== index)
return true;
if (!input.prevout.tx)
if (!input.output)
return false;
// Somethis is very wrong if this is
// not the case.
assert.equal(input.prevout.tx.hash('hex'), input.prevout.hash);
// Grab the previous output.
output = input.prevout.tx.outputs[input.prevout.index];
// Transaction is referencing an output
// that does not exist.
if (!output)
return false;
// Transaction cannot reference itself.
if (input.prevout.hash === this.hash('hex'))
return false;
return bcoin.script.verify(input.script, output.script, this, i, flags);
return bcoin.script.verify(input.script, input.output.script, this, i, flags);
}, this);
};
@ -933,19 +914,20 @@ TX.prototype.maxSize = function maxSize() {
input = copy.inputs[i];
size = 0;
assert(input.output);
// Get the previous output's subscript
s = input.prevout.tx.getSubscript(input.prevout.index);
s = input.output.script;
// If we have access to the redeem script,
// we can use it to calculate size much easier.
if (this.inputs[i].script.length && bcoin.script.isScripthash(s)) {
s = this.inputs[i].script[this.inputs[i].script.length - 1];
s = bcoin.script.getRedeem(this.inputs[i].script);
// Need to add the redeem script size
// here since it will be ignored by
// the isMultisig clause.
// OP_PUSHDATA2 [redeem]
size += 3 + s.length;
s = bcoin.script.getSubscript(bcoin.script.decode(s));
size += 3 + bcoin.script.getSize(s);
}
if (bcoin.script.isPubkey(s)) {
@ -1244,18 +1226,15 @@ TX.prototype.getFee = function getFee() {
TX.prototype.getInputValue = function getInputValue() {
var acc = new bn(0);
var inputs = this.inputs.filter(function(input) {
return input.prevout.tx;
});
if (inputs.length === 0)
if (this.inputs.length === 0)
return acc;
inputs.reduce(function(acc, input) {
return acc.iadd(input.prevout.tx.outputs[input.prevout.index].value);
}, acc);
if (!this.hasPrevout())
return acc;
return acc;
return this.inputs.reduce(function(acc, input) {
return acc.iadd(input.output.value);
}, acc);
};
TX.prototype.getOutputValue = function getOutputValue() {
@ -1264,11 +1243,9 @@ TX.prototype.getOutputValue = function getOutputValue() {
if (this.outputs.length === 0)
return acc;
this.outputs.reduce(function(acc, output) {
return this.outputs.reduce(function(acc, output) {
return acc.iadd(output.value);
}, acc);
return acc;
};
TX.prototype.getFunds = function getFunds(side) {
@ -1313,7 +1290,7 @@ TX.prototype.getTargetLocktime = function getTargetLocktime() {
TX.prototype.testInputs = function testInputs(addressTable, index, collect) {
var inputs = [];
var i, input, prev, data, j;
var i, input, j, addresses, scriptAddress;
if (typeof addressTable === 'string')
addressTable = [addressTable];
@ -1337,16 +1314,10 @@ TX.prototype.testInputs = function testInputs(addressTable, index, collect) {
input = this.inputs[i];
if (input.prevout.tx)
prev = input.prevout.tx.outputs[input.prevout.index].script;
else
prev = null;
data = bcoin.script.getInputData(input.script, prev);
if (data.addresses) {
for (j = 0; j < data.addresses.length; j++) {
if (addressTable[data.addresses[j]] != null) {
addresses = input.getAddresses();
if (addresses) {
for (j = 0; j < addresses.length; j++) {
if (addressTable[addresses[j]] != null) {
if (!collect)
return true;
inputs.push(input);
@ -1354,8 +1325,9 @@ TX.prototype.testInputs = function testInputs(addressTable, index, collect) {
}
}
if (data.scriptAddress) {
if (addressTable[data.scriptAddress] != null) {
scriptAddress = input.getScriptAddress();
if (scriptAddress) {
if (addressTable[scriptAddress] != null) {
if (!collect)
return true;
inputs.push(input);
@ -1374,7 +1346,7 @@ TX.prototype.testInputs = function testInputs(addressTable, index, collect) {
TX.prototype.testOutputs = function testOutputs(addressTable, index, collect) {
var outputs = [];
var i, output, data, j;
var i, output, data, j, addresses, scriptAddress;
if (typeof addressTable === 'string')
addressTable = [addressTable];
@ -1398,11 +1370,10 @@ TX.prototype.testOutputs = function testOutputs(addressTable, index, collect) {
output = this.outputs[i];
data = bcoin.script.getOutputData(output.script);
if (data.addresses) {
for (j = 0; j < data.addresses.length; j++) {
if (addressTable[data.addresses[j]] != null) {
addresses = output.getAddresses();
if (addresses) {
for (j = 0; j < addresses.length; j++) {
if (addressTable[addresses[j]] != null) {
if (!collect)
return true;
outputs.push(output);
@ -1410,8 +1381,9 @@ TX.prototype.testOutputs = function testOutputs(addressTable, index, collect) {
}
}
if (data.scriptAddress) {
if (addressTable[data.scriptAddress] != null) {
scriptAddress = output.getScriptAddress();
if (scriptAddress) {
if (addressTable[scriptAddress] != null) {
if (!collect)
return true;
outputs.push(output);
@ -1465,6 +1437,7 @@ TX.prototype.increaseFee = function increaseFee(fee) {
TX.prototype.hasPrevout = function hasPrevout() {
if (this.inputs.length === 0)
return false;
return this.inputs.every(function(input) {
return !!input.prevout.tx;
});
@ -1614,7 +1587,7 @@ TX.prototype.isStandard = function isStandard() {
};
TX.prototype.isStandardInputs = function isStandardInputs(flags) {
var i, input, prev, args, stack, res, s, targs;
var i, input, args, stack, res, s, targs;
if (this.isCoinbase())
return true;
@ -1622,15 +1595,10 @@ TX.prototype.isStandardInputs = function isStandardInputs(flags) {
for (i = 0; i < this.inputs.length; i++) {
input = this.inputs[i];
if (!input.prevout.tx)
if (!input.output)
return false;
prev = input.prevout.tx.outputs[input.prevout.index];
if (!prev)
return false;
args = bcoin.script.getArgs(prev.script);
args = bcoin.script.getArgs(input.output.script);
if (args < 0)
return false;
@ -1642,7 +1610,7 @@ TX.prototype.isStandardInputs = function isStandardInputs(flags) {
if (!res)
return false;
if (bcoin.script.isScripthash(prev.script)) {
if (bcoin.script.isScripthash(input.output.script)) {
if (stack.length === 0)
return false;
@ -1651,7 +1619,7 @@ TX.prototype.isStandardInputs = function isStandardInputs(flags) {
if (!Array.isArray(s))
return false;
s = bcoin.script.getSubscript(bcoin.script.decode(s));
s = bcoin.script.decode(s);
if (bcoin.script.getType(s) !== 'unknown') {
targs = bcoin.script.getArgs(s);
@ -1671,7 +1639,7 @@ TX.prototype.isStandardInputs = function isStandardInputs(flags) {
};
TX.prototype.getPriority = function getPriority() {
var size, value, i, input, output, age;
var size, value, i, input, age;
size = this.maxSize();
value = new bn(0);
@ -1682,7 +1650,6 @@ TX.prototype.getPriority = function getPriority() {
if (!input.prevout.tx)
return constants.tx.freeThreshold.clone();
output = input.prevout.tx.outputs[input.prevout.index];
age = input.prevout.tx.getConfirmations();
if (age === -1)
@ -1691,7 +1658,7 @@ TX.prototype.getPriority = function getPriority() {
if (age !== 0)
age += 1;
value.iadd(output.value.muln(age));
value.iadd(input.output.value.muln(age));
}
return priority.divn(size);

View File

@ -763,17 +763,35 @@ Wallet.prototype.createKey = function createKey(change, index) {
};
Wallet.prototype.setAddressDepth = function setAddressDepth(depth) {
var i;
assert(this.derivation !== 'normal');
for (var i = this.addressDepth; i < depth; i++)
if (depth <= this.addressDepth)
return false;
for (i = this.addressDepth; i < depth; i++)
this.currentAddress = this.createAddress(false, i);
this.addressDepth = depth;
return true;
};
Wallet.prototype.setChangeDepth = function setChangeDepth(depth) {
var i;
assert(this.derivation !== 'normal');
for (var i = this.addressDepth; i < depth; i++)
if (depth <= this.changeDepth)
return false;
for (i = this.changeDepth; i < depth; i++)
this.changeAddress = this.createAddress(true, i);
this.changeDepth = depth;
return true;
};
Wallet.prototype.getKeyHash =