From 1550a79e82568bfb7d008d410802cee7c971ab0c Mon Sep 17 00:00:00 2001 From: thephez Date: Wed, 11 Sep 2024 14:34:02 -0400 Subject: [PATCH 1/3] feat: groundwork for mnhfsignal special tx --- index.d.ts | 1 + lib/constants/index.js | 1 + lib/transaction/payload/index.js | 1 + lib/transaction/payload/payload.js | 3 +++ test-d/index.test-d.ts | 1 + typings/transaction/payload/Payload.d.ts | 2 ++ 6 files changed, 9 insertions(+) diff --git a/index.d.ts b/index.d.ts index 65b32b8f3..3d7c8c7a1 100644 --- a/index.d.ts +++ b/index.d.ts @@ -60,6 +60,7 @@ export { MultiSigScriptHashInput } from './typings/transaction/input/MultiSigScr export { AbstractPayload }; export { CoinbasePayload } from './typings/transaction/payload/CoinbasePayload'; export { CommitmentTxPayload } from './typings/transaction/payload/CommitmentTxPayload'; +export { MnHfSignalPayload } from './typings/transaction/payload/MnHfSignalPayload'; export { ProRegTxPayload } from './typings/transaction/payload/ProRegTxPayload'; export { ProUpRegTxPayload } from './typings/transaction/payload/ProUpRegTxPayload'; export { ProUpRevTxPayload } from './typings/transaction/payload/ProUpRevTxPayload'; diff --git a/lib/constants/index.js b/lib/constants/index.js index bac664c53..c0151a29d 100644 --- a/lib/constants/index.js +++ b/lib/constants/index.js @@ -24,6 +24,7 @@ module.exports = { TRANSACTION_PROVIDER_UPDATE_REVOKE: 4, TRANSACTION_COINBASE: 5, TRANSACTION_QUORUM_COMMITMENT: 6, + TRANSACTION_MASTERNODE_HARD_FORK_SIGNAL: 7, TRANSACTION_ASSET_LOCK: 8, TRANSACTION_ASSET_UNLOCK: 9, }, diff --git a/lib/transaction/payload/index.js b/lib/transaction/payload/index.js index 69f87ff4e..fa64a7e06 100644 --- a/lib/transaction/payload/index.js +++ b/lib/transaction/payload/index.js @@ -10,6 +10,7 @@ Payload.ProTxUpServPayload = require('./proupservtxpayload'); Payload.CoinbasePayload = require('./coinbasepayload'); Payload.constants = require('../../constants'); Payload.CommitmentTxPayload = require('./commitmenttxpayload'); +Payload.MnHfSignalPayload = require('./mnhfsignalpayload'); Payload.AssetLockPayload = require('./assetlockpayload'); Payload.AssetUnlockPayload = require('./assetunlockpayload'); diff --git a/lib/transaction/payload/payload.js b/lib/transaction/payload/payload.js index cad426608..15aee1c03 100644 --- a/lib/transaction/payload/payload.js +++ b/lib/transaction/payload/payload.js @@ -10,6 +10,7 @@ var ProRegTxPayload = require('./proregtxpayload'); var ProTxUpServPayload = require('./proupservtxpayload'); var ProUpRegTxPayload = require('./proupregtxpayload'); var ProUpRevTxPayload = require('./prouprevtxpayload'); +var MnHfSignalPayload = require('./mnhfsignalpayload'); var AssetLockPayload = require('./assetlockpayload'); var AssetUnlockPayload = require('./assetunlockpayload'); @@ -25,6 +26,8 @@ PayloadClasses[RegisteredPayloadTypes.TRANSACTION_PROVIDER_UPDATE_REGISTRAR] = ProUpRegTxPayload; PayloadClasses[RegisteredPayloadTypes.TRANSACTION_PROVIDER_UPDATE_REVOKE] = ProUpRevTxPayload; +PayloadClasses[RegisteredPayloadTypes.TRANSACTION_MASTERNODE_HARD_FORK_SIGNAL] = + MnHfSignalPayload; PayloadClasses[RegisteredPayloadTypes.TRANSACTION_ASSET_LOCK] = AssetLockPayload; PayloadClasses[RegisteredPayloadTypes.TRANSACTION_ASSET_UNLOCK] = diff --git a/test-d/index.test-d.ts b/test-d/index.test-d.ts index 20499dece..fee209fba 100644 --- a/test-d/index.test-d.ts +++ b/test-d/index.test-d.ts @@ -41,6 +41,7 @@ import type { AssetUnlockPayload, CoinbasePayload, CommitmentTxPayload, + MnHfSignalPayload, ProRegTxPayload, ProUpRegTxPayload, ProUpRevTxPayload, diff --git a/typings/transaction/payload/Payload.d.ts b/typings/transaction/payload/Payload.d.ts index 7fcef71ff..7d201db54 100644 --- a/typings/transaction/payload/Payload.d.ts +++ b/typings/transaction/payload/Payload.d.ts @@ -4,6 +4,7 @@ import { ProUpRevTxPayload } from './ProUpRevTxPayload'; import { ProUpServTxPayload } from './ProUpServTxPayload'; import { CoinbasePayload } from './CoinbasePayload'; import { CommitmentTxPayload } from './CommitmentTxPayload'; +import { MnHfSignalPayload } from './MnHfSignalPayload'; import { AssetLockPayload } from './AssetLockPayload'; import { AssetUnlockPayload } from './AssetUnlockPayload'; @@ -14,6 +15,7 @@ export namespace Payload { export { ProUpServTxPayload }; export { CoinbasePayload }; export { CommitmentTxPayload }; + export { MnHfSignalPayload }; export { AssetLockPayload }; export { AssetUnlockPayload }; } From ed46e6ddf6f3549a61091ee86ff5b063720cb262 Mon Sep 17 00:00:00 2001 From: thephez Date: Wed, 11 Sep 2024 14:57:51 -0400 Subject: [PATCH 2/3] feat: add mnhfsignal --- lib/transaction/payload/mnhfsignalpayload.js | 163 ++++++++++++++ test/transaction/payload/mnhfsignalpayload.js | 203 ++++++++++++++++++ .../payload/MnHfSignalPayload.d.ts | 73 +++++++ 3 files changed, 439 insertions(+) create mode 100644 lib/transaction/payload/mnhfsignalpayload.js create mode 100644 test/transaction/payload/mnhfsignalpayload.js create mode 100644 typings/transaction/payload/MnHfSignalPayload.d.ts diff --git a/lib/transaction/payload/mnhfsignalpayload.js b/lib/transaction/payload/mnhfsignalpayload.js new file mode 100644 index 000000000..f83aee27f --- /dev/null +++ b/lib/transaction/payload/mnhfsignalpayload.js @@ -0,0 +1,163 @@ +/* eslint-disable */ +// TODO: Remove previous line and work through linting issues at next edit + +var Preconditions = require('../../util/preconditions'); +var BufferWriter = require('../../encoding/bufferwriter'); +var BufferReader = require('../../encoding/bufferreader'); +var AbstractPayload = require('./abstractpayload'); +var utils = require('../../util/js'); +const _ = require('lodash'); + +var isUnsignedInteger = utils.isUnsignedInteger; + +var CURRENT_PAYLOAD_VERSION = 1; + +/** + * @typedef {Object} MnHfSignalPayloadJSON + * @property {number} version + * @property {Object} signal + * @property {number} signal.versionBit + * @property {string} signal.quorumHash + * @property {string} signal.sig + */ + +/** + * @class MnHfSignalPayload + * @property {number} version + * @property {Object} signal + * @property {number} signal.versionBit + * @property {string} signal.quorumHash + * @property {string} signal.sig + */ +function MnHfSignalPayload() { + AbstractPayload.call(this); + this.version = CURRENT_PAYLOAD_VERSION; + this.signal = { + versionBit: 0, + quorumHash: '', + sig: '' + }; +} + +MnHfSignalPayload.prototype = Object.create(AbstractPayload.prototype); +MnHfSignalPayload.prototype.constructor = AbstractPayload; + +/* Static methods */ + +/** + * Parse raw payload + * @param {Buffer} rawPayload + * @return {MnHfSignalPayload} + */ +MnHfSignalPayload.fromBuffer = function (rawPayload) { + var payloadBufferReader = new BufferReader(rawPayload); + var payload = new MnHfSignalPayload(); + + payload.version = payloadBufferReader.readUInt8(); + payload.signal.versionBit = payloadBufferReader.readUInt8(); + payload.signal.quorumHash = payloadBufferReader.read(32).reverse().toString('hex'); + payload.signal.sig = payloadBufferReader.read(96).toString('hex'); + + if (!payloadBufferReader.finished()) { + throw new Error( + 'Failed to parse payload: raw payload is bigger than expected.' + ); + } + + payload.validate(); + return payload; +}; + +/** + * Create new instance of payload from JSON + * @param {string|MnHfSignalPayloadJSON} payloadJson + * @return {MnHfSignalPayload} + */ +MnHfSignalPayload.fromJSON = function fromJSON(payloadJson) { + var payload = new MnHfSignalPayload(); + payload.version = payloadJson.version || CURRENT_PAYLOAD_VERSION; + payload.signal.versionBit = payloadJson.signal.versionBit; + payload.signal.quorumHash = payloadJson.signal.quorumHash; + payload.signal.sig = payloadJson.signal.sig; + + payload.validate(); + return payload; +}; + +/* Instance methods */ + +/** + * Validates payload data + * @return {boolean} + */ +MnHfSignalPayload.prototype.validate = function () { + Preconditions.checkArgument( + isUnsignedInteger(this.version), + 'Expect version to be an unsigned integer' + ); + + Preconditions.checkArgument( + this.version !== 0 && this.version <= CURRENT_PAYLOAD_VERSION, + 'Invalid version' + ); + + Preconditions.checkArgument( + isUnsignedInteger(this.signal.versionBit) && this.signal.versionBit <= 255, + 'Expect signal.versionBit to be an unsigned 8-bit integer' + ); + + Preconditions.checkArgument( + utils.isSha256HexString(this.signal.quorumHash), + 'Expect signal.quorumHash to be a valid 32-byte SHA256 hex string' + ); + + Preconditions.checkArgument( + utils.isHexaString(this.signal.sig) && Buffer.from(this.signal.sig, 'hex').length === 96, + 'Expect signal.sig to be a valid 96-byte hex string' + ); + + return true; +}; + +/** + * Serializes payload to JSON + * @return {MnHfSignalPayloadJSON} + */ +MnHfSignalPayload.prototype.toJSON = function toJSON() { + this.validate(); + return { + version: this.version, + signal: { + versionBit: this.signal.versionBit, + quorumHash: this.signal.quorumHash, + sig: this.signal.sig + } + }; +}; + +/** + * Serialize payload to buffer + * @return {Buffer} + */ +MnHfSignalPayload.prototype.toBuffer = function toBuffer() { + this.validate(); + var payloadBufferWriter = new BufferWriter(); + + payloadBufferWriter + .writeUInt8(this.version) + .writeUInt8(this.signal.versionBit) + .write(Buffer.from(this.signal.quorumHash, 'hex').reverse()) + .write(Buffer.from(this.signal.sig, 'hex')); + + return payloadBufferWriter.toBuffer(); +}; + +/** + * Copy payload instance + * @return {MnHfSignalPayload} + */ +MnHfSignalPayload.prototype.copy = function copy() { + return MnHfSignalPayload.fromJSON(this.toJSON()); +}; + +module.exports = MnHfSignalPayload; diff --git a/test/transaction/payload/mnhfsignalpayload.js b/test/transaction/payload/mnhfsignalpayload.js new file mode 100644 index 000000000..b97380007 --- /dev/null +++ b/test/transaction/payload/mnhfsignalpayload.js @@ -0,0 +1,203 @@ +/* eslint-disable */ +// TODO: Remove previous line and work through linting issues at next edit + +var expect = require('chai').expect; +var sinon = require('sinon'); + +var DashcoreLib = require('../../../index'); + +var MnHfSignalPayload = DashcoreLib.Transaction.Payload.MnHfSignalPayload; + +var validMnHfSignalPayloadJSON = { + version: 1, + signal: { + versionBit: 10, + quorumHash: '00000000000000107c98e94bdb9ffb729fe9c190a6d1223fd9b6700ccb79b627', + sig: 'a12bc6a3d43e76fd6ab5d48dff11998811747bf51ffe722d9fda93ae892e4b18a716f58045c86459d0dafd38ae1f7f520519002983fc307e92fa606c3eb5ac8cf6ca03a102889866d58c9207b483e0b975baee63c1202209293ff7393222f812' + } +}; + +// Contains same data as JSON above +var validMnHfSignalPayload = MnHfSignalPayload.fromJSON(validMnHfSignalPayloadJSON); +var validMnHfSignalPayloadBuffer = validMnHfSignalPayload.toBuffer(); +var validMnHfSignalPayloadHexString = validMnHfSignalPayloadBuffer.toString('hex'); + +describe('MnHfSignalPayload', function () { + describe('.fromBuffer', function () { + beforeEach(function () { + sinon.spy(MnHfSignalPayload.prototype, 'validate'); + }); + + afterEach(function () { + MnHfSignalPayload.prototype.validate.restore(); + }); + + it('Should return instance of MnHfSignalPayload and call #validate on it', function () { + var payload = MnHfSignalPayload.fromBuffer(validMnHfSignalPayloadBuffer); + + expect(payload).to.be.an.instanceOf(MnHfSignalPayload); + expect(payload.version).to.be.equal(1); + expect(payload.signal.versionBit).to.be.equal(10); + expect(payload.signal.quorumHash).to.be.equal('00000000000000107c98e94bdb9ffb729fe9c190a6d1223fd9b6700ccb79b627'); + expect(payload.signal.sig).to.be.equal(validMnHfSignalPayloadJSON.signal.sig); + expect(payload.validate.callCount).to.be.equal(1); + }); + + it('Should throw in case if there is some unexpected information in raw payload', function () { + var payloadWithAdditionalZeros = Buffer.from( + validMnHfSignalPayloadHexString + '0000', + 'hex' + ); + + expect(function () { + MnHfSignalPayload.fromBuffer(payloadWithAdditionalZeros); + }).to.throw( + 'Failed to parse payload: raw payload is bigger than expected.' + ); + }); + }); + + describe('.fromJSON', function () { + before(function () { + sinon.spy(MnHfSignalPayload.prototype, 'validate'); + }); + + it('Should return instance of MnHfSignalPayload and call #validate on it', function () { + var payload = MnHfSignalPayload.fromJSON(validMnHfSignalPayloadJSON); + + expect(payload).to.be.an.instanceOf(MnHfSignalPayload); + expect(payload.version).to.be.equal(1); + expect(payload.signal.versionBit).to.be.equal(10); + expect(payload.signal.quorumHash).to.be.equal('00000000000000107c98e94bdb9ffb729fe9c190a6d1223fd9b6700ccb79b627'); + expect(payload.signal.sig).to.be.equal(validMnHfSignalPayloadJSON.signal.sig); + }); + + after(function () { + MnHfSignalPayload.prototype.validate.restore(); + }); + }); + + describe('#validate', function () { + it('Should allow only unsigned integer as version', function () { + var payload = validMnHfSignalPayload.copy(); + + payload.version = -1; + + expect(function () { + payload.validate(); + }).to.throw('Invalid Argument: Expect version to be an unsigned integer'); + + payload.version = 1.5; + + expect(function () { + payload.validate(); + }).to.throw('Invalid Argument: Expect version to be an unsigned integer'); + + payload.version = '12'; + + expect(function () { + payload.validate(); + }).to.throw('Invalid Argument: Expect version to be an unsigned integer'); + + payload.version = Buffer.from('0a0f', 'hex'); + + expect(function () { + payload.validate(); + }).to.throw('Invalid Argument: Expect version to be an unsigned integer'); + + payload.version = 123; + + expect(function () { + payload.validate(); + }).not.to.throw; + }); + + it('Should validate signal fields correctly', function () { + var payload = validMnHfSignalPayload.copy(); + + // Invalid versionBit + payload.signal.versionBit = 256; + + expect(function () { + payload.validate(); + }).to.throw('Invalid Argument: Expect signal.versionBit to be an unsigned 8-bit integer'); + + payload.signal.versionBit = 10; + + // Invalid quorumHash + payload.signal.quorumHash = 'invalidhash'; + + expect(function () { + payload.validate(); + }).to.throw('Invalid Argument: Expect signal.quorumHash to be a valid 32-byte SHA256 hex string'); + + payload.signal.quorumHash = validMnHfSignalPayloadJSON.signal.quorumHash; + + // Invalid sig + payload.signal.sig = 'invalidsig'; + + expect(function () { + payload.validate(); + }).to.throw('Invalid Argument: Expect signal.sig to be a valid 96-byte hex string'); + + payload.signal.sig = validMnHfSignalPayloadJSON.signal.sig; + + expect(function () { + payload.validate(); + }).not.to.throw; + }); + }); + + describe('#toJSON', function () { + beforeEach(function () { + sinon.spy(MnHfSignalPayload.prototype, 'validate'); + }); + + afterEach(function () { + MnHfSignalPayload.prototype.validate.restore(); + }); + + it('Should serialize payload to JSON', function () { + var payload = validMnHfSignalPayload.copy(); + + var payloadJSON = payload.toJSON(); + + expect(payloadJSON.version).to.be.equal(payload.version); + expect(payloadJSON.signal).to.deep.equal(payload.signal); + }); + + it('Should call #validate', function () { + var payload = MnHfSignalPayload.fromJSON(validMnHfSignalPayloadJSON); + MnHfSignalPayload.prototype.validate.resetHistory(); + payload.toJSON(); + expect(payload.validate.callCount).to.be.equal(1); + }); + }); + + describe('#toBuffer', function () { + beforeEach(function () { + sinon.spy(MnHfSignalPayload.prototype, 'validate'); + }); + + afterEach(function () { + MnHfSignalPayload.prototype.validate.restore(); + }); + + it('Should serialize payload to Buffer', function () { + var payload = validMnHfSignalPayload.copy(); + + var serializedPayload = payload.toBuffer(); + var restoredPayload = MnHfSignalPayload.fromBuffer(serializedPayload); + + expect(restoredPayload.version).to.be.equal(payload.version); + expect(restoredPayload.signal).to.deep.equal(payload.signal); + }); + + it('Should call #validate', function () { + var payload = MnHfSignalPayload.fromJSON(validMnHfSignalPayloadJSON); + MnHfSignalPayload.prototype.validate.resetHistory(); + payload.toBuffer(); + expect(payload.validate.callCount).to.be.equal(1); + }); + }); +}); diff --git a/typings/transaction/payload/MnHfSignalPayload.d.ts b/typings/transaction/payload/MnHfSignalPayload.d.ts new file mode 100644 index 000000000..e35f249e6 --- /dev/null +++ b/typings/transaction/payload/MnHfSignalPayload.d.ts @@ -0,0 +1,73 @@ +/** + * @typedef {Object} MnHfSignalSignalJSON + * @property {number} versionBit - The version bit associated with the hard fork. + * @property {string} quorumHash - Hash of the quorum signing this message. + * @property {string} sig - BLS signature on the version bit by the public key associated with the quorum. + */ +export type MnHfSignalSignalJSON = { + versionBit: number; + quorumHash: string; + sig: string; +}; + +/** + * @typedef {Object} MnHfSignalPayloadJSON + * @property {number} version - The version number of the transaction. + * @property {MnHfSignalSignalJSON} signal - Signal data containing version bit, quorum hash, and signature. + */ +export type MnHfSignalPayloadJSON = { + version: number; + signal: MnHfSignalSignalJSON; +}; + +/** + * @class MnHfSignalPayload + * @property {number} version - The version number of the transaction. + * @property {MnHfSignalSignal} signal - Signal data containing version bit, quorum hash, and signature. + */ +export class MnHfSignalPayload { + /** + * Parse raw payload buffer. + * @param {Buffer} rawPayload - The raw payload buffer. + * @return {MnHfSignalPayload} - Parsed MnHfSignalPayload instance. + */ + static fromBuffer(rawPayload: Buffer): MnHfSignalPayload; + + /** + * Create a new instance of the payload from JSON. + * @param {string | MnHfSignalPayloadJSON} payloadJson - The JSON object or string representing the payload. + * @return {MnHfSignalPayload} - Parsed MnHfSignalPayload instance. + */ + static fromJSON(payloadJson: string | MnHfSignalPayloadJSON): MnHfSignalPayload; + + /** + * Validates the payload data. + * @return {boolean} - Whether the payload is valid. + */ + validate(): boolean; + + /** + * Serializes the payload to JSON. + * @return {MnHfSignalPayloadJSON} - Serialized JSON representation of the payload. + */ + toJSON(): MnHfSignalPayloadJSON; + + /** + * Serializes the payload to a buffer. + * @return {Buffer} - The buffer representation of the payload. + */ + toBuffer(): Buffer; + + /** + * Create a copy of the payload instance. + * @return {MnHfSignalPayload} - A new copy of the payload. + */ + copy(): MnHfSignalPayload; + + version: number; + signal: { + versionBit: number; + quorumHash: string; + sig: string; + }; +} From 89ef601d6bc9ba97cec4998c36bc98a3085c3f0b Mon Sep 17 00:00:00 2001 From: thephez Date: Wed, 11 Sep 2024 15:13:19 -0400 Subject: [PATCH 3/3] feat: test using an actual tx from mainnet --- lib/transaction/payload/mnhfsignalpayload.js | 21 ++++++++------- test/transaction/payload/mnhfsignalpayload.js | 27 +++++++++++++++++++ 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/lib/transaction/payload/mnhfsignalpayload.js b/lib/transaction/payload/mnhfsignalpayload.js index f83aee27f..07aa63e9b 100644 --- a/lib/transaction/payload/mnhfsignalpayload.js +++ b/lib/transaction/payload/mnhfsignalpayload.js @@ -55,13 +55,14 @@ MnHfSignalPayload.fromBuffer = function (rawPayload) { payload.version = payloadBufferReader.readUInt8(); payload.signal.versionBit = payloadBufferReader.readUInt8(); - payload.signal.quorumHash = payloadBufferReader.read(32).reverse().toString('hex'); + + // Reverse the quorumHash to correct the byte order (from little-endian to big-endian) + payload.signal.quorumHash = payloadBufferReader.read(32).toString('hex'); + payload.signal.sig = payloadBufferReader.read(96).toString('hex'); if (!payloadBufferReader.finished()) { - throw new Error( - 'Failed to parse payload: raw payload is bigger than expected.' - ); + throw new Error('Failed to parse payload: raw payload is bigger than expected.'); } payload.validate(); @@ -143,11 +144,13 @@ MnHfSignalPayload.prototype.toBuffer = function toBuffer() { this.validate(); var payloadBufferWriter = new BufferWriter(); - payloadBufferWriter - .writeUInt8(this.version) - .writeUInt8(this.signal.versionBit) - .write(Buffer.from(this.signal.quorumHash, 'hex').reverse()) - .write(Buffer.from(this.signal.sig, 'hex')); + payloadBufferWriter.writeUInt8(this.version); + payloadBufferWriter.writeUInt8(this.signal.versionBit); + + // Reverse the quorumHash to write in little-endian format + payloadBufferWriter.write(Buffer.from(this.signal.quorumHash, 'hex')); + + payloadBufferWriter.write(Buffer.from(this.signal.sig, 'hex')); return payloadBufferWriter.toBuffer(); }; diff --git a/test/transaction/payload/mnhfsignalpayload.js b/test/transaction/payload/mnhfsignalpayload.js index b97380007..5db4db10a 100644 --- a/test/transaction/payload/mnhfsignalpayload.js +++ b/test/transaction/payload/mnhfsignalpayload.js @@ -8,6 +8,7 @@ var DashcoreLib = require('../../../index'); var MnHfSignalPayload = DashcoreLib.Transaction.Payload.MnHfSignalPayload; +// Valid test data based on an actual MnHfSignal var validMnHfSignalPayloadJSON = { version: 1, signal: { @@ -22,7 +23,12 @@ var validMnHfSignalPayload = MnHfSignalPayload.fromJSON(validMnHfSignalPayloadJS var validMnHfSignalPayloadBuffer = validMnHfSignalPayload.toBuffer(); var validMnHfSignalPayloadHexString = validMnHfSignalPayloadBuffer.toString('hex'); +// An example of an actual MnHfSignal in hex format +var mainnetMnHfSignalHex = '010a00000000000000107c98e94bdb9ffb729fe9c190a6d1223fd9b6700ccb79b627a12bc6a3d43e76fd6ab5d48dff11998811747bf51ffe722d9fda93ae892e4b18a716f58045c86459d0dafd38ae1f7f520519002983fc307e92fa606c3eb5ac8cf6ca03a102889866d58c9207b483e0b975baee63c1202209293ff7393222f812'; +var mainnetMnHfSignalBuffer = Buffer.from(mainnetMnHfSignalHex, 'hex'); + describe('MnHfSignalPayload', function () { + describe('.fromBuffer', function () { beforeEach(function () { sinon.spy(MnHfSignalPayload.prototype, 'validate'); @@ -200,4 +206,25 @@ describe('MnHfSignalPayload', function () { expect(payload.validate.callCount).to.be.equal(1); }); }); + + describe('Actual MnHfSignalHex parsing', function () { + it('Should parse an actual MnHfSignal (hex) into MnHfSignalPayload', function () { + var payload = MnHfSignalPayload.fromBuffer(mainnetMnHfSignalBuffer); + + expect(payload).to.be.an.instanceOf(MnHfSignalPayload); + expect(payload.version).to.be.equal(1); + expect(payload.signal.versionBit).to.be.equal(10); + expect(payload.signal.quorumHash).to.be.equal('00000000000000107c98e94bdb9ffb729fe9c190a6d1223fd9b6700ccb79b627'); + expect(payload.signal.sig).to.be.equal(validMnHfSignalPayloadJSON.signal.sig); + }); + + it('Should serialize MnHfSignalPayload back to hex and match the original MnHfSignal hex', function () { + var payload = MnHfSignalPayload.fromBuffer(mainnetMnHfSignalBuffer); + + var serializedBuffer = payload.toBuffer(); + var serializedHex = serializedBuffer.toString('hex'); + + expect(serializedHex).to.be.equal(mainnetMnHfSignalHex); + }); + }); });