serialization: add size calculation and static writer.
This commit is contained in:
parent
808d8678a6
commit
1296bb2302
@ -319,6 +319,15 @@ Input.fromJSON = function fromJSON(json) {
|
||||
return new Input().fromJSON(json);
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate size of serialized input.
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
Input.prototype.getSize = function getSize() {
|
||||
return 40 + this.script.getVarSize();
|
||||
};
|
||||
|
||||
/**
|
||||
* Serialize the input.
|
||||
* @param {String?} enc - Encoding, can be `'hex'` or null.
|
||||
|
||||
@ -109,6 +109,15 @@ Outpoint.prototype.toWriter = function toWriter(bw) {
|
||||
return bw;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate size of outpoint.
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
Outpoint.prototype.getSize = function getSize() {
|
||||
return 36;
|
||||
};
|
||||
|
||||
/**
|
||||
* Serialize outpoint.
|
||||
* @returns {Buffer}
|
||||
|
||||
@ -192,7 +192,7 @@ Output.prototype.getDustThreshold = function getDustThreshold(rate) {
|
||||
*/
|
||||
|
||||
Output.prototype.getSize = function getSize() {
|
||||
return this.toWriter(new BufferWriter()).written;
|
||||
return 8 + this.script.getVarSize();
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -14,6 +14,7 @@ var util = require('../utils/util');
|
||||
var encoding = require('./encoding');
|
||||
var BufferReader = require('../utils/reader');
|
||||
var BufferWriter = require('../utils/writer');
|
||||
var StaticWriter = require('../utils/staticwriter');
|
||||
var opcodes = constants.opcodes;
|
||||
|
||||
/**
|
||||
@ -85,7 +86,32 @@ Opcode.prototype.toWriter = function toWriter(bw) {
|
||||
*/
|
||||
|
||||
Opcode.prototype.toRaw = function toRaw() {
|
||||
return this.toWriter(new BufferWriter()).render();
|
||||
var size = this.getSize();
|
||||
return this.toWriter(new StaticWriter(size)).render();
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate opcode size.
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
Opcode.prototype.getSize = function getSize() {
|
||||
if (!this.data)
|
||||
return 1;
|
||||
|
||||
if (this.value <= 0x4b)
|
||||
return 1 + this.data.length;
|
||||
|
||||
switch (this.value) {
|
||||
case opcodes.OP_PUSHDATA1:
|
||||
return 2 + this.data.length;
|
||||
case opcodes.OP_PUSHDATA2:
|
||||
return 3 + this.data.length;
|
||||
case opcodes.OP_PUSHDATA4:
|
||||
return 5 + this.data.length;
|
||||
default:
|
||||
throw new Error('Unknown pushdata opcode.');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -14,6 +14,7 @@ var crypto = require('../crypto/crypto');
|
||||
var assert = require('assert');
|
||||
var BufferWriter = require('../utils/writer');
|
||||
var BufferReader = require('../utils/reader');
|
||||
var StaticWriter = require('../utils/staticwriter');
|
||||
var opcodes = constants.opcodes;
|
||||
var STACK_TRUE = new Buffer([1]);
|
||||
var STACK_FALSE = new Buffer(0);
|
||||
@ -25,6 +26,7 @@ var Opcode = require('./opcode');
|
||||
var Stack = require('./stack');
|
||||
var sigcache = require('./sigcache');
|
||||
var encoding = require('./encoding');
|
||||
var enc = require('../utils/encoding');
|
||||
var ec = require('../crypto/ec');
|
||||
var Address = require('../primitives/address');
|
||||
|
||||
@ -251,7 +253,8 @@ Script.prototype.toASM = function toASM(decode) {
|
||||
*/
|
||||
|
||||
Script.prototype.compile = function compile() {
|
||||
var bw = new BufferWriter();
|
||||
var size = this.getCodeSize();
|
||||
var bw = new StaticWriter(size);
|
||||
var i, op;
|
||||
|
||||
for (i = 0; i < this.code.length; i++) {
|
||||
@ -1942,7 +1945,8 @@ Script.prototype.isStandard = function isStandard() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate size of script excluding the varint size bytes.
|
||||
* Calculate the size of the script
|
||||
* excluding the varint size bytes.
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
@ -1950,6 +1954,33 @@ Script.prototype.getSize = function getSize() {
|
||||
return this.raw.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate the size of the script
|
||||
* including the varint size bytes.
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
Script.prototype.getVarSize = function getVarSize() {
|
||||
return enc.sizeVarint(this.raw.length) + this.raw.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate size of code to be compiled.
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
Script.prototype.getCodeSize = function getCodeSize() {
|
||||
var size = 0;
|
||||
var i, op;
|
||||
|
||||
for (i = 0; i < this.code.length; i++) {
|
||||
op = this.code[i];
|
||||
size += op.getSize();
|
||||
}
|
||||
|
||||
return size;
|
||||
};
|
||||
|
||||
/**
|
||||
* "Guess" the address of the input script.
|
||||
* This method is not 100% reliable.
|
||||
|
||||
@ -17,8 +17,10 @@ var STACK_NEGATE = new Buffer([0x81]);
|
||||
var scriptTypes = constants.scriptTypes;
|
||||
var Script = require('./script');
|
||||
var encoding = require('./encoding');
|
||||
var enc = require('../utils/encoding');
|
||||
var Opcode = require('./opcode');
|
||||
var BufferWriter = require('../utils/writer');
|
||||
var StaticWriter = require('../utils/staticwriter');
|
||||
var BufferReader = require('../utils/reader');
|
||||
var Address = require('../primitives/address');
|
||||
var Stack = require('./stack');
|
||||
@ -303,6 +305,35 @@ Witness.prototype.indexOf = function indexOf(data) {
|
||||
return util.indexOf(this.items, data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate size of the witness
|
||||
* excluding the varint size bytes.
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
Witness.prototype.getSize = function getSize() {
|
||||
var size = 0;
|
||||
var i, item;
|
||||
|
||||
for (i = 0; i < this.items.length; i++) {
|
||||
item = this.items[i];
|
||||
size += enc.sizeVarint(item.length);
|
||||
size += item.length;
|
||||
}
|
||||
|
||||
return size;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate size of the witness
|
||||
* including the varint size bytes.
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
Witness.prototype.getVarSize = function getVarSize() {
|
||||
return enc.sizeVarint(this.items.length) + this.getSize();
|
||||
};
|
||||
|
||||
/**
|
||||
* Write witness to a buffer writer.
|
||||
* @param {BufferWriter} bw
|
||||
@ -326,7 +357,8 @@ Witness.prototype.toWriter = function toWriter(bw) {
|
||||
*/
|
||||
|
||||
Witness.prototype.toRaw = function toRaw() {
|
||||
return this.toWriter(new BufferWriter()).render();
|
||||
var size = this.getSize();
|
||||
return this.toWriter(new StaticWriter(size)).render();
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
429
lib/utils/staticwriter.js
Normal file
429
lib/utils/staticwriter.js
Normal file
@ -0,0 +1,429 @@
|
||||
/*!
|
||||
* writer.js - buffer writer for bcoin
|
||||
* Copyright (c) 2014-2015, Fedor Indutny (MIT License)
|
||||
* Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
|
||||
* https://github.com/bcoin-org/bcoin
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var encoding = require('./encoding');
|
||||
var crypto = require('../crypto/crypto');
|
||||
|
||||
/**
|
||||
* An object that allows writing of buffers in a
|
||||
* sane manner. This buffer writer is extremely
|
||||
* optimized since it does not actually write
|
||||
* anything until `render` is called. It makes
|
||||
* one allocation: at the end, once it knows the
|
||||
* size of the buffer to be allocated. Because
|
||||
* of this, it can also act as a size calculator
|
||||
* which is useful for guaging block size
|
||||
* without actually serializing any data.
|
||||
* @exports StaticWriter
|
||||
* @constructor
|
||||
* @param {(StaticWriter|Object)?} options
|
||||
*/
|
||||
|
||||
function StaticWriter(size) {
|
||||
if (!(this instanceof StaticWriter))
|
||||
return new StaticWriter(size);
|
||||
|
||||
this.data = new Buffer(size);
|
||||
this.written = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate and render the final buffer.
|
||||
* @param {Boolean?} keep - Do not destroy the writer.
|
||||
* @returns {Buffer} Rendered buffer.
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.render = function render(keep) {
|
||||
var data = this.data;
|
||||
|
||||
assert.equal(this.written, data.length);
|
||||
|
||||
if (!keep)
|
||||
this.destroy();
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get size of data written so far.
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.getSize = function getSize() {
|
||||
return this.written;
|
||||
};
|
||||
|
||||
/**
|
||||
* Seek to relative offset.
|
||||
* @param {Number} offset
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.seek = function seek(offset) {
|
||||
this.written += offset;
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy the buffer writer.
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.destroy = function destroy() {
|
||||
this.data = null;
|
||||
this.written = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Write uint8.
|
||||
* @param {Number} value
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.writeU8 = function writeU8(value) {
|
||||
this.written = this.data.writeUInt8(value, this.written, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Write uint16le.
|
||||
* @param {Number} value
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.writeU16 = function writeU16(value) {
|
||||
this.written = this.data.writeUInt16LE(value, this.written, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Write uint16be.
|
||||
* @param {Number} value
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.writeU16BE = function writeU16BE(value) {
|
||||
this.written = this.data.writeUInt16BE(value, this.written, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Write uint32le.
|
||||
* @param {Number} value
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.writeU32 = function writeU32(value) {
|
||||
this.written = this.data.writeUInt32LE(value, this.written, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Write uint32be.
|
||||
* @param {Number} value
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.writeU32BE = function writeU32BE(value) {
|
||||
this.written = this.data.writeUInt32BE(value, this.written, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Write uint64le.
|
||||
* @param {Number} value
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.writeU64 = function writeU64(value) {
|
||||
this.written = encoding.writeU64(this.data, value, this.written);
|
||||
};
|
||||
|
||||
/**
|
||||
* Write uint64be.
|
||||
* @param {Number} value
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.writeU64BE = function writeU64BE(value) {
|
||||
this.written = encoding.writeU64BE(this.data, value, this.written);
|
||||
};
|
||||
|
||||
/**
|
||||
* Write uint64le.
|
||||
* @param {BN} value
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.writeU64BN = function writeU64BN(value) {
|
||||
assert(false, 'Not implemented.');
|
||||
};
|
||||
|
||||
/**
|
||||
* Write uint64be.
|
||||
* @param {BN} value
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.writeU64BEBN = function writeU64BEBN(value) {
|
||||
assert(false, 'Not implemented.');
|
||||
};
|
||||
|
||||
/**
|
||||
* Write int8.
|
||||
* @param {Number} value
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.write8 = function write8(value) {
|
||||
this.written = this.data.writeInt8(value, this.written, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Write int16le.
|
||||
* @param {Number} value
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.write16 = function write16(value) {
|
||||
this.written = this.data.writeInt16LE(value, this.written, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Write int16be.
|
||||
* @param {Number} value
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.write16BE = function write16BE(value) {
|
||||
this.written = this.data.writeInt16BE(value, this.written, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Write int32le.
|
||||
* @param {Number} value
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.write32 = function write32(value) {
|
||||
this.written = this.data.writeInt32LE(value, this.written, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Write int32be.
|
||||
* @param {Number} value
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.write32BE = function write32BE(value) {
|
||||
this.written = this.data.writeInt32BE(value, this.written, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Write int64le.
|
||||
* @param {Number} value
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.write64 = function write64(value) {
|
||||
this.written = encoding.write64(this.data, value, this.written);
|
||||
};
|
||||
|
||||
/**
|
||||
* Write int64be.
|
||||
* @param {Number} value
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.write64BE = function write64BE(value) {
|
||||
this.written = encoding.write64BE(this.data, value, this.written);
|
||||
};
|
||||
|
||||
/**
|
||||
* Write int64le.
|
||||
* @param {BN} value
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.write64BN = function write64BN(value) {
|
||||
assert(false, 'Not implemented.');
|
||||
};
|
||||
|
||||
/**
|
||||
* Write int64be.
|
||||
* @param {BN} value
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.write64BEBN = function write64BEBN(value) {
|
||||
assert(false, 'Not implemented.');
|
||||
};
|
||||
|
||||
/**
|
||||
* Write float le.
|
||||
* @param {Number} value
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.writeFloat = function writeFloat(value) {
|
||||
this.written = this.data.writeFloatLE(value, this.written, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Write float be.
|
||||
* @param {Number} value
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.writeFloatBE = function writeFloatBE(value) {
|
||||
this.written = this.data.writeFloatBE(value, this.written, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Write double le.
|
||||
* @param {Number} value
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.writeDouble = function writeDouble(value) {
|
||||
this.written = this.data.writeDoubleLE(value, this.written, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Write double be.
|
||||
* @param {Number} value
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.writeDoubleBE = function writeDoubleBE(value) {
|
||||
this.written = this.data.writeDoubleBE(value, this.written, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Write a varint.
|
||||
* @param {Number} value
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.writeVarint = function writeVarint(value) {
|
||||
this.written = encoding.writeVarint(this.data, value, this.written);
|
||||
};
|
||||
|
||||
/**
|
||||
* Write a varint.
|
||||
* @param {BN} value
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.writeVarintBN = function writeVarintBN(value) {
|
||||
assert(false, 'Not implemented.');
|
||||
};
|
||||
|
||||
/**
|
||||
* Write a varint (type 2).
|
||||
* @param {Number} value
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.writeVarint2 = function writeVarint2(value) {
|
||||
this.written = encoding.writeVarint2(this.data, value, this.written);
|
||||
};
|
||||
|
||||
/**
|
||||
* Write a varint (type 2).
|
||||
* @param {BN} value
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.writeVarint2BN = function writeVarint2BN(value) {
|
||||
assert(false, 'Not implemented.');
|
||||
};
|
||||
|
||||
/**
|
||||
* Write bytes.
|
||||
* @param {Buffer} value
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.writeBytes = function writeBytes(value) {
|
||||
if (value.length === 0)
|
||||
return;
|
||||
|
||||
this.written += value.copy(this.data, this.written);
|
||||
};
|
||||
|
||||
/**
|
||||
* Write bytes with a varint length before them.
|
||||
* @param {Buffer} value
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.writeVarBytes = function writeVarBytes(value) {
|
||||
this.writeVarint(value.length);
|
||||
|
||||
if (value.length === 0)
|
||||
return;
|
||||
|
||||
this.writeBytes(value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Write string to buffer.
|
||||
* @param {String|Buffer} value
|
||||
* @param {String?} enc - Any buffer-supported encoding.
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.writeString = function writeString(value, enc) {
|
||||
if (typeof value !== 'string')
|
||||
return this.writeBytes(value);
|
||||
|
||||
if (value.length === 0)
|
||||
return;
|
||||
|
||||
this.written += this.data.write(value, this.written, enc);
|
||||
};
|
||||
|
||||
/**
|
||||
* Write a hash/hex-string.
|
||||
* @param {Hash|Buffer}
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.writeHash = function writeHash(value) {
|
||||
this.writeString(value, 'hex');
|
||||
};
|
||||
|
||||
/**
|
||||
* Write a string with a varint length before it.
|
||||
* @param {String|Buffer}
|
||||
* @param {String?} enc - Any buffer-supported encoding.
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.writeVarString = function writeVarString(value, enc) {
|
||||
var size;
|
||||
|
||||
if (typeof value !== 'string')
|
||||
return this.writeVarBytes(value);
|
||||
|
||||
size = Buffer.byteLength(value, enc);
|
||||
|
||||
this.writeVarint(size);
|
||||
|
||||
if (value.length === 0)
|
||||
return;
|
||||
|
||||
this.writeString(value, enc);
|
||||
};
|
||||
|
||||
/**
|
||||
* Write a null-terminated string.
|
||||
* @param {String|Buffer}
|
||||
* @param {String?} enc - Any buffer-supported encoding.
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.writeNullString = function writeNullString(value, enc) {
|
||||
this.writeString(value, enc);
|
||||
this.writeU8(0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate and write a checksum for the data written so far.
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.writeChecksum = function writeChecksum() {
|
||||
var data = this.data.slice(0, this.written);
|
||||
var hash = crypto.hash256(data);
|
||||
this.written += hash.copy(this.data, this.written, 0, 4);
|
||||
};
|
||||
|
||||
/**
|
||||
* Fill N bytes with value.
|
||||
* @param {Number} value
|
||||
* @param {Number} size
|
||||
*/
|
||||
|
||||
StaticWriter.prototype.fill = function fill(value, size) {
|
||||
assert(size >= 0);
|
||||
|
||||
if (size === 0)
|
||||
return;
|
||||
|
||||
this.data.fill(value, this.written, this.written + size);
|
||||
this.written += size;
|
||||
};
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
module.exports = StaticWriter;
|
||||
Loading…
Reference in New Issue
Block a user