|
1 | 1 | import { AuthenticationErrorCommon, AuthenticationInstruction, AuthenticationProgramCommon, AuthenticationProgramStateCommon, AuthenticationVirtualMachine, ResolvedTransactionCommon, WalletTemplate, WalletTemplateScriptUnlocking, binToHex, createCompiler, createVirtualMachineBch2023, createVirtualMachineBch2025, createVirtualMachineBch2026, createVirtualMachineBchSpec, decodeAuthenticationInstructions, encodeAuthenticationInstruction, walletTemplateToCompilerConfiguration } from '@bitauth/libauth'; |
2 | | -import { Artifact, LogEntry, Op, PrimitiveType, StackItem, asmToBytecode, bytecodeToAsm, decodeBool, decodeInt, decodeString } from '@cashscript/utils'; |
| 2 | +import { Artifact, LogData, LogEntry, Op, PrimitiveType, StackItem, asmToBytecode, bytecodeToAsm, decodeBool, decodeInt, decodeString } from '@cashscript/utils'; |
3 | 3 | import { findLastIndex, toRegExp } from './utils.js'; |
4 | 4 | import { FailedRequireError, FailedTransactionError, FailedTransactionEvaluationError } from './Errors.js'; |
5 | 5 | import { getBitauthUri } from './libauth-template/LibauthTemplate.js'; |
@@ -84,12 +84,26 @@ const debugSingleScenario = ( |
84 | 84 |
|
85 | 85 | // P2PKH inputs do not have an artifact, so we skip the console.log handling |
86 | 86 | if (artifact) { |
87 | | - const executedLogs = (artifact.debug?.logs ?? []) |
88 | | - .filter((log) => executedDebugSteps.some((debugStep) => log.ip === debugStep.ip)); |
89 | | - |
90 | | - for (const log of executedLogs) { |
| 87 | + // Try to match each executed debug step to a log entry if it exists. Note that inside loops, |
| 88 | + // the same log statement may be executed multiple times in different debug steps |
| 89 | + // Also note that multiple log statements may exist for the same ip, so we need to handle all of them |
| 90 | + const executedLogs = executedDebugSteps |
| 91 | + .flatMap((debugStep, index) => { |
| 92 | + const logEntries = artifact.debug?.logs?.filter((log) => log.ip === debugStep.ip); |
| 93 | + if (!logEntries || logEntries.length === 0) return []; |
| 94 | + |
| 95 | + const reversedPriorDebugSteps = executedDebugSteps.slice(0, index + 1).reverse(); |
| 96 | + |
| 97 | + return logEntries.map((logEntry) => { |
| 98 | + const decodedLogData = logEntry.data |
| 99 | + .map((dataEntry) => decodeLogDataEntry(dataEntry, reversedPriorDebugSteps, vm)); |
| 100 | + return { logEntry, decodedLogData }; |
| 101 | + }); |
| 102 | + }); |
| 103 | + |
| 104 | + for (const { logEntry, decodedLogData } of executedLogs) { |
91 | 105 | const inputIndex = extractInputIndexFromScenario(scenarioId); |
92 | | - logConsoleLogStatement(log, executedDebugSteps, artifact, inputIndex, vm); |
| 106 | + logConsoleLogStatement(logEntry, decodedLogData, artifact.contractName, inputIndex); |
93 | 107 | } |
94 | 108 | } |
95 | 109 |
|
@@ -221,20 +235,28 @@ const createProgram = (template: WalletTemplate, unlockingScriptId: string, scen |
221 | 235 |
|
222 | 236 | const logConsoleLogStatement = ( |
223 | 237 | log: LogEntry, |
224 | | - debugSteps: AuthenticationProgramStateCommon[], |
225 | | - artifact: Artifact, |
| 238 | + decodedLogData: Array<string | bigint | boolean>, |
| 239 | + contractName: string, |
226 | 240 | inputIndex: number, |
227 | | - vm: VM, |
228 | 241 | ): void => { |
229 | | - let line = `${artifact.contractName}.cash:${log.line}`; |
230 | | - const decodedData = log.data.map((element) => { |
231 | | - if (typeof element === 'string') return element; |
| 242 | + console.log(`[Input #${inputIndex}] ${contractName}.cash:${log.line} ${decodedLogData.join(' ')}`); |
| 243 | +}; |
232 | 244 |
|
233 | | - const debugStep = debugSteps.find((step) => step.ip === element.ip)!; |
234 | | - const transformedDebugStep = applyStackItemTransformations(element, debugStep, vm); |
235 | | - return decodeStackItem(element, transformedDebugStep.stack); |
236 | | - }); |
237 | | - console.log(`[Input #${inputIndex}] ${line} ${decodedData.join(' ')}`); |
| 245 | +const decodeLogDataEntry = ( |
| 246 | + dataEntry: LogData, |
| 247 | + reversedPriorDebugSteps: AuthenticationProgramStateCommon[], |
| 248 | + vm: VM, |
| 249 | +): string | bigint | boolean => { |
| 250 | + if (typeof dataEntry === 'string') return dataEntry; |
| 251 | + |
| 252 | + const dataEntryDebugStep = reversedPriorDebugSteps.find((step) => step.ip === dataEntry.ip); |
| 253 | + |
| 254 | + if (!dataEntryDebugStep) { |
| 255 | + throw new Error(`Should not happen: corresponding data entry debug step not found for entry at ip ${dataEntry.ip}`); |
| 256 | + } |
| 257 | + |
| 258 | + const transformedDebugStep = applyStackItemTransformations(dataEntry, dataEntryDebugStep, vm); |
| 259 | + return decodeStackItem(dataEntry, transformedDebugStep.stack); |
238 | 260 | }; |
239 | 261 |
|
240 | 262 | const applyStackItemTransformations = ( |
@@ -267,7 +289,7 @@ const applyStackItemTransformations = ( |
267 | 289 | return transformationsEndState; |
268 | 290 | }; |
269 | 291 |
|
270 | | -const decodeStackItem = (element: StackItem, stack: Uint8Array[]): any => { |
| 292 | +const decodeStackItem = (element: StackItem, stack: Uint8Array[]): string | bigint | boolean => { |
271 | 293 | // Reversed since stack is in reverse order |
272 | 294 | const stackItem = [...stack].reverse()[element.stackIndex]; |
273 | 295 |
|
|
0 commit comments