Skip to content

[Feature] Dynamic dispatch.#3062

Merged
vicsn merged 373 commits intostagingfrom
feat/dynamic-dispatch
Mar 15, 2026
Merged

[Feature] Dynamic dispatch.#3062
vicsn merged 373 commits intostagingfrom
feat/dynamic-dispatch

Conversation

@d0cd
Copy link
Copy Markdown
Collaborator

@d0cd d0cd commented Dec 11, 2025

TODO

  • Resolve merge conflicts after all V13 changes are merged.
  • Complete the VK migration PR.
  • Complete the field-based identifier PR
  • Prepare the deployment cannon.
  • Generate redeployments and check if any of the new constraint counts exceed the current MAX.
  • Upload parameters for the credits.aleo record translation keys.
  • Dynamic futures should be displayed on the Explorer in a human readable way.
  • Update developer docs.
  • Run regressions.
    • Number of programs with records
      • mainnet: 81 programs, 117 records total
      • testnet: 1097 programs, 1790 records total
      • canary: 3 programs, 3 records total
    • The modified cost calculation produces the same cost as snarkVM 4.4.0
    • Sync test from genesis to tip

For Reviewers

  • Is local record consumption the expected behavior in recursive calls?

You may find these questions in context by grepping for @reviewers.

Summary

Implements ARC-0009: Dynamic Dispatch for ConsensusVersion::V14.


Implementation Overview

1. Console Data Types (console/program/src/data/dynamic/)

DynamicRecord represents a static record as a fixed-size Merkle commitment.
It stores the owner, a Merkle root of the record entries, the nonce, and the version.
The from_record() method builds a depth-5 tree (max 32 entries) using Poseidon8 for leaf hashes and Poseidon2 for path nodes.
The optional data field holds the full entry map when available for extraction.

DynamicFuture represents a future as a fixed-size Merkle commitment.
It stores the program name, network, and function name as field elements, plus a Merkle root of the arguments.
The from_future() method builds a depth-4 tree (max 16 arguments) using the same hash scheme.
Field encoding of identifiers enables runtime resolution without variable-size strings.

2. Circuit Types (circuit/program/src/data/dynamic/)

circuit::DynamicRecord mirrors the console type with circuit-native field representations.
It optionally stores the console data for ejection.
The from_record() method performs in-circuit Merkle tree construction.
The find() method verifies a Merkle path to a specific entry and returns the entry value.

circuit::DynamicFuture mirrors the console type for circuit verification.
It optionally stores the console arguments for ejection.

3. Request and Transition Versioning

Request (console/program/src/request/) is now versioned.
V1 requests represent static calls with no dynamic flag.
V2 requests add an optional dynamic flag and methods caller_inputs() and caller_input_ids() that convert record inputs to their dynamic representations.

Transition (ledger/block/src/transition/) is now versioned.
V2 transitions include TransitionCallerMetadata containing the caller's view of inputs and outputs.
This metadata is essential because the caller sees static records while the callee sees dynamic records.
Translation proofs use this dual view to verify record equivalence.

Both versions of requests and transitions are supported.

4. Instructions (synthesizer/program/src/logic/instruction/operation/)

call.dynamic (call/dynamic.rs) enables runtime function dispatch.
The first three operands specify the program name, network, and function name as field elements.
Subsequent operands are the function arguments.
Records are automatically converted to dynamic records at call boundaries.
Destinations receive plaintexts, dynamic records, or dynamic futures.

get.record.dynamic (get_record_dynamic.rs) extracts an entry from a dynamic record.
In evaluate mode, it retrieves the entry from the data map.
In execute mode, it constructs the expected leaf hash and verifies a Merkle path against the record's root.

5. Execution (synthesizer/process/src/stack/call/dynamic.rs)

CallDynamic implements the evaluate() and execute() methods of the CallTrait.
The implementation is similar in structure to the Call, however it does differ non-trivially.
The resolve_dynamic_target() method converts field operands to a program ID and function name.
Dynamic calls to credits.aleo/fee_private and fee_public are blocked.
The collect_input_translations() and collect_output_translations() methods identify records that require translation proofs.

6. Translation Proofs (synthesizer/process/src/trace/translation/)

Translation proofs verify that a dynamic record and static record represent the same underlying data.

RecordTranslationData captures the metadata for one translation: both record forms, the function ID, whether it is an input or output, and the computed IDs for each form.

TranslationAssignment (assignment.rs) defines the translation circuit.
The circuit proves that the owner, nonce, and version match, and that the Merkle root of the static record entries equals the dynamic record's root.

prepare.rs handles key synthesis for translation circuits, with separate paths for regular calls and fee operations.

7. Varuna Integration (algorithms/src/snark/varuna/)

Translation proofs are batched with execution proofs in prove_batch_with_terminator().
The Translation struct in synthesizer/process/src/trace/translation/mod.rs generates verifier inputs by matching caller and callee views across transitions.

8. Dynamic Mapping Operations (synthesizer/program/src/logic/command/)

These finalize-time commands access mappings using runtime-resolved program IDs.

contains.dynamic checks if a key exists in an external mapping.
get.dynamic retrieves a value from an external mapping, failing if the key is absent.
get.or_use.dynamic retrieves a value with a fallback default if the key is absent.

Each command takes field operands for the program name, network, and mapping name, plus the key operand.


Tests (synthesizer/src/vm/tests/test_v14/)

File Coverage
call_dynamic.rs Call patterns including credits.aleo transfers, AMM swaps, conditional execution, deep call hierarchies, circular calls, fee function restrictions, and closure rejection
dynamic_futures.rs Await ordering, nested futures, finalize failures, type/mode mismatches, double-await detection, and skipped await detection
dynamic_mapping_operations.rs External and local mapping access, type validation, non-existent mappings, and struct values
get_record_dynamic.rs Polymorphic entry extraction, Merkle verification, boundary indices, and credits.aleo record handling
translation.rs Input/output translations, chained translations, key differentiation across programs, and tampered metadata detection
recursion.rs Self-recursive calls and double-spend detection across static/dynamic record consumption patterns
cast.rs Circuit and console DynamicRecord::from_record() consistency, external and non-external record casting
mixed.rs Cost estimation with translation proofs, combined translation/cast/extraction scenarios

Appended: an overview of semver breaking changes from commit 44644ef
semver_checks_results.txt

Antonio95 and others added 30 commits November 21, 2025 23:58
…ng dynamic calls, restrict input types for dynamic calls
… prepare-verifier-inputs; prevoius translation tests working again
…ecord_view_key and gamma to Option; minor polishing
d0cd and others added 10 commits March 4, 2026 12:58
Re-add defense-in-depth check that compares the number of translation
inputs derived from the authorization against those computed during
verification, ensuring they agree before dispatching to the verifier.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@vicsn vicsn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Comment thread console/program/src/data/dynamic/future/bytes.rs
Comment thread circuit/program/src/data/dynamic/future/mod.rs
Comment thread synthesizer/process/src/stack/call/dynamic.rs
@vicsn vicsn merged commit 85e53fb into staging Mar 15, 2026
5 of 7 checks passed
@vicsn vicsn deleted the feat/dynamic-dispatch branch March 15, 2026 07:42
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.

7 participants