-
Notifications
You must be signed in to change notification settings - Fork 1.5k
[Feature] Dynamic dispatch. #3062
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: staging
Are you sure you want to change the base?
Conversation
| let child_function_id = | ||
| compute_function_id(&U16::new(N::ID), child_transition.program_id(), child_transition.function_name())?; | ||
|
|
||
| let call_dynamic_instruction_opt = match parent_function_call { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better variable name
| if is_dynamic { | ||
| verifier_inputs.extend(child_transition.program_id().to_fields()?.into_iter().map(|field| *field)); | ||
| verifier_inputs.extend([*child_transition.function_name().to_field()?]); | ||
| verifier_inputs.extend([*compute_function_id( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re-use the function ID.
| // Note: call_dynamic_instruction_opt.unwrap() cannot panic by construction (is_dynamic is true). | ||
| let call_dynamic_instruction = call_dynamic_instruction_opt.clone().unwrap(); | ||
| let operand_types = call_dynamic_instruction.operand_types(); | ||
| for (i, (input, input_type)) in child_transition.inputs().iter().zip(operand_types.iter()).enumerate() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Length equality check and then zip_eq.
| ); | ||
| // Use the dynamic ID if present, otherwise use normal verifier inputs. | ||
| match input.dynamic_id() { | ||
| Some(dynamic_id) => verifier_inputs.push(**dynamic_id), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Explicitly check that Input is not Record or ExternalRecord.
| } else { | ||
| verifier_inputs.extend(child_transition.inputs().iter().flat_map(|input| input.verifier_inputs())); | ||
| } | ||
|
|
||
| // [Inputs] Extend the verifier inputs with the output IDs of the external call. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| // [Inputs] Extend the verifier inputs with the output IDs of the external call. | |
| // [Outputs] Extend the verifier inputs with the output IDs of the external call. |
| ); | ||
| // Use the dynamic ID if present, otherwise use the output id. | ||
| match output.dynamic_id() { | ||
| Some(dynamic_id) => verifier_inputs.push(**dynamic_id), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Explicitly enumerate the output variants.
| }; | ||
| verifier_inputs.push(*dynamic_future_id); | ||
| } | ||
| _ => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Enumerate the match case.
| @@ -288,24 +491,32 @@ impl<N: Network> Process<N> { | |||
| // In order to reconstruct the call graph, we: | |||
| // - Iterate over the call structure in reverse post-order. The ordering is maintained by the `traversal_stack`. | |||
| // - Process each transition in the `Execution` in reverse, assigning its transition ID to the corresponding function call. | |||
| pub fn construct_call_graph( | |||
| pub fn construct_call_graph<'a>( | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@d0cd Give this a manual review.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we exhaustively test?
| /// | ||
| /// Input: A valid call graph as created by `fn construct_call_graph`. | ||
| /// Input: mapping from transition IDs to locators, in the same order of the transitions in the execution. | ||
| pub fn ensure_acyclic_call_graph( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove
| ensure!(function.input_variants() == fee_input_variants, "The fee input variants do not match"); | ||
| ensure!(function.output_variants() == fee_output_variants, "The fee output variants do not match"); | ||
| ensure!(function.input_types().len() == fee.inputs().len(), "The number of fee inputs is incorrect"); | ||
| for (function_input, fee_input) in function.input_types().iter().zip(fee.inputs().iter()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
zip_eq for both.
| @@ -105,3 +105,9 @@ impl<N: Network> Deref for ProvingKey<N> { | |||
| &self.proving_key | |||
| } | |||
| } | |||
|
|
|||
| impl<N: Network> PartialEq for ProvingKey<N> { | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove.
synthesizer/src/vm/mod.rs
Outdated
| @@ -2501,6 +2504,8 @@ finalize transfer_public_to_private: | |||
| // Get the address of the wrapper program. | |||
| let wrapper_program_id = ProgramID::from_str("credits_wrapper.aleo").unwrap(); | |||
|
|
|||
| println!("PRINT STRART"); | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Delete
…lve_dynamic_dispatch_todos
[Review] Address feedback on dynamic dispatch.
…lve_dynamic_dispatch_todos
Resolve dynamic dispatch todos
TODO
credits.aleorecord translation keys.mainnet: 81 programs, 117 records totaltestnet: 1097 programs, 1790 records totalcanary: 3 programs, 3 records totalFor Reviewers
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
datafield 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
dynamicflag and methodscaller_inputs()andcaller_input_ids()that convert record inputs to their dynamic representations.Transition (
ledger/block/src/transition/) is now versioned.V2 transitions include
TransitionCallerMetadatacontaining 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)CallDynamicimplements theevaluate()andexecute()methods of theCallTrait.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_privateandfee_publicare blocked.The
collect_input_translations()andcollect_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
Translationstruct insynthesizer/process/src/trace/translation/mod.rsgenerates 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/)call_dynamic.rsdynamic_futures.rsdynamic_mapping_operations.rsget_record_dynamic.rstranslation.rsrecursion.rscast.rsDynamicRecord::from_record()consistency, external and non-external record castingmixed.rsAppended: an overview of semver breaking changes from commit 44644ef
semver_checks_results.txt