refactoring.

This commit is contained in:
Christopher Jeffrey 2016-03-30 04:13:26 -07:00
parent 359cef93a0
commit d2767e4e34
9 changed files with 286 additions and 177 deletions

View File

@ -7,6 +7,7 @@
var bcoin = require('../bcoin'); var bcoin = require('../bcoin');
var utils = require('./utils'); var utils = require('./utils');
var assert = utils.assert; var assert = utils.assert;
var constants = bcoin.protocol.constants;
var BufferReader = require('./reader'); var BufferReader = require('./reader');
var BufferWriter = require('./writer'); var BufferWriter = require('./writer');
@ -133,7 +134,7 @@ Input.prototype.isFinal = function isFinal() {
}; };
Input.prototype.isCoinbase = function isCoinbase() { Input.prototype.isCoinbase = function isCoinbase() {
return +this.prevout.hash === 0; return this.prevout.hash === constants.nullHash;
}; };
Input.prototype.test = function test(addressMap) { Input.prototype.test = function test(addressMap) {

View File

@ -286,6 +286,7 @@ Mempool.prototype.add =
Mempool.prototype.addTX = function addTX(tx, peer, callback, force) { Mempool.prototype.addTX = function addTX(tx, peer, callback, force) {
var self = this; var self = this;
var hash, ts, height, now; var hash, ts, height, now;
var flags = Mempool.flags;
var ret = {}; var ret = {};
var unlock = this._lock(addTX, [tx, peer, callback], force); var unlock = this._lock(addTX, [tx, peer, callback], force);
@ -300,6 +301,11 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback, force) {
if (!peer) if (!peer)
peer = DUMMY_PEER; peer = DUMMY_PEER;
if (this.chain.segwitActive) {
flags |= constants.flags.VERIFY_WITNESS;
flags |= constants.flags.VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM;
}
hash = tx.hash('hex'); hash = tx.hash('hex');
assert(tx.ts === 0); assert(tx.ts === 0);
@ -308,24 +314,29 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback, force) {
callback = utils.asyncify(callback); callback = utils.asyncify(callback);
if (!this.chain.segwitActive) { if (!this.chain.segwitActive) {
if (tx.hasWitness()) if (tx.hasWitness()) {
peer.sendReject(tx, 'nonstandard', 'no-witness-yet', 0);
return callback(new VerifyError('nonstandard', 'no-witness-yet', 0)); return callback(new VerifyError('nonstandard', 'no-witness-yet', 0));
}
} }
if (!this.checkTX(tx, peer)) if (!tx.isSane(ret)) {
return callback(new VerifyError('invalid', 'CheckTransaction failed', -1)); peer.sendReject(tx, 'invalid', ret.reason, ret.score);
return callback(new VerifyError('invalid', ret.reason, ret.score));
}
if (tx.isCoinbase()) { if (tx.isCoinbase()) {
peer.sendReject(tx, 'invalid', 'coinbase', 100); peer.sendReject(tx, 'invalid', 'coinbase', 100);
return callback(new VerifyError('invalid', 'coinbase', 100)); return callback(new VerifyError('invalid', 'coinbase', 100));
} }
ts = utils.now(); if (this.requireStandard) {
height = this.chain.height + 1; ts = utils.now();
height = this.chain.height + 1;
if (this.requireStandard && !tx.isStandard(Mempool.flags, ts, height, ret)) { if (!tx.isStandard(flags, ts, height, ret)) {
peer.sendReject(tx, 'nonstandard', ret.reason, 0); peer.sendReject(tx, 'nonstandard', ret.reason, 0);
return callback(new VerifyError(ret.reason, 0)); return callback(new VerifyError(ret.reason, 0));
}
} }
this._hasTX(tx, function(err, exists) { this._hasTX(tx, function(err, exists) {
@ -441,6 +452,7 @@ Mempool.prototype.verify = function verify(tx, callback) {
if (this.chain.segwitActive) { if (this.chain.segwitActive) {
flags |= constants.flags.VERIFY_WITNESS; flags |= constants.flags.VERIFY_WITNESS;
flags |= constants.flags.VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM;
mandatory |= constants.flags.VERIFY_WITNESS; mandatory |= constants.flags.VERIFY_WITNESS;
} }
@ -455,7 +467,7 @@ Mempool.prototype.verify = function verify(tx, callback) {
0)); 0));
} }
if (self.requireStandard && !tx.isStandardInputs(flags)) { if (self.requireStandard && !tx.hasStandardInputs(flags)) {
return callback(new VerifyError( return callback(new VerifyError(
'nonstandard', 'nonstandard',
'bad-txns-nonstandard-inputs', 'bad-txns-nonstandard-inputs',
@ -794,68 +806,6 @@ Mempool.prototype.getSnapshot = function getSnapshot(callback) {
return this.tx.getAllHashes(callback); return this.tx.getAllHashes(callback);
}; };
Mempool.prototype.checkTX = function checkTX(tx, peer) {
return Mempool.checkTX(tx, peer);
};
Mempool.checkTX = function checkTX(tx, peer) {
var uniq = {};
var total = new bn(0);
var i, input, output, size;
if (!peer)
peer = DUMMY_PEER;
if (tx.inputs.length === 0)
return peer.sendReject(tx, 'invalid', 'bad-txns-vin-empty', 100);
if (tx.outputs.length === 0)
return peer.sendReject(tx, 'invalid', 'bad-txns-vout-empty', 100);
if (tx.getVirtualSize() > constants.block.maxSize)
return peer.sendReject(tx, 'invalid', 'bad-txns-oversize', 100);
for (i = 0; i < tx.outputs.length; i++) {
output = tx.outputs[i];
if (output.value.cmpn(0) < 0)
return peer.sendReject(tx, 'invalid', 'bad-txns-vout-negative', 100);
if (output.value.cmp(constants.maxMoney) > 0)
return peer.sendReject(tx, 'invalid', 'bad-txns-vout-toolarge', 100);
total.iadd(output.value);
if (total.cmpn(0) < 0 || total.cmp(constants.maxMoney) > 0) {
return peer.sendReject(tx,
'invalid',
'bad-txns-txouttotal-toolarge',
100);
}
}
for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i];
if (uniq[input.prevout.hash])
return peer.sendReject(tx, 'invalid', 'bad-txns-inputs-duplicate', 100);
uniq[input.prevout.hash] = true;
}
if (tx.isCoinbase()) {
size = tx.inputs[0].script.getSize();
if (size < 2 || size > 100)
return peer.sendReject(tx, 'invalid', 'bad-cb-length', 100);
} else {
for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i];
if (+input.prevout.hash === 0)
return peer.sendReject(tx, 'invalid', 'bad-txns-prevout-null', 10);
}
}
return true;
};
Mempool.prototype.getLocks = function getLocks(tx, flags, entry, callback) { Mempool.prototype.getLocks = function getLocks(tx, flags, entry, callback) {
var self = this; var self = this;
var mask = constants.sequenceLocktimeMask var mask = constants.sequenceLocktimeMask

View File

@ -522,13 +522,13 @@ Peer.prototype._handleVersion = function handleVersion(payload) {
} }
} }
if (this.options.witness) { // if (this.options.witness) {
if (!payload.witness) { // if (!payload.witness) {
// this._error('Peer does not support segregated witness service.'); // this._error('Peer does not support segregated witness service.');
// this.setMisbehavior(100); // this.setMisbehavior(100);
// return; // return;
} // }
} // }
if (payload.witness) if (payload.witness)
this.haveWitness = true; this.haveWitness = true;
@ -712,8 +712,19 @@ Peer.prototype._handleReject = function handleReject(payload) {
entry.e.emit('reject', payload); entry.e.emit('reject', payload);
}; };
Peer.prototype._handleAlert = function handleAlert(payload) { Peer.prototype._handleAlert = function handleAlert(details) {
this.emit('alert', payload); var hash = utils.dsha256(details.payload);
var signature = details.signature;
if (!bcoin.ec.verify(hash, signature, network.alertKey)) {
utils.debug('Peer %s sent a phony alert packet.', this.host);
// Let's look at it because why not?
utils.debug(details);
this.setMisbehavior(100);
return;
}
this.emit('alert', details);
}; };
Peer.prototype.getHeaders = function getHeaders(hashes, stop) { Peer.prototype.getHeaders = function getHeaders(hashes, stop) {

View File

@ -798,10 +798,13 @@ Pool.prototype._createPeer = function _createPeer(options) {
}); });
peer.on('reject', function(payload) { peer.on('reject', function(payload) {
var data = payload.data ? utils.revHex(utils.toHex(payload.data)) : null; var data = payload.data
? utils.revHex(utils.toHex(payload.data))
: null;
utils.debug( utils.debug(
'Reject: msg=%s ccode=%s reason=%s data=%s', 'Reject (%s): msg=%s ccode=%s reason=%s data=%s',
peer.host,
payload.message, payload.message,
payload.ccode, payload.ccode,
payload.reason, payload.reason,
@ -1663,7 +1666,7 @@ Pool.prototype.getTX = function getTX(hash, range, callback) {
Pool.prototype.sendTX = function sendTX(tx, callback) { Pool.prototype.sendTX = function sendTX(tx, callback) {
// Failsafe to avoid getting banned by bitcoind nodes. // Failsafe to avoid getting banned by bitcoind nodes.
if (!bcoin.mempool.checkTX(tx)) if (!tx.isSane())
return utils.asyncify(callback)(new Error('CheckTransaction failed.')); return utils.asyncify(callback)(new Error('CheckTransaction failed.'));
return this.broadcast(tx, callback); return this.broadcast(tx, callback);

View File

@ -269,6 +269,9 @@ exports.zeroHash = new Buffer(
'hex' 'hex'
); );
exports.nullHash =
'0000000000000000000000000000000000000000000000000000000000000000';
exports.userVersion = require('../../../package.json').version; exports.userVersion = require('../../../package.json').version;
exports.userAgent = '/bcoin:' + exports.userVersion + '/'; exports.userAgent = '/bcoin:' + exports.userVersion + '/';

View File

@ -736,10 +736,6 @@ Parser.parseAlert = function parseAlert(p) {
signature = p.readVarBytes(); signature = p.readVarBytes();
size = p.end(); size = p.end();
assert(
bcoin.ec.verify(utils.dsha256(payload), signature, network.alertKey),
'Alert does not verify.');
p = new BufferReader(payload); p = new BufferReader(payload);
p.start(); p.start();
version = p.read32(); version = p.read32();

View File

@ -175,11 +175,14 @@ Stack.prototype.__defineSetter__('length', function(value) {
return this.items.length = value; return this.items.length = value;
}); });
Stack.prototype.getRedeem = function getRedeem() { Stack.prototype.getRedeem = function getRedeem(pop) {
var redeem = Script.getRedeem(this.items); var redeem = Script.getRedeem(this.items);
if (!redeem) if (!redeem)
return; return;
this.pop();
if (pop !== false)
this.pop();
return redeem; return redeem;
}; };
@ -1399,6 +1402,10 @@ Script.prototype.getType = function getType() {
|| 'unknown'; || 'unknown';
}; };
Script.prototype.isUnknown = function isUnknown() {
return this.getType() === 'unknown';
};
Script.prototype.isStandard = function isStandard() { Script.prototype.isStandard = function isStandard() {
var type = this.getType(); var type = this.getType();
var m, n; var m, n;
@ -1420,6 +1427,41 @@ Script.prototype.isStandard = function isStandard() {
return type !== 'unknown'; return type !== 'unknown';
}; };
Script.prototype.isStandardProgram = function isStandardProgram(witness, flags) {
var program, witnessScript, i;
assert((flags & constants.flags.VERIFY_WITNESS) !== 0);
assert(this.isWitnessProgram());
program = this.getWitnessProgram();
if (!program.type)
return false;
if (program.version > 0) {
if (flags & constants.flags.VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM)
return false;
return true;
}
if (program.type === 'witnesspubkeyhash') {
if (witness.items.length !== 2)
return false;
} else if (program.type === 'witnessscripthash') {
if (witness.items.length === 0)
return false;
} else {
assert(false);
}
for (i = 0; i < witness.items.length; i++) {
if (witness.items[i].length > constants.script.maxSize)
return false;
}
return true;
};
Script.prototype.getSize = function getSize() { Script.prototype.getSize = function getSize() {
return this.encode().length; return this.encode().length;
}; };
@ -1745,6 +1787,10 @@ Script.getInputType = function getInputType(code, prev, isWitness) {
return type; return type;
}; };
Script.prototype.isInputUnknown = function isInputUnknown() {
return this.getInputType() === 'unknown';
};
Script.createOutputScript = function(options) { Script.createOutputScript = function(options) {
var script, keys, m, n, hash, flags, address, redeem; var script, keys, m, n, hash, flags, address, redeem;

View File

@ -12,6 +12,7 @@ var assert = utils.assert;
var constants = bcoin.protocol.constants; var constants = bcoin.protocol.constants;
var network = bcoin.protocol.network; var network = bcoin.protocol.network;
var Script = bcoin.script; var Script = bcoin.script;
var Stack = bcoin.script.stack;
var BufferReader = require('./reader'); var BufferReader = require('./reader');
var BufferWriter = require('./writer'); var BufferWriter = require('./writer');
@ -243,7 +244,7 @@ TX.prototype.signatureHashV0 = function signatureHashV0(index, prev, type) {
type = constants.hashType[type]; type = constants.hashType[type];
assert(index >= 0 && index < this.inputs.length); assert(index >= 0 && index < this.inputs.length);
assert(prev instanceof bcoin.script); assert(prev instanceof Script);
// Clone the transaction. // Clone the transaction.
copy = { copy = {
@ -333,7 +334,7 @@ TX.prototype.signatureHashV1 = function signatureHashV1(index, prev, type) {
type = constants.hashType[type]; type = constants.hashType[type];
assert(index >= 0 && index < this.inputs.length); assert(index >= 0 && index < this.inputs.length);
assert(prev instanceof bcoin.script); assert(prev instanceof Script);
if (!(type & constants.hashType.anyonecanpay)) { if (!(type & constants.hashType.anyonecanpay)) {
hashPrevouts = new BufferWriter(); hashPrevouts = new BufferWriter();
@ -412,7 +413,7 @@ TX.prototype.verify = function verify(index, force, flags) {
return false; return false;
} }
return bcoin.script.verify( return Script.verify(
input.script, input.script,
input.witness, input.witness,
input.coin.script, input.coin.script,
@ -437,7 +438,8 @@ TX.prototype.verifyAsync = function verifyAsync(index, force, flags, callback) {
}; };
TX.prototype.isCoinbase = function isCoinbase() { TX.prototype.isCoinbase = function isCoinbase() {
return this.inputs.length === 1 && +this.inputs[0].prevout.hash === 0; return this.inputs.length === 1
&& this.inputs[0].prevout.hash === constants.nullHash;
}; };
TX.prototype.getFee = function getFee() { TX.prototype.getFee = function getFee() {
@ -719,6 +721,88 @@ TX.prototype.getSigops = function getSigops(scriptHash, accurate) {
return (cost + 3) / 4 | 0; return (cost + 3) / 4 | 0;
}; };
// CheckTransaction
TX.prototype.isSane = function isSane(ret) {
var uniq = {};
var total = new bn(0);
var i, input, output, size;
if (!ret)
ret = {};
if (this.inputs.length === 0) {
ret.reason = 'bad-txns-vin-empty';
ret.score = 100;
return false;
}
if (this.outputs.length === 0) {
ret.reason = 'bad-txns-vout-empty';
ret.score = 100;
return false;
}
if (this.getVirtualSize() > constants.block.maxSize) {
ret.reason = 'bad-txns-oversize';
ret.score = 100;
return false;
}
for (i = 0; i < this.outputs.length; i++) {
output = this.outputs[i];
if (output.value.cmpn(0) < 0) {
ret.reason = 'bad-txns-vout-negative';
ret.score = 100;
return false;
}
if (output.value.cmp(constants.maxMoney) > 0) {
ret.reason = 'bad-txns-vout-toolarge';
ret.score = 100;
return false;
}
total.iadd(output.value);
if (total.cmpn(0) < 0 || total.cmp(constants.maxMoney) > 0) {
ret.reason = 'bad-txns-txouttotal-toolarge';
ret.score = 100;
return false;
}
}
for (i = 0; i < this.inputs.length; i++) {
input = this.inputs[i];
if (uniq[input.prevout.hash]) {
ret.reason = 'bad-txns-inputs-duplicate';
ret.score = 100;
return false;
}
uniq[input.prevout.hash] = true;
}
if (this.isCoinbase()) {
size = this.inputs[0].script.getSize();
if (size < 2 || size > 100) {
ret.reason = 'bad-cb-length';
ret.score = 100;
return false;
}
} else {
for (i = 0; i < this.inputs.length; i++) {
input = this.inputs[i];
if (input.prevout.hash === constants.nullHash) {
ret.reason = 'bad-txns-prevout-null';
ret.score = 10;
return false;
}
}
}
return true;
};
// IsStandardTx // IsStandardTx
TX.prototype.isStandard = function isStandard(flags, ts, height, ret) { TX.prototype.isStandard = function isStandard(flags, ts, height, ret) {
var i, input, output, type; var i, input, output, type;
@ -805,9 +889,9 @@ TX.prototype.isStandard = function isStandard(flags, ts, height, ret) {
}; };
// AreInputsStandard // AreInputsStandard
TX.prototype.isStandardInputs = function isStandardInputs(flags) { TX.prototype.hasStandardInputs = function hasStandardInputs(flags) {
var i, input, args, stack, res, redeem, targs, hadWitness;
var maxSigops = constants.script.maxScripthashSigops; var maxSigops = constants.script.maxScripthashSigops;
var i, input, args, stack, res, redeem, targs;
if (flags == null) if (flags == null)
flags = constants.flags.STANDARD_VERIFY_FLAGS; flags = constants.flags.STANDARD_VERIFY_FLAGS;
@ -821,67 +905,49 @@ TX.prototype.isStandardInputs = function isStandardInputs(flags) {
if (!input.coin) if (!input.coin)
return false; return false;
if ((flags & constants.flags.VERIFY_WITNESS)
&& input.coin.script.isWitnessProgram()) {
// Input script must be empty.
if (input.script.code.length !== 0)
return false;
// Verify the program in the output script
if (!input.coin.script.isStandardProgram(input.witness, flags))
return false;
continue;
}
args = input.coin.script.getArgs(); args = input.coin.script.getArgs();
if (args < 0) if (args < 0)
return false; return false;
stack = new bcoin.script.stack([]); // Not accurate:
// XXX Not accurate:
// Failsafe to avoid getting dos'd in case we ever // Failsafe to avoid getting dos'd in case we ever
// call isStandardInputs before isStandard. // call hasStandardInputs before isStandard.
if (!input.script.isPushOnly()) if (!input.script.isPushOnly())
return false; return false;
stack = new Stack([]);
res = input.script.execute(stack, flags, this, i, 0); res = input.script.execute(stack, flags, this, i, 0);
if (!res) if (!res)
return false; return false;
if ((flags & constants.flags.VERIFY_WITNESS)
&& input.coin.script.isWitnessProgram()) {
hadWitness = true;
// Input script must be empty.
if (input.script.code.length !== 0)
return false;
// Verify the program in the output script
if (!this.isStandardProgram(input.witness, input.coin.script, flags))
return false;
}
if ((flags & constants.flags.VERIFY_P2SH) if ((flags & constants.flags.VERIFY_P2SH)
&& input.coin.script.isScripthash()) { && input.coin.script.isScripthash()) {
if (stack.length === 0) if (stack.length === 0)
return false; return false;
redeem = bcoin.script.getRedeem(stack); redeem = stack.getRedeem(false);
if (!redeem) if (!redeem)
return false; return false;
// Not accurate?
if (redeem.getSize() > 520)
return false;
// Also consider scripthash "unknown"?
if (redeem.getType() === 'unknown') {
if (redeem.getSigops(true) > maxSigops)
return false;
continue;
}
targs = redeem.getArgs();
if (targs < 0)
return false;
args += targs;
if ((flags & constants.flags.VERIFY_WITNESS) if ((flags & constants.flags.VERIFY_WITNESS)
&& redeem.isWitnessProgram()) { && redeem.isWitnessProgram()) {
hasWitness = true;
// Input script must be exactly one push of the redeem script. // Input script must be exactly one push of the redeem script.
if (!(input.script.code.length === 1 if (!(input.script.code.length === 1
&& utils.isEqual(input.script.code[0], redeem.raw))) { && utils.isEqual(input.script.code[0], redeem.raw))) {
@ -889,13 +955,25 @@ TX.prototype.isStandardInputs = function isStandardInputs(flags) {
} }
// Verify the program in the redeem script // Verify the program in the redeem script
if (!this.isStandardProgram(input.witness, redeem, flags)) if (!redeem.isStandardProgram(input.witness, flags))
return false; return false;
}
}
if (hadWitness) continue;
continue; }
if (redeem.isUnknown()) {
if (redeem.getSigops(true) > maxSigops)
return false;
continue;
}
targs = redeem.getArgs();
if (targs < 0)
return false;
args += targs;
}
if (stack.length !== args) if (stack.length !== args)
return false; return false;
@ -904,36 +982,49 @@ TX.prototype.isStandardInputs = function isStandardInputs(flags) {
return true; return true;
}; };
TX.prototype.isStandardProgram = function isStandardProgram(witness, output, flags) { // AreInputsStandard (segwit branch)
var program, witnessScript, j; TX.prototype.hasStandardInputsWitness = function hasStandardInputsWitness(flags) {
var maxSigops = constants.script.maxScripthashSigops;
var i, input, stack, res, redeem;
assert((flags & constants.flags.VERIFY_WITNESS) !== 0); if (flags == null)
assert(output.isWitnessProgram()); flags = constants.flags.STANDARD_VERIFY_FLAGS;
program = output.getWitnessProgram(); if (this.isCoinbase())
if (!program.type)
return false;
if (program.version > 0) {
if (flags & constants.flags.VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM)
return false;
return true; return true;
}
if (program.type === 'witnesspubkeyhash') { for (i = 0; i < this.inputs.length; i++) {
if (witness.items.length !== 2) input = this.inputs[i];
return false;
} else if (program.type === 'witnessscripthash') {
if (witness.items.length === 0)
return false;
} else {
assert(false);
}
for (j = 0; j < witness.items.length; j++) { if (!input.coin)
if (witness.items[j].length > constants.script.maxSize)
return false; return false;
if ((flags & constants.flags.VERIFY_P2SH)
&& input.coin.script.isScripthash()) {
// Not accurate:
// Failsafe to avoid getting dos'd in case we ever
// call hasStandardInputs before isStandard.
if (!input.script.isPushOnly())
return false;
stack = new Stack([]);
res = input.script.execute(stack, flags, this, i, 0);
if (!res)
return false;
if (stack.length === 0)
return false;
redeem = stack.getRedeem(false);
if (!redeem)
return false;
if (redeem.getSigops(true) > maxSigops)
return false;
}
} }
return true; return true;
@ -962,18 +1053,7 @@ TX.prototype.getPriority = function getPriority(height, size) {
for (i = 0; i < this.inputs.length; i++) { for (i = 0; i < this.inputs.length; i++) {
input = this.inputs[i]; input = this.inputs[i];
age = input.coin.getAge(height);
if (!input.coin)
return new bn(0);
age = input.coin.getConfirmations(height);
if (age === -1)
age = 0;
if (age !== 0)
age += 1;
sum.iadd(input.coin.value.muln(age)); sum.iadd(input.coin.value.muln(age));
} }
@ -1011,7 +1091,7 @@ TX.prototype.getMinFee = function getMinFee(size) {
fee = new bn(constants.tx.minFee).muln(size).divn(1000); fee = new bn(constants.tx.minFee).muln(size).divn(1000);
if (fee.cmpn(0) === 0 && constants.tx.minFee.cmpn(0) > 0) if (fee.cmpn(0) === 0 && constants.tx.minFee > 0)
fee = new bn(constants.tx.minFee); fee = new bn(constants.tx.minFee);
return fee; return fee;
@ -1060,6 +1140,10 @@ TX.prototype.__defineGetter__('rhash', function() {
return utils.revHex(this.hash('hex')); return utils.revHex(this.hash('hex'));
}); });
TX.prototype.__defineGetter__('rwhash', function() {
return utils.revHex(this.witnessHash('hex'));
});
TX.prototype.__defineGetter__('fee', function() { TX.prototype.__defineGetter__('fee', function() {
return this.getFee(); return this.getFee();
}); });
@ -1233,7 +1317,7 @@ TX._fromExtended = function _fromExtended(buf, saveCoins) {
tx.ps = p.readU32(); tx.ps = p.readU32();
// tx.changeIndex = p.readU32(); // tx.changeIndex = p.readU32();
if (+tx.block === 0) if (tx.block === constants.nullHash)
tx.block = null; tx.block = null;
if (tx.height === 0x7fffffff) if (tx.height === 0x7fffffff)

View File

@ -1751,6 +1751,19 @@ utils.mb = function mb(size) {
}; };
utils.inherits = function inherits(obj, from) { utils.inherits = function inherits(obj, from) {
var f;
obj.super_ = from;
if (Object.setPrototypeOf) {
Object.setPrototypeOf(obj.prototype, from.prototype);
Object.defineProperty(obj.prototype, 'constructor', {
value: obj,
enumerable: false
});
return;
}
if (Object.create) { if (Object.create) {
obj.prototype = Object.create(from.prototype, { obj.prototype = Object.create(from.prototype, {
constructor: { constructor: {
@ -1760,9 +1773,11 @@ utils.inherits = function inherits(obj, from) {
}); });
return; return;
} }
var f = function() {};
f = function() {};
f.prototype = from.prototype; f.prototype = from.prototype;
obj.prototype = new f; obj.prototype = new f;
obj.prototype.constructor = obj;
}; };
utils.once = function once(callback) { utils.once = function once(callback) {