// SPDX-License-Identifier: MIT pragma solidity ^0.8.13; contract DeadManSwitchManifestRegistry { error TimeoutZero(); error NotRegistered(); error NotSuccessor(); error TimeoutNotElapsed(); error SuccessorUsed(); struct Entry { bytes32 manifestRoot; address successor; uint64 lastSeen; uint64 timeout; } mapping(address => Entry) public entries; mapping(address => address) private successorToOwner; event Registered(address indexed owner, bytes32 manifestRoot, address indexed successor, uint64 timeout); event Pinged(address indexed owner, uint64 lastSeen); event TakenOver(address indexed owner, address indexed successor, bytes32 newManifestRoot); function register(bytes32 root, address successor, uint64 timeout) external { if (timeout == 0) revert TimeoutZero(); Entry storage old = entries[msg.sender]; if (successorToOwner[old.successor] == msg.sender) { delete successorToOwner[old.successor]; } address existing = successorToOwner[successor]; if (existing != address(0) && existing != msg.sender) { revert SuccessorUsed(); } entries[msg.sender] = Entry({ manifestRoot: root, successor: successor, lastSeen: uint64(block.timestamp), timeout: timeout }); successorToOwner[successor] = msg.sender; emit Registered(msg.sender, root, successor, timeout); } function ping() external { Entry storage entry = entries[msg.sender]; if (entry.timeout == 0) revert NotRegistered(); entry.lastSeen = uint64(block.timestamp); emit Pinged(msg.sender, entry.lastSeen); } function takeover(bytes32 newRoot) external { address owner = successorToOwner[msg.sender]; if (owner == address(0)) revert NotSuccessor(); Entry storage entry = entries[owner]; if (block.timestamp < uint256(entry.lastSeen) + uint256(entry.timeout)) { revert TimeoutNotElapsed(); } entry.manifestRoot = newRoot; entry.lastSeen = uint64(block.timestamp); emit TakenOver(owner, msg.sender, newRoot); } }