139 lines
4.6 KiB
Solidity
139 lines
4.6 KiB
Solidity
|
// SPDX-License-Identifier: Apache-2.0
|
||
|
// Authors: six and Silur
|
||
|
pragma solidity ^0.8.16;
|
||
|
|
||
|
contract CCTF9 {
|
||
|
address public admin;
|
||
|
uint256 public volStart;
|
||
|
uint256 public volMaxPoints;
|
||
|
uint256 public powDiff;
|
||
|
bool public started;
|
||
|
|
||
|
enum PlayerStatus {
|
||
|
Unverified,
|
||
|
Verified,
|
||
|
Banned
|
||
|
}
|
||
|
|
||
|
struct Player {
|
||
|
PlayerStatus status;
|
||
|
uint256 points;
|
||
|
}
|
||
|
|
||
|
modifier onlyAdmin {
|
||
|
require(msg.sender == admin, "Not admin");
|
||
|
_;
|
||
|
}
|
||
|
|
||
|
modifier onlyActive {
|
||
|
require(started == true, "CCTF not started.");
|
||
|
_;
|
||
|
}
|
||
|
|
||
|
struct Flag {
|
||
|
address signer;
|
||
|
bool onlyFirstSolver;
|
||
|
uint256 points;
|
||
|
string skill_name;
|
||
|
}
|
||
|
|
||
|
mapping (address => Player) public players;
|
||
|
mapping (uint256 => Flag) public flags;
|
||
|
|
||
|
event CCTFStarted(uint256 timestamp);
|
||
|
event FlagAdded(uint256 indexed flagId, address flagSigner);
|
||
|
event FlagRemoved(uint256 indexed flagId);
|
||
|
event FlagSolved(uint256 indexed flagId, address indexed solver);
|
||
|
|
||
|
constructor(uint256 _volMaxPoints, uint256 _powDiff) {
|
||
|
admin = msg.sender;
|
||
|
volMaxPoints = _volMaxPoints;
|
||
|
powDiff = _powDiff;
|
||
|
started = false;
|
||
|
}
|
||
|
|
||
|
function setAdmin(address _admin) external onlyAdmin {
|
||
|
require(_admin != address(0));
|
||
|
admin = _admin;
|
||
|
}
|
||
|
|
||
|
function setCCTFStatus(bool _started) external onlyAdmin {
|
||
|
started = _started;
|
||
|
}
|
||
|
|
||
|
function setFlag(uint256 _flagId, address _flagSigner, bool _onlyFirstSolver, uint256 _points, string memory _skill) external onlyAdmin{
|
||
|
flags[_flagId] = Flag(_flagSigner, _onlyFirstSolver, _points, _skill);
|
||
|
emit FlagAdded(_flagId, _flagSigner);
|
||
|
}
|
||
|
|
||
|
function setPowDiff(uint256 _powDiff) external onlyAdmin {
|
||
|
powDiff = _powDiff;
|
||
|
}
|
||
|
|
||
|
|
||
|
function register(string memory _RTFM) external {
|
||
|
require(players[msg.sender].status == PlayerStatus.Unverified, 'Already registered or banned');
|
||
|
//uint256 pow = uint256(keccak256(abi.encodePacked("CCTF", msg.sender,"registration", nonce)));
|
||
|
//require(pow < powDiff, "invalid pow");
|
||
|
require(keccak256(abi.encodePacked('I_read_it')) == keccak256(abi.encodePacked(_RTFM))); // PoW can be used for harder challenges, this is Entry!
|
||
|
players[msg.sender].status = PlayerStatus.Verified;
|
||
|
}
|
||
|
|
||
|
function setPlayerStatus(address player, PlayerStatus status) external onlyAdmin {
|
||
|
players[player].status = status;
|
||
|
}
|
||
|
|
||
|
|
||
|
////////// Submit flags
|
||
|
mapping(bytes32 => bool) usedNs; // Against replay attack (we only check message signer)
|
||
|
mapping (address => mapping (uint256 => bool)) Solves; // address -> challenge ID -> solved/not
|
||
|
uint256 public submission_success_count = 0; // For statistics
|
||
|
|
||
|
function SubmitFlag(bytes32 _message, bytes memory signature, uint256 _submitFor) external onlyActive {
|
||
|
require(players[msg.sender].status == PlayerStatus.Verified, "You are not even playing");
|
||
|
require(bytes32(_message).length <= 256, "Too long message.");
|
||
|
require(!usedNs[_message]);
|
||
|
usedNs[_message] = true;
|
||
|
require(recoverSigner(_message, signature) == flags[_submitFor].signer, "Not signed with the correct key.");
|
||
|
require(Solves[msg.sender][_submitFor] == false);
|
||
|
|
||
|
Solves[msg.sender][_submitFor] = true;
|
||
|
players[msg.sender].points += flags[_submitFor].points;
|
||
|
players[msg.sender].points = players[msg.sender].points < volMaxPoints ? players[msg.sender].points : volMaxPoints;
|
||
|
|
||
|
if (flags[_submitFor].onlyFirstSolver) {
|
||
|
flags[_submitFor].points = 0;
|
||
|
}
|
||
|
|
||
|
submission_success_count = submission_success_count + 1;
|
||
|
emit FlagSolved(_submitFor, msg.sender);
|
||
|
}
|
||
|
|
||
|
function recoverSigner(bytes32 _ethSignedMessageHash, bytes memory _signature) public pure returns (address) {
|
||
|
(bytes32 r, bytes32 s, uint8 v) = splitSignature(_signature);
|
||
|
return ecrecover(_ethSignedMessageHash, v, r, s);
|
||
|
}
|
||
|
|
||
|
function splitSignature(bytes memory sig) public pure returns (bytes32 r, bytes32 s, uint8 v){
|
||
|
require(sig.length == 65, "Invalid signature length");
|
||
|
assembly {
|
||
|
r := mload(add(sig, 32))
|
||
|
s := mload(add(sig, 64))
|
||
|
v := byte(0, mload(add(sig, 96)))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////// Check status, scores, etc
|
||
|
function getPlayerStatus(address _player) external view returns (PlayerStatus) {
|
||
|
return players[_player].status;
|
||
|
}
|
||
|
|
||
|
function getPlayerPoints(address _player) external view returns (uint256) {
|
||
|
return players[_player].points < volMaxPoints ? players[_player].points : volMaxPoints;
|
||
|
}
|
||
|
|
||
|
function getSuccessfulSubmissionCount() external view returns (uint256){
|
||
|
return submission_success_count;
|
||
|
}
|
||
|
}
|