diff --git a/lib/transaction/payload/coinbasepayload.js b/lib/transaction/payload/coinbasepayload.js index 59797b8e8..1af327091 100644 --- a/lib/transaction/payload/coinbasepayload.js +++ b/lib/transaction/payload/coinbasepayload.js @@ -7,6 +7,7 @@ var BufferWriter = require('../../encoding/bufferwriter'); var BufferReader = require('../../encoding/bufferreader'); var AbstractPayload = require('./abstractpayload'); var utils = require('../../util/js'); +var BN = require('../../crypto/bn'); var isUnsignedInteger = utils.isUnsignedInteger; var isHexString = utils.isHexaString; @@ -20,6 +21,9 @@ var HASH_SIZE = constants.SHA256_HASH_SIZE; * @property {number} height * @property {string} merkleRootMNList * @property {string} merkleRootQuorums + * @property {number} bestCLHeight + * @property {Buffer} bestCLSignature + * @property {number} assetLockedAmount */ /** @@ -28,6 +32,9 @@ var HASH_SIZE = constants.SHA256_HASH_SIZE; * @property {number} height * @property {string} merkleRootMNList * @property {string} merkleRootQuorums + * @property {number} bestCLHeight + * @property {Buffer} bestCLSignature + * @property {number} assetLockedAmount */ function CoinbasePayload() { AbstractPayload.call(this); @@ -60,6 +67,12 @@ CoinbasePayload.fromBuffer = function (rawPayload) { .toString('hex'); } + if (payload.version >= 3) { + payload.bestCLHeight = payloadBufferReader.readVarintNum(); + payload.bestCLSignature = payloadBufferReader.read(96); + payload.assetLockedAmount = payloadBufferReader.readUInt64LEBN(); + } + if (!payloadBufferReader.finished()) { throw new Error( 'Failed to parse payload: raw payload is bigger than expected.' @@ -84,6 +97,12 @@ CoinbasePayload.fromJSON = function fromJSON(payloadJson) { payload.merkleRootQuorums = payloadJson.merkleRootQuorums; } + if (payload.version >= 3) { + payload.bestCLHeight = payloadJson.bestCLHeight; + payload.bestCLSignature = Buffer.from(payloadJson.bestCLSignature, 'hex'); + payload.assetLockedAmount = payloadJson.assetLockedAmount; + } + payload.validate(); return payload; }; @@ -123,6 +142,28 @@ CoinbasePayload.prototype.validate = function () { 'Invalid merkleRootQuorums size' ); } + if (this.version >= 3) { + Preconditions.checkArgument( + isUnsignedInteger(this.bestCLHeight), + 'Expect bestCLHeight to be an unsigned integer' + ); + Preconditions.checkArgument( + Buffer.isBuffer(this.bestCLSignature), + 'Expect bestCLSignature to be a buffer' + ); + Preconditions.checkArgument( + this.bestCLSignature.length === 96, + 'Invalid bestCLSignature size' + ); + Preconditions.checkArgument( + BN.isBN(this.assetLockedAmount), + 'Expect assetLockedAmount to be an instance of BN' + ); + Preconditions.checkArgument( + isUnsignedInteger(this.assetLockedAmount.toNumber()), + 'Expect assetLockedAmount to be an unsigned integer' + ); + } return true; }; @@ -140,6 +181,11 @@ CoinbasePayload.prototype.toJSON = function toJSON() { if (this.version >= 2) { json.merkleRootQuorums = this.merkleRootQuorums; } + if (this.version >= 3) { + json.bestCLHeight = this.bestCLHeight; + json.bestCLSignature = this.bestCLSignature.toString('hex'); + json.assetLockedAmount = this.assetLockedAmount; + } return json; }; @@ -162,6 +208,12 @@ CoinbasePayload.prototype.toBuffer = function toBuffer() { ); } + if (this.version >= 3) { + payloadBufferWriter.writeVarintNum(this.bestCLHeight); + payloadBufferWriter.write(this.bestCLSignature); + payloadBufferWriter.writeUInt64LEBN(new BN(this.assetLockedAmount)); + } + return payloadBufferWriter.toBuffer(); }; diff --git a/test/transaction/payload/coinbasepayload.js b/test/transaction/payload/coinbasepayload.js index 27c57f6b0..bdca771d9 100644 --- a/test/transaction/payload/coinbasepayload.js +++ b/test/transaction/payload/coinbasepayload.js @@ -6,28 +6,32 @@ var sinon = require('sinon'); var DashcoreLib = require('../../../index'); +var BN = require('../../../lib/crypto/bn'); + var CoinbasePayload = DashcoreLib.Transaction.Payload.CoinbasePayload; var merkleRootMNList = 'a1d4f77f5c85a9d56293878edda45ba6fb3e433e6b9bc278c0f4c5799748b975'; var merkleRootQuorums = '9491099bb93b789d8628acce8f8a84c0f4af8196d3dd6c2427aca0ee702fcc90'; +var bestCLHeight = 42; +var bestCLSignature = Buffer.alloc(96, 1); +var assetLockedAmount = new BN(1000); var validCoinbasePayloadJSON = { - version: 2, + version: 3, height: 80672, merkleRootMNList: merkleRootMNList, merkleRootQuorums: merkleRootQuorums, + bestCLHeight: bestCLHeight, + bestCLSignature: bestCLSignature, + assetLockedAmount: assetLockedAmount, }; // Contains same data as JSON above // 0200 is 16-bit unsigned 2, 203b0100 is 32 bit unsigned 80672, everything else is a hash. -var validCoinbasePayloadHexString = - '0200203b010075b9489779c5f4c078c29b6b3e433efba65ba4dd8e879362d5a9855c7ff7d4a190cc2f70eea0ac27246cddd39681aff4c0848a8fceac28869d783bb99b099194'; -var validCoinbasePayloadBuffer = Buffer.from( - validCoinbasePayloadHexString, - 'hex' -); var validCoinbasePayload = CoinbasePayload.fromJSON(validCoinbasePayloadJSON); +var validCoinbasePayloadBuffer = validCoinbasePayload.toBuffer(); +var validCoinbasePayloadHexString = validCoinbasePayloadBuffer.toString('hex'); describe('CoinbasePayload', function () { describe('.fromBuffer', function () { @@ -43,10 +47,13 @@ describe('CoinbasePayload', function () { var payload = CoinbasePayload.fromBuffer(validCoinbasePayloadBuffer); expect(payload).to.be.an.instanceOf(CoinbasePayload); - expect(payload.version).to.be.equal(2); + expect(payload.version).to.be.equal(3); expect(payload.height).to.be.equal(80672); expect(payload.merkleRootMNList).to.be.equal(merkleRootMNList); expect(payload.merkleRootQuorums).to.be.equal(merkleRootQuorums); + expect(payload.bestCLHeight).to.be.equal(bestCLHeight); + expect(payload.bestCLSignature).to.be.deep.equal(bestCLSignature); + expect(payload.assetLockedAmount).to.be.deep.equal(assetLockedAmount); expect(payload.validate.callCount).to.be.equal(1); }); @@ -73,9 +80,12 @@ describe('CoinbasePayload', function () { var payload = CoinbasePayload.fromJSON(validCoinbasePayloadJSON); expect(payload).to.be.an.instanceOf(CoinbasePayload); - expect(payload.version).to.be.equal(2); + expect(payload.version).to.be.equal(3); expect(payload.height).to.be.equal(80672); expect(payload.merkleRootMNList).to.be.equal(merkleRootMNList); + expect(payload.bestCLHeight).to.be.equal(bestCLHeight); + expect(payload.bestCLSignature).to.be.deep.equal(bestCLSignature); + expect(payload.assetLockedAmount).to.be.deep.equal(assetLockedAmount); expect(payload.validate.callCount).to.be.equal(1); }); @@ -231,6 +241,42 @@ describe('CoinbasePayload', function () { }).not.to.throw; } }); + it('Should allow only unsigned integer as bestCLHeight', function () { + var payload = validCoinbasePayload.copy(); + + payload.bestCLHeight = -10; + + expect(function () { + payload.validate(); + }).to.throw('Invalid Argument: Expect bestCLHeight to be an unsigned integer'); + }); + it('Should allow only 96 bytes as bestCLSignature', function () { + var payload = validCoinbasePayload.copy(); + + payload.bestCLSignature = Buffer.alloc(95, 1); + + expect(function () { + payload.validate(); + }).to.throw('Invalid Argument: Invalid bestCLSignature size'); + }); + it('Should allow only BN as assetLockedAmount', function () { + var payload = validCoinbasePayload.copy(); + + payload.assetLockedAmount = 10; + + expect(function () { + payload.validate(); + }).to.throw('Invalid Argument: Expect assetLockedAmount to be an instance of BN'); + }); + it('Should allow only unsigned integer as assetLockedAmount', function () { + var payload = validCoinbasePayload.copy(); + + payload.assetLockedAmount = new BN(-10); + + expect(function () { + payload.validate(); + }).to.throw('Invalid Argument: Expect assetLockedAmount to be an unsigned integer'); + }); }); describe('#toJSON', function () { @@ -252,6 +298,11 @@ describe('CoinbasePayload', function () { expect(payloadJSON.merkleRootMNList).to.be.equal( payload.merkleRootMNList ); + expect(payloadJSON.bestCLHeight).to.be.equal(payload.bestCLHeight); + expect(payloadJSON.bestCLSignature).to.be.equal(payload.bestCLSignature.toString('hex')); + expect(payloadJSON.assetLockedAmount).to.be.deep.equal( + payload.assetLockedAmount + ); }); it('Should call #validate', function () { var payload = CoinbasePayload.fromJSON(validCoinbasePayloadJSON); @@ -281,6 +332,13 @@ describe('CoinbasePayload', function () { expect(restoredPayload.merkleRootMNList).to.be.equal( payload.merkleRootMNList ); + expect(restoredPayload.bestCLHeight).to.be.equal(payload.bestCLHeight); + expect(restoredPayload.bestCLSignature).to.be.deep.equal( + payload.bestCLSignature + ); + expect(restoredPayload.assetLockedAmount).to.be.deep.equal( + payload.assetLockedAmount + ); }); it('Should call #validate', function () { var payload = CoinbasePayload.fromJSON(validCoinbasePayloadJSON);