Skip to content
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
293 changes: 164 additions & 129 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions packages/mesh-core-cst/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,20 @@
"dependencies": {
"@cardano-sdk/core": "^0.46.11",
"@cardano-sdk/crypto": "^0.4.4",
"@cardano-sdk/util": "^0.17.1",
"@cardano-sdk/input-selection": "^0.14.27",
"@cardano-sdk/util": "^0.17.1",
"@harmoniclabs/cbor": "1.6.0",
"@harmoniclabs/pair": "^1.0.0",
"@harmoniclabs/plutus-data": "1.2.6",
"@harmoniclabs/uplc": "1.4.1",
"@harmoniclabs/pair": "^1.0.0",
"@meshsdk/common": "1.9.0-beta.96",
"@types/base32-encoding": "^1.0.2",
"base32-encoding": "^1.0.0",
"bech32": "^2.0.0",
"blakejs": "^1.2.1",
"bn.js": "^5.2.0",
"libsodium-wrappers-sumo": "0.7.15"
"libsodium-wrappers-sumo": "0.7.15",
"scalus": "^0.14.2"
},
"overrides": {
"@cardano-sdk/crypto": {
Expand Down
1 change: 1 addition & 0 deletions packages/mesh-core-cst/src/offline-providers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./offline-evaluator-scalus";
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { Serialization } from "@cardano-sdk/core";
import { Scalus, SlotConfig as ScalusSlotConfig } from "scalus";

import {
Action,
DEFAULT_V1_COST_MODEL_LIST,
DEFAULT_V2_COST_MODEL_LIST,
DEFAULT_V3_COST_MODEL_LIST,
IEvaluator,
IFetcher,
Network,
RedeemerTagType,
SLOT_CONFIG_NETWORK,
SlotConfig,
UTxO,
} from "@meshsdk/common";

import { CborWriter } from "../types";
import { toTxUnspentOutput } from "../utils";
import {
getTransactionInputs,
getTransactionOutputs,
} from "../utils/transaction";

export class OfflineEvaluatorScalus implements IEvaluator {
private readonly fetcher: IFetcher;
private readonly network: Network;
public slotConfig: Omit<Omit<SlotConfig, "startEpoch">, "epochLength">;
public costModels: number[][];

/**
* Creates a new instance of OfflineEvaluatorScalus.
* @param fetcher - An implementation of IFetcher to resolve transaction UTXOs
* @param network - The network to evaluate scripts for
* @param slotConfig - Slot configuration for the network (optional, defaults to network-specific values)
* @param customCostModels - Custom cost models for Plutus versions (optional, defaults to mainnet cost models)
*/
constructor(
fetcher: IFetcher,
network: Network,
slotConfig?: Omit<Omit<SlotConfig, "startEpoch">, "epochLength">,
customCostModels?: number[][],
) {
this.fetcher = fetcher;
this.network = network;
this.slotConfig = slotConfig ?? {
slotLength: SLOT_CONFIG_NETWORK[network].slotLength,
zeroSlot: SLOT_CONFIG_NETWORK[network].zeroSlot,
zeroTime: SLOT_CONFIG_NETWORK[network].zeroTime,
};
this.costModels = customCostModels ?? [
DEFAULT_V1_COST_MODEL_LIST,
DEFAULT_V2_COST_MODEL_LIST,
DEFAULT_V3_COST_MODEL_LIST,
];
}

async evaluateTx(
tx: string,
additionalUtxos?: UTxO[],
additionalTxs?: string[],
): Promise<Omit<Action, "data">[]> {
const foundUtxos: Map<String, UTxO> = new Map<string, UTxO>();

if (additionalUtxos) {
for (const utxo of additionalUtxos) {
foundUtxos.set(`${utxo.input.txHash}:${utxo.input.outputIndex}`, utxo);
}
}

if (additionalTxs) {
for (const additionalTx of additionalTxs) {
const utxos = getTransactionOutputs(additionalTx);
for (const utxo of utxos) {
foundUtxos.set(
`${utxo.input.txHash}:${utxo.input.outputIndex}`,
utxo,
);
}
}
}

const inputsToResolve = getTransactionInputs(tx).filter((input) => {
return !foundUtxos.has(`${input.txHash}:${input.outputIndex}`);
});
const queriesNeeded: Set<string> = new Set<string>();
for (const input of inputsToResolve) {
queriesNeeded.add(input.txHash);
}
const fetchedUtxos: Map<string, UTxO[]> = new Map<string, UTxO[]>();
for (const txHash of queriesNeeded) {
const utxos = await this.fetcher.fetchUTxOs(txHash);
fetchedUtxos.set(txHash, utxos);
}

for (const input of inputsToResolve) {
const utxos = fetchedUtxos.get(input.txHash);
if (!utxos) {
throw new Error(
`Unable to fetch UTxOs for transaction hash: ${input.txHash}`,
);
}
const utxo = utxos.find((u) => u.input.outputIndex === input.outputIndex);
if (!utxo) {
throw new Error(
`UTxO not found for input: ${input.txHash}:${input.outputIndex}`,
);
}
foundUtxos.set(`${input.txHash}:${input.outputIndex}`, utxo);
}

const cborWriter = new CborWriter();
cborWriter.writeStartMap(foundUtxos.size);
for (const [key, utxo] of foundUtxos) {
const cardanoUtxo = toTxUnspentOutput(utxo);
cborWriter.writeEncodedValue(
Buffer.from(cardanoUtxo.input().toCbor(), "hex"),
);
cborWriter.writeEncodedValue(
Buffer.from(cardanoUtxo.output().toCbor(), "hex"),
);
}

return Scalus.evalPlutusScripts(
Buffer.from(tx, "hex"),
cborWriter.encode(),
new ScalusSlotConfig(
this.slotConfig.zeroTime,
this.slotConfig.zeroSlot,
this.slotConfig.slotLength,
),
this.costModels,
).map((scalusRedeemer: Scalus.Redeemer) => {
return {
tag: scalusRedeemer.tag.toUpperCase() as RedeemerTagType,
index: scalusRedeemer.index,
budget: {
mem: Number(scalusRedeemer.budget.memory),
steps: Number(scalusRedeemer.budget.steps),
},
};
});
}
}
33 changes: 16 additions & 17 deletions packages/mesh-core-cst/src/resolvers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ Apache-2.0 License for more details.
*/

import { Cardano } from "@cardano-sdk/core";
import { blake2b } from "@cardano-sdk/crypto";
import { HexBlob } from "@cardano-sdk/util";
import base32 from "base32-encoding";
import { bech32 } from "bech32";

Expand Down Expand Up @@ -42,12 +40,11 @@ import {
toScriptRef,
} from "../utils";
import { buildRewardAddress } from "../utils/builder";
import { hexToBytes } from "../utils/encoding";

export const resolveDataHash = (
rawData: BuilderData["content"],
type: PlutusDataType = "Mesh",
) => {
): string => {
const plutusData = fromBuilderToPlutusData({
content: rawData,
type,
Expand All @@ -58,7 +55,7 @@ export const resolveDataHash = (
export const resolveNativeScriptAddress = (
script: NativeScript,
networkId = 0,
) => {
): string => {
const nativeScript = toNativeScript(script);

const enterpriseAddress = EnterpriseAddress.fromCredentials(networkId, {
Expand All @@ -69,11 +66,11 @@ export const resolveNativeScriptAddress = (
return enterpriseAddress.toAddress().toBech32().toString();
};

export const resolveNativeScriptHash = (script: NativeScript) => {
export const resolveNativeScriptHash = (script: NativeScript): string => {
return toNativeScript(script).hash().toString();
};

export const resolvePaymentKeyHash = (bech32: string) => {
export const resolvePaymentKeyHash = (bech32: string): string => {
try {
const paymentKeyHash = [
toBaseAddress(bech32)?.getPaymentCredential().hash,
Expand All @@ -95,7 +92,7 @@ export const resolvePaymentKeyHash = (bech32: string) => {
export const resolvePlutusScriptAddress = (
script: PlutusScript,
networkId = 0,
) => {
): string => {
const plutusScript = deserializePlutusScript(script.code, script.version);

const enterpriseAddress = EnterpriseAddress.fromCredentials(networkId, {
Expand All @@ -106,7 +103,7 @@ export const resolvePlutusScriptAddress = (
return enterpriseAddress.toAddress().toBech32().toString();
};

export const resolvePlutusScriptHash = (bech32: string) => {
export const resolvePlutusScriptHash = (bech32: string): string => {
try {
const enterpriseAddress = toEnterpriseAddress(bech32);
const scriptHash = enterpriseAddress?.getPaymentCredential().hash;
Expand All @@ -119,11 +116,11 @@ export const resolvePlutusScriptHash = (bech32: string) => {
}
};

export const resolvePoolId = (hash: string) => {
export const resolvePoolId = (hash: string): string => {
return PoolId.fromKeyHash(Ed25519KeyHashHex(hash)).toString();
};

export const resolvePrivateKey = (words: string[]) => {
export const resolvePrivateKey = (words: string[]): string => {
const buildBip32PrivateKey = (
entropy: string,
password = "",
Expand All @@ -142,11 +139,13 @@ export const resolvePrivateKey = (words: string[]) => {
return bech32PrivateKey;
};

export const resolveScriptRef = (script: PlutusScript | NativeScript) => {
export const resolveScriptRef = (
script: PlutusScript | NativeScript,
): string => {
return toScriptRef(script).toCbor().toString();
};

export const resolveRewardAddress = (bech32: string) => {
export const resolveRewardAddress = (bech32: string): string => {
try {
const address = toAddress(bech32);
const baseAddress = toBaseAddress(bech32);
Expand All @@ -164,7 +163,7 @@ export const resolveRewardAddress = (bech32: string) => {
}
};

export const resolveStakeKeyHash = (bech32: string) => {
export const resolveStakeKeyHash = (bech32: string): string => {
try {
const stakeKeyHash = [
toBaseAddress(bech32)?.getStakeCredential().hash,
Expand All @@ -179,19 +178,19 @@ export const resolveStakeKeyHash = (bech32: string) => {
}
};

export const resolveTxHash = (txHex: string) => {
export const resolveTxHash = (txHex: string): string => {
const txBody = deserializeTx(txHex).body();
return txBody.hash().toString();
};

export const resolveScriptHashDRepId = (scriptHash: string) => {
export const resolveScriptHashDRepId = (scriptHash: string): string => {
return DRepID.cip129FromCredential({
type: Cardano.CredentialType.ScriptHash,
hash: Hash28ByteBase16(scriptHash),
}).toString();
};

export const resolveEd25519KeyHash = (bech32: string) => {
export const resolveEd25519KeyHash = (bech32: string): string => {
try {
const keyHash = [
toBaseAddress(bech32)?.getPaymentCredential().hash,
Expand Down
14 changes: 9 additions & 5 deletions packages/mesh-core-cst/src/utils/converter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
UTxO,
} from "@meshsdk/common";

import { resolveDataHash } from "../resolvers";
import {
Address,
AssetId,
Expand Down Expand Up @@ -81,8 +82,8 @@ export const toRewardAddress = (bech32: string): RewardAddress | undefined =>
export const fromTxUnspentOutput = (
txUnspentOutput: TransactionUnspentOutput,
): UTxO => {
const dataHash = txUnspentOutput.output().datum()
? txUnspentOutput.output().datum()?.toCbor().toString()
let dataHash = txUnspentOutput.output().datum()
? txUnspentOutput.output().datum()?.asDataHash()?.toString()
: undefined;

const scriptRef = txUnspentOutput.output().scriptRef()
Expand All @@ -93,6 +94,9 @@ export const fromTxUnspentOutput = (
? txUnspentOutput.output().datum()?.asInlineData()?.toCbor().toString()
: undefined;

if (plutusData && !dataHash) {
dataHash = resolveDataHash(plutusData, "CBOR");
}
return <UTxO>{
input: {
outputIndex: Number(txUnspentOutput.input().index()),
Expand Down Expand Up @@ -450,7 +454,9 @@ export const getDRepIds = (
return result;
};

export const toPlutusLanguageVersion = (version: LanguageVersion): PlutusLanguageVersion => {
export const toPlutusLanguageVersion = (
version: LanguageVersion,
): PlutusLanguageVersion => {
switch (version) {
case "V1":
return PlutusLanguageVersion.V1;
Expand All @@ -460,5 +466,3 @@ export const toPlutusLanguageVersion = (version: LanguageVersion): PlutusLanguag
return PlutusLanguageVersion.V3;
}
};


10 changes: 5 additions & 5 deletions packages/mesh-core-cst/src/utils/deserializer.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Serialization } from "@cardano-sdk/core";
import { Ed25519KeyHashHex } from "@cardano-sdk/crypto";
import { HexBlob } from "@cardano-sdk/util";

import { DeserializedAddress, LanguageVersion, toBytes } from "@meshsdk/common";
import { LanguageVersion, toBytes } from "@meshsdk/common";

import {
Ed25519KeyHash,
Expand All @@ -18,8 +17,9 @@ import {
Value,
} from "../types";

export const deserializeEd25519KeyHash = (ed25519KeyHash: string) =>
Ed25519KeyHash.fromBytes(toBytes(ed25519KeyHash));
export const deserializeEd25519KeyHash = (
ed25519KeyHash: string,
): Ed25519KeyHash => Ed25519KeyHash.fromBytes(toBytes(ed25519KeyHash));

export const deserializePlutusScript = (
plutusScript: string,
Expand All @@ -40,7 +40,7 @@ export const deserializePlutusScript = (
export const deserializeNativeScript = (nativeScript: string): NativeScript =>
NativeScript.fromCbor(HexBlob(nativeScript));

export const deserializeScriptHash = (scriptHash: string) =>
export const deserializeScriptHash = (scriptHash: string): ScriptHash =>
ScriptHash(scriptHash);

export const deserializeScriptRef = (scriptRef: string): Script =>
Expand Down
Loading