Skip to content

feat(external-call): protocol and transaction-hash foundation - #20

Draft
angelol wants to merge 23 commits into
angelol/external-call-05-extension-service-redesignfrom
angelol/external-call-06-split-pr1-foundation
Draft

feat(external-call): protocol and transaction-hash foundation#20
angelol wants to merge 23 commits into
angelol/external-call-05-extension-service-redesignfrom
angelol/external-call-06-split-pr1-foundation

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-05-extension-service-redesign. 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 1 of 8 in the external-call runtime-integration series tracked in digital-asset#513, stacked on digital-asset#537.

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 lays the protocol and transaction-hash foundation the rest of the series builds on: the Canton protocol fields that carry recorded external-call results, and the HashingSchemeVersion V4 hashing that covers them.

Scope

This PR adds:

  • ViewParticipantData.externalCallResults and the supporting protocol additions (participant_transaction.proto, SubmitterMetadata, ExternalAuthorization, LocalRejectError), including the ImmArray → Seq migration of the recorded-results collection across the base/common/participant transaction-tree construction sites
  • HashingSchemeVersion V4 (dev-gated) and the v4 node/transaction hashers that fold recorded external-call result payloads into the transaction hash, with the payloads surfaced in the HashTracer output
  • the HASHING_SCHEME_VERSION_V4 Ledger API enum value and the matching SubmitRequestValidator case (required for -Werror match exhaustiveness once the enum exists)
  • the daml-lf transaction.proto field for recorded results
  • the regenerated protobuf snapshot and openapi.yaml (V4 enum), plus hash tests, generators and docs

Out of scope

The prepared-transaction codec (PR 2), command-execution wiring (PR 3), engine replay (PR 4), the extension-service runtime (PR 5+), consistency checking, routing and validation (PR 6–8). No LF semantics change. The feature is ProtocolVersion.dev-gated, so this PR is dormant on released protocol versions.

Hashing Boundary

Recorded external-call result payloads are Canton protocol data: after submission they are carried in ViewParticipantData.externalCallResults and protected by the Canton view signature. V4 hashing folds them into the Canton transaction hash on that basis. (Their exclusion from the LF/prepared node hash is covered in PR 2.)

Refs digital-asset#513.

angelol added 2 commits June 30, 2026 12:21
…set#537)

* refactor(external-call): redesign extension service branch

* fix(external-call): enforce HTTP request timeout

* fix(external-call): log HTTP error bodies at debug

* fix(external-call): make response body limit configurable

* fix(external-call): bound protocol header values

* fix(external-call): accept IPv6 service addresses

* fix(external-call): cancel completed HTTP timeouts
…chemeVersion V4)

Carves PR1 of the external-call split: the protocol/transaction-hash
foundation plus the ViewParticipantData.externalCallResults ImmArray->Seq
migration (construction sites in base, common, and participant).

Includes two hunks HashingSchemeVersion.V4 transitively requires to compile
standalone under -Werror (both byte-identical to the golden reference, so the
union of the split still equals it):
- interactive_submission_service.proto: HASHING_SCHEME_VERSION_V4 enum value
  (V4.toLedgerApiProto maps to ApiHashingSchemeVersion.HASHING_SCHEME_VERSION_V4).
- SubmitRequestValidator.scala: the matching V4 case, otherwise the enum makes
  the existing match non-exhaustive.

Also traces the external-call result config/input/output payloads in the V4
node hash builder: they were fed to the digest via the no-context
addByteString overload, so the HashTracer recorded only their length, not
their value. Passing a trace context makes the values appear in the
human-readable hash trace; the digest is unchanged (the context is trace-only),
so existing hash values are unaffected.

Proto snapshot regenerated fresh from PR1's tree (v32 messages + V4 enum;
excludes PR2's interactive_submission_data.proto additions). openapi.yaml
regenerated for the V4 enum.
@angelol
angelol force-pushed the angelol/external-call-06-split-pr1-foundation branch from 8255166 to b62f4f6 Compare June 30, 2026 12:19
angelol and others added 21 commits June 30, 2026 18:05
Review feedback on digital-asset#549: bind every field via `val ExternalCallResult(...) = result`
so that if a field is added to ExternalCallResult the binding stops compiling,
forcing a conscious decision about whether it is included in the hash. The hash
value is unchanged.
Review feedback on digital-asset#549: the shared (V2/V3) exercise-node encoding silently
ignored externalCallResults. It now fails (NodeHashingError.UnsupportedFeature)
when results are present, so they can never be dropped from the hash unnoticed.

Uses a virtual `supportsExternalCallResults` hook (default false; V4 overrides to
true) rather than reading `hashingSchemeVersion` in NodeHashBuilderCommon, where
that name resolves to the constructor parameter (V3) even for a V4 instance and
would wrongly reject V4. Adds a test that pre-V4 schemes reject results-bearing
exercise nodes.
…to <=100 cols

Review feedback on digital-asset#549: break the single long @resolution string into a
|-margin multi-line string (no explicit .stripMargin, matching the @explanation
above it).
Review feedback on digital-asset#549: 'Exercise nodes with version dev include ...'.
…tream wording

Review feedback on digital-asset#549 (rst:61): the prior 'stable vs development protocol'
wording was imprecise (singular dev version; non-standard 'development protocol'
term). Restored the original: every new Daml/LF version maps to a new hashing
version (LF V1->V2, V2->V3, dev->V4).
Review feedback on digital-asset#549 (rst:110): the line was not a change from V2 (it
restated V3's PV support + V4's external_call_results exclusion). V3's PV
support is in the table; external_call_results is covered in the V4 section.
…rding

Review feedback on digital-asset#549 (rst:125): removed the V4 statement from the V3 section
(V4 has its own section), restored the :ref: on V3, and dropped 'only' — per
HashingSchemeVersion, V3 is supported from v35 onwards (including dev), not v35 alone.
Review feedback on digital-asset#550 (rst exercise node): convert the exercise encoding to
sphinx tabs (V3/V4) like the 3.5 branch, replacing the free-text V4 note.
Dropped the V2-only '0x01 || # Node encoding version' line (removed in V3
onwards per the v2 hasher and 3.5's V3 tab) so both tabs are accurate.
… tabs

Review feedback on digital-asset#550: replace the inline 'hashing_scheme_version_byte (e.g.
0x03 for V3, 0x04 for V4)' with V3/V4 tabs (0x03/0x04), matching the 3.5 branch.
…in node hashing

Review feedback on digital-asset#550 (NodeHashBuilderCommon): key the reject on the node's LF
serialization version (external-call results are only valid on dev nodes),
mirroring PreparedTransactionDecoder, instead of a hashing-scheme capability hook.
Removes the supportsExternalCallResults hook and its V4 override; also catches the
invalid 'results on a pre-dev node' case the hook missed. V2/V3 still reject (they
only ever hash pre-dev nodes) and V4 is unaffected.
…(foundation)

digital-asset#557 refactored ViewParticipantData validation from an in-constructor block
into a validated()/checkX structure (incl. checkExternalCallResults).
Conflicts resolved by adopting digital-asset#557's structure and re-applying our changes:

- ViewParticipantData: keep digital-asset#557's validation refactor; re-apply the
  externalCallResults ImmArray->Seq migration; keep our supportsExternalCallResults
  helper (public API consumed by the runtime-integration PRs) and route digital-asset#557's
  checkExternalCallResults dev-PV gate through it (single source, same behavior).
- LegacyTransactionTreeFactory, ExampleTransactionFactory, TransactionViewTest:
  adopt digital-asset#557's call shape (single arg-list, keyResolution rename) with Seq.empty.
- .proto_snapshot_image.bin.gz: regenerated from the merged proto tree.
…' in V2-rejection assertions

The prepare/execute V2-rejection assertions expected 'Please using hashing scheme
V3 or higher' but the production message says 'Please use' (grammar fixed in the
transaction-hash foundation without updating these two integration assertions),
so both failed in CI. Align them with the actual message.
This file was only added to force a one-off full recompile in CI; it is not
present on the base branch, so removing it keeps the PR clean and returns CI to
incremental builds for this branch.
The full-message shouldBe added in c0f3fb5 had the +-continuation string
over-indented; align it per scalafmt so the format check passes.
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