@dashevo/dashcore-lib
Version:
A pure and powerful JavaScript Dash library.
359 lines (325 loc) • 12.8 kB
JavaScript
/* eslint-disable */
// TODO: Remove previous line and work through linting issues at next edit
var utils = require('../../util/js');
var constants = require('../../constants');
var Preconditions = require('../../util/preconditions');
var BufferWriter = require('../../encoding/bufferwriter');
var BufferReader = require('../../encoding/bufferreader');
var AbstractPayload = require('./abstractpayload');
var Script = require('../../script');
var ipUtil = require('../../util/ip');
const { PLATFORM_NODE_ID_SIZE,
MASTERNODE_TYPE_HP
} = require("../../constants");
var CURRENT_PAYLOAD_VERSION = 2;
/**
* @typedef {Object} ProRegTxPayloadJSON
* @property {number} version uint_16 2 Provider transaction version number. Currently set to 1.
* @property {string} collateralHash
* @property {number} collateralIndex uint_32 4 The collateral index.
* @property {string} service - service address, ip and port
* @property {string} keyIDOwner CKeyID 20 The public key hash used for owner related signing (ProTx updates, governance voting)
* @property {string} pubKeyOperator BLSPubKey 48 The public key used for operational related signing (network messages, ProTx updates)
* @property {string} keyIDVoting CKeyID 20 The public key hash used for voting.
* @property {number} operatorReward uint_16 2 A value from 0 to 10000.
* @property {number} operatorReward uint_16 2 A value from 0 to 10000.
* @property {string} payoutAddress
* @property {string} inputsHash uint256 32 Hash of all the outpoints of the transaction inputs
* @property {string} [platformNodeID] Platform Node ID
* @property {number} [platformP2PPort] Platform P2P port
* @property {number} [platformHTTPPort] Platform HTTP port
* @property {number} [payloadSigSize] Size of the Signature
* @property {string} [payloadSig] Signature of the hash of the ProTx fields. Signed with keyIDOwner
*/
/**
* @class ProRegTxPayload
* @property {number} version uint_16 2 Provider transaction version number. Currently set to 1.
* @property {number} type
* @property {number} mode
* @property {string} collateralHash
* @property {number} collateralIndex uint_32 4 The collateral index.
* @property {string} service - service address, ip and port
* @property {string} keyIDOwner CKeyID 20 The public key hash used for owner related signing (ProTx updates, governance voting)
* @property {string} pubKeyOperator BLSPubKey 48 The public key used for operational related signing (network messages, ProTx updates)
* @property {string} keyIDVoting CKeyID 20 The public key hash used for voting.
* @property {number} operatorReward uint_16 2 A value from 0 to 10000.
* @property {string} scriptPayout Script Variable Payee script (p2pkh/p2sh)
* @property {string} inputsHash uint256 32 Hash of all the outpoints of the transaction inputs
* @property {string} [platformNodeID] Platform Node ID
* @property {number} [platformP2PPort] Platform P2P port
* @property {number} [platformHTTPPort] Platform HTTP port
* @property {number} [payloadSigSize] Size of the Signature
* @property {string} [payloadSig] Signature of the hash of the ProTx fields. Signed with keyIDOwner
*/
function ProRegTxPayload(options) {
AbstractPayload.call(this);
this.version = CURRENT_PAYLOAD_VERSION;
if (options) {
this.type = options.type;
this.mode = options.mode;
this.collateralHash = options.collateralHash;
this.collateralIndex = options.collateralIndex;
this.service = options.service;
this.keyIDOwner = options.keyIDOwner;
this.pubKeyOperator = options.pubKeyOperator;
this.keyIDVoting = options.keyIDVoting;
this.operatorReward = options.operatorReward;
this.scriptPayout = Script.fromAddress(options.payoutAddress).toHex();
this.inputsHash = options.inputsHash;
this.platformNodeID = options.platformNodeID;
this.platformP2PPort = options.platformP2PPort;
this.platformHTTPPort = options.platformHTTPPort;
this.payloadSig = options.payloadSig;
this.payloadSigSize = this.payloadSig
? Buffer.from(this.payloadSig, 'hex').length
: 0;
}
}
ProRegTxPayload.prototype = Object.create(AbstractPayload.prototype);
ProRegTxPayload.prototype.constructor = AbstractPayload;
/* Static methods */
/**
* Parse raw payload
* @param {Buffer} rawPayload
* @return {ProRegTxPayload}
*/
ProRegTxPayload.fromBuffer = function fromBuffer(rawPayload) {
var payloadBufferReader = new BufferReader(rawPayload);
var payload = new ProRegTxPayload();
payload.version = payloadBufferReader.readUInt16LE();
payload.type = payloadBufferReader.readUInt16LE();
payload.mode = payloadBufferReader.readUInt16LE();
payload.collateralHash = payloadBufferReader
.read(constants.SHA256_HASH_SIZE)
.reverse()
.toString('hex');
payload.collateralIndex = payloadBufferReader.readUInt32LE();
payload.service = ipUtil.bufferToIPAndPort(
payloadBufferReader.read(ipUtil.IP_AND_PORT_SIZE)
);
payload.keyIDOwner = payloadBufferReader
.read(constants.PUBKEY_ID_SIZE)
.reverse()
.toString('hex');
payload.pubKeyOperator = payloadBufferReader
.read(constants.BLS_PUBLIC_KEY_SIZE)
.toString('hex');
payload.keyIDVoting = payloadBufferReader
.read(constants.PUBKEY_ID_SIZE)
.reverse()
.toString('hex');
payload.operatorReward = payloadBufferReader. readUInt16LE();
var scriptPayoutSize = payloadBufferReader.readVarintNum();
payload.scriptPayout = payloadBufferReader
.read(scriptPayoutSize)
.toString('hex');
payload.inputsHash = payloadBufferReader
.read(constants.SHA256_HASH_SIZE)
.reverse()
.toString('hex');
if (payload.version >= 2 && payload.type === MASTERNODE_TYPE_HP) {
payload.platformNodeID = payloadBufferReader.read(PLATFORM_NODE_ID_SIZE).reverse().toString('hex');
payload.platformP2PPort = payloadBufferReader.readUInt16LE();
payload.platformHTTPPort = payloadBufferReader.readUInt16LE();
}
payload.payloadSigSize = payloadBufferReader.readVarintNum();
if (payload.payloadSigSize > 0) {
payload.payloadSig = payloadBufferReader
.read(payload.payloadSigSize)
.toString('hex');
}
if (!payloadBufferReader.finished()) {
throw new Error(
'Failed to parse payload: raw payload is bigger than expected.'
);
}
return payload;
};
/**
* Create new instance of payload from JSON
* @param {string|ProRegTxPayloadJSON} payloadJson
* @return {ProRegTxPayload}
*/
ProRegTxPayload.fromJSON = function fromJSON(payloadJson) {
var payload = new ProRegTxPayload(payloadJson);
payload.validate();
return payload;
};
/* Instance methods */
/**
* Validate payload
* @return {boolean}
*/
ProRegTxPayload.prototype.validate = function () {
Preconditions.checkArgument(
utils.isUnsignedInteger(this.version),
'Expect version to be an unsigned integer'
);
Preconditions.checkArgumentType(
this.collateralIndex,
'number',
'collateralIndex'
);
Preconditions.checkArgument(
utils.isSha256HexString(this.collateralHash),
'Expect collateralHash to be a hex string representing sha256 hash'
);
if (!ipUtil.isZeroAddress(this.service)) {
Preconditions.checkArgument(
ipUtil.isIPV4(this.service),
'Expected service to be a string with ip address and port'
);
}
Preconditions.checkArgument(
utils.isHexaString(this.keyIDOwner),
'Expect keyIDOwner to be a hex string'
);
Preconditions.checkArgument(
utils.isHexaString(this.pubKeyOperator),
'Expect pubKeyOperator to be a hex string'
);
Preconditions.checkArgument(
utils.isHexaString(this.keyIDVoting),
'Expect keyIDVoting to be a hex string'
);
Preconditions.checkArgument(
this.keyIDOwner.length === constants.PUBKEY_ID_SIZE * 2,
'Expect keyIDOwner to be 20 bytes in size '
);
Preconditions.checkArgument(
this.pubKeyOperator.length === constants.BLS_PUBLIC_KEY_SIZE * 2,
'Expect keyIDOwner to be 48 bytes in size '
);
Preconditions.checkArgument(
this.keyIDVoting.length === constants.PUBKEY_ID_SIZE * 2,
'Expect keyIDOwner to be 20 bytes in size '
);
Preconditions.checkArgumentType(
this.operatorReward,
'number',
'operatorReward'
);
Preconditions.checkArgument(
this.operatorReward <= 10000,
'Expect operatorReward to be lesser than or equal 10000'
);
Preconditions.checkArgument(
utils.isHexaString(this.inputsHash),
'Expect inputsHash to be a hex string'
);
if (this.version >= 2 && this.type === MASTERNODE_TYPE_HP) {
Preconditions.checkArgument(
utils.isHexaString(this.platformNodeID),
'Expect platformNodeID to be a hex string'
);
Preconditions.checkArgument(
utils.isUnsignedInteger(this.platformP2PPort),
'Expect platformP2PPort to be an integer'
);
Preconditions.checkArgument(
utils.isUnsignedInteger(this.platformHTTPPort),
'Expect platformHTTPPort to be an integer'
);
}
if (this.scriptPayout) {
var script = new Script(this.scriptPayout);
Preconditions.checkArgument(
script.isPublicKeyHashOut() || script.isScriptHashOut(),
'Expected scriptOperatorPayout to be a p2pkh/p2sh'
);
}
if (Boolean(this.payloadSig)) {
Preconditions.checkArgumentType(
this.payloadSigSize,
'number',
'payloadSigSize'
);
Preconditions.checkArgument(
utils.isUnsignedInteger(this.payloadSigSize),
'Expect payloadSigSize to be an unsigned integer'
);
Preconditions.checkArgument(
utils.isHexaString(this.payloadSig),
'Expect payload sig to be a hex string'
);
}
};
/**
* Serializes payload to JSON
* @param [options]
* @param [options.skipSignature]
* @param [options.network] - network for address serialization
* @return {ProRegTxPayloadJSON}
*/
ProRegTxPayload.prototype.toJSON = function toJSON(options) {
var noSignature = !Boolean(this.payloadSig);
var skipSignature = noSignature || (options && options.skipSignature);
var network = options && options.network;
this.validate();
var payloadJSON = {
version: this.version,
collateralHash: this.collateralHash,
collateralIndex: this.collateralIndex,
service: this.service,
keyIDOwner: this.keyIDOwner,
pubKeyOperator: this.pubKeyOperator,
keyIDVoting: this.keyIDVoting,
operatorReward: this.operatorReward,
payoutAddress: new Script(this.scriptPayout).toAddress(network).toString(),
inputsHash: this.inputsHash,
};
if (this.version >= 2 && this.type === MASTERNODE_TYPE_HP) {
payloadJSON.platformNodeID = this.platformNodeID;
payloadJSON.platformP2PPort = this.platformP2PPort;
payloadJSON.platformHTTPPort = this.platformHTTPPort;
}
if (!skipSignature) {
payloadJSON.payloadSigSize = this.payloadSigSize;
payloadJSON.payloadSig = this.payloadSig;
}
return payloadJSON;
};
/**
* Serialize payload to buffer
* @param [options]
* @param {Boolean} [options.skipSignature] - skip signature. Needed for signing
* @return {Buffer}
*/
ProRegTxPayload.prototype.toBuffer = function toBuffer(options) {
var noSignature = !Boolean(this.payloadSig);
var skipSignature = noSignature || (options && options.skipSignature);
this.validate();
var payloadBufferWriter = new BufferWriter();
payloadBufferWriter
.writeUInt16LE(this.version)
.writeUInt16LE(this.type)
.writeUInt16LE(this.mode)
.write(Buffer.from(this.collateralHash, 'hex').reverse())
.writeInt32LE(this.collateralIndex)
.write(ipUtil.ipAndPortToBuffer(this.service))
.write(Buffer.from(this.keyIDOwner, 'hex').reverse())
.write(Buffer.from(this.pubKeyOperator, 'hex'))
.write(Buffer.from(this.keyIDVoting, 'hex').reverse())
.writeUInt16LE(this.operatorReward)
.writeVarintNum(Buffer.from(this.scriptPayout, 'hex').length)
.write(Buffer.from(this.scriptPayout, 'hex'))
.write(Buffer.from(this.inputsHash, 'hex').reverse());
if (this.version >= 2 && this.type === MASTERNODE_TYPE_HP) {
payloadBufferWriter.write(Buffer.from(this.platformNodeID, 'hex').reverse())
.writeUInt16LE(this.platformP2PPort)
.writeUInt16LE(this.platformHTTPPort);
}
if (!skipSignature && this.payloadSig) {
payloadBufferWriter.writeVarintNum(
Buffer.from(this.payloadSig, 'hex').length
);
payloadBufferWriter.write(Buffer.from(this.payloadSig, 'hex'));
} else {
payloadBufferWriter.writeVarintNum(constants.EMPTY_SIGNATURE_SIZE);
}
return payloadBufferWriter.toBuffer();
};
ProRegTxPayload.prototype.copy = function copy() {
return ProRegTxPayload.fromBuffer(this.toBuffer());
};
module.exports = ProRegTxPayload;