Skip to content

Commit

Permalink
feat: masternode hard fork signal transaction payload (#301)
Browse files Browse the repository at this point in the history
* feat: groundwork for mnhfsignal special tx

* feat: add mnhfsignal

* feat: test using an actual tx from mainnet
  • Loading branch information
thephez authored Sep 16, 2024
1 parent bd0b2c6 commit 8b249a1
Show file tree
Hide file tree
Showing 9 changed files with 478 additions and 0 deletions.
1 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
1 change: 1 addition & 0 deletions lib/constants/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand Down
1 change: 1 addition & 0 deletions lib/transaction/payload/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand Down
166 changes: 166 additions & 0 deletions lib/transaction/payload/mnhfsignalpayload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/* 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();

// 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.');
}

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);
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();
};

/**
* Copy payload instance
* @return {MnHfSignalPayload}
*/
MnHfSignalPayload.prototype.copy = function copy() {
return MnHfSignalPayload.fromJSON(this.toJSON());
};

module.exports = MnHfSignalPayload;
3 changes: 3 additions & 0 deletions lib/transaction/payload/payload.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand All @@ -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] =
Expand Down
1 change: 1 addition & 0 deletions test-d/index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import type {
AssetUnlockPayload,
CoinbasePayload,
CommitmentTxPayload,
MnHfSignalPayload,
ProRegTxPayload,
ProUpRegTxPayload,
ProUpRevTxPayload,
Expand Down
Loading

0 comments on commit 8b249a1

Please sign in to comment.