// SPDX-License-Identifier: MIT pragma solidity 0.8.24; /// @title RevowAnchor /// @notice Timestamps publisher logs, and records withdrawals. /// /// No owner, no admin, no proxy, no upgrade path, no constructor /// argument. Nothing in it can be changed after deployment, including /// by whoever deploys it. A later version is a new deployment under a /// new specification number, never an upgrade of this one. /// /// It stores no claim, no licence reference, no name and no personal /// data. The protocol works without it: claims are files, bindings are /// DNS records, authority files are CSV. This contract buys two things /// and nothing else. A timestamp nobody can move, and a withdrawal that /// propagates in seconds instead of at the next fetch. contract RevowAnchor { /// @notice A publisher recorded the Merkle root of its log for one UTC day. /// @dev Deliberately event only, with no storage. The event is the record, /// consumers index it, and nothing can overwrite or hide an earlier /// anchor. Re-anchoring the same day is allowed and leaves both /// entries visible, which is what an amendment should look like. event Anchored(address indexed publisher, uint32 indexed day, bytes32 root, uint64 at); /// @notice A subject withdrew one of its own claims. event Revoked(address indexed subject, bytes32 indexed claimId, uint64 at); /// @notice subject => claim identifier => unix time of withdrawal, 0 if not withdrawn. /// @dev Stored rather than event only, because a consumer about to display a /// claim needs to answer "is this withdrawn" in one cheap call. mapping(address => mapping(bytes32 => uint64)) public revokedAt; error EmptyRoot(); error AlreadyRevoked(); /// @notice Record the Merkle root of your log for a UTC day. /// @param day Days since the Unix epoch, in UTC. /// @param root Merkle root over the document digests of that day. function anchor(uint32 day, bytes32 root) external { if (root == bytes32(0)) revert EmptyRoot(); emit Anchored(msg.sender, day, root, uint64(block.timestamp)); } /// @notice Withdraw one of your own claims. /// @dev msg.sender is the subject, so no signature is needed and nobody, /// including the deployer, can withdraw on another subject behalf. /// @param claimId The claim identifier, sha256 over the canonical document. function revoke(bytes32 claimId) external { if (revokedAt[msg.sender][claimId] != 0) revert AlreadyRevoked(); revokedAt[msg.sender][claimId] = uint64(block.timestamp); emit Revoked(msg.sender, claimId, uint64(block.timestamp)); } }