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 use super::*; 17 18 use deltavm_ledger_committee::{MAX_DELEGATORS, MIN_DELEGATOR_STAKE, MIN_VALIDATOR_SELF_STAKE}; 19 use deltavm_utilities::{cfg_sort_by_cached_key, defer, dev_eprintln}; 20 21 impl<N: Network, C: ConsensusStorage<N>> VM<N, C> { 22 /// Speculates on the given list of transactions in the VM. 23 /// This function aborts all transactions that are not are well-formed or unique. 24 /// 25 /// 26 /// Returns the confirmed transactions, aborted transaction IDs, 27 /// and finalize operations from pre-ratify and post-ratify. 28 /// 29 /// # Note 30 /// This method is used to create a new block (including the genesis block). 31 /// - If `coinbase_reward = None`, then the `ratifications` will not be modified. 32 /// - If `coinbase_reward = Some(coinbase_reward)`, then the method will append a 33 /// `Ratify::BlockReward(block_reward)` and `Ratify::PuzzleReward(puzzle_reward)` 34 /// to the front of the `ratifications` list. 35 /// 36 /// # Panics 37 /// This function panics if called from an async context. 38 #[inline] 39 #[allow(clippy::too_many_arguments)] 40 pub fn speculate<'a, R: Rng + CryptoRng>( 41 &self, 42 state: FinalizeGlobalState, 43 time_since_last_block: i64, // TODO (raychu86): Consider moving this value into `FinalizeGlobalState`. 44 coinbase_reward: Option<u64>, 45 candidate_ratifications: Vec<Ratify<N>>, 46 candidate_solutions: &Solutions<N>, 47 candidate_transactions: impl ExactSizeIterator<Item = &'a Transaction<N>>, 48 rng: &mut R, 49 ) -> Result<(Ratifications<N>, Transactions<N>, Vec<N::TransactionID>, Vec<FinalizeOperation<N>>)> { 50 let timer = timer!("VM::speculate"); 51 52 // Collect the candidate transactions into a vector. 53 let candidate_transactions: Vec<_> = candidate_transactions.collect::<Vec<_>>(); 54 let candidate_transaction_ids: Vec<_> = candidate_transactions.iter().map(|tx| tx.id()).collect(); 55 56 // Determine if the vm is currently processing the genesis block. 57 let is_genesis = 58 self.block_store().find_block_height_from_state_root(self.block_store().current_state_root())?.is_none(); 59 // If the transactions are not part of the genesis block, ensure each transaction is well-formed and unique. Abort any transactions that are not. 60 let (verified_transactions, verification_aborted_transactions) = match is_genesis { 61 // If the current state root does not exist in the block store, then the genesis block has not been introduced yet. 62 true => (candidate_transactions, vec![]), 63 // Verify transactions for all non-genesis cases. 64 false => self.prepare_for_speculate(&candidate_transactions, rng)?, 65 }; 66 67 // Performs a **dry-run** over the list of ratifications, solutions, and transactions. 68 let (ratifications, confirmed_transactions, speculation_aborted_transactions, ratified_finalize_operations) = 69 self.atomic_speculate( 70 state, 71 time_since_last_block, 72 coinbase_reward, 73 candidate_ratifications, 74 candidate_solutions.clone(), 75 verified_transactions.into_iter().cloned().collect(), 76 )?; 77 78 // Get the aborted transaction ids. 79 let verification_aborted_transaction_ids = verification_aborted_transactions.iter().map(|(tx, e)| (tx.id(), e)); 80 let speculation_aborted_transaction_ids = speculation_aborted_transactions.iter().map(|(tx, e)| (tx.id(), e)); 81 let unordered_aborted_transaction_ids: IndexMap<N::TransactionID, &String> = 82 verification_aborted_transaction_ids.chain(speculation_aborted_transaction_ids).collect(); 83 84 // Filter and order the aborted transaction ids according to candidate_transactions 85 let aborted_transaction_ids: Vec<_> = candidate_transaction_ids 86 .into_iter() 87 .filter_map(|tx_id| { 88 unordered_aborted_transaction_ids.get(&tx_id).map(|error| { 89 warn!("Speculation safely aborted a transaction - {error} ({tx_id})"); 90 tx_id 91 }) 92 }) 93 .collect(); 94 95 finish!(timer, "Finished dry-run of the transactions"); 96 97 // Return the ratifications, confirmed transactions, aborted transaction IDs, and ratified finalize operations. 98 Ok(( 99 ratifications, 100 confirmed_transactions.into_iter().collect(), 101 aborted_transaction_ids, 102 ratified_finalize_operations, 103 )) 104 } 105 106 /// Checks the speculation on the given transactions in the VM. 107 /// This function also ensure that the given transactions are well-formed and unique. 108 /// 109 /// Returns the finalize operations from pre-ratify and post-ratify. 110 /// 111 /// # Panics 112 /// This function panics if called from an async context. 113 #[inline] 114 pub fn check_speculate<R: Rng + CryptoRng>( 115 &self, 116 state: FinalizeGlobalState, 117 time_since_last_block: i64, 118 ratifications: &Ratifications<N>, 119 solutions: &Solutions<N>, 120 transactions: &Transactions<N>, 121 rng: &mut R, 122 ) -> Result<Vec<FinalizeOperation<N>>> { 123 let timer = timer!("VM::check_speculate"); 124 125 // Retrieve the transactions and their rejected IDs. 126 let transactions_and_rejected_ids = cfg_iter!(transactions) 127 .map(|transaction| transaction.to_rejected_id().map(|rejected_id| (transaction.deref(), rejected_id))) 128 .collect::<Result<Vec<_>>>()?; 129 // Ensure each transaction is well-formed and unique. 130 // NOTE: We perform the transaction checks here prior to `atomic_speculate` because we must 131 // ensure that the `Fee` transactions are valid. We can't unify the transaction checks in `atomic_speculate` 132 // because we run speculation on the unconfirmed variant of the transactions. 133 self.check_transactions(&transactions_and_rejected_ids, rng)?; 134 135 // Reconstruct the candidate ratifications to verify the speculation. 136 let candidate_ratifications = ratifications.iter().cloned().collect::<Vec<_>>(); 137 // Reconstruct the unconfirmed transactions to verify the speculation. 138 let candidate_transactions = 139 transactions.iter().map(|confirmed| confirmed.to_unconfirmed_transaction()).collect::<Result<Vec<_>>>()?; 140 141 // Performs a **dry-run** over the list of ratifications, solutions, and transactions. 142 let (speculate_ratifications, confirmed_transactions, aborted_transactions, ratified_finalize_operations) = 143 self.atomic_speculate( 144 state, 145 time_since_last_block, 146 None, 147 candidate_ratifications, 148 solutions.clone(), 149 candidate_transactions, 150 )?; 151 152 // Ensure the ratifications after speculation match. 153 if ratifications != &speculate_ratifications { 154 bail!("The ratifications after speculation do not match the ratifications in the block"); 155 } 156 // Ensure the transactions after speculation match. 157 if transactions != &confirmed_transactions.into_iter().collect() { 158 bail!("The transactions after speculation do not match the transactions in the block"); 159 } 160 // Ensure there are no aborted transaction IDs from this speculation. 161 // Note: There should be no aborted transactions, because we are checking a block, 162 // where any aborted transactions should be in the aborted transaction ID list, not in transactions. 163 ensure!(aborted_transactions.is_empty(), "Aborted transactions found in the block (from speculation)"); 164 165 finish!(timer, "Finished dry-run of the transactions"); 166 167 // Return the ratified finalize operations. 168 Ok(ratified_finalize_operations) 169 } 170 171 /// Finalizes the given transactions into the VM. 172 /// 173 /// Returns the finalize operations from pre-ratify and post-ratify. 174 #[inline] 175 pub fn finalize( 176 &self, 177 state: FinalizeGlobalState, 178 ratifications: &Ratifications<N>, 179 solutions: &Solutions<N>, 180 transactions: &Transactions<N>, 181 ) -> Result<Vec<FinalizeOperation<N>>> { 182 let timer = timer!("VM::finalize"); 183 184 // Performs a **real-run** of finalize over the list of ratifications, solutions, and transactions. 185 let ratified_finalize_operations = self.atomic_finalize(state, ratifications, solutions, transactions)?; 186 187 finish!(timer, "Finished real-run of finalize"); 188 Ok(ratified_finalize_operations) 189 } 190 } 191 192 impl<N: Network, C: ConsensusStorage<N>> VM<N, C> { 193 /// The maximum number of confirmed transactions allowed in a block. 194 #[cfg(not(any(test, feature = "test")))] 195 pub const MAXIMUM_CONFIRMED_TRANSACTIONS: usize = Transactions::<N>::MAX_TRANSACTIONS; 196 /// The maximum number of confirmed transactions allowed in a block. 197 /// This is deliberately set to a low value (8) for testing purposes only. 198 #[cfg(any(test, feature = "test"))] 199 pub const MAXIMUM_CONFIRMED_TRANSACTIONS: usize = 8; 200 201 /// Performs atomic speculation over a list of transactions. 202 /// 203 /// Returns the ratifications, confirmed transactions, aborted transactions, 204 /// and finalize operations from pre-ratify and post-ratify. 205 /// 206 /// # Note 207 /// This method is used by `VM::speculate` and `VM::check_speculate`. 208 /// - If `coinbase_reward = None`, then the `ratifications` will not be modified. 209 /// - If `coinbase_reward = Some(coinbase_reward)`, then the method will append a 210 /// `Ratify::BlockReward(block_reward)` and `Ratify::PuzzleReward(puzzle_reward)` 211 /// to the front of the `ratifications` list. 212 /// 213 /// # Panics 214 /// This function panics if called from an async context. 215 fn atomic_speculate( 216 &self, 217 state: FinalizeGlobalState, 218 time_since_last_block: i64, 219 coinbase_reward: Option<u64>, 220 ratifications: Vec<Ratify<N>>, 221 solutions: Solutions<N>, 222 transactions: Vec<Transaction<N>>, 223 ) -> Result<( 224 Ratifications<N>, 225 Vec<ConfirmedTransaction<N>>, 226 Vec<(Transaction<N>, String)>, 227 Vec<FinalizeOperation<N>>, 228 )> { 229 let sequential_op = SequentialOperation::AtomicSpeculate( 230 state, 231 time_since_last_block, 232 coinbase_reward, 233 ratifications, 234 solutions, 235 transactions, 236 ); 237 let Some(SequentialOperationResult::AtomicSpeculate(ret)) = self.run_sequential_operation(sequential_op) else { 238 bail!("Already shutting down"); 239 }; 240 241 ret 242 } 243 244 /// Internal function called when invoking [`Self::atomic_speculate`]. 245 /// 246 /// # Note 247 /// This function must only be called from the sequential operation thread. 248 /// 249 /// # Panics 250 /// This function panics if not called from the sequential operation thread. 251 pub(crate) fn atomic_speculate_inner( 252 &self, 253 state: FinalizeGlobalState, 254 time_since_last_block: i64, 255 coinbase_reward: Option<u64>, 256 ratifications: Vec<Ratify<N>>, 257 solutions: Solutions<N>, 258 transactions: Vec<Transaction<N>>, 259 ) -> Result<( 260 Ratifications<N>, 261 Vec<ConfirmedTransaction<N>>, 262 Vec<(Transaction<N>, String)>, 263 Vec<FinalizeOperation<N>>, 264 )> { 265 self.ensure_sequential_processing(); 266 267 let timer = timer!("VM::atomic_speculate"); 268 269 // Retrieve the number of solutions. 270 let num_solutions = solutions.len(); 271 // Retrieve the number of transactions. 272 let num_transactions = transactions.len(); 273 // Determine the maximum number of aborted solutions allowed in a block. 274 let max_aborted_solutions = Solutions::<N>::max_aborted_solutions()?; 275 // Determine the maximum number of aborted transactions allowed in a block. 276 let max_aborted_transactions = Transactions::<N>::max_aborted_transactions()?; 277 278 // Perform the finalize operation on the preset finalize mode. 279 atomic_finalize!(self.finalize_store(), FinalizeMode::DryRun, { 280 // Ensure the number of solutions does not exceed the maximum. 281 if num_solutions > max_aborted_solutions { 282 // Note: This will abort the entire atomic batch. 283 return Err(format!("Too many solutions in the block - {num_solutions}",)); 284 } 285 286 // Ensure the number of transactions does not exceed the maximum. 287 if num_transactions > max_aborted_transactions { 288 // Note: This will abort the entire atomic batch. 289 return Err(format!( 290 "Too many transactions in the block - {num_transactions} (max: {max_aborted_transactions})", 291 )); 292 } 293 294 // Initialize an iterator for ratifications before finalize. 295 let pre_ratifications = ratifications.iter().filter(|r| match r { 296 Ratify::Genesis(_, _, _) => true, 297 Ratify::BlockReward(..) | Ratify::PuzzleReward(..) => false, 298 }); 299 // Initialize an iterator for ratifications after finalize. 300 let post_ratifications = ratifications.iter().filter(|r| match r { 301 Ratify::Genesis(_, _, _) => false, 302 Ratify::BlockReward(..) | Ratify::PuzzleReward(..) => true, 303 }); 304 305 // Initialize a list of finalize operations. 306 let mut ratified_finalize_operations = Vec::new(); 307 308 // Retrieve the finalize store. 309 let store = self.finalize_store(); 310 311 /* Perform the ratifications before finalize. */ 312 313 match Self::atomic_pre_ratify(store, state, pre_ratifications) { 314 // Store the finalize operations from the post-ratify. 315 Ok(operations) => ratified_finalize_operations.extend(operations), 316 // Note: This will abort the entire atomic batch. 317 Err(e) => return Err(format!("Failed to pre-ratify - {e}")), 318 } 319 320 /* Perform the atomic finalize over the transactions. */ 321 322 // Acquire the write lock on the process. 323 // Note: Due to the highly-sensitive nature of processing all `finalize` calls, 324 // we choose to acquire the write lock for the entire duration of this atomic batch. 325 let process = self.process.write(); 326 327 // Revert any unstaged stacks, when the function returns. 328 // Note. This function does not call `commit_stacks` so the staged stacks will always be reverted 329 // regardless of whether the function succeeds or fails. 330 defer! { 331 process.revert_stacks(); 332 } 333 334 // Initialize a list of the confirmed transactions. 335 let mut confirmed = Vec::with_capacity(num_transactions); 336 // Initialize a list of the aborted transactions. 337 let mut aborted = Vec::new(); 338 // Initialize a counter for the confirmed transaction index. 339 let mut counter = 0u32; 340 // Initialize a list of created transition IDs. 341 let mut transition_ids: IndexSet<N::TransitionID> = IndexSet::new(); 342 // Initialize a list of spent input IDs. 343 let mut input_ids: IndexSet<Field<N>> = IndexSet::new(); 344 // Initialize a list of created output IDs. 345 let mut output_ids: IndexSet<Field<N>> = IndexSet::new(); 346 // Initialize the list of created transition public keys. 347 let mut tpks: IndexSet<Group<N>> = IndexSet::new(); 348 // Initialize the list of deployment payers. 349 let mut deployment_payers: IndexSet<Address<N>> = IndexSet::new(); 350 // Initialize a list of the successful deployments. 351 let mut deployments = IndexSet::new(); 352 353 // Finalize the transactions. 354 'outer: for transaction in transactions { 355 // Ensure the number of confirmed transactions does not exceed the maximum. 356 // Upon reaching the maximum number of confirmed transactions, all remaining transactions are aborted. 357 if confirmed.len() >= Self::MAXIMUM_CONFIRMED_TRANSACTIONS { 358 // Store the aborted transaction. 359 aborted.push((transaction.clone(), "Exceeds block transaction limit".to_string())); 360 // Continue to the next transaction. 361 continue 'outer; 362 } 363 364 // Determine if the transaction should be aborted. 365 if let Some(reason) = self.should_abort_transaction( 366 &transaction, 367 &transition_ids, 368 &input_ids, 369 &output_ids, 370 &tpks, 371 &deployment_payers, 372 &deployments, 373 ) { 374 // Store the aborted transaction. 375 aborted.push((transaction.clone(), reason)); 376 // Continue to the next transaction. 377 continue 'outer; 378 } 379 380 // Process the transaction in an isolated atomic batch. 381 // - If the transaction succeeds, the finalize operations are stored. 382 // - If the transaction fails, the atomic batch is aborted and no finalize operations are stored. 383 let outcome = match &transaction { 384 // The finalize operation here involves appending the 'stack', 385 // and adding the program to the finalize tree. 386 Transaction::Deploy(_, _, program_owner, deployment, fee) => { 387 // Define the closure for processing a rejected deployment. 388 let process_rejected_deployment = 389 |fee: &Fee<N>, 390 deployment: Deployment<N>| 391 -> Result<Result<ConfirmedTransaction<N>, String>> { 392 process 393 .finalize_fee(state, store, fee) 394 .and_then(|finalize| { 395 Transaction::from_fee(fee.clone()).map(|fee_tx| (fee_tx, finalize)) 396 }) 397 .map(|(fee_tx, finalize)| { 398 let rejected = Rejected::new_deployment(*program_owner, deployment); 399 ConfirmedTransaction::rejected_deploy(counter, fee_tx, rejected, finalize) 400 .map_err(|e| e.to_string()) 401 }) 402 }; 403 404 // Check if the program has already been deployed in this block. 405 match deployments.contains(deployment.program_id()) { 406 // If the program has already been deployed, construct the rejected deploy transaction. 407 true => match process_rejected_deployment(fee, *deployment.clone()) { 408 Ok(result) => result, 409 Err(error) => { 410 // Note: On failure, skip this transaction, and continue speculation. 411 dev_eprintln!("Failed to finalize the fee in a rejected deploy - {error}"); 412 // Store the aborted transaction. 413 aborted.push((transaction.clone(), error.to_string())); 414 // Continue to the next transaction. 415 continue 'outer; 416 } 417 }, 418 // If the program has not yet been deployed, attempt to deploy it. 419 false => match process.finalize_deployment(state, store, deployment, fee) { 420 // Construct the accepted deploy transaction. 421 Ok((stack, finalize)) => { 422 // Add the stack to the process with the option to be reverted. 423 process.stage_stack(stack); 424 ConfirmedTransaction::accepted_deploy(counter, transaction.clone(), finalize) 425 .map_err(|e| e.to_string()) 426 } 427 // Construct the rejected deploy transaction. 428 Err(error) => { 429 trace!("Failed to finalize deploy tx {} - {error}", transaction.id()); 430 match process_rejected_deployment(fee, *deployment.clone()) { 431 Ok(result) => result, 432 Err(error) => { 433 // Note: On failure, skip this transaction, and continue speculation. 434 dev_eprintln!("Failed to finalize the fee in a rejected deploy - {error}"); 435 // Store the aborted transaction. 436 aborted.push((transaction.clone(), error.to_string())); 437 // Continue to the next transaction. 438 continue 'outer; 439 } 440 } 441 } 442 }, 443 } 444 } 445 // The finalize operation here involves calling 'update_key_value', 446 // and update the respective leaves of the finalize tree. 447 Transaction::Execute(_, _, execution, fee) => { 448 // Determine if the transaction is safe for execution, and proceed to execute it. 449 match Self::prepare_for_execution(state, store, execution) 450 .and_then(|_| process.finalize_execution(state, store, execution, fee.as_ref())) 451 { 452 // Construct the accepted execute transaction. 453 Ok(finalize) => { 454 ConfirmedTransaction::accepted_execute(counter, transaction.clone(), finalize) 455 .map_err(|e| e.to_string()) 456 } 457 // Construct the rejected execute transaction. 458 Err(error) => { 459 trace!("Failed to finalize execute tx {} - {error}", transaction.id()); 460 match fee { 461 // Finalize the fee, to ensure it is valid. 462 Some(fee) => { 463 match process.finalize_fee(state, store, fee).and_then(|finalize| { 464 Transaction::from_fee(fee.clone()).map(|fee_tx| (fee_tx, finalize)) 465 }) { 466 Ok((fee_tx, finalize)) => { 467 // Construct the rejected execution. 468 let rejected = Rejected::new_execution(*execution.clone()); 469 // Construct the rejected execute transaction. 470 ConfirmedTransaction::rejected_execute( 471 counter, fee_tx, rejected, finalize, 472 ) 473 .map_err(|e| e.to_string()) 474 } 475 Err(error) => { 476 // Note: On failure, skip this transaction, and continue speculation. 477 dev_eprintln!( 478 "Failed to finalize the fee in a rejected execute - {error}" 479 ); 480 // Store the aborted transaction. 481 aborted.push((transaction.clone(), error.to_string())); 482 // Continue to the next transaction. 483 continue 'outer; 484 } 485 } 486 } 487 488 // This is a foundational bug - the caller is violating protocol rules. 489 // It is possible that a `credits.delta/split` transaction has no fee. However, it 490 // is a simple transition without finalize operations and should not fail here. 491 // If a `credits.delta/upgrade` transaction has no fee and fails, we simply abort it. 492 // Note: This will abort the entire atomic batch. 493 None => { 494 // Abort the upgrade transaction. 495 if transaction.contains_upgrade() && execution.len() == 1 { 496 aborted.push(( 497 transaction.clone(), 498 "Failed to finalize a `credits.delta/upgrade` call with no fee" 499 .to_string(), 500 )); 501 // Continue to the next transaction. 502 continue 'outer; 503 } 504 Err("Rejected execute transaction has no fee".to_string()) 505 } 506 } 507 } 508 } 509 } 510 // There are no finalize operations here. 511 // Note: This will abort the entire atomic batch. 512 Transaction::Fee(..) => Err("Cannot speculate on a fee transaction".to_string()), 513 }; 514 lap!(timer, "Speculated on transaction '{}'", transaction.id()); 515 516 match outcome { 517 // If the transaction succeeded, store it and continue to the next transaction. 518 Ok(confirmed_transaction) => { 519 // Add the transition IDs to the set of produced transition IDs. 520 transition_ids.extend(confirmed_transaction.transaction().transition_ids()); 521 // Add the input IDs to the set of spent input IDs. 522 input_ids.extend(confirmed_transaction.transaction().input_ids()); 523 // Add the output IDs to the set of produced output IDs. 524 output_ids.extend(confirmed_transaction.transaction().output_ids()); 525 // Add the transition public keys to the set of produced transition public keys. 526 tpks.extend(confirmed_transaction.transaction().transition_public_keys()); 527 // Add the program owner to the set of deployment payers and the program ID to the set of deployments. 528 if let Transaction::Deploy(_, _, _, deployment, fee) = confirmed_transaction.transaction() { 529 fee.payer().map(|payer| deployment_payers.insert(payer)); 530 deployments.insert(*deployment.program_id()); 531 } 532 // Store the confirmed transaction. 533 confirmed.push(confirmed_transaction); 534 // Increment the transaction index counter. 535 counter = counter.saturating_add(1); 536 } 537 // If the transaction failed, abort the entire batch. 538 Err(error) => { 539 error!("Critical bug in speculate: {error}\n\n{transaction}"); 540 dev_eprintln!("Critical bug in speculate: {error}\n\n{transaction}"); 541 // Note: This will abort the entire atomic batch. 542 return Err(format!("Failed to speculate on transaction - {error}")); 543 } 544 } 545 } 546 547 // Ensure all transactions were processed. 548 if confirmed.len() + aborted.len() != num_transactions { 549 // Note: This will abort the entire atomic batch. 550 return Err("Not all transactions were processed in 'VM::atomic_speculate'".to_string()); 551 } 552 553 /* Perform the ratifications after finalize. */ 554 555 // Prepare the reward ratifications, if any. 556 let reward_ratifications = match coinbase_reward { 557 // If the coinbase reward is `None`, then there are no reward ratifications. 558 None => vec![], 559 // If the coinbase reward is `Some(coinbase_reward)`, then we must compute the reward ratifications. 560 Some(coinbase_reward) => { 561 // Calculate the transaction fees. 562 let Ok(transaction_fees) = 563 confirmed.iter().map(|tx| Ok(*tx.priority_fee_amount()?)).sum::<Result<u64>>() 564 else { 565 // Note: This will abort the entire atomic batch. 566 return Err("Failed to calculate the transaction fees during speculation".to_string()); 567 }; 568 569 // Compute the block reward. 570 let block_reward = deltavm_ledger_block::block_reward::<N>( 571 state.block_height(), 572 N::STARTING_SUPPLY, 573 N::BLOCK_TIME, 574 time_since_last_block, 575 coinbase_reward, 576 transaction_fees, 577 ) 578 .map_err(|e| format!("Failed to compute the block reward - {e}"))?; 579 // Compute the puzzle reward. 580 let puzzle_reward = deltavm_ledger_block::puzzle_reward(coinbase_reward); 581 582 // Output the reward ratifications. 583 vec![Ratify::BlockReward(block_reward), Ratify::PuzzleReward(puzzle_reward)] 584 } 585 }; 586 587 // Update the post-ratifications iterator. 588 let post_ratifications = reward_ratifications.iter().chain(post_ratifications); 589 590 // Process the post-ratifications. 591 match Self::atomic_post_ratify::<false>(&self.puzzle, store, state, post_ratifications, &solutions) { 592 // Store the finalize operations from the post-ratify. 593 Ok(operations) => ratified_finalize_operations.extend(operations), 594 // Note: This will abort the entire atomic batch. 595 Err(e) => return Err(format!("Failed to post-ratify - {e}")), 596 } 597 598 /* Construct the ratifications after speculation. */ 599 600 let Ok(ratifications) = 601 Ratifications::try_from_iter(reward_ratifications.into_iter().chain(ratifications.into_iter())) 602 else { 603 // Note: This will abort the entire atomic batch. 604 return Err("Failed to construct the ratifications after speculation".to_string()); 605 }; 606 607 finish!(timer); 608 609 // On return, 'atomic_finalize!' will abort the batch, and return the ratifications, 610 // confirmed & aborted transactions, and finalize operations from pre-ratify and post-ratify. 611 Ok((ratifications, confirmed, aborted, ratified_finalize_operations)) 612 }) 613 } 614 615 /// Performs atomic finalization over a list of transactions. 616 /// 617 /// Returns the finalize operations from pre-ratify and post-ratify. 618 #[inline] 619 fn atomic_finalize( 620 &self, 621 state: FinalizeGlobalState, 622 ratifications: &Ratifications<N>, 623 solutions: &Solutions<N>, 624 transactions: &Transactions<N>, 625 ) -> Result<Vec<FinalizeOperation<N>>> { 626 // The tests may run this method ad-hoc, outside of the context of add_next_block. 627 #[cfg(not(test))] 628 self.ensure_sequential_processing(); 629 630 let timer = timer!("VM::atomic_finalize"); 631 632 // Perform the finalize operation on the preset finalize mode. 633 atomic_finalize!(self.finalize_store(), FinalizeMode::RealRun, { 634 // Initialize an iterator for ratifications before finalize. 635 let pre_ratifications = ratifications.iter().filter(|r| match r { 636 Ratify::Genesis(_, _, _) => true, 637 Ratify::BlockReward(..) | Ratify::PuzzleReward(..) => false, 638 }); 639 // Initialize an iterator for ratifications after finalize. 640 let post_ratifications = ratifications.iter().filter(|r| match r { 641 Ratify::Genesis(_, _, _) => false, 642 Ratify::BlockReward(..) | Ratify::PuzzleReward(..) => true, 643 }); 644 645 // Initialize a list of finalize operations. 646 let mut ratified_finalize_operations = Vec::new(); 647 648 // Retrieve the finalize store. 649 let store = self.finalize_store(); 650 651 /* Perform the ratifications before finalize. */ 652 653 match Self::atomic_pre_ratify(store, state, pre_ratifications) { 654 // Store the finalize operations from the post-ratify. 655 Ok(operations) => ratified_finalize_operations.extend(operations), 656 // Note: This will abort the entire atomic batch. 657 Err(e) => return Err(format!("Failed to pre-ratify - {e}")), 658 } 659 660 /* Perform the atomic finalize over the transactions. */ 661 662 // Acquire the write lock on the process. 663 // Note: Due to the highly-sensitive nature of processing all `finalize` calls, 664 // we choose to acquire the write lock for the entire duration of this atomic batch. 665 let process = self.process.write(); 666 667 // Revert any unstaged stacks, when the function returns. 668 // Note. `commit_stacks` is called at the bottom of this function after successful finalization. 669 // The staged stacks are only reverted if the function returns an error. 670 defer! { 671 process.revert_stacks(); 672 } 673 674 // Finalize the transactions. 675 for (index, transaction) in transactions.iter().enumerate() { 676 // Convert the transaction index to a u32. 677 // Note: On failure, this will abort the entire atomic batch. 678 let index = u32::try_from(index).map_err(|_| "Failed to convert transaction index".to_string())?; 679 // Ensure the index matches the expected index. 680 if index != transaction.index() { 681 // Note: This will abort the entire atomic batch. 682 return Err(format!("Mismatch in {} transaction index", transaction.variant())); 683 } 684 // Process the transaction in an isolated atomic batch. 685 // - If the transaction succeeds, the finalize operations are stored. 686 // - If the transaction fails, the atomic batch is aborted and no finalize operations are stored. 687 let outcome: Result<(), String> = match transaction { 688 ConfirmedTransaction::AcceptedDeploy(_, transaction, finalize) => { 689 // Extract the deployment and fee from the transaction. 690 let (deployment, fee) = match transaction { 691 Transaction::Deploy(_, _, _, deployment, fee) => (deployment, fee), 692 // Note: This will abort the entire atomic batch. 693 _ => return Err("Expected deploy transaction".to_string()), 694 }; 695 // The finalize operation here involves appending the 'stack', and adding the program to the finalize tree. 696 match process.finalize_deployment(state, store, deployment, fee) { 697 // Ensure the finalize operations match the expected. 698 Ok((stack, finalize_operations)) => match finalize == &finalize_operations { 699 // Add the stack to the process with the option to be reverted. 700 true => process.stage_stack(stack), 701 // Note: This will abort the entire atomic batch. 702 false => { 703 return Err(format!( 704 "Mismatch in finalize operations for an accepted deploy - (found: {finalize_operations:?}, expected: {finalize:?})" 705 )); 706 } 707 }, 708 // Note: This will abort the entire atomic batch. 709 Err(error) => { 710 return Err(format!("Failed to finalize an accepted deploy transaction - {error}")); 711 } 712 }; 713 Ok(()) 714 } 715 ConfirmedTransaction::AcceptedExecute(_, transaction, finalize) => { 716 // Extract the execution and fee from the transaction. 717 let (execution, fee) = match transaction { 718 Transaction::Execute(_, _, execution, fee) => (execution, fee), 719 // Note: This will abort the entire atomic batch. 720 _ => return Err("Expected execute transaction".to_string()), 721 }; 722 // The finalize operation here involves calling 'update_key_value', 723 // and update the respective leaves of the finalize tree. 724 match process.finalize_execution(state, store, execution, fee.as_ref()) { 725 // Ensure the finalize operations match the expected. 726 Ok(finalize_operations) => { 727 if finalize != &finalize_operations { 728 // Note: This will abort the entire atomic batch. 729 return Err(format!( 730 "Mismatch in finalize operations for an accepted execute - (found: {finalize_operations:?}, expected: {finalize:?})" 731 )); 732 } 733 } 734 // Note: This will abort the entire atomic batch. 735 Err(error) => { 736 return Err(format!("Failed to finalize an accepted execute transaction - {error}")); 737 } 738 } 739 Ok(()) 740 } 741 ConfirmedTransaction::RejectedDeploy(_, Transaction::Fee(_, fee), rejected, finalize) => { 742 // Extract the rejected deployment. 743 let Some(deployment) = rejected.deployment() else { 744 // Note: This will abort the entire atomic batch. 745 return Err("Expected rejected deployment".to_string()); 746 }; 747 // Compute the expected deployment ID. 748 let Ok(expected_deployment_id) = deployment.to_deployment_id() else { 749 // Note: This will abort the entire atomic batch. 750 return Err("Failed to compute the deployment ID for a rejected deployment".to_string()); 751 }; 752 // Retrieve the candidate deployment ID. 753 let Ok(candidate_deployment_id) = fee.deployment_or_execution_id() else { 754 // Note: This will abort the entire atomic batch. 755 return Err("Failed to retrieve the deployment ID from the fee".to_string()); 756 }; 757 // Ensure this fee corresponds to the deployment. 758 if candidate_deployment_id != expected_deployment_id { 759 // Note: This will abort the entire atomic batch. 760 return Err("Mismatch in fee for a rejected deploy transaction".to_string()); 761 } 762 // Lastly, finalize the fee. 763 match process.finalize_fee(state, store, fee) { 764 // Ensure the finalize operations match the expected. 765 Ok(finalize_operations) => { 766 if finalize != &finalize_operations { 767 // Note: This will abort the entire atomic batch. 768 return Err(format!( 769 "Mismatch in finalize operations for a rejected deploy - (found: {finalize_operations:?}, expected: {finalize:?})" 770 )); 771 } 772 } 773 // Note: This will abort the entire atomic batch. 774 Err(_e) => { 775 return Err("Failed to finalize the fee in a rejected deploy transaction".to_string()); 776 } 777 } 778 Ok(()) 779 } 780 ConfirmedTransaction::RejectedExecute(_, Transaction::Fee(_, fee), rejected, finalize) => { 781 // Extract the rejected execution. 782 let Some(execution) = rejected.execution() else { 783 // Note: This will abort the entire atomic batch. 784 return Err("Expected rejected execution".to_string()); 785 }; 786 // Compute the expected execution ID. 787 let Ok(expected_execution_id) = execution.to_execution_id() else { 788 // Note: This will abort the entire atomic batch. 789 return Err("Failed to compute the execution ID for a rejected execution".to_string()); 790 }; 791 // Retrieve the candidate execution ID. 792 let Ok(candidate_execution_id) = fee.deployment_or_execution_id() else { 793 // Note: This will abort the entire atomic batch. 794 return Err("Failed to retrieve the execution ID from the fee".to_string()); 795 }; 796 // Ensure this fee corresponds to the execution. 797 if candidate_execution_id != expected_execution_id { 798 // Note: This will abort the entire atomic batch. 799 return Err("Mismatch in fee for a rejected execute transaction".to_string()); 800 } 801 // Lastly, finalize the fee. 802 match process.finalize_fee(state, store, fee) { 803 // Ensure the finalize operations match the expected. 804 Ok(finalize_operations) => { 805 if finalize != &finalize_operations { 806 // Note: This will abort the entire atomic batch. 807 return Err(format!( 808 "Mismatch in finalize operations for a rejected execute - (found: {finalize_operations:?}, expected: {finalize:?})" 809 )); 810 } 811 } 812 // Note: This will abort the entire atomic batch. 813 Err(_e) => { 814 return Err("Failed to finalize the fee in a rejected execute transaction".to_string()); 815 } 816 } 817 Ok(()) 818 } 819 // Note: This will abort the entire atomic batch. 820 _ => return Err("Invalid confirmed transaction type".to_string()), 821 }; 822 lap!(timer, "Finalizing transaction {}", transaction.id()); 823 824 match outcome { 825 // If the transaction succeeded to finalize, continue to the next transaction. 826 Ok(()) => (), 827 // If the transaction failed to finalize, abort and continue to the next transaction. 828 Err(error) => { 829 error!("Critical bug in finalize: {error}\n\n{transaction}"); 830 dev_eprintln!("Critical bug in finalize: {error}\n\n{transaction}"); 831 // Note: This will abort the entire atomic batch. 832 return Err(format!("Failed to finalize on transaction - {error}")); 833 } 834 } 835 } 836 837 /* Perform the ratifications after finalize. */ 838 839 match Self::atomic_post_ratify::<true>(&self.puzzle, store, state, post_ratifications, solutions) { 840 // Store the finalize operations from the post-ratify. 841 Ok(operations) => ratified_finalize_operations.extend(operations), 842 // Note: This will abort the entire atomic batch. 843 Err(e) => return Err(format!("Failed to post-ratify - {e}")), 844 } 845 846 /* Start the commit process. */ 847 848 // Commit all the stacks to the process. 849 process.commit_stacks(); 850 851 finish!(timer); // <- Note: This timer does **not** include the time to write batch to DB. 852 853 Ok(ratified_finalize_operations) 854 }) 855 } 856 857 /// Returns `Some(reason)` if the transaction is aborted. Otherwise, returns `None`. 858 /// 859 /// The transaction will be aborted if any of the following conditions are met: 860 /// - The transaction is producing a duplicate transition 861 /// - The transaction is double-spending an input 862 /// - The transaction is producing a duplicate output 863 /// - The transaction is producing a duplicate transition public key 864 /// - The transaction is another deployment in the block from the same public fee payer. 865 /// - The transaction contains a transition that has been deployed or upgraded in this block. 866 /// 867 /// - Note: If a transaction is a deployment for a program following its deployment or redeployment in this block, 868 /// it is not aborted. Instead, it will be rejected and its fee will be consumed. 869 #[allow(clippy::too_many_arguments)] 870 fn should_abort_transaction( 871 &self, 872 transaction: &Transaction<N>, 873 transition_ids: &IndexSet<N::TransitionID>, 874 input_ids: &IndexSet<Field<N>>, 875 output_ids: &IndexSet<Field<N>>, 876 tpks: &IndexSet<Group<N>>, 877 deployment_payers: &IndexSet<Address<N>>, 878 deployments: &IndexSet<ProgramID<N>>, 879 ) -> Option<String> { 880 // Ensure that: 881 // - the transaction is not producing a duplicate transition. 882 // - the programs in the component transitions haven't been deployed or upgraded in this block. 883 for transition in transaction.transitions() { 884 // Get the transition ID. 885 let transition_id = transition.id(); 886 // If the transition ID is already produced in this block or previous blocks, abort the transaction. 887 if transition_ids.contains(transition_id) 888 || self.transition_store().contains_transition_id(transition_id).unwrap_or(true) 889 { 890 return Some(format!("Duplicate transition {transition_id}")); 891 } 892 // If the transition's program is being deployed or redeployed in this block, abort the transaction. 893 if deployments.contains(transition.program_id()) { 894 return Some(format!( 895 "Program {} is being deployed or redeployed in this block", 896 transition.program_id() 897 )); 898 } 899 } 900 901 // Ensure that the transaction is not double-spending an input. 902 for input_id in transaction.input_ids() { 903 // If the input ID is already spent in this block or previous blocks, abort the transaction. 904 if input_ids.contains(input_id) || self.transition_store().contains_input_id(input_id).unwrap_or(true) { 905 return Some(format!("Double-spending input {input_id}")); 906 } 907 } 908 909 // Ensure that the transaction is not producing a duplicate output. 910 for output_id in transaction.output_ids() { 911 // If the output ID is already produced in this block or previous blocks, abort the transaction. 912 if output_ids.contains(output_id) || self.transition_store().contains_output_id(output_id).unwrap_or(true) { 913 return Some(format!("Duplicate output {output_id}")); 914 } 915 } 916 917 // Ensure that the transaction is not producing a duplicate transition public key. 918 // Note that the tpk and tcm are corresponding, so a uniqueness check for just the tpk is sufficient. 919 for tpk in transaction.transition_public_keys() { 920 // If the transition public key is already produced in this block or previous blocks, abort the transaction. 921 if tpks.contains(tpk) || self.transition_store().contains_tpk(tpk).unwrap_or(true) { 922 return Some(format!("Duplicate transition public key {tpk}")); 923 } 924 } 925 926 // If the transaction is a deployment, ensure that it is not another deployment in the block from the same public fee payer. 927 if let Transaction::Deploy(_, _, _, _, fee) = transaction { 928 // If any public deployment payer has already deployed in this block, abort the transaction. 929 if let Some(payer) = fee.payer() { 930 if deployment_payers.contains(&payer) { 931 return Some(format!("Another deployment in the block from the same public fee payer {payer}")); 932 } 933 } 934 } 935 936 // Return `None` because the transaction is well-formed. 937 None 938 } 939 940 /// Performs precondition checks on the transactions prior to speculation. 941 /// 942 /// This method is used to check the following conditions: 943 /// - If a transaction is a fee transaction or if it is invalid, 944 /// then the transaction will be aborted. 945 pub(crate) fn prepare_for_speculate<'a, R: CryptoRng + Rng>( 946 &self, 947 transactions: &[&'a Transaction<N>], 948 rng: &mut R, 949 ) -> Result<(Vec<&'a Transaction<N>>, Vec<(&'a Transaction<N>, String)>)> { 950 // Construct the list of transactions that need to verified. 951 let mut transactions_to_verify = Vec::with_capacity(transactions.len()); 952 // Construct the list of valid and invalid transactions. 953 let mut valid_transactions = Vec::with_capacity(transactions.len()); 954 let mut aborted_transactions = Vec::with_capacity(transactions.len()); 955 956 // Initialize a list of created transition IDs. 957 let mut transition_ids: IndexSet<N::TransitionID> = Default::default(); 958 // Initialize a list of spent input IDs. 959 let mut input_ids: IndexSet<Field<N>> = Default::default(); 960 // Initialize a list of created output IDs. 961 let mut output_ids: IndexSet<Field<N>> = Default::default(); 962 // Initialize the list of created transition public keys. 963 let mut tpks: IndexSet<Group<N>> = Default::default(); 964 // Initialize the list of deployment payers. 965 let mut deployment_payers: IndexSet<Address<N>> = Default::default(); 966 // Initialize a list of the successful deployments. 967 let mut deployments = IndexSet::new(); 968 969 // Abort the transactions that are have duplicates or are invalid. This will prevent the VM from performing 970 // verification on transactions that would have been aborted in `VM::atomic_speculate`. 971 for transaction in transactions.iter() { 972 // Abort the transaction early if it is a fee transaction. 973 if transaction.is_fee() { 974 aborted_transactions.push((*transaction, "Fee transactions are not allowed in speculate".to_string())); 975 continue; 976 } 977 978 // Determine if the transaction should be aborted. 979 match self.should_abort_transaction( 980 transaction, 981 &transition_ids, 982 &input_ids, 983 &output_ids, 984 &tpks, 985 &deployment_payers, 986 &deployments, 987 ) { 988 // Store the aborted transaction. 989 Some(reason) => aborted_transactions.push((*transaction, reason.to_string())), 990 // Track the transaction state. 991 None => { 992 // Add the transition IDs to the set of produced transition IDs. 993 transition_ids.extend(transaction.transition_ids()); 994 // Add the input IDs to the set of spent input IDs. 995 input_ids.extend(transaction.input_ids()); 996 // Add the output IDs to the set of produced output IDs. 997 output_ids.extend(transaction.output_ids()); 998 // Add the transition public keys to the set of produced transition public keys. 999 tpks.extend(transaction.transition_public_keys()); 1000 // Add the program owner to the set of deployment payers and the program ID to the set of deployments. 1001 if let Transaction::Deploy(_, _, _, deployment, fee) = transaction { 1002 fee.payer().map(|payer| deployment_payers.insert(payer)); 1003 deployments.insert(*deployment.program_id()); 1004 } 1005 1006 // Add the transaction to the list of transactions to verify. 1007 transactions_to_verify.push(transaction); 1008 } 1009 }; 1010 } 1011 1012 // Separate the transactions into deploys and executions. 1013 let (deployments, executions): (Vec<&Transaction<N>>, Vec<&Transaction<N>>) = 1014 transactions_to_verify.into_iter().partition(|tx| tx.is_deploy()); 1015 // Chunk the deploys and executions into groups for parallel verification. 1016 let deployments_for_verification = deployments.chunks(Self::MAX_PARALLEL_DEPLOY_VERIFICATIONS); 1017 let executions_for_verification = executions.chunks(Self::MAX_PARALLEL_EXECUTE_VERIFICATIONS); 1018 1019 // Verify the transactions in batches and separate the valid and invalid transactions. 1020 for transactions in deployments_for_verification.chain(executions_for_verification) { 1021 let rngs = (0..transactions.len()).map(|_| StdRng::from_seed(rng.r#gen())).collect::<Vec<_>>(); 1022 // Verify the transactions and collect the error message if there is one. 1023 let (valid, invalid): (Vec<_>, Vec<_>) = 1024 cfg_into_iter!(transactions).zip(rngs).partition_map(|(transaction, mut rng)| { 1025 // Verify the transaction. 1026 match self.check_transaction(transaction, None, &mut rng) { 1027 // If the transaction is valid, add it to the list of valid transactions. 1028 Ok(_) => Either::Left(*transaction), 1029 // If the transaction is invalid, add it to the list of aborted transactions. 1030 Err(e) => Either::Right((*transaction, e.to_string())), 1031 } 1032 }); 1033 1034 // Collect the valid and aborted transactions. 1035 valid_transactions.extend(valid); 1036 aborted_transactions.extend(invalid); 1037 } 1038 1039 // Sort the valid and aborted transactions based on their position in the original list. 1040 let position: IndexSet<_> = transactions.iter().map(|tx| tx.id()).collect(); 1041 cfg_sort_by_cached_key!(valid_transactions, |tx| position.get_index_of(&tx.id())); 1042 cfg_sort_by_cached_key!(aborted_transactions, |tx| position.get_index_of(&tx.0.id())); 1043 1044 // Return the valid and invalid transactions. 1045 Ok((valid_transactions, aborted_transactions)) 1046 } 1047 1048 /// Performs precondition checks on the transaction prior to execution. 1049 /// 1050 /// This method is used to check the following conditions: 1051 /// - If the transaction contains a `credits.delta/bond_public` transition, 1052 /// then the outcome should not exceed the maximum committee size. 1053 #[inline] 1054 fn prepare_for_execution( 1055 state: FinalizeGlobalState, 1056 store: &FinalizeStore<N, C::FinalizeStorage>, 1057 execution: &Execution<N>, 1058 ) -> Result<()> { 1059 // Construct the program ID. 1060 let program_id = ProgramID::from_str("credits.delta")?; 1061 // Construct the committee mapping name. 1062 let committee_mapping = Identifier::from_str("committee")?; 1063 1064 // Check if the execution has any `bond_validator` transitions, and collect 1065 // the unique validator addresses if so. 1066 // Note: This does not dedup for existing and new validator addresses. 1067 let bond_validator_addresses: HashSet<_> = execution 1068 .transitions() 1069 .filter_map(|transition| match transition.is_bond_validator() { 1070 // Get the first argument of the transition output if it is a `Future` with a `Plaintext` argument. 1071 true => match transition.outputs().first() { 1072 Some(Output::Future(_, Some(future))) => future.arguments().first().and_then(|arg| match arg { 1073 Argument::Plaintext(Plaintext::Literal(Literal::Address(address), _)) => Some(*address), 1074 _ => None, 1075 }), 1076 _ => None, 1077 }, 1078 false => None, 1079 }) 1080 .collect(); 1081 1082 // Check if we need to reject the execution if the number of new validators exceeds the maximum committee size. 1083 match bond_validator_addresses.is_empty() { 1084 false => { 1085 // Retrieve the committee members from storage. 1086 let committee_members = store 1087 .get_mapping_speculative(program_id, committee_mapping)? 1088 .into_iter() 1089 .map(|(key, _)| match key { 1090 // Extract the address from the key. 1091 Plaintext::Literal(Literal::Address(address), _) => Ok(address), 1092 _ => Err(anyhow!("Invalid committee key (missing address) - {key}")), 1093 }) 1094 .collect::<Result<HashSet<_>>>()?; 1095 // Get the number of new validators being bonded to. 1096 let num_new_validators = 1097 bond_validator_addresses.into_iter().filter(|address| !committee_members.contains(address)).count(); 1098 // Compute the next committee size. 1099 let next_committee_size = committee_members.len().saturating_add(num_new_validators); 1100 // Determine the maximum committee size to use. 1101 let max_committee_size = consensus_config_value!(N, MAX_CERTIFICATES, state.block_height()) 1102 .ok_or(anyhow!("Failed to retrieve the maximum committee size"))?; 1103 // Check that the number of new validators being bonded does not exceed the maximum number of validators. 1104 match next_committee_size > max_committee_size as usize { 1105 true => Err(anyhow!("Call to 'credits.delta/bond_public' exceeds the committee size")), 1106 false => Ok(()), 1107 } 1108 } 1109 true => Ok(()), 1110 } 1111 } 1112 1113 /// Performs the pre-ratifications before finalizing transactions. 1114 #[inline] 1115 fn atomic_pre_ratify<'a>( 1116 store: &FinalizeStore<N, C::FinalizeStorage>, 1117 state: FinalizeGlobalState, 1118 pre_ratifications: impl Iterator<Item = &'a Ratify<N>>, 1119 ) -> Result<Vec<FinalizeOperation<N>>> { 1120 // Construct the program ID. 1121 let program_id = ProgramID::from_str("credits.delta")?; 1122 // Construct the committee mapping name. 1123 let committee_mapping = Identifier::from_str("committee")?; 1124 // Construct the delegated mapping name. 1125 let delegated_mapping: Identifier<N> = Identifier::from_str("delegated")?; 1126 // Construct the bonded mapping name. 1127 let bonded_mapping = Identifier::from_str("bonded")?; 1128 // Construct the account mapping name. 1129 let account_mapping = Identifier::from_str("account")?; 1130 // Construct the metadata mapping name. 1131 let metadata_mapping = Identifier::from_str("metadata")?; 1132 // Construct the withdraw mapping name. 1133 let withdraw_mapping = Identifier::from_str("withdraw")?; 1134 1135 // Initialize a list of finalize operations. 1136 let mut finalize_operations = Vec::new(); 1137 1138 // Initialize a flag for the genesis ratification. 1139 let mut is_genesis_ratified = false; 1140 1141 // Iterate over the ratifications. 1142 for ratify in pre_ratifications { 1143 match ratify { 1144 Ratify::Genesis(committee, public_balances, bonded_balances) => { 1145 // Ensure this is the genesis block. 1146 ensure!(state.block_height() == 0, "Ratify::Genesis(..) expected a genesis block"); 1147 // Ensure the genesis committee round is 0. 1148 ensure!( 1149 committee.starting_round() == 0, 1150 "Ratify::Genesis(..) expected a genesis committee round of 0" 1151 ); 1152 // Ensure that the number of members in the committee does not exceed the maximum. 1153 let max_committee_size = consensus_config_value!(N, MAX_CERTIFICATES, state.block_height()) 1154 .ok_or(anyhow!("Ratify::Genesis(..) failed to retrieve the maximum committee size"))?; 1155 ensure!( 1156 committee.members().len() <= max_committee_size as usize, 1157 "Ratify::Genesis(..) exceeds the maximum number of committee members" 1158 ); 1159 // Ensure that the number of delegators does not exceed the maximum. 1160 ensure!( 1161 bonded_balances.len().saturating_sub(committee.members().len()) <= MAX_DELEGATORS as usize, 1162 "Ratify::Genesis(..) exceeds the maximum number of delegators" 1163 ); 1164 // Ensure genesis has not been ratified yet. 1165 ensure!(!is_genesis_ratified, "Ratify::Genesis(..) has already been ratified"); 1166 1167 // TODO (howardwu): Consider whether to initialize the mappings here. 1168 // Currently, this is breaking for test cases that use VM but do not insert the genesis block. 1169 // // Initialize the store for 'credits.delta'. 1170 // let credits = Program::<N>::credits()?; 1171 // for mapping in credits.mappings().values() { 1172 // // Ensure that all mappings are initialized. 1173 // if !store.contains_mapping_confirmed(credits.id(), mapping.name())? { 1174 // // Initialize the mappings for 'credits.delta'. 1175 // finalize_operations.push(store.initialize_mapping(*credits.id(), *mapping.name())?); 1176 // } 1177 // } 1178 1179 // Calculate the stake per validator using `bonded_balances`. 1180 // 1181 // Note: There is no need to check the `delegated` mapping in the genesis block, 1182 // because the design of `bonded_balances` by definition does not support 1183 // delegating to a non-bonded validator. Thus, the assumption is that the 1184 // `delegated` mapping will be correct by construction. 1185 let mut stake_per_validator = IndexMap::with_capacity(committee.members().len()); 1186 for (address, (validator_address, _, amount)) in bonded_balances.iter() { 1187 // Check that the amount meets the minimum requirement, depending on whether the address is a validator. 1188 if *address == *validator_address { 1189 ensure!( 1190 *amount >= MIN_VALIDATOR_SELF_STAKE, 1191 "Ratify::Genesis(..) the validator {address} must stake at least {MIN_VALIDATOR_SELF_STAKE}", 1192 ); 1193 } else { 1194 ensure!( 1195 *amount >= MIN_DELEGATOR_STAKE, 1196 "Ratify::Genesis(..) the delegator {address} must stake at least {MIN_DELEGATOR_STAKE}", 1197 ); 1198 // If the corresponding validator is not a committee member yet, then continue. 1199 if !committee.is_committee_member(*validator_address) { 1200 continue; 1201 } 1202 // If the address is a delegator, check that the corresponding validator is open. 1203 ensure!( 1204 committee.is_committee_member_open(*validator_address), 1205 "Ratify::Genesis(..) the delegator {address} is delegating to a closed validator {validator_address}", 1206 ); 1207 } 1208 // Accumulate the staked amount per validator. 1209 let total = stake_per_validator.entry(validator_address).or_insert(0u64); 1210 *total = total.saturating_add(*amount); 1211 } 1212 // Ensure the stake per validator matches the committee. 1213 ensure!( 1214 stake_per_validator.len() == committee.members().len(), 1215 "Ratify::Genesis(..) the number of validators in the committee does not match the number of validators in the bonded balances", 1216 ); 1217 1218 // Check that `committee` is consistent with `stake_per_validator`. 1219 for (validator_address, amount) in &stake_per_validator { 1220 // Retrieve the expected validator stake from the committee. 1221 let Some((expected_amount, _, _)) = committee.members().get(*validator_address) else { 1222 bail!( 1223 "Ratify::Genesis(..) found a validator in the bonded balances that is not in the committee" 1224 ) 1225 }; 1226 // Ensure the staked amount matches the committee. 1227 ensure!( 1228 *expected_amount == *amount, 1229 "Ratify::Genesis(..) inconsistent staked amount for validator {validator_address}", 1230 ); 1231 } 1232 // Ensure that the total stake matches the sum of the staked amounts. 1233 ensure!( 1234 committee.total_stake() == stake_per_validator.values().sum::<u64>(), 1235 "Ratify::Genesis(..) incorrect total total stake for the committee" 1236 ); 1237 1238 // Split the bonded balances into stakers and withdrawal addresses. 1239 let (next_stakers, withdrawal_addresses) = bonded_balances.iter().fold( 1240 ( 1241 IndexMap::with_capacity(bonded_balances.len()), 1242 IndexMap::with_capacity(bonded_balances.len()), 1243 ), 1244 |(mut stakers, mut withdrawal_addresses), (staker, (validator, withdrawal_address, amount))| { 1245 stakers.insert(*staker, (*validator, *amount)); 1246 withdrawal_addresses.insert(*staker, *withdrawal_address); 1247 (stakers, withdrawal_addresses) 1248 }, 1249 ); 1250 1251 let next_delegated = to_next_delegated(&next_stakers); 1252 1253 // Construct the next committee map and next bonded map. 1254 let (next_committee_map, next_bonded_map, next_delegated_map) = 1255 to_next_committee_bonded_delegated_map(committee, &next_stakers, &next_delegated); 1256 1257 // Construct the next withdraw map. 1258 let next_withdraw_map = to_next_withdraw_map(&withdrawal_addresses); 1259 1260 // Insert the next committee into storage. 1261 store.committee_store().insert(state.block_height(), *(committee.clone()))?; 1262 // Store the finalize operations for updating the committee and bonded mapping. 1263 finalize_operations.extend(&[ 1264 // Replace the committee mapping in storage. 1265 store.replace_mapping(program_id, committee_mapping, next_committee_map)?, 1266 // Replace the delegated mapping in storage. 1267 store.replace_mapping(program_id, delegated_mapping, next_delegated_map)?, 1268 // Replace the bonded mapping in storage. 1269 store.replace_mapping(program_id, bonded_mapping, next_bonded_map)?, 1270 // Replace the withdraw mapping in storage. 1271 store.replace_mapping(program_id, withdraw_mapping, next_withdraw_map)?, 1272 ]); 1273 1274 // Update the number of validators. 1275 finalize_operations.extend(&[ 1276 // Update the number of validators in the metadata mapping. 1277 store.update_key_value( 1278 program_id, 1279 metadata_mapping, 1280 Plaintext::from_str("dx1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3qtczd")?, 1281 Value::from_str(&format!("{}u32", committee.num_members()))?, 1282 )?, 1283 ]); 1284 1285 // Update the number of delegators. 1286 finalize_operations.extend(&[ 1287 // Update the number of delegators in the metadata mapping. 1288 store.update_key_value( 1289 program_id, 1290 metadata_mapping, 1291 Plaintext::from_str("dx1qgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqavzal6")?, 1292 Value::from_str(&format!( 1293 "{}u32", 1294 bonded_balances.len().saturating_sub(committee.num_members()) 1295 ))?, 1296 )?, 1297 ]); 1298 1299 // Map the public balances into the appropriate format. 1300 let public_balances = public_balances 1301 .iter() 1302 .map(|(address, amount)| { 1303 (Plaintext::from(Literal::Address(*address)), Value::from(Literal::U64(U64::new(*amount)))) 1304 }) 1305 .collect::<Vec<_>>(); 1306 1307 // Update the public balances. 1308 finalize_operations.extend(&[ 1309 // Update the public balances in storage. 1310 store.replace_mapping(program_id, account_mapping, public_balances)?, 1311 ]); 1312 1313 // Set the genesis ratification flag. 1314 is_genesis_ratified = true; 1315 } 1316 Ratify::BlockReward(..) | Ratify::PuzzleReward(..) => continue, 1317 } 1318 } 1319 1320 // Return the finalize operations. 1321 Ok(finalize_operations) 1322 } 1323 1324 /// Performs the post-ratifications after finalizing transactions. 1325 #[inline] 1326 fn atomic_post_ratify<'a, const IS_FINALIZE: bool>( 1327 puzzle: &Puzzle<N>, 1328 store: &FinalizeStore<N, C::FinalizeStorage>, 1329 state: FinalizeGlobalState, 1330 post_ratifications: impl Iterator<Item = &'a Ratify<N>>, 1331 solutions: &Solutions<N>, 1332 ) -> Result<Vec<FinalizeOperation<N>>> { 1333 // Construct the program ID. 1334 let program_id = ProgramID::from_str("credits.delta")?; 1335 // Construct the committee mapping name. 1336 let committee_mapping = Identifier::from_str("committee")?; 1337 // Construct the delegated mapping name. 1338 let delegated_mapping = Identifier::from_str("delegated")?; 1339 // Construct the bonded mapping name. 1340 let bonded_mapping = Identifier::from_str("bonded")?; 1341 // Construct the account mapping name. 1342 let account_mapping = Identifier::from_str("account")?; 1343 1344 // Initialize a list of finalize operations. 1345 let mut finalize_operations = Vec::new(); 1346 1347 // Initialize a flag for the block reward ratification. 1348 let mut is_block_reward_ratified = false; 1349 // Initialize a flag for the puzzle reward ratification. 1350 let mut is_puzzle_reward_ratified = false; 1351 1352 // Iterate over the ratifications. 1353 for ratify in post_ratifications { 1354 match ratify { 1355 Ratify::Genesis(..) => continue, 1356 Ratify::BlockReward(block_reward) => { 1357 // Ensure the block reward has not been ratified yet. 1358 ensure!(!is_block_reward_ratified, "Ratify::BlockReward(..) has already been ratified"); 1359 1360 // Retrieve the committee mapping from storage. 1361 let current_committee_map = store.get_mapping_speculative(program_id, committee_mapping)?; 1362 // Retrieve the delegator mapping from storage. 1363 let current_delegator_map = store.get_mapping_speculative(program_id, delegated_mapping)?; 1364 // Convert the committee mapping into a committee. 1365 let current_committee = committee_and_delegated_maps_into_committee( 1366 state.block_round(), 1367 current_committee_map, 1368 current_delegator_map, 1369 )?; 1370 // Retrieve the bonded mapping from storage. 1371 let current_bonded_map = store.get_mapping_speculative(program_id, bonded_mapping)?; 1372 // Convert the bonded map into stakers. 1373 let current_stakers = bonded_map_into_stakers(current_bonded_map)?; 1374 1375 // Ensure the committee matches the bonded mapping. 1376 ensure_stakers_matches(¤t_committee, ¤t_stakers)?; 1377 1378 // Compute the updated stakers, using the committee and block reward. 1379 let next_stakers = staking_rewards(¤t_stakers, ¤t_committee, *block_reward); 1380 1381 // Compute the updated delegated amounts, using the next_stakers updated amounts. 1382 let next_delegated = to_next_delegated(&next_stakers); 1383 1384 // Compute the updated committee, using the delegatees. 1385 let next_committee = to_next_committee(¤t_committee, state.block_round(), &next_delegated)?; 1386 1387 // Construct the next committee map, the next bonded map, and the next delegated map. 1388 let (next_committee_map, next_bonded_map, next_delegated_map) = 1389 to_next_committee_bonded_delegated_map(&next_committee, &next_stakers, &next_delegated); 1390 1391 // Insert the next committee into storage. 1392 store.committee_store().insert(state.block_height(), next_committee)?; 1393 1394 #[cfg(all(feature = "history", feature = "rocks"))] 1395 { 1396 // When finalizing in `FinalizeMode::RealRun`, store the delegated and bonded mappings in history. 1397 if IS_FINALIZE { 1398 // Load a `History` object. 1399 let history = History::new(N::ID, store.storage_mode()); 1400 1401 // Write the delegated mapping as JSON. 1402 history.store_mapping(state.block_height(), MappingName::Delegated, &next_delegated_map)?; 1403 1404 // Write the bonded mapping as JSON. 1405 history.store_mapping(state.block_height(), MappingName::Bonded, &next_bonded_map)?; 1406 1407 // Write the metadata mapping as JSON. 1408 let metadata_mapping = Identifier::from_str("metadata")?; 1409 let metadata_map = store.get_mapping_speculative(program_id, metadata_mapping)?; 1410 history.store_mapping(state.block_height(), MappingName::Metadata, &metadata_map)?; 1411 1412 // Write the unbonding mapping as JSON. 1413 let unbonding_mapping = Identifier::from_str("unbonding")?; 1414 let unbonding_map = store.get_mapping_speculative(program_id, unbonding_mapping)?; 1415 history.store_mapping(state.block_height(), MappingName::Unbonding, &unbonding_map)?; 1416 1417 // Write the withdraw mapping as JSON. 1418 let withdraw_mapping = Identifier::from_str("withdraw")?; 1419 let withdraw_map = store.get_mapping_speculative(program_id, withdraw_mapping)?; 1420 history.store_mapping(state.block_height(), MappingName::Withdraw, &withdraw_map)?; 1421 } 1422 } 1423 1424 // Store the finalize operations for updating the committee and bonded mapping. 1425 finalize_operations.extend(&[ 1426 // Replace the committee mapping in storage. 1427 store.replace_mapping(program_id, committee_mapping, next_committee_map)?, 1428 // Replace the delegated mapping in storage. 1429 store.replace_mapping(program_id, delegated_mapping, next_delegated_map)?, 1430 // Replace the bonded mapping in storage. 1431 store.replace_mapping(program_id, bonded_mapping, next_bonded_map)?, 1432 ]); 1433 1434 // Set the block reward ratification flag. 1435 is_block_reward_ratified = true; 1436 } 1437 Ratify::PuzzleReward(puzzle_reward) => { 1438 // Ensure the puzzle reward has not been ratified yet. 1439 ensure!(!is_puzzle_reward_ratified, "Ratify::PuzzleReward(..) has already been ratified"); 1440 1441 // If the puzzle reward is zero, skip. 1442 if *puzzle_reward == 0 { 1443 continue; 1444 } 1445 // Retrieve the solutions. 1446 let Some(solutions) = solutions.deref() else { 1447 continue; 1448 }; 1449 // Compute the proof targets, with the corresponding addresses. 1450 let proof_targets = solutions 1451 .values() 1452 .map(|s| Ok((s.address(), puzzle.get_proof_target(s)?))) 1453 .collect::<Result<Vec<_>>>()?; 1454 // Calculate the proving rewards. 1455 let proving_rewards = proving_rewards(proof_targets, *puzzle_reward); 1456 // Iterate over the proving rewards. 1457 for (address, amount) in proving_rewards { 1458 // Construct the key. 1459 let key = Plaintext::from(Literal::Address(address)); 1460 // Retrieve the current public balance. 1461 let value = store.get_value_speculative(program_id, account_mapping, &key)?; 1462 // Compute the next public balance. 1463 let next_value = Value::from(Literal::U64(U64::new(match value { 1464 Some(Value::Plaintext(Plaintext::Literal(Literal::U64(value), _))) => { 1465 (*value).saturating_add(amount) 1466 } 1467 None => amount, 1468 v => bail!("Critical bug in post-ratify puzzle reward- Invalid amount ({v:?})"), 1469 }))); 1470 // Update the public balance in finalize storage. 1471 let operation = store.update_key_value(program_id, account_mapping, key, next_value)?; 1472 finalize_operations.push(operation); 1473 } 1474 1475 // Set the puzzle reward ratification flag. 1476 is_puzzle_reward_ratified = true; 1477 } 1478 } 1479 } 1480 1481 // Return the finalize operations. 1482 Ok(finalize_operations) 1483 } 1484 } 1485 1486 #[cfg(test)] 1487 mod tests { 1488 use super::*; 1489 use crate::vm::{ 1490 test_helpers, 1491 test_helpers::{sample_finalize_state, sample_vm}, 1492 }; 1493 use deltavm_ledger_block::{Block, Header, Metadata, Transaction, Transition}; 1494 use deltavm_ledger_committee::{MAX_DELEGATORS, MIN_VALIDATOR_STAKE}; 1495 use deltavm_synthesizer_program::Program; 1496 use console::{ 1497 account::{Address, PrivateKey, ViewKey}, 1498 program::{Ciphertext, Entry, Record}, 1499 types::Field, 1500 }; 1501 1502 use rand::distributions::DistString; 1503 1504 type CurrentNetwork = test_helpers::CurrentNetwork; 1505 #[cfg(not(feature = "rocks"))] 1506 type LedgerType = deltavm_ledger_store::helpers::memory::ConsensusMemory<CurrentNetwork>; 1507 #[cfg(feature = "rocks")] 1508 type LedgerType = deltavm_ledger_store::helpers::rocksdb::ConsensusDB<CurrentNetwork>; 1509 1510 /// Sample a new program and deploy it to the VM. Returns the program name. 1511 fn new_program_deployment<R: Rng + CryptoRng>( 1512 vm: &VM<CurrentNetwork, LedgerType>, 1513 private_key: &PrivateKey<CurrentNetwork>, 1514 previous_block: &Block<CurrentNetwork>, 1515 unspent_records: &mut Vec<Record<CurrentNetwork, Ciphertext<CurrentNetwork>>>, 1516 rng: &mut R, 1517 ) -> Result<(String, Block<CurrentNetwork>)> { 1518 let program_name = format!("a{}.delta", Alphanumeric.sample_string(rng, 8).to_lowercase()); 1519 1520 let program = Program::<CurrentNetwork>::from_str(&format!( 1521 " 1522 program {program_name}; 1523 1524 mapping account: 1525 // The token owner. 1526 key as address.public; 1527 // The token amount. 1528 value as u64.public; 1529 1530 function mint_public: 1531 input r0 as address.public; 1532 input r1 as u64.public; 1533 async mint_public r0 r1 into r2; 1534 output r2 as {program_name}/mint_public.future; 1535 1536 finalize mint_public: 1537 input r0 as address.public; 1538 input r1 as u64.public; 1539 1540 get.or_use account[r0] 0u64 into r2; 1541 add r2 r1 into r3; 1542 set r3 into account[r0]; 1543 1544 function transfer_public: 1545 input r0 as address.public; 1546 input r1 as u64.public; 1547 async transfer_public self.caller r0 r1 into r2; 1548 output r2 as {program_name}/transfer_public.future; 1549 1550 finalize transfer_public: 1551 input r0 as address.public; 1552 input r1 as address.public; 1553 input r2 as u64.public; 1554 1555 get.or_use account[r0] 0u64 into r3; 1556 get.or_use account[r1] 0u64 into r4; 1557 1558 sub r3 r2 into r5; 1559 add r4 r2 into r6; 1560 1561 set r5 into account[r0]; 1562 set r6 into account[r1];" 1563 ))?; 1564 1565 // Prepare the additional fee. 1566 let view_key = ViewKey::<CurrentNetwork>::try_from(private_key)?; 1567 let credits = Some(unspent_records.pop().unwrap().decrypt(&view_key)?); 1568 1569 // Deploy. 1570 let transaction = vm.deploy(private_key, &program, credits, 10, None, rng)?; 1571 1572 // Construct the new block. 1573 let next_block = sample_next_block(vm, private_key, &[transaction], previous_block, unspent_records, rng)?; 1574 1575 Ok((program_name, next_block)) 1576 } 1577 1578 /// Construct a new block based on the given transactions. 1579 fn sample_next_block<R: Rng + CryptoRng>( 1580 vm: &VM<CurrentNetwork, LedgerType>, 1581 private_key: &PrivateKey<CurrentNetwork>, 1582 transactions: &[Transaction<CurrentNetwork>], 1583 previous_block: &Block<CurrentNetwork>, 1584 unspent_records: &mut Vec<Record<CurrentNetwork, Ciphertext<CurrentNetwork>>>, 1585 rng: &mut R, 1586 ) -> Result<Block<CurrentNetwork>> { 1587 // Create the finalize state for the next block height. 1588 let next_block_height = previous_block.height() + 1; 1589 let time_since_last_block = MainnetV0::BLOCK_TIME as i64; 1590 let next_block_timestamp = previous_block.timestamp().saturating_add(time_since_last_block); 1591 let next_timestamp = (next_block_height 1592 >= MainnetV0::CONSENSUS_HEIGHT(ConsensusVersion::V12).unwrap_or_default()) 1593 .then_some(next_block_timestamp); 1594 let finalize_state = 1595 FinalizeGlobalState::from(next_block_height as u64, next_block_height, next_timestamp, [0u8; 32]); 1596 1597 // Speculate on the candidate ratifications, solutions, and transactions. 1598 let (ratifications, transactions, aborted_transaction_ids, ratified_finalize_operations) = 1599 vm.speculate(finalize_state, time_since_last_block, None, vec![], &None.into(), transactions.iter(), rng)?; 1600 1601 // Construct the metadata associated with the block. 1602 let metadata = Metadata::new( 1603 CurrentNetwork::ID, 1604 previous_block.round() + 1, 1605 previous_block.height() + 1, 1606 0, 1607 0, 1608 CurrentNetwork::GENESIS_COINBASE_TARGET, 1609 CurrentNetwork::GENESIS_PROOF_TARGET, 1610 previous_block.last_coinbase_target(), 1611 previous_block.last_coinbase_timestamp(), 1612 next_block_timestamp, 1613 )?; 1614 1615 // Construct the new block header. 1616 let header = Header::from( 1617 vm.block_store().current_state_root(), 1618 transactions.to_transactions_root().unwrap(), 1619 transactions.to_finalize_root(ratified_finalize_operations).unwrap(), 1620 ratifications.to_ratifications_root().unwrap(), 1621 Field::zero(), 1622 Field::zero(), 1623 metadata, 1624 )?; 1625 1626 let block = Block::new_beacon( 1627 private_key, 1628 previous_block.hash(), 1629 header, 1630 ratifications, 1631 None.into(), 1632 vec![], 1633 transactions, 1634 aborted_transaction_ids, 1635 rng, 1636 )?; 1637 1638 // Track the new records. 1639 let new_records = block 1640 .transitions() 1641 .cloned() 1642 .flat_map(Transition::into_records) 1643 .map(|(_, record)| record) 1644 .collect::<Vec<_>>(); 1645 unspent_records.extend(new_records); 1646 1647 Ok(block) 1648 } 1649 1650 /// Generate split transactions for the unspent records. 1651 fn generate_splits<R: Rng + CryptoRng>( 1652 vm: &VM<CurrentNetwork, LedgerType>, 1653 private_key: &PrivateKey<CurrentNetwork>, 1654 previous_block: &Block<CurrentNetwork>, 1655 unspent_records: &mut Vec<Record<CurrentNetwork, Ciphertext<CurrentNetwork>>>, 1656 rng: &mut R, 1657 ) -> Result<Block<CurrentNetwork>> { 1658 // Prepare the additional fee. 1659 let view_key = ViewKey::<CurrentNetwork>::try_from(private_key)?; 1660 1661 // Generate split transactions. 1662 let mut transactions = Vec::new(); 1663 while !unspent_records.is_empty() { 1664 let record = unspent_records.pop().unwrap().decrypt(&view_key)?; 1665 1666 // Fetch the record balance and divide it in half. 1667 let split_balance = match record.find(&[Identifier::from_str("microcredits")?]) { 1668 Ok(Entry::Private(Plaintext::Literal(Literal::U64(amount), _))) => *amount / 2, 1669 _ => bail!("fee record does not contain a microcredits entry"), 1670 }; 1671 1672 // Prepare the inputs. 1673 let inputs = [ 1674 Value::<CurrentNetwork>::Record(record), 1675 Value::<CurrentNetwork>::from_str(&format!("{split_balance}u64")).unwrap(), 1676 ] 1677 .into_iter(); 1678 1679 // Execute. 1680 let transaction = vm.execute(private_key, ("credits.delta", "split"), inputs, None, 0, None, rng).unwrap(); 1681 1682 transactions.push(transaction); 1683 } 1684 1685 // Construct the new block. 1686 sample_next_block(vm, private_key, &transactions, previous_block, unspent_records, rng) 1687 } 1688 1689 /// Create an execution transaction. 1690 fn create_execution( 1691 vm: &VM<CurrentNetwork, LedgerType>, 1692 caller_private_key: PrivateKey<CurrentNetwork>, 1693 program_id: &str, 1694 function_name: &str, 1695 inputs: Vec<Value<CurrentNetwork>>, 1696 unspent_records: &mut Vec<Record<CurrentNetwork, Ciphertext<CurrentNetwork>>>, 1697 rng: &mut TestRng, 1698 ) -> Transaction<CurrentNetwork> { 1699 assert!(vm.contains_program(&ProgramID::from_str(program_id).unwrap())); 1700 1701 // Prepare the additional fee. 1702 let view_key = ViewKey::<CurrentNetwork>::try_from(caller_private_key).unwrap(); 1703 let unspent_record = unspent_records.pop().unwrap(); 1704 let credits = Some(unspent_record.decrypt(&view_key).unwrap()); 1705 1706 // Execute. 1707 let transaction = vm 1708 .execute(&caller_private_key, (program_id, function_name), inputs.into_iter(), credits, 1, None, rng) 1709 .unwrap(); 1710 // Verify. 1711 vm.check_transaction(&transaction, None, rng).unwrap(); 1712 1713 // Return the transaction. 1714 transaction 1715 } 1716 1717 /// Sample a public mint transaction. 1718 fn sample_mint_public( 1719 vm: &VM<CurrentNetwork, LedgerType>, 1720 caller_private_key: PrivateKey<CurrentNetwork>, 1721 program_id: &str, 1722 recipient: Address<CurrentNetwork>, 1723 amount: u64, 1724 unspent_records: &mut Vec<Record<CurrentNetwork, Ciphertext<CurrentNetwork>>>, 1725 rng: &mut TestRng, 1726 ) -> Transaction<CurrentNetwork> { 1727 let inputs = vec![ 1728 Value::<CurrentNetwork>::from_str(&recipient.to_string()).unwrap(), 1729 Value::<CurrentNetwork>::from_str(&format!("{amount}u64")).unwrap(), 1730 ]; 1731 1732 create_execution(vm, caller_private_key, program_id, "mint_public", inputs, unspent_records, rng) 1733 } 1734 1735 /// Sample a public transfer transaction. 1736 fn sample_transfer_public( 1737 vm: &VM<CurrentNetwork, LedgerType>, 1738 caller_private_key: PrivateKey<CurrentNetwork>, 1739 program_id: &str, 1740 recipient: Address<CurrentNetwork>, 1741 amount: u64, 1742 unspent_records: &mut Vec<Record<CurrentNetwork, Ciphertext<CurrentNetwork>>>, 1743 rng: &mut TestRng, 1744 ) -> Transaction<CurrentNetwork> { 1745 let inputs = vec![ 1746 Value::<CurrentNetwork>::from_str(&recipient.to_string()).unwrap(), 1747 Value::<CurrentNetwork>::from_str(&format!("{amount}u64")).unwrap(), 1748 ]; 1749 1750 create_execution(vm, caller_private_key, program_id, "transfer_public", inputs, unspent_records, rng) 1751 } 1752 1753 /// A helper method to construct the rejected transaction format for `atomic_finalize`. 1754 fn reject( 1755 index: u32, 1756 transaction: &Transaction<CurrentNetwork>, 1757 finalize: &[FinalizeOperation<CurrentNetwork>], 1758 ) -> ConfirmedTransaction<CurrentNetwork> { 1759 match transaction { 1760 Transaction::Execute(_, _, execution, fee) => ConfirmedTransaction::RejectedExecute( 1761 index, 1762 Transaction::from_fee(fee.clone().unwrap()).unwrap(), 1763 Rejected::new_execution(*execution.clone()), 1764 finalize.to_vec(), 1765 ), 1766 _ => panic!("only reject execution transactions"), 1767 } 1768 } 1769 1770 /// Samples the validators. 1771 fn sample_validators<N: Network>( 1772 num_validators: usize, 1773 rng: &mut TestRng, 1774 ) -> IndexMap<PrivateKey<N>, (u64, bool, u8)> { 1775 (0..num_validators) 1776 .map(|_| { 1777 let private_key = PrivateKey::new(rng).unwrap(); 1778 let amount = MIN_VALIDATOR_STAKE; 1779 let is_open = true; 1780 let commission: u8 = 0; 1781 (private_key, (amount, is_open, commission)) 1782 }) 1783 .collect::<IndexMap<_, _>>() 1784 } 1785 1786 /// Returns a `committee_map` and the `allocated_amount` given the validators and delegators. 1787 fn sample_committee_map_and_allocated_amount<N: Network>( 1788 validators: &IndexMap<PrivateKey<N>, (u64, bool, u8)>, 1789 delegators: &IndexMap<PrivateKey<N>, (Address<N>, u64)>, 1790 ) -> (IndexMap<Address<N>, (u64, bool, u8)>, u64) { 1791 // Reset the tracked amount. 1792 let mut allocated_amount = 0; 1793 1794 // Construct the **correct** committee. 1795 let mut committee_map = IndexMap::new(); 1796 for (private_key, (amount, is_open, commission)) in validators { 1797 let address = Address::try_from(private_key).unwrap(); 1798 committee_map.insert(address, (*amount, *is_open, *commission)); 1799 allocated_amount += amount; 1800 } 1801 for (delegator, (validator, amount)) in delegators { 1802 if let indexmap::map::Entry::Occupied(mut entry) = committee_map.entry(*validator) { 1803 let (current_amount, is_open, commission) = entry.get(); 1804 // Ensure the validator is open. 1805 assert!(*is_open, "delegator {delegator} is delegating {amount} microcredits to a closed validator"); 1806 // Update the committee map. 1807 entry.insert((current_amount + amount, *is_open, *commission)); 1808 } else { 1809 unreachable!("delegator {delegator} is delegating to a closed validator") 1810 } 1811 // Accumulate the allocated amount. 1812 allocated_amount += amount; 1813 } 1814 1815 (committee_map, allocated_amount) 1816 } 1817 1818 /// Returns the `bonded_balances` given the validators and delegators. 1819 /// Note that the withdrawal address is the same as the staker address. 1820 fn sample_bonded_balances<N: Network>( 1821 validators: &IndexMap<PrivateKey<N>, (u64, bool, u8)>, 1822 delegators: &IndexMap<PrivateKey<N>, (Address<N>, u64)>, 1823 ) -> IndexMap<Address<N>, (Address<N>, Address<N>, u64)> { 1824 let mut bonded_balances = IndexMap::with_capacity(validators.len() + delegators.len()); 1825 for (private_key, (amount, _, _)) in validators { 1826 let address = Address::try_from(private_key).unwrap(); 1827 bonded_balances.insert(address, (address, address, *amount)); 1828 } 1829 for (private_key, (validator, amount)) in delegators { 1830 let address = Address::try_from(private_key).unwrap(); 1831 bonded_balances.insert(address, (*validator, address, *amount)); 1832 } 1833 bonded_balances 1834 } 1835 1836 /// Returns the `public_balances` given the addresses and total amount. 1837 /// Note that the balances are evenly distributed among the addresses. 1838 fn sample_public_balances<N: Network>(addresses: &[Address<N>], total_amount: u64) -> IndexMap<Address<N>, u64> { 1839 // Check that the addresses are not empty. 1840 assert!(!addresses.is_empty(), "must provide at least one address"); 1841 // Distribute the total amount evenly among the addresses. 1842 let amount_per_address = total_amount / addresses.len() as u64; 1843 let mut public_balances: IndexMap<_, _> = 1844 addresses.iter().map(|address| (*address, amount_per_address)).collect(); 1845 // Distribute the remainder to the first address. 1846 let remaining = total_amount % addresses.len() as u64; 1847 if remaining > 0 { 1848 *public_balances.get_mut(&addresses[0]).unwrap() += remaining; 1849 } 1850 // Return the public balances. 1851 public_balances 1852 } 1853 1854 #[test] 1855 fn test_finalize_duplicate_deployment() { 1856 let rng = &mut TestRng::default(); 1857 1858 let vm = crate::vm::test_helpers::sample_vm(); 1859 1860 // Fetch a deployment transaction. 1861 let deployment_transaction = crate::vm::test_helpers::sample_deployment_transaction(rng); 1862 let deployment_transaction_id = deployment_transaction.id(); 1863 1864 // Construct the program name. 1865 let program_id = ProgramID::from_str("testing.delta").unwrap(); 1866 1867 // Prepare the confirmed transactions. 1868 let (ratifications, confirmed_transactions, aborted_transaction_ids, _) = vm 1869 .speculate( 1870 sample_finalize_state(1), 1871 CurrentNetwork::BLOCK_TIME as i64, 1872 None, 1873 vec![], 1874 &None.into(), 1875 [deployment_transaction.clone()].iter(), 1876 rng, 1877 ) 1878 .unwrap(); 1879 assert_eq!(confirmed_transactions.len(), 1); 1880 assert!(aborted_transaction_ids.is_empty()); 1881 1882 // Ensure the VM does not contain this program. 1883 assert!(!vm.contains_program(&program_id)); 1884 1885 // Finalize the transaction. 1886 assert!(vm.finalize(sample_finalize_state(1), &ratifications, &None.into(), &confirmed_transactions).is_ok()); 1887 1888 // Ensure the VM contains this program. 1889 assert!(vm.contains_program(&program_id)); 1890 1891 // Ensure the VM can't redeploy the same transaction. 1892 assert!(vm.finalize(sample_finalize_state(1), &ratifications, &None.into(), &confirmed_transactions).is_err()); 1893 1894 // Ensure the VM contains this program. 1895 assert!(vm.contains_program(&program_id)); 1896 1897 // Ensure the dry run of the redeployment will cause a reject transaction to be created. 1898 let (_, candidate_transactions, aborted_transaction_ids, _) = vm 1899 .atomic_speculate( 1900 sample_finalize_state(1), 1901 CurrentNetwork::BLOCK_TIME as i64, 1902 None, 1903 vec![], 1904 None.into(), 1905 vec![deployment_transaction], 1906 ) 1907 .unwrap(); 1908 assert_eq!(candidate_transactions.len(), 1); 1909 assert!(matches!(candidate_transactions[0], ConfirmedTransaction::RejectedDeploy(..))); 1910 assert!(aborted_transaction_ids.is_empty()); 1911 1912 // Check that the unconfirmed transaction ID of the rejected deployment is correct. 1913 assert_eq!(candidate_transactions[0].to_unconfirmed_transaction_id().unwrap(), deployment_transaction_id); 1914 } 1915 1916 #[test] 1917 fn test_bond_validator_above_maximum_fails() { 1918 // Initialize an RNG. 1919 let rng = &mut TestRng::default(); 1920 1921 // Initialize the VM. 1922 let vm = sample_vm(); 1923 1924 // Initialize the validators with the maximum number of validators. 1925 let validators = sample_validators::<CurrentNetwork>( 1926 consensus_config_value!(CurrentNetwork, MAX_CERTIFICATES, 0).unwrap() as usize, 1927 rng, 1928 ); 1929 1930 // Initialize a new address. 1931 let new_validator_private_key = PrivateKey::<CurrentNetwork>::new(rng).unwrap(); 1932 let new_validator_address = Address::try_from(&new_validator_private_key).unwrap(); 1933 1934 // Construct the committee. 1935 // Track the allocated amount. 1936 let (committee_map, allocated_amount) = 1937 sample_committee_map_and_allocated_amount(&validators, &IndexMap::new()); 1938 1939 // Collect all of the addresses in a single place 1940 let validator_addresses = 1941 validators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(); 1942 1943 // Construct the public balances, allocating the remaining supply. 1944 let new_validator_balance = MIN_VALIDATOR_STAKE + 100_000_000; 1945 let mut public_balances = sample_public_balances( 1946 &validator_addresses, 1947 <CurrentNetwork as Network>::STARTING_SUPPLY - allocated_amount - new_validator_balance, 1948 ); 1949 // Set the public balance of the new validator to the minimum validator stake. 1950 public_balances.insert(new_validator_address, new_validator_balance); 1951 1952 // Construct the bonded balances. 1953 let bonded_balances = sample_bonded_balances(&validators, &IndexMap::new()); 1954 1955 // Construct the genesis block, which should pass. 1956 let block = vm 1957 .genesis_quorum( 1958 validators.keys().next().unwrap(), 1959 Committee::new_genesis(committee_map).unwrap(), 1960 public_balances, 1961 bonded_balances, 1962 rng, 1963 ) 1964 .unwrap(); 1965 1966 // Add the block. 1967 vm.add_next_block(&block).unwrap(); 1968 1969 // Attempt to bond a new validator above the maximum number of validators. 1970 let inputs = vec![ 1971 Value::<CurrentNetwork>::from_str(&validator_addresses.first().unwrap().to_string()).unwrap(), // Withdrawal address 1972 Value::<CurrentNetwork>::from_str(&format!("{MIN_VALIDATOR_STAKE}u64")).unwrap(), // Amount 1973 Value::<CurrentNetwork>::from_str("42u8").unwrap(), // Commission 1974 ]; 1975 1976 // Execute. 1977 let bond_validator_transaction = vm 1978 .execute( 1979 &new_validator_private_key, 1980 ("credits.delta", "bond_validator"), 1981 inputs.into_iter(), 1982 None, 1983 1, 1984 None, 1985 rng, 1986 ) 1987 .unwrap(); 1988 1989 // Verify. 1990 vm.check_transaction(&bond_validator_transaction, None, rng).unwrap(); 1991 1992 // Speculate on the transactions. 1993 let transactions = vec![bond_validator_transaction.clone()]; 1994 let (_, confirmed_transactions, _, _) = vm 1995 .atomic_speculate( 1996 sample_finalize_state(1), 1997 CurrentNetwork::BLOCK_TIME as i64, 1998 None, 1999 vec![], 2000 None.into(), 2001 transactions, 2002 ) 2003 .unwrap(); 2004 2005 // Assert that the transaction is rejected. 2006 assert_eq!(confirmed_transactions.len(), 1); 2007 assert_eq!( 2008 confirmed_transactions[0], 2009 reject(0, &bond_validator_transaction, confirmed_transactions[0].finalize_operations()) 2010 ); 2011 } 2012 2013 #[cfg(feature = "test")] 2014 #[test] 2015 fn test_genesis_num_validators_does_not_exceed_maximum_before_v3() { 2016 // This test will fail if the consensus v3 height is 0 2017 assert_ne!(0, CurrentNetwork::CONSENSUS_HEIGHT(ConsensusVersion::V3).unwrap()); 2018 2019 // Initialize an RNG. 2020 let rng = &mut TestRng::default(); 2021 2022 // TODO: Fix this test by adding additional constraints to `Committee::new_genesis` 2023 // Initialize the validators with the maximum number of validators before consensus v3. 2024 let validators = sample_validators::<CurrentNetwork>( 2025 consensus_config_value!(CurrentNetwork, MAX_CERTIFICATES, 0).unwrap() as usize + 5, 2026 rng, 2027 ); 2028 2029 // Construct the committee. 2030 // Track the allocated amount. 2031 let (committee_map, _allocated_amount) = 2032 sample_committee_map_and_allocated_amount(&validators, &IndexMap::new()); 2033 2034 assert!(Committee::new_genesis(committee_map).is_err()); 2035 } 2036 2037 #[cfg(not(feature = "test"))] 2038 #[test] 2039 #[allow(clippy::assertions_on_constants)] 2040 fn test_migration_v3_maximum_validator_increase() { 2041 // This test will fail if the consensus v3 height is 0 2042 assert_ne!(0, CurrentNetwork::CONSENSUS_HEIGHT(ConsensusVersion::V3).unwrap()); 2043 2044 // Initialize an RNG. 2045 let rng = &mut TestRng::default(); 2046 2047 // Initialize the VM. 2048 let vm = sample_vm(); 2049 2050 // Initialize the validators with the maximum number of validators before consensus v3. 2051 let validators = sample_validators::<CurrentNetwork>( 2052 consensus_config_value!(CurrentNetwork, MAX_CERTIFICATES, 0).unwrap() as usize, 2053 rng, 2054 ); 2055 2056 // Initialize a new address. 2057 let new_validator_private_key = PrivateKey::<CurrentNetwork>::new(rng).unwrap(); 2058 let new_validator_address = Address::try_from(&new_validator_private_key).unwrap(); 2059 2060 // Construct the committee. 2061 // Track the allocated amount. 2062 let (committee_map, allocated_amount) = 2063 sample_committee_map_and_allocated_amount(&validators, &IndexMap::new()); 2064 2065 // Collect all of the addresses in a single place 2066 let validator_addresses = 2067 validators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(); 2068 2069 // Construct the public balances, allocating the remaining supply. 2070 let new_validator_balance = MIN_VALIDATOR_STAKE + 100_000_000; 2071 let mut public_balances = sample_public_balances( 2072 &validator_addresses, 2073 <CurrentNetwork as Network>::STARTING_SUPPLY - allocated_amount - new_validator_balance, 2074 ); 2075 // Set the public balance of the new validator to the minimum validator stake. 2076 public_balances.insert(new_validator_address, new_validator_balance); 2077 2078 // Construct the bonded balances. 2079 let bonded_balances = sample_bonded_balances(&validators, &IndexMap::new()); 2080 2081 // Construct the genesis block, which should pass. 2082 let block = vm 2083 .genesis_quorum( 2084 validators.keys().next().unwrap(), 2085 Committee::new_genesis(committee_map).unwrap(), 2086 public_balances, 2087 bonded_balances, 2088 rng, 2089 ) 2090 .unwrap(); 2091 2092 // Add the block. 2093 vm.add_next_block(&block).unwrap(); 2094 2095 // Attempt to bond a new validator above the maximum number of validators. 2096 let inputs = vec![ 2097 Value::<CurrentNetwork>::from_str(&validator_addresses.first().unwrap().to_string()).unwrap(), // Withdrawal address 2098 Value::<CurrentNetwork>::from_str(&format!("{MIN_VALIDATOR_STAKE}u64")).unwrap(), // Amount 2099 Value::<CurrentNetwork>::from_str("42u8").unwrap(), // Commission 2100 ]; 2101 2102 // Execute. 2103 let bond_validator_transaction = vm 2104 .execute( 2105 &new_validator_private_key, 2106 ("credits.delta", "bond_validator"), 2107 inputs.clone().into_iter(), 2108 None, 2109 1, 2110 None, 2111 rng, 2112 ) 2113 .unwrap(); 2114 2115 // Verify. 2116 vm.check_transaction(&bond_validator_transaction, None, rng).unwrap(); 2117 2118 // Speculate on the transactions. 2119 let transactions = vec![bond_validator_transaction.clone()]; 2120 let (_, confirmed_transactions, _, _) = vm 2121 .atomic_speculate( 2122 sample_finalize_state(1), 2123 CurrentNetwork::BLOCK_TIME as i64, 2124 None, 2125 vec![], 2126 None.into(), 2127 transactions, 2128 ) 2129 .unwrap(); 2130 2131 // Assert that the transaction is rejected. 2132 assert_eq!(confirmed_transactions.len(), 1); 2133 assert_eq!( 2134 confirmed_transactions[0], 2135 reject(0, &bond_validator_transaction, confirmed_transactions[0].finalize_operations()) 2136 ); 2137 2138 // Speculate on the transactions. 2139 let transactions = vec![bond_validator_transaction.clone()]; 2140 let (_, confirmed_transactions, aborted_transaction_ids, _) = vm 2141 .atomic_speculate( 2142 sample_finalize_state(CurrentNetwork::CONSENSUS_HEIGHT(ConsensusVersion::V3).unwrap()), 2143 CurrentNetwork::BLOCK_TIME as i64, 2144 None, 2145 vec![], 2146 None.into(), 2147 transactions, 2148 ) 2149 .unwrap(); 2150 2151 // Assert that the transaction is accepted. 2152 assert_eq!(confirmed_transactions.len(), 1); 2153 assert!(confirmed_transactions[0].is_accepted()); 2154 assert!(aborted_transaction_ids.is_empty()); 2155 2156 assert_eq!(confirmed_transactions[0].transaction(), &bond_validator_transaction); 2157 } 2158 2159 #[test] 2160 fn test_atomic_finalize_many() { 2161 let rng = &mut TestRng::default(); 2162 2163 // Sample a private key and address for the caller. 2164 let caller_private_key = test_helpers::sample_genesis_private_key(rng); 2165 let caller_address = Address::try_from(&caller_private_key).unwrap(); 2166 2167 // Sample a private key and address for the recipient. 2168 let recipient_private_key = PrivateKey::new(rng).unwrap(); 2169 let recipient_address = Address::try_from(&recipient_private_key).unwrap(); 2170 2171 // Initialize the vm. 2172 let vm = test_helpers::sample_vm_with_genesis_block(rng); 2173 2174 // Deploy a new program. 2175 let genesis = 2176 vm.block_store().get_block(&vm.block_store().get_block_hash(0).unwrap().unwrap()).unwrap().unwrap(); 2177 2178 // Get the unspent records. 2179 let mut unspent_records = genesis 2180 .transitions() 2181 .cloned() 2182 .flat_map(Transition::into_records) 2183 .map(|(_, record)| record) 2184 .collect::<Vec<_>>(); 2185 2186 // Construct the deployment block. 2187 let (program_id, deployment_block) = 2188 new_program_deployment(&vm, &caller_private_key, &genesis, &mut unspent_records, rng).unwrap(); 2189 2190 // Add the deployment block to the VM. 2191 vm.add_next_block(&deployment_block).unwrap(); 2192 2193 // Generate more records to use for the next block. 2194 let splits_block = 2195 generate_splits(&vm, &caller_private_key, &deployment_block, &mut unspent_records, rng).unwrap(); 2196 2197 // Add the splits block to the VM. 2198 vm.add_next_block(&splits_block).unwrap(); 2199 2200 // Construct the initial mint. 2201 let initial_mint = 2202 sample_mint_public(&vm, caller_private_key, &program_id, caller_address, 20, &mut unspent_records, rng); 2203 let initial_mint_block = 2204 sample_next_block(&vm, &caller_private_key, &[initial_mint], &splits_block, &mut unspent_records, rng) 2205 .unwrap(); 2206 2207 // Add the block to the vm. 2208 vm.add_next_block(&initial_mint_block).unwrap(); 2209 2210 // Construct a mint and a transfer. 2211 let mint_10 = 2212 sample_mint_public(&vm, caller_private_key, &program_id, caller_address, 10, &mut unspent_records, rng); 2213 let mint_20 = 2214 sample_mint_public(&vm, caller_private_key, &program_id, caller_address, 20, &mut unspent_records, rng); 2215 let transfer_10 = sample_transfer_public( 2216 &vm, 2217 caller_private_key, 2218 &program_id, 2219 recipient_address, 2220 10, 2221 &mut unspent_records, 2222 rng, 2223 ); 2224 let transfer_20 = sample_transfer_public( 2225 &vm, 2226 caller_private_key, 2227 &program_id, 2228 recipient_address, 2229 20, 2230 &mut unspent_records, 2231 rng, 2232 ); 2233 let transfer_30 = sample_transfer_public( 2234 &vm, 2235 caller_private_key, 2236 &program_id, 2237 recipient_address, 2238 30, 2239 &mut unspent_records, 2240 rng, 2241 ); 2242 2243 // TODO (raychu86): Confirm that the finalize_operations here are correct. 2244 2245 // Starting Balance = 20 2246 // Mint_10 -> Balance = 20 + 10 = 30 2247 // Transfer_10 -> Balance = 30 - 10 = 20 2248 // Transfer_20 -> Balance = 20 - 20 = 0 2249 { 2250 let transactions = vec![mint_10.clone(), transfer_10.clone(), transfer_20.clone()]; 2251 let (_, confirmed_transactions, aborted_transaction_ids, _) = vm 2252 .atomic_speculate( 2253 sample_finalize_state(1), 2254 CurrentNetwork::BLOCK_TIME as i64, 2255 None, 2256 vec![], 2257 None.into(), 2258 transactions, 2259 ) 2260 .unwrap(); 2261 2262 // Assert that all the transactions are accepted. 2263 assert_eq!(confirmed_transactions.len(), 3); 2264 confirmed_transactions.iter().for_each(|confirmed_tx| assert!(confirmed_tx.is_accepted())); 2265 assert!(aborted_transaction_ids.is_empty()); 2266 2267 assert_eq!(confirmed_transactions[0].transaction(), &mint_10); 2268 assert_eq!(confirmed_transactions[1].transaction(), &transfer_10); 2269 assert_eq!(confirmed_transactions[2].transaction(), &transfer_20); 2270 } 2271 2272 // Starting Balance = 20 2273 // Transfer_20 -> Balance = 20 - 20 = 0 2274 // Mint_10 -> Balance = 0 + 10 = 10 2275 // Mint_20 -> Balance = 10 + 20 = 30 2276 // Transfer_30 -> Balance = 30 - 30 = 0 2277 { 2278 let transactions = vec![transfer_20.clone(), mint_10.clone(), mint_20.clone(), transfer_30.clone()]; 2279 let (_, confirmed_transactions, aborted_transaction_ids, _) = vm 2280 .atomic_speculate( 2281 sample_finalize_state(1), 2282 CurrentNetwork::BLOCK_TIME as i64, 2283 None, 2284 vec![], 2285 None.into(), 2286 transactions, 2287 ) 2288 .unwrap(); 2289 2290 // Assert that all the transactions are accepted. 2291 assert_eq!(confirmed_transactions.len(), 4); 2292 confirmed_transactions.iter().for_each(|confirmed_tx| assert!(confirmed_tx.is_accepted())); 2293 assert!(aborted_transaction_ids.is_empty()); 2294 2295 // Ensure that the transactions are in the correct order. 2296 assert_eq!(confirmed_transactions[0].transaction(), &transfer_20); 2297 assert_eq!(confirmed_transactions[1].transaction(), &mint_10); 2298 assert_eq!(confirmed_transactions[2].transaction(), &mint_20); 2299 assert_eq!(confirmed_transactions[3].transaction(), &transfer_30); 2300 } 2301 2302 // Starting Balance = 20 2303 // Transfer_20 -> Balance = 20 - 20 = 0 2304 // Transfer_10 -> Balance = 0 - 10 = -10 (should be rejected) 2305 { 2306 let transactions = vec![transfer_20.clone(), transfer_10.clone()]; 2307 let (_, confirmed_transactions, aborted_transaction_ids, _) = vm 2308 .atomic_speculate( 2309 sample_finalize_state(1), 2310 CurrentNetwork::BLOCK_TIME as i64, 2311 None, 2312 vec![], 2313 None.into(), 2314 transactions, 2315 ) 2316 .unwrap(); 2317 2318 // Assert that the accepted and rejected transactions are correct. 2319 assert_eq!(confirmed_transactions.len(), 2); 2320 assert!(aborted_transaction_ids.is_empty()); 2321 2322 assert!(confirmed_transactions[0].is_accepted()); 2323 assert!(confirmed_transactions[1].is_rejected()); 2324 2325 assert_eq!(confirmed_transactions[0].transaction(), &transfer_20); 2326 assert_eq!( 2327 confirmed_transactions[1], 2328 reject(1, &transfer_10, confirmed_transactions[1].finalize_operations()) 2329 ); 2330 } 2331 2332 // Starting Balance = 20 2333 // Mint_20 -> Balance = 20 + 20 2334 // Transfer_30 -> Balance = 40 - 30 = 10 2335 // Transfer_20 -> Balance = 10 - 20 = -10 (should be rejected) 2336 // Transfer_10 -> Balance = 10 - 10 = 0 2337 { 2338 let transactions = vec![mint_20.clone(), transfer_30.clone(), transfer_20.clone(), transfer_10.clone()]; 2339 let (_, confirmed_transactions, aborted_transaction_ids, _) = vm 2340 .atomic_speculate( 2341 sample_finalize_state(1), 2342 CurrentNetwork::BLOCK_TIME as i64, 2343 None, 2344 vec![], 2345 None.into(), 2346 transactions, 2347 ) 2348 .unwrap(); 2349 2350 // Assert that the accepted and rejected transactions are correct. 2351 assert_eq!(confirmed_transactions.len(), 4); 2352 assert!(aborted_transaction_ids.is_empty()); 2353 2354 assert!(confirmed_transactions[0].is_accepted()); 2355 assert!(confirmed_transactions[1].is_accepted()); 2356 assert!(confirmed_transactions[2].is_rejected()); 2357 assert!(confirmed_transactions[3].is_accepted()); 2358 2359 assert_eq!(confirmed_transactions[0].transaction(), &mint_20); 2360 assert_eq!(confirmed_transactions[1].transaction(), &transfer_30); 2361 assert_eq!( 2362 confirmed_transactions[2], 2363 reject(2, &transfer_20, confirmed_transactions[2].finalize_operations()) 2364 ); 2365 assert_eq!(confirmed_transactions[3].transaction(), &transfer_10); 2366 } 2367 } 2368 2369 #[test] 2370 fn test_finalize_catch_halt() { 2371 let rng = &mut TestRng::default(); 2372 2373 // Sample a private key, view key, and address for the caller. 2374 let caller_private_key = test_helpers::sample_genesis_private_key(rng); 2375 let caller_view_key = ViewKey::try_from(&caller_private_key).unwrap(); 2376 2377 for finalize_logic in &[ 2378 "finalize ped_hash: 2379 input r0 as u128.public; 2380 hash.ped64 r0 into r1 as field; 2381 set r1 into hashes[r0];", 2382 "finalize ped_hash: 2383 input r0 as u128.public; 2384 div r0 0u128 into r1;", 2385 ] { 2386 // Initialize the vm. 2387 let vm = test_helpers::sample_vm_with_genesis_block(rng); 2388 2389 // Deploy a new program. 2390 let genesis = 2391 vm.block_store().get_block(&vm.block_store().get_block_hash(0).unwrap().unwrap()).unwrap().unwrap(); 2392 2393 // Get the unspent records. 2394 let mut unspent_records = genesis 2395 .transitions() 2396 .cloned() 2397 .flat_map(Transition::into_records) 2398 .map(|(_, record)| record) 2399 .collect::<Vec<_>>(); 2400 2401 // Create a program that will always cause a E::halt in the finalize execution. 2402 let program_id = "testing.delta"; 2403 let program = Program::<CurrentNetwork>::from_str(&format!( 2404 " 2405 program {program_id}; 2406 2407 mapping hashes: 2408 key as u128.public; 2409 value as field.public; 2410 2411 function ped_hash: 2412 input r0 as u128.public; 2413 // hash.ped64 r0 into r1 as field; // <--- This will cause a E::halt. 2414 async ped_hash r0 into r1; 2415 output r1 as {program_id}/ped_hash.future; 2416 2417 {finalize_logic}" 2418 )) 2419 .unwrap(); 2420 2421 let credits = Some(unspent_records.pop().unwrap().decrypt(&caller_view_key).unwrap()); 2422 2423 // Deploy the program. 2424 let deployment_transaction = vm.deploy(&caller_private_key, &program, credits, 10, None, rng).unwrap(); 2425 2426 // Construct the deployment block. 2427 let deployment_block = sample_next_block( 2428 &vm, 2429 &caller_private_key, 2430 &[deployment_transaction], 2431 &genesis, 2432 &mut unspent_records, 2433 rng, 2434 ) 2435 .unwrap(); 2436 2437 // Add the deployment block to the VM. 2438 vm.add_next_block(&deployment_block).unwrap(); 2439 2440 // Construct a transaction that will cause a E::halt in the finalize execution. 2441 let inputs = vec![Value::<CurrentNetwork>::from_str("1u128").unwrap()]; 2442 let transaction = 2443 create_execution(&vm, caller_private_key, program_id, "ped_hash", inputs, &mut unspent_records, rng); 2444 2445 // Speculatively execute the transaction. Ensure that this call does not panic and returns a rejected transaction. 2446 let (_, confirmed_transactions, aborted_transaction_ids, _) = vm 2447 .speculate( 2448 sample_finalize_state(1), 2449 CurrentNetwork::BLOCK_TIME as i64, 2450 None, 2451 vec![], 2452 &None.into(), 2453 [transaction.clone()].iter(), 2454 rng, 2455 ) 2456 .unwrap(); 2457 assert!(aborted_transaction_ids.is_empty()); 2458 2459 // Ensure that the transaction is rejected. 2460 assert_eq!(confirmed_transactions.len(), 1); 2461 assert!(transaction.is_execute()); 2462 if let Transaction::Execute(_, _, execution, fee) = transaction { 2463 let fee_transaction = Transaction::from_fee(fee.unwrap()).unwrap(); 2464 let expected_confirmed_transaction = ConfirmedTransaction::RejectedExecute( 2465 0, 2466 fee_transaction, 2467 Rejected::new_execution(*execution), 2468 vec![], 2469 ); 2470 2471 let confirmed_transaction = confirmed_transactions.iter().next().unwrap(); 2472 assert_eq!(confirmed_transaction, &expected_confirmed_transaction); 2473 } 2474 } 2475 } 2476 2477 #[test] 2478 fn test_rejected_transaction_should_not_update_storage() { 2479 let rng = &mut TestRng::default(); 2480 2481 // Sample a private key. 2482 let private_key = test_helpers::sample_genesis_private_key(rng); 2483 let address = Address::try_from(&private_key).unwrap(); 2484 2485 // Initialize the vm. 2486 let vm = test_helpers::sample_vm_with_genesis_block(rng); 2487 2488 // Deploy a new program. 2489 let genesis = 2490 vm.block_store().get_block(&vm.block_store().get_block_hash(0).unwrap().unwrap()).unwrap().unwrap(); 2491 2492 // Get the unspent records. 2493 let mut unspent_records = genesis 2494 .transitions() 2495 .cloned() 2496 .flat_map(Transition::into_records) 2497 .map(|(_, record)| record) 2498 .collect::<Vec<_>>(); 2499 2500 // Generate more records to use for the next block. 2501 let splits_block = generate_splits(&vm, &private_key, &genesis, &mut unspent_records, rng).unwrap(); 2502 2503 // Add the splits block to the VM. 2504 vm.add_next_block(&splits_block).unwrap(); 2505 2506 // Construct the deployment block. 2507 let deployment_block = { 2508 let program = Program::<CurrentNetwork>::from_str( 2509 " 2510 program testing.delta; 2511 2512 mapping entries: 2513 key as address.public; 2514 value as u8.public; 2515 2516 function compute: 2517 input r0 as u8.public; 2518 async compute self.caller r0 into r1; 2519 output r1 as testing.delta/compute.future; 2520 2521 finalize compute: 2522 input r0 as address.public; 2523 input r1 as u8.public; 2524 get.or_use entries[r0] r1 into r2; 2525 add r1 r2 into r3; 2526 set r3 into entries[r0]; 2527 get entries[r0] into r4; 2528 add r4 r1 into r5; 2529 set r5 into entries[r0]; 2530 ", 2531 ) 2532 .unwrap(); 2533 2534 // Prepare the additional fee. 2535 let view_key = ViewKey::<CurrentNetwork>::try_from(private_key).unwrap(); 2536 let credits = Some(unspent_records.pop().unwrap().decrypt(&view_key).unwrap()); 2537 2538 // Deploy. 2539 let transaction = vm.deploy(&private_key, &program, credits, 10, None, rng).unwrap(); 2540 2541 // Construct the new block. 2542 sample_next_block(&vm, &private_key, &[transaction], &splits_block, &mut unspent_records, rng).unwrap() 2543 }; 2544 2545 // Add the deployment block to the VM. 2546 vm.add_next_block(&deployment_block).unwrap(); 2547 2548 // Generate more records to use for the next block. 2549 let splits_block = generate_splits(&vm, &private_key, &deployment_block, &mut unspent_records, rng).unwrap(); 2550 2551 // Add the splits block to the VM. 2552 vm.add_next_block(&splits_block).unwrap(); 2553 2554 // Create an execution transaction, that will be rejected. 2555 let r0 = Value::<CurrentNetwork>::from_str("100u8").unwrap(); 2556 let first = create_execution(&vm, private_key, "testing.delta", "compute", vec![r0], &mut unspent_records, rng); 2557 2558 // Construct the next block. 2559 let next_block = 2560 sample_next_block(&vm, &private_key, &[first], &splits_block, &mut unspent_records, rng).unwrap(); 2561 2562 // Check that the transaction was rejected. 2563 assert!(next_block.transactions().iter().next().unwrap().is_rejected()); 2564 2565 // Add the next block to the VM. 2566 vm.add_next_block(&next_block).unwrap(); 2567 2568 // Check that the storage was not updated. 2569 let program_id = ProgramID::from_str("testing.delta").unwrap(); 2570 let mapping_name = Identifier::from_str("entries").unwrap(); 2571 assert!( 2572 !vm.finalize_store() 2573 .contains_key_confirmed(program_id, mapping_name, &Plaintext::from(Literal::Address(address))) 2574 .unwrap() 2575 ); 2576 2577 // Create an execution transaction, that will be rejected. 2578 let r0 = Value::<CurrentNetwork>::from_str("100u8").unwrap(); 2579 let first = create_execution(&vm, private_key, "testing.delta", "compute", vec![r0], &mut unspent_records, rng); 2580 2581 // Create an execution transaction, that will be accepted. 2582 let r0 = Value::<CurrentNetwork>::from_str("1u8").unwrap(); 2583 let second = 2584 create_execution(&vm, private_key, "testing.delta", "compute", vec![r0], &mut unspent_records, rng); 2585 2586 // Construct the next block. 2587 let next_block = 2588 sample_next_block(&vm, &private_key, &[first, second], &next_block, &mut unspent_records, rng).unwrap(); 2589 2590 // Check that the first transaction was rejected. 2591 assert!(next_block.transactions().iter().next().unwrap().is_rejected()); 2592 2593 // Add the next block to the VM. 2594 vm.add_next_block(&next_block).unwrap(); 2595 2596 // Check that the storage was updated correctly. 2597 let value = vm 2598 .finalize_store() 2599 .get_value_speculative(program_id, mapping_name, &Plaintext::from(Literal::Address(address))) 2600 .unwrap() 2601 .unwrap(); 2602 let expected = Value::<CurrentNetwork>::from_str("3u8").unwrap(); 2603 assert_eq!(value, expected); 2604 } 2605 2606 #[test] 2607 fn test_excess_transactions_should_be_aborted() { 2608 let rng = &mut TestRng::default(); 2609 2610 // Sample a private key. 2611 let caller_private_key = test_helpers::sample_genesis_private_key(rng); 2612 let caller_address = Address::try_from(&caller_private_key).unwrap(); 2613 2614 // Initialize the vm. 2615 let vm = test_helpers::sample_vm_with_genesis_block(rng); 2616 2617 // Deploy a new program. 2618 let genesis = 2619 vm.block_store().get_block(&vm.block_store().get_block_hash(0).unwrap().unwrap()).unwrap().unwrap(); 2620 2621 // Get the unspent records. 2622 let mut unspent_records = genesis 2623 .transitions() 2624 .cloned() 2625 .flat_map(Transition::into_records) 2626 .map(|(_, record)| record) 2627 .collect::<Vec<_>>(); 2628 2629 // Construct the deployment block. 2630 let (program_id, deployment_block) = 2631 new_program_deployment(&vm, &caller_private_key, &genesis, &mut unspent_records, rng).unwrap(); 2632 2633 // Add the deployment block to the VM. 2634 vm.add_next_block(&deployment_block).unwrap(); 2635 2636 // Generate more records to use for the next block. 2637 let splits_block = 2638 generate_splits(&vm, &caller_private_key, &deployment_block, &mut unspent_records, rng).unwrap(); 2639 2640 // Add the splits block to the VM. 2641 vm.add_next_block(&splits_block).unwrap(); 2642 2643 // Generate more records to use for the next block. 2644 let splits_block = generate_splits(&vm, &caller_private_key, &splits_block, &mut unspent_records, rng).unwrap(); 2645 2646 // Add the splits block to the VM. 2647 vm.add_next_block(&splits_block).unwrap(); 2648 2649 // Generate the transactions. 2650 let mut transactions = Vec::new(); 2651 let mut excess_transaction_ids = Vec::new(); 2652 2653 for _ in 0..VM::<CurrentNetwork, LedgerType>::MAXIMUM_CONFIRMED_TRANSACTIONS + 1 { 2654 let transaction = 2655 sample_mint_public(&vm, caller_private_key, &program_id, caller_address, 10, &mut unspent_records, rng); 2656 // Abort the transaction if the block is full. 2657 if transactions.len() >= VM::<CurrentNetwork, LedgerType>::MAXIMUM_CONFIRMED_TRANSACTIONS { 2658 excess_transaction_ids.push(transaction.id()); 2659 } 2660 2661 transactions.push(transaction); 2662 } 2663 2664 // Construct the next block. 2665 let next_block = 2666 sample_next_block(&vm, &caller_private_key, &transactions, &splits_block, &mut unspent_records, rng) 2667 .unwrap(); 2668 2669 // Ensure that the excess transactions were aborted. 2670 assert_eq!(next_block.aborted_transaction_ids(), &excess_transaction_ids); 2671 assert_eq!(next_block.transactions().len(), VM::<CurrentNetwork, LedgerType>::MAXIMUM_CONFIRMED_TRANSACTIONS); 2672 } 2673 2674 #[test] 2675 fn test_ratify_genesis_greater_than_max_committee_size() { 2676 // Initialize an RNG. 2677 let rng = &mut TestRng::default(); 2678 2679 // Initialize the VM. 2680 let vm = sample_vm(); 2681 2682 // Construct the validators, greater than the maximum committee size. 2683 let validators = sample_validators::<CurrentNetwork>( 2684 Committee::<CurrentNetwork>::max_committee_size().unwrap() as usize + 1, 2685 rng, 2686 ); 2687 2688 // Construct the committee. 2689 let mut committee_map = IndexMap::new(); 2690 for (private_key, (amount, _, _)) in &validators { 2691 let address = Address::try_from(private_key).unwrap(); 2692 committee_map.insert(address, (*amount, true, 0)); 2693 } 2694 2695 // Attempt to construct a `Committee` with more than the maximum committee size. 2696 let result = Committee::new_genesis(committee_map); 2697 assert!(result.is_err()); 2698 2699 // Reset the validators. 2700 // Note: We use a smaller committee size to ensure that there is enough supply to allocate to the validators and genesis block transactions. 2701 let validators = sample_validators::<CurrentNetwork>( 2702 consensus_config_value!(CurrentNetwork, MAX_CERTIFICATES, 0).unwrap() as usize, 2703 rng, 2704 ); 2705 2706 // Construct the committee. 2707 // Track the allocated amount. 2708 let (committee_map, allocated_amount) = 2709 sample_committee_map_and_allocated_amount(&validators, &IndexMap::new()); 2710 2711 // Construct the public balances, allocating the remaining supply. 2712 let public_balances = sample_public_balances( 2713 &validators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(), 2714 <CurrentNetwork as Network>::STARTING_SUPPLY - allocated_amount, 2715 ); 2716 2717 // Construct the bonded balances. 2718 let bonded_balances = sample_bonded_balances(&validators, &IndexMap::new()); 2719 2720 // Construct the genesis block, which should pass. 2721 let block = vm 2722 .genesis_quorum( 2723 validators.keys().next().unwrap(), 2724 Committee::new_genesis(committee_map).unwrap(), 2725 public_balances, 2726 bonded_balances, 2727 rng, 2728 ) 2729 .unwrap(); 2730 2731 // Add the block. 2732 vm.add_next_block(&block).unwrap(); 2733 } 2734 2735 // Note that the maximum delegator size is large enough that the ratification ID cannot be computed. 2736 #[test] 2737 fn test_ratify_genesis_greater_than_max_delegator_size() { 2738 // Initialize an RNG. 2739 let rng = &mut TestRng::default(); 2740 2741 // Initialize the VM. 2742 let vm = sample_vm(); 2743 2744 // Construct the validators. 2745 // Note: We use a smaller committee size to ensure that there is enough supply to allocate to the validators and genesis block transactions. 2746 let validators = sample_validators::<CurrentNetwork>( 2747 consensus_config_value!(CurrentNetwork, MAX_CERTIFICATES, 0).unwrap() as usize / 4, 2748 rng, 2749 ); 2750 2751 // Construct the delegators, greater than the maximum delegator size. 2752 let delegators = (0..MAX_DELEGATORS + 1) 2753 .map(|_| { 2754 let private_key = PrivateKey::<CurrentNetwork>::new(rng).unwrap(); 2755 let validator = Address::try_from(validators.keys().next().unwrap()).unwrap(); 2756 let amount = MIN_DELEGATOR_STAKE; 2757 (private_key, (validator, amount)) 2758 }) 2759 .collect::<IndexMap<_, _>>(); 2760 2761 // Construct the committee. 2762 // Track the allocated amount. 2763 let (committee_map, allocated_amount) = sample_committee_map_and_allocated_amount(&validators, &delegators); 2764 2765 // Construct the public balances, allocating the remaining supply to the validators and zero to the delegators. 2766 let mut public_balances = sample_public_balances( 2767 &validators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(), 2768 <CurrentNetwork as Network>::STARTING_SUPPLY - allocated_amount, 2769 ); 2770 public_balances.extend(sample_public_balances( 2771 &delegators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(), 2772 0, 2773 )); 2774 2775 // Construct the bonded balances. 2776 let bonded_balances = sample_bonded_balances(&validators, &delegators); 2777 2778 // Construct the genesis block, which should fail. 2779 let result = vm.genesis_quorum( 2780 validators.keys().next().unwrap(), 2781 Committee::new_genesis(committee_map).unwrap(), 2782 public_balances, 2783 bonded_balances, 2784 rng, 2785 ); 2786 assert!(result.is_err()); 2787 } 2788 2789 #[test] 2790 fn test_ratify_genesis_is_correct() { 2791 const NUM_VALIDATORS: usize = 5; 2792 const NUM_DELEGATORS: usize = 8; 2793 2794 // Sample an RNG. 2795 let rng = &mut TestRng::default(); 2796 2797 println!("Initializing VMs."); 2798 2799 // Initialize the VM. 2800 let vm = sample_vm(); 2801 2802 println!("Constructing validator and delegator sets."); 2803 2804 // Sample the validators. 2805 let validators = sample_validators(NUM_VALIDATORS, rng); 2806 2807 // Sample the delegators, cycling through the validators. 2808 let delegators: IndexMap<_, _> = (0..NUM_DELEGATORS) 2809 .map(|i| { 2810 let private_key = PrivateKey::new(rng).unwrap(); 2811 let validator = Address::try_from(validators.keys().nth(i % NUM_VALIDATORS).unwrap()).unwrap(); 2812 let amount = MIN_DELEGATOR_STAKE; 2813 (private_key, (validator, amount)) 2814 }) 2815 .collect(); 2816 2817 // Sample a genesis block without any delegators. 2818 // Specifically, the genesis block will contain a `Ratification` with: 2819 // - the committee state, containing only the validator amounts. 2820 // - the public balances for the delegators, with 10_000_000u64 microcredits each (plus 843_880u64 microcredits for fees). 2821 // - the public balances for the validators dividing up the remaining starting supply. 2822 // - the bonded balances, only containing the validators. 2823 2824 println!("Initializing the VM."); 2825 2826 // Construct the committee. 2827 // Track the allocated amount. 2828 let (committee_map, allocated_amount) = sample_committee_map_and_allocated_amount(&validators, &delegators); 2829 let committee = Committee::new_genesis(committee_map).unwrap(); 2830 2831 // Construct the public balances, allocating the remaining supply to the validators and zero to the delegators. 2832 let mut public_balances = sample_public_balances( 2833 &validators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(), 2834 <CurrentNetwork as Network>::STARTING_SUPPLY - allocated_amount, 2835 ); 2836 public_balances.extend(sample_public_balances( 2837 &delegators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(), 2838 0, 2839 )); 2840 2841 // Construct the bonded balances. 2842 let bonded_balances = sample_bonded_balances(&validators, &delegators); 2843 2844 println!("Generating the genesis block."); 2845 2846 let genesis = vm 2847 .genesis_quorum( 2848 validators.keys().next().unwrap(), 2849 committee.clone(), 2850 public_balances.clone(), 2851 bonded_balances.clone(), 2852 rng, 2853 ) 2854 .unwrap(); 2855 2856 println!("Adding the genesis block to the VM."); 2857 2858 // Add the genesis block to the VM. 2859 vm.add_next_block(&genesis).unwrap(); 2860 2861 // Check that the state of the `credits.delta` program is correct. 2862 let program_id = ProgramID::from_str("credits.delta").unwrap(); 2863 let committee_mapping_name = Identifier::from_str("committee").unwrap(); 2864 let account_mapping_name = Identifier::from_str("account").unwrap(); 2865 let bonded_mapping_name = Identifier::from_str("bonded").unwrap(); 2866 let metadata_mapping_name = Identifier::from_str("metadata").unwrap(); 2867 let unbonding_mapping_name = Identifier::from_str("unbonding").unwrap(); 2868 let withdraw_mapping_name = Identifier::from_str("withdraw").unwrap(); 2869 2870 // Get and check the committee mapping. 2871 let actual_committee = vm.finalize_store().get_mapping_confirmed(program_id, committee_mapping_name).unwrap(); 2872 let expected_committee = committee 2873 .members() 2874 .iter() 2875 .map(|(address, (_, is_open, commission))| { 2876 ( 2877 Plaintext::from_str(&address.to_string()).unwrap(), 2878 Value::from_str(&format!("{{ is_open: {is_open}, commission: {commission}u8 }}")).unwrap(), 2879 ) 2880 }) 2881 .collect_vec(); 2882 // Note that `actual_committee` and `expected_committee` are vectors and not necessarily in the same order. 2883 // By checking that the lengths of the vector are equal and that all entries in `actual_committee` are in `expected_committee`, 2884 // we can ensure that the two vectors contain the same data. 2885 assert_eq!(actual_committee.len(), expected_committee.len()); 2886 for entry in actual_committee.iter() { 2887 assert!(expected_committee.contains(entry)); 2888 } 2889 2890 // Get and check the account mapping. 2891 let actual_account = vm.finalize_store().get_mapping_confirmed(program_id, account_mapping_name).unwrap(); 2892 let expected_account = public_balances 2893 .iter() 2894 .map(|(address, amount)| { 2895 (Plaintext::from_str(&address.to_string()).unwrap(), Value::from_str(&format!("{amount}u64")).unwrap()) 2896 }) 2897 .collect_vec(); 2898 // Note that `actual_account` and `expected_account` are vectors and not necessarily in the same order. 2899 // By checking that the lengths of the vector are equal and that all entries in `actual_account` are in `expected_account`, 2900 // we can ensure that the two vectors contain the same data. 2901 assert_eq!(actual_account.len(), expected_account.len()); 2902 // Check that all entries except for the first validator are the same. 2903 for entry in actual_account.iter() { 2904 let first_validator = Address::try_from(validators.keys().next().unwrap()).unwrap(); 2905 // Note that the first validator is used to execute additional transactions in `VM::genesis_quorum`. 2906 // Therefore, the balance of the first validator will be different from the expected balance. 2907 if entry.0 == Plaintext::from_str(&first_validator.to_string()).unwrap() { 2908 assert_eq!(entry.1, Value::from_str("144991999894112u64").unwrap()); 2909 } else { 2910 assert!(expected_account.contains(entry)); 2911 } 2912 } 2913 2914 // Get and check the bonded mapping. 2915 let actual_bonded = vm.finalize_store().get_mapping_confirmed(program_id, bonded_mapping_name).unwrap(); 2916 let expected_bonded = bonded_balances 2917 .iter() 2918 .map(|(address, (validator, _, amount))| { 2919 ( 2920 Plaintext::from_str(&address.to_string()).unwrap(), 2921 Value::from_str(&format!("{{ validator: {validator}, microcredits: {amount}u64 }}")).unwrap(), 2922 ) 2923 }) 2924 .collect_vec(); 2925 // Note that `actual_bonded` and `expected_bonded` are vectors and not necessarily in the same order. 2926 // By checking that the lengths of the vector are equal and that all entries in `actual_bonded` are in `expected_bonded`, 2927 // we can ensure that the two vectors contain the same data. 2928 assert_eq!(actual_bonded.len(), expected_bonded.len()); 2929 for entry in actual_bonded.iter() { 2930 assert!(expected_bonded.contains(entry)); 2931 } 2932 2933 // Get and check the withdraw mapping. 2934 let actual_withdraw = vm.finalize_store().get_mapping_confirmed(program_id, withdraw_mapping_name).unwrap(); 2935 let expected_withdraw = bonded_balances 2936 .iter() 2937 .map(|(address, (_, withdrawal_address, _))| { 2938 ( 2939 Plaintext::from_str(&address.to_string()).unwrap(), 2940 Value::from_str(&withdrawal_address.to_string()).unwrap(), 2941 ) 2942 }) 2943 .collect_vec(); 2944 // Note that `actual_withdraw` and `expected_withdraw` are vectors and not necessarily in the same order. 2945 // By checking that the lengths of the vector are equal and that all entries in `actual_withdraw` are in `expected_withdraw`, 2946 // we can ensure that the two vectors contain the same data. 2947 assert_eq!(actual_withdraw.len(), expected_withdraw.len()); 2948 for entry in actual_withdraw.iter() { 2949 assert!(expected_withdraw.contains(entry)); 2950 } 2951 2952 // Get and check the entry in metadata mapping corresponding to the number of validators. 2953 let num_validators = vm 2954 .finalize_store() 2955 .get_value_confirmed( 2956 program_id, 2957 metadata_mapping_name, 2958 &Plaintext::from_str("dx1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3qtczd").unwrap(), 2959 ) 2960 .unwrap() 2961 .unwrap(); 2962 assert_eq!(num_validators, Value::from_str(&format!("{NUM_VALIDATORS}u32")).unwrap()); 2963 2964 // Get and check the entry in metadata mapping corresponding to the number of delegators. 2965 let num_delegators = vm 2966 .finalize_store() 2967 .get_value_confirmed( 2968 program_id, 2969 metadata_mapping_name, 2970 &Plaintext::from_str("dx1qgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqavzal6").unwrap(), 2971 ) 2972 .unwrap() 2973 .unwrap(); 2974 assert_eq!(num_delegators, Value::from_str(&format!("{NUM_DELEGATORS}u32")).unwrap()); 2975 2976 // Get and check the unbonding mapping. 2977 let actual_unbonding = vm.finalize_store().get_mapping_confirmed(program_id, unbonding_mapping_name).unwrap(); 2978 assert!(actual_unbonding.is_empty()); 2979 } 2980 2981 #[test] 2982 fn test_ratify_genesis_is_consistent() { 2983 const NUM_VALIDATORS: usize = 5; 2984 const NUM_DELEGATORS: usize = 8; 2985 2986 // Sample an RNG. 2987 let rng = &mut TestRng::default(); 2988 2989 println!("Initializing VMs."); 2990 2991 // Initialize two VMs. 2992 let vm_1 = sample_vm(); 2993 let vm_2 = sample_vm(); 2994 2995 println!("Constructing validator and delegator sets."); 2996 2997 // Sample the validators. 2998 let validators = sample_validators(NUM_VALIDATORS, rng); 2999 3000 // Sample the delegators, cycling through the validators. 3001 let delegators: IndexMap<_, _> = (0..NUM_DELEGATORS) 3002 .map(|i| { 3003 let private_key = PrivateKey::new(rng).unwrap(); 3004 let validator = Address::try_from(validators.keys().nth(i % NUM_VALIDATORS).unwrap()).unwrap(); 3005 let amount = MIN_DELEGATOR_STAKE; 3006 (private_key, (validator, amount)) 3007 }) 3008 .collect(); 3009 3010 // For the first VM, sample a genesis block without any delegators. 3011 // Specifically, the genesis block will contain a `Ratification` with: 3012 // - the committee state, containing only the validator amounts. 3013 // - the public balances for the delegators, with 10_000_000u64 microcredits each (plus 843_880u64 microcredits for fees). 3014 // - the public balances for the validators dividing up the remaining starting supply. 3015 // - the bonded balances, only containing the validators. 3016 3017 println!("Initializing the first VM."); 3018 3019 // Construct the committee. 3020 // Track the allocated amount. 3021 let (committee_map, mut allocated_amount) = 3022 sample_committee_map_and_allocated_amount(&validators, &IndexMap::new()); 3023 let committee = Committee::new_genesis(committee_map).unwrap(); 3024 3025 // Construct the public balances. 3026 let mut public_balances = IndexMap::new(); 3027 for (private_key, (_validator, _amount)) in &delegators { 3028 let address = Address::try_from(private_key).unwrap(); 3029 let amount = MIN_DELEGATOR_STAKE * 2; 3030 public_balances.insert(address, amount); 3031 allocated_amount += amount; 3032 } 3033 public_balances.extend(sample_public_balances( 3034 &validators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(), 3035 <CurrentNetwork as Network>::STARTING_SUPPLY - allocated_amount, 3036 )); 3037 3038 // Construct the bonded balances. 3039 let bonded_balances = sample_bonded_balances(&validators, &IndexMap::new()); 3040 3041 println!("[VM1] Generating the genesis block."); 3042 3043 let genesis_1 = vm_1 3044 .genesis_quorum(validators.keys().next().unwrap(), committee, public_balances, bonded_balances, rng) 3045 .unwrap(); 3046 3047 println!("[VM1] Adding the genesis block to the VM."); 3048 3049 // Add the genesis block to the VM. 3050 vm_1.add_next_block(&genesis_1).unwrap(); 3051 3052 println!("[VM1] Generating bond transactions for each of the delegators."); 3053 3054 // Generate bond transactions for each of the delegators. 3055 let mut transactions = Vec::new(); 3056 for (private_key, (validator, amount)) in &delegators { 3057 let transaction = vm_1 3058 .execute( 3059 private_key, 3060 ("credits.delta", "bond_public"), 3061 vec![ 3062 Value::<CurrentNetwork>::from_str(&validator.to_string()).unwrap(), 3063 Value::<CurrentNetwork>::from_str(&Address::try_from(private_key).unwrap().to_string()) 3064 .unwrap(), 3065 Value::<CurrentNetwork>::from_str(&format!("{amount}u64")).unwrap(), 3066 ] 3067 .into_iter(), 3068 None, 3069 0, 3070 None, 3071 rng, 3072 ) 3073 .unwrap(); 3074 transactions.push(transaction); 3075 } 3076 3077 println!("[VM1] Generating the next block."); 3078 let next_block = 3079 sample_next_block(&vm_1, validators.keys().next().unwrap(), &transactions, &genesis_1, &mut vec![], rng) 3080 .unwrap(); 3081 3082 println!("[VM1] Adding the next block to the VM."); 3083 vm_1.add_next_block(&next_block).unwrap(); 3084 3085 // For the second VM, sample a genesis block with the same validators and delegators. 3086 // Specifically, the genesis block will contain a `Ratification` with: 3087 // - the committee state, containing the total staked amount per validator. 3088 // - the public balances for the delegators, with 0 microcredits each. 3089 // - the public balances for the validators dividing up the remaining starting supply. 3090 // - the bonded balances, containing the validators and delegators. 3091 3092 println!("Initializing the second VM."); 3093 3094 // Construct the committee. 3095 // Track the allocated amount. 3096 let (committee_map, allocated_amount) = sample_committee_map_and_allocated_amount(&validators, &delegators); 3097 let committee = Committee::new_genesis(committee_map).unwrap(); 3098 3099 // Construct the public balances, allocating the remaining supply to the validators and zero to the delegators. 3100 let mut public_balances = sample_public_balances( 3101 &validators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(), 3102 <CurrentNetwork as Network>::STARTING_SUPPLY - allocated_amount, 3103 ); 3104 public_balances.extend(sample_public_balances( 3105 &delegators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(), 3106 0, 3107 )); 3108 3109 // Construct the bonded balances. 3110 let bonded_balances = sample_bonded_balances(&validators, &delegators); 3111 3112 println!("[VM2] Generating the genesis block."); 3113 3114 // Construct the genesis block. 3115 let genesis_2 = vm_2 3116 .genesis_quorum(validators.keys().next().unwrap(), committee, public_balances, bonded_balances, rng) 3117 .unwrap(); 3118 3119 println!("[VM2] Adding the genesis block to the VM."); 3120 3121 // Add the genesis block to the VM. 3122 vm_2.add_next_block(&genesis_2).unwrap(); 3123 3124 println!("Checking that all mappings in `credits.delta` are equal across the two VMs."); 3125 3126 // Check that all mappings in `credits.delta` are equal across the two VMs. 3127 let program_id = ProgramID::from_str("credits.delta").unwrap(); 3128 let committee_mapping_name = Identifier::from_str("committee").unwrap(); 3129 let bonded_mapping_name = Identifier::from_str("bonded").unwrap(); 3130 let unbonding_mapping_name = Identifier::from_str("unbonding").unwrap(); 3131 let account_mapping_name = Identifier::from_str("account").unwrap(); 3132 let metadata_mapping_name = Identifier::from_str("metadata").unwrap(); 3133 let withdraw_mapping_name = Identifier::from_str("withdraw").unwrap(); 3134 3135 let committee_1 = vm_1.finalize_store().get_mapping_confirmed(program_id, committee_mapping_name).unwrap(); 3136 let committee_2 = vm_2.finalize_store().get_mapping_confirmed(program_id, committee_mapping_name).unwrap(); 3137 assert_eq!(committee_1, committee_2); 3138 3139 let bonded_1 = vm_1.finalize_store().get_mapping_confirmed(program_id, bonded_mapping_name).unwrap(); 3140 let bonded_2 = vm_2.finalize_store().get_mapping_confirmed(program_id, bonded_mapping_name).unwrap(); 3141 assert_eq!(bonded_1, bonded_2); 3142 3143 let unbonding_1 = vm_1.finalize_store().get_mapping_confirmed(program_id, unbonding_mapping_name).unwrap(); 3144 let unbonding_2 = vm_2.finalize_store().get_mapping_confirmed(program_id, unbonding_mapping_name).unwrap(); 3145 assert_eq!(unbonding_1, unbonding_2); 3146 3147 // Check that the account mapping across both VMs have the same keys. 3148 let account_1 = vm_1 3149 .finalize_store() 3150 .get_mapping_confirmed(program_id, account_mapping_name) 3151 .unwrap() 3152 .into_iter() 3153 .map(|(k, _)| k.to_string()) 3154 .collect::<std::collections::HashSet<_>>(); 3155 let account_2 = vm_2 3156 .finalize_store() 3157 .get_mapping_confirmed(program_id, account_mapping_name) 3158 .unwrap() 3159 .into_iter() 3160 .map(|(k, _)| k.to_string()) 3161 .collect::<std::collections::HashSet<_>>(); 3162 assert_eq!(account_1, account_2); 3163 3164 // Check that the metadata mapping across both VMs are equal. 3165 let metadata_1 = vm_1.finalize_store().get_mapping_confirmed(program_id, metadata_mapping_name).unwrap(); 3166 let metadata_2 = vm_2.finalize_store().get_mapping_confirmed(program_id, metadata_mapping_name).unwrap(); 3167 assert_eq!(metadata_1, metadata_2); 3168 3169 // Check that the withdraw mapping across both VMs are equal. 3170 let withdraw_1 = vm_1.finalize_store().get_mapping_confirmed(program_id, withdraw_mapping_name).unwrap(); 3171 let withdraw_2 = vm_2.finalize_store().get_mapping_confirmed(program_id, withdraw_mapping_name).unwrap(); 3172 assert_eq!(withdraw_1, withdraw_2); 3173 } 3174 3175 #[test] 3176 fn test_ratify_genesis_with_insufficient_validator_balance() { 3177 // Sample an RNG. 3178 let rng = &mut TestRng::default(); 3179 3180 // Initialize the VM. 3181 let vm = sample_vm(); 3182 3183 // Attempt to construct a genesis quorum, with a validator with an insufficient amount. 3184 let mut validators = (0..3) 3185 .map(|_| { 3186 let private_key = PrivateKey::<CurrentNetwork>::new(rng).unwrap(); 3187 let address = Address::try_from(&private_key).unwrap(); 3188 let amount = MIN_VALIDATOR_STAKE; 3189 let is_open = true; 3190 let commission = 0u8; 3191 (address, (amount, is_open, commission)) 3192 }) 3193 .collect::<IndexMap<_, _>>(); 3194 validators 3195 .insert(Address::try_from(PrivateKey::new(rng).unwrap()).unwrap(), (MIN_VALIDATOR_STAKE - 1, true, 0)); 3196 3197 // Construct the committee. 3198 let result = Committee::new_genesis(validators); 3199 assert!(result.is_err()); 3200 3201 // Track the allocated amount. 3202 let mut allocated_amount = 0; 3203 3204 // Reset the validators. 3205 let validators = sample_validators(4, rng); 3206 3207 // Construct the committee. 3208 let committee = Committee::new_genesis( 3209 validators 3210 .iter() 3211 .map(|(private_key, (amount, _, _))| { 3212 let address = Address::try_from(private_key).unwrap(); 3213 allocated_amount += *amount; 3214 (address, (*amount, true, 0u8)) 3215 }) 3216 .collect(), 3217 ) 3218 .unwrap(); 3219 3220 // Construct the public balances, allocating the remaining supply to rest of the validators. 3221 let public_balances = sample_public_balances( 3222 &validators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(), 3223 <CurrentNetwork as Network>::STARTING_SUPPLY - allocated_amount, 3224 ); 3225 3226 // Construct the bonded balances. 3227 let bonded_balances = sample_bonded_balances(&validators, &IndexMap::new()); 3228 3229 // Construct the genesis block, which should pass. 3230 let block = vm 3231 .genesis_quorum(validators.keys().next().unwrap(), committee, public_balances, bonded_balances, rng) 3232 .unwrap(); 3233 3234 // Add the block. 3235 vm.add_next_block(&block).unwrap(); 3236 } 3237 3238 #[test] 3239 fn test_ratify_genesis_with_insufficient_delegator_balance() { 3240 // Sample an RNG. 3241 let rng = &mut TestRng::default(); 3242 3243 // Initialize the VM. 3244 let vm = sample_vm(); 3245 3246 // Track the allocated amount. 3247 let mut allocated_amount = 0; 3248 3249 // Sample the validators. 3250 let validators = sample_validators(4, rng); 3251 3252 // Attempt to construct a genesis quorum, with a delegator with an insufficient amount. 3253 let mut delegators = IndexMap::new(); 3254 delegators.insert( 3255 PrivateKey::new(rng).unwrap(), 3256 (Address::try_from(validators.keys().next().unwrap()).unwrap(), MIN_DELEGATOR_STAKE - 1), 3257 ); 3258 3259 // Construct the committee. 3260 let mut committee_map = IndexMap::new(); 3261 for (private_key, (amount, _, _)) in &validators { 3262 let address = Address::try_from(private_key).unwrap(); 3263 let amount = if address == Address::try_from(validators.keys().next().unwrap()).unwrap() { 3264 *amount + MIN_DELEGATOR_STAKE - 1 3265 } else { 3266 *amount 3267 }; 3268 committee_map.insert(address, (amount, true, 0u8)); 3269 allocated_amount += amount; 3270 } 3271 let committee = Committee::new_genesis(committee_map).unwrap(); 3272 3273 // Construct the public balances, allocating the remaining supply to rest of the validators. 3274 let mut public_balances = sample_public_balances( 3275 &validators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(), 3276 <CurrentNetwork as Network>::STARTING_SUPPLY - allocated_amount, 3277 ); 3278 public_balances 3279 .extend(sample_public_balances(&[Address::try_from(delegators.keys().next().unwrap()).unwrap()], 0)); 3280 3281 // Construct the bonded balances. 3282 let bonded_balances = sample_bonded_balances(&validators, &delegators); 3283 3284 // Construct the genesis block, which should fail. 3285 let result = 3286 vm.genesis_quorum(validators.keys().next().unwrap(), committee, public_balances, bonded_balances, rng); 3287 assert!(result.is_err()); 3288 3289 // Reset the delegators. 3290 let mut delegators = IndexMap::new(); 3291 delegators.insert( 3292 PrivateKey::new(rng).unwrap(), 3293 (Address::try_from(validators.keys().next().unwrap()).unwrap(), MIN_DELEGATOR_STAKE), 3294 ); 3295 3296 // Track the allocated amount. 3297 let mut allocated_amount = 0; 3298 3299 // Construct the committee. 3300 let mut committee_map = IndexMap::new(); 3301 for (private_key, (amount, _, _)) in &validators { 3302 let address = Address::try_from(private_key).unwrap(); 3303 let amount = if address == Address::try_from(validators.keys().next().unwrap()).unwrap() { 3304 *amount + MIN_DELEGATOR_STAKE 3305 } else { 3306 *amount 3307 }; 3308 committee_map.insert(address, (amount, true, 0u8)); 3309 allocated_amount += amount; 3310 } 3311 let committee = Committee::new_genesis(committee_map).unwrap(); 3312 3313 // Construct the public balances, allocating the remaining supply to rest of the validators. 3314 let mut public_balances = sample_public_balances( 3315 &validators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(), 3316 <CurrentNetwork as Network>::STARTING_SUPPLY - allocated_amount, 3317 ); 3318 public_balances 3319 .extend(sample_public_balances(&[Address::try_from(delegators.keys().next().unwrap()).unwrap()], 0)); 3320 3321 // Construct the bonded balances. 3322 let bonded_balances = sample_bonded_balances(&validators, &delegators); 3323 3324 // Construct the genesis block, which should pass. 3325 let block = vm 3326 .genesis_quorum(validators.keys().next().unwrap(), committee, public_balances, bonded_balances, rng) 3327 .unwrap(); 3328 3329 // Add the block. 3330 vm.add_next_block(&block).unwrap(); 3331 } 3332 3333 #[test] 3334 fn test_ratify_genesis_with_incorrect_committee_amounts() { 3335 // Sample an RNG. 3336 let rng = &mut TestRng::default(); 3337 3338 // Initialize the VM. 3339 let vm = sample_vm(); 3340 3341 // Initialize the validators. 3342 let validators = sample_validators(4, rng); 3343 3344 // Initialize the delegators. 3345 let delegators = (0..4) 3346 .map(|_| { 3347 let private_key = PrivateKey::new(rng).unwrap(); 3348 let validator = Address::try_from(validators.keys().next().unwrap()).unwrap(); 3349 let amount = MIN_DELEGATOR_STAKE; 3350 (private_key, (validator, amount)) 3351 }) 3352 .collect::<IndexMap<_, _>>(); 3353 3354 // Construct the **incorrect** committee. 3355 // Track the allocated amount. 3356 // Note: this committee is missing the additional stake from the delegators. 3357 let (committee_map, allocated_amount) = 3358 sample_committee_map_and_allocated_amount(&validators, &IndexMap::new()); 3359 let committee = Committee::new_genesis(committee_map).unwrap(); 3360 3361 // Construct the public balances, allocating the remaining supply to rest of the validators. 3362 let mut public_balances = sample_public_balances( 3363 &validators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(), 3364 <CurrentNetwork as Network>::STARTING_SUPPLY - allocated_amount, 3365 ); 3366 public_balances.extend(sample_public_balances( 3367 &delegators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(), 3368 0, 3369 )); 3370 3371 // Construct the bonded balances. 3372 let bonded_balances = sample_bonded_balances(&validators, &delegators); 3373 3374 // Construct the genesis block, which should fail. 3375 let result = vm.genesis_quorum( 3376 validators.keys().next().unwrap(), 3377 committee, 3378 public_balances, 3379 bonded_balances.clone(), 3380 rng, 3381 ); 3382 assert!(result.is_err()); 3383 3384 // Construct the **correct** committee. 3385 // Reset the tracked amount. 3386 let (committee_map, allocated_amount) = sample_committee_map_and_allocated_amount(&validators, &delegators); 3387 let committee = Committee::new_genesis(committee_map).unwrap(); 3388 3389 // Construct the public balances, allocating the remaining supply to rest of the validators. 3390 let mut public_balances = sample_public_balances( 3391 &validators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(), 3392 <CurrentNetwork as Network>::STARTING_SUPPLY - allocated_amount, 3393 ); 3394 public_balances.extend(sample_public_balances( 3395 &delegators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(), 3396 0, 3397 )); 3398 3399 // Construct the genesis block, which should pass. 3400 let block = vm 3401 .genesis_quorum(validators.keys().next().unwrap(), committee, public_balances, bonded_balances, rng) 3402 .unwrap(); 3403 3404 // Add the block. 3405 vm.add_next_block(&block).unwrap(); 3406 } 3407 3408 #[test] 3409 fn test_ratify_genesis_with_closed_validator() { 3410 // Sample an RNG. 3411 let rng = &mut TestRng::default(); 3412 3413 // Initialize the VM. 3414 let vm = sample_vm(); 3415 3416 // Initialize the validators, with one closed. 3417 let validators = (0..4) 3418 .map(|i| { 3419 let private_key = PrivateKey::new(rng).unwrap(); 3420 let amount = MIN_VALIDATOR_STAKE; 3421 let is_open = i != 0; 3422 let commission = 0; 3423 (private_key, (amount, is_open, commission)) 3424 }) 3425 .collect::<IndexMap<_, _>>(); 3426 3427 // Initialize a potential delegator. 3428 let delegator_key = PrivateKey::new(rng).unwrap(); 3429 let delegator_address = Address::try_from(delegator_key).unwrap(); 3430 3431 // Construct the committee. 3432 // Track the allocated amount. 3433 let (committee_map, allocated_amount) = 3434 sample_committee_map_and_allocated_amount(&validators, &IndexMap::new()); 3435 3436 // Construct the public balances, allocating half to the first validator and the remaining to the delegator. 3437 let public_balances = sample_public_balances( 3438 &[Address::try_from(validators.keys().next().unwrap()).unwrap(), delegator_address], 3439 <CurrentNetwork as Network>::STARTING_SUPPLY - allocated_amount, 3440 ); 3441 3442 // Construct the bonded balances. 3443 let bonded_balances = sample_bonded_balances(&validators, &IndexMap::new()); 3444 3445 // Construct the genesis block, which should pass. 3446 let block = vm 3447 .genesis_quorum( 3448 validators.keys().next().unwrap(), 3449 Committee::new_genesis(committee_map).unwrap(), 3450 public_balances, 3451 bonded_balances, 3452 rng, 3453 ) 3454 .unwrap(); 3455 3456 // Add the block. 3457 vm.add_next_block(&block).unwrap(); 3458 3459 // Attempt to bond the potential delegator to the closed validator. 3460 let transaction = vm 3461 .execute( 3462 &delegator_key, 3463 ("credits.delta", "bond_public"), 3464 vec![ 3465 Value::<CurrentNetwork>::from_str( 3466 &Address::try_from(validators.keys().next().unwrap()).unwrap().to_string(), 3467 ) 3468 .unwrap(), 3469 Value::<CurrentNetwork>::from_str(&Address::try_from(delegator_key).unwrap().to_string()).unwrap(), 3470 Value::<CurrentNetwork>::from_str(&format!("{MIN_DELEGATOR_STAKE}u64")).unwrap(), 3471 ] 3472 .into_iter(), 3473 None, 3474 0, 3475 None, 3476 rng, 3477 ) 3478 .unwrap(); 3479 3480 // Generate the next block. 3481 let next_block = 3482 sample_next_block(&vm, validators.keys().next().unwrap(), &vec![transaction], &block, &mut vec![], rng) 3483 .unwrap(); 3484 3485 // Add the next block. 3486 vm.add_next_block(&next_block).unwrap(); 3487 3488 // Check that the delegator is not in the `bonded` mapping. 3489 let bonded_mapping = vm 3490 .finalize_store() 3491 .get_mapping_confirmed( 3492 ProgramID::from_str("credits.delta").unwrap(), 3493 Identifier::from_str("bonded").unwrap(), 3494 ) 3495 .unwrap(); 3496 assert_eq!(bonded_mapping.len(), validators.len()); 3497 3498 // Attempt to bond the potential delegator to the open validator. 3499 let transaction = vm 3500 .execute( 3501 &delegator_key, 3502 ("credits.delta", "bond_public"), 3503 vec![ 3504 Value::<CurrentNetwork>::from_str( 3505 &Address::try_from(validators.keys().nth(1).unwrap()).unwrap().to_string(), 3506 ) 3507 .unwrap(), 3508 Value::<CurrentNetwork>::from_str(&Address::try_from(delegator_key).unwrap().to_string()).unwrap(), 3509 Value::<CurrentNetwork>::from_str(&format!("{MIN_DELEGATOR_STAKE}u64")).unwrap(), 3510 ] 3511 .into_iter(), 3512 None, 3513 0, 3514 None, 3515 rng, 3516 ) 3517 .unwrap(); 3518 3519 // Generate the next block. 3520 let next_block = sample_next_block( 3521 &vm, 3522 validators.keys().next().unwrap(), 3523 &vec![transaction], 3524 &next_block, 3525 &mut vec![], 3526 rng, 3527 ) 3528 .unwrap(); 3529 3530 // Add the next block. 3531 vm.add_next_block(&next_block).unwrap(); 3532 3533 // Check that the delegator is in the `bonded` mapping. 3534 let bonded_mapping = vm 3535 .finalize_store() 3536 .get_mapping_confirmed( 3537 ProgramID::from_str("credits.delta").unwrap(), 3538 Identifier::from_str("bonded").unwrap(), 3539 ) 3540 .unwrap(); 3541 assert_eq!(bonded_mapping.len(), validators.len() + 1); 3542 } 3543 3544 #[test] 3545 fn test_ratify_genesis_withdrawal_address() { 3546 const NUM_VALIDATORS: usize = 5; 3547 const NUM_DELEGATORS: usize = 8; 3548 3549 // Sample an RNG. 3550 let rng = &mut TestRng::default(); 3551 3552 // Initialize the VM. 3553 let vm = sample_vm(); 3554 3555 // Sample the validators. 3556 let validators = sample_validators(NUM_VALIDATORS, rng); 3557 3558 // Sample the delegators, cycling through the validators. 3559 let delegators: IndexMap<_, _> = (0..NUM_DELEGATORS) 3560 .map(|i| { 3561 let private_key = PrivateKey::new(rng).unwrap(); 3562 let validator = Address::try_from(validators.keys().nth(i % NUM_VALIDATORS).unwrap()).unwrap(); 3563 let amount = MIN_DELEGATOR_STAKE; 3564 (private_key, (validator, amount)) 3565 }) 3566 .collect(); 3567 3568 // Construct the committee. 3569 // Track the allocated amount. 3570 let (committee_map, allocated_amount) = sample_committee_map_and_allocated_amount(&validators, &delegators); 3571 let committee = Committee::new_genesis(committee_map).unwrap(); 3572 3573 // Construct the public balances, allocating the remaining supply to the validators and zero to the delegators. 3574 let mut public_balances = sample_public_balances( 3575 &validators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(), 3576 <CurrentNetwork as Network>::STARTING_SUPPLY - allocated_amount, 3577 ); 3578 public_balances.extend(sample_public_balances( 3579 &delegators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(), 3580 0, 3581 )); 3582 3583 // Construct the bonded balances. 3584 let mut bonded_balances = sample_bonded_balances(&validators, &delegators); 3585 3586 // Randomly sample and update the withdrawal addresses in the bonded balances. 3587 for (_, (_, withdrawal_address, _)) in bonded_balances.iter_mut() { 3588 *withdrawal_address = Address::rand(rng); 3589 } 3590 3591 // Construct the genesis block. 3592 let genesis = vm 3593 .genesis_quorum( 3594 validators.keys().next().unwrap(), 3595 committee.clone(), 3596 public_balances.clone(), 3597 bonded_balances.clone(), 3598 rng, 3599 ) 3600 .unwrap(); 3601 3602 // Add the genesis block to the VM. 3603 vm.add_next_block(&genesis).unwrap(); 3604 3605 // Check that the state of the `credits.delta` program is correct. 3606 let program_id = ProgramID::from_str("credits.delta").unwrap(); 3607 let withdraw_mapping_name = Identifier::from_str("withdraw").unwrap(); 3608 3609 // Get and check the withdraw mapping. 3610 let actual_withdraw = vm.finalize_store().get_mapping_confirmed(program_id, withdraw_mapping_name).unwrap(); 3611 let expected_withdraw = bonded_balances 3612 .iter() 3613 .map(|(address, (_, withdrawal_address, _))| { 3614 ( 3615 Plaintext::from_str(&address.to_string()).unwrap(), 3616 Value::from_str(&withdrawal_address.to_string()).unwrap(), 3617 ) 3618 }) 3619 .collect_vec(); 3620 // Note that `actual_withdraw` and `expected_withdraw` are vectors and not necessarily in the same order. 3621 // By checking that the lengths of the vector are equal and that all entries in `actual_withdraw` are in `expected_withdraw`, 3622 // we can ensure that the two vectors contain the same data. 3623 assert_eq!(actual_withdraw.len(), expected_withdraw.len()); 3624 for entry in actual_withdraw.iter() { 3625 assert!(expected_withdraw.contains(entry)); 3626 } 3627 } 3628 }