-
Notifications
You must be signed in to change notification settings - Fork 0
test: add integration tests for transaction resubmission #81
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?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,6 +31,12 @@ const FINALIZE_TRANSACTIONS_DELAY: Duration = Duration::from_mins(2); | |
| const RESUBMIT_TRANSACTIONS_DELAY: Duration = Duration::from_mins(3); | ||
| const DEPOSIT_CONSOLIDATION_DELAY: Duration = Duration::from_mins(10); | ||
|
|
||
| /// Must match `MAX_BLOCKHASH_AGE` in the minter's monitor module. | ||
| const MAX_BLOCKHASH_AGE: Slot = 150; | ||
| /// The SOL RPC canister rounds the slot returned by getSlot down to the nearest multiple | ||
| /// of this value before querying getBlock and returning the slot to callers. | ||
| const SOL_RPC_SLOT_ROUNDING: u64 = 20; | ||
|
|
||
| /// Deposits funds into the minter via `process_deposit`, consolidates them, | ||
| /// and finalizes the consolidation so the minter's internal balance is credited. | ||
| /// | ||
|
|
@@ -262,11 +268,6 @@ mod withdrawal_tests { | |
|
|
||
| use super::*; | ||
|
|
||
| const MAX_BLOCKHASH_AGE: Slot = 150; | ||
| /// The SOL RPC canister rounds the slot returned by getSlot down to the nearest multiple | ||
| /// of this value before querying getBlock and returning the slot to callers. | ||
| const SOL_RPC_SLOT_ROUNDING: u64 = 20; | ||
|
|
||
| #[tokio::test] | ||
| async fn should_validate_solana_address() { | ||
| let setup = SetupBuilder::new().build().await; | ||
|
|
@@ -1065,6 +1066,8 @@ mod anonymous_caller_tests { | |
| mod consolidation_tests { | ||
| use super::*; | ||
|
|
||
| const INITIAL_SLOT: Slot = 100_000_000; | ||
|
|
||
| #[tokio::test] | ||
| async fn should_consolidate_deposits_after_timer() { | ||
| let setup = SetupBuilder::new().with_proxy_canister().build().await; | ||
|
|
@@ -1096,16 +1099,91 @@ mod consolidation_tests { | |
| setup.drop().await; | ||
| } | ||
|
|
||
| #[tokio::test] | ||
| async fn should_resubmit_expired_consolidation_transaction() { | ||
| let setup = SetupBuilder::new().with_proxy_canister().build().await; | ||
|
|
||
|
lpahlavi marked this conversation as resolved.
|
||
| let result = setup | ||
| .minter() | ||
| .with_http_mocks(MockBuilder::new().get_deposit_transaction().build()) | ||
| .process_deposit(default_process_deposit_args()) | ||
| .await; | ||
| let mint_block_index = | ||
| assert_matches!(result, Ok(DepositStatus::Minted { block_index, .. }) => block_index); | ||
|
|
||
| // Advance time past the consolidation delay to trigger the timer | ||
| setup.advance_time(DEPOSIT_CONSOLIDATION_DELAY).await; | ||
| setup | ||
| .execute_http_mocks(http_mocks_for_deposit_consolidation()) | ||
| .await; | ||
|
|
||
| setup.minter().assert_that_events().await.satisfy(|events| { | ||
| check!(events.iter().any(|e| matches!( | ||
| e, | ||
| EventType::SubmittedTransaction { | ||
| purpose: TransactionPurpose::ConsolidateDeposits { mint_indices }, | ||
| .. | ||
| } if mint_indices == &[mint_block_index] | ||
| ))); | ||
| }); | ||
|
|
||
| // Advance time to trigger finalize_transactions. The mocked slot exceeds | ||
| // INITIAL_SLOT + MAX_BLOCKHASH_AGE + SOL_RPC_SLOT_ROUNDING, so the | ||
| // consolidation transaction is considered expired. | ||
| let resubmission_slot = INITIAL_SLOT + MAX_BLOCKHASH_AGE + SOL_RPC_SLOT_ROUNDING + 1; | ||
| setup.advance_time(FINALIZE_TRANSACTIONS_DELAY).await; | ||
| setup | ||
| .execute_http_mocks(mark_expired_consolidation_http_mocks(resubmission_slot)) | ||
| .await; | ||
|
|
||
| // Advance time to trigger resubmit_transactions | ||
| setup.advance_time(RESUBMIT_TRANSACTIONS_DELAY).await; | ||
| setup | ||
| .execute_http_mocks(resubmit_consolidation_http_mocks(resubmission_slot)) | ||
| .await; | ||
|
|
||
| setup.minter().assert_that_events().await.satisfy(|events| { | ||
| check!(events.iter().any(|e| matches!( | ||
| e, | ||
| // new_slot must be past the original blockhash expiry threshold, | ||
| // confirming the resubmitted transaction uses a fresh blockhash. | ||
| EventType::ResubmittedTransaction { new_slot, .. } | ||
| if *new_slot >= INITIAL_SLOT + MAX_BLOCKHASH_AGE | ||
| ))); | ||
|
Comment on lines
+1146
to
+1152
|
||
| }); | ||
|
|
||
| setup.drop().await; | ||
| } | ||
|
|
||
| // Returns the required HTTP outcall mocks for executing the deposit consolidation task | ||
| fn http_mocks_for_deposit_consolidation() -> MockHttpOutcalls { | ||
| MockBuilder::with_start_id(4) | ||
| .submit_transaction( | ||
| 100_000_000, | ||
| INITIAL_SLOT, | ||
| "4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZAMdL4VZHirAn", | ||
| "5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW", | ||
| ) | ||
| .build() | ||
|
lpahlavi marked this conversation as resolved.
|
||
| } | ||
|
|
||
| /// HTTP mocks for finalize_transactions detecting an expired consolidation transaction. | ||
| fn mark_expired_consolidation_http_mocks(current_slot: Slot) -> MockHttpOutcalls { | ||
| MockBuilder::with_start_id(16) | ||
| .get_current_slot(current_slot, "9ZNTfG4NyQgxy2SWjSiQoUyBPEvXT2xo7fKc5hPYYJ7b") | ||
| .check_signature_statuses_not_found(1) | ||
| .build() | ||
| } | ||
|
|
||
| /// HTTP mocks for resubmit_transactions sending the replacement consolidation transaction. | ||
| fn resubmit_consolidation_http_mocks(current_slot: Slot) -> MockHttpOutcalls { | ||
| MockBuilder::with_start_id(28) | ||
| .submit_transaction( | ||
| current_slot, | ||
| "9ZNTfG4NyQgxy2SWjSiQoUyBPEvXT2xo7fKc5hPYYJ7b", | ||
| "2gQDVht4vqs8FeKnbGCtXjCXjbTwRnKJNzuYfDFXhkWBn5MhZKXaKMDJSzaq4G7FnNmah7SWj4TX2mB3bo7NQGnm", | ||
| ) | ||
| .build() | ||
| } | ||
| } | ||
|
|
||
| mod metrics_tests { | ||
|
|
||
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.
MAX_BLOCKHASH_AGEandSOL_RPC_SLOT_ROUNDINGare duplicated test constants that must stay in sync with production code and the HTTP mock behavior (e.g.,integration_tests/src/fixtures.rshard-codes the/ 20 * 20rounding inget_block_request). To avoid drift, consider defining these values in one place (e.g., export a constant from the SOL-RPC fixture/helpers for the rounding, and/or exposeMAX_BLOCKHASH_AGEfrom the minter monitor module for tests) and referencing that here instead of re-stating the numbers.