output.rs
1 // Copyright (c) 2019-2025 Alpha-Delta Network Inc. 2 // This file is part of the alphavm 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 crate::{ 17 atomic_batch_scope, 18 helpers::{Map, MapRead}, 19 }; 20 use alphavm_ledger_block::Output; 21 use console::{ 22 network::prelude::*, 23 program::{Ciphertext, Future, Plaintext, Record}, 24 types::{Field, Group}, 25 }; 26 27 use alphastd_storage::StorageMode; 28 use anyhow::Result; 29 use std::borrow::Cow; 30 31 /// A trait for transition output storage. 32 pub trait OutputStorage<N: Network>: Clone + Send + Sync { 33 /// The mapping of `transition ID` to `output IDs`. 34 type IDMap: for<'a> Map<'a, N::TransitionID, Vec<Field<N>>>; 35 /// The mapping of `output ID` to `transition ID`. 36 type ReverseIDMap: for<'a> Map<'a, Field<N>, N::TransitionID>; 37 /// The mapping of `plaintext hash` to `(optional) plaintext`. 38 type ConstantMap: for<'a> Map<'a, Field<N>, Option<Plaintext<N>>>; 39 /// The mapping of `plaintext hash` to `(optional) plaintext`. 40 type PublicMap: for<'a> Map<'a, Field<N>, Option<Plaintext<N>>>; 41 /// The mapping of `ciphertext hash` to `(optional) ciphertext`. 42 type PrivateMap: for<'a> Map<'a, Field<N>, Option<Ciphertext<N>>>; 43 /// The mapping of `commitment` to `(checksum, (optional) record ciphertext)`. 44 type RecordMap: for<'a> Map<'a, Field<N>, (Field<N>, Option<Record<N, Ciphertext<N>>>)>; 45 /// The mapping of `record nonce` to `commitment`. 46 type RecordNonceMap: for<'a> Map<'a, Group<N>, Field<N>>; 47 /// The mapping of `record nonce` to `sender ciphertext`. 48 type RecordSenderMap: for<'a> Map<'a, Group<N>, Option<Field<N>>>; 49 /// The mapping of `external hash` to `()`. Note: This is **not** the record commitment. 50 type ExternalRecordMap: for<'a> Map<'a, Field<N>, ()>; 51 /// The mapping of `future hash` to `(optional) future`. 52 type FutureMap: for<'a> Map<'a, Field<N>, Option<Future<N>>>; 53 54 /// Initializes the transition output storage. 55 fn open<S: Into<StorageMode>>(storage: S) -> Result<Self>; 56 57 /// Returns the ID map. 58 fn id_map(&self) -> &Self::IDMap; 59 /// Returns the reverse ID map. 60 fn reverse_id_map(&self) -> &Self::ReverseIDMap; 61 /// Returns the constant map. 62 fn constant_map(&self) -> &Self::ConstantMap; 63 /// Returns the public map. 64 fn public_map(&self) -> &Self::PublicMap; 65 /// Returns the private map. 66 fn private_map(&self) -> &Self::PrivateMap; 67 /// Returns the record map. 68 fn record_map(&self) -> &Self::RecordMap; 69 /// Returns the record nonce map. 70 fn record_nonce_map(&self) -> &Self::RecordNonceMap; 71 /// Returns the record sender map. 72 fn record_sender_map(&self) -> &Self::RecordSenderMap; 73 /// Returns the external record map. 74 fn external_record_map(&self) -> &Self::ExternalRecordMap; 75 /// Returns the future map. 76 fn future_map(&self) -> &Self::FutureMap; 77 78 /// Returns the storage mode. 79 fn storage_mode(&self) -> &StorageMode; 80 81 /// Starts an atomic batch write operation. 82 fn start_atomic(&self) { 83 self.id_map().start_atomic(); 84 self.reverse_id_map().start_atomic(); 85 self.constant_map().start_atomic(); 86 self.public_map().start_atomic(); 87 self.private_map().start_atomic(); 88 self.record_map().start_atomic(); 89 self.record_nonce_map().start_atomic(); 90 self.record_sender_map().start_atomic(); 91 self.external_record_map().start_atomic(); 92 self.future_map().start_atomic(); 93 } 94 95 /// Checks if an atomic batch is in progress. 96 fn is_atomic_in_progress(&self) -> bool { 97 self.id_map().is_atomic_in_progress() 98 || self.reverse_id_map().is_atomic_in_progress() 99 || self.constant_map().is_atomic_in_progress() 100 || self.public_map().is_atomic_in_progress() 101 || self.private_map().is_atomic_in_progress() 102 || self.record_map().is_atomic_in_progress() 103 || self.record_nonce_map().is_atomic_in_progress() 104 || self.record_sender_map().is_atomic_in_progress() 105 || self.external_record_map().is_atomic_in_progress() 106 || self.future_map().is_atomic_in_progress() 107 } 108 109 /// Checkpoints the atomic batch. 110 fn atomic_checkpoint(&self) { 111 self.id_map().atomic_checkpoint(); 112 self.reverse_id_map().atomic_checkpoint(); 113 self.constant_map().atomic_checkpoint(); 114 self.public_map().atomic_checkpoint(); 115 self.private_map().atomic_checkpoint(); 116 self.record_map().atomic_checkpoint(); 117 self.record_nonce_map().atomic_checkpoint(); 118 self.record_sender_map().atomic_checkpoint(); 119 self.external_record_map().atomic_checkpoint(); 120 self.future_map().atomic_checkpoint(); 121 } 122 123 /// Clears the latest atomic batch checkpoint. 124 fn clear_latest_checkpoint(&self) { 125 self.id_map().clear_latest_checkpoint(); 126 self.reverse_id_map().clear_latest_checkpoint(); 127 self.constant_map().clear_latest_checkpoint(); 128 self.public_map().clear_latest_checkpoint(); 129 self.private_map().clear_latest_checkpoint(); 130 self.record_map().clear_latest_checkpoint(); 131 self.record_nonce_map().clear_latest_checkpoint(); 132 self.record_sender_map().clear_latest_checkpoint(); 133 self.external_record_map().clear_latest_checkpoint(); 134 self.future_map().clear_latest_checkpoint(); 135 } 136 137 /// Rewinds the atomic batch to the previous checkpoint. 138 fn atomic_rewind(&self) { 139 self.id_map().atomic_rewind(); 140 self.reverse_id_map().atomic_rewind(); 141 self.constant_map().atomic_rewind(); 142 self.public_map().atomic_rewind(); 143 self.private_map().atomic_rewind(); 144 self.record_map().atomic_rewind(); 145 self.record_nonce_map().atomic_rewind(); 146 self.record_sender_map().atomic_rewind(); 147 self.external_record_map().atomic_rewind(); 148 self.future_map().atomic_rewind(); 149 } 150 151 /// Aborts an atomic batch write operation. 152 fn abort_atomic(&self) { 153 self.id_map().abort_atomic(); 154 self.reverse_id_map().abort_atomic(); 155 self.constant_map().abort_atomic(); 156 self.public_map().abort_atomic(); 157 self.private_map().abort_atomic(); 158 self.record_map().abort_atomic(); 159 self.record_nonce_map().abort_atomic(); 160 self.record_sender_map().abort_atomic(); 161 self.external_record_map().abort_atomic(); 162 self.future_map().abort_atomic(); 163 } 164 165 /// Finishes an atomic batch write operation. 166 fn finish_atomic(&self) -> Result<()> { 167 self.id_map().finish_atomic()?; 168 self.reverse_id_map().finish_atomic()?; 169 self.constant_map().finish_atomic()?; 170 self.public_map().finish_atomic()?; 171 self.private_map().finish_atomic()?; 172 self.record_map().finish_atomic()?; 173 self.record_nonce_map().finish_atomic()?; 174 self.record_sender_map().finish_atomic()?; 175 self.external_record_map().finish_atomic()?; 176 self.future_map().finish_atomic() 177 } 178 179 /// Stores the given `(transition ID, output)` pair into storage. 180 fn insert(&self, transition_id: N::TransitionID, outputs: &[Output<N>]) -> Result<()> { 181 atomic_batch_scope!(self, { 182 // Store the output IDs. 183 self.id_map().insert(transition_id, outputs.iter().map(Output::id).copied().collect())?; 184 185 // Store the outputs. 186 for output in outputs { 187 // Store the reverse output ID. 188 self.reverse_id_map().insert(*output.id(), transition_id)?; 189 // Store the output. 190 match output.clone() { 191 Output::Constant(output_id, constant) => self.constant_map().insert(output_id, constant)?, 192 Output::Public(output_id, public) => self.public_map().insert(output_id, public)?, 193 Output::Private(output_id, private) => self.private_map().insert(output_id, private)?, 194 Output::Record(commitment, checksum, optional_record, optional_sender) => { 195 // If the optional record ciphertext exists, insert the record nonce. 196 if let Some(record) = &optional_record { 197 // Insert the record nonce to commitment. 198 self.record_nonce_map().insert(*record.nonce(), commitment)?; 199 // If the optional sender ciphertext exists, insert the record sender. 200 self.record_sender_map().insert(*record.nonce(), optional_sender)?; 201 } 202 // Insert the record entry. 203 self.record_map().insert(commitment, (checksum, optional_record))? 204 } 205 Output::ExternalRecord(output_id) => self.external_record_map().insert(output_id, ())?, 206 Output::Future(output_id, future) => self.future_map().insert(output_id, future)?, 207 } 208 } 209 210 Ok(()) 211 }) 212 } 213 214 /// Removes the output for the given `transition ID`. 215 fn remove(&self, transition_id: &N::TransitionID) -> Result<()> { 216 // Retrieve the output IDs. 217 let output_ids: Vec<_> = match self.id_map().get_confirmed(transition_id)? { 218 Some(Cow::Borrowed(ids)) => ids.to_vec(), 219 Some(Cow::Owned(ids)) => ids.into_iter().collect(), 220 None => return Ok(()), 221 }; 222 223 atomic_batch_scope!(self, { 224 // Remove the output IDs. 225 self.id_map().remove(transition_id)?; 226 227 // Remove the outputs. 228 for output_id in output_ids { 229 // Remove the reverse output ID. 230 self.reverse_id_map().remove(&output_id)?; 231 232 // If the output is a record, remove the record nonce and record sender. 233 if let Some(record) = self.record_map().get_confirmed(&output_id)? { 234 if let Some(record) = &record.1 { 235 // Remove the record nonce. 236 self.record_nonce_map().remove(record.nonce())?; 237 // Remove the record sender, if it exists. 238 self.record_sender_map().remove(record.nonce())?; 239 } 240 } 241 242 // Remove the output. 243 self.constant_map().remove(&output_id)?; 244 self.public_map().remove(&output_id)?; 245 self.private_map().remove(&output_id)?; 246 self.record_map().remove(&output_id)?; 247 self.external_record_map().remove(&output_id)?; 248 self.future_map().remove(&output_id)?; 249 } 250 251 Ok(()) 252 }) 253 } 254 255 /// Returns the transition ID that contains the given `output ID`. 256 fn find_transition_id(&self, output_id: &Field<N>) -> Result<Option<N::TransitionID>> { 257 match self.reverse_id_map().get_confirmed(output_id)? { 258 Some(Cow::Borrowed(transition_id)) => Ok(Some(*transition_id)), 259 Some(Cow::Owned(transition_id)) => Ok(Some(transition_id)), 260 None => Ok(None), 261 } 262 } 263 264 /// Returns the output IDs for the given `transition ID`. 265 fn get_ids(&self, transition_id: &N::TransitionID) -> Result<Vec<Field<N>>> { 266 // Retrieve the output IDs. 267 match self.id_map().get_confirmed(transition_id)? { 268 Some(Cow::Borrowed(outputs)) => Ok(outputs.to_vec()), 269 Some(Cow::Owned(outputs)) => Ok(outputs), 270 None => Ok(vec![]), 271 } 272 } 273 274 /// Returns the output for the given `transition ID`. 275 fn get(&self, transition_id: &N::TransitionID) -> Result<Vec<Output<N>>> { 276 // Constructs the output given the output ID and output value. 277 macro_rules! into_output { 278 (Output::Record($output_id:ident, $output:expr)) => { 279 match $output { 280 Cow::Borrowed((checksum, Some(record))) => Output::Record($output_id, *checksum, Some(record.clone()), match self.record_sender_map().get_confirmed(&record.nonce())? { 281 Some(sender) => *sender, 282 None => None, 283 }), 284 Cow::Borrowed((checksum, None)) => Output::Record($output_id, *checksum, None, None), 285 Cow::Owned((checksum, Some(record))) => { 286 let record_nonce = *record.nonce(); // We extract the nonce here to avoid cloning the entire record. 287 Output::Record($output_id, checksum, Some(record), match self.record_sender_map().get_confirmed(&record_nonce)? { 288 Some(sender) => *sender, 289 None => None, 290 }) 291 }, 292 Cow::Owned((checksum, None)) => Output::Record($output_id, checksum, None, None), 293 } 294 }; 295 (Output::$Variant:ident($output_id:ident, $output:expr)) => { 296 match $output { 297 Cow::Borrowed(output) => Output::$Variant($output_id, output.clone()), 298 Cow::Owned(output) => Output::$Variant($output_id, output), 299 } 300 }; 301 } 302 303 // A helper function to construct the output given the output ID. 304 let construct_output = |output_id| { 305 if let Some(constant) = self.constant_map().get_confirmed(&output_id)? { 306 return Ok(into_output!(Output::Constant(output_id, constant))); 307 } 308 if let Some(public) = self.public_map().get_confirmed(&output_id)? { 309 return Ok(into_output!(Output::Public(output_id, public))); 310 } 311 if let Some(private) = self.private_map().get_confirmed(&output_id)? { 312 return Ok(into_output!(Output::Private(output_id, private))); 313 } 314 if let Some(record) = self.record_map().get_confirmed(&output_id)? { 315 return Ok(into_output!(Output::Record(output_id, record))); 316 } 317 if self.external_record_map().get_confirmed(&output_id)?.is_some() { 318 return Ok(Output::ExternalRecord(output_id)); 319 } 320 if let Some(future) = self.future_map().get_confirmed(&output_id)? { 321 return Ok(into_output!(Output::Future(output_id, future))); 322 } 323 324 bail!("Missing output '{output_id}' in transition '{transition_id}'") 325 }; 326 327 // Retrieve the output IDs. 328 match self.id_map().get_confirmed(transition_id)? { 329 Some(Cow::Borrowed(ids)) => ids.iter().map(|output_id| construct_output(*output_id)).collect(), 330 Some(Cow::Owned(ids)) => ids.iter().map(|output_id| construct_output(*output_id)).collect(), 331 None => Ok(vec![]), 332 } 333 } 334 } 335 336 /// The transition output store. 337 #[derive(Clone)] 338 pub struct OutputStore<N: Network, O: OutputStorage<N>> { 339 /// The map of constant outputs. 340 constant: O::ConstantMap, 341 /// The map of public outputs. 342 public: O::PublicMap, 343 /// The map of private outputs. 344 private: O::PrivateMap, 345 /// The map of record outputs. 346 record: O::RecordMap, 347 /// The map of record nonces. 348 record_nonce: O::RecordNonceMap, 349 /// The map of record senders. 350 record_sender: O::RecordSenderMap, 351 /// The map of external record outputs. 352 external_record: O::ExternalRecordMap, 353 /// The map of future outputs. 354 future: O::FutureMap, 355 /// The output storage. 356 storage: O, 357 } 358 359 impl<N: Network, O: OutputStorage<N>> OutputStore<N, O> { 360 /// Initializes the transition output store. 361 pub fn open<S: Into<StorageMode>>(storage: S) -> Result<Self> { 362 // Initialize a new transition output storage. 363 let storage = O::open(storage)?; 364 // Return the transition output store. 365 Ok(Self { 366 constant: storage.constant_map().clone(), 367 public: storage.public_map().clone(), 368 private: storage.private_map().clone(), 369 record: storage.record_map().clone(), 370 record_nonce: storage.record_nonce_map().clone(), 371 record_sender: storage.record_sender_map().clone(), 372 external_record: storage.external_record_map().clone(), 373 future: storage.future_map().clone(), 374 storage, 375 }) 376 } 377 378 /// Initializes a transition output store from storage. 379 pub fn from(storage: O) -> Self { 380 Self { 381 constant: storage.constant_map().clone(), 382 public: storage.public_map().clone(), 383 private: storage.private_map().clone(), 384 record: storage.record_map().clone(), 385 record_nonce: storage.record_nonce_map().clone(), 386 record_sender: storage.record_sender_map().clone(), 387 external_record: storage.external_record_map().clone(), 388 future: storage.future_map().clone(), 389 storage, 390 } 391 } 392 393 /// Stores the given `(transition ID, output)` pair into storage. 394 pub fn insert(&self, transition_id: N::TransitionID, outputs: &[Output<N>]) -> Result<()> { 395 self.storage.insert(transition_id, outputs) 396 } 397 398 /// Removes the output for the given `transition ID`. 399 pub fn remove(&self, transition_id: &N::TransitionID) -> Result<()> { 400 self.storage.remove(transition_id) 401 } 402 403 /// Starts an atomic batch write operation. 404 pub fn start_atomic(&self) { 405 self.storage.start_atomic(); 406 } 407 408 /// Checks if an atomic batch is in progress. 409 pub fn is_atomic_in_progress(&self) -> bool { 410 self.storage.is_atomic_in_progress() 411 } 412 413 /// Checkpoints the atomic batch. 414 pub fn atomic_checkpoint(&self) { 415 self.storage.atomic_checkpoint(); 416 } 417 418 /// Clears the latest atomic batch checkpoint. 419 pub fn clear_latest_checkpoint(&self) { 420 self.storage.clear_latest_checkpoint(); 421 } 422 423 /// Rewinds the atomic batch to the previous checkpoint. 424 pub fn atomic_rewind(&self) { 425 self.storage.atomic_rewind(); 426 } 427 428 /// Aborts an atomic batch write operation. 429 pub fn abort_atomic(&self) { 430 self.storage.abort_atomic(); 431 } 432 433 /// Finishes an atomic batch write operation. 434 pub fn finish_atomic(&self) -> Result<()> { 435 self.storage.finish_atomic() 436 } 437 438 /// Returns the storage mode. 439 pub fn storage_mode(&self) -> &StorageMode { 440 self.storage.storage_mode() 441 } 442 } 443 444 impl<N: Network, O: OutputStorage<N>> OutputStore<N, O> { 445 /// Returns the output IDs for the given `transition ID`. 446 pub fn get_output_ids(&self, transition_id: &N::TransitionID) -> Result<Vec<Field<N>>> { 447 self.storage.get_ids(transition_id) 448 } 449 450 /// Returns the outputs for the given `transition ID`. 451 pub fn get_outputs(&self, transition_id: &N::TransitionID) -> Result<Vec<Output<N>>> { 452 self.storage.get(transition_id) 453 } 454 455 /// Returns the record for the given `commitment`. 456 /// 457 /// If the record exists, `Ok(Some(record))` is returned. 458 /// If the record was purged, `Ok(None)` is returned. 459 /// If the record does not exist, `Err(error)` is returned. 460 pub fn get_record(&self, commitment: &Field<N>) -> Result<Option<Record<N, Ciphertext<N>>>> { 461 match self.record.get_confirmed(commitment) { 462 Ok(Some(Cow::Borrowed((_, Some(record))))) => Ok(Some((*record).clone())), 463 Ok(Some(Cow::Owned((_, Some(record))))) => Ok(Some(record)), 464 Ok(Some(Cow::Borrowed((_, None)))) => Ok(None), 465 Ok(Some(Cow::Owned((_, None)))) => Ok(None), 466 Ok(None) => bail!("Record '{commitment}' not found"), 467 Err(e) => Err(e), 468 } 469 } 470 471 /// Returns the record sender ciphertext for the given `nonce`. 472 pub fn get_record_sender_ciphertext(&self, nonce: &Group<N>) -> Result<Option<Field<N>>> { 473 match self.record_sender.get_confirmed(nonce)? { 474 Some(Cow::Borrowed(sender)) => Ok(*sender), 475 Some(Cow::Owned(sender)) => Ok(sender), 476 None => Ok(None), 477 } 478 } 479 } 480 481 impl<N: Network, O: OutputStorage<N>> OutputStore<N, O> { 482 /// Returns the transition ID that contains the given `output ID`. 483 pub fn find_transition_id(&self, output_id: &Field<N>) -> Result<Option<N::TransitionID>> { 484 self.storage.find_transition_id(output_id) 485 } 486 } 487 488 impl<N: Network, O: OutputStorage<N>> OutputStore<N, O> { 489 /// Returns `true` if the given output ID exists. 490 pub fn contains_output_id(&self, output_id: &Field<N>) -> Result<bool> { 491 self.storage.reverse_id_map().contains_key_confirmed(output_id) 492 } 493 494 /// Returns `true` if the given commitment exists. 495 pub fn contains_commitment(&self, commitment: &Field<N>) -> Result<bool> { 496 self.record.contains_key_confirmed(commitment) 497 } 498 499 /// Returns `true` if the given checksum exists. 500 pub fn contains_checksum(&self, checksum: &Field<N>) -> bool { 501 self.checksums().contains(checksum) 502 } 503 504 /// Returns `true` if the given nonce exists. 505 pub fn contains_nonce(&self, nonce: &Group<N>) -> Result<bool> { 506 self.record_nonce.contains_key_confirmed(nonce) 507 } 508 } 509 510 impl<N: Network, O: OutputStorage<N>> OutputStore<N, O> { 511 /// Returns an iterator over the output IDs, for all transition outputs. 512 pub fn output_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> { 513 self.storage.reverse_id_map().keys_confirmed() 514 } 515 516 /// Returns an iterator over the constant output IDs, for all transition outputs that are constant. 517 pub fn constant_output_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> { 518 self.constant.keys_confirmed() 519 } 520 521 /// Returns an iterator over the public output IDs, for all transition outputs that are public. 522 pub fn public_output_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> { 523 self.public.keys_confirmed() 524 } 525 526 /// Returns an iterator over the private output IDs, for all transition outputs that are private. 527 pub fn private_output_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> { 528 self.private.keys_confirmed() 529 } 530 531 /// Returns an iterator over the commitments, for all transition outputs that are records. 532 pub fn commitments(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> { 533 self.record.keys_confirmed() 534 } 535 536 /// Returns an iterator over the external record output IDs, for all transition outputs that are external records. 537 pub fn external_output_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> { 538 self.external_record.keys_confirmed() 539 } 540 541 /// Returns an iterator over the future output IDs, for all transition outputs that are future outputs. 542 pub fn future_output_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> { 543 self.future.keys_confirmed() 544 } 545 } 546 547 impl<N: Network, I: OutputStorage<N>> OutputStore<N, I> { 548 /// Returns an iterator over the constant outputs, for all transitions. 549 pub fn constant_outputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Plaintext<N>>> { 550 self.constant.values_confirmed().flat_map(|output| match output { 551 Cow::Borrowed(Some(output)) => Some(Cow::Borrowed(output)), 552 Cow::Owned(Some(output)) => Some(Cow::Owned(output)), 553 _ => None, 554 }) 555 } 556 557 /// Returns an iterator over the constant outputs, for all transitions. 558 pub fn public_outputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Plaintext<N>>> { 559 self.public.values_confirmed().flat_map(|output| match output { 560 Cow::Borrowed(Some(output)) => Some(Cow::Borrowed(output)), 561 Cow::Owned(Some(output)) => Some(Cow::Owned(output)), 562 _ => None, 563 }) 564 } 565 566 /// Returns an iterator over the private outputs, for all transitions. 567 pub fn private_outputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Ciphertext<N>>> { 568 self.private.values_confirmed().flat_map(|output| match output { 569 Cow::Borrowed(Some(output)) => Some(Cow::Borrowed(output)), 570 Cow::Owned(Some(output)) => Some(Cow::Owned(output)), 571 _ => None, 572 }) 573 } 574 575 /// Returns an iterator over the checksums, for all transition outputs that are records. 576 pub fn checksums(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> { 577 self.record.values_confirmed().map(|output| match output { 578 Cow::Borrowed((checksum, _)) => Cow::Borrowed(checksum), 579 Cow::Owned((checksum, _)) => Cow::Owned(checksum), 580 }) 581 } 582 583 /// Returns an iterator over the nonces, for all transition outputs that are records. 584 pub fn nonces(&self) -> impl '_ + Iterator<Item = Cow<'_, Group<N>>> { 585 self.record_nonce.keys_confirmed() 586 } 587 588 /// Returns an iterator over the `(commitment, record)` pairs, for all transition outputs that are records. 589 #[allow(clippy::type_complexity)] 590 pub fn records(&self) -> impl '_ + Iterator<Item = (Cow<'_, Field<N>>, Cow<'_, Record<N, Ciphertext<N>>>)> { 591 self.record.iter_confirmed().flat_map(|(commitment, output)| match output { 592 Cow::Borrowed((_, Some(record))) => Some((commitment, Cow::Borrowed(record))), 593 Cow::Owned((_, Some(record))) => Some((commitment, Cow::Owned(record))), 594 _ => None, 595 }) 596 } 597 598 /// Returns an iterator over the future outputs, for all transitions. 599 pub fn future_outputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Future<N>>> { 600 self.future.values_confirmed().flat_map(|output| match output { 601 Cow::Borrowed(Some(output)) => Some(Cow::Borrowed(output)), 602 Cow::Owned(Some(output)) => Some(Cow::Owned(output)), 603 _ => None, 604 }) 605 } 606 } 607 608 #[cfg(test)] 609 mod tests { 610 use super::*; 611 use crate::helpers::memory::OutputMemory; 612 613 #[test] 614 fn test_insert_get_remove() { 615 // Sample the transition outputs. 616 for (transition_id, output) in alphavm_ledger_test_helpers::sample_outputs() { 617 // Initialize a new output store. 618 let output_store = OutputMemory::open(StorageMode::Test(None)).unwrap(); 619 620 // Ensure the transition output does not exist. 621 let candidate = output_store.get(&transition_id).unwrap(); 622 assert!(candidate.is_empty()); 623 624 // Insert the transition output. 625 output_store.insert(transition_id, &[output.clone()]).unwrap(); 626 627 // Retrieve the transition output. 628 let candidate = output_store.get(&transition_id).unwrap(); 629 assert_eq!(vec![output.clone()], candidate); 630 631 // Remove the transition output. 632 output_store.remove(&transition_id).unwrap(); 633 634 // Retrieve the transition output. 635 let candidate = output_store.get(&transition_id).unwrap(); 636 assert!(candidate.is_empty()); 637 } 638 } 639 640 #[test] 641 fn test_find_transition_id() { 642 // Sample the transition outputs. 643 for (transition_id, output) in alphavm_ledger_test_helpers::sample_outputs() { 644 // Initialize a new output store. 645 let output_store = OutputMemory::open(StorageMode::Test(None)).unwrap(); 646 647 // Ensure the transition output does not exist. 648 let candidate = output_store.get(&transition_id).unwrap(); 649 assert!(candidate.is_empty()); 650 651 // Ensure the transition ID is not found. 652 let candidate = output_store.find_transition_id(output.id()).unwrap(); 653 assert!(candidate.is_none()); 654 655 // Insert the transition output. 656 output_store.insert(transition_id, &[output.clone()]).unwrap(); 657 658 // Find the transition ID. 659 let candidate = output_store.find_transition_id(output.id()).unwrap(); 660 assert_eq!(Some(transition_id), candidate); 661 662 // Remove the transition output. 663 output_store.remove(&transition_id).unwrap(); 664 665 // Ensure the transition ID is not found. 666 let candidate = output_store.find_transition_id(output.id()).unwrap(); 667 assert!(candidate.is_none()); 668 } 669 } 670 }