/ console / program / src / data / record / is_owner.rs
is_owner.rs
  1  // Copyright (c) 2025-2026 ACDC Network
  2  // This file is part of the alphavm library.
  3  //
  4  // Alpha Chain | Delta Chain Protocol
  5  // International Monetary Graphite.
  6  //
  7  // Derived from Aleo (https://aleo.org) and ProvableHQ (https://provable.com).
  8  // They built world-class ZK infrastructure. We installed the EASY button.
  9  // Their cryptography: elegant. Our modifications: bureaucracy-compatible.
 10  // Original brilliance: theirs. Robert's Rules: ours. Bugs: definitely ours.
 11  //
 12  // Original Aleo/ProvableHQ code subject to Apache 2.0 https://www.apache.org/licenses/LICENSE-2.0
 13  // All modifications and new work: CC0 1.0 Universal Public Domain Dedication.
 14  // No rights reserved. No permission required. No warranty. No refunds.
 15  //
 16  // https://creativecommons.org/publicdomain/zero/1.0/
 17  // SPDX-License-Identifier: CC0-1.0
 18  
 19  use super::*;
 20  
 21  impl<N: Network> Record<N, Ciphertext<N>> {
 22      /// Returns `true` if the given view key corresponds to the owner of the record.
 23      /// Decrypts `self` into plaintext using the given view key.
 24      pub fn is_owner(&self, view_key: &ViewKey<N>) -> bool {
 25          // Compute the address.
 26          let address = view_key.to_address();
 27          // Check if the address is the owner.
 28          self.is_owner_with_address_x_coordinate(view_key, &address.to_x_coordinate())
 29      }
 30  
 31      /// Returns `true` if the given view key and address x-coordinate corresponds to the owner of the record.
 32      /// Decrypts `self` into plaintext using the x-coordinate of the address corresponding to the given view key.
 33      pub fn is_owner_with_address_x_coordinate(&self, view_key: &ViewKey<N>, address_x_coordinate: &Field<N>) -> bool {
 34          // In debug mode, check that the address corresponds to the given view key.
 35          debug_assert_eq!(
 36              &view_key.to_address().to_x_coordinate(),
 37              address_x_coordinate,
 38              "Failed to check record - view key and address do not match"
 39          );
 40  
 41          match &self.owner {
 42              // If the owner is public, check if the address is the owner.
 43              Owner::Public(owner) => &owner.to_x_coordinate() == address_x_coordinate,
 44              // If the owner is private, decrypt the owner to check if it matches the address.
 45              Owner::Private(ciphertext) => {
 46                  // Compute the record view key.
 47                  let record_view_key = (self.nonce * **view_key).to_x_coordinate();
 48                  // Compute the 0th randomizer.
 49                  let randomizer = N::hash_many_psd8(&[N::encryption_domain(), record_view_key], 1);
 50                  // Decrypt the owner.
 51                  let owner_x = ciphertext[0] - randomizer[0];
 52                  // Compare the x coordinates of computed and supplied addresses.
 53                  // We can skip recomputing the address from `owner_x` due to the following reasoning.
 54                  // First, the transaction SNARK that generated the ciphertext would have checked that the ciphertext encrypts a valid address.
 55                  // Now, since a valid address is an element of the prime-order subgroup of the curve, we know that the encrypted x-coordinate corresponds to a prime-order element.
 56                  // Finally, since the SNARK + hybrid encryption
 57                  // together are an authenticated encryption scheme, we know that the ciphertext has not been malleated.
 58                  // Thus overall we know that if the x-coordinate matches that of `address`, then the underlying `address`es must also match.
 59                  // Therefore we can skip recomputing the address from `owner_x` and instead compare the x-coordinates directly.
 60                  &owner_x == address_x_coordinate
 61              }
 62          }
 63      }
 64  }
 65  
 66  #[cfg(test)]
 67  mod tests {
 68      use super::*;
 69      use crate::Literal;
 70      use alphavm_console_account::PrivateKey;
 71      use alphavm_console_network::MainnetV0;
 72      use alphavm_console_types::Field;
 73  
 74      type CurrentNetwork = MainnetV0;
 75  
 76      const ITERATIONS: u64 = 1_000;
 77  
 78      fn check_is_owner<N: Network>(
 79          view_key: ViewKey<N>,
 80          owner: Owner<N, Plaintext<N>>,
 81          rng: &mut TestRng,
 82      ) -> Result<()> {
 83          // Prepare the record.
 84          let randomizer = Scalar::rand(rng);
 85          let record = Record {
 86              owner,
 87              data: IndexMap::from_iter(vec![
 88                  (Identifier::from_str("a")?, Entry::Private(Plaintext::from(Literal::Field(Field::rand(rng))))),
 89                  (Identifier::from_str("b")?, Entry::Private(Plaintext::from(Literal::Scalar(Scalar::rand(rng))))),
 90              ]),
 91              nonce: N::g_scalar_multiply(&randomizer),
 92              version: U8::rand(rng),
 93          };
 94  
 95          // Encrypt the record.
 96          let ciphertext = record.encrypt(randomizer)?;
 97  
 98          // Ensure the record belongs to the owner.
 99          assert!(ciphertext.is_owner(&view_key));
100  
101          // Sample a random view key and address.
102          let private_key = PrivateKey::<N>::new(rng)?;
103          let view_key = ViewKey::try_from(&private_key)?;
104  
105          // Ensure the random address is not the owner.
106          assert!(!ciphertext.is_owner(&view_key));
107  
108          Ok(())
109      }
110  
111      #[test]
112      fn test_is_owner() -> Result<()> {
113          let mut rng = TestRng::default();
114  
115          for _ in 0..ITERATIONS {
116              // Sample a view key and address.
117              let private_key = PrivateKey::<CurrentNetwork>::new(&mut rng)?;
118              let view_key = ViewKey::try_from(&private_key)?;
119              let address = Address::try_from(&private_key)?;
120  
121              // Public owner.
122              let owner = Owner::Public(address);
123              check_is_owner::<CurrentNetwork>(view_key, owner, &mut rng)?;
124  
125              // Private owner.
126              let owner = Owner::Private(Plaintext::from(Literal::Address(address)));
127              check_is_owner::<CurrentNetwork>(view_key, owner, &mut rng)?;
128          }
129          Ok(())
130      }
131  }