Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/tx builder fetcher logic #381

Merged
merged 7 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/mesh-common/src/types/transaction-builder/txin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import { Asset } from "../asset";
import { DatumSource, Redeemer } from "./data";
import { ScriptSource, SimpleScriptSourceInfo } from "./script";

export type RefTxIn = { txHash: string; txIndex: number, scriptSize?: number };
export type RefTxIn = { txHash: string; txIndex: number; scriptSize?: number };

export type TxInParameter = {
txHash: string;
txIndex: number;
amount?: Asset[];
address?: string;
scriptSize?: number;
};

export type TxIn = PubKeyTxIn | SimpleScriptTxIn | ScriptTxIn;
Expand Down
58 changes: 26 additions & 32 deletions packages/mesh-transaction/src/mesh-tx-builder/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,39 +84,12 @@ export class MeshTxBuilder extends MeshTxBuilderCore {

// Checking if all inputs are complete
const { inputs, collaterals, mints } = this.meshTxBuilderBody;
// We must check every input for ref scripts
const incompleteTxIns = [...inputs, ...collaterals];
const incompleteTxIns = [...inputs, ...collaterals].filter(
(txIn) => !this.isInputComplete(txIn),
);
const incompleteMints = mints.filter((mint) => !this.isMintComplete(mint));
// Getting all missing utxo information
await this.queryAllTxInfo(incompleteTxIns, incompleteMints);
// Gather all utxos with ref scripts
Object.values(this.queriedUTxOs).forEach((utxos) => {
for (let utxo of utxos) {
if (utxo.output.scriptRef !== undefined) {
this.utxosWithRefScripts.push(utxo);
}
}
});
const missingRefInput = this.utxosWithRefScripts.filter((utxo) => {
this.meshTxBuilderBody.referenceInputs.forEach((refInput) => {
if (
refInput.txHash === utxo.input.txHash &&
refInput.txIndex === utxo.input.outputIndex
) {
return false;
}
});
return true;
});
// Add any inputs with ref scripts into reference inputs
// serializer will then deduplicate, but keep the script size for fee calc
missingRefInput.forEach((utxo) => {
this.meshTxBuilderBody.referenceInputs.push({
txHash: utxo.input.txHash,
txIndex: utxo.input.outputIndex,
scriptSize: utxo.output.scriptRef!.length / 2,
});
});
// Completing all inputs
incompleteTxIns.forEach((txIn) => {
this.completeTxInformation(txIn);
Expand All @@ -131,6 +104,22 @@ export class MeshTxBuilder extends MeshTxBuilderCore {
this.completeSimpleScriptInfo(scriptSource);
}
});
this.meshTxBuilderBody.inputs.forEach((input) => {
if (input.txIn.scriptSize && input.txIn.scriptSize > 0) {
if (
this.meshTxBuilderBody.referenceInputs.find((refTxIn) => {
refTxIn.txHash === input.txIn.txHash &&
refTxIn.txIndex === input.txIn.txIndex;
}) === undefined
) {
this.meshTxBuilderBody.referenceInputs.push({
txHash: input.txIn.txHash,
txIndex: input.txIn.txIndex,
scriptSize: input.txIn.scriptSize,
});
}
}
});
this.addUtxosFromSelection();

let txHex = this.serializer.serializeTxBody(
Expand Down Expand Up @@ -285,6 +274,11 @@ export class MeshTxBuilder extends MeshTxBuilderCore {
);
input.txIn.address = address;
}
if (utxo?.output.scriptRef) {
input.txIn.scriptSize = utxo.output.scriptRef.length / 2;
} else {
input.txIn.scriptSize = 0;
}
};

protected completeScriptInfo = (scriptSource: ScriptSource) => {
Expand Down Expand Up @@ -331,8 +325,8 @@ export class MeshTxBuilder extends MeshTxBuilderCore {
};

protected isInputInfoComplete = (txIn: TxIn): boolean => {
const { amount, address } = txIn.txIn;
if (!amount || !address) return false;
const { amount, address, scriptSize } = txIn.txIn;
if (!amount || !address || scriptSize === undefined) return false;
return true;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,15 @@ export class MeshTxBuilderCore {
* @param txIndex The transaction index of the input UTxO
* @param amount The asset amount of index of the input UTxO
* @param address The address of the input UTxO
* @param scriptSize The size of the ref script at this input (if there isn't one, explicitly put 0 as scriptSize for offline tx building)
* @returns The MeshTxBuilder instance
*/
txIn = (
txHash: string,
txIndex: number,
amount?: Asset[],
address?: string,
scriptSize?: number,
) => {
if (this.txInQueueItem) {
this.queueInput();
Expand All @@ -92,6 +94,7 @@ export class MeshTxBuilderCore {
txIndex: txIndex,
amount: amount,
address: address,
scriptSize: scriptSize,
},
};
} else {
Expand All @@ -102,6 +105,7 @@ export class MeshTxBuilderCore {
txIndex: txIndex,
amount: amount,
address: address,
scriptSize: scriptSize,
},
scriptTxIn: {},
};
Expand Down
46 changes: 28 additions & 18 deletions packages/mesh-transaction/src/transaction/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ import {
import {
Cardano,
CardanoSDKUtil,
deserializeTx,
deserializeNativeScript,
deserializePlutusScript,
deserializeTx,
fromScriptRef,
Serialization,
Transaction as Tx,
Expand All @@ -48,23 +48,22 @@ export class Transaction {
this.initiator = options.initiator;
}

static attachMetadata(
cborTx: string,
cborTxMetadata: string,
) {
static attachMetadata(cborTx: string, cborTxMetadata: string) {
const tx = deserializeTx(cborTx);
const txAuxData = tx.auxiliaryData() ?? new Serialization.AuxiliaryData();

txAuxData.setMetadata(
Serialization.GeneralTransactionMetadata.fromCbor(CardanoSDKUtil.HexBlob(cborTxMetadata))
Serialization.GeneralTransactionMetadata.fromCbor(
CardanoSDKUtil.HexBlob(cborTxMetadata),
),
);

if (
Cardano.computeAuxiliaryDataHash(txAuxData.toCore())?.toString() !==
tx.body().auxiliaryDataHash()?.toString()
) {
throw new Error(
'[Transaction] attachMetadata: The metadata hash does not match the auxiliary data hash.'
"[Transaction] attachMetadata: The metadata hash does not match the auxiliary data hash.",
);
}

Expand All @@ -81,8 +80,15 @@ export class Transaction {
const txMetadata = tx.auxiliaryData()?.metadata();

if (txMetadata !== undefined) {
const mockMetadata = new Map<bigint, Serialization.TransactionMetadatum>();
txMetadata.metadata()?.forEach((metadatum, label) => mockMetadata.set(label, mask(metadatum)));
const mockMetadata = new Map<
bigint,
Serialization.TransactionMetadatum
>();
txMetadata
.metadata()
?.forEach((metadatum, label) =>
mockMetadata.set(label, mask(metadatum)),
);
const txAuxData = tx.auxiliaryData();
txMetadata.setMetadata(mockMetadata);
txAuxData?.setMetadata(txMetadata);
Expand All @@ -94,18 +100,17 @@ export class Transaction {

static readMetadata(cborTx: string) {
const tx = deserializeTx(cborTx);
return tx.auxiliaryData()?.metadata()?.toCbor().toString() ?? '';
return tx.auxiliaryData()?.metadata()?.toCbor().toString() ?? "";
}

static writeMetadata(
cborTx: string,
cborTxMetadata: string,
) {
static writeMetadata(cborTx: string, cborTxMetadata: string) {
const tx = deserializeTx(cborTx);
const txAuxData = tx.auxiliaryData() ?? new Serialization.AuxiliaryData();

txAuxData.setMetadata(
Serialization.GeneralTransactionMetadata.fromCbor(CardanoSDKUtil.HexBlob(cborTxMetadata))
Serialization.GeneralTransactionMetadata.fromCbor(
CardanoSDKUtil.HexBlob(cborTxMetadata),
),
);

return new Tx(tx.body(), tx.witnessSet(), txAuxData).toCbor().toString();
Expand Down Expand Up @@ -216,6 +221,7 @@ export class Transaction {
input.input.outputIndex,
input.output.amount,
input.output.address,
input.output.scriptRef ? input.output.scriptRef.length / 2 : 0,
);
});

Expand Down Expand Up @@ -715,13 +721,17 @@ export class Transaction {
}
}

function mask(metadatum: Serialization.TransactionMetadatum): Serialization.TransactionMetadatum {
function mask(
metadatum: Serialization.TransactionMetadatum,
): Serialization.TransactionMetadatum {
switch (metadatum.getKind()) {
case Serialization.TransactionMetadatumKind.Text:
return Serialization.TransactionMetadatum.newText("0".repeat(metadatum.asText()?.length ?? 0));
return Serialization.TransactionMetadatum.newText(
"0".repeat(metadatum.asText()?.length ?? 0),
);
case Serialization.TransactionMetadatumKind.Bytes:
case Serialization.TransactionMetadatumKind.Integer:
return metadatum
return metadatum;
case Serialization.TransactionMetadatumKind.List:
const list = new Serialization.MetadatumList();
for (let i = 0; i < (metadatum.asList()?.getLength() ?? 0); i++) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ describe("MeshTxBuilder", () => {
it("should return true for complete PubKey input", () => {
const txIn: TxIn = {
type: "PubKey",
txIn: { txHash: "hash", txIndex: 0, amount: [], address: "address" },
txIn: { txHash: "hash", txIndex: 0, amount: [], address: "address", scriptSize: 0 },
};
expect(txBuilder.isInputCompleteExtended(txIn)).toBe(true);
});
Expand Down Expand Up @@ -89,7 +89,7 @@ describe("MeshTxBuilder", () => {
it("should return true for complete Script input", () => {
const txIn: TxIn = {
type: "Script",
txIn: { txHash: "hash", txIndex: 0, amount: [], address: "address" },
txIn: { txHash: "hash", txIndex: 0, amount: [], address: "address", scriptSize: 0 },
scriptTxIn: {
scriptSource: {
type: "Provided",
Expand All @@ -102,7 +102,7 @@ describe("MeshTxBuilder", () => {
it("should return true for complete Script input, with complete script ref", () => {
const txIn: TxIn = {
type: "Script",
txIn: { txHash: "hash", txIndex: 0, amount: [], address: "address" },
txIn: { txHash: "hash", txIndex: 0, amount: [], address: "address", scriptSize: 0 },
scriptTxIn: {
scriptSource: {
type: "Inline",
Expand Down Expand Up @@ -176,7 +176,7 @@ describe("MeshTxBuilder", () => {
it("should return true for complete PubKey input", () => {
const txIn: TxIn = {
type: "PubKey",
txIn: { txHash: "hash", txIndex: 0, amount: [], address: "address" },
txIn: { txHash: "hash", txIndex: 0, amount: [], address: "address", scriptSize: 0 },
};
expect(txBuilder.isInputInfoCompleteExtended(txIn)).toBe(true);
});
Expand Down