Reversible Commit Receipt
reversible-commit-receipt · receipt-attestation · 2670 B runtime · registry-info
scores
- usefulness: 4
- safety: 4
- liveness: 3
- authenticity: 3
- extensibility: 5
- bytecodeDiscipline: 7
- practical: 4
source
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract ReversibleCommitReceipt {
struct Commitment {
address committer;
uint256 createdAt;
}
uint256 public immutable duration;
uint256 public immutable fee;
address public immutable owner;
mapping(bytes32 => Commitment) private commitments;
mapping(bytes32 => bool) public finalizedReceipts;
bytes32[] private activeHashes;
mapping(bytes32 => uint256) private hashToListIndex;
event Committed(address indexed committer, bytes32 indexed hash, uint256 blockNumber);
event Revoked(address indexed committer, bytes32 indexed hash, uint256 blockNumber);
event Finalized(address indexed committer, bytes32 indexed hash, uint256 blockNumber);
constructor(uint256 _duration, uint256 _fee) {
duration = _duration;
fee = _fee;
owner = msg.sender;
}
function commit(bytes32 _hash) external {
require(commitments[_hash].committer == address(0), "Hash already used");
commitments[_hash] = Commitment(msg.sender, block.number);
activeHashes.push(_hash);
hashToListIndex[_hash] = activeHashes.length;
emit Committed(msg.sender, _hash, block.number);
}
function revoke(bytes32 _hash) external payable {
Commitment storage c = commitments[_hash];
require(c.committer != address(0), "Commitment not found");
require(msg.sender == c.committer, "Only committer");
require(block.number < c.createdAt + duration, "Revocation window closed");
require(msg.value == fee, "Revocation fee required");
_removeHash(_hash);
delete commitments[_hash];
emit Revoked(msg.sender, _hash, block.number);
}
function finalize(bytes32 _hash) external {
Commitment storage c = commitments[_hash];
require(c.committer != address(0), "Commitment not found");
require(block.number >= c.createdAt + duration, "Revocation window open");
require(!finalizedReceipts[_hash], "Already finalized");
finalizedReceipts[_hash] = true;
_removeHash(_hash);
emit Finalized(c.committer, _hash, block.number);
}
function collectExpired(uint256 _limit) external {
uint256 processed = 0;
uint256 i = 0;
while (i < activeHashes.length && processed < _limit) {
bytes32 h = activeHashes[i];
Commitment storage c = commitments[h];
if (block.number >= c.createdAt + duration) {
_removeHash(h);
delete commitments[h];
} else {
++i;
}
++processed;
}
}
function withdraw() external {
require(msg.sender == owner, "Not owner");
payable(msg.sender).transfer(address(this).balance);
}
function activeCount() external view returns (uint256) {
return activeHashes.length;
}
function getCommitment(bytes32 _hash) external view returns (address committer, uint256 createdAt, bool finalized) {
Commitment storage c = commitments[_hash];
return (c.committer, c.createdAt, finalizedReceipts[_hash]);
}
function _removeHash(bytes32 _hash) internal {
uint256 idx = hashToListIndex[_hash] - 1;
uint256 last = activeHashes.length - 1;
if (idx != last) {
bytes32 movedHash = activeHashes[last];
activeHashes[idx] = movedHash;
hashToListIndex[movedHash] = idx + 1;
}
activeHashes.pop();
delete hashToListIndex[_hash];
}
}