diff --git a/packages/cashc/package.json b/packages/cashc/package.json index 0c592984..0e90a460 100644 --- a/packages/cashc/package.json +++ b/packages/cashc/package.json @@ -51,7 +51,7 @@ "test": "NODE_OPTIONS='--experimental-vm-modules --no-warnings' jest" }, "dependencies": { - "@bitauth/libauth": "^3.1.0-next.2", + "@bitauth/libauth": "^3.1.0-next.8", "@cashscript/utils": "^0.11.5", "antlr4": "^4.13.2", "commander": "^14.0.0", diff --git a/packages/cashscript/package.json b/packages/cashscript/package.json index d200fc41..c8722e06 100644 --- a/packages/cashscript/package.json +++ b/packages/cashscript/package.json @@ -45,7 +45,7 @@ "test": "NODE_OPTIONS='--experimental-vm-modules --no-warnings' jest" }, "dependencies": { - "@bitauth/libauth": "^3.1.0-next.2", + "@bitauth/libauth": "^3.1.0-next.8", "@cashscript/utils": "^0.11.5", "@electrum-cash/network": "^4.1.3", "@mr-zwets/bchn-api-wrapper": "^1.0.1", diff --git a/packages/cashscript/src/LibauthTemplate.ts b/packages/cashscript/src/LibauthTemplate.ts index e09652eb..979e7357 100644 --- a/packages/cashscript/src/LibauthTemplate.ts +++ b/packages/cashscript/src/LibauthTemplate.ts @@ -42,11 +42,13 @@ import { TokenDetails, UnlockableUtxo, Utxo, + VmTarget, } from './interfaces.js'; import SignatureTemplate from './SignatureTemplate.js'; import { addressToLockScript, extendedStringify, getSignatureAndPubkeyFromP2PKHInput, zip } from './utils.js'; import { TransactionBuilder } from './TransactionBuilder.js'; import { deflate } from 'pako'; +import MockNetworkProvider from './network/MockNetworkProvider.js'; // TODO: Add / improve descriptions throughout the template generation @@ -60,11 +62,13 @@ export const getLibauthTemplates = ( const libauthTransaction = txn.buildLibauthTransaction(); const csTransaction = createTransactionTypeFromTransactionBuilder(txn); + const vmTarget = txn.provider instanceof MockNetworkProvider ? txn.provider.vmTarget : VmTarget.BCH_2025_05; + const baseTemplate: WalletTemplate = { $schema: 'https://ide.bitauth.com/authentication-template-v0.schema.json', description: 'Imported from cashscript', name: 'CashScript Generated Debugging Template', - supported: ['BCH_2025_05'], + supported: [vmTarget], version: 0, entities: {}, scripts: {}, diff --git a/packages/cashscript/src/debugging.ts b/packages/cashscript/src/debugging.ts index 82023d5d..c138e819 100644 --- a/packages/cashscript/src/debugging.ts +++ b/packages/cashscript/src/debugging.ts @@ -1,12 +1,37 @@ -import { AuthenticationErrorCommon, AuthenticationInstruction, AuthenticationProgramCommon, AuthenticationProgramStateCommon, AuthenticationVirtualMachine, ResolvedTransactionCommon, WalletTemplate, WalletTemplateScriptUnlocking, binToHex, createCompiler, createVirtualMachineBch2025, decodeAuthenticationInstructions, encodeAuthenticationInstruction, walletTemplateToCompilerConfiguration } from '@bitauth/libauth'; +import { AuthenticationErrorCommon, AuthenticationInstruction, AuthenticationProgramCommon, AuthenticationProgramStateCommon, AuthenticationVirtualMachine, ResolvedTransactionCommon, WalletTemplate, WalletTemplateScriptUnlocking, binToHex, createCompiler, createVirtualMachineBch2023, createVirtualMachineBch2025, createVirtualMachineBch2026, createVirtualMachineBchSpec, decodeAuthenticationInstructions, encodeAuthenticationInstruction, walletTemplateToCompilerConfiguration } from '@bitauth/libauth'; import { Artifact, LogEntry, Op, PrimitiveType, StackItem, asmToBytecode, bytecodeToAsm, decodeBool, decodeInt, decodeString } from '@cashscript/utils'; import { findLastIndex, toRegExp } from './utils.js'; import { FailedRequireError, FailedTransactionError, FailedTransactionEvaluationError } from './Errors.js'; import { getBitauthUri } from './LibauthTemplate.js'; +import { VmTarget } from './interfaces.js'; export type DebugResult = AuthenticationProgramStateCommon[]; export type DebugResults = Record; +/* eslint-disable @typescript-eslint/indent */ +type VM = AuthenticationVirtualMachine< + ResolvedTransactionCommon, + AuthenticationProgramCommon, + AuthenticationProgramStateCommon +>; +/* eslint-enable @typescript-eslint/indent */ + +const createVirtualMachine = (vmTarget: VmTarget): VM => { + switch (vmTarget) { + case 'BCH_2023_05': + return createVirtualMachineBch2023(); + case 'BCH_2025_05': + return createVirtualMachineBch2025(); + case 'BCH_2026_05': + return createVirtualMachineBch2026(); + case 'BCH_SPEC': + // TODO: This typecast is shitty, but it's hard to fix + return createVirtualMachineBchSpec() as unknown as VM; + default: + throw new Error(`Debugging is not supported for the ${vmTarget} virtual machine.`); + } +}; + // debugs the template, optionally logging the execution data export const debugTemplate = (template: WalletTemplate, artifacts: Artifact[]): DebugResults => { // If a contract has the same name, but a different bytecode, then it is considered a name collision @@ -61,7 +86,7 @@ const debugSingleScenario = ( for (const log of executedLogs) { const inputIndex = extractInputIndexFromScenario(scenarioId); - logConsoleLogStatement(log, executedDebugSteps, artifact, inputIndex); + logConsoleLogStatement(log, executedDebugSteps, artifact, inputIndex, vm); } } @@ -157,21 +182,13 @@ const extractInputIndexFromScenario = (scenarioId: string): number => { return parseInt(match[1]); }; -/* eslint-disable @typescript-eslint/indent */ -type VM = AuthenticationVirtualMachine< - ResolvedTransactionCommon, - AuthenticationProgramCommon, - AuthenticationProgramStateCommon ->; -/* eslint-enable @typescript-eslint/indent */ - type Program = AuthenticationProgramCommon; type CreateProgramResult = { vm: VM, program: Program }; // internal util. instantiates the virtual machine and compiles the template into a program const createProgram = (template: WalletTemplate, unlockingScriptId: string, scenarioId: string): CreateProgramResult => { const configuration = walletTemplateToCompilerConfiguration(template); - const vm = createVirtualMachineBch2025(); + const vm = createVirtualMachine(template.supported[0] as VmTarget); const compiler = createCompiler(configuration); if (!template.scripts[unlockingScriptId]) { @@ -204,13 +221,14 @@ const logConsoleLogStatement = ( debugSteps: AuthenticationProgramStateCommon[], artifact: Artifact, inputIndex: number, + vm: VM, ): void => { let line = `${artifact.contractName}.cash:${log.line}`; const decodedData = log.data.map((element) => { if (typeof element === 'string') return element; const debugStep = debugSteps.find((step) => step.ip === element.ip)!; - const transformedDebugStep = applyStackItemTransformations(element, debugStep); + const transformedDebugStep = applyStackItemTransformations(element, debugStep, vm); return decodeStackItem(element, transformedDebugStep.stack); }); console.log(`[Input #${inputIndex}] ${line} ${decodedData.join(' ')}`); @@ -219,6 +237,7 @@ const logConsoleLogStatement = ( const applyStackItemTransformations = ( element: StackItem, debugStep: AuthenticationProgramStateCommon, + vm: VM, ): AuthenticationProgramStateCommon => { if (!element.transformations) return debugStep; @@ -236,9 +255,10 @@ const applyStackItemTransformations = ( instructions: transformationsAuthenticationInstructions, signedMessages: [], program: { ...debugStep.program }, + functionTable: debugStep.functionTable ?? {}, + functionCount: debugStep.functionCount ?? 0, }; - const vm = createVirtualMachineBch2025(); const transformationsEndState = vm.stateEvaluate(transformationsStartState); return transformationsEndState; diff --git a/packages/cashscript/src/interfaces.ts b/packages/cashscript/src/interfaces.ts index d5a88c48..8e89e39e 100644 --- a/packages/cashscript/src/interfaces.ts +++ b/packages/cashscript/src/interfaces.ts @@ -144,6 +144,15 @@ export const Network = { export type Network = (typeof Network)[keyof typeof Network]; +export const VmTarget = { + BCH_2023_05: literal('BCH_2023_05'), + BCH_2025_05: literal('BCH_2025_05'), + BCH_2026_05: literal('BCH_2026_05'), + BCH_SPEC: literal('BCH_SPEC'), +}; + +export type VmTarget = (typeof VmTarget)[keyof typeof VmTarget]; + export interface TransactionDetails extends Transaction { txid: string; hex: string; diff --git a/packages/cashscript/src/network/MockNetworkProvider.ts b/packages/cashscript/src/network/MockNetworkProvider.ts index 52e9d02b..2af9b763 100644 --- a/packages/cashscript/src/network/MockNetworkProvider.ts +++ b/packages/cashscript/src/network/MockNetworkProvider.ts @@ -1,11 +1,12 @@ import { binToHex, decodeTransactionUnsafe, hexToBin, isHex } from '@bitauth/libauth'; import { sha256 } from '@cashscript/utils'; -import { Utxo, Network } from '../interfaces.js'; +import { Utxo, Network, VmTarget } from '../interfaces.js'; import NetworkProvider from './NetworkProvider.js'; import { addressToLockScript, libauthTokenDetailsToCashScriptTokenDetails } from '../utils.js'; -interface MockNetworkProviderOptions { +export interface MockNetworkProviderOptions { updateUtxoSet: boolean; + vmTarget?: VmTarget; } export default class MockNetworkProvider implements NetworkProvider { @@ -15,9 +16,11 @@ export default class MockNetworkProvider implements NetworkProvider { public network: Network = Network.MOCKNET; public blockHeight: number = 133700; public options: MockNetworkProviderOptions; + public vmTarget: VmTarget; constructor(options?: Partial) { this.options = { updateUtxoSet: true, ...options }; + this.vmTarget = this.options.vmTarget ?? VmTarget.BCH_2025_05; } async getUtxos(address: string): Promise { diff --git a/packages/cashscript/test/debugging.test.ts b/packages/cashscript/test/debugging.test.ts index c174f473..3183bb64 100644 --- a/packages/cashscript/test/debugging.test.ts +++ b/packages/cashscript/test/debugging.test.ts @@ -1,4 +1,4 @@ -import { Contract, FailedTransactionError, MockNetworkProvider, SignatureAlgorithm, SignatureTemplate, TransactionBuilder } from '../src/index.js'; +import { Contract, FailedTransactionError, MockNetworkProvider, SignatureAlgorithm, SignatureTemplate, TransactionBuilder, VmTarget } from '../src/index.js'; import { aliceAddress, alicePriv, alicePub, bobPriv, bobPub } from './fixture/vars.js'; import '../src/test/JestExtensions.js'; import { randomUtxo } from '../src/utils.js'; @@ -649,4 +649,32 @@ describe('Debugging tests', () => { expect(() => transactionBuilder.debug()).toThrow(FailedTransactionError); }); }); + + describe('VmTargets', () => { + const vmTargets = [ + undefined, + VmTarget.BCH_2023_05, + VmTarget.BCH_2025_05, + VmTarget.BCH_2026_05, + VmTarget.BCH_SPEC, + ] as const; + + for (const vmTarget of vmTargets) { + it(`should execute and log correctly with vmTarget ${vmTarget}`, async () => { + const provider = new MockNetworkProvider({ vmTarget }); + const contractTestLogs = new Contract(artifactTestLogs, [alicePub], { provider }); + const contractUtxo = randomUtxo(); + provider.addUtxo(contractTestLogs.address, contractUtxo); + + const transaction = new TransactionBuilder({ provider }) + .addInput(contractUtxo, contractTestLogs.unlock.transfer(new SignatureTemplate(alicePriv), 1000n)) + .addOutput({ to: contractTestLogs.address, amount: 10000n }); + + expect(transaction.getLibauthTemplate().supported[0]).toBe(vmTarget ?? 'BCH_2025_05'); + + const expectedLog = new RegExp(`^\\[Input #0] Test.cash:10 0x[0-9a-f]{130} 0x${binToHex(alicePub)} 1000 0xbeef 1 test true$`); + expect(transaction).toLog(expectedLog); + }); + } + }); }); diff --git a/packages/utils/package.json b/packages/utils/package.json index 76a99341..c29a3171 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -44,7 +44,7 @@ "test": "NODE_OPTIONS='--experimental-vm-modules --no-warnings' jest" }, "dependencies": { - "@bitauth/libauth": "^3.1.0-next.2" + "@bitauth/libauth": "^3.1.0-next.8" }, "devDependencies": { "@jest/globals": "^29.7.0", diff --git a/website/docs/releases/release-notes.md b/website/docs/releases/release-notes.md index 703479ab..9576a66d 100644 --- a/website/docs/releases/release-notes.md +++ b/website/docs/releases/release-notes.md @@ -9,6 +9,7 @@ title: Release Notes - :boom: **BREAKING**: Make `provider` a required option in `Contract` constructor. - :boom: **BREAKING**: Remove deprecated "old" transaction builder (`contract.functions`). - :boom: **BREAKING**: No longer seed the MockNetworkProvider with any test UTXOs. +- :sparkles: Add a configurable `vmTarget` option to `MockNetworkProvider`. - :hammer_and_wrench: Improve libauth template generation. ## v0.11.5 diff --git a/yarn.lock b/yarn.lock index 0daa8efd..ad42ae0b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -630,6 +630,11 @@ resolved "https://registry.yarnpkg.com/@bitauth/libauth/-/libauth-3.1.0-next.2.tgz#121782b38774d9fba8226406db9b9af0c8d8e464" integrity sha512-XRtk9p8UHvtjSPS38rsfHXzaPHG5j9FpN4qHqqGLoAuZYy675PBiOy9zP6ah8lTnnIVaCFl2ekct8w0Hy1oefw== +"@bitauth/libauth@^3.1.0-next.8": + version "3.1.0-next.8" + resolved "https://registry.yarnpkg.com/@bitauth/libauth/-/libauth-3.1.0-next.8.tgz#d130e5db6c3c8b24731c8d04c4091be07f48b0ee" + integrity sha512-Pm+Ju+YP3JeBLLTiVrBnia2wwE4G17r4XqpvPRMcklElJTe8J6x3JgKRg1by0Xm3ZY6UFxACkEAoSA+x419/zA== + "@chris.troutner/bip32-utils@1.0.5": version "1.0.5" resolved "https://registry.yarnpkg.com/@chris.troutner/bip32-utils/-/bip32-utils-1.0.5.tgz#b6722aeaad5fcda6fba69cbeda7e2a5e8afbd692"