mirror of
https://github.com/morluto/rea
synced 2026-10-26 22:28:27 -04:00
7af7bc88ce
* feat(evm): inspect offline bytecode interfaces through evmole * fix(evm): distinguish unsupported bytecode targets * style(evm): format verifier string-prefix checks
93 lines
3.3 KiB
TypeScript
93 lines
3.3 KiB
TypeScript
import { isAbsolute } from "node:path";
|
|
import { z } from "zod";
|
|
import type { ExecutionOptions } from "../AnalysisProvider.js";
|
|
import type { EvmInterfacePort } from "./EvmInterfacePort.js";
|
|
import type { AnalysisError } from "../../domain/analysisErrorBase.js";
|
|
import {
|
|
AnalysisCancelledError,
|
|
AnalysisInputError,
|
|
AnalysisOutputError,
|
|
} from "../../domain/analysisErrorCore.js";
|
|
import { createEvidence, type Evidence } from "../../domain/evidence.js";
|
|
import {
|
|
inspectEvmInterfaceInputSchema,
|
|
evmInterfaceSchema,
|
|
} from "../../domain/evm/evmInterface.js";
|
|
import { projectInputIssues } from "../../domain/inputIssueProjection.js";
|
|
import { err, ok, type Result } from "../../domain/result.js";
|
|
|
|
const OPERATION = "inspect_evm_interface";
|
|
const diagnosticsSchema = z.object({
|
|
diagnostics: evmInterfaceSchema.shape.diagnostics,
|
|
});
|
|
|
|
/** Shared carrier validation and typed inline Evidence for CLI/MCP. */
|
|
export class EvmInterfaceService {
|
|
constructor(readonly provider: EvmInterfacePort) {}
|
|
|
|
/** Preserve selected byte identity while clearly labeling recovered ABI candidates as inferences. */
|
|
async inspect(
|
|
rawInput: unknown,
|
|
options?: ExecutionOptions,
|
|
): Promise<Result<Evidence, AnalysisError>> {
|
|
if (options?.signal?.aborted)
|
|
return err(new AnalysisCancelledError(OPERATION));
|
|
const input = inspectEvmInterfaceInputSchema.safeParse(rawInput);
|
|
if (!input.success)
|
|
return err(
|
|
new AnalysisInputError(
|
|
OPERATION,
|
|
{ cause: input.error },
|
|
projectInputIssues(input.error.issues, rawInput),
|
|
),
|
|
);
|
|
if (!isAbsolute(input.data.path))
|
|
return err(
|
|
new AnalysisInputError(OPERATION, undefined, [
|
|
{
|
|
path: ["path"],
|
|
reason: "invalid_format",
|
|
message: "Expected an absolute filesystem path on this host.",
|
|
},
|
|
]),
|
|
);
|
|
const inspected = await this.provider.inspect(input.data, options);
|
|
if (!inspected.ok) return inspected;
|
|
const diagnostics = diagnosticsSchema.safeParse(inspected.value.result);
|
|
const outputOptions = diagnostics.success
|
|
? { capturedOutput: diagnostics.data.diagnostics }
|
|
: undefined;
|
|
if (options?.signal?.aborted)
|
|
return err(new AnalysisCancelledError(OPERATION, outputOptions));
|
|
const execution = inspected.value;
|
|
const report = evmInterfaceSchema.safeParse(execution.result);
|
|
if (
|
|
!report.success ||
|
|
report.data.artifact.path !== input.data.path ||
|
|
report.data.artifact.encoding !== input.data.encoding ||
|
|
execution.subject?.path !== input.data.path ||
|
|
execution.subject.sha256 !== report.data.artifact.sha256
|
|
)
|
|
return err(
|
|
new AnalysisOutputError(
|
|
OPERATION,
|
|
"Interface adapter returned malformed data or changed the selected carrier identity/encoding.",
|
|
outputOptions,
|
|
),
|
|
);
|
|
return ok(
|
|
createEvidence(execution.subject, execution.provider, {
|
|
operation: OPERATION,
|
|
parameters: input.data,
|
|
result: report.data,
|
|
...(execution.rawResult === null
|
|
? {}
|
|
: { rawResult: execution.rawResult }),
|
|
confidence: "inferred",
|
|
locations: execution.locations,
|
|
limitations: execution.limitations,
|
|
}),
|
|
);
|
|
}
|
|
}
|