Init
parent
fba8616fe0
commit
3643b67ba0
|
@ -1,3 +1,5 @@
|
|||
# From_Solidity_To_Substrate
|
||||
|
||||
Workshop repository for the workshop at BME, 2023 August.
|
||||
Workshop repository for the workshop at BME, 2023 August.
|
||||
|
||||
Presentation slides and references TBA
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Authors: Anonz team, developed at Polkadot Metaverse Championship, as part of their CCTF track solution.
|
||||
// Based on Six's and Silur's CCTF 2022 code.
|
||||
pragma solidity ^0.8.16;
|
||||
|
||||
contract CryptoCTFX {
|
||||
enum PlayerStatus {
|
||||
Unverified,
|
||||
Verified,
|
||||
Banned
|
||||
}
|
||||
|
||||
struct Player {
|
||||
PlayerStatus status;
|
||||
uint score;
|
||||
}
|
||||
|
||||
modifier onlyExistingContest(uint contestID) {
|
||||
require(contests[contestID].admin != address(0), "Unknown contest ID");
|
||||
_;
|
||||
}
|
||||
|
||||
modifier onlyAdmin(uint contestID) {
|
||||
require(msg.sender == contests[contestID].admin, "You are not the admin of this contest");
|
||||
_;
|
||||
}
|
||||
|
||||
modifier onlyOpen(uint contestID) {
|
||||
require(contests[contestID].submissionsOpen, "Submissions are not open for this contest at this time");
|
||||
_;
|
||||
}
|
||||
|
||||
modifier onlyExistingChallenge(uint contestID, uint challengeID) {
|
||||
require(contests[contestID].challenges[challengeID].obscuredFlag != address(0), "Unknown challenge ID");
|
||||
_;
|
||||
}
|
||||
|
||||
struct Challenge {
|
||||
address obscuredFlag; // public key of the flag
|
||||
uint worth;
|
||||
uint256 descriptionFingerprint;
|
||||
bool onlyFirstSolver;
|
||||
string skill;
|
||||
}
|
||||
|
||||
struct Contest {
|
||||
address admin;
|
||||
mapping (uint => Challenge) challenges;
|
||||
mapping (address => Player) players;
|
||||
bool submissionsOpen;
|
||||
mapping (address => mapping (uint => bool)) solves; // address -> challengeID -> solved/not
|
||||
mapping (uint => bool) anySolves; // challengeID -> solved/not
|
||||
}
|
||||
|
||||
mapping (uint => Contest) public contests;
|
||||
|
||||
event ChallengeAddedOrUpdated(uint contestID, uint indexed challengeID);
|
||||
event ChallengeSolved(uint contestID, uint indexed challengeID, address indexed solver);
|
||||
|
||||
function createContest(uint contestID) external {
|
||||
require(contests[contestID].admin == address(0), "This contest ID has already been registered");
|
||||
contests[contestID].admin = msg.sender;
|
||||
contests[contestID].submissionsOpen = false;
|
||||
}
|
||||
|
||||
function setAdmin(uint contestID, address newAdmin) external onlyExistingContest(contestID) onlyAdmin(contestID) {
|
||||
require(newAdmin != address(0));
|
||||
contests[contestID].admin = newAdmin;
|
||||
}
|
||||
|
||||
function setSubmissionsStatus(uint contestID, bool open) external onlyExistingContest(contestID) onlyAdmin(contestID) {
|
||||
contests[contestID].submissionsOpen = open;
|
||||
}
|
||||
|
||||
function addOrUpdateChallenge(uint contestID, uint challengeID, address obscuredFlag, uint worth, uint256 descriptionFingerprint, bool onlyFirstSolver, string memory skill) external onlyExistingContest(contestID) onlyAdmin(contestID) {
|
||||
require(obscuredFlag != address(0), "The obscured flag value must not be 0");
|
||||
contests[contestID].challenges[challengeID] = Challenge(obscuredFlag, worth, descriptionFingerprint, onlyFirstSolver, skill);
|
||||
emit ChallengeAddedOrUpdated(contestID, challengeID);
|
||||
}
|
||||
|
||||
function register(uint contestID, string memory password) external onlyExistingContest(contestID) {
|
||||
require(contests[contestID].players[msg.sender].status == PlayerStatus.Unverified, "You are already registered or banned in this contest");
|
||||
require(keccak256(abi.encodePacked("I_read_it")) == keccak256(abi.encodePacked(password)));
|
||||
contests[contestID].players[msg.sender].status = PlayerStatus.Verified;
|
||||
}
|
||||
|
||||
function setPlayerStatus(uint contestID, address player, PlayerStatus status) external onlyExistingContest(contestID) onlyAdmin(contestID) {
|
||||
contests[contestID].players[player].status = status;
|
||||
}
|
||||
|
||||
function submitFlag(uint contestID, uint challengeID, bytes memory signature) external onlyExistingContest(contestID) onlyExistingChallenge(contestID, challengeID) onlyOpen(contestID) {
|
||||
require(contests[contestID].players[msg.sender].status == PlayerStatus.Verified, "You are unverified or banned in this contest");
|
||||
// the correct signature is an ECDSA signature where (1) the message (hash) is the sender address and (2) the private key is the flag;
|
||||
// (2) is checked by testing against the public key, which can then be public information
|
||||
address recoveredSigner = recoverSigner(bytes32(abi.encodePacked(msg.sender)), signature);
|
||||
require(recoveredSigner != address(0), "Invalid signature");
|
||||
require(recoveredSigner == contests[contestID].challenges[challengeID].obscuredFlag, "Wrong answer");
|
||||
require(!contests[contestID].solves[msg.sender][challengeID], "You have already solved this challenge of this contest");
|
||||
|
||||
if (!contests[contestID].anySolves[challengeID] || !contests[contestID].challenges[challengeID].onlyFirstSolver) {
|
||||
contests[contestID].players[msg.sender].score += contests[contestID].challenges[challengeID].worth;
|
||||
}
|
||||
contests[contestID].solves[msg.sender][challengeID] = true;
|
||||
contests[contestID].anySolves[challengeID] = true;
|
||||
|
||||
emit ChallengeSolved(contestID, challengeID, msg.sender);
|
||||
}
|
||||
|
||||
function recoverSigner(bytes32 messageHash, bytes memory signature) public pure returns (address) {
|
||||
(bytes32 r, bytes32 s, uint8 v) = splitSignature(signature);
|
||||
return ecrecover(messageHash, 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)))
|
||||
}
|
||||
}
|
||||
|
||||
function getPlayerStatus(uint contestID, address player) external view onlyExistingContest(contestID) returns (PlayerStatus) {
|
||||
return contests[contestID].players[player].status;
|
||||
}
|
||||
|
||||
function getPlayerScore(uint contestID, address player) external view onlyExistingContest(contestID) returns (uint) {
|
||||
return contests[contestID].players[player].score;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
// SPDX-License-Identifier: GPL-3.0
|
||||
|
||||
pragma solidity >=0.8.2 <0.9.0;
|
||||
|
||||
/**
|
||||
* @title Storage
|
||||
* @dev Store & retrieve value in a variable
|
||||
* @custom:dev-run-script ./scripts/deploy_with_ethers.ts
|
||||
*/
|
||||
contract Storage {
|
||||
|
||||
address private owner;
|
||||
|
||||
// event for EVM logging
|
||||
event OwnerSet(address indexed oldOwner, address indexed newOwner);
|
||||
|
||||
// modifier to check if caller is owner
|
||||
modifier isOwner() {
|
||||
// If the first argument of 'require' evaluates to 'false', execution terminates and all
|
||||
// changes to the state and to Ether balances are reverted.
|
||||
// This used to consume all gas in old EVM versions, but not anymore.
|
||||
// It is often a good idea to use 'require' to check if functions are called correctly.
|
||||
// As a second argument, you can also provide an explanation about what went wrong.
|
||||
require(msg.sender == owner, "Caller is not owner");
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Set contract deployer as owner
|
||||
*/
|
||||
constructor() {
|
||||
console.log("Owner contract deployed by:", msg.sender);
|
||||
owner = msg.sender; // 'msg.sender' is sender of current call, contract deployer for a constructor
|
||||
emit OwnerSet(address(0), owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Change owner
|
||||
* @param newOwner address of new owner
|
||||
*/
|
||||
function changeOwner(address newOwner) public isOwner {
|
||||
emit OwnerSet(owner, newOwner);
|
||||
owner = newOwner;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return owner address
|
||||
* @return address of owner
|
||||
*/
|
||||
function getOwner() external view returns (address) {
|
||||
return owner;
|
||||
}
|
||||
|
||||
|
||||
uint256 number;
|
||||
|
||||
/**
|
||||
* @dev Store value in variable
|
||||
* @param num value to store
|
||||
*/
|
||||
function store(uint256 num) public {
|
||||
number = num;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return value
|
||||
* @return value of 'number'
|
||||
*/
|
||||
function retrieve() public view returns (uint256){
|
||||
return number;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue