Skip to content

Commit 8038932

Browse files
authored
Merge pull request #2596 from ProvableHQ/fix/rng-finalize
[Fix] Selection of transition ID in finalize.
2 parents 53cab07 + 7f40f99 commit 8038932

File tree

11 files changed

+379
-30
lines changed

11 files changed

+379
-30
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -702,7 +702,7 @@ jobs:
702702
resource_class: << pipeline.parameters.twoxlarge >>
703703
steps:
704704
- run_serial:
705-
flags: --test '*' -- --test-threads=8
705+
flags: --test '*' --features test -- --test-threads=8
706706
workspace_member: synthesizer
707707
cache_key: v1.0.0-rust-1.81.0-snarkvm-synthesizer-integration-cache
708708

synthesizer/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ serial = [
4444
"synthesizer-snark/serial"
4545
]
4646
setup = [ ]
47-
test = [ "console/test" ]
47+
test = [ "console/test", "ledger-block/test", "ledger-store/test" ]
4848
timer = [ "aleo-std/timer" ]
4949
wasm = [
5050
"process",

synthesizer/process/src/finalize.rs

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,11 @@ impl<N: Network> Process<N> {
104104
lap!(timer, "Verify the number of transitions");
105105

106106
// Construct the call graph.
107-
let call_graph = self.construct_call_graph(execution)?;
107+
// If the height is greater than or equal to `CONSENSUS_V3_HEIGHT`, then provide an empty call graph, as it is no longer used during finalization.
108+
let call_graph = match state.block_height() < N::CONSENSUS_V3_HEIGHT {
109+
true => self.construct_call_graph(execution)?,
110+
false => HashMap::new(),
111+
};
108112

109113
atomic_batch_scope!(store, {
110114
// Finalize the root transition.
@@ -160,9 +164,11 @@ fn finalize_fee_transition<N: Network, P: FinalizeStorage<N>>(
160164
fee: &Fee<N>,
161165
) -> Result<Vec<FinalizeOperation<N>>> {
162166
// Construct the call graph.
163-
let mut call_graph = HashMap::new();
164-
// Insert the fee transition.
165-
call_graph.insert(*fee.transition_id(), Vec::new());
167+
// If the height is greater than or equal to `CONSENSUS_V3_HEIGHT`, then provide an empty call graph, as it is no longer used during finalization.
168+
let call_graph = match state.block_height() < N::CONSENSUS_V3_HEIGHT {
169+
true => HashMap::from([(*fee.transition_id(), Vec::new())]),
170+
false => HashMap::new(),
171+
};
166172

167173
// Finalize the transition.
168174
match finalize_transition(state, store, stack, fee, call_graph) {
@@ -208,8 +214,12 @@ fn finalize_transition<N: Network, P: FinalizeStorage<N>>(
208214
// Initialize a stack of active finalize states.
209215
let mut states = Vec::new();
210216

217+
// Initialize a nonce for the finalize registers.
218+
// Note that this nonce must be unique for each sub-transition being finalized.
219+
let mut nonce = 0;
220+
211221
// Initialize the top-level finalize state.
212-
states.push(initialize_finalize_state(state, future, stack, *transition.id())?);
222+
states.push(initialize_finalize_state(state, future, stack, *transition.id(), nonce)?);
213223

214224
// While there are active finalize states, finalize them.
215225
'outer: while let Some(FinalizeState {
@@ -263,20 +273,31 @@ fn finalize_transition<N: Network, P: FinalizeStorage<N>>(
263273
await_.register()
264274
);
265275

266-
// Get the current transition ID.
267-
let transition_id = registers.transition_id();
268-
// Get the child transition ID.
269-
let child_transition_id = match call_graph.get(transition_id) {
270-
Some(transitions) => match transitions.get(call_counter) {
271-
Some(transition_id) => *transition_id,
272-
None => bail!("Child transition ID not found."),
273-
},
274-
None => bail!("Transition ID '{transition_id}' not found in call graph"),
276+
// Get the transition ID used to initialize the finalize registers.
277+
// If the block height is greater than or equal to `CONSENSUS_V3_HEIGHT`, then use the top-level transition ID.
278+
// Otherwise, query the call graph for the child transition ID corresponding to the future that is being awaited.
279+
let transition_id = match state.block_height() < N::CONSENSUS_V3_HEIGHT {
280+
true => {
281+
// Get the current transition ID.
282+
let transition_id = registers.transition_id();
283+
// Get the child transition ID.
284+
match call_graph.get(transition_id) {
285+
Some(transitions) => match transitions.get(call_counter) {
286+
Some(transition_id) => *transition_id,
287+
None => bail!("Child transition ID not found."),
288+
},
289+
None => bail!("Transition ID '{transition_id}' not found in call graph"),
290+
}
291+
}
292+
false => *transition.id(),
275293
};
276294

295+
// Increment the nonce.
296+
nonce += 1;
297+
277298
// Set up the finalize state for the await.
278299
let callee_state =
279-
match try_vm_runtime!(|| setup_await(state, await_, stack, &registers, child_transition_id)) {
300+
match try_vm_runtime!(|| setup_await(state, await_, stack, &registers, transition_id, nonce)) {
280301
Ok(Ok(callee_state)) => callee_state,
281302
// If the evaluation fails, bail and return the error.
282303
Ok(Err(error)) => bail!("'finalize' failed to evaluate command ({command}): {error}"),
@@ -357,6 +378,7 @@ fn initialize_finalize_state<'a, N: Network>(
357378
future: &Future<N>,
358379
stack: &'a Stack<N>,
359380
transition_id: N::TransitionID,
381+
nonce: u64,
360382
) -> Result<FinalizeState<'a, N>> {
361383
// Get the finalize logic and the stack.
362384
let (finalize, stack) = match stack.program_id() == future.program_id() {
@@ -381,6 +403,7 @@ fn initialize_finalize_state<'a, N: Network>(
381403
transition_id,
382404
*future.function_name(),
383405
stack.get_finalize_types(future.function_name())?.clone(),
406+
nonce,
384407
);
385408

386409
// Store the inputs.
@@ -402,14 +425,15 @@ fn setup_await<'a, N: Network>(
402425
stack: &'a Stack<N>,
403426
registers: &FinalizeRegisters<N>,
404427
transition_id: N::TransitionID,
428+
nonce: u64,
405429
) -> Result<FinalizeState<'a, N>> {
406430
// Retrieve the input as a future.
407431
let future = match registers.load(stack, &Operand::Register(await_.register().clone()))? {
408432
Value::Future(future) => future,
409433
_ => bail!("The input to 'await' is not a future"),
410434
};
411435
// Initialize the state.
412-
initialize_finalize_state(state, &future, stack, transition_id)
436+
initialize_finalize_state(state, &future, stack, transition_id, nonce)
413437
}
414438

415439
// A helper function that returns the index to branch to.

synthesizer/process/src/stack/finalize_registers/mod.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ pub struct FinalizeRegisters<N: Network> {
4646
finalize_types: FinalizeTypes<N>,
4747
/// The mapping of assigned registers to their values.
4848
registers: IndexMap<u64, Value<N>>,
49+
/// A nonce for finalize registers.
50+
nonce: u64,
4951
/// The tracker for the last register locator.
5052
last_register: Option<u64>,
5153
}
@@ -58,8 +60,17 @@ impl<N: Network> FinalizeRegisters<N> {
5860
transition_id: N::TransitionID,
5961
function_name: Identifier<N>,
6062
finalize_types: FinalizeTypes<N>,
63+
nonce: u64,
6164
) -> Self {
62-
Self { state, transition_id, finalize_types, function_name, registers: IndexMap::new(), last_register: None }
65+
Self {
66+
state,
67+
transition_id,
68+
finalize_types,
69+
function_name,
70+
registers: IndexMap::new(),
71+
nonce,
72+
last_register: None,
73+
}
6374
}
6475
}
6576

@@ -81,4 +92,10 @@ impl<N: Network> FinalizeRegistersState<N> for FinalizeRegisters<N> {
8192
fn function_name(&self) -> &Identifier<N> {
8293
&self.function_name
8394
}
95+
96+
/// Returns the nonce for the finalize registers.
97+
#[inline]
98+
fn nonce(&self) -> u64 {
99+
self.nonce
100+
}
84101
}

synthesizer/process/src/stack/register_types/initialize.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ impl<N: Network> RegisterTypes<N> {
158158
}
159159

160160
/* Additional checks. */
161-
// - All futures produces before the `async` call must be consumed by the `async` call.
161+
// - All futures produced before the `async` call must be consumed by the `async` call.
162162

163163
// Get all registers containing futures.
164164
let mut future_registers: IndexSet<(Register<N>, Locator<N>)> = register_types

synthesizer/program/src/logic/command/rand_chacha.rs

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,29 @@ impl<N: Network> RandChaCha<N> {
8989
let seeds: Vec<_> = self.operands.iter().map(|operand| registers.load(stack, operand)).try_collect()?;
9090

9191
// Construct the random seed.
92-
let preimage = to_bits_le![
93-
registers.state().random_seed(),
94-
**registers.transition_id(),
95-
stack.program_id(),
96-
registers.function_name(),
97-
self.destination.locator(),
98-
self.destination_type.type_id(),
99-
seeds
100-
];
92+
// If the height is greater than or equal to `CONSENSUS_V3_HEIGHT`, then use the new preimage definition.
93+
// The difference is that a nonce is also included in the new definition.
94+
let preimage = match registers.state().block_height() < N::CONSENSUS_V3_HEIGHT {
95+
true => to_bits_le![
96+
registers.state().random_seed(),
97+
**registers.transition_id(),
98+
stack.program_id(),
99+
registers.function_name(),
100+
self.destination.locator(),
101+
self.destination_type.type_id(),
102+
seeds
103+
],
104+
false => to_bits_le![
105+
registers.state().random_seed(),
106+
**registers.transition_id(),
107+
stack.program_id(),
108+
registers.function_name(),
109+
registers.nonce(),
110+
self.destination.locator(),
111+
self.destination_type.type_id(),
112+
seeds
113+
],
114+
};
101115

102116
// Hash the preimage.
103117
let digest = N::hash_bhp1024(&preimage)?.to_bytes_le()?;

synthesizer/program/src/traits/stack_and_registers.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ pub trait FinalizeRegistersState<N: Network> {
123123

124124
/// Returns the function name for the finalize scope.
125125
fn function_name(&self) -> &Identifier<N>;
126+
127+
/// Returns the nonce for the finalize registers.
128+
fn nonce(&self) -> u64;
126129
}
127130

128131
pub trait RegistersSigner<N: Network> {

synthesizer/program/tests/helpers/sample.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ pub fn sample_finalize_registers(
7373
<CurrentNetwork as Network>::TransitionID::default(),
7474
*function_name,
7575
stack.get_finalize_types(function_name)?.clone(),
76+
0u64,
7677
);
7778

7879
// For each literal,

0 commit comments

Comments
 (0)