UNPKG

@dashevo/dashcore-lib

Version:

A pure and powerful JavaScript Dash library.

359 lines (325 loc) 12.8 kB
/* 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;