/ synthesizer / tests / test_vm_execute_and_finalize.rs
test_vm_execute_and_finalize.rs
  1  // Copyright (c) 2019-2025 Alpha-Delta Network Inc.
  2  // This file is part of the deltavm library.
  3  
  4  // Licensed under the Apache License, Version 2.0 (the "License");
  5  // you may not use this file except in compliance with the License.
  6  // You may obtain a copy of the License at:
  7  
  8  // http://www.apache.org/licenses/LICENSE-2.0
  9  
 10  // Unless required by applicable law or agreed to in writing, software
 11  // distributed under the License is distributed on an "AS IS" BASIS,
 12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  // See the License for the specific language governing permissions and
 14  // limitations under the License.
 15  
 16  mod utilities;
 17  
 18  use alphastd::StorageMode;
 19  use deltavm_console::{
 20      account::{PrivateKey, ViewKey},
 21      network::prelude::*,
 22      program::{Entry, Identifier, Literal, Plaintext, ProgramID, Record, U64, Value},
 23      types::{Boolean, Field},
 24  };
 25  use deltavm_ledger_block::{
 26      Block,
 27      ConfirmedTransaction,
 28      Header,
 29      Metadata,
 30      Ratifications,
 31      Transaction,
 32      Transactions,
 33      Transition,
 34  };
 35  use deltavm_ledger_store::{ConsensusStorage, ConsensusStore};
 36  use deltavm_synthesizer::{Authorization, VM, program::FinalizeOperation};
 37  use deltavm_synthesizer_process::{execution_cost, execution_cost_for_authorization};
 38  use deltavm_synthesizer_program::FinalizeGlobalState;
 39  
 40  use deltavm_console::account::Address;
 41  use anyhow::Result;
 42  use indexmap::IndexMap;
 43  use utilities::*;
 44  
 45  #[cfg(not(feature = "rocks"))]
 46  type LedgerType = deltavm_ledger_store::helpers::memory::ConsensusMemory<CurrentNetwork>;
 47  #[cfg(feature = "rocks")]
 48  type LedgerType = deltavm_ledger_store::helpers::rocksdb::ConsensusDB<CurrentNetwork>;
 49  
 50  #[test]
 51  fn test_vm_execute_and_finalize() {
 52      // Load the tests.
 53      let tests =
 54          load_tests::<_, ProgramTest>("./tests/vm/execute_and_finalize", "./expectations/vm/execute_and_finalize");
 55  
 56      // Run each test and compare it against its corresponding expectation.
 57      tests.iter().for_each(|test| {
 58          // Run the test.
 59          let output = run_test(test);
 60          // Check against the expected output.
 61          test.check(&output).unwrap();
 62          // Save the output.
 63          test.save(&output).unwrap();
 64      });
 65  }
 66  
 67  // A helper function to run the test and extract the outputs as YAML, to be compared against the expectation.
 68  fn run_test(test: &ProgramTest) -> serde_yaml::Mapping {
 69      // Initialize the RNG.
 70      let rng = &mut match test.randomness() {
 71          None => TestRng::fixed(123456789),
 72          Some(randomness) => TestRng::fixed(randomness),
 73      };
 74  
 75      // Initialize a private key.
 76      let genesis_private_key = PrivateKey::<CurrentNetwork>::new(rng).unwrap();
 77  
 78      // Initialize the VM.
 79      let (vm, _) = initialize_vm(&genesis_private_key, test.start_height(), rng);
 80  
 81      // Fund the additional keys.
 82      for key in test.keys() {
 83          // Transfer 1_000_000_000_000
 84          let transaction = vm
 85              .execute(
 86                  &genesis_private_key,
 87                  ("credits.delta", "transfer_public"),
 88                  vec![
 89                      Value::Plaintext(Plaintext::from(Literal::Address(Address::try_from(key).unwrap()))),
 90                      Value::Plaintext(Plaintext::from(Literal::U64(U64::new(1_000_000_000_000)))),
 91                  ]
 92                  .iter(),
 93                  None,
 94                  0,
 95                  None,
 96                  rng,
 97              )
 98              .unwrap();
 99          let time_since_last_block = CurrentNetwork::BLOCK_TIME as i64;
100          let (ratifications, transactions, aborted_transaction_ids, ratified_finalize_operations) = vm
101              .speculate(
102                  construct_finalize_global_state(&vm, time_since_last_block),
103                  time_since_last_block,
104                  Some(0u64),
105                  vec![],
106                  &None.into(),
107                  [transaction].iter(),
108                  rng,
109              )
110              .unwrap();
111          assert!(aborted_transaction_ids.is_empty());
112  
113          let block = construct_next_block(
114              &vm,
115              time_since_last_block,
116              &genesis_private_key,
117              ratifications,
118              transactions,
119              aborted_transaction_ids,
120              ratified_finalize_operations,
121              rng,
122          );
123          vm.add_next_block(&block.unwrap()).unwrap();
124      }
125  
126      // Deploy the programs.
127      for program in test.programs() {
128          let transaction = match vm.deploy(&genesis_private_key, program, None, 0, None, rng) {
129              Ok(transaction) => transaction,
130              Err(error) => {
131                  let mut output = serde_yaml::Mapping::new();
132                  output.insert(
133                      serde_yaml::Value::String("errors".to_string()),
134                      serde_yaml::Value::Sequence(vec![serde_yaml::Value::String(format!(
135                          "Failed to run `VM::deploy for program {}: {}",
136                          program.id(),
137                          error
138                      ))]),
139                  );
140                  output
141                      .insert(serde_yaml::Value::String("outputs".to_string()), serde_yaml::Value::Sequence(Vec::new()));
142                  return output;
143              }
144          };
145  
146          let time_since_last_block = CurrentNetwork::BLOCK_TIME as i64;
147          let (ratifications, transactions, aborted_transaction_ids, ratified_finalize_operations) = vm
148              .speculate(
149                  construct_finalize_global_state(&vm, time_since_last_block),
150                  time_since_last_block,
151                  Some(0u64),
152                  vec![],
153                  &None.into(),
154                  [transaction].iter(),
155                  rng,
156              )
157              .unwrap();
158          assert!(aborted_transaction_ids.is_empty());
159  
160          let block = construct_next_block(
161              &vm,
162              time_since_last_block,
163              &genesis_private_key,
164              ratifications,
165              transactions,
166              aborted_transaction_ids,
167              ratified_finalize_operations,
168              rng,
169          )
170          .unwrap();
171          vm.add_next_block(&block).unwrap();
172      }
173  
174      // Run each test case, aggregating the errors, outputs, and additional information.
175      let mut outputs = Vec::with_capacity(test.cases().len());
176      let mut additional = Vec::with_capacity(test.cases().len());
177  
178      for value in test.cases() {
179          // TODO: Dedup from other integration tests.
180          // Extract the function name, inputs, and optional private key.
181          let value = value.as_mapping().expect("expected mapping for test case");
182          let program_id = ProgramID::<CurrentNetwork>::from_str(
183              value
184                  .get("program")
185                  .expect("expected program name for test case")
186                  .as_str()
187                  .expect("expected string for program name"),
188          )
189          .expect("unable to parse program name");
190          let function_name = Identifier::<CurrentNetwork>::from_str(
191              value
192                  .get("function")
193                  .expect("expected function name for test case")
194                  .as_str()
195                  .expect("expected string for function name"),
196          )
197          .expect("unable to parse function name");
198          let inputs = value
199              .get("inputs")
200              .expect("expected inputs for test case")
201              .as_sequence()
202              .expect("expected sequence for inputs")
203              .iter()
204              .map(|input| match &input {
205                  serde_yaml::Value::Bool(bool) => Value::<CurrentNetwork>::from(Literal::Boolean(Boolean::new(*bool))),
206                  _ => Value::<CurrentNetwork>::from_str(input.as_str().expect("expected string for input"))
207                      .expect("unable to parse input"),
208              })
209              .collect_vec();
210          // TODO: Support fee records for custom private keys.
211          let private_key = match value.get("private_key") {
212              Some(private_key) => {
213                  PrivateKey::<CurrentNetwork>::from_str(private_key.as_str().expect("expected string for private key"))
214                      .expect("unable to parse private key")
215              }
216              None => genesis_private_key,
217          };
218  
219          // A helper function to run the test and extract the outputs as YAML, to be compared against the expectation.
220          let mut run_test = || -> (serde_yaml::Value, serde_yaml::Value) {
221              // Create a mapping to store the result of the test.
222              let mut result = serde_yaml::Mapping::new();
223              // Create a mapping to store the other items.
224              let mut other = serde_yaml::Mapping::new();
225  
226              // Execute the function, extracting the transaction.
227              let transaction =
228                  match vm.execute(&private_key, (program_id, function_name), inputs.iter(), None, 0u64, None, rng) {
229                      Ok(transaction) => transaction,
230                      // If the execution fails, return the error.
231                      Err(err) => {
232                          result.insert(
233                              serde_yaml::Value::String("execute".to_string()),
234                              serde_yaml::Value::String(err.to_string()),
235                          );
236                          return (serde_yaml::Value::Mapping(result), serde_yaml::Value::Mapping(Default::default()));
237                      }
238                  };
239  
240              // Test cost computation for Authorization
241              if transaction.is_execute() {
242                  let consensus_version =
243                      CurrentNetwork::CONSENSUS_VERSION(vm.block_store().current_block_height()).unwrap();
244  
245                  if consensus_version >= ConsensusVersion::V4 {
246                      let execution = transaction.execution().unwrap();
247  
248                      let actual_cost = execution_cost(&vm.process().read(), execution, consensus_version).unwrap();
249  
250                      let authorization =
251                          Authorization::from_unchecked((vec![], execution.transitions().cloned().collect()));
252                      let expected_cost =
253                          execution_cost_for_authorization(&vm.process().read(), &authorization, consensus_version)
254                              .unwrap();
255  
256                      assert_eq!(actual_cost, expected_cost);
257                  }
258              }
259  
260              // Attempt to verify the transaction.
261              let verified = vm.check_transaction(&transaction, None, rng).is_ok();
262              // Store the verification result.
263              result.insert(serde_yaml::Value::String("verified".to_string()), serde_yaml::Value::Bool(verified));
264  
265              // For each root transition in the transaction, extract the transition outputs and the inputs for finalize.
266              let mut execute = serde_yaml::Mapping::new();
267              // Store the outputs for child transitions separately, so that they are not checked for consistency.
268              let mut child_outputs = serde_yaml::Mapping::new();
269  
270              let transitions = transaction.transitions().collect::<Vec<_>>();
271              for transition in transitions.iter() {
272                  let mut transition_output = serde_yaml::Mapping::new();
273                  let outputs = transition
274                      .outputs()
275                      .iter()
276                      .map(|output| serde_yaml::Value::String(output.to_string()))
277                      .collect::<Vec<_>>();
278                  transition_output
279                      .insert(serde_yaml::Value::String("outputs".to_string()), serde_yaml::Value::Sequence(outputs));
280  
281                  // If this is the last transition, add the outputs to the `execute` mapping.
282                  if transition.program_id() == &program_id && transition.function_name() == &function_name {
283                      execute.insert(
284                          serde_yaml::Value::String(format!(
285                              "{}/{}",
286                              transition.program_id(),
287                              transition.function_name()
288                          )),
289                          serde_yaml::Value::Mapping(transition_output),
290                      );
291                  }
292                  // Otherwise, add the outputs to the `child_outputs` mapping.
293                  // This is done to avoid checking the sub-transitions for consistency (since they change every execution).
294                  else {
295                      child_outputs.insert(
296                          serde_yaml::Value::String(format!(
297                              "{}/{}",
298                              transition.program_id(),
299                              transition.function_name()
300                          )),
301                          serde_yaml::Value::Mapping(transition_output),
302                      );
303                  }
304              }
305  
306              // Add the `execute` mapping to `result` mapping.
307              result.insert(serde_yaml::Value::String("execute".to_string()), serde_yaml::Value::Mapping(execute));
308              // Add the child outputs to the `other` mapping.
309              other.insert(
310                  serde_yaml::Value::String("child_outputs".to_string()),
311                  serde_yaml::Value::Mapping(child_outputs),
312              );
313  
314              // Speculate on the ratifications, solutions, and transaction.
315              let time_since_last_block = CurrentNetwork::BLOCK_TIME as i64;
316              let (ratifications, transactions, aborted_transaction_ids, ratified_finalize_operations) = match vm
317                  .speculate(
318                      construct_finalize_global_state(&vm, time_since_last_block),
319                      time_since_last_block,
320                      Some(0u64),
321                      vec![],
322                      &None.into(),
323                      [transaction].iter(),
324                      rng,
325                  ) {
326                  Ok((ratifications, transactions, aborted_transaction_ids, ratified_finalize_operations)) => {
327                      result.insert(
328                          serde_yaml::Value::String("speculate".to_string()),
329                          serde_yaml::Value::String(match transactions.iter().next().unwrap() {
330                              ConfirmedTransaction::AcceptedExecute(_, _, _) => "the execution was accepted".to_string(),
331                              ConfirmedTransaction::RejectedExecute(_, _, _, _) => {
332                                  "the execution was rejected".to_string()
333                              }
334                              ConfirmedTransaction::AcceptedDeploy(_, _, _)
335                              | ConfirmedTransaction::RejectedDeploy(_, _, _, _) => {
336                                  unreachable!("unexpected deployment transaction")
337                              }
338                          }),
339                      );
340                      (ratifications, transactions, aborted_transaction_ids, ratified_finalize_operations)
341                  }
342                  Err(err) => {
343                      result.insert(
344                          serde_yaml::Value::String("speculate".to_string()),
345                          serde_yaml::Value::String(err.to_string()),
346                      );
347                      return (serde_yaml::Value::Mapping(result), serde_yaml::Value::Mapping(Default::default()));
348                  }
349              };
350              assert!(aborted_transaction_ids.is_empty());
351  
352              // Construct the next block.
353              let block = construct_next_block(
354                  &vm,
355                  time_since_last_block,
356                  &private_key,
357                  ratifications,
358                  transactions,
359                  aborted_transaction_ids,
360                  ratified_finalize_operations,
361                  rng,
362              )
363              .unwrap();
364              // Add the next block.
365              result.insert(
366                  serde_yaml::Value::String("add_next_block".to_string()),
367                  serde_yaml::Value::String(match vm.add_next_block(&block) {
368                      Ok(_) => "succeeded.".to_string(),
369                      Err(err) => err.to_string(),
370                  }),
371              );
372              (serde_yaml::Value::Mapping(result), serde_yaml::Value::Mapping(other))
373          };
374  
375          // Run the test.
376          let (result, other) = run_test();
377          outputs.push(result);
378          additional.push(other);
379      }
380  
381      let mut output = serde_yaml::Mapping::new();
382      output.insert(serde_yaml::Value::String("errors".to_string()), serde_yaml::Value::Sequence(vec![]));
383      output.insert(serde_yaml::Value::String("outputs".to_string()), serde_yaml::Value::Sequence(outputs));
384      output.insert(serde_yaml::Value::String("additional".to_string()), serde_yaml::Value::Sequence(additional));
385      output
386  }
387  
388  // A helper function to initialize the VM.
389  // Returns a VM and the first record in the genesis block.
390  #[allow(clippy::type_complexity)]
391  fn initialize_vm<R: Rng + CryptoRng>(
392      private_key: &PrivateKey<CurrentNetwork>,
393      height: u32,
394      rng: &mut R,
395  ) -> (VM<CurrentNetwork, LedgerType>, Vec<Record<CurrentNetwork, Plaintext<CurrentNetwork>>>) {
396      // Initialize a VM.
397      let vm: VM<CurrentNetwork, LedgerType> =
398          VM::from(ConsensusStore::open(StorageMode::new_test(None)).unwrap()).unwrap();
399  
400      // Initialize the genesis block.
401      let genesis = vm.genesis_beacon(private_key, rng).unwrap();
402  
403      // Select a record to spend.
404      let view_key = ViewKey::try_from(private_key).unwrap();
405      let records = genesis.transitions().cloned().flat_map(Transition::into_records).collect::<IndexMap<_, _>>();
406      let records = records.values().map(|record| record.decrypt(&view_key).unwrap()).collect::<Vec<_>>();
407  
408      // Add the genesis block to the VM.
409      vm.add_next_block(&genesis).unwrap();
410  
411      // If the desired height is greater than zero, add additional blocks to the VM.
412      for _ in 0..height {
413          let time_since_last_block = CurrentNetwork::BLOCK_TIME as i64;
414          let (ratifications, transactions, aborted_transaction_ids, ratified_finalize_operations) = vm
415              .speculate(
416                  construct_finalize_global_state(&vm, time_since_last_block),
417                  time_since_last_block,
418                  Some(0u64),
419                  vec![],
420                  &None.into(),
421                  [].into_iter(),
422                  rng,
423              )
424              .unwrap();
425          assert!(aborted_transaction_ids.is_empty());
426  
427          let block = construct_next_block(
428              &vm,
429              time_since_last_block,
430              private_key,
431              ratifications,
432              transactions,
433              aborted_transaction_ids,
434              ratified_finalize_operations,
435              rng,
436          )
437          .unwrap();
438          vm.add_next_block(&block).unwrap();
439      }
440  
441      (vm, records)
442  }
443  
444  // A helper function construct the desired number of fee records from an initial record, all owned by the same key.
445  #[allow(unused)]
446  fn construct_fee_records<C: ConsensusStorage<CurrentNetwork>, R: Rng + CryptoRng>(
447      vm: &VM<CurrentNetwork, C>,
448      private_key: &PrivateKey<CurrentNetwork>,
449      records: Vec<Record<CurrentNetwork, Plaintext<CurrentNetwork>>>,
450      num_fee_records: usize,
451      rng: &mut R,
452  ) -> Vec<(Record<CurrentNetwork, Plaintext<CurrentNetwork>>, u64)> {
453      // Helper function to get the balance of a `credits.delta` record.
454      let get_balance = |record: &Record<CurrentNetwork, Plaintext<CurrentNetwork>>| -> u64 {
455          match record.data().get(&Identifier::from_str("microcredits").unwrap()).unwrap() {
456              Entry::Private(Plaintext::Literal(Literal::U64(amount), ..)) => **amount,
457              _ => unreachable!("Invalid entry type for credits.delta."),
458          }
459      };
460  
461      println!("Splitting the initial fee record into {num_fee_records} fee records.");
462  
463      // Construct fee records for the tests.
464      let mut fee_records = records
465          .into_iter()
466          .map(|record| {
467              let balance = get_balance(&record);
468              (record, balance)
469          })
470          .collect::<Vec<_>>();
471      let mut fee_counter = 1;
472      while fee_records.len() < num_fee_records {
473          let mut transactions = Vec::with_capacity(fee_records.len());
474          for (fee_record, balance) in fee_records.drain(..).collect_vec() {
475              if fee_counter < num_fee_records {
476                  println!("Splitting out the {}-th record of size {}.", fee_counter, balance / 2);
477                  let (mut records, txns) = split(vm, private_key, fee_record, balance / 2, rng);
478                  let second = records.pop().unwrap();
479                  let first = records.pop().unwrap();
480                  let balance = get_balance(&first);
481                  fee_records.push((first, balance));
482                  let balance = get_balance(&second);
483                  fee_records.push((second, balance));
484                  transactions.extend(txns);
485                  fee_counter += 1;
486              } else {
487                  fee_records.push((fee_record, balance));
488              }
489          }
490  
491          let time_since_last_block = CurrentNetwork::BLOCK_TIME as i64;
492          let (ratifications, transactions, aborted_transaction_ids, ratified_finalize_operations) = vm
493              .speculate(
494                  construct_finalize_global_state(vm, time_since_last_block),
495                  time_since_last_block,
496                  Some(0u64),
497                  vec![],
498                  &None.into(),
499                  transactions.iter(),
500                  rng,
501              )
502              .unwrap();
503          assert!(aborted_transaction_ids.is_empty());
504  
505          // Create a block for the fee transactions and add them to the VM.
506          let block = construct_next_block(
507              vm,
508              time_since_last_block,
509              private_key,
510              ratifications,
511              transactions,
512              aborted_transaction_ids,
513              ratified_finalize_operations,
514              rng,
515          )
516          .unwrap();
517          vm.add_next_block(&block).unwrap();
518      }
519  
520      println!("Constructed fee records.");
521  
522      fee_records
523  }
524  
525  // A helper function to construct the next block.
526  #[allow(clippy::too_many_arguments)]
527  fn construct_next_block<C: ConsensusStorage<CurrentNetwork>, R: Rng + CryptoRng>(
528      vm: &VM<CurrentNetwork, C>,
529      time_since_last_block: i64,
530      private_key: &PrivateKey<CurrentNetwork>,
531      ratifications: Ratifications<CurrentNetwork>,
532      transactions: Transactions<CurrentNetwork>,
533      aborted_transaction_ids: Vec<<CurrentNetwork as Network>::TransactionID>,
534      ratified_finalize_operations: Vec<FinalizeOperation<CurrentNetwork>>,
535      rng: &mut R,
536  ) -> Result<Block<CurrentNetwork>> {
537      // Get the most recent block.
538      let block_hash = vm.block_store().get_block_hash(vm.block_store().max_height().unwrap()).unwrap().unwrap();
539      let previous_block = vm.block_store().get_block(&block_hash).unwrap().unwrap();
540  
541      // Construct the metadata associated with the block.
542      let metadata = Metadata::new(
543          CurrentNetwork::ID,
544          previous_block.round() + 1,
545          previous_block.height() + 1,
546          0,
547          0,
548          CurrentNetwork::GENESIS_COINBASE_TARGET,
549          CurrentNetwork::GENESIS_PROOF_TARGET,
550          previous_block.last_coinbase_target(),
551          previous_block.last_coinbase_timestamp(),
552          previous_block.timestamp().saturating_add(time_since_last_block),
553      )?;
554      // Construct the block header.
555      let header = Header::from(
556          vm.block_store().current_state_root(),
557          transactions.to_transactions_root().unwrap(),
558          transactions.to_finalize_root(ratified_finalize_operations).unwrap(),
559          ratifications.to_ratifications_root().unwrap(),
560          Field::zero(),
561          Field::zero(),
562          metadata,
563      )?;
564  
565      // Construct the new block.
566      Block::new_beacon(
567          private_key,
568          previous_block.hash(),
569          header,
570          ratifications,
571          None.into(),
572          vec![],
573          transactions,
574          aborted_transaction_ids,
575          rng,
576      )
577  }
578  
579  // A helper function to invoke `credits.delta/split`.
580  #[allow(clippy::type_complexity, unused)]
581  fn split<C: ConsensusStorage<CurrentNetwork>, R: Rng + CryptoRng>(
582      vm: &VM<CurrentNetwork, C>,
583      private_key: &PrivateKey<CurrentNetwork>,
584      record: Record<CurrentNetwork, Plaintext<CurrentNetwork>>,
585      amount: u64,
586      rng: &mut R,
587  ) -> (Vec<Record<CurrentNetwork, Plaintext<CurrentNetwork>>>, Vec<Transaction<CurrentNetwork>>) {
588      let inputs = vec![Value::Record(record), Value::Plaintext(Plaintext::from(Literal::U64(U64::new(amount))))];
589      let transaction = vm.execute(private_key, ("credits.delta", "split"), inputs.iter(), None, 0, None, rng).unwrap();
590      let records = transaction
591          .records()
592          .map(|(_, record)| record.decrypt(&ViewKey::try_from(private_key).unwrap()).unwrap())
593          .collect_vec();
594      assert_eq!(records.len(), 2);
595      (records, vec![transaction])
596  }
597  
598  // Construct `FinalizeGlobalState` from the current `VM` state.
599  fn construct_finalize_global_state<C: ConsensusStorage<CurrentNetwork>>(
600      vm: &VM<CurrentNetwork, C>,
601      time_since_last_block: i64,
602  ) -> FinalizeGlobalState {
603      // Retrieve the latest block.
604      let block_height = vm.block_store().max_height().unwrap();
605      let latest_block_hash = vm.block_store().get_block_hash(block_height).unwrap().unwrap();
606      let latest_block = vm.block_store().get_block(&latest_block_hash).unwrap().unwrap();
607      // Retrieve the latest round.
608      let latest_round = latest_block.round();
609      // Retrieve the latest height.
610      let latest_height = latest_block.height();
611      // Retrieve the latest cumulative weight.
612      let latest_cumulative_weight = latest_block.cumulative_weight();
613  
614      // Compute the next round number./
615      let next_round = latest_round.saturating_add(1);
616      // Compute the next height.
617      let next_height = latest_height.saturating_add(1);
618  
619      // Determine the block timestamp based on the consensus version.
620      let block_timestamp =
621          match next_height >= CurrentNetwork::CONSENSUS_HEIGHT(ConsensusVersion::V12).unwrap_or_default() {
622              true => Some(latest_block.timestamp().saturating_add(time_since_last_block)),
623              false => None,
624          };
625      // Construct the finalize state.
626      FinalizeGlobalState::new::<CurrentNetwork>(
627          next_round,
628          next_height,
629          block_timestamp,
630          latest_cumulative_weight,
631          0u128,
632          latest_block.hash(),
633      )
634      .unwrap()
635  }