add-nonce-based-replay-protection-for-operator-actions
Implement nonce-based replay protection for operator-authorized operations to prevent replay attacks and ensure that operator actions can only be executed once.
OperatorNonce(Address) // Tracks nonce per operator in instance storageInvalidNonce = 901, // Nonce is too high (future nonce)
StaleNonce = 902, // Nonce is too low (already used/replay attempt)-
get_operator_nonce(env: Env, operator: Address) -> u64- Public function to query the current nonce for an operator
- Returns 0 for operators that haven't performed any actions yet
-
validate_and_increment_nonce(env: &Env, operator: &Address, provided_nonce: u64) -> Result<(), Error>- Internal function that validates the provided nonce matches the expected value
- Increments the nonce atomically after successful validation
- Publishes a "nonce_inc" event for monitoring
- Returns
StaleNonceerror if nonce is too low (replay attempt) - Returns
InvalidNonceerror if nonce is too high (skipped ahead)
heartbeat(env: Env, operator: Address, nonce: u64) -> Result<(), Error>- BREAKING CHANGE: Added
nonce: u64parameter - Now validates nonce before updating heartbeat timestamp
- Ensures replay protection for all operator heartbeat operations
- BREAKING CHANGE: Added
Added 13 comprehensive tests:
- test_operator_nonce_starts_at_zero - Initial state verification
- test_heartbeat_with_valid_nonce_succeeds - Normal operation flow
- test_heartbeat_with_stale_nonce_fails - Replay attack prevention
- test_heartbeat_with_future_nonce_fails - Invalid nonce rejection
- test_heartbeat_replay_attack_prevented - Full replay scenario
- test_nonce_is_per_operator - Per-operator isolation
- test_nonce_increments_monotonically - Sequential increment verification
- test_nonce_skipping_not_allowed - Gap prevention
- test_nonce_persists_across_operator_deactivation - Persistence testing
- test_duplicate_nonce_rejected - Duplicate detection
- test_nonce_validation_before_heartbeat_update - Validation order
- test_non_operator_cannot_use_nonce - Authorization check
- test_concurrent_operators_independent_nonces - Multi-operator scenarios
- Added new error codes: InvalidNonce (901), StaleNonce (902)
- Added missing error codes: NotOperator (205), AddressDenied (309), RescueForbidden (310), NoFeesToWithdraw (402)
- Organized error codes by category (900 series for Replay Protection)
Comprehensive documentation including:
- Overview of the implementation
- Detailed explanation of all changes
- Security properties and guarantees
- Usage examples
- Migration guide for clients
- Error handling instructions
- Monitoring and observability recommendations
Automated verification script that checks:
- Storage key presence
- Error code definitions
- Function implementations
- Test coverage
- Documentation completeness
✅ Require monotonically increasing nonce for operator actions
- Implemented in
validate_and_increment_noncefunction - Nonces must increment by exactly 1 for each operation
- No gaps or skips allowed
✅ Persist and validate nonce per operator
- Nonces stored in instance storage with
DataKey::OperatorNonce(Address) - Each operator has independent nonce counters
- Nonces persist across operator deactivation/reactivation
- Validation occurs before any state changes
✅ Reject stale or duplicate nonces
- Stale nonces (already used) rejected with
Error::StaleNonce - Future nonces (skipped) rejected with
Error::InvalidNonce - Duplicate nonces are a subset of stale nonces and are rejected
✅ Add tests covering replay attempts
- 13 comprehensive tests added
- Tests cover replay attacks, stale nonces, future nonces, and edge cases
- Tests verify per-operator isolation and concurrent operations
- Tests ensure validation happens before state changes
- Nonces must increase monotonically (by exactly 1)
- Only the current expected nonce is accepted
- Stale nonces are immediately rejected
- Future nonces are rejected to prevent gaps
- Each operator has independent nonce counter
- Multiple operators can operate concurrently
- Nonces persist across operator status changes
- Nonce validation occurs before state changes
- Failed validation does not update heartbeat
- Nonce increments are atomic with operations
- Events published for auditability
Before:
pub fn heartbeat(env: Env, operator: Address) -> Result<(), Error>After:
pub fn heartbeat(env: Env, operator: Address, nonce: u64) -> Result<(), Error>All clients calling heartbeat() must:
- Track the current nonce for each operator (starts at 0)
- Increment the nonce after each successful call
- Handle
StaleNonceandInvalidNonceerrors - Implement nonce recovery logic for failures
Run the verification script:
./verify_nonce_implementation.shRun the test suite:
cd stellar-contracts
cargo test --lib nonceRun all tests:
cd stellar-contracts
cargo test --lib- Review all code changes
- Run full test suite and verify all tests pass
- Deploy to test environment
- Verify nonce behavior in test environment
- Update client applications with new signature
- Test client applications in test environment
- Monitor for nonce-related errors
- Deploy to production
- Monitor production metrics
nonce_incevents - Track nonce increments per operatorheartbeatevents - Track operator activity
- Nonce Errors: Count of
StaleNonceandInvalidNonceerrors - Replay Attempts: Monitor
StaleNonceerrors as potential attacks - Operator Activity: Track heartbeat frequency per operator
- Nonce Gaps: Detect if nonces are being skipped (should never happen)
- High rate of
StaleNonceerrors (potential replay attack) - Any
InvalidNonceerrors (indicates client logic error) - Operator nonce not incrementing (indicates stuck client)
- stellar-contracts/src/lib.rs - Core implementation
- stellar-contracts/src/test.rs - Test suite
- ERROR_CODES.md - Error code documentation
- NONCE_REPLAY_PROTECTION.md - Implementation documentation
- verify_nonce_implementation.sh - Verification script
Branch: add-nonce-based-replay-protection-for-operator-actions
Commit Message:
Add nonce-based replay protection for operator actions
Implements monotonically increasing nonce validation for operator-authorized
operations to prevent replay attacks.
- Code Review: Have the implementation reviewed by team members
- Testing: Run full test suite to ensure all tests pass
- Client Updates: Update all operator clients to use new signature
- Documentation: Share NONCE_REPLAY_PROTECTION.md with operators
- Deployment: Deploy to test environment first, then production
- Monitoring: Set up alerts and dashboards for nonce metrics
- Issue: Add nonce-based replay protection for operator actions
- Documentation: NONCE_REPLAY_PROTECTION.md
- Error Codes: ERROR_CODES.md
- Tests: stellar-contracts/src/test.rs (lines 1383-1680)