diff --git a/crates/core/app-tests/tests/spend.rs b/crates/core/app-tests/tests/spend.rs index bbdba236a6..8be7a66917 100644 --- a/crates/core/app-tests/tests/spend.rs +++ b/crates/core/app-tests/tests/spend.rs @@ -17,7 +17,7 @@ use penumbra_sdk_sct::{ use penumbra_sdk_shielded_pool::{ component::ShieldedPool, Note, SpendPlan, SpendProof, SpendProofPrivate, SpendProofPublic, }; -use penumbra_sdk_txhash::{EffectHash, TransactionContext}; +use penumbra_sdk_txhash::{EffectHash, TransactionContext, TransactionId}; use rand_core::{OsRng, SeedableRng}; use std::{ops::Deref, sync::Arc}; use tendermint::abci; @@ -65,6 +65,7 @@ async fn spend_happy_path() -> anyhow::Result<()> { let transaction_context = TransactionContext { anchor: root, effect_hash: EffectHash(dummy_effect_hash), + transaction_id: TransactionId([0; 32]), }; // 3. Simulate execution of the Spend action @@ -189,6 +190,7 @@ async fn invalid_dummy_spend() { let transaction_context = TransactionContext { anchor: root, effect_hash: EffectHash(dummy_effect_hash), + transaction_id: TransactionId([0; 32]), }; // 3. Simulate execution of the Spend action diff --git a/crates/core/component/funding/src/component.rs b/crates/core/component/funding/src/component.rs index 728d06a33b..f9b8c9d7ed 100644 --- a/crates/core/component/funding/src/component.rs +++ b/crates/core/component/funding/src/component.rs @@ -9,6 +9,7 @@ use penumbra_sdk_asset::{Value, STAKING_TOKEN_ASSET_ID}; use penumbra_sdk_proto::{DomainType, StateWriteProto}; use penumbra_sdk_stake::component::validator_handler::ValidatorDataRead; pub use view::{StateReadExt, StateWriteExt}; +pub(crate) mod liquidity_tournament; use std::sync::Arc; diff --git a/crates/core/component/funding/src/component/liquidity_tournament/mod.rs b/crates/core/component/funding/src/component/liquidity_tournament/mod.rs new file mode 100644 index 0000000000..1d68566db0 --- /dev/null +++ b/crates/core/component/funding/src/component/liquidity_tournament/mod.rs @@ -0,0 +1 @@ +pub mod nullifier; diff --git a/crates/core/component/funding/src/component/liquidity_tournament/nullifier/mod.rs b/crates/core/component/funding/src/component/liquidity_tournament/nullifier/mod.rs new file mode 100644 index 0000000000..59a867eb38 --- /dev/null +++ b/crates/core/component/funding/src/component/liquidity_tournament/nullifier/mod.rs @@ -0,0 +1,53 @@ +use async_trait::async_trait; +use penumbra_sdk_txhash::TransactionId; + +use crate::component::state_key; +use cnidarium::{StateRead, StateWrite}; +use penumbra_sdk_proto::{StateReadProto, StateWriteProto}; +use penumbra_sdk_sct::{component::clock::EpochRead, Nullifier}; + +#[allow(dead_code)] +#[async_trait] +pub trait NullifierRead: StateRead { + /// Returns the `TransactionId` if the nullifier has been spent; otherwise, returns None. + async fn get_lqt_spent_nullifier(&self, nullifier: Nullifier) -> Option { + // Grab the ambient epoch index. + let epoch_index = self + .get_current_epoch() + .await + .expect("epoch is always set") + .index; + + let nullifier_key = &state_key::lqt::v1::nullifier::key(epoch_index, &nullifier); + + let tx_id: Option = self + .nonverifiable_get(&nullifier_key.as_bytes()) + .await + .expect(&format!( + "failed to retrieve key {} from non-verifiable storage", + nullifier_key, + )); + + tx_id + } +} + +impl NullifierRead for T {} + +#[allow(dead_code)] +#[async_trait] +pub trait NullifierWrite: StateWrite { + /// Sets the LQT nullifier in the NV storage. + fn put_lqt_spent_nullifier( + &mut self, + epoch_index: u64, + nullifier: Nullifier, + tx_id: TransactionId, + ) { + let nullifier_key = state_key::lqt::v1::nullifier::key(epoch_index, &nullifier); + + self.nonverifiable_put(nullifier_key.into(), tx_id); + } +} + +impl NullifierWrite for T {} diff --git a/crates/core/component/funding/src/component/state_key.rs b/crates/core/component/funding/src/component/state_key.rs index 5f8fe1975c..7a31d41ff3 100644 --- a/crates/core/component/funding/src/component/state_key.rs +++ b/crates/core/component/funding/src/component/state_key.rs @@ -1,3 +1,16 @@ pub fn funding_parameters() -> &'static str { "funding/parameters" } + +pub mod lqt { + pub mod v1 { + pub mod nullifier { + use penumbra_sdk_sct::Nullifier; + + /// A nullifier set indexed by epoch, mapping each epoch to its corresponding `TransactionId`. + pub(crate) fn key(epoch_index: u64, nullifier: &Nullifier) -> String { + format!("funding/lqt/v1/nullifier/{epoch_index:020}/lookup/{nullifier}") + } + } + } +} diff --git a/crates/core/component/funding/src/component/view.rs b/crates/core/component/funding/src/component/view.rs index 83fbdd0398..2235729763 100644 --- a/crates/core/component/funding/src/component/view.rs +++ b/crates/core/component/funding/src/component/view.rs @@ -1,7 +1,6 @@ -use async_trait::async_trait; - use crate::{component::state_key, params::FundingParameters}; use anyhow::Result; +use async_trait::async_trait; use cnidarium::{StateRead, StateWrite}; use penumbra_sdk_proto::{StateReadProto, StateWriteProto}; diff --git a/crates/core/transaction/src/transaction.rs b/crates/core/transaction/src/transaction.rs index d7154e9c72..42d91fd173 100644 --- a/crates/core/transaction/src/transaction.rs +++ b/crates/core/transaction/src/transaction.rs @@ -144,6 +144,7 @@ impl Transaction { TransactionContext { anchor: self.anchor, effect_hash: self.effect_hash(), + transaction_id: self.id(), } } diff --git a/crates/core/txhash/src/context.rs b/crates/core/txhash/src/context.rs index e036657a5a..9659883c01 100644 --- a/crates/core/txhash/src/context.rs +++ b/crates/core/txhash/src/context.rs @@ -1,4 +1,4 @@ -use crate::EffectHash; +use crate::{EffectHash, TransactionId}; use penumbra_sdk_tct as tct; /// Stateless verification context for a transaction. @@ -10,4 +10,6 @@ pub struct TransactionContext { pub anchor: tct::Root, /// The transaction's effect hash. pub effect_hash: EffectHash, + /// The transaction's id. + pub transaction_id: TransactionId, }