The floowing PoC tests show that the issue will cause the contract to be permanently locked
pragma solidity ^0.8.27;
import {Test} from "forge-std/Test.sol";
import {TreasureHunt} from "../contracts/src/TreasureHunt.sol";
import {IVerifier} from "../contracts/src/Verifier.sol";
contract MockVerifier is IVerifier {
function verify(bytes calldata, bytes32[] calldata) external pure returns (bool) {
return true;
}
}
contract TreasureHuntUpdateVerifierTest is Test {
TreasureHunt treasureHunt;
MockVerifier mockVerifier;
address owner;
function setUp() public {
owner = address(this);
mockVerifier = new MockVerifier();
treasureHunt = new TreasureHunt(address(mockVerifier));
}
function testUpdateVerifierWithZeroAddressBreaksContract() public {
assert(treasureHunt.getVerifier() != address(0), "Initial verifier should not be zero");
treasureHunt.pause();
assertTrue(treasureHunt.isPaused(), "Contract should be paused");
treasureHunt.updateVerifier(IVerifier(address(0)));
assertEq(treasureHunt.getVerifier(), address(0), "Verifier should now be address(0)");
treasureHunt.unpause();
assertFalse(treasureHunt.isPaused(), "Contract should be unpaused");
bytes32 treasureHash = keccak256("treasure1");
address recipient = address(0xAAAA);
vm.prank(address(0xBBBB));
vm.expectRevert();
treasureHunt.claim(
abi.encode(uint256(1)),
treasureHash,
payable(recipient)
);
}
function testContractBecomesPermanentlyBroken() public {
treasureHunt.fund{value: 100 ether}();
treasureHunt.pause();
treasureHunt.updateVerifier(IVerifier(address(0)));
treasureHunt.unpause();
bytes32 treasureHash = keccak256("treasure1");
address participant = address(0xCCCC);
vm.prank(participant);
vm.expectRevert();
treasureHunt.claim(
abi.encode(uint256(1)),
treasureHash,
payable(participant)
);
}
function testConstructorValidatesZeroAddressButUpdateDoesNot() public {
vm.expectRevert(TreasureHunt.InvalidVerifier.selector);
new TreasureHunt(address(0));
treasureHunt.pause();
treasureHunt.updateVerifier(IVerifier(address(0)));
assertEq(treasureHunt.getVerifier(), address(0), "Inconsistent validation!");
}
function testAccidentalZeroAddressUpdate() public {
address newVerifier = address(0);
treasureHunt.pause();
treasureHunt.updateVerifier(IVerifier(newVerifier));
treasureHunt.unpause();
assertTrue(treasureHunt.isPaused() == false);
bytes memory dummyProof = abi.encode(uint256(1));
bytes32 treasureHash = keccak256("treasure1");
vm.prank(address(0xDDDD));
vm.expectRevert();
treasureHunt.claim(dummyProof, treasureHash, payable(address(0xDDDD)));
}
}
Simply check if the newVerifer is the zero address and revert if it is not.