Files
fhe/examples/data-seal/contracts/DataSeal.sol
T
Zach Kelling 961c42e204 feat: add FHE example apps for all Pars PIPs
- data-seal: Verifiable Data Integrity Seal (PIP-0010/LP-0535)
  FHE-encrypted tamper-proof sealing with Public/ZK/Private modes,
  batch sealing, homomorphic verification

- content-provenance: AI & Media Content Provenance (PIP-0011/LP-7110)
  Model manifests, output attestation via homomorphic comparison,
  media derivation DAGs

- encrypted-crdt: Encrypted CRDT (PIP-0013/LP-6500)
  LWW-Register with FHE values, OR-Set with tag-based add/remove,
  Lamport timestamp conflict resolution, deterministic merge

- shadow-governance: Shadow Government Protocol (PIP-7010)
  Anonymous ministries, encrypted voting with homomorphic tallying,
  nullifier-based anti-fraud, quorum enforcement

Each example includes Solidity contracts, Hardhat tests, CLI tasks,
and README linking to corresponding PIP and LP specifications.
2026-02-13 14:16:46 -08:00

174 lines
6.3 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
import "@luxfhe/luxfhe-contracts/FHE.sol";
/// @title DataSeal - Verifiable Data Integrity Seal Protocol
/// @notice FHE-encrypted tamper-proof document sealing with three seal modes
/// @dev Implements PIP-0010 / LP-0535: Public, ZK, and Private (FHE) seals
contract DataSeal {
/// @notice Seal modes matching PIP-0010 specification
enum SealMode {
Public, // Fully transparent, anyone can verify
ZK, // Content hidden, properties provable via ZK
Private // FHE-encrypted with selective disclosure
}
struct Seal {
bytes32 contentHash; // SHA-256 of original content
euint32 encryptedTag; // FHE-encrypted integrity tag
address creator; // Seal creator
uint256 timestamp; // Block timestamp at creation
SealMode mode; // Seal type
bool revoked; // Revocation status
}
/// @notice All seals indexed by ID
mapping(uint256 => Seal) public seals;
uint256 public sealCount;
/// @notice Batch seal tracking
mapping(bytes32 => uint256[]) public batchSeals; // batchId => sealIds
/// @notice Seal verification results (encrypted)
mapping(uint256 => euint32) private verificationResults;
event SealCreated(uint256 indexed sealId, address indexed creator, SealMode mode, bytes32 contentHash);
event SealVerified(uint256 indexed sealId, address indexed verifier);
event SealRevoked(uint256 indexed sealId, address indexed revoker);
event BatchSealed(bytes32 indexed batchId, uint256 sealCount);
/// @notice Create a new data integrity seal
/// @param contentHash SHA-256 hash of the content being sealed
/// @param encryptedTag FHE-encrypted integrity tag (creator's secret)
/// @param mode Seal mode: Public, ZK, or Private
/// @return sealId The ID of the newly created seal
function createSeal(
bytes32 contentHash,
inEuint32 calldata encryptedTag,
SealMode mode
) external returns (uint256 sealId) {
sealId = sealCount++;
euint32 tag = FHE.asEuint32(encryptedTag);
FHE.allowThis(tag);
FHE.allow(tag, msg.sender);
seals[sealId] = Seal({
contentHash: contentHash,
encryptedTag: tag,
creator: msg.sender,
timestamp: block.timestamp,
mode: mode,
revoked: false
});
emit SealCreated(sealId, msg.sender, mode, contentHash);
}
/// @notice Verify a seal by comparing encrypted tags homomorphically
/// @param sealId The seal to verify
/// @param challengeTag Encrypted tag from the verifier
/// @dev Result is FHE-encrypted: 1 = match, 0 = mismatch
function verifySeal(
uint256 sealId,
inEuint32 calldata challengeTag
) external {
Seal storage seal = seals[sealId];
require(!seal.revoked, "Seal revoked");
euint32 challenge = FHE.asEuint32(challengeTag);
// Homomorphic equality check: encrypted comparison
// If tags match, result = 1; otherwise result = 0
ebool isEqual = FHE.eq(seal.encryptedTag, challenge);
euint32 result = FHE.select(isEqual, FHE.asEuint32(1), FHE.asEuint32(0));
FHE.allowThis(result);
FHE.allow(result, msg.sender);
verificationResults[sealId] = result;
// Request decryption for the verifier
FHE.decrypt(result);
emit SealVerified(sealId, msg.sender);
}
/// @notice Get the decrypted verification result
/// @param sealId The seal that was verified
/// @return matched Whether the verification passed
/// @return ready Whether the decryption is complete
function getVerificationResult(uint256 sealId) external view returns (bool matched, bool ready) {
euint32 result = verificationResults[sealId];
(uint256 value, bool decrypted) = FHE.getDecryptResultSafe(result);
return (value == 1, decrypted);
}
/// @notice Create multiple seals in a single transaction (batch sealing)
/// @param contentHashes Array of content hashes
/// @param encryptedTags Array of encrypted integrity tags
/// @param mode Seal mode for all seals in the batch
/// @return batchId Identifier for the batch
function batchSeal(
bytes32[] calldata contentHashes,
inEuint32[] calldata encryptedTags,
SealMode mode
) external returns (bytes32 batchId) {
require(contentHashes.length == encryptedTags.length, "Length mismatch");
require(contentHashes.length > 0, "Empty batch");
batchId = keccak256(abi.encodePacked(msg.sender, block.timestamp, sealCount));
uint256[] storage ids = batchSeals[batchId];
for (uint256 i = 0; i < contentHashes.length; i++) {
uint256 sealId = sealCount++;
euint32 tag = FHE.asEuint32(encryptedTags[i]);
FHE.allowThis(tag);
FHE.allow(tag, msg.sender);
seals[sealId] = Seal({
contentHash: contentHashes[i],
encryptedTag: tag,
creator: msg.sender,
timestamp: block.timestamp,
mode: mode,
revoked: false
});
ids.push(sealId);
emit SealCreated(sealId, msg.sender, mode, contentHashes[i]);
}
emit BatchSealed(batchId, contentHashes.length);
}
/// @notice Revoke a seal (only creator can revoke)
/// @param sealId The seal to revoke
function revokeSeal(uint256 sealId) external {
Seal storage seal = seals[sealId];
require(seal.creator == msg.sender, "Not seal creator");
require(!seal.revoked, "Already revoked");
seal.revoked = true;
emit SealRevoked(sealId, msg.sender);
}
/// @notice Get seal metadata (public fields only)
function getSeal(uint256 sealId) external view returns (
bytes32 contentHash,
address creator,
uint256 timestamp,
SealMode mode,
bool revoked
) {
Seal storage seal = seals[sealId];
return (seal.contentHash, seal.creator, seal.timestamp, seal.mode, seal.revoked);
}
/// @notice Get all seal IDs in a batch
function getBatchSeals(bytes32 batchId) external view returns (uint256[] memory) {
return batchSeals[batchId];
}
}