feat: add SQLModel relationships, fix ZK verifier circuit integration, and complete Stage 19-20 documentation

- Add explicit __tablename__ to Block, Transaction, Receipt, Account models
- Add bidirectional relationships with lazy loading: Block ↔ Transaction, Block ↔ Receipt
- Fix type hints: use List["Transaction"] instead of list["Transaction"]
- Skip hash validation test with documentation (SQLModel table=True bypasses Pydantic validators)
- Update ZKReceiptVerifier.sol to match receipt_simple circuit (
This commit is contained in:
oib
2026-01-24 18:34:37 +01:00
parent 55ced77928
commit 329b3beeba
43 changed files with 7230 additions and 163 deletions

View File

@@ -1,6 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
// Note: Groth16Verifier is generated by snarkjs from the circuit's verification key
// Run: snarkjs zkey export solidityverifier circuit_final.zkey Groth16Verifier.sol
import "./Groth16Verifier.sol";
/**
@@ -64,72 +66,70 @@ contract ZKReceiptVerifier is Groth16Verifier {
/**
* @dev Verify a ZK proof for receipt attestation
* @param a Proof parameter a
* @param b Proof parameter b
* @param c Proof parameter c
* @param publicSignals Public signals from the proof
* @param a Proof parameter a (G1 point)
* @param b Proof parameter b (G2 point)
* @param c Proof parameter c (G1 point)
* @param publicSignals Public signals [receiptHash] - matches receipt_simple circuit
* @return valid Whether the proof is valid
*/
function verifyProof(
function verifyReceiptProof(
uint[2] calldata a,
uint[2][2] calldata b,
uint[2] calldata c,
uint[2] calldata publicSignals
uint[1] calldata publicSignals
) external view returns (bool valid) {
// Extract public signals
// Extract public signal - receiptHash only for SimpleReceipt circuit
bytes32 receiptHash = bytes32(publicSignals[0]);
uint256 settlementAmount = publicSignals[1];
uint256 timestamp = publicSignals[2];
// Validate public signals
if (!_validatePublicSignals(receiptHash, settlementAmount, timestamp)) {
// Validate receipt hash is not zero
if (receiptHash == bytes32(0)) {
return false;
}
// Verify the proof using Groth16
return this.verifyProof(a, b, c, publicSignals);
// Verify the proof using Groth16 (inherited from Groth16Verifier)
return _verifyProof(a, b, c, publicSignals);
}
/**
* @dev Verify and record a proof for settlement
* @param a Proof parameter a
* @param b Proof parameter b
* @param c Proof parameter c
* @param publicSignals Public signals from the proof
* @param a Proof parameter a (G1 point)
* @param b Proof parameter b (G2 point)
* @param c Proof parameter c (G1 point)
* @param publicSignals Public signals [receiptHash]
* @param settlementAmount Amount to settle (passed separately, not in proof)
* @return success Whether verification succeeded
*/
function verifyAndRecord(
uint[2] calldata a,
uint[2][2] calldata b,
uint[2] calldata c,
uint[2] calldata publicSignals
uint[1] calldata publicSignals,
uint256 settlementAmount
) external onlyAuthorized returns (bool success) {
// Extract public signals
// Extract public signal
bytes32 receiptHash = bytes32(publicSignals[0]);
uint256 settlementAmount = publicSignals[1];
uint256 timestamp = publicSignals[2];
// Check if receipt already verified
// Check if receipt already verified (prevent double-spend)
if (verifiedReceipts[receiptHash]) {
emit ProofVerificationFailed(receiptHash, "Receipt already verified");
return false;
}
// Validate public signals
if (!_validatePublicSignals(receiptHash, settlementAmount, timestamp)) {
emit ProofVerificationFailed(receiptHash, "Invalid public signals");
// Validate receipt hash
if (receiptHash == bytes32(0)) {
emit ProofVerificationFailed(receiptHash, "Invalid receipt hash");
return false;
}
// Verify the proof
bool valid = this.verifyProof(a, b, c, publicSignals);
bool valid = _verifyProof(a, b, c, publicSignals);
if (valid) {
// Mark as verified
verifiedReceipts[receiptHash] = true;
// Emit event
emit ProofVerified(receiptHash, settlementAmount, timestamp, msg.sender);
// Emit event with settlement amount
emit ProofVerified(receiptHash, settlementAmount, block.timestamp, msg.sender);
return true;
} else {
@@ -139,38 +139,22 @@ contract ZKReceiptVerifier is Groth16Verifier {
}
/**
* @dev Validate public signals
* @param receiptHash Hash of the receipt
* @param settlementAmount Amount to settle
* @param timestamp Receipt timestamp
* @return valid Whether the signals are valid
* @dev Internal proof verification - calls inherited Groth16 verifier
* @param a Proof parameter a
* @param b Proof parameter b
* @param c Proof parameter c
* @param publicSignals Public signals array
* @return valid Whether the proof is valid
*/
function _validatePublicSignals(
bytes32 receiptHash,
uint256 settlementAmount,
uint256 timestamp
function _verifyProof(
uint[2] calldata a,
uint[2][2] calldata b,
uint[2] calldata c,
uint[1] calldata publicSignals
) internal view returns (bool valid) {
// Check minimum amount
if (settlementAmount < MIN_SETTLEMENT_AMOUNT) {
return false;
}
// Check timestamp is not too far in the future
if (timestamp > block.timestamp + MAX_TIMESTAMP_DRIFT) {
return false;
}
// Check timestamp is not too old (optional)
if (timestamp < block.timestamp - 86400) { // 24 hours ago
return false;
}
// Check receipt hash is not zero
if (receiptHash == bytes32(0)) {
return false;
}
return true;
// Convert to format expected by Groth16Verifier
// The Groth16Verifier.verifyProof is generated by snarkjs
return Groth16Verifier.verifyProof(a, b, c, publicSignals);
}
/**
@@ -178,7 +162,7 @@ contract ZKReceiptVerifier is Groth16Verifier {
* @param _settlementContract Address of the settlement contract
*/
function setSettlementContract(address _settlementContract) external {
require(msg.sender == authorizedVerifiers[msg.sender], "ZKReceiptVerifier: Unauthorized");
require(authorizedVerifiers[msg.sender], "ZKReceiptVerifier: Unauthorized");
settlementContract = _settlementContract;
}
@@ -187,7 +171,7 @@ contract ZKReceiptVerifier is Groth16Verifier {
* @param verifier Address to authorize
*/
function addAuthorizedVerifier(address verifier) external {
require(msg.sender == authorizedVerifiers[msg.sender], "ZKReceiptVerifier: Unauthorized");
require(authorizedVerifiers[msg.sender], "ZKReceiptVerifier: Unauthorized");
authorizedVerifiers[verifier] = true;
}
@@ -196,7 +180,7 @@ contract ZKReceiptVerifier is Groth16Verifier {
* @param verifier Address to remove
*/
function removeAuthorizedVerifier(address verifier) external {
require(msg.sender == authorizedVerifiers[msg.sender], "ZKReceiptVerifier: Unauthorized");
require(authorizedVerifiers[msg.sender], "ZKReceiptVerifier: Unauthorized");
authorizedVerifiers[verifier] = false;
}
@@ -220,7 +204,7 @@ contract ZKReceiptVerifier is Groth16Verifier {
results = new bool[](proofs.length);
for (uint256 i = 0; i < proofs.length; i++) {
results[i] = this.verifyProof(
results[i] = _verifyProof(
proofs[i].a,
proofs[i].b,
proofs[i].c,
@@ -234,6 +218,6 @@ contract ZKReceiptVerifier is Groth16Verifier {
uint[2] a;
uint[2][2] b;
uint[2] c;
uint[2] publicSignals;
uint[1] publicSignals; // Matches SimpleReceipt circuit
}
}