tx: improve witness parsing.

This commit is contained in:
Christopher Jeffrey 2016-10-03 04:55:32 -07:00
parent 32a2e119e1
commit 476cc48702
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD

View File

@ -2128,7 +2128,7 @@ TX.fromRaw = function fromRaw(data, enc) {
*/ */
TX.prototype.fromRaw = function fromRaw(data) { TX.prototype.fromRaw = function fromRaw(data) {
var p, i, inCount, outCount; var p, i, count;
if (TX.isWitness(data)) if (TX.isWitness(data))
return this.fromWitness(data); return this.fromWitness(data);
@ -2138,14 +2138,14 @@ TX.prototype.fromRaw = function fromRaw(data) {
this.version = p.readU32(); // Technically signed this.version = p.readU32(); // Technically signed
inCount = p.readVarint(); count = p.readVarint();
for (i = 0; i < inCount; i++) for (i = 0; i < count; i++)
this.inputs.push(Input.fromRaw(p)); this.inputs.push(Input.fromRaw(p));
outCount = p.readVarint(); count = p.readVarint();
for (i = 0; i < outCount; i++) for (i = 0; i < count; i++)
this.outputs.push(Output.fromRaw(p)); this.outputs.push(Output.fromRaw(p));
this.locktime = p.readU32(); this.locktime = p.readU32();
@ -2170,48 +2170,60 @@ TX.prototype.fromRaw = function fromRaw(data) {
TX.prototype.fromWitness = function fromWitness(data) { TX.prototype.fromWitness = function fromWitness(data) {
var p = BufferReader(data); var p = BufferReader(data);
var i, marker, inCount, outCount, input, hasWitness, witnessSize; var flag = 0;
var witnessSize = 0;
var hasWitness = false;
var i, count, input;
p.start(); p.start();
this.version = p.readU32(); // Technically signed this.version = p.readU32(); // Technically signed
marker = p.readU8(); assert(p.readU8() === 0, 'Non-zero marker.');
this.flag = p.readU8();
if (marker !== 0) flag = p.readU8();
throw new Error('Invalid witness tx (marker != 0)');
if (this.flag === 0) assert(flag !== 0, 'Flag byte is zero.');
throw new Error('Invalid witness tx (flag == 0)');
inCount = p.readVarint(); this.flag = flag;
for (i = 0; i < inCount; i++) count = p.readVarint();
for (i = 0; i < count; i++)
this.inputs.push(Input.fromRaw(p)); this.inputs.push(Input.fromRaw(p));
outCount = p.readVarint(); count = p.readVarint();
for (i = 0; i < outCount; i++) for (i = 0; i < count; i++)
this.outputs.push(Output.fromRaw(p)); this.outputs.push(Output.fromRaw(p));
p.start(); if (flag & 1) {
flag ^= 1;
for (i = 0; i < inCount; i++) { p.start();
input = this.inputs[i];
input.witness.fromRaw(p); for (i = 0; i < this.inputs.length; i++) {
if (input.witness.items.length > 0) input = this.inputs[i];
hasWitness = true; input.witness.fromRaw(p);
if (input.witness.items.length > 0)
hasWitness = true;
}
witnessSize = p.end() + 2;
} }
if (!hasWitness) if (flag !== 0)
throw new Error('Witness tx has an empty witness.'); throw new Error('Unknown witness flag.');
witnessSize = p.end() + 2; // We'll never be able to reserialize
// this to get the regular txid, and
// there's no way it's valid anyway.
if (this.inputs.length === 0 && this.outputs.length !== 0)
throw new Error('Zero input witness tx.');
this.locktime = p.readU32(); this.locktime = p.readU32();
if (!this.mutable) { if (!this.mutable && hasWitness) {
this._raw = p.endData(); this._raw = p.endData();
this._size = this._raw.length; this._size = this._raw.length;
this._witnessSize = witnessSize; this._witnessSize = witnessSize;
@ -2222,27 +2234,6 @@ TX.prototype.fromWitness = function fromWitness(data) {
return this; return this;
}; };
/**
* Test whether data is a witness transaction.
* @param {Buffer|BufferReader} data
* @returns {Boolean}
*/
TX.isWitness = function isWitness(data) {
if (Buffer.isBuffer(data)) {
if (data.length < 12)
return false;
return data[4] === 0 && data[5] !== 0;
}
if (data.left() < 12)
return false;
return data.data[data.offset + 4] === 0
&& data.data[data.offset + 5] !== 0;
};
/** /**
* Serialize transaction without witness. * Serialize transaction without witness.
* @private * @private
@ -2254,7 +2245,7 @@ TX.prototype.frameNormal = function frameNormal(writer) {
var p = BufferWriter(writer); var p = BufferWriter(writer);
var i; var i;
if (this.inputs.length === 0 && this.outputs.length === 1) if (this.inputs.length === 0 && this.outputs.length !== 0)
throw new Error('Cannot serialize zero-input tx.'); throw new Error('Cannot serialize zero-input tx.');
p.write32(this.version); p.write32(this.version);
@ -2292,6 +2283,9 @@ TX.prototype.frameWitness = function frameWitness(writer) {
var witnessSize = 0; var witnessSize = 0;
var i, start; var i, start;
if (this.inputs.length === 0 && this.outputs.length !== 0)
throw new Error('Cannot serialize zero-input tx.');
p.write32(this.version); p.write32(this.version);
p.writeU8(0); p.writeU8(0);
p.writeU8(this.flag); p.writeU8(this.flag);
@ -2326,6 +2320,27 @@ TX.prototype.frameWitness = function frameWitness(writer) {
return p; return p;
}; };
/**
* Test whether data is a witness transaction.
* @param {Buffer|BufferReader} data
* @returns {Boolean}
*/
TX.isWitness = function isWitness(data) {
if (Buffer.isBuffer(data)) {
if (data.length < 6)
return false;
return data[4] === 0 && data[5] !== 0;
}
if (data.left() < 6)
return false;
return data.data[data.offset + 4] === 0
&& data.data[data.offset + 5] !== 0;
};
/** /**
* Serialize a transaction to BCoin "extended format". * Serialize a transaction to BCoin "extended format".
* This is the serialization format BCoin uses internally * This is the serialization format BCoin uses internally