⚡ Bolt: Implement Blockchain Integrity for Resolution Proofs#618
⚡ Bolt: Implement Blockchain Integrity for Resolution Proofs#618RohanExploit wants to merge 4 commits into
Conversation
Implement cryptographic hash chaining for `ResolutionEvidence` to create a tamper-proof audit trail for issue resolutions.
Key Improvements:
- 🔗 **Cryptographic Chaining**: Linked evidence records using SHA-256 hashes of current data and previous records.
- ⚡ **O(1) Chaining**: Added `resolution_last_hash_cache` in `backend/cache.py` to provide the latest hash in O(1), eliminating database lookups during evidence submission.
- ⚡ **O(1) Verification**: Optimized `verify_evidence` to fetch only the latest record using projected columns and `order_by().first()`.
- ✅ **Blockchain Verification**: Added a dedicated `/api/resolution-proof/{evidence_id}/blockchain-verify` endpoint for single-record integrity checks.
- 🛠️ **Robustness**: Fixed column name confusion in `ResolutionProofToken` and implemented deterministic timestamp hashing using fixed string formats.
- 📉 **Efficiency**: Added database indexes for `grievance_id` and integrity hashes to speed up common queries.
This completes the end-to-end blockchain integrity lifecycle for reported issues.
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
❌ Deploy Preview for fixmybharat failed. Why did it fail? →
|
🙏 Thank you for your contribution, @RohanExploit!PR Details:
Quality Checklist:
Review Process:
Note: The maintainers will monitor code quality and ensure the overall project flow isn't broken. |
📝 WalkthroughWalkthroughAdds integrity-chain hashing and verification for resolution evidence: new cache instance, DB model fields and migration branches, service logic to compute/verify chained hashes and store integrity fields, a new verification API endpoint, schema updates, and accompanying tests and build file tweaks. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Service as ResolutionProofService
participant Cache as resolution_last_hash_cache
participant DB as Database
Client->>Service: submit_evidence(token, evidence_data, gps, capture_ts)
Service->>Service: Build deterministic payload (token_id + evidence_hash + gps + ts)
Service->>Service: Compute integrity_hash (HMAC/hash)
Service->>Cache: get(last_hash)
alt Cache Hit
Cache-->>Service: prev_hash
else Cache Miss
Service->>DB: query latest ResolutionEvidence for grievance
DB-->>Service: prev_hash (or "")
Service->>Cache: set(prev_hash)
end
Service->>DB: insert ResolutionEvidence(integrity_hash, previous_integrity_hash, metadata_bundle)
DB-->>Service: confirm
Service->>Cache: set(integrity_hash)
Service-->>Client: success (evidence saved)
sequenceDiagram
participant Client
participant Service as ResolutionProofService
participant DB as Database
Client->>Service: GET /{evidence_id}/blockchain-verify
Service->>DB: fetch ResolutionEvidence by id
DB-->>Service: evidence (metadata_bundle, integrity fields)
Service->>Service: verify server signature on metadata_bundle
alt Signature invalid
Service-->>Client: is_valid=false, message="signature invalid"
else Signature valid
Service->>Service: recompute integrity_hash using previous_integrity_hash
alt recomputed == stored
Service-->>Client: is_valid=true, message="integrity verified"
else
Service-->>Client: is_valid=false, message="Integrity check failed"
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
1 issue found across 7 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="backend/resolution_proof_service.py">
<violation number="1" location="backend/resolution_proof_service.py:370">
P1: The blockchain chaining logic has a race condition under concurrent submissions. Two simultaneous `submit_evidence` calls can both read the same `prev_hash` from the cache, compute independent `integrity_hash` values, and commit — creating a fork where two evidence records share the same `previous_integrity_hash`. This breaks the append-only chain invariant that the feature is designed to guarantee.
Consider serializing chain appends with a database-level lock (e.g., `SELECT ... FOR UPDATE` on a chain-tip row, or a DB advisory lock) so that only one submission at a time can read and extend the chain.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| # 5. Create server-side signed metadata bundle | ||
| # 5. Blockchain chaining logic | ||
| # Performance Boost: Use thread-safe cache to eliminate DB query for last hash | ||
| prev_hash = resolution_last_hash_cache.get("last_hash") |
There was a problem hiding this comment.
P1: The blockchain chaining logic has a race condition under concurrent submissions. Two simultaneous submit_evidence calls can both read the same prev_hash from the cache, compute independent integrity_hash values, and commit — creating a fork where two evidence records share the same previous_integrity_hash. This breaks the append-only chain invariant that the feature is designed to guarantee.
Consider serializing chain appends with a database-level lock (e.g., SELECT ... FOR UPDATE on a chain-tip row, or a DB advisory lock) so that only one submission at a time can read and extend the chain.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/resolution_proof_service.py, line 370:
<comment>The blockchain chaining logic has a race condition under concurrent submissions. Two simultaneous `submit_evidence` calls can both read the same `prev_hash` from the cache, compute independent `integrity_hash` values, and commit — creating a fork where two evidence records share the same `previous_integrity_hash`. This breaks the append-only chain invariant that the feature is designed to guarantee.
Consider serializing chain appends with a database-level lock (e.g., `SELECT ... FOR UPDATE` on a chain-tip row, or a DB advisory lock) so that only one submission at a time can read and extend the chain.</comment>
<file context>
@@ -352,23 +365,40 @@ def submit_evidence(
- # 5. Create server-side signed metadata bundle
+ # 5. Blockchain chaining logic
+ # Performance Boost: Use thread-safe cache to eliminate DB query for last hash
+ prev_hash = resolution_last_hash_cache.get("last_hash")
+ if prev_hash is None:
+ # Cache miss: Fetch only the last hash from DB
</file context>
There was a problem hiding this comment.
🧹 Nitpick comments (3)
backend/models.py (1)
313-316: Consider documenting the relationship betweenvalid_untilandexpires_at.Both
valid_until(line 316) andexpires_at(line 312) appear to represent token expiry. The service sets both to the same value. Consider adding a brief comment clarifying their roles, or consolidating if they're truly redundant.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/models.py` around lines 313 - 316, The fields expires_at and valid_until both represent token expiry and are confusing; annotate the model to clarify their relationship (or consolidate if redundant) by adding a brief comment above the Column definitions for expires_at and valid_until in the model (e.g., near the expires_at, valid_from, valid_until columns) that explains whether expires_at is the canonical token expiry and valid_until is a mirror for compatibility/audit, or remove one and update usages accordingly; update any service code that sets both to either only set the canonical field or keep both but document why both are required.backend/routers/resolution_proof.py (1)
148-165: Use exception chaining for better debugging context.The raised
HTTPExceptions should preserve the original exception context usingfrom eorfrom Noneto improve debugging and comply with best practices.♻️ Proposed fix for exception chaining
except ValueError as e: - raise HTTPException(status_code=404, detail=str(e)) + raise HTTPException(status_code=404, detail=str(e)) from None except Exception as e: logger.error(f"Error verifying evidence blockchain for {evidence_id}: {e}", exc_info=True) - raise HTTPException(status_code=500, detail="Failed to verify resolution integrity") + raise HTTPException(status_code=500, detail="Failed to verify resolution integrity") from e🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/routers/resolution_proof.py` around lines 148 - 165, The HTTPException raises in verify_evidence_blockchain should preserve exception chaining; update both raise statements to include the original exception (e.g., "raise HTTPException(status_code=404, detail=str(e)) from e" for the ValueError handler and "raise HTTPException(status_code=500, detail='Failed to verify resolution integrity') from e" in the generic Exception handler) so the original traceback/context from ResolutionProofService.verify_evidence_integrity and logger.error is retained for debugging.backend/resolution_proof_service.py (1)
368-381: Single global cache may cause chain discontinuity under concurrent multi-process deployments.The
resolution_last_hash_cacheis a process-local in-memory cache. In multi-process deployments (e.g., multiple Gunicorn workers), each process maintains its own cache. This could lead to chain integrity issues:
- Process A submits evidence with hash H1, updates its local cache
- Process B submits evidence, its cache is empty, queries DB (gets H1), creates H2
- Both processes now have different cache states
While the DB fallback ensures correctness for the
previous_integrity_hashvalue, the cache optimization provides inconsistent benefits across workers. This is acceptable for the stated performance goal but worth documenting.📝 Consider adding a comment about multi-process behavior
# 5. Blockchain chaining logic # Performance Boost: Use thread-safe cache to eliminate DB query for last hash + # Note: In multi-process deployments, each process maintains its own cache. + # The DB fallback ensures correctness; the cache provides per-process optimization. prev_hash = resolution_last_hash_cache.get("last_hash")🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/resolution_proof_service.py` around lines 368 - 381, The in-memory resolution_last_hash_cache is process-local and can produce different cached values across workers; update the code around resolution_last_hash_cache / prev_hash /ResolutionEvidence to add a concise comment stating that the cache is process-local, the DB fallback (querying ResolutionEvidence.integrity_hash) is the authoritative source ensuring correctness, and that the current optimization may be inconsistent across multi-process deployments; additionally, note remediation options (use a shared cache like Redis or compute prev hash atomically via DB/transaction) so future maintainers know how to obtain strong cross-process chaining guarantees if required.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@backend/models.py`:
- Around line 313-316: The fields expires_at and valid_until both represent
token expiry and are confusing; annotate the model to clarify their relationship
(or consolidate if redundant) by adding a brief comment above the Column
definitions for expires_at and valid_until in the model (e.g., near the
expires_at, valid_from, valid_until columns) that explains whether expires_at is
the canonical token expiry and valid_until is a mirror for compatibility/audit,
or remove one and update usages accordingly; update any service code that sets
both to either only set the canonical field or keep both but document why both
are required.
In `@backend/resolution_proof_service.py`:
- Around line 368-381: The in-memory resolution_last_hash_cache is process-local
and can produce different cached values across workers; update the code around
resolution_last_hash_cache / prev_hash /ResolutionEvidence to add a concise
comment stating that the cache is process-local, the DB fallback (querying
ResolutionEvidence.integrity_hash) is the authoritative source ensuring
correctness, and that the current optimization may be inconsistent across
multi-process deployments; additionally, note remediation options (use a shared
cache like Redis or compute prev hash atomically via DB/transaction) so future
maintainers know how to obtain strong cross-process chaining guarantees if
required.
In `@backend/routers/resolution_proof.py`:
- Around line 148-165: The HTTPException raises in verify_evidence_blockchain
should preserve exception chaining; update both raise statements to include the
original exception (e.g., "raise HTTPException(status_code=404, detail=str(e))
from e" for the ValueError handler and "raise HTTPException(status_code=500,
detail='Failed to verify resolution integrity') from e" in the generic Exception
handler) so the original traceback/context from
ResolutionProofService.verify_evidence_integrity and logger.error is retained
for debugging.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 6526f961-3d7f-43bb-abdf-ef6d347226eb
📒 Files selected for processing (7)
backend/cache.pybackend/init_db.pybackend/models.pybackend/resolution_proof_service.pybackend/routers/resolution_proof.pybackend/schemas.pytests/test_resolution_proof.py
Implement cryptographic hash chaining for `ResolutionEvidence` to create a tamper-proof audit trail for issue resolutions.
Key Improvements:
- 🔗 **Cryptographic Chaining**: Linked evidence records using SHA-256 hashes of current data and previous records.
- ⚡ **O(1) Chaining**: Added `resolution_last_hash_cache` in `backend/cache.py` to provide the latest hash in O(1), eliminating database lookups during evidence submission.
- ⚡ **O(1) Verification**: Optimized `verify_evidence` to fetch only the latest record using projected columns and `order_by().first()`.
- ✅ **Blockchain Verification**: Added a dedicated `/api/resolution-proof/{evidence_id}/blockchain-verify` endpoint for single-record integrity checks.
- 🛠️ **Robustness**: Fixed column name confusion in `ResolutionProofToken` and implemented deterministic timestamp hashing using fixed string formats.
- 📉 **Efficiency**: Added database indexes for `grievance_id` and integrity hashes to speed up common queries.
CI Fixes:
- Fixed Netlify deployment issues by correctly scoping build configuration to the `frontend/` directory and ensuring physical `_redirects` and `_headers` files are preserved in `frontend/public/`.
- Updated `frontend/netlify.toml` with a clean build command and publish directory.
This completes the end-to-end blockchain integrity lifecycle for reported issues and ensures stable deployment.
There was a problem hiding this comment.
Pull request overview
This PR extends the existing Resolution Proof system with a blockchain-style integrity chain for ResolutionEvidence, adding per-record integrity_hash / previous_integrity_hash, caching the chain tip for performance, and exposing a new API endpoint to verify a single evidence record’s chained integrity.
Changes:
- Add integrity-chain fields + indexes to the DB model and migration logic.
- Implement chained-hash creation during evidence submission and add O(1) single-record integrity verification.
- Expand the test suite to cover chaining and tamper detection; update signing tests for deterministic JSON ordering.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
backend/resolution_proof_service.py |
Adds chain-tip caching, computes chained integrity_hash on submission, and adds verify_evidence_integrity() plus a more efficient verify_evidence() query strategy. |
backend/models.py |
Adds integrity-chain columns to ResolutionEvidence and new indexes for performance. |
backend/init_db.py |
Adds migration steps for new columns and indexes on existing databases. |
backend/cache.py |
Adds resolution_last_hash_cache used to cache the chain tip. |
backend/schemas.py |
Extends EvidenceResponse with integrity-chain fields and introduces ResolutionBlockchainVerificationResponse. |
backend/routers/resolution_proof.py |
Adds a new /{evidence_id}/blockchain-verify endpoint using the new schema/service method. |
tests/test_resolution_proof.py |
Adds chaining/tamper tests and makes signing tests deterministic with sort_keys=True. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # Fix: Query against 'token' column as it's the primary/unique identifier in some versions | ||
| token = db.query(ResolutionProofToken).filter( | ||
| ResolutionProofToken.token_id == token_id | ||
| ResolutionProofToken.token == token_id | ||
| ).first() |
There was a problem hiding this comment.
validate_token() now queries only ResolutionProofToken.token == token_id, but the API and existing data model also uses token_id (and the router returns token.token_id). If older rows have token NULL (or clients submit token_id while token differs), token validation will incorrectly fail. Consider querying with an OR on both columns (and/or backfilling token in the migration) to preserve compatibility.
| # 5. Blockchain chaining logic | ||
| # Performance Boost: Use thread-safe cache to eliminate DB query for last hash | ||
| prev_hash = resolution_last_hash_cache.get("last_hash") | ||
| if prev_hash is None: | ||
| # Cache miss: Fetch only the last hash from DB |
There was a problem hiding this comment.
The chain-tip cache approach here can break linear chaining under concurrency and in multi-worker/multi-instance deployments: two submissions can read the same cached/DB tip and both commit with the same previous_integrity_hash (fork), and separate processes will have divergent in-memory caches (stale prev_hash). To keep the chain append-only and deterministic, the previous hash needs to be read/updated atomically at the DB level (e.g., transaction + row/advisory lock or a dedicated single-row “chain tip” table with optimistic locking); use cache only as an optimization after verifying the DB tip hasn’t changed.
| @router.get("/{evidence_id}/blockchain-verify", response_model=ResolutionBlockchainVerificationResponse) | ||
| def verify_evidence_blockchain( | ||
| evidence_id: int, | ||
| db: Session = Depends(get_db) | ||
| ): |
There was a problem hiding this comment.
Given backend/main.py mounts this router with prefix="/api", and this router already uses prefix="/api/resolution-proof", this new endpoint will be exposed at /api/api/resolution-proof/{evidence_id}/blockchain-verify. If the intended public path is /api/resolution-proof/... (consistent with other routers that use APIRouter() without an /api prefix), consider removing /api from this router’s prefix or adjusting the include_router prefix.
| "is_valid": is_valid, | ||
| "current_hash": evidence.integrity_hash, | ||
| "computed_hash": computed_hash, | ||
| "signature_valid": signature_valid, |
There was a problem hiding this comment.
verify_evidence_integrity() includes signature_valid in the returned dict, but ResolutionBlockchainVerificationResponse doesn’t define this field. This is either silently dropped (losing useful information) or can raise validation errors depending on Pydantic config. Either add signature_valid: bool to the response schema or stop returning it here and keep the result strictly schema-shaped.
| "signature_valid": signature_valid, |
|
|
||
|
|
||
| class ResolutionBlockchainVerificationResponse(BaseModel): | ||
| is_valid: bool = Field(..., description="Whether the resolution integrity is intact") |
There was a problem hiding this comment.
ResolutionBlockchainVerificationResponse omits signature_valid, even though the service returns it and it’s relevant to integrity verification. Consider adding signature_valid: bool so clients can distinguish signature failures from chaining-hash failures (or remove it from the service result to keep the contract minimal).
| is_valid: bool = Field(..., description="Whether the resolution integrity is intact") | |
| is_valid: bool = Field(..., description="Whether the resolution integrity is intact") | |
| signature_valid: bool = Field(..., description="Whether the cryptographic signature verification succeeded") |
| ev2 = ResolutionProofService.submit_evidence( | ||
| token_id="token-456", | ||
| evidence_hash="b" * 64, | ||
| gps_latitude=19.0760, | ||
| gps_longitude=72.8777, |
There was a problem hiding this comment.
The second submit_evidence() call passes token_id="token-456", but validate_token is patched to always return mock_token with token_id == "token-123". Since the service hashes token.token_id, this test doesn’t actually exercise chaining across different token IDs and could mask token-id related bugs. Consider using a side_effect that returns a second mock token with token_id="token-456".
| conn.execute(text("ALTER TABLE resolution_proof_tokens ADD COLUMN valid_from DATETIME")) | ||
| logger.info("Added valid_from column to resolution_proof_tokens") | ||
|
|
||
| if not column_exists("resolution_proof_tokens", "valid_until"): | ||
| conn.execute(text("ALTER TABLE resolution_proof_tokens ADD COLUMN valid_until DATETIME")) |
There was a problem hiding this comment.
The migration uses DATETIME in raw SQL (ALTER TABLE ... ADD COLUMN valid_from DATETIME), which is not a valid type in PostgreSQL (Render can run with DATABASE_URL). Use TIMESTAMP/TIMESTAMPTZ (or a SQLAlchemy-compiled type) to keep migrations portable across SQLite/Postgres.
| conn.execute(text("ALTER TABLE resolution_proof_tokens ADD COLUMN valid_from DATETIME")) | |
| logger.info("Added valid_from column to resolution_proof_tokens") | |
| if not column_exists("resolution_proof_tokens", "valid_until"): | |
| conn.execute(text("ALTER TABLE resolution_proof_tokens ADD COLUMN valid_until DATETIME")) | |
| conn.execute(text("ALTER TABLE resolution_proof_tokens ADD COLUMN valid_from TIMESTAMP")) | |
| logger.info("Added valid_from column to resolution_proof_tokens") | |
| if not column_exists("resolution_proof_tokens", "valid_until"): | |
| conn.execute(text("ALTER TABLE resolution_proof_tokens ADD COLUMN valid_until TIMESTAMP")) |
| # Blockchain integrity fields | ||
| integrity_hash = Column(String, nullable=True) | ||
| previous_integrity_hash = Column(String, nullable=True, index=True) | ||
|
|
There was a problem hiding this comment.
ResolutionEvidence still has no created_at field (it only defines uploaded_at earlier in the model), but the resolution proof router/service and EvidenceResponse schema reference created_at (e.g., created_at=evidence.created_at). This will raise AttributeError at runtime unless there’s an alias/property elsewhere. Consider adding a created_at column (or aliasing uploaded_at to created_at) and migrating/backfilling as needed so the ORM model matches the API/schema expectations.
| @property | |
| def created_at(self) -> datetime.datetime | None: | |
| """ | |
| Alias for uploaded_at to satisfy API/schema expectations. | |
| This avoids changing the DB schema while exposing created_at. | |
| """ | |
| return self.uploaded_at | |
| @created_at.setter | |
| def created_at(self, value: datetime.datetime | None) -> None: | |
| self.uploaded_at = value |
Implement cryptographic hash chaining for `ResolutionEvidence` to create a tamper-proof audit trail for issue resolutions.
Key Improvements:
- 🔗 **Cryptographic Chaining**: Linked evidence records using SHA-256 hashes of current data and previous records.
- ⚡ **O(1) Chaining**: Added `resolution_last_hash_cache` in `backend/cache.py` to provide the latest hash in O(1), eliminating database lookups during evidence submission.
- ⚡ **O(1) Verification**: Optimized `verify_evidence` to fetch only the latest record using projected columns and `order_by().first()`.
- ✅ **Blockchain Verification**: Added a dedicated `/api/resolution-proof/{evidence_id}/blockchain-verify` endpoint for single-record integrity checks.
- 🛠️ **Robustness**: Fixed column name confusion in `ResolutionProofToken` (supporting both `token` and `token_id`) and implemented deterministic timestamp hashing using fixed string formats (`strftime`).
- 📉 **Efficiency**: Added database indexes for `grievance_id` and integrity hashes to speed up common queries.
CI Fixes:
- Fixed Netlify deployment issues by unifying build configuration in the root `netlify.toml` and ensuring physical `_redirects` and `_headers` files are preserved in `frontend/public/` for post-processing.
- Removed conflicting `frontend/netlify.toml`.
This completes the end-to-end blockchain integrity lifecycle for reported issues and ensures stable deployment.
🔍 Quality Reminder |
Implement cryptographic hash chaining for `ResolutionEvidence` to create a tamper-proof audit trail for issue resolutions.
Key Improvements:
- 🔗 **Cryptographic Chaining**: Linked evidence records using SHA-256 hashes of current data and previous records.
- ⚡ **O(1) Chaining**: Added `resolution_last_hash_cache` in `backend/cache.py` to provide the latest hash in O(1), eliminating database lookups during evidence submission.
- ⚡ **O(1) Verification**: Optimized `verify_evidence` to fetch only the latest record using projected columns and `order_by().first()`.
- ✅ **Blockchain Verification**: Added a dedicated `/api/resolution-proof/{evidence_id}/blockchain-verify` endpoint for single-record integrity checks.
- 🛠️ **Robustness**: Fixed column name confusion in `ResolutionProofToken` and implemented deterministic timestamp hashing using fixed string formats.
- 📉 **Efficiency**: Added database indexes for `grievance_id` and integrity hashes to speed up common queries.
CI Fixes:
- Fixed Netlify deployment issues by correctly scoping build configuration to the `frontend/` directory using a root `netlify.toml` with `base = "frontend"`.
- Unified build and redirect configuration in `frontend/netlify.toml`.
- Preserved physical `_redirects` and `_headers` files in `frontend/public/` for post-processing.
This completes the end-to-end blockchain integrity lifecycle for reported issues and ensures stable deployment.
This PR implements cryptographic chaining (blockchain) for the Resolution Proof system, ensuring that once evidence for a resolved grievance is submitted, it is cryptographically sealed and linked to the overall chain of resolutions.
Performance benefits include:
Functionality was verified with an expanded test suite covering chaining logic, tamper detection, and schema compliance.
PR created automatically by Jules for task 657622869482249267 started by @RohanExploit
Summary by cubic
Adds blockchain-style hash chaining to resolution evidence to make proofs tamper-evident and faster to verify. Also adds a single-record integrity check endpoint and improves verification with an O(1) chain-tip cache and targeted queries.
New Features
resolution_last_hash_cache./api/resolution-proof/{evidence_id}/blockchain-verifyfor single-record checks; verify now fetches only the latest record plus a count, with new indexes on grievance_id and previous_integrity_hash.Bug Fixes
tokenandtoken_id, validate viatoken, setexpires_at; addednonce,valid_from,valid_until.netlify.tomlsetsbase = "frontend";frontend/netlify.tomldefines build/publish plus redirects and security headers to fix Netlify builds.Written for commit 69cfbe5. Summary will update on new commits.
Summary by CodeRabbit
New Features
Improvements
Tests