input.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 crate::{ 17 atomic_batch_scope, 18 helpers::{Map, MapRead}, 19 }; 20 use deltavm_ledger_block::Input; 21 use console::{ 22 network::prelude::*, 23 program::{Ciphertext, Plaintext}, 24 types::Field, 25 }; 26 27 use alphastd_storage::StorageMode; 28 use anyhow::Result; 29 use std::borrow::Cow; 30 31 /// A trait for transition input storage. 32 pub trait InputStorage<N: Network>: Clone + Send + Sync { 33 /// The mapping of `transition ID` to `input IDs`. 34 type IDMap: for<'a> Map<'a, N::TransitionID, Vec<Field<N>>>; 35 /// The mapping of `input 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 `serial number` to `tag`. 44 type RecordMap: for<'a> Map<'a, Field<N>, Field<N>>; 45 /// The mapping of `tag` to `serial number`. 46 type RecordTagMap: for<'a> Map<'a, Field<N>, Field<N>>; 47 /// The mapping of `external hash` to `()`. Note: This is **not** the record commitment. 48 type ExternalRecordMap: for<'a> Map<'a, Field<N>, ()>; 49 50 /// Initializes the transition input storage. 51 fn open<S: Into<StorageMode>>(storage: S) -> Result<Self>; 52 53 /// Returns the ID map. 54 fn id_map(&self) -> &Self::IDMap; 55 /// Returns the reverse ID map. 56 fn reverse_id_map(&self) -> &Self::ReverseIDMap; 57 /// Returns the constant map. 58 fn constant_map(&self) -> &Self::ConstantMap; 59 /// Returns the public map. 60 fn public_map(&self) -> &Self::PublicMap; 61 /// Returns the private map. 62 fn private_map(&self) -> &Self::PrivateMap; 63 /// Returns the record map. 64 fn record_map(&self) -> &Self::RecordMap; 65 /// Returns the record tag map. 66 fn record_tag_map(&self) -> &Self::RecordTagMap; 67 /// Returns the external record map. 68 fn external_record_map(&self) -> &Self::ExternalRecordMap; 69 70 /// Returns the storage mode. 71 fn storage_mode(&self) -> &StorageMode; 72 73 /// Starts an atomic batch write operation. 74 fn start_atomic(&self) { 75 self.id_map().start_atomic(); 76 self.reverse_id_map().start_atomic(); 77 self.constant_map().start_atomic(); 78 self.public_map().start_atomic(); 79 self.private_map().start_atomic(); 80 self.record_map().start_atomic(); 81 self.record_tag_map().start_atomic(); 82 self.external_record_map().start_atomic(); 83 } 84 85 /// Checks if an atomic batch is in progress. 86 fn is_atomic_in_progress(&self) -> bool { 87 self.id_map().is_atomic_in_progress() 88 || self.reverse_id_map().is_atomic_in_progress() 89 || self.constant_map().is_atomic_in_progress() 90 || self.public_map().is_atomic_in_progress() 91 || self.private_map().is_atomic_in_progress() 92 || self.record_map().is_atomic_in_progress() 93 || self.record_tag_map().is_atomic_in_progress() 94 || self.external_record_map().is_atomic_in_progress() 95 } 96 97 /// Checkpoints the atomic batch. 98 fn atomic_checkpoint(&self) { 99 self.id_map().atomic_checkpoint(); 100 self.reverse_id_map().atomic_checkpoint(); 101 self.constant_map().atomic_checkpoint(); 102 self.public_map().atomic_checkpoint(); 103 self.private_map().atomic_checkpoint(); 104 self.record_map().atomic_checkpoint(); 105 self.record_tag_map().atomic_checkpoint(); 106 self.external_record_map().atomic_checkpoint(); 107 } 108 109 /// Clears the latest atomic batch checkpoint. 110 fn clear_latest_checkpoint(&self) { 111 self.id_map().clear_latest_checkpoint(); 112 self.reverse_id_map().clear_latest_checkpoint(); 113 self.constant_map().clear_latest_checkpoint(); 114 self.public_map().clear_latest_checkpoint(); 115 self.private_map().clear_latest_checkpoint(); 116 self.record_map().clear_latest_checkpoint(); 117 self.record_tag_map().clear_latest_checkpoint(); 118 self.external_record_map().clear_latest_checkpoint(); 119 } 120 121 /// Rewinds the atomic batch to the previous checkpoint. 122 fn atomic_rewind(&self) { 123 self.id_map().atomic_rewind(); 124 self.reverse_id_map().atomic_rewind(); 125 self.constant_map().atomic_rewind(); 126 self.public_map().atomic_rewind(); 127 self.private_map().atomic_rewind(); 128 self.record_map().atomic_rewind(); 129 self.record_tag_map().atomic_rewind(); 130 self.external_record_map().atomic_rewind(); 131 } 132 133 /// Aborts an atomic batch write operation. 134 fn abort_atomic(&self) { 135 self.id_map().abort_atomic(); 136 self.reverse_id_map().abort_atomic(); 137 self.constant_map().abort_atomic(); 138 self.public_map().abort_atomic(); 139 self.private_map().abort_atomic(); 140 self.record_map().abort_atomic(); 141 self.record_tag_map().abort_atomic(); 142 self.external_record_map().abort_atomic(); 143 } 144 145 /// Finishes an atomic batch write operation. 146 fn finish_atomic(&self) -> Result<()> { 147 self.id_map().finish_atomic()?; 148 self.reverse_id_map().finish_atomic()?; 149 self.constant_map().finish_atomic()?; 150 self.public_map().finish_atomic()?; 151 self.private_map().finish_atomic()?; 152 self.record_map().finish_atomic()?; 153 self.record_tag_map().finish_atomic()?; 154 self.external_record_map().finish_atomic() 155 } 156 157 /// Stores the given `(transition ID, input)` pair into storage. 158 fn insert(&self, transition_id: N::TransitionID, inputs: &[Input<N>]) -> Result<()> { 159 atomic_batch_scope!(self, { 160 // Store the input IDs. 161 self.id_map().insert(transition_id, inputs.iter().map(Input::id).copied().collect())?; 162 163 // Store the inputs. 164 for input in inputs { 165 // Store the reverse input ID. 166 self.reverse_id_map().insert(*input.id(), transition_id)?; 167 // Store the input. 168 match input.clone() { 169 Input::Constant(input_id, constant) => self.constant_map().insert(input_id, constant)?, 170 Input::Public(input_id, public) => self.public_map().insert(input_id, public)?, 171 Input::Private(input_id, private) => self.private_map().insert(input_id, private)?, 172 Input::Record(serial_number, tag) => { 173 // Store the record tag. 174 self.record_tag_map().insert(tag, serial_number)?; 175 // Store the record. 176 self.record_map().insert(serial_number, tag)? 177 } 178 Input::ExternalRecord(input_id) => self.external_record_map().insert(input_id, ())?, 179 } 180 } 181 182 Ok(()) 183 }) 184 } 185 186 /// Removes the input for the given `transition ID`. 187 fn remove(&self, transition_id: &N::TransitionID) -> Result<()> { 188 // Retrieve the input IDs. 189 let input_ids: Vec<_> = match self.id_map().get_confirmed(transition_id)? { 190 Some(Cow::Borrowed(ids)) => ids.to_vec(), 191 Some(Cow::Owned(ids)) => ids.into_iter().collect(), 192 None => return Ok(()), 193 }; 194 195 atomic_batch_scope!(self, { 196 // Remove the input IDs. 197 self.id_map().remove(transition_id)?; 198 199 // Remove the inputs. 200 for input_id in input_ids { 201 // Remove the reverse input ID. 202 self.reverse_id_map().remove(&input_id)?; 203 204 // If the input is a record, remove the record tag. 205 if let Some(tag) = self.record_map().get_confirmed(&input_id)? { 206 self.record_tag_map().remove(&tag)?; 207 } 208 209 // Remove the input. 210 self.constant_map().remove(&input_id)?; 211 self.public_map().remove(&input_id)?; 212 self.private_map().remove(&input_id)?; 213 self.record_map().remove(&input_id)?; 214 self.external_record_map().remove(&input_id)?; 215 } 216 217 Ok(()) 218 }) 219 } 220 221 /// Returns the transition ID that contains the given `input ID`. 222 fn find_transition_id(&self, input_id: &Field<N>) -> Result<Option<N::TransitionID>> { 223 match self.reverse_id_map().get_confirmed(input_id)? { 224 Some(Cow::Borrowed(transition_id)) => Ok(Some(*transition_id)), 225 Some(Cow::Owned(transition_id)) => Ok(Some(transition_id)), 226 None => Ok(None), 227 } 228 } 229 230 /// Returns the input IDs for the given `transition ID`. 231 fn get_ids(&self, transition_id: &N::TransitionID) -> Result<Vec<Field<N>>> { 232 // Retrieve the input IDs. 233 match self.id_map().get_confirmed(transition_id)? { 234 Some(Cow::Borrowed(inputs)) => Ok(inputs.to_vec()), 235 Some(Cow::Owned(inputs)) => Ok(inputs), 236 None => Ok(vec![]), 237 } 238 } 239 240 /// Returns the input for the given `transition ID`. 241 fn get(&self, transition_id: &N::TransitionID) -> Result<Vec<Input<N>>> { 242 // Constructs the input given the input ID and input value. 243 macro_rules! into_input { 244 (Input::Record($input_id:ident, $input:expr)) => { 245 match $input { 246 Cow::Borrowed(tag) => Input::Record($input_id, *tag), 247 Cow::Owned(tag) => Input::Record($input_id, tag), 248 } 249 }; 250 (Input::$Variant:ident($input_id:ident, $input:expr)) => { 251 match $input { 252 Cow::Borrowed(input) => Input::$Variant($input_id, input.clone()), 253 Cow::Owned(input) => Input::$Variant($input_id, input), 254 } 255 }; 256 } 257 258 // A helper function to construct the input given the input ID. 259 let construct_input = |input_id| { 260 let constant = self.constant_map().get_confirmed(&input_id)?; 261 let public = self.public_map().get_confirmed(&input_id)?; 262 let private = self.private_map().get_confirmed(&input_id)?; 263 let record = self.record_map().get_confirmed(&input_id)?; 264 let external_record = self.external_record_map().get_confirmed(&input_id)?; 265 266 // Retrieve the input. 267 let input = match (constant, public, private, record, external_record) { 268 (Some(constant), None, None, None, None) => into_input!(Input::Constant(input_id, constant)), 269 (None, Some(public), None, None, None) => into_input!(Input::Public(input_id, public)), 270 (None, None, Some(private), None, None) => into_input!(Input::Private(input_id, private)), 271 (None, None, None, Some(record), None) => into_input!(Input::Record(input_id, record)), 272 (None, None, None, None, Some(_)) => Input::ExternalRecord(input_id), 273 (None, None, None, None, None) => bail!("Missing input '{input_id}' in transition '{transition_id}'"), 274 _ => bail!("Found multiple inputs for the input ID '{input_id}' in transition '{transition_id}'"), 275 }; 276 277 Ok(input) 278 }; 279 280 // Retrieve the input IDs. 281 match self.id_map().get_confirmed(transition_id)? { 282 Some(Cow::Borrowed(ids)) => ids.iter().map(|input_id| construct_input(*input_id)).collect(), 283 Some(Cow::Owned(ids)) => ids.iter().map(|input_id| construct_input(*input_id)).collect(), 284 None => Ok(vec![]), 285 } 286 } 287 } 288 289 /// The transition input store. 290 #[derive(Clone)] 291 pub struct InputStore<N: Network, I: InputStorage<N>> { 292 /// The map of constant inputs. 293 constant: I::ConstantMap, 294 /// The map of public inputs. 295 public: I::PublicMap, 296 /// The map of private inputs. 297 private: I::PrivateMap, 298 /// The map of record inputs. 299 record: I::RecordMap, 300 /// The map of record tags. 301 record_tag: I::RecordTagMap, 302 /// The map of external record inputs. 303 external_record: I::ExternalRecordMap, 304 /// The input storage. 305 storage: I, 306 } 307 308 impl<N: Network, I: InputStorage<N>> InputStore<N, I> { 309 /// Initializes the transition input store. 310 pub fn open<S: Into<StorageMode>>(storage: S) -> Result<Self> { 311 // Initialize a new transition input storage. 312 let storage = I::open(storage)?; 313 // Return the transition input store. 314 Ok(Self { 315 constant: storage.constant_map().clone(), 316 public: storage.public_map().clone(), 317 private: storage.private_map().clone(), 318 record: storage.record_map().clone(), 319 record_tag: storage.record_tag_map().clone(), 320 external_record: storage.external_record_map().clone(), 321 storage, 322 }) 323 } 324 325 /// Initializes a transition input store from storage. 326 pub fn from(storage: I) -> Self { 327 Self { 328 constant: storage.constant_map().clone(), 329 public: storage.public_map().clone(), 330 private: storage.private_map().clone(), 331 record: storage.record_map().clone(), 332 record_tag: storage.record_tag_map().clone(), 333 external_record: storage.external_record_map().clone(), 334 storage, 335 } 336 } 337 338 /// Stores the given `(transition ID, input)` pair into storage. 339 pub fn insert(&self, transition_id: N::TransitionID, inputs: &[Input<N>]) -> Result<()> { 340 self.storage.insert(transition_id, inputs) 341 } 342 343 /// Removes the input for the given `transition ID`. 344 pub fn remove(&self, transition_id: &N::TransitionID) -> Result<()> { 345 self.storage.remove(transition_id) 346 } 347 348 /// Starts an atomic batch write operation. 349 pub fn start_atomic(&self) { 350 self.storage.start_atomic(); 351 } 352 353 /// Checks if an atomic batch is in progress. 354 pub fn is_atomic_in_progress(&self) -> bool { 355 self.storage.is_atomic_in_progress() 356 } 357 358 /// Checkpoints the atomic batch. 359 pub fn atomic_checkpoint(&self) { 360 self.storage.atomic_checkpoint(); 361 } 362 363 /// Clears the latest atomic batch checkpoint. 364 pub fn clear_latest_checkpoint(&self) { 365 self.storage.clear_latest_checkpoint(); 366 } 367 368 /// Rewinds the atomic batch to the previous checkpoint. 369 pub fn atomic_rewind(&self) { 370 self.storage.atomic_rewind(); 371 } 372 373 /// Aborts an atomic batch write operation. 374 pub fn abort_atomic(&self) { 375 self.storage.abort_atomic(); 376 } 377 378 /// Finishes an atomic batch write operation. 379 pub fn finish_atomic(&self) -> Result<()> { 380 self.storage.finish_atomic() 381 } 382 383 /// Returns the storage mode. 384 pub fn storage_mode(&self) -> &StorageMode { 385 self.storage.storage_mode() 386 } 387 } 388 389 impl<N: Network, I: InputStorage<N>> InputStore<N, I> { 390 /// Returns the input IDs for the given `transition ID`. 391 pub fn get_input_ids(&self, transition_id: &N::TransitionID) -> Result<Vec<Field<N>>> { 392 self.storage.get_ids(transition_id) 393 } 394 395 /// Returns the inputs for the given `transition ID`. 396 pub fn get_inputs(&self, transition_id: &N::TransitionID) -> Result<Vec<Input<N>>> { 397 self.storage.get(transition_id) 398 } 399 } 400 401 impl<N: Network, I: InputStorage<N>> InputStore<N, I> { 402 /// Returns the transition ID that contains the given `input ID`. 403 pub fn find_transition_id(&self, input_id: &Field<N>) -> Result<Option<N::TransitionID>> { 404 self.storage.find_transition_id(input_id) 405 } 406 } 407 408 impl<N: Network, I: InputStorage<N>> InputStore<N, I> { 409 /// Returns `true` if the given input ID exists. 410 pub fn contains_input_id(&self, input_id: &Field<N>) -> Result<bool> { 411 self.storage.reverse_id_map().contains_key_confirmed(input_id) 412 } 413 414 /// Returns `true` if the given serial number exists. 415 pub fn contains_serial_number(&self, serial_number: &Field<N>) -> Result<bool> { 416 self.record.contains_key_confirmed(serial_number) 417 } 418 419 /// Returns `true` if the given tag exists. 420 pub fn contains_tag(&self, tag: &Field<N>) -> Result<bool> { 421 self.record_tag.contains_key_confirmed(tag) 422 } 423 } 424 425 impl<N: Network, I: InputStorage<N>> InputStore<N, I> { 426 /// Returns an iterator over the input IDs, for all transition inputs. 427 pub fn input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> { 428 self.storage.reverse_id_map().keys_confirmed() 429 } 430 431 /// Returns an iterator over the constant input IDs, for all transition inputs that are constant. 432 pub fn constant_input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> { 433 self.constant.keys_confirmed() 434 } 435 436 /// Returns an iterator over the public input IDs, for all transition inputs that are public. 437 pub fn public_input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> { 438 self.public.keys_confirmed() 439 } 440 441 /// Returns an iterator over the private input IDs, for all transition inputs that are private. 442 pub fn private_input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> { 443 self.private.keys_confirmed() 444 } 445 446 /// Returns an iterator over the serial numbers, for all transition inputs that are records. 447 pub fn serial_numbers(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> { 448 self.record.keys_confirmed() 449 } 450 451 /// Returns an iterator over the external record input IDs, for all transition inputs that are external records. 452 pub fn external_input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> { 453 self.external_record.keys_confirmed() 454 } 455 } 456 457 impl<N: Network, I: InputStorage<N>> InputStore<N, I> { 458 /// Returns an iterator over the constant inputs, for all transitions. 459 pub fn constant_inputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Plaintext<N>>> { 460 self.constant.values_confirmed().flat_map(|input| match input { 461 Cow::Borrowed(Some(input)) => Some(Cow::Borrowed(input)), 462 Cow::Owned(Some(input)) => Some(Cow::Owned(input)), 463 _ => None, 464 }) 465 } 466 467 /// Returns an iterator over the constant inputs, for all transitions. 468 pub fn public_inputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Plaintext<N>>> { 469 self.public.values_confirmed().flat_map(|input| match input { 470 Cow::Borrowed(Some(input)) => Some(Cow::Borrowed(input)), 471 Cow::Owned(Some(input)) => Some(Cow::Owned(input)), 472 _ => None, 473 }) 474 } 475 476 /// Returns an iterator over the private inputs, for all transitions. 477 pub fn private_inputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Ciphertext<N>>> { 478 self.private.values_confirmed().flat_map(|input| match input { 479 Cow::Borrowed(Some(input)) => Some(Cow::Borrowed(input)), 480 Cow::Owned(Some(input)) => Some(Cow::Owned(input)), 481 _ => None, 482 }) 483 } 484 485 /// Returns an iterator over the tags, for all transition inputs that are records. 486 pub fn tags(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> { 487 self.record_tag.keys_confirmed() 488 } 489 } 490 491 #[cfg(test)] 492 mod tests { 493 use super::*; 494 use crate::helpers::memory::InputMemory; 495 496 #[test] 497 fn test_insert_get_remove() { 498 // Sample the transition inputs. 499 for (transition_id, input) in deltavm_ledger_test_helpers::sample_inputs() { 500 // Initialize a new input store. 501 let input_store = InputMemory::open(StorageMode::Test(None)).unwrap(); 502 503 // Ensure the transition input does not exist. 504 let candidate = input_store.get(&transition_id).unwrap(); 505 assert!(candidate.is_empty()); 506 507 // Insert the transition input. 508 input_store.insert(transition_id, &[input.clone()]).unwrap(); 509 510 // Retrieve the transition input. 511 let candidate = input_store.get(&transition_id).unwrap(); 512 assert_eq!(vec![input.clone()], candidate); 513 514 // Remove the transition input. 515 input_store.remove(&transition_id).unwrap(); 516 517 // Retrieve the transition input. 518 let candidate = input_store.get(&transition_id).unwrap(); 519 assert!(candidate.is_empty()); 520 } 521 } 522 523 #[test] 524 fn test_find_transition_id() { 525 // Sample the transition inputs. 526 for (transition_id, input) in deltavm_ledger_test_helpers::sample_inputs() { 527 // Initialize a new input store. 528 let input_store = InputMemory::open(StorageMode::Test(None)).unwrap(); 529 530 // Ensure the transition input does not exist. 531 let candidate = input_store.get(&transition_id).unwrap(); 532 assert!(candidate.is_empty()); 533 534 // Ensure the transition ID is not found. 535 let candidate = input_store.find_transition_id(input.id()).unwrap(); 536 assert!(candidate.is_none()); 537 538 // Insert the transition input. 539 input_store.insert(transition_id, &[input.clone()]).unwrap(); 540 541 // Find the transition ID. 542 let candidate = input_store.find_transition_id(input.id()).unwrap(); 543 assert_eq!(Some(transition_id), candidate); 544 545 // Remove the transition input. 546 input_store.remove(&transition_id).unwrap(); 547 548 // Ensure the transition ID is not found. 549 let candidate = input_store.find_transition_id(input.id()).unwrap(); 550 assert!(candidate.is_none()); 551 } 552 } 553 }