Skip to content

feat(external-call): engine execution, model conformance and validation SPI - #23

Draft
angelol wants to merge 16 commits into
angelol/external-call-06-split-pr3-interpfrom
angelol/external-call-06-split-pr4-damle-mcc
Draft

feat(external-call): engine execution, model conformance and validation SPI#23
angelol wants to merge 16 commits into
angelol/external-call-06-split-pr3-interpfrom
angelol/external-call-06-split-pr4-damle-mcc

Conversation

@angelol

@angelol angelol commented Jun 30, 2026

Copy link
Copy Markdown

Review-only — do not merge. This is the clean per-PR diff for upstream review, stacked on angelol/external-call-06-split-pr3-interp (PR 3). The mergeable upstream PR (targets main) opens after digital-asset#537 lands. Part of the 8-PR split that supersedes digital-asset#541.

Summary

This is PR 4 of 8 in the external-call runtime-integration series tracked in digital-asset#513, stacked on PR 3.

The external-call stack (digital-asset#513): digital-asset#506 added the transaction-side representation for recording external-call results; digital-asset#514 added the LF EXTERNAL_CALL builtin surface; digital-asset#518 wired it into Speedy and the LF engine; digital-asset#522 added transaction protobuf encoding/decoding; digital-asset#526 added Canton protocol serialization for recorded results; digital-asset#537 added the participant-side extension-service client. digital-asset#541 implemented the remaining runtime integration as one PR; this series splits digital-asset#541 into 8 independently reviewable PRs (each < 1000 lines) and supersedes it.

This PR adds the engine-side replay of recorded external-call results and the validation SPI used during confirmation.

Scope

This PR adds:

  • DAMLe reinterpretation support for replaying recorded external-call results, with ExternalCallPayloadDescription
  • the ExternalCallValidator SPI trait and ExternalCallValidationError
  • ModelConformanceChecker integration that replays recorded results during conformance checking
  • engine, model-conformance and DAMLe tests

Out of scope

The concrete validator implementation (PR 6), the extension-service runtime (PR 5), consistency checking and routing (PR 6–7), and activation of validation in transaction processing (PR 8).

Refs digital-asset#513.

@angelol
angelol force-pushed the angelol/external-call-06-split-pr3-interp branch from 5fb3a81 to e2cd4bd Compare June 30, 2026 12:19
@angelol
angelol force-pushed the angelol/external-call-06-split-pr4-damle-mcc branch from b22f73b to bcd36d4 Compare June 30, 2026 12:19
@angelol
angelol force-pushed the angelol/external-call-06-split-pr3-interp branch from e2cd4bd to 62d58c0 Compare June 30, 2026 14:33
@angelol
angelol force-pushed the angelol/external-call-06-split-pr4-damle-mcc branch 2 times, most recently from 1c261f2 to 7dc5bcb Compare June 30, 2026 16:31
@angelol
angelol force-pushed the angelol/external-call-06-split-pr3-interp branch 2 times, most recently from 85b4627 to 9afe465 Compare July 1, 2026 19:21
@angelol
angelol force-pushed the angelol/external-call-06-split-pr4-damle-mcc branch from 106c4d7 to 3776335 Compare July 1, 2026 19:26
@angelol
angelol force-pushed the angelol/external-call-06-split-pr3-interp branch from 04bbe6c to 5ddbc6f Compare July 3, 2026 07:17
@angelol
angelol force-pushed the angelol/external-call-06-split-pr4-damle-mcc branch from 3f92db5 to 2bda8c0 Compare July 3, 2026 07:53
angelol and others added 15 commits July 3, 2026 10:53
…SPI (digital-asset#551)

## Summary

This is PR 3 of 8 in the external-call runtime-integration series tracked in digital-asset#513, stacked on PR 2.

The external-call stack (digital-asset#513): digital-asset#506 added the transaction-side representation for recording external-call results; digital-asset#514 added the LF `EXTERNAL_CALL` builtin surface; digital-asset#518 wired it into Speedy and the LF engine; digital-asset#522 added transaction protobuf encoding/decoding; digital-asset#526 added Canton protocol serialization for recorded results; digital-asset#537 added the participant-side extension-service client. digital-asset#541 implemented the remaining runtime integration as one PR; this series splits digital-asset#541 into 8 independently reviewable PRs (each < 1000 lines) and supersedes it.

This PR adds the command-execution error surface and the handler service-provider interface (SPI) that later PRs implement and wire in.

## Scope

This PR adds:

- external-call-aware command-execution errors (`CommandExecutionErrors`) and the `ErrorResource` metadata for them
- rejection generators for external-call failures (`RejectionGenerators`)
- the external-call handler SPI (`ExternalCallHandler`, `ExternalCallMode`) — the interface used by command interpretation to dispatch engine external-call questions
- tests for rejection generation, request validation, and the LF/Speedy external-call surface

## Out of scope

The concrete extension-service handler implementation and its wiring into command execution (PR 5), engine replay (PR 4), and validation/routing/factory (PR 6–8). The SPI ships with a no-op `Unsupported` handler; nothing dispatches to a real service yet.

## Stacking & review

This PR is stacked on PR 2, so its diff against `main` is **cumulative**. The incremental change for this PR alone:
digital-asset/canton@zenith-network:angelol/external-call-06-split-pr2-codec...zenith-network:angelol/external-call-06-split-pr3-interp

Refs digital-asset#513.
…ining

The inlining was incidental and unrelated to the external-call feature;
reverting it keeps the PR diff focused.
The test feeds two different outputs for one input identity, so "conflicting"
(not "duplicate") is accurate; the new name also matches its non-leakage
assertions rather than "without choosing one".
…e results type

ExternalCallReplayData was a redundant single-field wrapper around
StoredExternalCallResults, which held all the logic. Fold that logic into
ExternalCallReplayData and delete the separate results type; behavior unchanged.
…ay-data thunk

The reinterpret SPI's externalCallReplayData thunk returned
FutureUnlessShutdown[ExternalCallReplayData], but every producer wrapped a
synchronous in-memory value in FutureUnlessShutdown.pure -- there is no async
source. Make the thunk () => ExternalCallReplayData and unwrap the future in
handleExternalCall accordingly; behavior unchanged.
… aggregation

ViewParticipantData.validated already rejects non-empty externalCallResults
below protocol version dev on every construction path, so the supports-guard
was dead; fromResults(empty) equals empty, so the isEmpty short-circuit was
too. Drop both and the now-unused viewParticipantData parameter.
ExternalCallKey implements PrettyPrinting (payload sizes only, never values),
so both replay-error prettys collapse to param("key", _.key). Also closes the
payload-leak channel through the key's default case-class toString.
resumeWithExternalCallOutput and replayStoredExternalCallOutput each had one
caller and formed a single chain; fold both into handleExternalCall.
The replayDataO parameter cached the externalCallReplayData thunk across
external calls within one reinterpretation, but the only production caller
already passes a lazy-val-backed thunk, so the internal memoization was
redundant. handleExternalCall now calls the thunk directly.
Replace the hardcoded dev-PV factory with the suite factory and
testedProtocolVersion, gated onlyRunWithOrGreaterThan(dev) like the sibling
tampered-metadata test, so the test no longer re-runs identically under every
protocol version. Add a counterpart below dev asserting the aggregated replay
data stays empty. Fold buildUnderTestWithFactory into its now-single caller.
Left behind by the earlier type-shortening rename; caught by
Test/scalafmtCheck, which the Compile-scoped check used before misses.
Follow-up to the earlier helper inlining: with those gone, this two-use
wrapper no longer pays for itself either.
@angelol
angelol force-pushed the angelol/external-call-06-split-pr4-damle-mcc branch from 2bda8c0 to 42bfe55 Compare July 3, 2026 15:36
No longer needed per review feedback on digital-asset#551.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants