Plu-ts Tx API hash calculation Mismatch

Incorrect Hash for Tx

I have passed all the required data to Tx API and calculated the hash

  • for simple transaction without assets/datum/refScript the Hash returned is correct
  • for transaction with assets/datum/refscript the Hash returned is incorrect

there are two files 1. calculate.ts & 2. utils.ts

import { Value, TxBuilder, defaultProtocolParameters } from "@harmoniclabs/plu-ts";
import { TxBody, TxIn, TxOut, TxWitnessSet, UTxO, Tx, ITxOut } from "@harmoniclabs/cardano-ledger-ts";
import { toPlutsUtxo, toPlutsTxOut, toPlutsTxBody, toPlutsTx, toPlutsTxWitness } from './utils'
import * as WASM_lib from "@emurgo/cardano-serialization-lib-nodejs"


export function validateHash(hash: string, utxo: any, txn): boolean {
    const utxosInputs = utxo.inputs
    const utxosOutputs = utxo.outputs
    
    // separating collateral, reference and inputs
    var utxoInput = [];
    var collateralInput = [];
    var referenceInput = [];
    
    utxosInputs.forEach(function (input) {
        if (input.collateral) {
            collateralInput.push(input);
        } else if (input.reference) {
            referenceInput.push(input);
        } else {
            utxoInput.push(input);
        }
    });

    // separating output and collateral
    var utxoOutput = [];
    var collateralOutput;
    utxosOutputs.forEach(function (output) {
        if (output.collateral) {
            collateralOutput = output;
        } else {
            utxoOutput.push(output);
        }
    });

    // setting up inputs
    const myUTxOInputs: UTxO[] = utxoInput.map(toPlutsUtxo);
    const inputs = myUTxOInputs.map(input => new UTxO(input));
    // let arrayUTxO: [UTxO, ...UTxO[]];
    // if (inputs.length > 0) {
    //     arrayUTxO = [inputs[0], ...inputs.slice(1)];
    // } else {
    //     console.error("Error: 'inputs' array is empty");
    // }

    //reference input
    let referenceInputs: UTxO[]
    if(referenceInput.length >= 1){
        const myReferenceInputs: UTxO[] = referenceInput.map(toPlutsUtxo);
        referenceInputs = myReferenceInputs.map(input => new UTxO(input));
    }
    
    //collateral input
    let collateralInputs: UTxO[]
    if(collateralInput.length >= 1){
    const myCollateralInputs: UTxO[] = collateralInput.map(toPlutsUtxo);
    collateralInputs = myCollateralInputs.map(input => new UTxO(input));
    }

    // setting outputs
    const myUTxOOutputs: ITxOut[] = utxoOutput.map(toPlutsTxOut);
    const outputs = myUTxOOutputs.map(output => new TxOut(output));

    //collateral output
    let collateralOutputs: TxOut
    if(collateralOutput){
    const myCollateralOutputs = toPlutsTxOut(collateralOutput);
    collateralOutputs = new TxOut(myCollateralOutputs);
    }

    // body parameter fees, invalid_after, invalid_before
    const fees= txn.fees? parseInt(txn.fees) : undefined
    const invalidBefore = txn.invalid_before? parseInt(txn.invalid_before) : undefined
    const invalidAfter = txn.invalid_hereafter? parseInt(txn.invalid_hereafter): undefined
    //setting body
    const myTxbody = toPlutsTxBody(inputs, outputs, fees, invalidBefore, invalidAfter, collateralInputs, referenceInputs, collateralOutputs)
    const txbody = new TxBody(myTxbody)
    console.log(txbody)

    // witness
    const myWitnesses = toPlutsTxWitness()
    const witnesses = new TxWitnessSet(myWitnesses)

    //tx
    const mytx = toPlutsTx(txbody, witnesses)
    const tx = new Tx(mytx)

    //
    const cbor = tx.toCbor()
    const rawTx = WASM_lib.Transaction.from_bytes(Buffer.from(cbor.toString(), 'hex'));
    const txHash = WASM_lib.hash_transaction(rawTx.body());

    console.log(txHash.to_hex())
    return hash == txHash.to_hex()
}


const utxoData = <utxo data> //blockfrost api - /txs/{hash}/utxos
const hash = <transaction hash>
const txn = <transaction details> // blockfrost api - /txs/{hash}/

const hashCorrect = validateHash(hash, utxoData, txn)
console.log(hashCorrect)

utils.ts to convert objects and values into correct type

"use strict";
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
    if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
        if (ar || !(i in from)) {
            if (!ar) ar = Array.prototype.slice.call(from, 0, i);
            ar[i] = from[i];
        }
    }
    return to.concat(ar || Array.prototype.slice.call(from));
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.toPlutsTx = exports.toPlutsTxWitness = exports.toPlutsTxBody = exports.toPlutsTxOut = exports.toPlutsUtxo = void 0;
var plu_ts_1 = require("@harmoniclabs/plu-ts");
var uint8array_utils_1 = require("@harmoniclabs/uint8array-utils");
function toPlutsValue(units) {
    return units.map(function (_a) {
        var unit = _a.unit, quantity = _a.quantity;
        if (unit.length === 0 || unit === "lovelace") {
            return plu_ts_1.Value.lovelaces(BigInt(quantity));
        }
        var policy = new plu_ts_1.Hash28(unit.slice(0, 56));
        var assetName = (0, uint8array_utils_1.fromHex)(unit.slice(56));
        return plu_ts_1.Value.singleAsset(policy, assetName, BigInt(quantity));
    })
        .reduce(function (a, b) { return plu_ts_1.Value.add(a, b); });
}
function toPlutsUtxo(utxoInput) {
    // const utxoInput = u.inputs[0]
    var input = new plu_ts_1.UTxO({
        utxoRef: {
            id: utxoInput.tx_hash,
            index: utxoInput.output_index
        },
        resolved: {
            address: utxoInput.address,
            value: toPlutsValue(utxoInput.amount),
            datum: utxoInput.inline_datum !== null ?
                (0, plu_ts_1.dataFromCbor)(utxoInput.inline_datum) :
                utxoInput.data_hash !== null ?
                    new plu_ts_1.Hash32(utxoInput.data_hash) :
                    undefined,
            refScript: utxoInput.reference_script_hash !== null ?
                new plu_ts_1.Script("PlutusScriptV2", (0, uint8array_utils_1.fromHex)(utxoInput.reference_script_hash)) :
                undefined
        }
    });
    // console.log(`address: ${input.resolved.address} \n value: ${input.resolved.value} \n datum: ${input.resolved.datum} \n refscript ${input.resolved.refScript} \n --------------------------------------------`)
    // console.log((utxoInput.inline_datum !== null ? dataFromCbor(utxoInput.inline_datum) : "null"))
    return input;
}
exports.toPlutsUtxo = toPlutsUtxo;
function toPlutsTxOut(utxoOutput) {
    // const utxoOutput = u.outputs[0]
    return ({
        address: utxoOutput.address,
        datum: utxoOutput.inline_datum !== null ?
            (0, plu_ts_1.dataFromCbor)(utxoOutput.inline_datum) :
            utxoOutput.data_hash !== null ?
                new plu_ts_1.Hash32(utxoOutput.data_hash) :
                undefined,
        refScript: utxoOutput.reference_script_hash !== null ?
            new plu_ts_1.Script("PlutusScriptV2", (0, uint8array_utils_1.fromHex)(utxoOutput.reference_script_hash)) :
            undefined,
        value: toPlutsValue(utxoOutput.amount),
    });
    // console.log(`address: ${output.address} \n value: ${output.value} \n datum: ${output.datum} \n refscript: ${output.refScript}`)
}
exports.toPlutsTxOut = toPlutsTxOut;
function toPlutsTxBody(inputs, output, fee, invalid_before, ttl, collateralInput, refInput, collateralOutputs) {
    if (invalid_before === void 0) { invalid_before = undefined; }
    if (ttl === void 0) { ttl = undefined; }
    if (collateralInput === void 0) { collateralInput = undefined; }
    if (refInput === void 0) { refInput = undefined; }
    if (collateralOutputs === void 0) { collateralOutputs = undefined; }
    // const utxoOutput = u.outputs[0]
    return ({
        inputs: __spreadArray([inputs[0]], inputs.slice(1), true),
        outputs: output,
        fee: fee,
        ttl: ttl,
        collateralInputs: collateralInput,
        refInputs: refInput,
        validityIntervalStart: invalid_before,
        collateralReturn: collateralOutputs
    });
}
exports.toPlutsTxBody = toPlutsTxBody;
function toPlutsTxWitness() {
    return ({});
}
exports.toPlutsTxWitness = toPlutsTxWitness;
function toPlutsTx(body, witnesses) {
    // const utxoOutput = u.outputs[0]
    return ({
        body: body,
        witnesses: witnesses
    });
}
exports.toPlutsTx = toPlutsTx;

.

Also is there a way to calculate the hash without using CSL, tx.hash gives me a str value am I supposed to convert it to different form?