refactor(cketh): make the transaction pipeline generic over its request - #11178
refactor(cketh): make the transaction pipeline generic over its request#11178gregorydemay wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR refactors the ckETH minter’s transaction state machine by making TransactionLane generic over a new LaneRequest trait, so the same send/resubmit/finalize machinery can later be reused for a second sender address without coupling it to withdrawal-specific concepts.
Changes:
- Introduces
LaneRequestand makesTransactionLane<R>generic over it (withWithdrawalRequestas the current implementation). - Moves transaction construction from a free function (
create_transaction) intoLaneRequest::to_transaction, and updates all call sites accordingly. - Renames lane-facing APIs/vocabulary from “withdrawal” to generic “request” (e.g.,
record_request,requests_batch,oldest_incomplete_request_timestamp) while keeping withdrawal/reimbursement concepts onEthTransactions.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| rs/ethereum/cketh/minter/src/withdraw.rs | Updates batching flow to use generic request APIs and LaneRequest::to_transaction. |
| rs/ethereum/cketh/minter/src/state/transactions/tests.rs | Updates tests for renamed APIs and moves the “accepted withdrawal request event” helper into tests. |
| rs/ethereum/cketh/minter/src/state/transactions/mod.rs | Introduces LaneRequest, makes TransactionLane generic, migrates transaction creation into to_transaction, and updates request-centric terminology. |
| rs/ethereum/cketh/minter/src/state/tests.rs | Updates state tests to construct transactions via to_transaction. |
| rs/ethereum/cketh/minter/src/state/audit.rs | Updates audit transition handling to call record_request. |
| rs/ethereum/cketh/minter/src/state.rs | Updates state methods to use renamed request accessors (record_request, get_processed_request). |
| rs/ethereum/cketh/minter/src/main.rs | Updates metric to use oldest_incomplete_request_timestamp. |
| rs/ethereum/cketh/minter/src/guard/tests.rs | Updates guard tests to call record_request. |
| rs/ethereum/cketh/minter/src/guard/mod.rs | Updates pending-request count to use requests_len. |
| rs/ethereum/cketh/minter/src/dashboard/tests.rs | Replaces removed event-conversion method with a local helper and uses to_transaction. |
| rs/ethereum/cketh/minter/src/dashboard.rs | Updates dashboard to iterate pending requests via requests_iter. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
65eda6f to
f597a7d
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.
Suppressed comments (2)
rs/ethereum/cketh/minter/src/state/transactions/mod.rs:1354
remove_requestremoves by fullEq(r != request), butreschedule_requestand the lane invariants are keyed byid(). If a caller passes a request value with the sameid()but different non-id fields,reschedule_requestwill assert there is exactly one matching id, butremove_requestwon't remove it (andrecord_requestwill then panic on duplicate id). Removing byid()makes the API consistent and robust for non-withdrawal lane types.
fn remove_request(&mut self, request: &R) {
self.pending_requests.retain(|r| r != request);
rs/ethereum/cketh/minter/src/state/transactions/mod.rs:599
- This doc comment says the timestamp covers requests “awaiting a transaction or a reimbursement”, but the implementation only considers pending requests and
maybe_reimburse(created-but-not-finalized). It does not include requests with a pendingreimbursement_requestsentry. Either adjust the wording to match the current behavior, or extend the calculation to include reimbursement backlog.
/// Whether any request is still in flight, either awaiting a transaction or a reimbursement.
| impl TransactionLane { | ||
| /// One outcome of [`TransactionLane::create_resubmit_transactions`]: the fee-bumped transaction to | ||
| /// re-sign (paired with its lane id), or why it could not be bumped. | ||
| type ResubmitResult<Id> = Result<(Id, Eip1559TransactionRequest), ResubmitTransactionError<Id>>; |
There was a problem hiding this comment.
🤖 The compile claim is not right, and CI on this exact commit (db25106) disproves it: Cargo Build Linux, Cargo Lint Linux and Bazel Test All are all green, and locally cargo check --all-targets --all-features -p ic-cketh-minter plus clippy with --deny warnings are clean.
E0446 is about private types — structs and enums — appearing in a public signature. A type alias is transparent: it is only a name, and the type it expands to here (Result, Eip1559TransactionRequest, ResubmitTransactionError) is entirely public, so nothing private leaks. The alias merely is not nameable from outside this module, and rustdoc renders the expanded type.
There is a smaller, real point buried in this though: the sibling alias ReimbursedResult right above is pub type, so making ResubmitResult public too would be more consistent and would let the rendered docs show the alias name. Leaving that to @gregorydemay as a judgement call rather than restacking the whole stack for a visibility keyword.
db25106 to
00d183e
Compare
00d183e to
c69e4ce
Compare
c69e4ce to
3c390a4
Compare
3c390a4 to
6a2a1c8
Compare
6a2a1c8 to
6be91c0
Compare
`TransactionPipeline` served exactly one sender: the minter's main address. One nonce, every map keyed by a ckETH `LedgerBurnIndex`, and the request type baked in. A second sender address cannot reuse any of it. Introduce `PipelineRequest` — an identity usable as the pipeline's alternate map key, a destination, a creation time, a fee-bump strategy and the EIP-1559 transaction the request turns into — and make `TransactionPipeline<R>` generic over it. `WithdrawalRequest` is the only implementation, so `WithdrawalTransactions` now wraps `TransactionPipeline<WithdrawalRequest>` and every existing call site and behaviour is unchanged. With the request type behind a trait, the pipeline's vocabulary follows: it speaks of requests and ids rather than withdrawals, since a future pipeline's requests are not withdrawals and its ids are not ledger burn indices. Reimbursement and withdrawal status keep their names — they live on `WithdrawalTransactions` and really are withdrawal concepts. Two things fall out. `create_transaction` was a five-argument free function reaching into a request to build its transaction; that is now `PipelineRequest::to_transaction`, so the function goes. And `into_accepted_withdrawal_request_event` had no production caller left, so it moves to the test file that was its only consumer — `EventType` no longer appears anywhere in `state::transactions`. Preparatory, with no second pipeline yet: nothing instantiates `TransactionPipeline` with anything but `WithdrawalRequest`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
6be91c0 to
b659e58
Compare
Part of DEFI-2917 (deposit-from-CEX), stacked on #11177. Pure refactoring: no behaviour change, and no second pipeline yet.
Why
TransactionPipelineserves exactly one sender, the minter's main address: one nonce, every map keyed by a ckETHLedgerBurnIndex, the request type baked in. A second sender address cannot reuse any of it, and giving the sweeper its own address is the point of the stack.What
Introduces
PipelineRequest— an identity usable as the pipeline's alternate map key, a destination, a creation time, a fee-bump strategy, and the EIP-1559 transaction the request turns into — and makesTransactionPipeline<R>generic over it.WithdrawalRequestis the only implementation, soWithdrawalTransactionswrapsTransactionPipeline<WithdrawalRequest>and every existing call site and behaviour is unchanged.The pipeline's vocabulary follows the type: it speaks of requests and ids rather than withdrawals, since a future pipeline's requests are not withdrawals and its ids are not ledger burn indices. Reimbursement and withdrawal status keep their names — they live on
WithdrawalTransactionsand really are withdrawal concepts.Two things fall out:
create_transactionwas a five-argument free function reaching into a request to build its transaction and is nowPipelineRequest::to_transaction; andinto_accepted_withdrawal_request_eventhad no production caller left, so it moves to the test file that was its only consumer, leavingEventTypeabsent fromstate::transactionsentirely.Stack created with GitHub Stacks CLI • Give Feedback 💬