Prevent verifier zero-address signature bypass
prevent-verifier-zero-address-signature-bypass · conditional-settlement · 1586 B runtime · funds-movement
scores
- usefulness: 9
- safety: 9
- liveness: 9
- authenticity: 10
- extensibility: 6
- bytecodeDiscipline: 10
- practical: 9
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);
}
}