Skip to content

Conversation

@crodas
Copy link
Collaborator

@crodas crodas commented Jan 3, 2026

Description

This PR depends on #1490

Replace individual proof state operations with a unified ProofsWithState type that enforces the invariant that all proofs in a set share the same state. This shifts responsibility for state consistency to the database layer and simplifies state transition logic in the saga implementations.

Changes to ProofsTransaction trait:

  • add_proofs now returns Acquired
  • update_proofs_states(ys, state) -> update_proofs(&mut Acquired)
  • get_proofs_states(ys) -> get_proofs(ys) returning Acquired

Benefits:

  • State transitions validated in memory before persisting
  • Eliminates scattered state checking in saga code
  • Database layer guarantees proof state consistency
  • Cleaner API with state encapsulated in the type

Notes to the reviewers


Suggested CHANGELOG Updates

CHANGED

ADDED

REMOVED

FIXED


Checklist

@crodas crodas requested a review from thesimplekid January 3, 2026 03:44
@crodas crodas self-assigned this Jan 3, 2026
@crodas crodas force-pushed the feature/mint-acquired-proofs branch 2 times, most recently from 6a59579 to a22bdeb Compare January 3, 2026 04:57
@crodas crodas force-pushed the feature/mint-acquired-proofs branch 3 times, most recently from d1e8279 to e5f358d Compare January 4, 2026 05:31
@thesimplekid thesimplekid added this to the 0.15.0 milestone Jan 4, 2026
@crodas crodas force-pushed the feature/mint-acquired-proofs branch 2 times, most recently from 9dca039 to 0d761ef Compare January 4, 2026 21:26
crodas added 4 commits January 5, 2026 09:08
Split the large monolithic mod.rs file into separate files, each containing a
database trait and its corresponding transaction trait:

- proofs.rs: MintProofsTransaction + MintProofsDatabase
- keys.rs: MintKeyDatabaseTransaction + MintKeysDatabase
- quotes.rs: MintQuotesTransaction + MintQuotesDatabase
- signatures.rs: MintSignatureTransaction + MintSignaturesDatabase
- keyvalue.rs: KVStoreTransaction + KVStoreDatabase + KVStore
- saga.rs: SagaTransaction + SagaDatabase
- completed_operations.rs: CompletedOperationsTransaction +
  CompletedOperationsDatabase

The mod.rs now only contains struct definitions, migrations, and the main
MintDatabase/MintTransaction/DbTransactionFinalizer impls.
Replace individual proof state operations with a unified ProofsWithState type
that enforces the invariant that all proofs in a set share the same state. This
shifts responsibility for state consistency to the database layer and
simplifies state transition logic in the saga implementations.

Changes to ProofsTransaction trait:
- add_proofs now returns Acquired<ProofsWithState>
- update_proofs_states(ys, state) -> update_proofs(&mut
  Acquired<ProofsWithState>)
- get_proofs_states(ys) -> get_proofs(ys) returning Acquired<ProofsWithState>

Benefits:
- State transitions validated in memory before persisting
- Eliminates scattered state checking in saga code
- Database layer guarantees proof state consistency
- Cleaner API with state encapsulated in the type
Add get_proofs_fails_when_some_not_found test that verifies the database
returns an error when not all requested proofs exist, rather than silently
returning a partial result.

Fix get_proofs implementation to check that the number of rows returned matches
the number of requested Y values, returning ProofNotFound if any proofs are
missing.
@crodas crodas force-pushed the feature/mint-acquired-proofs branch from bbff771 to e520c90 Compare January 5, 2026 12:09
@crodas crodas force-pushed the feature/mint-acquired-proofs branch from e520c90 to b07bede Compare January 5, 2026 12:10
@crodas crodas requested a review from thesimplekid January 5, 2026 12:11
Comment on lines +353 to +355
if ys.is_empty() {
return Ok(ProofsWithState::new(vec![], State::Unspent).into());
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

This seems a bit weird setting this to unspent, should we just error on an empty query?

Comment on lines +100 to +114
/// Transitions all proofs to a new state.
///
/// Validates that the state transition is allowed before applying it.
/// Returns the previous state on success.
///
/// # Errors
///
/// Returns [`Error::UnexpectedProofState`] if the transition from the current
/// state to the new state is not permitted.
pub fn set_new_state(&mut self, new_state: State) -> Result<State, Error> {
check_state_transition(self.state, new_state).map_err(|_| Error::UnexpectedProofState)?;
let old_state = self.state;
self.state = new_state;
Ok(old_state)
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

This introduces a third state update pattern to the codebase. Currently we have:

  1. MeltQuote (DB-first): State is updated in-memory after writing to DB, ensuring the two are always in sync.
  2. MintQuote (change-tracking): Uses take_changes() to track and persist events. This is justified because MintQuote state is computed from payments/issuances rather than explicitly set.
  3. ProofsWithState (memory-first): Updates in-memory first via set_new_state(), then requires a separate update_proofs() call.

Since proofs follow a state machine (Unspent → Pending → Spent) just like MeltQuote, it makes sense to use the same pattern:

  // Current
  proofs.set_new_state(State::Pending)?;
  tx.update_proofs(&mut proofs).await?;

  // Suggested (matches MeltQuote pattern)
  tx.update_proofs_state(&mut proofs, State::Pending).await?;

This keeps persistence patterns consistent and eliminates the possibility of in-memory/DB state divergence if update_proofs() fails after set_new_state() succeeds.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants