feat: Implement payroll execution idempotency and replay protection - #232
Open
OG-wura wants to merge 7 commits into
Open
feat: Implement payroll execution idempotency and replay protection#232OG-wura wants to merge 7 commits into
OG-wura wants to merge 7 commits into
Conversation
|
@OG-wura Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
Resolved merge conflicts in contracts/payroll/src/lib.rs: - Kept origin/main's metadata hash verification tests (zkpayroll#177) - Added HEAD's idempotent retry & replay protection tests after them - Both test suites are preserved in the resolved file
- Resolved merge conflicts in contracts/payroll/src/lib.rs - Fixed no_std format! usage in payment_executor and payroll_registry - Added missing ToXdr imports for to_xdr() calls - Fixed sha256() return type (Hash<32> → BytesN<32> via .into()) - Fixed fingerprint clone-after-move in payment_executor - Added registry field to ContractAddresses in libmain.rs - Removed .unwrap() from execute_batch_payroll test calls (auto-unwrapped) - Added Events testutils import for env.events().all()
Resolve conflicts in payment_executor, payroll, and payroll_registry: - payment_executor: keep treasury asset mapping validation (issue zkpayroll#217) alongside batch execution idempotency; preserve both test suites. - payroll: combine idempotency/replay protection tests with treasury deposit, archived run, and company state gate tests; adapt nonce tests to the new idempotent retry semantics. - payroll_registry: adopt add_employee_by_wallet refactor; drop duplicate add_employee_record body. Follow-up fixes so the merged tree builds and tests pass: - Update payment_executor and integration_tests event-count assertions for the TreasuryAssetAllowedUpdated and run_state events. - Fix idempotency-related payroll tests for the new safe-retry behavior. - Remove invalid AuditModuleClient::initialize call in migration helpers. - Fix clippy clone-on-copy in payroll; dedupe BatchExecuted docs row.
…ecks - cargo fmt --all: reformat workspace drift that failed the Rustfmt and Format & Clippy checks (rustfmt now requires the changes CI expects). - clippy: remove unused imports/variables in migration test crates, allow documented-but-unused fixture constants, drop redundant Events import and unused treasury_owner bindings in payroll tests, fix assert_eq! bool literals and module_inception in integration_tests fixtures. - migration_tests: set a nonzero ledger timestamp in setup so fixtures write valid executed_at/requested_at timestamps; fix mg_03 company ID expectation to match 0-based register_company; register both companies in mg_14/mg_15 so the post-upgrade company lookups succeed; keep registry commitment in sync after the simulated rotation in mg_02; drop full-state migration helper calls from the minimal-state mg_14/mg_15 setups. All workspace tests pass: payroll 110, payroll_registry 41, audit_module 38, payment_executor 37, migration_tests 21, integration_tests 27, etc.
Contributor
|
@OG-wura, kindly resolve conflicts. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Close #165
Implement Payroll Execution Idempotency & Replay Protection
Summary
Adds contract-level protections that prevent duplicate payroll execution for the same company, payroll period, commitment batch, and treasury authorization context. Payroll execution is now safe under retries, delayed confirmations, client crashes, and malicious replay attempts.
Changes
contracts/payment_executor/src/lib.rsDataKey::BatchExecution(BytesN<32>)— tracks whether a specific batch fingerprint has been executedDataKey::BatchExecutionRecords(BytesN<32>)— cachesVec<PaymentRecord>for idempotent returnscompute_batch_fingerprint()— SHA-256 hash of (company_id, period, employees, amounts, nullifiers) usingsoroban_sdk::Bytes+env.crypto().sha256()execute_batch_payroll()— computes fingerprint upfront; if batch already executed, returns cached records without re-execution (safe idempotent retry); emitsBatchExecutedevent on first executiontest_execute_batch_idempotent_retry,test_different_batches_have_distinct_identitiescontracts/payroll/src/lib.rsDataKey::NonceFingerprint(BytesN<32>)— stores SHA-256 batch fingerprint alongside consumed noncecompute_batch_fingerprint()— SHA-256 hash of (total_spend, employees, amounts, proof prefixes)batch_process_payroll()— idempotent nonce check:run_id(safe retry)"Conflicting replay detected: nonce already used with a different batch payload"(malicious replay)prepare_payroll_run()— same idempotent/replay logic applied to the prepare pathtest_idempotent_retry_same_batch_returns_same_run_id— retry returns same run_idtest_conflicting_replay_with_modified_payload_is_rejected— different payload rejectedtest_prepare_payroll_run_idempotent_retry— prepare path idempotenttest_prepare_payroll_run_rejects_conflicting_replay— prepare path replay rejectedtest_distinct_nonces_with_same_payload_are_distinct_runs— different nonces = different runstest_cross_company_same_nonce_does_not_collide— same nonce on different contract instances is independentcontracts/payroll/src/libmain.rsNonceFingerprint(BytesN<32>)DataKey variant for consistencydocs/security/replay-protection.md(new)Comprehensive documentation covering:
docs/events.mdBatchExecutedevent to schema referenceExecution Identity Design
The canonical execution identity is a SHA-256 fingerprint computed from:
Two batches with identical parameters always produce the same fingerprint; any change produces a different one.
Acceptance Criteria
Testing