serialization... again.

This commit is contained in:
Christopher Jeffrey 2016-06-17 01:34:59 -07:00
parent f494762a67
commit 16404a03ba
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
26 changed files with 1245 additions and 1527 deletions

View File

@ -37,6 +37,28 @@ function AbstractBlock(data) {
if (!(this instanceof AbstractBlock))
return new AbstractBlock(data);
this.version = 1;
this.prevBlock = null;
this.merkleRoot = null;
this.ts = 0;
this.bits = 0;
this.nonce = 0;
this.totalTX = 0;
this.height = -1;
this.txs = null;
this.mutable = false;
this._valid = null;
this._hash = null;
this._size = null;
this._witnessSize = null;
if (data)
this.parseOptions(data);
}
AbstractBlock.prototype.parseOptions = function parseOptions(data) {
assert(data, 'Block data is required.');
assert(typeof data.version === 'number');
assert(typeof data.prevBlock === 'string');
@ -61,7 +83,7 @@ function AbstractBlock(data) {
this._hash = null;
this._size = null;
this._witnessSize = null;
}
};
/**
* Hash the block headers.
@ -86,8 +108,20 @@ AbstractBlock.prototype.hash = function hash(enc) {
* @returns {Buffer}
*/
AbstractBlock.prototype.abbr = function abbr() {
return bcoin.protocol.framer.blockHeaders(this);
AbstractBlock.prototype.abbr = function abbr(writer) {
var p = bcoin.writer(writer);
p.write32(this.version);
p.writeHash(this.prevBlock);
p.writeHash(this.merkleRoot);
p.writeU32(this.ts);
p.writeU32(this.bits);
p.writeU32(this.nonce);
if (!writer)
p = p.render();
return p;
};
/**

View File

@ -37,8 +37,6 @@ var constants = bcoin.protocol.constants;
*/
function Block(data) {
var i;
if (!(this instanceof Block))
return new Block(data);
@ -49,6 +47,24 @@ function Block(data) {
this._cbHeight = null;
this._commitmentHash = null;
this._raw = null;
this._size = null;
this._witnessSize = null;
if (data)
this.fromOptions(data);
}
utils.inherits(Block, bcoin.abstractblock);
Block.prototype.fromOptions = function fromOptions(data) {
var i;
this.txs = [];
this._cbHeight = null;
this._commitmentHash = null;
this._raw = data._raw || null;
this._size = data._size || null;
this._witnessSize = data._witnessSize != null ? data._witnessSize : null;
@ -57,17 +73,19 @@ function Block(data) {
for (i = 0; i < data.txs.length; i++)
this.addTX(data.txs[i]);
}
}
};
utils.inherits(Block, bcoin.abstractblock);
Block.fromOptions = function fromOptions(data) {
return new Block().fromOptions(data);
};
/**
* Serialize the block. Include witnesses if present.
* @returns {Buffer}
*/
Block.prototype.render = function render() {
return this.getRaw();
Block.prototype.render = function render(writer) {
return this.getRaw(writer);
};
/**
@ -75,10 +93,10 @@ Block.prototype.render = function render() {
* @returns {Buffer}
*/
Block.prototype.renderNormal = function renderNormal() {
Block.prototype.renderNormal = function renderNormal(writer) {
if (!this.hasWitness())
return this.getRaw();
return bcoin.protocol.framer.block(this);
return this.getRaw(writer);
return this.frameNormal(writer);
};
/**
@ -86,10 +104,10 @@ Block.prototype.renderNormal = function renderNormal() {
* @returns {Buffer}
*/
Block.prototype.renderWitness = function renderWitness() {
Block.prototype.renderWitness = function renderWitness(writer) {
if (this.hasWitness())
return this.getRaw();
return bcoin.protocol.framer.witnessBlock(this);
return this.getRaw(writer);
return this.frameWitness(writer);
};
/**
@ -98,19 +116,25 @@ Block.prototype.renderWitness = function renderWitness() {
* @returns {Buffer}
*/
Block.prototype.getRaw = function getRaw() {
Block.prototype.getRaw = function getRaw(writer) {
var raw;
if (this._raw) {
assert(this._size > 0);
assert(this._witnessSize >= 0);
if (writer) {
writer.writeBytes(this._raw);
writer._witnessSize = this._raw._witnessSize;
return writer;
}
return this._raw;
}
if (this.hasWitness())
raw = bcoin.protocol.framer.witnessBlock(this);
else
raw = bcoin.protocol.framer.block(this);
raw = this.frameWitness();
// if (this.hasWitness())
// raw = this.frameWitness();
// else
// raw = this.frameNormal();
if (!this.mutable) {
this._size = raw.length;
@ -118,6 +142,12 @@ Block.prototype.getRaw = function getRaw() {
this._raw = raw;
}
if (writer) {
writer.writeBytes(raw);
writer._witnessSize = raw._witnessSize;
return writer;
}
return raw;
};
@ -127,6 +157,8 @@ Block.prototype.getRaw = function getRaw() {
*/
Block.prototype.getSizes = function getSizes() {
var writer;
if (this._size != null) {
return {
size: this._size,
@ -142,7 +174,13 @@ Block.prototype.getSizes = function getSizes() {
};
}
return bcoin.protocol.framer.block.sizes(this);
writer = new bcoin.writer();
this.render(writer);
return {
size: writer.written,
witnessSize: writer._witnessSize
};
};
/**
@ -619,7 +657,7 @@ Block.parseJSON = function parseJSON(json) {
json.prevBlock = utils.revHex(json.prevBlock);
json.merkleRoot = utils.revHex(json.merkleRoot);
json.txs = json.txs.map(function(tx) {
return bcoin.tx.parseJSON(tx);
return bcoin.tx.fromJSON(tx);
});
return json;
};
@ -657,11 +695,34 @@ Block.prototype.toRaw = function toRaw(enc) {
* @returns {Object} A "naked" block object.
*/
Block.parseRaw = function parseRaw(data, enc) {
if (enc === 'hex')
data = new Buffer(data, 'hex');
Block.prototype.fromRaw = function fromRaw(data) {
var p = bcoin.reader(data);
var i, tx;
return bcoin.protocol.parser.parseBlock(data);
p.start();
this.version = p.readU32(); // Technically signed
this.prevBlock = p.readHash('hex');
this.merkleRoot = p.readHash('hex');
this.ts = p.readU32();
this.bits = p.readU32();
this.nonce = p.readU32();
this.totalTX = p.readVarint();
this._witnessSize = 0;
this.txs = [];
for (i = 0; i < this.totalTX; i++) {
tx = bcoin.tx.fromRaw(p);
this._witnessSize += tx._witnessSize;
this.txs.push(tx);
}
this._raw = p.endData();
this._size = this._raw.length;
return this;
};
/**
@ -672,7 +733,9 @@ Block.parseRaw = function parseRaw(data, enc) {
*/
Block.fromRaw = function fromRaw(data, enc) {
return new Block(Block.parseRaw(data, enc));
if (typeof data === 'string')
data = new Buffer(data, enc);
return new Block().fromRaw(data);
};
/**
@ -687,6 +750,43 @@ Block.prototype.toMerkle = function toMerkle(filter) {
return bcoin.merkleblock.fromBlock(this, filter);
};
Block.prototype.frame = function frame(witness, writer) {
var p = bcoin.writer(writer);
var witnessSize = 0;
var i;
p.write32(this.version);
p.writeHash(this.prevBlock);
p.writeHash(this.merkleRoot);
p.writeU32(this.ts);
p.writeU32(this.bits);
p.writeU32(this.nonce);
p.writeVarint(this.txs.length);
for (i = 0; i < this.txs.length; i++) {
if (witness)
this.txs[i].render(p);
else
this.txs[i].renderNormal(p);
witnessSize += p._witnessSize;
}
if (!writer)
p = p.render();
p._witnessSize = witnessSize;
return p;
};
Block.prototype.frameNormal = function frameNormal(writer) {
return this.frame(false, writer);
};
Block.prototype.frameWitness = function frameWitness(writer) {
return this.frame(true, writer);
};
/**
* Test whether an object is a Block.
* @param {Object} obj

View File

@ -670,7 +670,6 @@ Iterator.prototype.next = function(callback) {
Iterator.prototype.seek = function seek(key) {
var self = this;
var item;
assert(!this.ended, 'Already ended.');

View File

@ -301,10 +301,19 @@ Chain.prototype._preload = function _preload(callback) {
bcoin.debug('Loading %s.', url);
function parseHeader(buf) {
var headers = bcoin.protocol.parser.parseBlockHeaders(buf);
headers.hash = utils.dsha256(buf.slice(0, 80)).toString('hex');
return headers;
function parseHeader(data) {
var p = bcoin.reader(data, true);
var hash = utils.dsha256(p.readBytes(80)).toString('hex');
p.seek(-80);
return {
hash: hash,
version: p.readU32(), // Technically signed
prevBlock: p.readHash('hex'),
merkleRoot: p.readHash('hex'),
ts: p.readU32(),
bits: p.readU32(),
nonce: p.readU32()
};
}
function save(entry, header) {

View File

@ -15,8 +15,6 @@ var assert = utils.assert;
var DUMMY = new Buffer([0]);
var BufferWriter = require('./writer');
var BufferReader = require('./reader');
var Framer = bcoin.protocol.framer;
var Parser = bcoin.protocol.parser;
/*
* Database Layout:
@ -886,7 +884,7 @@ ChainDB.prototype.connectBlock = function connectBlock(block, view, batch, callb
}
}
Framer.coin(input.coin, false, undo);
input.coin.toRaw(undo);
}
for (j = 0; j < tx.outputs.length; j++) {
@ -1368,12 +1366,9 @@ ChainDB.prototype.getUndoCoins = function getUndoCoins(hash, callback) {
return this.db.fetch(layout.u(hash), function(data) {
var p = new BufferReader(data);
var coins = [];
var coin;
while (p.left()) {
coin = Parser.parseCoin(p, false);
coins.push(new bcoin.coin(coin));
}
while (p.left())
coins.push(bcoin.coin.fromRaw(p));
return coins;
}, callback);

View File

@ -34,12 +34,27 @@ function Coin(options) {
if (!(this instanceof Coin))
return new Coin(options);
this.version = null;
this.height = null;
this.value = null;
this.script = null;
this.coinbase = null;
this.hash = null;
this.index = null;
if (options)
this.fromOptions(options);
}
utils.inherits(Coin, bcoin.output);
Coin.prototype.fromOptions = function fromOptions(options) {
assert(options, 'Coin data is required.');
this.version = options.version;
this.height = options.height;
this.value = options.value;
this.script = bcoin.script(options.script, false);
this.script = bcoin.script(options.script);
this.coinbase = options.coinbase;
this.hash = options.hash;
this.index = options.index;
@ -51,9 +66,15 @@ function Coin(options) {
assert(typeof this.coinbase === 'boolean');
assert(!this.hash || typeof this.hash === 'string');
assert(!this.index || typeof this.index === 'number');
}
utils.inherits(Coin, bcoin.output);
return this;
};
Coin.fromOptions = function fromOptions(options) {
if (options instanceof Coin)
return options;
return new Coin().fromOptions(options);
};
/**
* Calculate number of confirmations since coin was created.
@ -129,32 +150,13 @@ Coin.prototype.toJSON = function toJSON() {
version: this.version,
height: this.height,
value: utils.btc(this.value),
script: this.script.toRaw('hex'),
script: this.script.toRaw().toString('hex'),
coinbase: this.coinbase,
hash: this.hash ? utils.revHex(this.hash) : null,
index: this.index
};
};
/**
* Handle a deserialized JSON coin object.
* @returns {NakedCoin} A "naked" coin (a
* plain javascript object which is suitable
* for passing to the Coin constructor).
*/
Coin.parseJSON = function parseJSON(json) {
return {
version: json.version,
height: json.height,
value: utils.satoshi(json.value),
script: bcoin.script.parseRaw(json.script, 'hex'),
coinbase: json.coinbase,
hash: json.hash ? utils.revHex(json.hash) : null,
index: json.index
};
};
/**
* Instantiate an Coin from a jsonified coin object.
* @param {Object} json - The jsonified coin object.
@ -162,7 +164,15 @@ Coin.parseJSON = function parseJSON(json) {
*/
Coin.fromJSON = function fromJSON(json) {
return new Coin(Coin.parseJSON(json));
return Coin.fromOptions({
version: json.version,
height: json.height,
value: utils.satoshi(json.value),
script: bcoin.script.fromRaw(new Buffer(json.script, 'hex')),
coinbase: json.coinbase,
hash: json.hash ? utils.revHex(json.hash) : null,
index: json.index
});
};
/**
@ -171,27 +181,38 @@ Coin.fromJSON = function fromJSON(json) {
* @returns {Buffer|String}
*/
Coin.prototype.toRaw = function toRaw(enc) {
var data = bcoin.protocol.framer.coin(this, false);
Coin.prototype.toRaw = function toRaw(writer) {
var p = bcoin.writer(writer);
var height = this.height;
if (enc === 'hex')
data = data.toString('hex');
if (height === -1)
height = 0x7fffffff;
return data;
p.writeU32(this.version);
p.writeU32(height);
p.write64(this.value);
p.writeVarBytes(this.script.toRaw());
p.writeU8(this.coinbase ? 1 : 0);
if (!writer)
p = p.render();
return p;
};
/**
* Parse a serialized coin.
* @param {Buffer} data
* @param {String?} enc - Encoding, can be `'hex'` or null.
* @returns {NakedCoin} A "naked" coin object.
*/
Coin.prototype.fromRaw = function fromRaw(data) {
var p = bcoin.reader(data);
Coin.parseRaw = function parseRaw(data, enc) {
if (enc === 'hex')
data = new Buffer(data, 'hex');
this.version = p.readU32();
this.height = p.readU32();
this.value = p.read64N();
this.script = bcoin.script.fromRaw(p.readVarBytes());
this.coinbase = p.readU8() === 1;
return bcoin.protocol.parser.parseCoin(data, false);
if (this.height === 0x7fffffff)
this.height = -1;
return this;
};
/**
@ -202,7 +223,9 @@ Coin.parseRaw = function parseRaw(data, enc) {
*/
Coin.fromRaw = function fromRaw(data, enc) {
return new Coin(Coin.parseRaw(data, enc));
if (typeof data === 'string')
data = new Buffer(data, enc);
return new Coin().fromRaw(data);
};
/**
@ -212,27 +235,25 @@ Coin.fromRaw = function fromRaw(data, enc) {
* @returns {Buffer|String}
*/
Coin.prototype.toExtended = function toExtended(enc) {
var data = bcoin.protocol.framer.coin(this, true);
Coin.prototype.toExtended = function toExtended(writer) {
var p = bcoin.writer(writer);
if (enc === 'hex')
data = data.toString('hex');
this.toRaw(p);
p.writeHash(this.hash);
p.writeU32(this.index);
return data;
if (!writer)
p = p.render();
return p;
};
/**
* Parse an coin in "extended" serialization format.
* @param {Buffer} data
* @param {String?} enc - Encoding, can be `'hex'` or null.
* @returns {NakedCoin} - A "naked" coin object.
*/
Coin.parseExtended = function parseExtended(data, enc) {
if (enc === 'hex')
data = new Buffer(data, 'hex');
return bcoin.protocol.parser.parseCoin(data, true);
Coin.prototype.fromExtended = function fromExtended(data) {
var p = bcoin.reader(data);
this.fromRaw(p);
this.hash = p.readHash('hex');
this.index = p.readU32();
return this;
};
/**
@ -244,7 +265,9 @@ Coin.parseExtended = function parseExtended(data, enc) {
*/
Coin.fromExtended = function fromExtended(data, enc) {
return new Coin(Coin.parseExtended(data, enc));
if (typeof data === 'string')
data = new Buffer(data, enc);
return new Coin().fromExtended(data);
};
/**
@ -254,16 +277,19 @@ Coin.fromExtended = function fromExtended(data, enc) {
* @returns {Coin}
*/
Coin.prototype.fromTX = function fromTX(tx, index) {
this.version = tx.version;
this.height = tx.height;
this.value = tx.outputs[index].value;
this.script = tx.outputs[index].script;
this.coinbase = tx.isCoinbase();
this.hash = tx.hash('hex');
this.index = index;
return this;
};
Coin.fromTX = function fromTX(tx, index) {
return new Coin({
version: tx.version,
height: tx.height,
value: tx.outputs[index].value,
script: tx.outputs[index].script,
coinbase: tx.isCoinbase(),
hash: tx.hash('hex'),
index: index
});
return new Coin().fromTX(tx, index);
};
/**

View File

@ -30,15 +30,28 @@ function Coins(options) {
if (!(this instanceof Coins))
return new Coins(options);
if (!options)
options = {};
this.version = -1;
this.hash = null;
this.height = -1;
this.coinbase = false;
this.outputs = [];
if (options)
this.fromOptions(options);
}
Coins.prototype.fromOptions = function fromOptions(options) {
this.version = options.version != null ? options.version : -1;
this.hash = options.hash || null;
this.height = options.height != null ? options.height : -1;
this.coinbase = options.coinbase || false;
this.outputs = options.outputs || [];
}
return this;
};
Coins.fromOptions = function fromOptions(options) {
return new Coins().fromOptions(options);
};
/**
* Add a single coin to the collection.
@ -167,7 +180,7 @@ Coins.prototype.toRaw = function toRaw(writer) {
if (prefix)
p.writeBytes(hash);
else
bcoin.protocol.framer.script(output.script, p);
p.writeVarBytes(output.script.toRaw());
p.writeVarint(output.value);
}
@ -185,24 +198,22 @@ Coins.prototype.toRaw = function toRaw(writer) {
* @returns {Object} A "naked" coins object.
*/
Coins.parseRaw = function parseRaw(data, hash, index) {
Coins.prototype.fromRaw = function fromRaw(data, hash, index) {
var p = new BufferReader(data);
var i = 0;
var version, height, coin, coins, mask, prefix, offset, size;
var version, height, coin, mask, prefix, offset, size;
version = p.readVarint();
height = p.readU32();
coins = {
version: version,
height: height >>> 1,
hash: hash,
coinbase: (height & 1) !== 0,
outputs: []
};
this.version = version;
this.height = height >>> 1;
this.hash = hash;
this.coinbase = (height & 1) !== 0;
this.outputs = [];
if (coins.height === 0x7fffffff)
coins.height = -1;
if (this.height === 0x7fffffff)
this.height = -1;
while (p.left()) {
offset = p.start();
@ -215,7 +226,7 @@ Coins.parseRaw = function parseRaw(data, hash, index) {
i++;
continue;
}
coins.outputs.push(null);
this.outputs.push(null);
i++;
continue;
}
@ -241,16 +252,16 @@ Coins.parseRaw = function parseRaw(data, hash, index) {
coin = new DeferredCoin(offset, size, data);
if (index != null)
return coin.toCoin(coins, i);
return coin.toCoin(this, i);
coins.outputs.push(coin);
this.outputs.push(coin);
i++;
}
assert(index == null, 'Bad coin index.');
return coins;
return this;
};
/**
@ -263,7 +274,7 @@ Coins.parseRaw = function parseRaw(data, hash, index) {
Coins.parseCoin = function parseCoin(data, hash, index) {
assert(index != null, 'Bad coin index.');
return Coins.parseRaw(data, hash, index);
return new Coins().fromRaw(data, hash, index);
};
/**
@ -274,7 +285,7 @@ Coins.parseCoin = function parseCoin(data, hash, index) {
*/
Coins.fromRaw = function fromRaw(data, hash) {
return new Coins(Coins.parseRaw(data, hash));
return new Coins().fromRaw(data, hash);
};
/**
@ -283,25 +294,27 @@ Coins.fromRaw = function fromRaw(data, hash) {
* @returns {Coins}
*/
Coins.fromTX = function fromTX(tx) {
var outputs = [];
Coins.prototype.fromTX = function fromTX(tx) {
var i;
this.version = tx.version;
this.hash = tx.hash('hex');
this.height = tx.height;
this.coinbase = tx.isCoinbase();
for (i = 0; i < tx.outputs.length; i++) {
if (tx.outputs[i].script.isUnspendable()) {
outputs.push(null);
this.outputs.push(null);
continue;
}
outputs.push(bcoin.coin.fromTX(tx, i));
this.outputs.push(bcoin.coin.fromTX(tx, i));
}
return new Coins({
version: tx.version,
hash: tx.hash('hex'),
height: tx.height,
coinbase: tx.isCoinbase(),
outputs: outputs
});
return this;
};
Coins.fromTX = function fromTX(tx) {
return new Coins().fromTX(tx);
};
/**
@ -349,7 +362,7 @@ DeferredCoin.prototype.toCoin = function toCoin(coins, index) {
prefix = p.readU8() & 3;
if (prefix === 0)
script = new bcoin.script(bcoin.protocol.parser.parseScript(p));
script = bcoin.script.fromRaw(p.readVarBytes());
else if (prefix === 1)
script = bcoin.script.createPubkeyhash(p.readBytes(20));
else if (prefix === 2)

View File

@ -153,6 +153,7 @@ function Environment(options) {
this.witness = this.script.Witness;
this.address = require('./address');
this.input = require('./input');
this.outpoint = this.input.Outpoint;
this.output = require('./output');
this.coin = require('./coin');
this.coins = require('./coins');

View File

@ -44,8 +44,8 @@ utils.inherits(Headers, bcoin.abstractblock);
* @returns {Buffer}
*/
Headers.prototype.render = function render() {
return this.getRaw();
Headers.prototype.render = function render(writer) {
return this.getRaw(writer);
};
/**
@ -66,16 +66,9 @@ Headers.prototype._verify = function _verify(ret) {
*/
Headers.prototype.getSize = function getSize() {
return 80;
};
/**
* Get the raw headers serialization.
* @returns {Buffer}
*/
Headers.prototype.getRaw = function getRaw() {
return this.abbr();
var writer = new bcoin.writer();
this.toRaw(writer);
return writer.written;
};
/**
@ -107,15 +100,21 @@ Headers.prototype.inspect = function inspect() {
* @returns {Buffer|String}
*/
Headers.prototype.toRaw = function toRaw(enc) {
var data;
Headers.prototype.toRaw = function toRaw(writer) {
var p = bcoin.writer(writer);
data = this.render();
p.write32(this.version);
p.writeHash(this.prevBlock);
p.writeHash(this.merkleRoot);
p.writeU32(this.ts);
p.writeU32(this.bits);
p.writeU32(this.nonce);
p.writeVarint(this.totalTX);
if (enc === 'hex')
data = data.toString('hex');
if (!writer)
p = p.render();
return data;
return p;
};
/**
@ -125,11 +124,18 @@ Headers.prototype.toRaw = function toRaw(enc) {
* @returns {NakedBlock} A "naked" headers object.
*/
Headers.parseRaw = function parseRaw(data, enc) {
if (enc === 'hex')
data = new Buffer(data, 'hex');
Headers.prototype.fromRaw = function fromRaw(data) {
var p = bcoin.reader(data);
return bcoin.protocol.parser.parseBlockHeaders(data);
this.version = p.readU32(); // Technically signed
this.prevBlock = p.readHash('hex');
this.merkleRoot = p.readHash('hex');
this.ts = p.readU32();
this.bits = p.readU32();
this.nonce = p.readU32();
this.totalTX = p.readVarint();
return this;
};
/**
@ -140,7 +146,9 @@ Headers.parseRaw = function parseRaw(data, enc) {
*/
Headers.fromRaw = function fromRaw(data, enc) {
return new Headers(Headers.parseRaw(data, enc));
if (typeof data === 'string')
data = new Buffer(data, enc);
return new Headers().fromRaw(data);
};
/**

View File

@ -772,7 +772,7 @@ HTTPClient.prototype.getBlock = function getBlock(hash, callback) {
*/
HTTPClient.prototype.broadcast = function broadcast(tx, callback) {
var body = { tx: tx.toRaw('hex') };
var body = { tx: tx.toRaw().toString('hex') };
callback = utils.ensure(callback);
@ -808,7 +808,7 @@ HTTPClient.prototype.walletSend = function walletSend(id, options, callback) {
address: output.address && output.address.toBase58
? output.address.toBase58()
: output.address,
script: output.script ? output.script.toRaw('hex') : null
script: output.script ? output.script.toRaw().toString('hex') : null
};
});
@ -855,7 +855,7 @@ HTTPClient.prototype.walletCreate = function walletCreate(id, options, outputs,
address: output.address && output.address.toBase58
? output.address.toBase58()
: output.address,
script: output.script ? output.script.toRaw('hex') : null
script: output.script ? output.script.toRaw().toString('hex') : null
};
});
@ -892,7 +892,7 @@ HTTPClient.prototype.walletSign = function walletCreate(id, tx, options, callbac
}
body = utils.merge({}, options || {}, {
tx: tx.toRaw('hex')
tx: tx.toRaw().toString('hex')
});
callback = utils.ensure(callback);
@ -918,7 +918,7 @@ HTTPClient.prototype.walletSign = function walletCreate(id, tx, options, callbac
*/
HTTPClient.prototype.walletFill = function walletFill(tx, callback) {
var body = { tx: tx.toRaw('hex') };
var body = { tx: tx.toRaw().toString('hex') };
callback = utils.ensure(callback);

View File

@ -11,8 +11,78 @@ var bcoin = require('./env');
var utils = require('./utils');
var assert = utils.assert;
var constants = bcoin.protocol.constants;
var BufferReader = require('./reader');
var BufferWriter = require('./writer');
function Outpoint(hash, index) {
if (!(this instanceof Outpoint))
return new Outpoint(hash, index);
this.hash = hash || null;
this.index = index || 0;
}
Outpoint.prototype.fromOptions = function fromOptions(data) {
this.hash = data.hash;
this.index = data.index;
assert(typeof this.hash === 'string');
assert(typeof this.index === 'number');
return this;
};
Outpoint.fromOptions = function fromOptions(data) {
if (data instanceof Outpoint)
return data;
return new Outpoint().fromOptions(data);
};
Outpoint.prototype.fromRaw = function fromRaw(data) {
var p = bcoin.reader(data);
this.hash = p.readHash('hex');
this.index = p.readU32();
return this;
};
Outpoint.fromRaw = function fromRaw(data) {
return new Outpoint().fromRaw(data);
};
Outpoint.prototype.toRaw = function toRaw(writer) {
var p = bcoin.writer(writer);
p.writeHash(this.hash);
p.writeU32(this.index);
if (!writer)
p = p.render();
return p;
};
Outpoint.prototype.fromJSON = function fromJSON(json) {
this.hash = utils.revHex(json.hash);
this.index = json.index;
return this;
};
Outpoint.fromJSON = function fromJSON(json) {
return Outpoint().fromJSON(json);
};
Outpoint.prototype.fromTX = function fromTX(tx, i) {
this.hash = tx.hash('hex');
this.index = i;
return this;
};
Outpoint.fromTX = function fromTX(tx, i) {
return Outpoint().fromTX(tx, i);
};
Outpoint.prototype.toJSON = function toJSON() {
return {
hash: utils.revHex(this.hash),
index: this.index
};
};
/**
* Represents a transaction input.
@ -36,10 +106,22 @@ function Input(options, mutable) {
if (!(this instanceof Input))
return new Input(options, mutable);
this.mutable = false;
this.prevout = null;
this.script = null;
this.sequence = null;
this.witness = null;
this.coin = null;
if (options)
this.fromOptions(options, mutable);
}
Input.prototype.fromOptions = function fromOptions(options, mutable) {
assert(options, 'Input data is required.');
this.mutable = !!mutable;
this.prevout = options.prevout;
this.prevout = Outpoint.fromOptions(options.prevout);
this.script = bcoin.script(options.script);
this.sequence = options.sequence == null ? 0xffffffff : options.sequence;
this.witness = bcoin.witness(options.witness);
@ -52,7 +134,13 @@ function Input(options, mutable) {
assert(typeof this.prevout.hash === 'string');
assert(typeof this.prevout.index === 'number');
assert(typeof this.sequence === 'number');
}
return this;
};
Input.fromOptions = function fromOptions(options) {
return new Input().fromOptions(options);
};
/**
* Get the previous output script type. Will "guess"
@ -268,6 +356,7 @@ Input.prototype.inspect = function inspect() {
witness: this.witness,
redeem: this.getRedeem(),
sequence: this.sequence,
prevout: this.prevout,
coin: coin
};
};
@ -282,35 +371,21 @@ Input.prototype.inspect = function inspect() {
Input.prototype.toJSON = function toJSON() {
return {
prevout: {
hash: utils.revHex(this.prevout.hash),
index: this.prevout.index
},
prevout: this.prevout.toJSON(),
coin: this.coin ? this.coin.toJSON() : null,
script: this.script.toRaw('hex'),
witness: this.witness.toRaw('hex'),
script: this.script.toRaw().toString('hex'),
witness: this.witness.toRaw().toString('hex'),
sequence: this.sequence
};
};
/**
* Handle a deserialized JSON input object.
* @returns {NakedInput} A "naked" input (a
* plain javascript object which is suitable
* for passing to the Input constructor).
*/
Input.parseJSON = function parseJSON(json) {
return {
prevout: {
hash: utils.revHex(json.prevout.hash),
index: json.prevout.index
},
coin: json.coin ? bcoin.coin.parseJSON(json.coin) : null,
script: bcoin.script.parseRaw(json.script, 'hex'),
witness: bcoin.witness.parseRaw(json.witness, 'hex'),
sequence: json.sequence
};
Input.prototype.fromJSON = function fromJSON(json) {
this.prevout = Outpoint.fromJSON(json.prevout);
this.coin = json.coin ? bcoin.coin.fromJSON(json.coin) : null;
this.script = bcoin.script.fromRaw(json.script, 'hex');
this.witness = bcoin.witness.fromRaw(json.witness, 'hex');
this.sequence = json.sequence;
return this;
};
/**
@ -320,7 +395,7 @@ Input.parseJSON = function parseJSON(json) {
*/
Input.fromJSON = function fromJSON(json) {
return new Input(Input.parseJSON(json));
return new Input().fromJSON(json);
};
/**
@ -329,29 +404,17 @@ Input.fromJSON = function fromJSON(json) {
* @returns {Buffer|String}
*/
Input.prototype.toRaw = function toRaw(enc) {
var data = bcoin.protocol.framer.input(this);
Input.prototype.toRaw = function toRaw(writer) {
var p = bcoin.writer(writer);
if (enc === 'hex')
data = data.toString('hex');
this.prevout.toRaw(p);
p.writeVarBytes(this.script.toRaw());
p.writeU32(this.sequence);
return data;
};
if (!writer)
p = p.render();
/**
* Parse a serialized input.
* @param {Buffer} data
* @param {String?} enc - Encoding, can be `'hex'` or null.
* @returns {NakedInput} A "naked" input object.
*/
Input.parseRaw = function parseRaw(data, enc) {
if (enc === 'hex')
data = new Buffer(data, 'hex');
data = bcoin.protocol.parser.parseInput(data);
return data;
return p;
};
/**
@ -361,8 +424,20 @@ Input.parseRaw = function parseRaw(data, enc) {
* @returns {Input}
*/
Input.prototype.fromRaw = function fromRaw(data) {
var p = bcoin.reader(data);
this.prevout = Outpoint.fromRaw(p);
this.script = bcoin.script.fromRaw(p.readVarBytes());
this.sequence = p.readU32();
return this;
};
Input.fromRaw = function fromRaw(data, enc) {
return new Input(Input.parseRaw(data, enc));
if (typeof data === 'string')
data = new Buffer(data, enc);
return new Input().fromRaw(data);
};
/**
@ -372,19 +447,16 @@ Input.fromRaw = function fromRaw(data, enc) {
* @returns {Buffer|String}
*/
Input.prototype.toExtended = function toExtended(enc) {
var p = new BufferWriter();
var data;
Input.prototype.toExtended = function toExtended(writer) {
var p = bcoin.writer(writer);
bcoin.protocol.framer.input(this, p);
bcoin.protocol.framer.witness(this.witness, p);
this.toRaw(p);
this.witness.toRaw(p);
data = p.render();
if (!writer)
p = p.render();
if (enc === 'hex')
data = data.toString('hex');
return data;
return p;
};
/**
@ -394,15 +466,12 @@ Input.prototype.toExtended = function toExtended(enc) {
* @returns {NakedInput} - A "naked" input object.
*/
Input.parseExtended = function parseExtended(data, enc) {
Input.prototype.fromExtended = function fromExtended(data) {
var input, p;
if (enc === 'hex')
data = new Buffer(data, 'hex');
p = new BufferReader(data);
input = bcoin.protocol.parser.parseInput(p);
input.witness = bcoin.protocol.parser.parseWitness(p);
p = bcoin.reader(data);
this.fromRaw(p);
this.witness = bcoin.witness.fromRaw(p);
return input;
};
@ -416,7 +485,9 @@ Input.parseExtended = function parseExtended(data, enc) {
*/
Input.fromExtended = function fromExtended(data, enc) {
return new Input(Input.parseExtended(data, enc));
if (typeof data === 'string')
data = new Buffer(data, enc);
return new Input().fromExtended(data);
};
/**
@ -437,4 +508,6 @@ Input.isInput = function isInput(obj) {
* Expose
*/
module.exports = Input;
exports = Input;
exports.Outpoint = Outpoint;
module.exports = exports;

View File

@ -149,7 +149,7 @@ KeyRing.prototype.getProgram = function getProgram() {
hash = utils.ripesha(this.getPublicKey());
program = bcoin.script.createWitnessProgram(0, hash);
} else if (this.type === 'multisig') {
hash = utils.sha256(this.getScript().encode());
hash = utils.sha256(this.getScript().toRaw());
program = bcoin.script.createWitnessProgram(0, hash);
} else {
assert(false, 'Unknown address type.');
@ -172,7 +172,7 @@ KeyRing.prototype.getProgramHash = function getProgramHash(enc) {
return;
if (!this._programHash)
this._programHash = utils.ripesha(this.getProgram().encode());
this._programHash = utils.ripesha(this.getProgram().toRaw());
return enc === 'hex'
? this._programHash.toString('hex')
@ -222,7 +222,7 @@ KeyRing.prototype.getScriptHash160 = function getScriptHash256(enc) {
return;
if (!this._scriptHash160)
this._scriptHash160 = utils.ripesha(this.getScript().encode());
this._scriptHash160 = utils.ripesha(this.getScript().toRaw());
return enc === 'hex'
? this._scriptHash160.toString('hex')
@ -240,7 +240,7 @@ KeyRing.prototype.getScriptHash256 = function getScriptHash256(enc) {
return;
if (!this._scriptHash256)
this._scriptHash256 = utils.sha256(this.getScript().encode());
this._scriptHash256 = utils.sha256(this.getScript().toRaw());
return enc === 'hex'
? this._scriptHash256.toString('hex')

View File

@ -55,12 +55,25 @@ function MemBlock(data) {
bcoin.abstractblock.call(this, data);
this.memory = true;
this.coinbaseHeight = data.coinbaseHeight;
this.raw = data.raw;
this.coinbaseHeight = null;
this.raw = null;
if (data)
this.fromOptions(data);
}
utils.inherits(MemBlock, bcoin.abstractblock);
MemBlock.prototype.fromOptions = function fromOptions(data) {
this.coinbaseHeight = data.coinbaseHeight;
this.raw = data.raw;
return this;
};
MemBlock.fromOptions = function fromOptions(data) {
return new MemBlock().fromOptions(data);
};
/**
* Get the full block size.
* @returns {Number}
@ -92,6 +105,46 @@ MemBlock.prototype.getCoinbaseHeight = function getCoinbaseHeight() {
return this.coinbaseHeight;
};
MemBlock.prototype.fromRaw = function fromRaw(data) {
var p = bcoin.reader(data);
var height = -1;
var inCount, input;
this.version = p.readU32(); // Technically signed
this.prevBlock = p.readHash('hex');
this.merkleRoot = p.readHash('hex');
this.ts = p.readU32();
this.bits = p.readU32();
this.nonce = p.readU32();
this.totalTX = p.readVarint();
if (this.version > 1 && this.totalTX > 0) {
p.readU32(); // Technically signed
inCount = p.readVarint();
if (inCount === 0) {
if (p.readU8() !== 0)
inCount = p.readVarint();
}
if (inCount > 0)
input = bcoin.input.fromRaw(p);
}
if (input)
height = bcoin.script.getCoinbaseHeight(input.script.raw);
this.coinbaseHeight = height;
this.txs = [];
this.raw = p.data;
return this;
};
MemBlock.fromRaw = function fromRaw(data) {
return new MemBlock().fromRaw(data);
};
/**
* Parse the serialized block data and create an actual {@link Block}.
* @returns {Block}
@ -99,9 +152,9 @@ MemBlock.prototype.getCoinbaseHeight = function getCoinbaseHeight() {
*/
MemBlock.prototype.toBlock = function toBlock() {
var data = bcoin.protocol.parser.parseBlock(this.raw);
var block = bcoin.block.fromRaw(this.raw);
this.raw = null;
return new bcoin.block(data);
return block;
};
/**

View File

@ -1515,7 +1515,7 @@ Mempool.prototype.getConfidence = function getConfidence(hash, callback) {
if (hash instanceof bcoin.tx)
return callback(null, hash, hash.hash('hex'));
return this.getTX(hash, function(err, tx) {
return self.getTX(hash, function(err, tx) {
if (err)
return callback(err);
return callback(null, tx, hash);
@ -1942,7 +1942,8 @@ MempoolEntry.fromTX = function fromTX(tx, height) {
MempoolEntry.prototype.toRaw = function toRaw(writer) {
var p = new BufferWriter(writer);
bcoin.protocol.framer.renderTX(this.tx, true, p);
this.tx.toRaw(p);
p.writeU32(this.height);
p.writeU32(this.size);
p.writeDouble(this.priority);

View File

@ -44,6 +44,24 @@ function MerkleBlock(data) {
bcoin.abstractblock.call(this, data);
this.hashes = null;
this.flags = null;
// List of matched TXs
this.map = {};
this.matches = [];
this._validPartial = null;
// TXs that will be pushed on
this.txs = [];
if (data)
this.fromOptions(data);
}
utils.inherits(MerkleBlock, bcoin.abstractblock);
MerkleBlock.prototype.fromOptions = function fromOptions(data) {
assert(Array.isArray(data.hashes));
assert(Buffer.isBuffer(data.flags));
@ -57,17 +75,21 @@ function MerkleBlock(data) {
// TXs that will be pushed on
this.txs = [];
}
utils.inherits(MerkleBlock, bcoin.abstractblock);
return this;
};
MerkleBlock.fromOptions = function fromOptions(data) {
return new MerkleBlock().fromOptions(data);
};
/**
* Serialize the merkleblock.
* @returns {Buffer}
*/
MerkleBlock.prototype.render = function render() {
return this.getRaw();
MerkleBlock.prototype.render = function render(writer) {
return this.toRaw(writer);
};
/**
@ -76,22 +98,9 @@ MerkleBlock.prototype.render = function render() {
*/
MerkleBlock.prototype.getSize = function getSize() {
if (this._size == null)
this.getRaw();
return this._size;
};
/**
* Get the raw merkleblock serialization.
* @returns {Buffer}
*/
MerkleBlock.prototype.getRaw = function getRaw() {
if (!this._raw) {
this._raw = bcoin.protocol.framer.merkleBlock(this);
this._size = this._raw.length;
}
return this._raw;
var writer = new bcoin.writer();
this.toRaw(writer);
return writer.written;
};
/**
@ -325,13 +334,29 @@ MerkleBlock.prototype.inspect = function inspect() {
* @returns {Buffer|String}
*/
MerkleBlock.prototype.toRaw = function toRaw(enc) {
var data = this.render();
MerkleBlock.prototype.toRaw = function toRaw(writer) {
var p = bcoin.writer(writer);
var i;
if (enc === 'hex')
data = data.toString('hex');
p.writeU32(this.version);
p.writeHash(this.prevBlock);
p.writeHash(this.merkleRoot);
p.writeU32(this.ts);
p.writeU32(this.bits);
p.writeU32(this.nonce);
p.writeU32(this.totalTX);
return data;
p.writeVarint(this.hashes.length);
for (i = 0; i < this.hashes.length; i++)
p.writeHash(this.hashes[i]);
p.writeVarBytes(this.flags);
if (!writer)
p = p.render();
return p;
};
/**
@ -341,11 +366,28 @@ MerkleBlock.prototype.toRaw = function toRaw(enc) {
* @returns {NakedBlock} A "naked" headers object.
*/
MerkleBlock.parseRaw = function parseRaw(data, enc) {
if (enc === 'hex')
data = new Buffer(data, 'hex');
MerkleBlock.prototype.fromRaw = function fromRaw(data) {
var p = bcoin.reader(data);
var i, hashCount;
return bcoin.protocol.parser.parseMerkleBlock(data);
this.version = p.readU32();
this.prevBlock = p.readHash('hex');
this.merkleRoot = p.readHash('hex');
this.ts = p.readU32();
this.bits = p.readU32();
this.nonce = p.readU32();
this.totalTX = p.readU32();
hashCount = p.readVarint();
this.hashes = [];
for (i = 0; i < hashCount; i++)
this.hashes.push(p.readHash('hex'));
this.flags = p.readVarBytes();
return this;
};
/**
@ -356,7 +398,9 @@ MerkleBlock.parseRaw = function parseRaw(data, enc) {
*/
MerkleBlock.fromRaw = function fromRaw(data, enc) {
return new MerkleBlock(MerkleBlock.parseRaw(data, enc));
if (typeof data === 'string')
data = new Buffer(data, enc);
return new MerkleBlock().fromRaw(data);
};
/**
@ -441,22 +485,20 @@ MerkleBlock.fromBlock = function fromBlock(block, filter) {
for (p = 0; p < bits.length; p++)
flags[p / 8 | 0] |= bits[p] << (p % 8);
block = new MerkleBlock({
version: block.version,
prevBlock: block.prevBlock,
merkleRoot: block.merkleRoot,
ts: block.ts,
bits: block.bits,
nonce: block.nonce,
totalTX: totalTX,
height: block.height,
hashes: hashes,
flags: flags
});
var merkle = new MerkleBlock();
merkle.version = block.version;
merkle.prevBlock = block.prevBlock;
merkle.merkleRoot = block.merkleRoot;
merkle.ts = block.ts;
merkle.bits = block.bits;
merkle.nonce = block.nonce;
merkle.totalTX = totalTX;
merkle.height = block.height;
merkle.hashes = hashes;
merkle.flags = flags;
merkle.txs = txs;
block.txs = txs;
return block;
return merkle;
};
/**

View File

@ -122,7 +122,7 @@ Miner.prototype._init = function _init() {
this.on('block', function(block) {
// Emit the block hex as a failsafe (in case we can't send it)
bcoin.debug('Found block: %d (%s).', block.height, block.rhash);
bcoin.debug('Raw: %s', block.toRaw('hex'));
bcoin.debug('Raw: %s', block.toRaw().toString('hex'));
});
this.on('status', function(stat) {

View File

@ -196,12 +196,12 @@ MTX.prototype.scriptInput = function scriptInput(index, addr) {
if (prev.isScripthash()) {
if (addr.program && utils.equal(prev.get(1), addr.programHash)) {
// Witness program nested in regular P2SH.
redeemScript = addr.program.encode();
redeemScript = addr.program.toRaw();
vector = input.witness;
if (addr.program.isWitnessScripthash()) {
// P2WSH nested within pay-to-scripthash
// (it had to be this complicated, didn't it?)
witnessScript = addr.script.encode();
witnessScript = addr.script.toRaw();
prev = addr.script;
} else if (addr.program.isWitnessPubkeyhash()) {
// P2WPKH nested within pay-to-scripthash.
@ -211,7 +211,7 @@ MTX.prototype.scriptInput = function scriptInput(index, addr) {
}
} else if (addr.script && utils.equal(prev.get(1), addr.scriptHash160)) {
// Regular P2SH.
redeemScript = addr.script.encode();
redeemScript = addr.script.toRaw();
vector = input.script;
prev = addr.script;
} else {
@ -226,7 +226,7 @@ MTX.prototype.scriptInput = function scriptInput(index, addr) {
if (!addr.script || !utils.equal(prev.get(1), addr.scriptHash256))
return false;
witnessScript = addr.script.encode();
witnessScript = addr.script.toRaw();
prev = addr.script;
} else if (prev.isWitnessPubkeyhash()) {
// Bare P2WPKH.
@ -1228,7 +1228,7 @@ MTX.prototype.sortMembers = function sortMembers() {
var res = a.value - b.value;
if (res !== 0)
return res;
return utils.cmp(a.encode(), b.encode());
return utils.cmp(a.script.toRaw(), b.script.toRaw());
});
if (this.changeIndex !== -1) {
@ -1273,46 +1273,32 @@ MTX.prototype.setLocktime = function setLocktime(locktime) {
this.locktime = locktime;
};
/**
* @see TX.parseJSON
*/
MTX.parseJSON = bcoin.tx.parseJSON;
/**
* @see TX.fromJSON
*/
MTX.fromJSON = function fromJSON(json) {
return new MTX(MTX.parseJSON(json));
return new MTX().fromJSON(JSON);
};
/**
* @see TX.parseRaw
*/
MTX.parseRaw = bcoin.tx.parseRaw;
/**
* @see TX.fromRaw
*/
MTX.fromRaw = function fromRaw(data, enc) {
return new MTX(MTX.parseRaw(data, enc));
if (typeof data === 'string')
data = new Buffer(data, enc);
return new MTX().fromRaw(data);
};
/**
* @see TX.parseExtended
*/
MTX.parseExtended = bcoin.tx.parseExtended;
/**
* @see TX.fromExtended
*/
MTX.fromExtended = function fromExtended(data, enc) {
return new MTX(MTX.parseExtended(data, enc));
if (typeof data === 'string')
data = new Buffer(data, enc);
return new MTX().fromExtended(data);
};
/**

View File

@ -12,7 +12,6 @@ var utils = require('./utils');
var constants = bcoin.protocol.constants;
var assert = utils.assert;
var BufferWriter = require('./writer');
var Framer = bcoin.protocol.framer;
/**
* Represents a transaction output.
@ -28,11 +27,20 @@ var Framer = bcoin.protocol.framer;
*/
function Output(options, mutable) {
var value;
if (!(this instanceof Output))
return new Output(options, mutable);
this.mutable = false;
this.value = 0;
this.script = null;
if (options)
this.fromOptions(options, mutable);
}
Output.prototype.fromOptions = function fromOptions(options, mutable) {
var value;
assert(options, 'Output data is required.');
value = options.value;
@ -46,7 +54,13 @@ function Output(options, mutable) {
assert(typeof this.value === 'number');
assert(!this.mutable || this.value >= 0);
}
return this;
};
Output.fromOptions = function fromOptions(options) {
return new Output().fromOptions(options);
};
/**
* Get the script type.
@ -147,7 +161,7 @@ Output.prototype.inspect = function inspect() {
Output.prototype.toJSON = function toJSON() {
return {
value: utils.btc(this.value),
script: this.script.toRaw('hex')
script: this.script.toRaw().toString('hex')
};
};
@ -167,7 +181,7 @@ Output.prototype.getDustThreshold = function getDustThreshold(rate) {
if (this.script.isUnspendable())
return 0;
size = Framer.output(this, new BufferWriter()).written;
size = this.toRaw(new BufferWriter()).written;
size += 148;
return 3 * bcoin.tx.getMinFee(size, rate);
@ -190,11 +204,11 @@ Output.prototype.isDust = function isDust(rate) {
* for passing to the Output constructor).
*/
Output.parseJSON = function parseJSON(json) {
return {
Output.prototype.fromJSON = function fromJSON(json) {
return Output.fromOptions({
value: utils.satoshi(json.value),
script: bcoin.script.parseRaw(json.script, 'hex')
};
script: bcoin.script.fromRaw(json.script, 'hex')
});
};
/**
@ -204,7 +218,7 @@ Output.parseJSON = function parseJSON(json) {
*/
Output.fromJSON = function fromJSON(json) {
return new Output(Output.parseJSON(json));
return new Output().fromJSON(json);
};
/**
@ -213,13 +227,16 @@ Output.fromJSON = function fromJSON(json) {
* @returns {Buffer|String}
*/
Output.prototype.toRaw = function toRaw(enc) {
var data = Framer.output(this);
Output.prototype.toRaw = function toRaw(writer) {
var p = bcoin.writer(writer);
if (enc === 'hex')
data = data.toString('hex');
p.write64(this.value);
p.writeVarBytes(this.script.toRaw());
return data;
if (!writer)
p = p.render();
return p;
};
/**
@ -229,13 +246,13 @@ Output.prototype.toRaw = function toRaw(enc) {
* @returns {NakedOutput} A "naked" output object.
*/
Output.parseRaw = function parseRaw(data, enc) {
if (enc === 'hex')
data = new Buffer(data, 'hex');
Output.prototype.fromRaw = function fromRaw(data) {
var p = bcoin.reader(data);
data = bcoin.protocol.parser.parseOutput(data);
this.value = p.read64N();
this.script = bcoin.script.fromRaw(p.readVarBytes());
return data;
return this;
};
/**
@ -246,7 +263,10 @@ Output.parseRaw = function parseRaw(data, enc) {
*/
Output.fromRaw = function fromRaw(data, enc) {
return new Output(Output.parseRaw(data, enc));
if (typeof data === 'string')
data = new Buffer(data, enc);
return new Output().fromRaw(data);
};
/**

View File

@ -719,11 +719,11 @@ Peer.prototype._onPacket = function onPacket(packet) {
case 'filterclear':
return this._handleFilterClear(payload);
case 'block':
payload = new bcoin.memblock(payload);
// payload = new bcoin.memblock(payload);
this.fire(cmd, payload);
break;
case 'merkleblock':
payload = new bcoin.merkleblock(payload);
// payload = new bcoin.merkleblock(payload);
payload.verifyPartial();
this.lastBlock = payload;
this.waiting = payload.matches.length;
@ -731,7 +731,7 @@ Peer.prototype._onPacket = function onPacket(packet) {
this._flushMerkle();
break;
case 'tx':
payload = new bcoin.tx(payload);
// payload = new bcoin.tx(payload);
if (this.lastBlock) {
if (this.lastBlock.hasTX(payload)) {
this.lastBlock.addTX(payload);

View File

@ -299,15 +299,8 @@ Framer.prototype.getBlocks = function getBlocks(data) {
* @returns {Buffer} tx packet.
*/
Framer.prototype.tx = function tx(tx) {
var checksum;
// Save some time by using the
// cached hash as our checksum.
if (tx.hash)
checksum = tx.hash();
return this.packet('tx', Framer.renderTX(tx, false), checksum);
Framer.prototype.tx = function _tx(tx) {
return this.packet('tx', Framer.tx(tx), tx.hash());
};
/**
@ -321,18 +314,16 @@ Framer.prototype.witnessTX = function witnessTX(tx) {
// Save some time by using the
// cached hash as our checksum.
if (tx.witnessHash) {
if (tx.hasWitness()) {
// We can't use the coinbase
// hash since it is all zeroes.
if (!tx.isCoinbase())
checksum = tx.witnessHash();
} else {
checksum = tx.hash();
}
if (tx.hasWitness()) {
// We can't use the coinbase
// hash since it is all zeroes.
if (!tx.isCoinbase())
checksum = tx.witnessHash();
} else {
checksum = tx.hash();
}
return this.packet('tx', Framer.renderTX(tx, true), checksum);
return this.packet('tx', Framer.witnessTX(tx), checksum);
};
/**
@ -660,38 +651,6 @@ Framer._getBlocks = function _getBlocks(data, writer, headers) {
return p;
};
/**
* Serialize a coin.
* @param {NakedCoin|Coin} coin
* @param {Boolean} extended - Whether to include the hash and index.
* @param {BufferWriter?} writer - A buffer writer to continue writing from.
* @returns {Buffer} Returns a BufferWriter if `writer` was passed in.
*/
Framer.coin = function _coin(coin, extended, writer) {
var p = new BufferWriter(writer);
var height = coin.height;
if (height === -1)
height = 0x7fffffff;
p.writeU32(coin.version);
p.writeU32(height);
p.write64(coin.value);
Framer.script(coin.script, p);
p.writeU8(coin.coinbase ? 1 : 0);
if (extended) {
p.writeHash(coin.hash);
p.writeU32(coin.index);
}
if (!writer)
p = p.render();
return p;
};
/**
* Serialize transaction without witness.
* @param {NakedTX|TX} tx
@ -700,87 +659,7 @@ Framer.coin = function _coin(coin, extended, writer) {
*/
Framer.tx = function _tx(tx, writer) {
var p = new BufferWriter(writer);
var i;
p.write32(tx.version);
p.writeVarint(tx.inputs.length);
for (i = 0; i < tx.inputs.length; i++)
Framer.input(tx.inputs[i], p);
p.writeVarint(tx.outputs.length);
for (i = 0; i < tx.outputs.length; i++)
Framer.output(tx.outputs[i], p);
p.writeU32(tx.locktime);
if (!writer)
p = p.render();
p._witnessSize = 0;
return p;
};
/**
* Serialize an outpoint.
* @param {Hash} hash
* @param {Number} index
* @param {BufferWriter?} writer - A buffer writer to continue writing from.
* @returns {Buffer} Returns a BufferWriter if `writer` was passed in.
*/
Framer.outpoint = function outpoint(hash, index, writer) {
var p = new BufferWriter(writer);
p.writeHash(hash);
p.writeU32(index);
if (!writer)
p = p.render();
return p;
};
/**
* Serialize an input.
* @param {NakedInput|Input} input
* @param {BufferWriter?} writer - A buffer writer to continue writing from.
* @returns {Buffer} Returns a BufferWriter if `writer` was passed in.
*/
Framer.input = function _input(input, writer) {
var p = new BufferWriter(writer);
p.writeHash(input.prevout.hash);
p.writeU32(input.prevout.index);
Framer.script(input.script, p);
p.writeU32(input.sequence);
if (!writer)
p = p.render();
return p;
};
/**
* Serialize an output.
* @param {NakedOutput|Output} output
* @param {BufferWriter?} writer - A buffer writer to continue writing from.
* @returns {Buffer} Returns a BufferWriter if `writer` was passed in.
*/
Framer.output = function _output(output, writer) {
var p = new BufferWriter(writer);
p.write64(output.value);
Framer.script(output.script, p);
if (!writer)
p = p.render();
return p;
return tx.renderNormal(writer);
};
/**
@ -792,85 +671,7 @@ Framer.output = function _output(output, writer) {
*/
Framer.witnessTX = function _witnessTX(tx, writer) {
var p = new BufferWriter(writer);
var witnessSize = 0;
var i, start;
p.write32(tx.version);
p.writeU8(0);
p.writeU8(tx.flag || 1);
p.writeVarint(tx.inputs.length);
for (i = 0; i < tx.inputs.length; i++)
Framer.input(tx.inputs[i], p);
p.writeVarint(tx.outputs.length);
for (i = 0; i < tx.outputs.length; i++)
Framer.output(tx.outputs[i], p);
for (i = 0; i < tx.inputs.length; i++) {
start = p.written;
Framer.witness(tx.inputs[i].witness, p);
witnessSize += p.written - start;
}
p.writeU32(tx.locktime);
if (!writer)
p = p.render();
p._witnessSize = witnessSize + 2;
return p;
};
/**
* Serialize a script. Note that scripts require
* extra magic since they're so goddamn bizarre.
* Normally in an "encoded" script we don't
* include the varint size because scripthashes
* don't include them. This is why
* script.encode/decode is separate from the
* framer and parser.
* @param {NakedScript|Script} script
* @param {BufferWriter?} writer - A buffer writer to continue writing from.
* @returns {Buffer} Returns a BufferWriter if `writer` was passed in.
*/
Framer.script = function _script(script, writer) {
var p = new BufferWriter(writer);
var data = script.raw || script;
p.writeVarBytes(data);
if (!writer)
p = p.render();
return p;
};
/**
* Serialize a witness.
* @param {NakedWitness|Witness} witness
* @param {BufferWriter?} writer - A buffer writer to continue writing from.
* @returns {Buffer} Returns a BufferWriter if `writer` was passed in.
*/
Framer.witness = function _witness(witness, writer) {
var p = new BufferWriter(writer);
var i;
p.writeVarint(witness.items.length);
for (i = 0; i < witness.items.length; i++)
p.writeVarBytes(witness.items[i]);
if (!writer)
p = p.render();
return p;
return tx.renderWitness(writer);
};
/**
@ -881,7 +682,7 @@ Framer.witness = function _witness(witness, writer) {
*/
Framer.block = function _block(block, writer) {
return Framer._block(block, false, writer);
return block.renderNormal(block);
};
/**
@ -893,152 +694,7 @@ Framer.block = function _block(block, writer) {
*/
Framer.witnessBlock = function _witnessBlock(block, writer) {
return Framer._block(block, true, writer);
};
/**
* Serialize a transaction lazily (use existing raw data if present).
* @param {NakedTX|TX} tx
* @param {Boolean} useWitness - Whether to include witness data.
* @param {BufferWriter?} writer - A buffer writer to continue writing from.
* @returns {Buffer} Returns a BufferWriter if `writer` was passed in.
*/
Framer.renderTX = function renderTX(tx, useWitness, writer) {
var p = new BufferWriter(writer);
var witnessSize;
// Cache the serialization if we can.
if (tx.render && !tx.mutable && !tx._raw)
tx.render();
// Try the cached raw data first.
if (tx._raw) {
if (useWitness) {
// If we're serializing the witness,
// we can use whatever data getRaw()
// gave us.
p.writeBytes(tx._raw);
witnessSize = tx._witnessSize;
} else {
// We have to use the standard format
// here. Try to grab it from cache.
if (bcoin.protocol.parser.isWitnessTX(tx._raw)) {
Framer.tx(tx, p);
witnessSize = p._witnessSize;
} else {
p.writeBytes(tx._raw);
witnessSize = tx._witnessSize;
}
}
} else {
if (useWitness) {
if (bcoin.tx.prototype.hasWitness.call(tx)) {
Framer.witnessTX(tx, p);
} else {
// Only use the witness serialization if
// we have a witness. This clause isn't
// necessary above since we already
// determined this in getRaw().
Framer.tx(tx, p);
}
} else {
// Any other case, we use
// the standard serialization.
Framer.tx(tx, p);
}
witnessSize = p._witnessSize;
}
if (!writer)
p = p.render();
p._witnessSize = witnessSize;
return p;
};
/**
* Serialize a transaction to BCoin "extended format".
* This is the serialization format BCoin uses internally
* to store transactions in the database. The extended
* serialization includes the height, block hash, index,
* timestamp, pending-since time, and optionally a vector
* for the serialized coins.
* @param {NakedTX|TX} tx
* @param {Boolean?} saveCoins - Whether to serialize the coins.
* @param {String?} enc - One of `"hex"` or `null`.
* @returns {Buffer}
*/
Framer.extendedTX = function extendedTX(tx, saveCoins, writer) {
var height = tx.height;
var index = tx.index;
var changeIndex = tx.changeIndex != null ? tx.changeIndex : -1;
var p = new BufferWriter(writer);
var i, input;
if (height === -1)
height = 0x7fffffff;
if (index === -1)
index = 0x7fffffff;
if (changeIndex === -1)
changeIndex = 0x7fffffff;
Framer.renderTX(tx, true, p);
p.writeU32(height);
p.writeHash(tx.block || constants.ZERO_HASH);
p.writeU32(index);
p.writeU32(tx.ts);
p.writeU32(tx.ps);
// p.writeU32(changeIndex);
if (saveCoins) {
p.writeVarint(tx.inputs.length);
for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i];
if (!input.coin) {
p.writeVarint(0);
continue;
}
p.writeVarBytes(Framer.coin(input.coin, false));
}
}
if (!writer)
p = p.render();
return p;
};
Framer._block = function _block(block, useWitness, writer) {
var p = new BufferWriter(writer);
var witnessSize = 0;
var i;
p.write32(block.version);
p.writeHash(block.prevBlock);
p.writeHash(block.merkleRoot);
p.writeU32(block.ts);
p.writeU32(block.bits);
p.writeU32(block.nonce);
p.writeVarint(block.txs.length);
for (i = 0; i < block.txs.length; i++) {
Framer.renderTX(block.txs[i], useWitness, p);
witnessSize += p._witnessSize;
}
if (!writer)
p = p.render();
p._witnessSize = witnessSize;
return p;
return block.renderWitness(writer);
};
/**
@ -1049,28 +705,7 @@ Framer._block = function _block(block, useWitness, writer) {
*/
Framer.merkleBlock = function _merkleBlock(block, writer) {
var p = new BufferWriter(writer);
var i;
p.writeU32(block.version);
p.writeHash(block.prevBlock);
p.writeHash(block.merkleRoot);
p.writeU32(block.ts);
p.writeU32(block.bits);
p.writeU32(block.nonce);
p.writeU32(block.totalTX);
p.writeVarint(block.hashes.length);
for (i = 0; i < block.hashes.length; i++)
p.writeHash(block.hashes[i]);
p.writeVarBytes(block.flags);
if (!writer)
p = p.render();
return p;
return block.toRaw(writer);
};
/**
@ -1082,43 +717,12 @@ Framer.merkleBlock = function _merkleBlock(block, writer) {
Framer.headers = function _headers(headers, writer) {
var p = new BufferWriter(writer);
var i, header;
var i;
p.writeVarint(headers.length);
for (i = 0; i < headers.length; i++) {
header = headers[i];
p.write32(header.version);
p.writeHash(header.prevBlock);
p.writeHash(header.merkleRoot);
p.writeU32(header.ts);
p.writeU32(header.bits);
p.writeU32(header.nonce);
p.writeVarint(header.totalTX);
}
if (!writer)
p = p.render();
return p;
};
/**
* Serialize a block header without any transaction count field.
* @param {NakedBlock|Block|MerkleBlock|Headers|ChainEntry} block
* @param {BufferWriter?} writer - A buffer writer to continue writing from.
* @returns {Buffer} Returns a BufferWriter if `writer` was passed in.
*/
Framer.blockHeaders = function blockHeaders(block, writer) {
var p = new BufferWriter(writer);
p.write32(block.version);
p.writeHash(block.prevBlock);
p.writeHash(block.merkleRoot);
p.writeU32(block.ts);
p.writeU32(block.bits);
p.writeU32(block.nonce);
for (i = 0; i < headers.length; i++)
headers[i].toRaw(p);
if (!writer)
p = p.render();
@ -1149,6 +753,7 @@ Framer.reject = function reject(details, writer) {
p.writeVarString(details.message || '', 'ascii');
p.writeU8(ccode);
p.writeVarString(details.reason || '', 'ascii');
if (details.data)
p.writeHash(details.data);
@ -1368,7 +973,8 @@ Framer.UTXOs = function UTXOs(data, writer) {
p.writeU32(coin.version);
p.writeU32(height);
Framer.output(coin, p);
p.write64(coin.value);
p.writeVarBytes(coin.script.toRaw());
}
if (!writer)
@ -1388,7 +994,7 @@ Framer.submitOrder = function submitOrder(order, writer) {
var p = new BufferWriter(writer);
p.writeHash(order.hash);
Framer.renderTX(order.tx, true, p);
order.tx.toRaw(p);
if (!writer)
p = p.render();
@ -1494,130 +1100,6 @@ Framer.feeFilter = function feeFilter(data, writer) {
return p;
};
/**
* Calculate total block size and
* witness size without serializing.
* @param {NakedBlock|Block} block
* @returns {Object} In the form of `{size: Number, witnessSize: Number}`.
*/
Framer.block.sizes = function blockSizes(block) {
var writer = new BufferWriter();
Framer.witnessBlock(block, writer);
return {
size: writer.written,
witnessSize: writer._witnessSize
};
};
/**
* Calculate total transaction size and
* witness size without serializing.
* @param {NakedBlock|Block} block
* @returns {Object} In the form of `{size: Number, witnessSize: Number}`.
*/
Framer.tx.sizes = function txSizes(tx) {
var writer = new BufferWriter();
Framer.renderTX(tx, true, writer);
return {
size: writer.written,
witnessSize: writer._witnessSize
};
};
/**
* Calculate block size with witness (if present).
* @param {NakedBlock|Block} block
* @returns {Number} Size.
*/
Framer.block.witnessSize = function blockWitnessSize(block) {
return Framer.block.sizes(block).size;
};
/**
* Calculate transaction size with witness (if present).
* @param {NakedTX|TX} tx
* @returns {Number} Size.
*/
Framer.tx.witnessSize = function txWitnessSize(tx) {
return Framer.tx.sizes(tx).size;
};
/**
* Calculate transaction size without witness.
* @param {NakedBlock|Block} block
* @returns {Number} Size.
*/
Framer.block.size = function blockSize(block) {
var writer = new BufferWriter();
Framer.block(block, writer);
return writer.written;
};
/**
* Calculate transaction size without witness.
* @param {NakedTX|TX} tx
* @returns {Number} Size.
*/
Framer.tx.size = function txSize(tx) {
var writer = new BufferWriter();
Framer.renderTX(tx, false, writer);
return writer.written;
};
/**
* Calculate block virtual size.
* @param {NakedBlock|Block} block
* @returns {Number} Virtual size.
*/
Framer.block.virtualSize = function blockVirtualSize(block) {
var scale = constants.WITNESS_SCALE_FACTOR;
return (Framer.block.cost(block) + scale - 1) / scale | 0;
};
/**
* Calculate transaction virtual size.
* @param {NakedTX|TX} tx
* @returns {Number} Virtual size.
*/
Framer.tx.virtualSize = function txVirtualSize(tx) {
var scale = constants.WITNESS_SCALE_FACTOR;
return (Framer.tx.cost(tx) + scale - 1) / scale | 0;
};
/**
* Calculate block cost.
* @param {NakedBlock|Block} block
* @returns {Number} cost
*/
Framer.block.cost = function blockCost(block) {
var sizes = Framer.block.sizes(block);
var base = sizes.size - sizes.witnessSize;
var scale = constants.WITNESS_SCALE_FACTOR;
return base * (scale - 1) + sizes.size;
};
/**
* Calculate transaction cost.
* @param {NakedTX|TX} tx
* @returns {Number} cost
*/
Framer.tx.cost = function txCost(tx) {
var sizes = Framer.tx.sizes(tx);
var base = sizes.size - sizes.witnessSize;
var scale = constants.WITNESS_SCALE_FACTOR;
return base * (scale - 1) + sizes.size;
};
/*
* Expose
*/

View File

@ -384,12 +384,8 @@ Parser.parseGetUTXOs = function parseGetUTXOs(p) {
prevout = [];
count = p.readVarint();
for (i = 0; i < count; i++) {
prevout.push({
hash: p.readHash('hex'),
index: p.readU32()
});
}
for (i = 0; i < count; i++)
prevout.push(bcoin.outpoint.fromRaw(p));
return {
mempool: mempool,
@ -428,7 +424,7 @@ Parser.parseUTXOs = function parseUTXOs(p) {
if (height === 0x7fffffff)
height = -1;
coin = Parser.parseOutput(p);
coin = bcoin.output.fromRaw(p);
coin.version = version;
coin.height = height;
coins.push(coin);
@ -642,39 +638,7 @@ Parser.parseInv = function parseInv(p) {
*/
Parser.parseMerkleBlock = function parseMerkleBlock(p) {
var version, prevBlock, merkleRoot, ts, bits, nonce, totalTX;
var i, hashCount, hashes, flags;
p = new BufferReader(p);
version = p.readU32();
prevBlock = p.readHash('hex');
merkleRoot = p.readHash('hex');
ts = p.readU32();
bits = p.readU32();
nonce = p.readU32();
totalTX = p.readU32();
hashCount = p.readVarint();
hashes = [];
for (i = 0; i < hashCount; i++)
hashes.push(p.readHash('hex'));
flags = p.readVarBytes();
return {
version: version,
prevBlock: prevBlock,
merkleRoot: merkleRoot,
ts: ts,
bits: bits,
nonce: nonce,
totalTX: totalTX,
hashes: hashes,
flags: flags
};
return bcoin.merkleblock.fromRaw(p);
};
/**
@ -691,91 +655,12 @@ Parser.parseHeaders = function parseHeaders(p) {
count = p.readVarint();
for (i = 0; i < count; i++) {
headers.push({
version: p.readU32(), // Technically signed
prevBlock: p.readHash('hex'),
merkleRoot: p.readHash('hex'),
ts: p.readU32(),
bits: p.readU32(),
nonce: p.readU32(),
totalTX: p.readVarint()
});
}
for (i = 0; i < count; i++)
headers.push(bcoin.headers.fromRaw(p));
return headers;
};
/**
* Parse headers packet.
* @param {Buffer|BufferReader} p
* @returns {NakedBlock}
*/
Parser.parseBlockHeaders = function parseBlockHeaders(p) {
p = new BufferReader(p);
return {
version: p.readU32(), // Technically signed
prevBlock: p.readHash('hex'),
merkleRoot: p.readHash('hex'),
ts: p.readU32(),
bits: p.readU32(),
nonce: p.readU32()
};
};
/**
* Parse a transaction in "extended" serialization format.
* @param {Buffer|BufferReader} p
* @param {Boolean?} saveCoins - If true, the function will
* attempt to parse the coins.
* @param {String?} enc - One of `"hex"` or `null`.
* @returns {NakedTX} - A "naked" transaction object.
*/
Parser.parseExtendedTX = function parseExtendedTX(p, saveCoins) {
var i, tx, coinCount, coin;
p = new BufferReader(p);
tx = Parser.parseTX(p);
tx.height = p.readU32();
tx.block = p.readHash('hex');
tx.index = p.readU32();
tx.ts = p.readU32();
tx.ps = p.readU32();
// tx.changeIndex = p.readU32();
if (tx.block === constants.NULL_HASH)
tx.block = null;
if (tx.height === 0x7fffffff)
tx.height = -1;
if (tx.index === 0x7fffffff)
tx.index = -1;
if (tx.changeIndex === 0x7fffffff)
tx.changeIndex = -1;
if (saveCoins) {
coinCount = p.readVarint();
for (i = 0; i < coinCount; i++) {
coin = p.readVarBytes();
if (coin.length === 0)
continue;
coin = Parser.parseCoin(coin, false);
coin.hash = tx.inputs[i].prevout.hash;
coin.index = tx.inputs[i].prevout.index;
tx.inputs[i].coin = coin;
}
}
return tx;
};
/**
* Parse block packet.
* @param {Buffer|BufferReader} p
@ -783,46 +668,7 @@ Parser.parseExtendedTX = function parseExtendedTX(p, saveCoins) {
*/
Parser.parseBlock = function parseBlock(p) {
var txs = [];
var version, prevBlock, merkleRoot, ts, bits, nonce;
var i, totalTX, tx;
var raw, size, witnessSize;
p = new BufferReader(p);
p.start();
version = p.readU32(); // Technically signed
prevBlock = p.readHash('hex');
merkleRoot = p.readHash('hex');
ts = p.readU32();
bits = p.readU32();
nonce = p.readU32();
totalTX = p.readVarint();
witnessSize = 0;
for (i = 0; i < totalTX; i++) {
tx = Parser.parseTX(p);
witnessSize += tx._witnessSize;
txs.push(tx);
}
raw = p.endData();
size = raw.length;
return {
version: version,
prevBlock: prevBlock,
merkleRoot: merkleRoot,
ts: ts,
bits: bits,
nonce: nonce,
txs: txs,
_raw: raw,
_size: size,
_witnessSize: witnessSize
};
return bcoin.block.fromRaw(p);
};
/**
@ -832,135 +678,7 @@ Parser.parseBlock = function parseBlock(p) {
*/
Parser.parseMemBlock = function parseMemBlock(p) {
var version, prevBlock, merkleRoot;
var ts, bits, nonce, totalTX, height;
var inCount, input;
p = new BufferReader(p);
version = p.readU32(); // Technically signed
prevBlock = p.readHash('hex');
merkleRoot = p.readHash('hex');
ts = p.readU32();
bits = p.readU32();
nonce = p.readU32();
totalTX = p.readVarint();
if (version > 1 && totalTX > 0) {
p.readU32(); // Technically signed
inCount = p.readVarint();
if (inCount === 0) {
if (p.readU8() !== 0)
inCount = p.readVarint();
}
if (inCount > 0)
input = Parser.parseInput(p);
}
if (input)
height = bcoin.script.getCoinbaseHeight(input.script.raw);
else
height = -1;
return {
version: version,
prevBlock: prevBlock,
merkleRoot: merkleRoot,
ts: ts,
bits: bits,
nonce: nonce,
totalTX: totalTX,
coinbaseHeight: height,
txs: [],
raw: p.data
};
};
/**
* Parse serialized input.
* @param {Buffer|BufferReader} p
* @returns {NakedInput}
*/
Parser.parseInput = function parseInput(p) {
var hash, index, script, sequence;
p = new BufferReader(p);
hash = p.readHash('hex');
index = p.readU32();
script = Parser.parseScript(p);
sequence = p.readU32();
return {
prevout: {
hash: hash,
index: index
},
coin: null,
script: script,
sequence: sequence
};
};
/**
* Parse serialized output.
* @param {Buffer|BufferReader} p
* @returns {NakedOutput}
*/
Parser.parseOutput = function parseOutput(p) {
var value, script;
p = new BufferReader(p);
value = p.read64N();
script = Parser.parseScript(p);
return {
value: value,
script: script
};
};
/**
* Parse serialized coin.
* @param {Buffer|BufferReader} p
* @param {Boolean} extended - Whether to parse the hash and index.
* @returns {NakedCoin}
*/
Parser.parseCoin = function parseCoin(p, extended) {
var version, height, value, script, hash, index, coinbase;
p = new BufferReader(p);
version = p.readU32();
height = p.readU32();
value = p.read64N();
script = Parser.parseScript(p);
coinbase = p.readU8() === 1;
if (extended) {
hash = p.readHash('hex');
index = p.readU32();
}
if (height === 0x7fffffff)
height = -1;
return {
version: version,
height: height,
value: value,
script: script,
coinbase: coinbase,
hash: hash,
index: index
};
return bcoin.memblock.fromRaw(p);
};
/**
@ -971,180 +689,7 @@ Parser.parseCoin = function parseCoin(p, extended) {
*/
Parser.parseTX = function parseTX(p) {
var inCount, inputs, input;
var outCount, outputs;
var version, locktime, i;
var raw, size, witnessSize;
if (Parser.isWitnessTX(p))
return Parser.parseWitnessTX(p);
p = new BufferReader(p);
p.start();
version = p.readU32(); // Technically signed
inCount = p.readVarint();
inputs = [];
for (i = 0; i < inCount; i++) {
input = Parser.parseInput(p);
input.witness = { items: [] };
inputs.push(input);
}
outCount = p.readVarint();
outputs = [];
for (i = 0; i < outCount; i++)
outputs.push(Parser.parseOutput(p));
locktime = p.readU32();
raw = p.endData();
size = raw.length;
witnessSize = 0;
return {
version: version,
flag: 1,
inputs: inputs,
outputs: outputs,
locktime: locktime,
_raw: raw,
_size: size,
_witnessSize: witnessSize
};
};
/**
* Test whether data is a witness transaction.
* @param {Buffer|BufferReader} p
* @returns {Boolean}
*/
Parser.isWitnessTX = function isWitnessTX(p) {
if (Buffer.isBuffer(p)) {
if (p.length < 12)
return false;
return p[4] === 0 && p[5] !== 0;
}
if (p.left() < 12)
return false;
return p.data[p.offset + 4] === 0 && p.data[p.offset + 5] !== 0;
};
/**
* Parse tx packet in witness serialization.
* @param {Buffer|BufferReader} p
* @returns {NakedTX}
*/
Parser.parseWitnessTX = function parseWitnessTX(p) {
var inCount, inputs, witness;
var outCount, outputs;
var marker, flag;
var version, locktime, i;
var raw, size, witnessSize, hasWitness;
p = new BufferReader(p);
p.start();
version = p.readU32(); // Technically signed
marker = p.readU8();
flag = p.readU8();
if (marker !== 0)
throw new Error('Invalid witness tx (marker != 0)');
if (flag === 0)
throw new Error('Invalid witness tx (flag == 0)');
inCount = p.readVarint();
inputs = [];
for (i = 0; i < inCount; i++)
inputs.push(Parser.parseInput(p));
outCount = p.readVarint();
outputs = [];
for (i = 0; i < outCount; i++)
outputs.push(Parser.parseOutput(p));
p.start();
for (i = 0; i < inCount; i++) {
witness = Parser.parseWitness(p);
inputs[i].witness = witness;
if (witness.items.length > 0)
hasWitness = true;
}
if (!hasWitness)
throw new Error('Witness tx has an empty witness.');
witnessSize = p.end() + 2;
locktime = p.readU32();
raw = p.endData();
size = raw.length;
return {
version: version,
flag: flag,
inputs: inputs,
outputs: outputs,
locktime: locktime,
_raw: raw,
_size: size,
_witnessSize: witnessSize
};
};
/**
* Parse serialized script (with varint length).
* @param {Buffer|BufferReader} p
* @returns {NakedScript}
*/
Parser.parseScript = function parseScript(p) {
var data;
p = new BufferReader(p);
data = p.readVarBytes();
return {
raw: data
};
};
/**
* Parse serialized witness.
* @param {Buffer|BufferReader} p
* @returns {NakedWitness}
*/
Parser.parseWitness = function parseWitness(p) {
var items = [];
var chunkCount, i;
p = new BufferReader(p);
chunkCount = p.readVarint();
for (i = 0; i < chunkCount; i++)
items.push(p.readVarBytes());
return {
items: items
};
return bcoin.tx.fromRaw(p);
};
/**

View File

@ -32,12 +32,20 @@ var ScriptError = bcoin.errors.ScriptError;
*/
function Witness(items) {
if (items instanceof Witness)
return items;
if (!(this instanceof Witness))
return new Witness(items);
if (!items)
items = [];
this.items = [];
this.redeem = null;
if (items)
this.fromOptions(items);
}
Witness.prototype.fromOptions = function fromOptions(items) {
if (items.items)
items = items.items;
@ -46,7 +54,13 @@ function Witness(items) {
this.redeem = null;
assert(Array.isArray(this.items));
}
return this;
};
Witness.fromOptions = function fromOptions(options) {
return new Witness().fromOptions(options);
};
Witness.prototype.toArray = function toArray() {
return this.items.slice();
@ -247,11 +261,19 @@ Witness.prototype.indexOf = function indexOf(data) {
* @returns {Buffer|String} Serialized script.
*/
Witness.prototype.toRaw = function toRaw(enc) {
var data = bcoin.protocol.framer.witness(this);
if (enc === 'hex')
data = data.toString('hex');
return data;
Witness.prototype.toRaw = function toRaw(writer) {
var p = bcoin.writer(writer);
var i;
p.writeVarint(this.items.length);
for (i = 0; i < this.items.length; i++)
p.writeVarBytes(this.items[i]);
if (!writer)
p = p.render();
return p;
};
/**
@ -417,18 +439,15 @@ Witness.encodeItem = function encodeItem(data) {
return data;
};
/**
* Create a witness from a serialized buffer.
* @param {Buffer|String} data - Serialized witness.
* @param {String?} enc - Either `"hex"` or `null`.
* @returns {Object} Naked witness object.
*/
Witness.prototype.fromRaw = function fromRaw(data) {
var p = bcoin.reader(data);
var chunkCount = p.readVarint();
var i;
Witness.parseRaw = function parseRaw(data, enc) {
if (enc === 'hex')
data = new Buffer(data, 'hex');
for (i = 0; i < chunkCount; i++)
this.items.push(p.readVarBytes());
return bcoin.protocol.parser.parseWitness(data);
return this;
};
/**
@ -439,7 +458,9 @@ Witness.parseRaw = function parseRaw(data, enc) {
*/
Witness.fromRaw = function fromRaw(data, enc) {
return new Witness(Witness.parseRaw(data, enc));
if (typeof data === 'string')
data = new Buffer(data, enc);
return new Witness().fromRaw(data);
};
/**
@ -1012,28 +1033,52 @@ function Script(raw) {
if (!(this instanceof Script))
return new Script(raw);
if (!raw)
raw = STACK_FALSE;
if (raw.raw)
raw = raw.raw;
this.raw = null;
this.code = null;
this.raw = STACK_FALSE;
this.code = [];
this.redeem = null;
if (Array.isArray(raw)) {
raw = Script.parseArray(raw);
this.raw = Script.encode(raw);
this.code = raw;
if (raw)
this.fromOptions(raw);
}
Script.prototype.fromOptions = function fromOptions(options) {
var code, raw;
if (Buffer.isBuffer(options)) {
raw = options;
} else if (Array.isArray(options)) {
code = options;
} else {
this.raw = raw;
this.code = Script.decode(raw);
code = options.code;
raw = options.raw;
}
if (code)
code = Script.parseArray(code);
this.raw = raw;
this.code = code;
this.redeem = null;
if (!this.raw) {
assert(this.code);
this.raw = Script.encode(this.code);
} else if (!this.code) {
assert(this.raw);
this.code = Script.decode(this.raw);
}
assert(Buffer.isBuffer(this.raw));
assert(Array.isArray(this.code));
}
return this;
};
Script.fromOptions = function fromOptions(raw) {
if (raw instanceof Script)
return raw;
return new Script().fromOptions(raw);
};
/**
* Convert the script to an array of
@ -1101,17 +1146,6 @@ Script.prototype.toASM = function toASM(decode) {
return Script.formatASM(this.code, decode);
};
/**
* Encode the script to a Buffer. Note that this
* will _not_ contain the varint size before it.
* This allows it to be hashed for scripthashes.
* @returns {Buffer} Serialized script.
*/
Script.prototype.encode = function encode() {
return this.raw;
};
/**
* Re-encode the script internally. Useful if you
* changed something manually in the `code` array.
@ -1127,11 +1161,12 @@ Script.prototype.compile = function compile() {
* @returns {Buffer|String} Serialized script.
*/
Script.prototype.toRaw = function toRaw(enc) {
var data = this.encode();
if (enc === 'hex')
data = data.toString('hex');
return data;
Script.prototype.toRaw = function toRaw(writer) {
if (writer) {
writer.writeVarBytes(this.raw);
return writer;
}
return this.raw;
};
/**
@ -3636,7 +3671,7 @@ Script.prototype.isPushOnly = function isPushOnly() {
if (op.data)
continue;
if (op.value == -1)
if (op.value === -1)
return false;
if (op.value > opcodes.OP_16)
@ -4112,25 +4147,6 @@ Script.sign = function sign(msg, key, type) {
return p.render();
};
/**
* Parse a serialized script, returning the "naked"
* representation of a Script object (the same
* properties, but it is not instantiated -- suitable
* as an options object for Script).
* @param {Buffer|String} data - Serialized script.
* @param {String?} enc - Either `"hex"` or `null`.
* @returns {Object} Naked script object.
*/
Script.parseRaw = function parseRaw(data, enc) {
if (enc === 'hex')
data = new Buffer(data, 'hex');
return {
raw: data
};
};
/**
* Create a script from a serialized buffer.
* @param {Buffer|String} data - Serialized script.
@ -4138,8 +4154,20 @@ Script.parseRaw = function parseRaw(data, enc) {
* @returns {Script}
*/
Script.prototype.fromRaw = function fromRaw(data) {
if (data instanceof bcoin.reader)
data = data.readVarBytes();
this.raw = data;
this.code = Script.decode(data);
return this;
};
Script.fromRaw = function fromRaw(data, enc) {
return new Script(Script.parseRaw(data, enc));
if (typeof data === 'string')
data = new Buffer(data, enc);
return new Script().fromRaw(data);
};
/**
@ -4344,8 +4372,8 @@ function Opcode(value, data) {
* @returns {Buffer}
*/
Opcode.prototype.toRaw = function toRaw() {
return Script.encode([this]);
Opcode.prototype.toRaw = function toRaw(writer) {
return Script.encode([this], writer);
};
/**

View File

@ -49,11 +49,41 @@ var BufferWriter = require('./writer');
*/
function TX(data) {
var i;
if (!(this instanceof TX))
return new TX(data);
this.version = 1;
this.flag = 1;
this.inputs = [];
this.outputs = [];
this.locktime = 0;
this.ts = 0;
this.block = null;
this.index = -1;
this.ps = utils.now();
this.height = -1;
this.mutable = false;
this._hash = null;
this._whash = null;
this._raw = null;
this._size = null;
this._witnessSize = null;
this._outputValue = null;
this._inputValue = null;
this._hashPrevouts = null;
this._hashSequence = null;
this._hashOutputs = null;
if (data)
this.fromOptions(data);
}
TX.prototype.fromOptions = function fromOptions(data) {
var i;
assert(data, 'TX data is required.');
assert(typeof data.version === 'number');
assert(typeof data.flag === 'number');
@ -91,7 +121,13 @@ function TX(data) {
for (i = 0; i < data.outputs.length; i++)
this.outputs.push(new bcoin.output(data.outputs[i]));
}
return this;
};
TX.fromOptions = function fromOptions(data) {
return new TX().fromOptions(data);
};
/**
* Clone the transaction.
@ -218,8 +254,14 @@ TX.prototype.witnessHash = function witnessHash(enc) {
* @returns {Buffer} Serialized transaction.
*/
TX.prototype.render = function render() {
return this.getRaw();
TX.prototype.render = function render(writer) {
var raw = this.getRaw();
if (writer) {
writer.writeBytes(raw);
writer._witnessSize = raw._witnessSize;
return writer;
}
return raw;
};
/**
@ -229,11 +271,17 @@ TX.prototype.render = function render() {
* @returns {Buffer} Serialized transaction.
*/
TX.prototype.renderNormal = function renderNormal() {
TX.prototype.renderNormal = function renderNormal(writer) {
var raw = this.getRaw();
if (!bcoin.protocol.parser.isWitnessTX(raw))
if (!TX.isWitness(raw)) {
if (writer) {
writer.writeBytes(raw);
writer._witnessSize = raw._witnessSize;
return writer;
}
return raw;
return bcoin.protocol.framer.tx(this);
}
return this.frameNormal(writer);
};
/**
@ -243,11 +291,17 @@ TX.prototype.renderNormal = function renderNormal() {
* @returns {Buffer} Serialized transaction.
*/
TX.prototype.renderWitness = function renderWitness() {
TX.prototype.renderWitness = function renderWitness(writer) {
var raw = this.getRaw();
if (bcoin.protocol.parser.isWitnessTX(raw))
if (TX.isWitness(raw)) {
if (writer) {
writer.writeBytes(raw);
writer._witnessSize = raw._witnessSize;
return writer;
}
return raw;
return bcoin.protocol.framer.witnessTX(this);
}
return this.frameWitness(writer);
};
/**
@ -268,9 +322,9 @@ TX.prototype.getRaw = function getRaw() {
}
if (this.hasWitness())
raw = bcoin.protocol.framer.witnessTX(this);
raw = this.frameWitness();
else
raw = bcoin.protocol.framer.tx(this);
raw = this.frameNormal();
if (!this.mutable) {
this._raw = raw;
@ -287,8 +341,16 @@ TX.prototype.getRaw = function getRaw() {
*/
TX.prototype.getSizes = function getSizes() {
if (this.mutable)
return bcoin.protocol.framer.tx.sizes(this);
var writer;
if (this.mutable) {
writer = new BufferWriter();
this.toRaw(writer);
return {
size: writer.written,
witnessSize: writer._witnessSize
};
}
this.getRaw();
@ -401,6 +463,7 @@ TX.prototype.signatureHashV0 = function signatureHashV0(index, prev, type) {
// Clone the transaction.
copy = {
version: this.version,
flag: 1,
inputs: [],
outputs: [],
locktime: this.locktime
@ -410,7 +473,6 @@ TX.prototype.signatureHashV0 = function signatureHashV0(index, prev, type) {
copy.inputs.push({
prevout: this.inputs[i].prevout,
script: this.inputs[i].script,
witness: this.inputs[i].witness,
sequence: this.inputs[i].sequence
});
}
@ -472,7 +534,7 @@ TX.prototype.signatureHashV0 = function signatureHashV0(index, prev, type) {
}
// Render the copy and append the hashtype.
bcoin.protocol.framer.tx(copy, p);
TX(copy).toRaw(p);
p.writeU32(type);
return utils.dsha256(p.render());
@ -480,7 +542,7 @@ TX.prototype.signatureHashV0 = function signatureHashV0(index, prev, type) {
TX.prototype.signatureHashV1 = function signatureHashV1(index, prev, type) {
var p = new BufferWriter();
var i, prevout, hashPrevouts, hashSequence, hashOutputs;
var i, hashPrevouts, hashSequence, hashOutputs;
if (typeof index !== 'number')
index = this.inputs.indexOf(index);
@ -496,11 +558,8 @@ TX.prototype.signatureHashV1 = function signatureHashV1(index, prev, type) {
hashPrevouts = this._hashPrevouts;
} else {
hashPrevouts = new BufferWriter();
for (i = 0; i < this.inputs.length; i++) {
prevout = this.inputs[i].prevout;
hashPrevouts.writeHash(prevout.hash);
hashPrevouts.writeU32(prevout.index);
}
for (i = 0; i < this.inputs.length; i++)
this.inputs[i].prevout.toRaw(hashPrevouts);
hashPrevouts = utils.dsha256(hashPrevouts.render());
if (!this.mutable)
this._hashPrevouts = hashPrevouts;
@ -533,13 +592,13 @@ TX.prototype.signatureHashV1 = function signatureHashV1(index, prev, type) {
} else {
hashOutputs = new BufferWriter();
for (i = 0; i < this.outputs.length; i++)
bcoin.protocol.framer.output(this.outputs[i], hashOutputs);
this.outputs[i].toRaw(hashOutputs);
hashOutputs = utils.dsha256(hashOutputs.render());
if (!this.mutable)
this._hashOutputs = hashOutputs;
}
} else if ((type & 0x1f) === constants.hashType.SINGLE && index < this.outputs.length) {
hashOutputs = bcoin.protocol.framer.output(this.outputs[index]);
hashOutputs = this.outputs[index].toRaw();
hashOutputs = utils.dsha256(hashOutputs);
} else {
hashOutputs = utils.copy(constants.ZERO_HASH);
@ -550,7 +609,7 @@ TX.prototype.signatureHashV1 = function signatureHashV1(index, prev, type) {
p.writeBytes(hashSequence);
p.writeHash(this.inputs[index].prevout.hash);
p.writeU32(this.inputs[index].prevout.index);
p.writeVarBytes(prev.encode());
p.writeVarBytes(prev.toRaw());
p.write64(this.inputs[index].coin.value);
p.writeU32(this.inputs[index].sequence);
p.writeBytes(hashOutputs);
@ -1640,7 +1699,7 @@ TX.prototype.getPrevout = function getPrevout() {
TX.prototype.isWatched = function isWatched(filter) {
var found = false;
var i, input, output, hash, index, outpoint;
var i, input, output, outpoint;
if (!filter)
return false;
@ -1656,12 +1715,12 @@ TX.prototype.isWatched = function isWatched(filter) {
// Test the output script
if (output.script.test(filter)) {
if (filter.update === constants.filterFlags.ALL) {
outpoint = bcoin.protocol.framer.outpoint(this.hash(), i);
filter.add(outpoint);
outpoint = bcoin.outpoint.fromTX(this, i);
filter.add(outpoint.toRaw());
} else if (filter.update === constants.filterFlags.PUBKEY_ONLY) {
if (output.script.isPubkey(true) || output.script.isMultisig()) {
outpoint = bcoin.protocol.framer.outpoint(this.hash(), i);
filter.add(outpoint);
outpoint = bcoin.outpoint.fromTX(this, i);
filter.add(outpoint.toRaw());
}
}
found = true;
@ -1675,9 +1734,7 @@ TX.prototype.isWatched = function isWatched(filter) {
// 4. Test data elements in input scripts
for (i = 0; i < this.inputs.length; i++) {
input = this.inputs[i];
hash = input.prevout.hash;
index = input.prevout.index;
outpoint = bcoin.protocol.framer.outpoint(hash, index);
outpoint = input.prevout.toRaw();
// Test the COutPoint structure
if (filter.test(outpoint))
@ -1835,25 +1892,31 @@ TX.prototype.toJSON = function toJSON() {
* for passing to the TX constructor).
*/
TX.parseJSON = function fromJSON(json) {
TX.prototype.fromJSON = function fromJSON(json) {
assert.equal(json.type, 'tx');
return {
block: json.block ? utils.revHex(json.block) : null,
height: json.height,
ts: json.ts,
ps: json.ps,
index: json.index,
changeIndex: json.changeIndex || -1,
version: json.version,
flag: json.flag,
inputs: json.inputs.map(function(input) {
return bcoin.input.parseJSON(input);
}),
outputs: json.outputs.map(function(output) {
return bcoin.output.parseJSON(output);
}),
locktime: json.locktime
};
this.block = json.block ? utils.revHex(json.block) : null;
this.height = json.height;
this.ts = json.ts;
this.ps = json.ps;
this.index = json.index;
this.changeIndex = json.changeIndex || -1;
this.version = json.version;
this.flag = json.flag;
this.inputs = json.inputs.map(function(input) {
return bcoin.input.fromJSON(input);
}),
this.outputs = json.outputs.map(function(output) {
return bcoin.output.fromJSON(output);
}),
this.locktime = json.locktime;
return this;
};
/**
@ -1864,8 +1927,7 @@ TX.parseJSON = function fromJSON(json) {
*/
TX.fromJSON = function fromJSON(json) {
assert.equal(json.type, 'tx');
return new TX(TX.parseJSON(json));
return new TX().fromJSON(json);
};
/**
@ -1875,27 +1937,8 @@ TX.fromJSON = function fromJSON(json) {
* @returns {Buffer|String}
*/
TX.prototype.toRaw = function toRaw(enc) {
var data = this.render();
if (enc === 'hex')
data = data.toString('hex');
return data;
};
/**
* Parse a serialized transaction.
* @param {Buffer} data
* @param {String?} enc - Encoding, can be `'hex'` or null.
* @returns {NakedTX} A "naked" transaction object.
*/
TX.parseRaw = function parseRaw(data, enc) {
if (enc === 'hex')
data = new Buffer(data, 'hex');
return bcoin.protocol.parser.parseTX(data);
TX.prototype.toRaw = function toRaw(writer) {
return this.render(writer);
};
/**
@ -1906,7 +1949,207 @@ TX.parseRaw = function parseRaw(data, enc) {
*/
TX.fromRaw = function fromRaw(data, enc) {
return new bcoin.tx(TX.parseRaw(data, enc));
if (typeof data === 'string')
data = new Buffer(data, enc);
return new TX().fromRaw(data);
};
/**
* Parse tx packet (will automatically switch to witness
* parsing if a witness transaction is detected).
* @param {Buffer|BufferReader} p
* @returns {NakedTX}
*/
TX.prototype.fromRaw = function fromRaw(data) {
var p, i, inCount, outCount, input;
if (TX.isWitness(data))
return this.fromWitness(data);
p = bcoin.reader(data);
p.start();
this.version = p.readU32(); // Technically signed
inCount = p.readVarint();
this.inputs = [];
for (i = 0; i < inCount; i++) {
input = bcoin.input.fromRaw(p);
input.witness = new bcoin.witness();
this.inputs.push(input);
}
outCount = p.readVarint();
this.outputs = [];
for (i = 0; i < outCount; i++)
this.outputs.push(bcoin.output.fromRaw(p));
this.locktime = p.readU32();
this._raw = p.endData();
this._size = this._raw.length;
this._witnessSize = 0;
return this;
};
/**
* Parse tx packet in witness serialization.
* @param {Buffer|BufferReader} p
* @returns {NakedTX}
*/
TX.prototype.fromWitness = function fromWitness(data) {
var p = bcoin.reader(data);
var i, marker, inCount, outCount, witness, hasWitness;
p.start();
this.version = p.readU32(); // Technically signed
marker = p.readU8();
this.flag = p.readU8();
if (marker !== 0)
throw new Error('Invalid witness tx (marker != 0)');
if (this.flag === 0)
throw new Error('Invalid witness tx (flag == 0)');
inCount = p.readVarint();
this.inputs = [];
for (i = 0; i < inCount; i++)
this.inputs.push(bcoin.input.fromRaw(p));
outCount = p.readVarint();
this.outputs = [];
for (i = 0; i < outCount; i++)
this.outputs.push(bcoin.output.fromRaw(p));
p.start();
for (i = 0; i < inCount; i++) {
witness = bcoin.witness.fromRaw(p);
this.inputs[i].witness = witness;
if (witness.items.length > 0)
hasWitness = true;
}
if (!hasWitness)
throw new Error('Witness tx has an empty witness.');
this._witnessSize = p.end() + 2;
this.locktime = p.readU32();
this._raw = p.endData();
this._size = this._raw.length;
return this;
};
/**
* Test whether data is a witness transaction.
* @param {Buffer|BufferReader} p
* @returns {Boolean}
*/
TX.isWitness = function isWitness(p) {
if (Buffer.isBuffer(p)) {
if (p.length < 12)
return false;
return p[4] === 0 && p[5] !== 0;
}
if (p.left() < 12)
return false;
return p.data[p.offset + 4] === 0 && p.data[p.offset + 5] !== 0;
};
/**
* Serialize transaction without witness.
* @param {NakedTX|TX} tx
* @param {BufferWriter?} writer - A buffer writer to continue writing from.
* @returns {Buffer} Returns a BufferWriter if `writer` was passed in.
*/
TX.prototype.frameNormal = function frameNormal(writer) {
var p = bcoin.writer(writer);
var i;
p.write32(this.version);
p.writeVarint(this.inputs.length);
for (i = 0; i < this.inputs.length; i++)
this.inputs[i].toRaw(p);
p.writeVarint(this.outputs.length);
for (i = 0; i < this.outputs.length; i++)
this.outputs[i].toRaw(p);
p.writeU32(this.locktime);
if (!writer)
p = p.render();
p._witnessSize = 0;
return p;
};
/**
* Serialize transaction with witness. Calculates the witness
* size as it is framing (exposed on return value as `_witnessSize`).
* @param {NakedTX|TX} tx
* @param {BufferWriter?} writer - A buffer writer to continue writing from.
* @returns {Buffer} Returns a BufferWriter if `writer` was passed in.
*/
TX.prototype.frameWitness = function frameWitness(writer) {
var p = bcoin.writer(writer);
var witnessSize = 0;
var i, start;
p.write32(this.version);
p.writeU8(0);
p.writeU8(this.flag || 1);
p.writeVarint(this.inputs.length);
for (i = 0; i < this.inputs.length; i++)
this.inputs[i].toRaw(p);
p.writeVarint(this.outputs.length);
for (i = 0; i < this.outputs.length; i++)
this.outputs[i].toRaw(p);
for (i = 0; i < this.inputs.length; i++) {
start = p.written;
this.inputs[i].witness.toRaw(p);
witnessSize += p.written - start;
}
p.writeU32(this.locktime);
if (!writer)
p = p.render();
p._witnessSize = witnessSize + 2;
return p;
};
/**
@ -1921,21 +2164,49 @@ TX.fromRaw = function fromRaw(data, enc) {
* @returns {Buffer}
*/
TX.prototype.toExtended = function toExtended(saveCoins, enc) {
var data, tmp;
TX.prototype.toExtended = function toExtended(saveCoins, writer) {
var height = this.height;
var index = this.index;
var changeIndex = this.changeIndex != null ? this.changeIndex : -1;
var p = bcoin.writer(writer);
var i, input;
if (typeof saveCoins === 'string') {
tmp = saveCoins;
saveCoins = enc;
enc = tmp;
if (height === -1)
height = 0x7fffffff;
if (index === -1)
index = 0x7fffffff;
if (changeIndex === -1)
changeIndex = 0x7fffffff;
this.toRaw(p);
p.writeU32(height);
p.writeHash(this.block || constants.ZERO_HASH);
p.writeU32(index);
p.writeU32(this.ts);
p.writeU32(this.ps);
// p.writeU32(changeIndex);
if (saveCoins) {
p.writeVarint(this.inputs.length);
for (i = 0; i < this.inputs.length; i++) {
input = this.inputs[i];
if (!input.coin) {
p.writeVarint(0);
continue;
}
p.writeVarBytes(input.coin.toRaw());
}
}
data = bcoin.protocol.framer.extendedTX(this, saveCoins);
if (!writer)
p = p.render();
if (enc === 'hex')
data = data.toString('hex');
return data;
return p;
};
/**
@ -1947,19 +2218,45 @@ TX.prototype.toExtended = function toExtended(saveCoins, enc) {
* @returns {NakedTX} - A "naked" transaction object.
*/
TX.parseExtended = function parseExtended(data, saveCoins, enc) {
var tmp;
TX.prototype.fromExtended = function fromExtended(data, saveCoins) {
var p = bcoin.reader(data);
var i, coinCount, coin;
if (typeof saveCoins === 'string') {
tmp = saveCoins;
saveCoins = enc;
enc = tmp;
this.fromRaw(p);
this.height = p.readU32();
this.block = p.readHash('hex');
this.index = p.readU32();
this.ts = p.readU32();
this.ps = p.readU32();
// this.changeIndex = p.readU32();
if (this.block === constants.NULL_HASH)
this.block = null;
if (this.height === 0x7fffffff)
this.height = -1;
if (this.index === 0x7fffffff)
this.index = -1;
if (this.changeIndex === 0x7fffffff)
this.changeIndex = -1;
if (saveCoins) {
coinCount = p.readVarint();
for (i = 0; i < coinCount; i++) {
coin = p.readVarBytes();
if (coin.length === 0)
continue;
coin = bcoin.coin.fromRaw(coin);
coin.hash = this.inputs[i].prevout.hash;
coin.index = this.inputs[i].prevout.index;
this.inputs[i].coin = coin;
}
}
if (enc === 'hex')
data = new Buffer(data, 'hex');
return bcoin.protocol.parser.parseExtendedTX(data, saveCoins);
return this;
};
/**
@ -1973,7 +2270,13 @@ TX.parseExtended = function parseExtended(data, saveCoins, enc) {
*/
TX.fromExtended = function fromExtended(data, saveCoins, enc) {
return new TX(TX.parseExtended(data, saveCoins, enc));
if (typeof saveCoins === 'string') {
enc = saveCoins;
saveCoins = false;
}
if (typeof data === 'string')
data = new Buffer(data, enc);
return new TX().fromExtended(data, saveCoins);
};
/**

View File

@ -817,13 +817,13 @@ Framer.item = function _item(item, p) {
} else {
if (item instanceof bcoin.block) {
p.writeU8(40);
bcoin.protocol.framer.witnessBlock(item, p);
item.render(p);
} else if (item instanceof bcoin.tx) {
p.writeU8(41);
bcoin.protocol.framer.extendedTX(item, true, p);
item.toExtended(true, p);
} else if (item instanceof bcoin.coin) {
p.writeU8(42);
bcoin.protocol.framer.coin(item, true, p);
item.toExtended(p);
} else if (item instanceof bcoin.chainentry) {
p.writeU8(43);
item.toRaw(p);

View File

@ -32,7 +32,7 @@ describe('Block', function() {
],
flags: new Buffer([245, 122, 0])
});
var raw = mblock.toRaw('hex');
var raw = mblock.toRaw().toString('hex');
var block;
var raw2 = '02000000d1831d4411bdfda89d9d8c842b541beafd1437fc560dbe5c0'

View File

@ -29,7 +29,7 @@ function parseTX(file) {
function parseExtended(file) {
file = fs.readFileSync(__dirname + '/' + file, 'utf8').trim();
return bcoin.tx.fromExtended(file, 'hex', true);
return bcoin.tx.fromExtended(file, true, 'hex');
}
function clearCache(tx, nocache) {
@ -152,7 +152,7 @@ describe('TX', function() {
assert(coolest.verify(constants.flags.VERIFY_NONE));
});
it('should parse witness tx properly', function() {
it('should parse witness tx properly' + suffix, function() {
clearCache(wtx, nocache);
assert.equal(wtx.inputs.length, 5);
assert.equal(wtx.outputs.length, 1980);
@ -503,14 +503,14 @@ describe('TX', function() {
assert(tx.outputs[0].value.bitLength() === 56);
var raw = tx.toRaw()
assert.throws(function() {
tx.fromRaw(raw);
bcoin.tx.fromRaw(raw);
});
delete tx._raw;
tx.outputs[0].value = new bn('00ffffffffffffff', 'hex').ineg();
assert(tx.outputs[0].value.bitLength() === 56);
var raw = tx.toRaw()
assert.throws(function() {
tx.fromRaw(raw);
bcoin.tx.fromRaw(raw);
});
});