Prevent replayable Finalized events with a finalized state flag
prevent-replayable-finalized-events-with-a-finalized-state-f · receipt-attestation · 1111 B runtime · funds-movement
scores
- usefulness: 4
- safety: 6
- liveness: 9
- authenticity: 3
- extensibility: 2
- bytecodeDiscipline: 10
- practical: 6
source
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract ReceiptNotary {
uint256 private data;
event AnchorSet(bytes32 indexed anchor, address indexed submitter, uint32 deadline);
event Disputed(bytes32 anchor, address indexed submitter);
event Finalized(bytes32 anchor);
uint256 private constant WINDOW = 2 days;
uint256 private constant HASH_BITS = 63;
uint256 private constant HASH_MASK = (1 << HASH_BITS) - 1;
uint256 private constant ADDR_MASK = (1 << 160) - 1;
uint256 private constant DEADLINE_MASK = (1 << 32) - 1;
function submit(bytes32 anchor) external {
require(data == 0);
uint32 deadline = uint32(block.timestamp + WINDOW);
uint64 hashPart = uint64(uint256(anchor) & HASH_MASK);
data = (uint256(hashPart) << 192) | (uint256(uint160(msg.sender)) << 32) | deadline;
emit AnchorSet(anchor, msg.sender, deadline);
}
function dispute(bytes calldata preimage) external {
require(block.timestamp <= deadline());
require(!disputed());
bytes32 anchor = keccak256(preimage);
require((uint256(anchor) & HASH_MASK) == ((data >> 192) & HASH_MASK));
data |= DEADLINE_MASK;
emit Disputed(anchor, submitter());
}
function finalize() external {
require(block.timestamp > deadline());
require(!disputed());
require((data & (uint256(1) << 255)) == 0);
data |= uint256(1) << 255;
emit Finalized(anchor());
}
function anchor() public view returns (bytes32) {
return bytes32((data >> 192) & HASH_MASK);
}
function submitter() public view returns (address) {
return address(uint160((data >> 32) & ADDR_MASK));
}
function deadline() public view returns (uint32) {
return uint32(data & DEADLINE_MASK);
}
function disputed() public view returns (bool) {
return (data & DEADLINE_MASK) == DEADLINE_MASK;
}
}