Skip to content

Commit 69b9edf

Browse files
authored
Merge pull request #2628 from ProvableHQ/feat/global-stack-map
[Refactor] Global stack map.
2 parents 5b37dc7 + 7e9cd68 commit 69b9edf

File tree

22 files changed

+284
-269
lines changed

22 files changed

+284
-269
lines changed

synthesizer/process/benches/check_deployment.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ fn transfer_private(c: &mut Criterion) {
9292
let r2 = Value::<CurrentNetwork>::from_str("1_500_000_000_000_000_u64").unwrap();
9393

9494
// Compute the assignment.
95-
prepare_check_deployment::<_, CurrentAleo>(c, stack, &private_key, function_name, &[r0, r1, r2], rng);
95+
prepare_check_deployment::<_, CurrentAleo>(c, &stack, &private_key, function_name, &[r0, r1, r2], rng);
9696
}
9797

9898
fn transfer_public(c: &mut Criterion) {
@@ -115,7 +115,7 @@ fn transfer_public(c: &mut Criterion) {
115115
let r1 = Value::<CurrentNetwork>::from_str("1_500_000_000_000_000_u64").unwrap();
116116

117117
// Compute the assignment.
118-
prepare_check_deployment::<_, CurrentAleo>(c, stack, &private_key, function_name, &[r0, r1], rng);
118+
prepare_check_deployment::<_, CurrentAleo>(c, &stack, &private_key, function_name, &[r0, r1], rng);
119119
}
120120

121121
fn large_program(c: &mut Criterion) {

synthesizer/process/src/cost.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ pub fn execution_cost_v1<N: Network>(process: &Process<N>, execution: &Execution
8787

8888
// Get the finalize cost for the root transition.
8989
let stack = process.get_stack(transition.program_id())?;
90-
let finalize_cost = cost_in_microcredits_v1(stack, transition.function_name())?;
90+
let finalize_cost = cost_in_microcredits_v1(&stack, transition.function_name())?;
9191

9292
// Compute the total cost in microcredits.
9393
let total_cost = storage_cost
@@ -446,7 +446,7 @@ pub fn cost_in_microcredits_v1<N: Network>(stack: &Stack<N>, function_name: &Ide
446446
let stack = stack.get_external_stack(future.program_id())?;
447447
// Accumulate the finalize cost of the future.
448448
future_cost = future_cost
449-
.checked_add(cost_in_microcredits_v1(stack, future.resource())?)
449+
.checked_add(cost_in_microcredits_v1(&stack, future.resource())?)
450450
.ok_or(anyhow!("Finalize cost overflowed"))?;
451451
}
452452
}

synthesizer/process/src/finalize.rs

Lines changed: 56 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ impl<N: Network> Process<N> {
5454
// Retrieve the fee stack.
5555
let fee_stack = self.get_stack(fee.program_id())?;
5656
// Finalize the fee transition.
57-
finalize_operations.extend(finalize_fee_transition(state, store, fee_stack, fee)?);
57+
finalize_operations.extend(finalize_fee_transition(state, store, &fee_stack, fee)?);
5858
lap!(timer, "Finalize transition for '{}/{}'", fee.program_id(), fee.function_name());
5959

6060
/* Finalize the deployment. */
@@ -116,15 +116,15 @@ impl<N: Network> Process<N> {
116116
// Finalize the root transition.
117117
// Note that this will result in all the remaining transitions being finalized, since the number
118118
// of calls matches the number of transitions.
119-
let mut finalize_operations = finalize_transition(state, store, stack, transition, call_graph)?;
119+
let mut finalize_operations = finalize_transition(state, store, &stack, transition, call_graph)?;
120120

121121
/* Finalize the fee. */
122122

123123
if let Some(fee) = fee {
124124
// Retrieve the fee stack.
125125
let fee_stack = self.get_stack(fee.program_id())?;
126126
// Finalize the fee transition.
127-
finalize_operations.extend(finalize_fee_transition(state, store, fee_stack, fee)?);
127+
finalize_operations.extend(finalize_fee_transition(state, store, &fee_stack, fee)?);
128128
lap!(timer, "Finalize transition for '{}/{}'", fee.program_id(), fee.function_name());
129129
}
130130

@@ -150,7 +150,7 @@ impl<N: Network> Process<N> {
150150
// Retrieve the stack.
151151
let stack = self.get_stack(fee.program_id())?;
152152
// Finalize the fee transition.
153-
let result = finalize_fee_transition(state, store, stack, fee);
153+
let result = finalize_fee_transition(state, store, &stack, fee);
154154
finish!(timer, "Finalize transition for '{}/{}'", fee.program_id(), fee.function_name());
155155
// Return the result.
156156
result
@@ -162,7 +162,7 @@ impl<N: Network> Process<N> {
162162
fn finalize_fee_transition<N: Network, P: FinalizeStorage<N>>(
163163
state: FinalizeGlobalState,
164164
store: &FinalizeStore<N, P>,
165-
stack: &Stack<N>,
165+
stack: &Arc<Stack<N>>,
166166
fee: &Fee<N>,
167167
) -> Result<Vec<FinalizeOperation<N>>> {
168168
// Construct the call graph.
@@ -187,7 +187,7 @@ fn finalize_fee_transition<N: Network, P: FinalizeStorage<N>>(
187187
fn finalize_transition<N: Network, P: FinalizeStorage<N>>(
188188
state: FinalizeGlobalState,
189189
store: &FinalizeStore<N, P>,
190-
stack: &Stack<N>,
190+
stack: &Arc<Stack<N>>,
191191
transition: &Transition<N>,
192192
call_graph: HashMap<N::TransitionID, Vec<N::TransitionID>>,
193193
) -> Result<Vec<FinalizeOperation<N>>> {
@@ -226,23 +226,26 @@ fn finalize_transition<N: Network, P: FinalizeStorage<N>>(
226226
states.push(initialize_finalize_state(state, future, stack, *transition.id(), nonce)?);
227227

228228
// While there are active finalize states, finalize them.
229-
'outer: while let Some(FinalizeState {
230-
mut counter,
231-
finalize,
232-
mut registers,
233-
stack,
234-
mut call_counter,
235-
mut awaited,
236-
}) = states.pop()
229+
'outer: while let Some(FinalizeState { mut counter, mut registers, stack, mut call_counter, mut awaited }) =
230+
states.pop()
237231
{
232+
// Get the finalize logic.
233+
let Some(finalize) = stack.get_function_ref(registers.function_name())?.finalize_logic() else {
234+
bail!(
235+
"The function '{}/{}' does not have an associated finalize block",
236+
stack.program_id(),
237+
registers.function_name()
238+
)
239+
};
238240
// Evaluate the commands.
239241
while counter < finalize.commands().len() {
240242
// Retrieve the command.
241243
let command = &finalize.commands()[counter];
242244
// Finalize the command.
243245
match &command {
244246
Command::BranchEq(branch_eq) => {
245-
let result = try_vm_runtime!(|| branch_to(counter, branch_eq, finalize, stack, &registers));
247+
let result =
248+
try_vm_runtime!(|| branch_to(counter, branch_eq, finalize.positions(), &stack, &registers));
246249
match result {
247250
Ok(Ok(new_counter)) => {
248251
counter = new_counter;
@@ -254,7 +257,8 @@ fn finalize_transition<N: Network, P: FinalizeStorage<N>>(
254257
}
255258
}
256259
Command::BranchNeq(branch_neq) => {
257-
let result = try_vm_runtime!(|| branch_to(counter, branch_neq, finalize, stack, &registers));
260+
let result =
261+
try_vm_runtime!(|| branch_to(counter, branch_neq, finalize.positions(), &stack, &registers));
258262
match result {
259263
Ok(Ok(new_counter)) => {
260264
counter = new_counter;
@@ -300,14 +304,20 @@ fn finalize_transition<N: Network, P: FinalizeStorage<N>>(
300304
nonce += 1;
301305

302306
// Set up the finalize state for the await.
303-
let callee_state =
304-
match try_vm_runtime!(|| setup_await(state, await_, stack, &registers, transition_id, nonce)) {
305-
Ok(Ok(callee_state)) => callee_state,
306-
// If the evaluation fails, bail and return the error.
307-
Ok(Err(error)) => bail!("'finalize' failed to evaluate command ({command}): {error}"),
308-
// If the evaluation fails, bail and return the error.
309-
Err(_) => bail!("'finalize' failed to evaluate command ({command})"),
310-
};
307+
let callee_state = match try_vm_runtime!(|| setup_await(
308+
state,
309+
await_,
310+
&stack,
311+
&registers,
312+
transition_id,
313+
nonce
314+
)) {
315+
Ok(Ok(callee_state)) => callee_state,
316+
// If the evaluation fails, bail and return the error.
317+
Ok(Err(error)) => bail!("'finalize' failed to evaluate command ({command}): {error}"),
318+
// If the evaluation fails, bail and return the error.
319+
Err(_) => bail!("'finalize' failed to evaluate command ({command})"),
320+
};
311321

312322
// Increment the call counter.
313323
call_counter += 1;
@@ -317,7 +327,7 @@ fn finalize_transition<N: Network, P: FinalizeStorage<N>>(
317327
awaited.insert(await_.register().clone());
318328

319329
// Aggregate the caller state.
320-
let caller_state = FinalizeState { counter, finalize, registers, stack, call_counter, awaited };
330+
let caller_state = FinalizeState { counter, registers, stack, call_counter, awaited };
321331

322332
// Push the caller state onto the stack.
323333
states.push(caller_state);
@@ -327,7 +337,7 @@ fn finalize_transition<N: Network, P: FinalizeStorage<N>>(
327337
continue 'outer;
328338
}
329339
_ => {
330-
let result = try_vm_runtime!(|| command.finalize(stack, store, &mut registers));
340+
let result = try_vm_runtime!(|| command.finalize(stack.deref(), store, &mut registers));
331341
match result {
332342
// If the evaluation succeeds with an operation, add it to the list.
333343
Ok(Ok(Some(finalize_operation))) => finalize_operations.push(finalize_operation),
@@ -361,39 +371,34 @@ fn finalize_transition<N: Network, P: FinalizeStorage<N>>(
361371
}
362372

363373
// A helper struct to track the execution of a finalize block.
364-
struct FinalizeState<'a, N: Network> {
374+
struct FinalizeState<N: Network> {
365375
// A counter for the index of the commands.
366376
counter: usize,
367-
// The finalize logic.
368-
finalize: &'a Finalize<N>,
369377
// The registers.
370378
registers: FinalizeRegisters<N>,
371379
// The stack.
372-
stack: &'a Stack<N>,
380+
stack: Arc<Stack<N>>,
373381
// Call counter.
374382
call_counter: usize,
375383
// Awaited futures.
376384
awaited: HashSet<Register<N>>,
377385
}
378386

379387
// A helper function to initialize the finalize state.
380-
fn initialize_finalize_state<'a, N: Network>(
388+
fn initialize_finalize_state<N: Network>(
381389
state: FinalizeGlobalState,
382390
future: &Future<N>,
383-
stack: &'a Stack<N>,
391+
stack: &Arc<Stack<N>>,
384392
transition_id: N::TransitionID,
385393
nonce: u64,
386-
) -> Result<FinalizeState<'a, N>> {
387-
// Get the finalize logic and the stack.
388-
let (finalize, stack) = match stack.program_id() == future.program_id() {
389-
true => (stack.get_function_ref(future.function_name())?.finalize_logic(), stack),
390-
false => {
391-
let stack = stack.get_external_stack(future.program_id())?.as_ref();
392-
(stack.get_function_ref(future.function_name())?.finalize_logic(), stack)
393-
}
394+
) -> Result<FinalizeState<N>> {
395+
// Get the stack.
396+
let stack = match stack.program_id() == future.program_id() {
397+
true => stack.clone(),
398+
false => stack.get_external_stack(future.program_id())?,
394399
};
395-
// Check that the finalize logic exists.
396-
let finalize = match finalize {
400+
// Get the finalize logic and check that it exists.
401+
let finalize = match stack.get_function_ref(future.function_name())?.finalize_logic() {
397402
Some(finalize) => finalize,
398403
None => bail!(
399404
"The function '{}/{}' does not have an associated finalize block",
@@ -414,25 +419,25 @@ fn initialize_finalize_state<'a, N: Network>(
414419
finalize.inputs().iter().map(|i| i.register()).zip_eq(future.arguments().iter()).try_for_each(
415420
|(register, input)| {
416421
// Assign the input value to the register.
417-
registers.store(stack, register, Value::from(input))
422+
registers.store(stack.deref(), register, Value::from(input))
418423
},
419424
)?;
420425

421-
Ok(FinalizeState { counter: 0, finalize, registers, stack, call_counter: 0, awaited: Default::default() })
426+
Ok(FinalizeState { counter: 0, registers, stack, call_counter: 0, awaited: Default::default() })
422427
}
423428

424429
// A helper function that sets up the await operation.
425430
#[inline]
426-
fn setup_await<'a, N: Network>(
431+
fn setup_await<N: Network>(
427432
state: FinalizeGlobalState,
428433
await_: &Await<N>,
429-
stack: &'a Stack<N>,
434+
stack: &Arc<Stack<N>>,
430435
registers: &FinalizeRegisters<N>,
431436
transition_id: N::TransitionID,
432437
nonce: u64,
433-
) -> Result<FinalizeState<'a, N>> {
438+
) -> Result<FinalizeState<N>> {
434439
// Retrieve the input as a future.
435-
let future = match registers.load(stack, &Operand::Register(await_.register().clone()))? {
440+
let future = match registers.load(stack.deref(), &Operand::Register(await_.register().clone()))? {
436441
Value::Future(future) => future,
437442
_ => bail!("The input to 'await' is not a future"),
438443
};
@@ -445,16 +450,16 @@ fn setup_await<'a, N: Network>(
445450
fn branch_to<N: Network, const VARIANT: u8>(
446451
counter: usize,
447452
branch: &Branch<N, VARIANT>,
448-
finalize: &Finalize<N>,
453+
positions: &HashMap<Identifier<N>, usize>,
449454
stack: &Stack<N>,
450-
registers: &FinalizeRegisters<N>,
455+
registers: &impl RegistersLoad<N>,
451456
) -> Result<usize> {
452457
// Retrieve the inputs.
453458
let first = registers.load(stack, branch.first())?;
454459
let second = registers.load(stack, branch.second())?;
455460

456461
// A helper to get the index corresponding to a position.
457-
let get_position_index = |position: &Identifier<N>| match finalize.positions().get(position) {
462+
let get_position_index = |position: &Identifier<N>| match positions.get(position) {
458463
Some(index) if *index > counter => Ok(*index),
459464
Some(_) => bail!("Cannot branch to an earlier position '{position}' in the program"),
460465
None => bail!("The position '{position}' does not exist."),

synthesizer/process/src/lib.rs

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ use synthesizer_program::{
5555
Branch,
5656
Closure,
5757
Command,
58-
Finalize,
5958
FinalizeGlobalState,
6059
FinalizeOperation,
6160
Instruction,
@@ -80,7 +79,7 @@ pub struct Process<N: Network> {
8079
/// The universal SRS.
8180
universal_srs: UniversalSRS<N>,
8281
/// The mapping of program IDs to stacks.
83-
stacks: IndexMap<ProgramID<N>, Arc<Stack<N>>>,
82+
stacks: Arc<RwLock<IndexMap<ProgramID<N>, Arc<Stack<N>>>>>,
8483
}
8584

8685
impl<N: Network> Process<N> {
@@ -90,7 +89,7 @@ impl<N: Network> Process<N> {
9089
let timer = timer!("Process:setup");
9190

9291
// Initialize the process.
93-
let mut process = Self { universal_srs: UniversalSRS::load()?, stacks: IndexMap::new() };
92+
let mut process = Self { universal_srs: UniversalSRS::load()?, stacks: Default::default() };
9493
lap!(timer, "Initialize process");
9594

9695
// Initialize the 'credits.aleo' program.
@@ -133,8 +132,12 @@ impl<N: Network> Process<N> {
133132
/// If you intend to `execute` the program, use `deploy` and `finalize_deployment` instead.
134133
#[inline]
135134
pub fn add_stack(&mut self, stack: Stack<N>) {
136-
// Add the stack to the process.
137-
self.stacks.insert(*stack.program_id(), Arc::new(stack));
135+
// Get the program ID.
136+
let program_id = *stack.program_id();
137+
// Arc the stack first to limit the scope of the write lock.
138+
let stack = Arc::new(stack);
139+
// Insert the stack into the process, replacing the existing stack if it exists.
140+
self.stacks.write().insert(program_id, stack);
138141
}
139142
}
140143

@@ -145,7 +148,7 @@ impl<N: Network> Process<N> {
145148
let timer = timer!("Process::load");
146149

147150
// Initialize the process.
148-
let mut process = Self { universal_srs: UniversalSRS::load()?, stacks: IndexMap::new() };
151+
let mut process = Self { universal_srs: UniversalSRS::load()?, stacks: Default::default() };
149152
lap!(timer, "Initialize process");
150153

151154
// Initialize the 'credits.aleo' program.
@@ -183,7 +186,7 @@ impl<N: Network> Process<N> {
183186
#[cfg(feature = "wasm")]
184187
pub fn load_web() -> Result<Self> {
185188
// Initialize the process.
186-
let mut process = Self { universal_srs: UniversalSRS::load()?, stacks: IndexMap::new() };
189+
let mut process = Self { universal_srs: UniversalSRS::load()?, stacks: Default::default() };
187190

188191
// Initialize the 'credits.aleo' program.
189192
let program = Program::credits()?;
@@ -207,28 +210,27 @@ impl<N: Network> Process<N> {
207210
/// Returns `true` if the process contains the program with the given ID.
208211
#[inline]
209212
pub fn contains_program(&self, program_id: &ProgramID<N>) -> bool {
210-
self.stacks.contains_key(program_id)
213+
self.stacks.read().contains_key(program_id)
211214
}
212215

213216
/// Returns the stack for the given program ID.
214217
#[inline]
215-
pub fn get_stack(&self, program_id: impl TryInto<ProgramID<N>>) -> Result<&Arc<Stack<N>>> {
218+
pub fn get_stack(&self, program_id: impl TryInto<ProgramID<N>>) -> Result<Arc<Stack<N>>> {
216219
// Prepare the program ID.
217220
let program_id = program_id.try_into().map_err(|_| anyhow!("Invalid program ID"))?;
218221
// Retrieve the stack.
219-
let stack = self.stacks.get(&program_id).ok_or_else(|| anyhow!("Program '{program_id}' does not exist"))?;
222+
let stack = self
223+
.stacks
224+
.read()
225+
.get(&program_id)
226+
.ok_or_else(|| anyhow!("Program '{program_id}' does not exist"))?
227+
.clone();
220228
// Ensure the program ID matches.
221229
ensure!(stack.program_id() == &program_id, "Expected program '{}', found '{program_id}'", stack.program_id());
222230
// Return the stack.
223231
Ok(stack)
224232
}
225233

226-
/// Returns the program for the given program ID.
227-
#[inline]
228-
pub fn get_program(&self, program_id: impl TryInto<ProgramID<N>>) -> Result<&Program<N>> {
229-
Ok(self.get_stack(program_id)?.program())
230-
}
231-
232234
/// Returns the proving key for the given program ID and function name.
233235
#[inline]
234236
pub fn get_proving_key(

0 commit comments

Comments
 (0)