"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Serializer = void 0;
const enums_1 = require("../enums");
const errors_1 = require("../errors");
const identities_1 = require("../identities");
const config_1 = require("../managers/config");
const utils_1 = require("../utils");
const utils_2 = require("../utils");
const types_1 = require("./types");
// Reference: https://github.com/ArkEcosystem/AIPs/blob/master/AIPS/aip-11.md
class Serializer {
    static getBytes(transaction, options = {}) {
        const version = transaction.version || 1;
        if (options.acceptLegacyVersion || options.disableVersionCheck || utils_2.isSupportedTransactionVersion(version)) {
            if (version === 1) {
                return this.getBytesV1(transaction, options);
            }
            return this.serialize(types_1.TransactionTypeFactory.create(transaction), options);
        }
        else {
            throw new errors_1.TransactionVersionError(version);
        }
    }
    /**
     * Serializes the given transaction according to AIP11.
     */
    static serialize(transaction, options = {}) {
        let size = 83886;
        const maxPayload = config_1.configManager.getMilestone(config_1.configManager.getHeight()).block?.maxPayload;
        const maxTransactions = config_1.configManager.getMilestone(config_1.configManager.getHeight()).block?.maxTransactions;
        if (maxPayload && maxTransactions) {
            size = Math.floor(maxPayload / maxTransactions) * 2;
        }
        const buff = new utils_2.ByteBuffer(Buffer.alloc(size));
        this.serializeCommon(transaction.data, buff);
        this.serializeVendorField(transaction, buff);
        const serialized = transaction.serialize(options);
        if (!serialized) {
            throw new Error();
        }
        buff.writeBuffer(serialized.getResult());
        this.serializeSignatures(transaction.data, buff, options);
        const bufferBuffer = buff.getResult();
        transaction.serialized = bufferBuffer;
        return bufferBuffer;
    }
    /**
     * Serializes the given transaction prior to AIP11 (legacy).
     */
    static getBytesV1(transaction, options = {}) {
        let assetSize = 0;
        let assetBytes;
        if (transaction.type === enums_1.TransactionType.SecondSignature && transaction.asset) {
            const { signature } = transaction.asset;
            const bytebuffer = new utils_2.ByteBuffer(Buffer.alloc(33));
            if (signature && signature.publicKey) {
                bytebuffer.writeBuffer(Buffer.from(signature.publicKey, "hex"));
            }
            assetBytes = bytebuffer.getResult();
            assetSize = assetBytes.length;
        }
        if (transaction.type === enums_1.TransactionType.DelegateRegistration &&
            transaction.asset &&
            transaction.asset.delegate) {
            assetBytes = Buffer.from(transaction.asset.delegate.username, "utf8");
            assetSize = assetBytes.length;
        }
        if (transaction.type === enums_1.TransactionType.Vote && transaction.asset && transaction.asset.votes) {
            assetBytes = Buffer.from(transaction.asset.votes.join(""), "utf8");
            assetSize = assetBytes.length;
        }
        if (transaction.type === enums_1.TransactionType.MultiSignature &&
            transaction.asset &&
            transaction.asset.multiSignatureLegacy) {
            const keysgroupBuffer = Buffer.from(transaction.asset.multiSignatureLegacy.keysgroup.join(""), "utf8");
            const bb = new utils_2.ByteBuffer(Buffer.alloc(1 + 1 + keysgroupBuffer.length));
            bb.writeUInt8(transaction.asset.multiSignatureLegacy.min);
            bb.writeUInt8(transaction.asset.multiSignatureLegacy.lifetime);
            for (const byte of keysgroupBuffer) {
                bb.writeUInt8(byte);
            }
            assetBytes = bb.getResult();
            assetSize = assetBytes.length;
        }
        const bb = new utils_2.ByteBuffer(Buffer.alloc(1 + 4 + 32 + 8 + 8 + 21 + 255 + 64 + 64 + 64 + assetSize));
        bb.writeUInt8(transaction.type);
        bb.writeUInt32LE(transaction.timestamp);
        if (transaction.senderPublicKey) {
            bb.writeBuffer(Buffer.from(transaction.senderPublicKey, "hex"));
            // Apply fix for broken type 1 and 4 transactions, which were
            // erroneously calculated with a recipient id.
            const { transactionIdFixTable } = config_1.configManager.get("exceptions");
            const isBrokenTransaction = transactionIdFixTable && Object.values(transactionIdFixTable).includes(transaction.id);
            if (isBrokenTransaction || (transaction.recipientId && transaction.type !== 1 && transaction.type !== 4)) {
                const recipientId = transaction.recipientId || identities_1.Address.fromPublicKey(transaction.senderPublicKey, transaction.network);
                bb.writeBuffer(identities_1.Address.toBuffer(recipientId).addressBuffer);
            }
            else {
                for (let i = 0; i < 21; i++) {
                    bb.writeUInt8(0);
                }
            }
        }
        if (transaction.vendorField) {
            const vf = Buffer.from(transaction.vendorField);
            const fillstart = vf.length;
            bb.writeBuffer(vf);
            for (let i = fillstart; i < 64; i++) {
                bb.writeUInt8(0);
            }
        }
        else {
            for (let i = 0; i < 64; i++) {
                bb.writeUInt8(0);
            }
        }
        bb.writeBigUInt64LE(transaction.amount.toBigInt());
        bb.writeBigUInt64LE(transaction.fee.toBigInt());
        if (assetSize > 0 && assetBytes) {
            for (let i = 0; i < assetSize; i++) {
                bb.writeUInt8(assetBytes[i]);
            }
        }
        if (!options.excludeSignature && transaction.signature) {
            bb.writeBuffer(Buffer.from(transaction.signature, "hex"));
        }
        if (!options.excludeSecondSignature && transaction.secondSignature) {
            bb.writeBuffer(Buffer.from(transaction.secondSignature, "hex"));
        }
        return bb.getResult();
    }
    static serializeCommon(transaction, buff) {
        transaction.version = transaction.version || 0x01;
        if (transaction.typeGroup === undefined) {
            transaction.typeGroup = enums_1.TransactionTypeGroup.Core;
        }
        buff.writeUInt8(0xff);
        buff.writeUInt8(transaction.version);
        buff.writeUInt8(transaction.network || config_1.configManager.get("network.pubKeyHash"));
        if (transaction.version === 1) {
            buff.writeUInt8(transaction.type);
            buff.writeUInt32LE(transaction.timestamp);
        }
        else {
            buff.writeUInt32LE(transaction.typeGroup);
            buff.writeUInt16LE(transaction.type);
            if (transaction.nonce) {
                buff.writeBigInt64LE(transaction.nonce.toBigInt());
            }
        }
        if (transaction.senderPublicKey) {
            buff.writeBuffer(Buffer.from(transaction.senderPublicKey, "hex"));
        }
        buff.writeBigInt64LE(transaction.fee.toBigInt());
    }
    static serializeVendorField(transaction, buff) {
        const { data } = transaction;
        if (transaction.hasVendorField() && data.vendorField) {
            const vf = Buffer.from(data.vendorField, "utf8");
            buff.writeUInt8(vf.length);
            buff.writeBuffer(vf);
        }
        else {
            buff.writeUInt8(0x00);
        }
    }
    static serializeSignatures(transaction, buff, options = {}) {
        if (transaction.signature && !options.excludeSignature) {
            buff.writeBuffer(Buffer.from(transaction.signature, "hex"));
        }
        const secondSignature = transaction.secondSignature || transaction.signSignature;
        if (secondSignature && !options.excludeSecondSignature) {
            buff.writeBuffer(Buffer.from(secondSignature, "hex"));
        }
        if (transaction.signatures) {
            if (transaction.version === 1 && utils_1.isException(transaction)) {
                buff.writeUInt8(0xff); // 0xff separator to signal start of multi-signature transactions
                buff.writeBuffer(Buffer.from(transaction.signatures.join(""), "hex"));
            }
            else if (!options.excludeMultiSignature) {
                buff.writeBuffer(Buffer.from(transaction.signatures.join(""), "hex"));
            }
        }
    }
}
exports.Serializer = Serializer;
//# sourceMappingURL=serializer.js.map