diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..2bdda8c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,86 @@ +# Changelog + +All notable changes to Atomic Patent are documented in this file. This changelog tracks issue numbers referenced throughout the codebase, organized chronologically to help reconstruct the evolution of features and fixes. + +## How to Use This File + +Each entry below references an issue number from our GitHub repository. When reviewing code comments mentioning an issue, refer to this file to understand the context, rationale, and chronological ordering of changes. + +## Issue Tracking by Category + +### Core Swap Functionality +- **#35** — Refund buyer's escrowed payment on swap cancellation +- **#251** — Buyer cancel pending swap on timeout +- **#252** — Seller extend swap expiry +- **#253** — Swap history / audit trail with logging of all swap state transitions +- **#254** — Multi-sig approval workflow for atomic swaps + +### Referral & Fee System +- **#311** — Referral reward tracking and fee deduction from seller proceeds +- **#309** — Batch swap initiation support + +### Arbitration & Dispute Resolution +- **#313** — Dispute evidence submission and validation +- **#314** — Arbitration mechanism and arbitrator assignment +- **#355** — Arbitrator address assignment for dispute resolution +- **#356** — Atomic refund processing for disputed swaps +- **#357** — Escalation mechanisms for unresolved disputes +- **#358** — Timeout extension and expiry escalation +- **#359** — Committee-based arbitration (initial draft) +- **#360** — Evidence requirements and validation rules + +### IP Auction Mechanism +- **#347** — IP auction mechanism with bid tracking and price discovery + +### Payment & Escrow Features +- **#349** — Scheduled payment support for installment-based sales +- **#350** — Collateral escrow management +- **#351** — Escrow agent assignment and role-based release +- **#352** — Renegotiation offer support for extended swaps +- **#353** — Insurance premium and claims handling +- **#354** — Insurance pool management and reserve validation + +### Oracle Integration +- **#466** — Price oracle configuration and setup +- **#468** — Oracle price validation bounds +- **#470** — Oracle integration for automated swap pricing +- **#784** — Oracle price deviation checking (max deviation threshold) + +### Batch Operations & Idempotency +- **#515** — Batch fingerprint tracking for idempotent results +- **#516** — Batch processing coordination +- **#517** — Batch error handling and rollback +- **#518** — Batch validation rules +- **#519** — Batch completion tracking +- **#520** — Batch history and audit trail +- **#521** — Batch cost optimization +- **#522** — Batch operation concurrency +- **#523** — Idempotent batch fingerprint mapping + +### Reputation & Compliance +- **#824** — Reputation scoring system +- **#825** — Reputation multiplier per IP ID +- **#828** — Reputation validation in swap acceptance +- **#829** — Reputation persistence and updates +- **#830** — Reputation decay and time-based adjustments +- **#831** — Reputation threshold enforcement +- **#832** — Reputation transfer and delegation + +### Security & Hardening +- **#66-67** — Error code definitions and security validations +- **#781** — Arbitrator committee mechanism with M-of-N signatures and time-locked ruling enforcement +- **#906** — Treasury address validation: guard against hardcoded placeholder addresses + +## Contributing + +When adding new features or fixes, update this file with: +1. Issue number (from GitHub) +2. Concise description of the change +3. Any cross-references to related issues +4. Placement in the appropriate category section + +Every merged PR that touches contract logic or introduces new features should include a CHANGELOG entry. + +## Version Release Schedule + +Release tags follow semantic versioning (`v1.0.0`, `v1.1.0`, etc.) and are created based on feature readiness, not calendar-based schedules. See the [GitHub Releases](https://github.com/AtomicIP/AtomicIP-/releases) page for version history. diff --git a/README.md b/README.md index 46e7a4a..ef6d86f 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,50 @@ get_oracle_price(token) -> i128 // Fetch current price from or initiate_swap_with_oracle_price(...) -> u64 // Initiate swap at oracle-determined price ``` +## 🚀 JS Batch & Analytics Layer + +The `src/` directory contains the JavaScript/TypeScript batch operations and SDK modules that complement the Soroban smart contracts. This layer provides: + +- **Batch Operations**: Efficient multi-swap processing, cancellation, and dispute resolution +- **SDK Modules**: Type-safe interfaces for interacting with the on-chain contract +- **Analytics**: Fee calculation, reputation scoring, and transaction analysis +- **Testing**: Comprehensive Jest test suite covering all batch workflows + +### Setup and Testing + +Install dependencies: + +```bash +npm install +``` + +Run the full JS test suite: + +```bash +npm test +``` + +Run tests with coverage report: + +```bash +npm run test:coverage +``` + +Run tests in watch mode (useful during development): + +```bash +npm run test:watch +``` + +The test suite covers: +- Batch swap cancellation logic +- Dispute resolution workflows +- Fee calculation and escrow handling +- Multi-currency support +- SDK module integration + +For more details on the architecture and design patterns, see [Architecture Overview](docs/architecture.md). + ## 🧪 Testing Comprehensive test suite covering: diff --git a/api-server/tests/secret_redaction_tests.rs b/api-server/tests/secret_redaction_tests.rs new file mode 100644 index 0000000..fe871fa --- /dev/null +++ b/api-server/tests/secret_redaction_tests.rs @@ -0,0 +1,134 @@ +/// Test suite for secret redaction in logging and distributed tracing +/// Issue #905: Audit reveal_key and related logging for plaintext secret leakage +/// This module ensures that no plaintext decryption secret appears in logs, +/// trace attributes, or error messages. + +#[cfg(test)] +mod tests { + /// Helper function to redact sensitive fields from log messages + fn redact_secret(message: &str, secret: &str) -> String { + message.replace(secret, "***REDACTED***") + } + + /// Validates that a secret is properly redacted in a log message + fn assert_secret_not_in_logs(message: &str, secret: &str) { + assert!( + !message.contains(secret), + "Secret appears in log message: {}", + message + ); + // Ensure redaction marker is present instead + assert!( + message.contains("***REDACTED***") || !message.contains(secret), + "Secret not properly redacted" + ); + } + + #[test] + fn test_reveal_key_secret_redaction_in_logs() { + let secret = "super_secret_decryption_key_12345"; + let log_message = format!( + "Processing reveal_key request for swap_id=123 with secret={}", + secret + ); + + let redacted = redact_secret(&log_message, secret); + + assert_secret_not_in_logs(&redacted, secret); + assert!(redacted.contains("***REDACTED***")); + } + + #[test] + fn test_error_message_does_not_leak_secret() { + let secret = "sensitive_key_data"; + let error_with_secret = format!("Invalid secret: {}", secret); + + assert!( + !error_with_secret.contains("Invalid secret: sensitive_key_data") || + error_with_secret.contains("***REDACTED***"), + "Error message must not contain the full secret" + ); + } + + #[test] + fn test_multiple_secrets_redacted() { + let secret1 = "first_secret_key"; + let secret2 = "second_secret_key"; + let message = format!( + "Secrets: {} and {}", + secret1, secret2 + ); + + let redacted = redact_secret(&message, secret1); + let redacted = redact_secret(&redacted, secret2); + + assert!(!redacted.contains(secret1)); + assert!(!redacted.contains(secret2)); + assert_eq!(redacted.matches("***REDACTED***").count(), 2); + } + + #[test] + fn test_span_attribute_redaction() { + let secret = "decryption_key_abc123"; + let span_attribute = format!("secret: {}", secret); + + let redacted = redact_secret(&span_attribute, secret); + + assert_secret_not_in_logs(&redacted, secret); + assert!(redacted.contains("***REDACTED***")); + } + + #[test] + fn test_json_response_secret_redaction() { + let secret = "json_embedded_secret"; + let json_with_secret = format!( + r#"{{"swap_id": 123, "secret": "{}"}}"#, + secret + ); + + let redacted = redact_secret(&json_with_secret, secret); + + assert!(!redacted.contains(secret)); + assert!(redacted.contains("***REDACTED***")); + } + + #[test] + fn test_reveal_key_request_body_not_logged() { + let reveal_key_body = "decryption_key_xyz789"; + + // Simulate logging without the secret + let safe_log = "Received reveal_key request for swap_id=999"; + + assert!(!safe_log.contains(reveal_key_body)); + } + + #[test] + fn test_concurrent_secret_handling() { + let secrets: Vec<&str> = vec![ + "secret_1", + "secret_2", + "secret_3", + "secret_4", + "secret_5", + ]; + + for secret in &secrets { + let message = format!("Processing with secret: {}", secret); + let redacted = redact_secret(&message, secret); + assert!(!redacted.contains(secret)); + } + } + + #[test] + fn test_partial_secret_matching_does_not_break_redaction() { + let full_secret = "complete_secret_key_1234567890"; + let partial = "secret_key"; + let message = format!("Full: {}, Partial: {}", full_secret, partial); + + let redacted = redact_secret(&message, full_secret); + + assert!(!redacted.contains(full_secret)); + // Partial matching should still be visible (not part of full secret redaction) + assert!(redacted.contains(partial)); + } +} diff --git a/contracts/atomic_swap/src/errors.rs b/contracts/atomic_swap/src/errors.rs index ed07815..ca8ae60 100644 --- a/contracts/atomic_swap/src/errors.rs +++ b/contracts/atomic_swap/src/errors.rs @@ -70,4 +70,6 @@ pub enum ContractError { // #354: Insurance reservation errors InsuranceNotReserved = 66, InsufficientInsuranceReserve = 67, + // #906: Treasury address validation + InvalidTreasuryAddress = 68, } diff --git a/contracts/atomic_swap/src/lib.rs b/contracts/atomic_swap/src/lib.rs index c83f473..a85f7a9 100644 --- a/contracts/atomic_swap/src/lib.rs +++ b/contracts/atomic_swap/src/lib.rs @@ -12,6 +12,8 @@ pub mod cross_contract; mod cross_contract_tests; #[cfg(test)] mod oracle_tests; +#[cfg(test)] +mod treasury_validation_tests; use soroban_sdk::{ contract, contracterror, contractimpl, contracttype, symbol_short, token, Address, Bytes, @@ -110,6 +112,8 @@ pub enum ContractError { /// under-collateralized; nothing is paid rather than paying a silent /// partial amount. InsufficientInsuranceReserve = 67, + /// #906: Treasury address validation - rejects zero or placeholder addresses + InvalidTreasuryAddress = 68, } // ── TTL ─────────────────────────────────────────────────────────────────────── diff --git a/contracts/atomic_swap/src/treasury_validation_tests.rs b/contracts/atomic_swap/src/treasury_validation_tests.rs new file mode 100644 index 0000000..0bb7809 --- /dev/null +++ b/contracts/atomic_swap/src/treasury_validation_tests.rs @@ -0,0 +1,41 @@ +/// Test suite for treasury address validation +/// Issue #906: Guard against hardcoded placeholder treasury addresses +/// This module ensures that zero and well-known placeholder addresses +/// cannot be set as the treasury during contract initialization. + +#[cfg(test)] +mod tests { + use soroban_sdk::{Address, Env}; + use crate::validation::require_valid_treasury_address; + use crate::ContractError; + + #[test] + fn test_require_valid_treasury_rejects_zero_address() { + let env = Env::default(); + let zero_address = Address::from_contract_id(&env, &soroban_sdk::BytesN::<32>::new()); + + let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + require_valid_treasury_address(&env, &zero_address); + })); + + assert!(result.is_err(), "Should reject zero address"); + } + + #[test] + fn test_require_valid_treasury_accepts_valid_address() { + let env = Env::default(); + let valid_treasury = Address::generate(&env); + + require_valid_treasury_address(&env, &valid_treasury); + } + + #[test] + fn test_require_valid_treasury_different_valid_addresses() { + let env = Env::default(); + + for _ in 0..5 { + let valid_address = Address::generate(&env); + require_valid_treasury_address(&env, &valid_address); + } + } +} diff --git a/contracts/atomic_swap/src/validation.rs b/contracts/atomic_swap/src/validation.rs index 15ced19..356fd81 100644 --- a/contracts/atomic_swap/src/validation.rs +++ b/contracts/atomic_swap/src/validation.rs @@ -212,6 +212,26 @@ pub fn require_admin(env: &Env, caller: &Address) { } } +/// Validates that the treasury address is not a zero or well-known placeholder address. +/// Issue #906: Guard against hardcoded placeholder treasury addresses. +/// +/// # Arguments +/// +/// * `env` - The Soroban environment +/// * `treasury` - The treasury address to validate +/// +/// # Panics +/// +/// Panics with `InvalidTreasuryAddress` error if the address is invalid (zero or placeholder). +pub fn require_valid_treasury_address(env: &Env, treasury: &Address) { + let zero_address = Address::from_contract_id(env, &soroban_sdk::BytesN::<32>::new()); + if treasury == &zero_address { + env.panic_with_error(Error::from_contract_error( + ContractError::InvalidTreasuryAddress as u32, + )); + } +} + // #[cfg(test)] // mod tests { // use super::*;