Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions gateway-contract/contracts/core_contract/src/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use soroban_sdk::contracterror;

#[contracterror]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ContractError {
AlreadyInitialized = 1,
NotInitialized = 2,
RootMismatch = 3,
InvalidProof = 4,
DuplicateCommitment = 5,
}
12 changes: 12 additions & 0 deletions gateway-contract/contracts/core_contract/src/events.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
use soroban_sdk::{contractevent, BytesN};

#[contractevent]
pub struct UsernameRegistered {
pub commitment: BytesN<32>,
}

#[contractevent]
pub struct MerkleRootUpdated {
pub old_root: BytesN<32>,
pub new_root: BytesN<32>,
}
#![allow(dead_code)]

use soroban_sdk::{symbol_short, Symbol};
Expand Down
94 changes: 66 additions & 28 deletions gateway-contract/contracts/core_contract/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,48 +1,86 @@
#![no_std]

pub mod events;
mod errors;
mod events;
mod storage;
mod types;

use soroban_sdk::{
contract, contracterror, contractimpl, contracttype, panic_with_error, Address, BytesN, Env,
};
#[cfg(test)]
mod test;

use soroban_sdk::{contract, contractclient, contractimpl, panic_with_error, Address, BytesN, Env};

pub use crate::errors::ContractError;
pub use crate::events::{MerkleRootUpdated, UsernameRegistered};
pub use crate::types::{Proof, PublicSignals};

#[contract]
pub struct Contract;
pub struct CoreContract;

#[contracttype]
#[derive(Clone)]
pub enum DataKey {
Resolver(BytesN<32>),
#[contractclient(name = "VerifierContractClient")]
pub trait VerifierContract {
fn verify_proof(env: Env, proof: Proof, public_signals: PublicSignals) -> bool;
}

#[contracttype]
#[derive(Clone)]
pub struct ResolveData {
pub wallet: Address,
pub memo: Option<u64>,
fn current_merkle_root(env: &Env) -> BytesN<32> {
storage::get_merkle_root(env)
.unwrap_or_else(|| panic_with_error!(env, ContractError::NotInitialized))
}

#[contracterror]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ResolverError {
NotFound = 1,
fn update_merkle_root(env: &Env, old_root: BytesN<32>, new_root: BytesN<32>) {
storage::set_merkle_root(env, &new_root);

MerkleRootUpdated { old_root, new_root }.publish(env);
}

#[contractimpl]
impl Contract {
pub fn register_resolver(env: Env, commitment: BytesN<32>, wallet: Address, memo: Option<u64>) {
let key = DataKey::Resolver(commitment);
let data = ResolveData { wallet, memo };
impl CoreContract {
pub fn init(env: Env, verifier: Address, root: BytesN<32>) {
if storage::is_initialized(&env) {
panic_with_error!(&env, ContractError::AlreadyInitialized);
}

env.storage().persistent().set(&key, &data);
storage::set_verifier(&env, &verifier);
storage::set_merkle_root(&env, &root);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cd gateway-contract && find . -type f -name "*.rs" | head -20

Repository: Alien-Protocol/Alien-Gateway

Length of output: 896


🏁 Script executed:

cd gateway-contract && rg "fn init" --type rust -B 2 -A 15

Repository: Alien-Protocol/Alien-Gateway

Length of output: 2467


🏁 Script executed:

cd gateway-contract && rg "invoke_contract_check_host_approval|__check_auth" --type rust

Repository: Alien-Protocol/Alien-Gateway

Length of output: 54


🏁 Script executed:

cd gateway-contract && git log --oneline --all | head -20

Repository: Alien-Protocol/Alien-Gateway

Length of output: 1611


🏁 Script executed:

cd gateway-contract && find . -type f \( -name "*.toml" -o -name "*.md" \) | head -20

Repository: Alien-Protocol/Alien-Gateway

Length of output: 404


🏁 Script executed:

cd gateway-contract && sed -n '1,100p' contracts/core_contract/src/test.rs | grep -A 20 "fn setup"

Repository: Alien-Protocol/Alien-Gateway

Length of output: 766


🏁 Script executed:

cd gateway-contract && rg "fn setup" contracts/core_contract/src/test.rs -A 25

Repository: Alien-Protocol/Alien-Gateway

Length of output: 884


🏁 Script executed:

cd gateway-contract && cat -n contracts/core_contract/src/storage.rs | head -80

Repository: Alien-Protocol/Alien-Gateway

Length of output: 1558


🏁 Script executed:

cd gateway-contract && rg "is_initialized\|initialization" contracts/core_contract/src/ -B 2 -A 3

Repository: Alien-Protocol/Alien-Gateway

Length of output: 54


🏁 Script executed:

cd gateway-contract && cat contracts/core_contract/Cargo.toml | grep -A 10 "soroban"

Repository: Alien-Protocol/Alien-Gateway

Length of output: 187


🏁 Script executed:

cd gateway-contract && rg "contractimpl|contract_fn|soroban_sdk" contracts/core_contract/src/lib.rs | head -20

Repository: Alien-Protocol/Alien-Gateway

Length of output: 186


🏁 Script executed:

cd gateway-contract && find . -type f -name "*.rs" -exec grep -l "deploy\|register" {} \; | head -10

Repository: Alien-Protocol/Alien-Gateway

Length of output: 195


🏁 Script executed:

cd gateway-contract && cat -n contracts/core_contract/src/lib.rs | sed -n '1,70p'

Repository: Alien-Protocol/Alien-Gateway

Length of output: 2839


🏁 Script executed:

cd gateway-contract && rg "require_auth\|require\|auth" contracts/core_contract/src/lib.rs

Repository: Alien-Protocol/Alien-Gateway

Length of output: 54


🏁 Script executed:

cd gateway-contract && find . -type f -name "*.rs" -path "*/factory*" -o -name "*deploy*" | xargs cat 2>/dev/null | head -100

Repository: Alien-Protocol/Alien-Gateway

Length of output: 207


🏁 Script executed:

cd gateway-contract && cat contracts/core_contract/core.md 2>/dev/null | head -150

Repository: Alien-Protocol/Alien-Gateway

Length of output: 54


🏁 Script executed:

cd gateway-contract && cat readme.md 2>/dev/null | grep -A 20 -i "init\|deploy\|setup"

Repository: Alien-Protocol/Alien-Gateway

Length of output: 54


🏁 Script executed:

cd gateway-contract && rg "fn init" contracts/ -B 5 -A 20 --type rust | grep -E "(init|pub|auth|require)" | head -30

Repository: Alien-Protocol/Alien-Gateway

Length of output: 1143


🏁 Script executed:

cd gateway-contract && web_search "Soroban public contract function authorization require_auth"

Repository: Alien-Protocol/Alien-Gateway

Length of output: 122


🏁 Script executed:

cd gateway-contract && find . -type f \( -name "*.md" -o -name "*.txt" \) -exec grep -l "init\|deploy" {} \;

Repository: Alien-Protocol/Alien-Gateway

Length of output: 54


🌐 Web query:

Soroban contract require_auth authorization public functions

💡 Result:

In Soroban, any exported contract function (often called “public”) is callable by anyone unless your code explicitly enforces authorization. Soroban does not auto-restrict access based on “public/private”; you enforce permissions by calling require_auth (or require_auth_for_args) on the Address that must approve the action. [1][9]

How require_auth works (what it guarantees)

  • some_address.require_auth() tells the Soroban host: “this invocation must be authorized by some_address”.
  • The host then handles signature verification + replay prevention (you don’t manually verify signatures in the contract). [9]
  • If some_address is a contract address (account abstraction), the host calls that contract’s __check_auth hook to validate the authorization. [1]

Pattern for “public” functions that must be protected

Call require_auth() at the start of the function for the party whose rights are being exercised:

pub fn withdraw(env: Env, from: Address, amount: i128) {
    from.require_auth();          // must be authorized by `from`
    // ... mutate balances / transfer tokens ...
}

This is the core pattern used in Soroban auth examples. [2]

require_auth_for_args (more specific authorization)

Use require_auth_for_args(...) when you want the signature to be bound to specific arguments / intent, which enables safer delegation patterns (e.g., delegated signers / smart accounts authorizing particular calls). [3]

Cross-contract calls and “sub-invocation” authorization

If your contract calls another contract that does require_auth, Soroban’s auth framework supports authorizing those sub-contract calls as part of the same authorized invocation tree (you generally don’t need special wiring beyond using require_auth). [1]

Practical “when do I require auth?” rule of thumb

  • Read-only access to a user’s data: usually no require_auth needed.
  • Mutating a user’s state in a way that isn’t strictly beneficial (e.g., decreasing balance / transferring out): you should require_auth. [1]

Sources: Stellar authorization docs and examples. [1][2][3][9]


🏁 Script executed:

cd gateway-contract && cat -n contracts/core_contract/src/lib.rs | sed -n '30,80p'

Repository: Alien-Protocol/Alien-Gateway

Length of output: 2224


🏁 Script executed:

cd gateway-contract && rg "TODO\|FIXME\|XXX\|HACK\|NOTE" contracts/core_contract/src/lib.rs

Repository: Alien-Protocol/Alien-Gateway

Length of output: 54


🏁 Script executed:

cd gateway-contract && git log --all --oneline -- contracts/core_contract/src/lib.rs | head -10

Repository: Alien-Protocol/Alien-Gateway

Length of output: 474


🏁 Script executed:

cd gateway-contract && git show d864384 | grep -A 5 -B 5 "init\|auth" | head -40

Repository: Alien-Protocol/Alien-Gateway

Length of output: 2151


🏁 Script executed:

cd gateway-contract && git show d864384 | grep -A 50 "init\|Entry point" | head -80

Repository: Alien-Protocol/Alien-Gateway

Length of output: 4103


🏁 Script executed:

cd gateway-contract && git show d864384 | grep -B 5 -A 5 "^| \`init\`"

Repository: Alien-Protocol/Alien-Gateway

Length of output: 54


🏁 Script executed:

cd gateway-contract && git show d864384 | grep -C 10 "core_contract" | grep -A 30 "Entry point"

Repository: Alien-Protocol/Alien-Gateway

Length of output: 2951


🏁 Script executed:

cd gateway-contract && git show d864384:contracts/core_contract/src/lib.rs 2>/dev/null | head -100

Repository: Alien-Protocol/Alien-Gateway

Length of output: 54


🏁 Script executed:

cd gateway-contract && rg "fn init\|pub fn " contracts/core_contract/src/lib.rs | head -15

Repository: Alien-Protocol/Alien-Gateway

Length of output: 54


🏁 Script executed:

cd gateway-contract && git log --all --oneline --diff-filter=A -- contracts/core_contract/src/lib.rs

Repository: Alien-Protocol/Alien-Gateway

Length of output: 183


🏁 Script executed:

cd gateway-contract && wc -l contracts/core_contract/src/lib.rs && grep -c "pub fn" contracts/core_contract/src/lib.rs

Repository: Alien-Protocol/Alien-Gateway

Length of output: 111


🏁 Script executed:

cd gateway-contract && cat -n contracts/core_contract/src/lib.rs | grep "pub fn"

Repository: Alien-Protocol/Alien-Gateway

Length of output: 430


Add require_auth() to gate initialization to a trusted deployer.

This entrypoint lacks authorization checks, so any caller can invoke init() first and permanently set both the verifier and initial merkle root. If deployment and initialization are separate transactions, a frontrunner can anchor arbitrary state before the intended owner. Call require_auth() on a trusted deployer address, or ensure initialization is atomic with contract deployment.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gateway-contract/contracts/core_contract/src/lib.rs` around lines 38 - 45,
The init() entrypoint must require authorization before mutating state: call
env.require_auth(...) at the start of init to restrict who can initialize
(either require_auth(&env, &verifier) if the verifier is the trusted deployer,
or add an explicit deployer Address parameter and call
env.require_auth(&deployer)); keep the existing storage::is_initialized check
and ContractError::AlreadyInitialized panic, and only call storage::set_verifier
and storage::set_merkle_root after the require_auth check succeeds.


pub fn resolve(env: Env, commitment: BytesN<32>) -> ResolveData {
let key = DataKey::Resolver(commitment);
pub fn submit_proof(env: Env, proof: Proof, public_signals: PublicSignals) {
let current_root = current_merkle_root(&env);

match env.storage().persistent().get::<DataKey, ResolveData>(&key) {
Some(data) => data,
None => panic_with_error!(&env, ResolverError::NotFound),
if current_root != public_signals.old_root.clone() {
panic_with_error!(&env, ContractError::RootMismatch);
}

if storage::has_commitment(&env, &public_signals.commitment) {
panic_with_error!(&env, ContractError::DuplicateCommitment);
}

let verifier = storage::get_verifier(&env)
.unwrap_or_else(|| panic_with_error!(&env, ContractError::NotInitialized));
let verifier_client = VerifierContractClient::new(&env, &verifier);
let is_valid = verifier_client.verify_proof(&proof, &public_signals);
if !is_valid {
panic_with_error!(&env, ContractError::InvalidProof);
}

storage::store_commitment(&env, &public_signals.commitment);
update_merkle_root(&env, current_root, public_signals.new_root.clone());
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's explore the repository structure
find . -type f -name "*.rs" | grep -E "(lib|contract)" | head -20

Repository: Alien-Protocol/Alien-Gateway

Length of output: 1236


🏁 Script executed:

# Check the exact file and lines mentioned
if [ -f "gateway-contract/contracts/core_contract/src/lib.rs" ]; then
  wc -l gateway-contract/contracts/core_contract/src/lib.rs
fi

Repository: Alien-Protocol/Alien-Gateway

Length of output: 126


🏁 Script executed:

# Read the file around the specified lines
if [ -f "gateway-contract/contracts/core_contract/src/lib.rs" ]; then
  head -80 gateway-contract/contracts/core_contract/src/lib.rs | tail -40
fi

Repository: Alien-Protocol/Alien-Gateway

Length of output: 1463


🏁 Script executed:

# Search for reentrancy guard implementations in the codebase
rg "reentrancy|Reentrancy|re_entrant|ReEntrant" --type rs

Repository: Alien-Protocol/Alien-Gateway

Length of output: 98


🏁 Script executed:

# Look for any mutex or lock patterns that might be reentrancy guards
rg "Mutex|RwLock|atomic" gateway-contract/contracts/core_contract/src/ --type rs

Repository: Alien-Protocol/Alien-Gateway

Length of output: 98


🏁 Script executed:

# Check error handling in verifier calls - search for try_ patterns
rg "try_invoke|try_call|\.try_" gateway-contract/contracts/core_contract/src/ --type rs -A 3

Repository: Alien-Protocol/Alien-Gateway

Length of output: 98


🏁 Script executed:

# Search for reentrancy patterns without type restriction
rg "reentrancy|Reentrancy|REENTRANCY" gateway-contract/

Repository: Alien-Protocol/Alien-Gateway

Length of output: 206


🏁 Script executed:

# Look at the full submit_proof function and surrounding context
cat -n gateway-contract/contracts/core_contract/src/lib.rs | sed -n '40,75p'

Repository: Alien-Protocol/Alien-Gateway

Length of output: 1655


🏁 Script executed:

# Search for VerifierContractClient definition and available methods
rg "VerifierContractClient|verify_proof" gateway-contract/ -A 5 -B 2

Repository: Alien-Protocol/Alien-Gateway

Length of output: 4213


🏁 Script executed:

# Check if there's a try_verify_proof method
rg "try_verify_proof|try_" gateway-contract/contracts/core_contract/src/lib.rs -B 2 -A 2

Repository: Alien-Protocol/Alien-Gateway

Length of output: 54


🏁 Script executed:

# Search for reentrancy guard patterns more broadly
rg "Mutex|RwLock|guard|locked|re_enter" gateway-contract/contracts/core_contract/ -i

Repository: Alien-Protocol/Alien-Gateway

Length of output: 54


🏁 Script executed:

# Check the storage module for any reentrancy guards on state
cat -n gateway-contract/contracts/core_contract/src/storage.rs | head -50

Repository: Alien-Protocol/Alien-Gateway

Length of output: 1558


🏁 Script executed:

# Look for any comments or patterns related to reentrancy in the entire core_contract
grep -r "reenter\|re-enter\|reentr" gateway-contract/contracts/core_contract/ -i

Repository: Alien-Protocol/Alien-Gateway

Length of output: 54


🏁 Script executed:

# Check how panic_with_error is defined
rg "macro_rules! panic_with_error|fn panic_with_error" gateway-contract/ -A 5

Repository: Alien-Protocol/Alien-Gateway

Length of output: 54


🏁 Script executed:

# Look at the error types
cat -n gateway-contract/contracts/core_contract/src/errors.rs

Repository: Alien-Protocol/Alien-Gateway

Length of output: 395


🏁 Script executed:

# Check if there are try_ methods in the contract
rg "try_submit_proof|pub fn" gateway-contract/contracts/core_contract/src/lib.rs

Repository: Alien-Protocol/Alien-Gateway

Length of output: 395


🏁 Script executed:

# Look for try_ invocation patterns in Soroban
rg "try_invoke|try_call" gateway-contract/ -B 2 -A 2

Repository: Alien-Protocol/Alien-Gateway

Length of output: 515


🏁 Script executed:

# Check the full context of how verifier_client is used - look for all invocations
rg "verifier_client\." gateway-contract/contracts/core_contract/src/ -B 1 -A 1

Repository: Alien-Protocol/Alien-Gateway

Length of output: 980


🏁 Script executed:

# Let's check if there's a try_submit_proof method or any try_ variant methods
cat -n gateway-contract/contracts/core_contract/src/lib.rs

Repository: Alien-Protocol/Alien-Gateway

Length of output: 3313


🏁 Script executed:

# Search for how other contracts handle verifier calls to see if there's a pattern
rg "verify_proof|verifier_client" gateway-contract/ -B 3 -A 3

Repository: Alien-Protocol/Alien-Gateway

Length of output: 3686


🏁 Script executed:

# Check if there are any contract test cases that demonstrate reentrancy behavior
cat -n gateway-contract/contracts/core_contract/src/test.rs | grep -A 20 "reenter\|reentr" -i

Repository: Alien-Protocol/Alien-Gateway

Length of output: 54


🏁 Script executed:

# Look for the complete lib.rs to see all public functions and their signatures
cat -n gateway-contract/contracts/core_contract/src/lib.rs | grep -E "pub fn|pub trait"

Repository: Alien-Protocol/Alien-Gateway

Length of output: 466


🏁 Script executed:

# Check if there are any reentrancy guard patterns in other contracts
cat gateway-contract/contracts/escrow_contract/src/lib.rs | head -100

Repository: Alien-Protocol/Alien-Gateway

Length of output: 3159


🏁 Script executed:

# Let's check the test to understand the behavior better
cat -n gateway-contract/contracts/core_contract/src/test.rs | head -100

Repository: Alien-Protocol/Alien-Gateway

Length of output: 3389


🏁 Script executed:

# Search for any context/mutex/lock patterns in the Soroban SDK usage
rg "env\.|storage\(\)" gateway-contract/contracts/core_contract/src/lib.rs | head -20

Repository: Alien-Protocol/Alien-Gateway

Length of output: 54


Add reentrancy protection or re-validate state after the verifier call.

The root and duplicate-commitment checks (lines 50–56) execute before the external verifier call (line 61), then their results are trusted without re-validation. A malicious verifier that reenters submit_proof can invalidate those checks. Additionally, the direct verify_proof() invocation on line 61 does not use try_ error handling, so verifier panics or errors will leak raw invoke errors instead of mapping to ContractError.

Either (1) re-read current_root and recheck has_commitment after the verifier returns, or (2) add a reentrancy guard to prevent reentry during the verification call. Explicitly map any verifier invocation errors to ContractError variants.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gateway-contract/contracts/core_contract/src/lib.rs` around lines 47 - 67,
submit_proof performs root and commitment checks before calling out to the
external verifier (VerifierContractClient::verify_proof) which allows reentrancy
and also doesn't map verifier errors; fix by either adding a reentrancy guard
around the verify_proof call or by re-reading and re-checking
current_merkle_root(&env) and storage::has_commitment(&env,
&public_signals.commitment) immediately after verify_proof returns, and ensure
the verifier invocation uses the fallible/try-style API (map the Result/err to a
ContractError like ContractError::VerifierError instead of letting raw panics
propagate). Update relevant symbols: submit_proof,
VerifierContractClient::verify_proof, storage::get_verifier,
current_merkle_root, storage::has_commitment, storage::store_commitment,
update_merkle_root.


UsernameRegistered {
commitment: public_signals.commitment,
}
.publish(&env);
}

pub fn get_merkle_root(env: Env) -> BytesN<32> {
current_merkle_root(&env)
}

pub fn get_verifier(env: Env) -> Option<Address> {
storage::get_verifier(&env)
}

pub fn has_commitment(env: Env, commitment: BytesN<32>) -> bool {
storage::has_commitment(&env, &commitment)
}
}
44 changes: 44 additions & 0 deletions gateway-contract/contracts/core_contract/src/storage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use soroban_sdk::{contracttype, Address, BytesN, Env};

#[contracttype]
#[derive(Clone)]
pub enum DataKey {
CurrentMerkleRoot,
Verifier,
Commitment(BytesN<32>),
}

pub fn is_initialized(env: &Env) -> bool {
env.storage().persistent().has(&DataKey::CurrentMerkleRoot)
&& env.storage().instance().has(&DataKey::Verifier)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fail closed on partial initialization state.

is_initialized() currently returns false unless both keys exist, but only one missing key is enough to make a second init() dangerous. If the verifier entry ever disappears while the persistent root remains, init() can overwrite the anchored root outside the proof flow. At minimum, treat either key as “already initialized”; ideally keep the verifier in the same durability class as the root.

🛠️ Minimal fail-closed fix
 pub fn is_initialized(env: &Env) -> bool {
     env.storage().persistent().has(&DataKey::CurrentMerkleRoot)
-        && env.storage().instance().has(&DataKey::Verifier)
+        || env.storage().instance().has(&DataKey::Verifier)
 }

Also applies to: 26-31

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gateway-contract/contracts/core_contract/src/storage.rs` around lines 11 -
13, The current is_initialized function uses a logical AND so it returns false
if either DataKey::CurrentMerkleRoot or DataKey::Verifier is missing, which
allows init() to run when one of the two is present — change the check in
is_initialized (function name: is_initialized) to treat the presence of either
key as “already initialized” (use logical OR between
env.storage().persistent().has(&DataKey::CurrentMerkleRoot) and
env.storage().instance().has(&DataKey::Verifier)) so init() will fail-closed;
additionally consider moving the Verifier entry to persistent storage (make
DataKey::Verifier stored via env.storage().persistent()) so both items share
durability to avoid asymmetric deletion risks.

}

pub fn get_merkle_root(env: &Env) -> Option<BytesN<32>> {
env.storage().persistent().get(&DataKey::CurrentMerkleRoot)
}

pub fn set_merkle_root(env: &Env, root: &BytesN<32>) {
env.storage()
.persistent()
.set(&DataKey::CurrentMerkleRoot, root);
}

pub fn get_verifier(env: &Env) -> Option<Address> {
env.storage().instance().get(&DataKey::Verifier)
}

pub fn set_verifier(env: &Env, verifier: &Address) {
env.storage().instance().set(&DataKey::Verifier, verifier);
}

pub fn has_commitment(env: &Env, commitment: &BytesN<32>) -> bool {
env.storage()
.persistent()
.has(&DataKey::Commitment(commitment.clone()))
}

pub fn store_commitment(env: &Env, commitment: &BytesN<32>) {
env.storage()
.persistent()
.set(&DataKey::Commitment(commitment.clone()), &true);
}
Loading
Loading