Prevent verifier zero-address signature bypass

prevent-verifier-zero-address-signature-bypass · conditional-settlement · 1586 B runtime · funds-movement

scores

source

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

contract OracleSignedTaskEscrow {
    struct Escrow {
        address payable depositor;
        address payable worker;
        address verifier;
        uint256 amount;
        uint256 deadline;
        bool settled;
    }

    mapping(uint256 => Escrow) private escrows;
    uint256 internal constant TIMEOUT = 7 days;

    error ZeroValue();
    error TaskExists();
    error TaskNotFound();
    error AlreadySettled();
    error NotWorker();
    error InvalidSignature();
    error NotDepositor();
    error DeadlineNotPassed();

    function create(uint256 taskId, address payable worker, address verifier) external payable {
        if (msg.value == 0) revert ZeroValue();
        if (escrows[taskId].amount != 0) revert TaskExists();
        require(verifier != address(0));

        escrows[taskId] = Escrow({
            depositor: payable(msg.sender),
            worker: worker,
            verifier: verifier,
            amount: msg.value,
            deadline: block.timestamp + TIMEOUT,
            settled: false
        });
    }

    function settle(uint256 taskId, bytes calldata signature) external {
        Escrow storage e = escrows[taskId];
        if (e.amount == 0) revert TaskNotFound();
        if (e.settled) revert AlreadySettled();
        if (msg.sender != e.worker) revert NotWorker();
        if (signature.length != 65) revert();

        uint8 v;
        bytes32 r;
        bytes32 s;
        assembly {
            r := calldataload(signature.offset)
            s := calldataload(add(signature.offset, 32))
            v := byte(0, calldataload(add(signature.offset, 64)))
        }
        if (v < 27) v += 27;

        bytes32 hash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", bytes32(taskId)));
        if (ecrecover(hash, v, r, s) != e.verifier) revert InvalidSignature();

        e.settled = true;
        e.worker.transfer(e.amount);
    }

    function refund(uint256 taskId) external {
        Escrow storage e = escrows[taskId];
        if (e.amount == 0) revert TaskNotFound();
        if (e.settled) revert AlreadySettled();
        if (msg.sender != e.depositor) revert NotDepositor();
        if (block.timestamp < e.deadline) revert DeadlineNotPassed();

        e.settled = true;
        e.depositor.transfer(e.amount);
    }
}