@signumjs/core
Version:
Principal package with functions and models for building Signum Network applications.
336 lines • 13.4 kB
JavaScript
"use strict";
/** @ignore */
/** @internal */
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.rebuildTransactionPostData = rebuildTransactionPostData;
const bignumber_js_1 = __importDefault(require("bignumber.js"));
const byteBuffer_1 = __importDefault(require("./byteBuffer"));
const getAttachmentFields_1 = require("./getAttachmentFields");
const getRequestRebuildInfo_1 = require("./getRequestRebuildInfo");
/**
* Try to rebuild the form data based on unsignedBytes in a response.
* @param hexUnsignedBytes string with unsignedBytes
* @returns Any object, expected to match the form data
* @throws Error on failure
* @internal
*/
function rebuildTransactionPostData(hexUnsignedBytes) {
const trBytes = new byteBuffer_1.default(hexUnsignedBytes);
const transaction = parseBaseTransaction(trBytes);
let rebuiltData = {};
rebuiltData.feeNQT = transaction.feeNQT;
rebuiltData.publicKey = transaction.senderPublicKey;
rebuiltData.deadline = transaction.deadline;
if (transaction.amountNQT) {
rebuiltData.amountNQT = transaction.amountNQT;
}
if (transaction.recipient) {
rebuiltData.recipient = transaction.recipient;
}
if (transaction.referencedTransactionFullHash) {
rebuiltData['referencedTransactionFullHash'] = transaction.referencedTransactionFullHash;
}
const requestRebuildInfo = (0, getRequestRebuildInfo_1.getRequestRebuildInfo)(transaction);
if (requestRebuildInfo.hasAttachment) {
rebuiltData = parseAttachment(requestRebuildInfo.requestType, rebuiltData, trBytes);
}
rebuiltData = processSpecialCases(requestRebuildInfo.requestType, rebuiltData);
rebuiltData = parseMessage(transaction.flags, rebuiltData, trBytes);
rebuiltData = parseEncryptedMessage(transaction.flags, rebuiltData, trBytes);
rebuiltData = parseRecipientPublicKey(transaction.flags, rebuiltData, trBytes);
rebuiltData = parseEncryptToSelfMessage(transaction.flags, rebuiltData, trBytes);
return {
requestType: requestRebuildInfo.requestType,
rebuiltData,
};
}
/**
* If needed, add here special cases (exceptions) to make further modifications
* on the rebuiltData properties.
* @return the updated object.
*/
function processSpecialCases(requestType, rebuiltData) {
switch (requestType) {
case 'sendMoney':
case 'sendMoneySubscription':
case 'transferAsset':
case 'transferAssetMulti':
// Fixes burning transactions
if (!rebuiltData.recipient) {
rebuiltData.recipient = '0';
}
break;
case 'sendMoneyMultiSame':
rebuiltData.amountNQT = new bignumber_js_1.default(rebuiltData.amountNQT)
.dividedBy(rebuiltData.recipients.split(';').length)
.toFixed(0);
break;
case 'issueAsset':
if (rebuiltData.mintable === '1') {
rebuiltData.mintable = 'true';
if (!rebuiltData.quantityQNT) { // when initial supply is '0'
rebuiltData.quantityQNT = '0';
}
}
else {
rebuiltData.mintable = 'false';
}
if (!rebuiltData.decimals) {
rebuiltData.decimals = '0';
}
break;
case 'createATProgram':
delete rebuiltData.creationBytes;
if (rebuiltData.referencedTransactionFullHash) {
delete rebuiltData.code;
if (rebuiltData.data === '') {
delete rebuiltData.data;
}
delete rebuiltData.dpages;
delete rebuiltData.cspages;
delete rebuiltData.uspages;
delete rebuiltData.minActivationAmountNQT;
}
}
return rebuiltData;
}
/**
* From a given trByteBuffer, parse the transaction fields common for all
* kinds of transactions. Returns an incomplete Transaction object
*/
function parseBaseTransaction(trByteBuffer) {
const transactionJSON = {};
transactionJSON.type = trByteBuffer.readByte();
const subtypeAndVersion = trByteBuffer.readByte();
transactionJSON.subtype = subtypeAndVersion & 0x0F;
transactionJSON.version = (subtypeAndVersion & 0xF0) >> 4;
if (transactionJSON.version < 2) {
throw new Error('Unsupported transaction version.');
}
transactionJSON.timestamp = trByteBuffer.readInt();
transactionJSON.deadline = trByteBuffer.readShort();
transactionJSON.senderPublicKey = trByteBuffer.readHexString(32);
transactionJSON.recipient = trByteBuffer.readLong().toString();
if (transactionJSON.recipient === '0') {
delete transactionJSON.recipient;
}
transactionJSON.amountNQT = trByteBuffer.readLong().toString();
if (transactionJSON.amountNQT === '0') {
delete transactionJSON.amountNQT;
}
transactionJSON.feeNQT = trByteBuffer.readLong().toString();
transactionJSON.referencedTransactionFullHash = trByteBuffer.readHexString(32);
if (/^0+$/.test(transactionJSON.referencedTransactionFullHash)) {
delete transactionJSON.referencedTransactionFullHash;
}
transactionJSON.signature = trByteBuffer.readHexString(64);
if (/^0+$/.test(transactionJSON.signature)) {
delete transactionJSON.signature;
}
transactionJSON.flags = trByteBuffer.readInt();
transactionJSON.ecBlockHeight = trByteBuffer.readInt();
transactionJSON.ecBlockId = trByteBuffer.readLong().toString();
transactionJSON.cashBackId = trByteBuffer.readLong().toString();
return transactionJSON;
}
/**
* Updates the incoming 'data' object (rebuiltData) accordingly the 'requestType'
* and bytes from 'trBytes'.
* @return the updated object.
* @throw Error on failure
*/
function parseAttachment(requestType, data, trBytes) {
const attachmentVersion = trBytes.readByte();
const fields = (0, getAttachmentFields_1.getAttachmentFields)(attachmentVersion, requestType);
const pastValues = [];
let sizeOfString;
for (const item of fields) {
const itemType = item.type.split('*');
const typeSpec = itemType[0];
const repetitionSpec = itemType[1];
let repetition;
if (repetitionSpec.startsWith('$')) {
// variable repetition, depending on previous element
const repVal = parseInt(repetitionSpec.substring(1), 10);
repetition = parseInt(pastValues[repVal], 10);
}
else {
// fixed repetition
repetition = parseInt(repetitionSpec, 10);
}
const currentValues = [];
for (let amount = 0; amount < repetition; amount++) {
switch (typeSpec) {
case 'ByteString':
sizeOfString = trBytes.readByte();
currentValues.push(trBytes.readString(sizeOfString));
break;
case 'ShortString':
sizeOfString = trBytes.readShort();
currentValues.push(trBytes.readString(sizeOfString));
break;
case 'Long:Long':
currentValues.push(trBytes.readLong().toString() +
':' +
trBytes.readLong().toString());
break;
case 'Long':
currentValues.push(trBytes.readLong().toString());
break;
case 'Int':
currentValues.push(trBytes.readInt().toString());
break;
case 'Short':
currentValues.push(trBytes.readShort().toString());
break;
case 'Byte':
currentValues.push(trBytes.readByte().toString());
break;
case 'Delete':
break;
case 'CreationBytes':
Object.assign(data, parseCreationBytes(trBytes));
break;
default:
throw new Error('Internal error');
}
}
if (item.parameterName && (currentValues.length !== 1 || currentValues[0] !== '0')) {
// Only add property if it is not zero!
data[item.parameterName] = currentValues.join(';');
}
if (typeSpec === 'Delete') {
delete data[item.parameterName];
}
pastValues.push(currentValues.toString());
}
return data;
}
function parseCreationBytes(trBytes) {
const initialPos = trBytes.position();
const version = trBytes.readShort();
if (version !== 3 && version !== 2) {
throw new Error('Incorrect version for creationBytes');
}
const _reserved = trBytes.readShort();
const codePages = trBytes.readShort();
const dpages = trBytes.readShort();
const cspages = trBytes.readShort();
const uspages = trBytes.readShort();
const minActivationAmountNQT = trBytes.readLong().toString();
let codeLen = (codePages <= 1) ? trBytes.readByte() : trBytes.readShort();
if (codeLen === 0 && codePages === 1 && version > 2) {
codeLen = 256;
}
const code = trBytes.readHexString(codeLen);
let dataLen = (dpages <= 1) ? trBytes.readByte() : trBytes.readShort();
if (dataLen === 0 && dpages === 1 && trBytes.length() - trBytes.position() === 256 && version > 2) {
// Note: Could not work if there are attachments: message, messageToEncrypt
// encryptedMessageData, messageToEncryptToSelf or encryptToSelfMessageData.
// The problem is that creationBytes has no size information.
// This is not a problem in node because the attachments with flags are stored
// in different fields in the database, so the creationBytes size can be calculated.
dataLen = 256;
}
const data = trBytes.readHexString(dataLen);
const creationBytesLen = trBytes.position() - initialPos;
trBytes.position(initialPos);
const creationBytes = trBytes.readHexString(creationBytesLen);
const retObj = {
creationBytes,
code,
data,
dpages: dpages.toString(),
cspages: cspages.toString(),
uspages: uspages.toString(),
minActivationAmountNQT
};
if (retObj.code === '') {
delete retObj.code;
}
if (retObj.data === '') {
delete retObj.data;
}
if (retObj.dpages === '0') {
delete retObj.dpages;
}
if (retObj.cspages === '0') {
delete retObj.cspages;
}
if (retObj.uspages === '0') {
delete retObj.uspages;
}
return retObj;
}
function parseMessage(transactionFlags, data, trBytes) {
// flag for non-encrypted message
const flagBit = 0b1;
if ((transactionFlags & flagBit) === 0) {
return data;
}
const attachmentVersion = trBytes.readByte();
if (attachmentVersion !== 1) {
throw new Error(`Unsupported 'message' flag.`);
}
const lengthBytes = trBytes.readInt();
const messageIsText = (lengthBytes & 0x80000000) !== 0;
const messageLength = (lengthBytes & 0x7fffffff);
if (messageIsText) {
data.message = trBytes.readString(messageLength);
}
else {
data.message = trBytes.readHexString(messageLength);
}
data.messageIsText = messageIsText ? 'true' : 'false';
return data;
}
function parseEncryptedMessage(transactionFlags, data, trBytes) {
// flag for encrypted note
const flagBit = 0b10;
if ((transactionFlags & flagBit) === 0) {
return data;
}
const attachmentVersion = trBytes.readByte();
if (attachmentVersion !== 1) {
throw new Error(`Unsupported 'EncryptedMessage' flag.`);
}
const lengthBytes = trBytes.readInt();
data.messageToEncryptIsText = (lengthBytes & 0x80000000) ? 'true' : 'false';
const messageLength = (lengthBytes & 0x7fffffff);
data.encryptedMessageData = trBytes.readHexString(messageLength);
data.encryptedMessageNonce = trBytes.readHexString(32);
return data;
}
function parseRecipientPublicKey(transactionFlags, data, trBytes) {
// flag for encrypted note
const flagBit = 0b100;
if ((transactionFlags & flagBit) === 0) {
return data;
}
const attachmentVersion = trBytes.readByte();
if (attachmentVersion !== 1) {
throw new Error(`Unsupported 'RecipientPublicKey' flag.`);
}
data.recipientPublicKey = trBytes.readHexString(32);
return data;
}
function parseEncryptToSelfMessage(transactionFlags, data, trBytes) {
const flagBit = 0b1000;
if ((transactionFlags & flagBit) === 0) {
return data;
}
const attachmentVersion = trBytes.readByte();
if (attachmentVersion !== 1) {
throw new Error(`Unsupported 'EncryptedMessage' flag.`);
}
const lengthBytes = trBytes.readInt();
data.messageToEncryptToSelfIsText = (lengthBytes & 0x80000000) ? 'true' : 'false';
const messageLength = (lengthBytes & 0x7fffffff);
data.encryptToSelfMessageData = trBytes.readHexString(messageLength);
data.encryptToSelfMessageNonce = trBytes.readHexString(32);
return data;
}
//# sourceMappingURL=rebuildTransactionPostData.js.map