diff --git a/lib/bcoin/coin.js b/lib/bcoin/coin.js index df439fac..51439cfe 100644 --- a/lib/bcoin/coin.js +++ b/lib/bcoin/coin.js @@ -30,18 +30,22 @@ function Coin(tx, index) { return tx; if (tx instanceof bcoin.tx) { - this.hash = tx.hash('hex'); - this.index = index; + this.version = tx.version; + this.height = tx.height; this.value = tx.outputs[index].value; this.script = tx.outputs[index].script; - this.height = tx.height; + this.hash = tx.hash('hex'); + this.index = index; + this.spent = false; } else { options = tx; - this.hash = options.hash; - this.index = options.index; + this.version = options.version; + this.height = options.height; this.value = options.value; this.script = options.script; - this.height = options.height; + this.hash = options.hash; + this.index = options.index; + this.spent = options.spent; } if (utils.isBuffer(this.hash)) @@ -49,13 +53,15 @@ function Coin(tx, index) { this.rhash = utils.revHex(this.hash); - assert(typeof this.hash === 'string'); - assert(utils.isFinite(this.index)); + // Object.freeze(this); + + assert(typeof this.version === 'number'); + assert(utils.isFinite(this.height)); assert(this.value instanceof bn); assert(Array.isArray(this.script)); - assert(utils.isFinite(this.height)); - - // Object.freeze(this); + assert(typeof this.hash === 'string'); + assert(utils.isFinite(this.index)); + assert(typeof this.spent === 'boolean'); } inherits(Coin, bcoin.output); @@ -107,42 +113,54 @@ Coin.prototype.__defineGetter__('age', function() { Coin.prototype.toJSON = function toJSON() { return { - hash: this.hash, - index: this.index, + version: this.version, + height: this.height, value: utils.btc(this.value), script: utils.toHex(bcoin.script.encode(this.script)), - height: this.height, + hash: this.hash, + index: this.index, + spent: this.spent, address: this.getAddress() }; }; Coin.fromJSON = function fromJSON(json) { return new Coin({ - hash: json.hash, - index: json.index, + version: json.version, + height: json.height, value: utils.satoshi(json.value), script: bcoin.script.decode(utils.toArray(json.script, 'hex')), - height: json.height, + hash: json.hash, + index: json.index, + spent: json.spent, address: json.address }); }; -// Not totally necessary, but this is 3 times -// faster than JSON serialization if high performance -// is a goal. -Coin.prototype.toRaw = function toRaw(enc) { +// This is basically BIP64 with some +// extra fields tacked on the end. +Coin.prototype.toRaw = function toRaw(enc, strict) { var script = bcoin.script.encode(this.script); + var intSize = utils.sizeIntv(script.length); var height = this.height; - var data = new Buffer(48 + script.length); + var data = new Buffer(16 + intSize + script.length + (!strict ? 37 : 0)); + var off = 0; if (height === -1) - height = 0xffffffff; + height = 0x7fffffff; - utils.copy(utils.toArray(this.hash, 'hex'), data, 0); - utils.writeU32(data, this.index, 32); - utils.writeU32(data, height, 36); - utils.write64(data, this.value, 40); - utils.copy(script, data, 48); + off += utils.writeU32(data, this.version, off); + off += utils.writeU32(data, height, off); + off += utils.write64(data, this.value, off); + assert(this.value.byteLength() <= 8); + off += utils.writeIntv(data, script.length, off); + off += utils.copy(script, data, off); + + if (!strict) { + off += utils.copy(utils.toArray(this.hash, 'hex'), data, off); + off += utils.writeU32(data, this.index, off); + off += utils.writeU8(data, this.spent ? 1 : 0, off); + } if (enc === 'hex') data = utils.toHex(data); @@ -150,23 +168,60 @@ Coin.prototype.toRaw = function toRaw(enc) { return data; }; -Coin.fromRaw = function fromRaw(data, enc) { - var height; +Coin.fromRaw = function fromRaw(data, enc, strict) { + var off = 0; + var version, height, value, script, hash, index, spent, scriptLen; if (enc === 'hex') data = utils.toArray(data, 'hex'); - height = utils.readU32(data, 36); + if (data.length < 17 + (!strict ? 37 : 0)) + throw new Error('Invalid utxo size'); - if (height === 0xffffffff) + version = utils.readU32(data, off); + off += 4; + + height = utils.readU32(data, off); + if (height === 0x7fffffff) height = -1; + off += 4; + + value = utils.read64(data, off); + off += 8; + + scriptLen = utils.readIntv(data, off); + off = scriptLen.off; + scriptLen = scriptLen.r; + + if (off + scriptLen > data.length - (!strict ? 37 : 0)) + throw new Error('Invalid utxo script length'); + + script = bcoin.script.decode(utils.toArray(data.slice(off, off + scriptLen))); + off += scriptLen; + + if (!strict) { + hash = utils.toHex(data.slice(off, off + 32)); + off += 32; + + index = utils.readU32(data, off); + off += 4; + + spent = utils.readU8(data, off) === 1; + off += 1; + } else { + hash = constants.zeroHash.slice(); + index = 0xffffffff; + spent = false; + } return new Coin({ - hash: utils.toHex(data.slice(0, 32)), - index: utils.readU32(data, 32), + version: version, height: height, - value: utils.read64(data, 40), - script: bcoin.script.decode(utils.toArray(data.slice(48))) + value: value, + script: script, + hash: hash, + index: index, + spent: spent }); }; diff --git a/lib/bcoin/utils.js b/lib/bcoin/utils.js index 18646121..96eaf68b 100644 --- a/lib/bcoin/utils.js +++ b/lib/bcoin/utils.js @@ -1314,3 +1314,22 @@ utils.writeIntv = function writeIntv(dst, num, off) { utils.writeU64(dst, num, off + 1); return 9; }; + +utils.sizeIntv = function sizeIntv(num) { + if (num instanceof bn) { + if (num.cmpn(0xffffffff) > 0) + return 9; + num = num.toNumber(); + } + + if (num < 0xfd) + return 1; + + if (num <= 0xffff) + return 3; + + if (num <= 0xffffffff) + return 5; + + return 9; +};