-
Notifications
You must be signed in to change notification settings - Fork 109
Introduce ProofsWithState for atomic proof state management #1488
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
6a59579 to
a22bdeb
Compare
d1e8279 to
e5f358d
Compare
9dca039 to
0d761ef
Compare
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.
bbff771 to
e520c90
Compare
e520c90 to
b07bede
Compare
| if ys.is_empty() { | ||
| return Ok(ProofsWithState::new(vec![], State::Unspent).into()); | ||
| } |
There was a problem hiding this comment.
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?
| /// 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) | ||
| } |
There was a problem hiding this comment.
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:
- MeltQuote (DB-first): State is updated in-memory after writing to DB, ensuring the two are always in sync.
- 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.
- 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.
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:
Benefits:
Notes to the reviewers
Suggested CHANGELOG Updates
CHANGED
ADDED
REMOVED
FIXED
Checklist
just final-checkbefore committing