Compare commits

..

21 Commits

Author SHA1 Message Date
bzp99 7047793fd2
update README 2022-12-07 10:32:59 +01:00
bzp99 b04dc83ae1
Add RMRK proposal 2022-12-07 09:22:15 +01:00
bzp99 1737245885
rename kusamaverse proposal directory 2022-12-07 09:22:15 +01:00
jazzysnake 1911680b62 Merge branch 'main' of https://git.hsbp.org/BMEta/solutions 2022-12-07 08:51:50 +01:00
jazzysnake e14f77b5fa skeleton 2022-12-07 08:51:05 +01:00
bzp99 69cfe72035
Factor more potentially offline operations out of JS code 2022-12-07 08:39:40 +01:00
jazzysnake 86f5bae9b5 kusamaverse proposal 2022-12-07 08:35:51 +01:00
Bazsalanszky 82eb729ae2
Merge remote-tracking branch 'origin/frontend' 2022-12-07 06:05:36 +01:00
jazzysnake f7ddb447eb frontend 2022-12-07 06:03:10 +01:00
ProMate04 de8b6dae44 FAVICON 2022-12-07 05:24:28 +01:00
ProMate04 2c1c360de3 Basics of the Kusamaverse Proposal 2022-12-07 05:13:48 +01:00
Bazsalanszky dfb39edb99
Better UI elements 2022-12-07 03:36:09 +01:00
ProMate04 425b6ae6dd Integrated MetaMask into the UI elements 2022-12-07 00:36:25 +01:00
bzp99 90a40a0be9
Add initial front-end logic 2022-12-06 23:46:13 +01:00
Bazsalanszky 856b568780
Updated PlayerRegistered event to the CTF contract 2022-12-06 22:32:24 +01:00
Bazsalanszky cb4cabd600
Added ZoKrates Kotlin library 2022-12-06 21:18:53 +01:00
Bazsalanszky 0f981ffea9
Fixed ZoKrates vulnerability 2022-12-06 21:18:32 +01:00
Bazsalanszky ec0fae56dc
Fixed ZoKrates compile error 2022-12-06 20:48:34 +01:00
Bazsalanszky f90e4abbfc
Merge remote-tracking branch 'origin/main' 2022-12-06 20:35:06 +01:00
Bazsalanszky 06b80d8cde
Updated zkp scheme 2022-12-06 20:34:54 +01:00
bzp99 1f00258d94
Add static frontend elements
(Just a few HTML files using bootstrap)
2022-12-06 19:37:14 +01:00
35 changed files with 50455 additions and 38 deletions

View File

@ -0,0 +1,9 @@
#
# https://help.github.com/articles/dealing-with-line-endings/
#
# Linux start script should use lf
/gradlew text eol=lf
# These are Windows script files and should use crlf
*.bat text eol=crlf

View File

@ -141,3 +141,5 @@ crashlytics-build.properties
fabric.properties fabric.properties
.zokrates .zokrates
# Ignore Gradle build output directory
build

View File

@ -0,0 +1,35 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm") version "1.7.10"
id("org.jetbrains.compose") version "1.2.1"
application
}
group = "me.balazs"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
google()
}
dependencies {
implementation(compose.desktop.currentOs)
testImplementation(kotlin("test"))
}
tasks.test {
useJUnitPlatform()
}
tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = "1.8"
}
application {
mainClass.set("MainKt")
}

View File

@ -8,7 +8,6 @@ pragma solidity ^0.8.16;
import "./verifier.sol"; import "./verifier.sol";
contract CCTF9 is Verifier { contract CCTF9 is Verifier {
address public admin; address public admin;
uint256 public volMaxPoints; uint256 public volMaxPoints;
@ -25,12 +24,12 @@ contract CCTF9 is Verifier{
uint256 points; uint256 points;
} }
modifier onlyAdmin { modifier onlyAdmin() {
require(msg.sender == admin, "Not admin"); require(msg.sender == admin, "Not admin");
_; _;
} }
modifier onlyActive { modifier onlyActive() {
require(started == true, "CCTF not started."); require(started == true, "CCTF not started.");
_; _;
} }
@ -53,6 +52,7 @@ contract CCTF9 is Verifier{
event FlagAdded(uint256 indexed flagId, Hash flagHash); event FlagAdded(uint256 indexed flagId, Hash flagHash);
event FlagRemoved(uint256 indexed flagId); event FlagRemoved(uint256 indexed flagId);
event FlagSolved(uint256 indexed flagId, address indexed solver); event FlagSolved(uint256 indexed flagId, address indexed solver);
event PlayerRegistered(address playerAddress);
constructor(uint256 _volMaxPoints) { constructor(uint256 _volMaxPoints) {
admin = msg.sender; admin = msg.sender;
@ -72,44 +72,67 @@ contract CCTF9 is Verifier{
} }
/// creates a new capturable flag according to the arguments /// creates a new capturable flag according to the arguments
function setFlag(uint256 _flagId,Hash memory _flagHash, bool _onlyFirstSolver, uint256 _points, string memory _skill) external onlyAdmin{ function setFlag(
uint256 _flagId,
Hash memory _flagHash,
bool _onlyFirstSolver,
uint256 _points,
string memory _skill
) external onlyAdmin {
flags[_flagId] = Flag(_flagHash, _onlyFirstSolver, _points, _skill); flags[_flagId] = Flag(_flagHash, _onlyFirstSolver, _points, _skill);
emit FlagAdded(_flagId, _flagHash); emit FlagAdded(_flagId, _flagHash);
} }
/// registers the caller as a player /// registers the caller as a player
function register() external { function register() external {
require(players[msg.sender].status == PlayerStatus.Unverified, 'Already registered or banned'); require(
players[msg.sender].status == PlayerStatus.Unverified,
"Already registered or banned"
);
players[msg.sender].status = PlayerStatus.Verified; players[msg.sender].status = PlayerStatus.Verified;
} emit PlayerRegistered(msg.sender);
/// sets the player's status either to Unverified, Verified or Banned
function setPlayerStatus(address player, PlayerStatus status) external onlyAdmin {
players[player].status = status;
} }
/// sets the player's status either to Unverified, Verified or Banned
function setPlayerStatus(address player, PlayerStatus status)
external
onlyAdmin
{
players[player].status = status;
}
////////// Submit flags ////////// Submit flags
mapping(address => mapping(uint256 => bool)) Solves; // address -> challenge ID -> solved/not mapping(address => mapping(uint256 => bool)) Solves; // address -> challenge ID -> solved/not
uint256 public submission_success_count = 0; // For statistics uint256 public submission_success_count = 0; // For statistics
function SubmitFlag(Proof memory p, uint256 _submitFor) external onlyActive { function SubmitFlag(Proof memory p, uint256 _submitFor)
require(players[msg.sender].status == PlayerStatus.Verified, "You are not even playing"); external
onlyActive
{
require(
players[msg.sender].status == PlayerStatus.Verified,
"You are not even playing"
);
uint[13] memory inputs; uint256[13] memory inputs;
for(uint i =0 ; i < 8;++i){ for (uint256 i = 0; i < 8; ++i) {
inputs[i] = flags[_submitFor].flagHash.value[i]; inputs[i] = flags[_submitFor].flagHash.value[i];
} }
uint32[5] memory addr = addressToUint32(msg.sender); uint32[5] memory addr = addressToUint32(msg.sender);
for(uint i =0; i < 5;++i){ for (uint256 i = 0; i < 5; ++i) {
inputs[8 + i] = addr[i]; inputs[8 + i] = addr[i];
} }
bool valid = verifyTx(p,inputs);
assert(valid);
require(!Solves[msg.sender][_submitFor]); // can't submit the same flag multiple times
bool valid = verifyTx(p, inputs);
require(valid,"The proof is invalid");
require(!Solves[msg.sender][_submitFor],"You cannot submit your solution more than once"); // can't submit the same flag multiple times
Solves[msg.sender][_submitFor] = true; Solves[msg.sender][_submitFor] = true;
players[msg.sender].points += flags[_submitFor].points; players[msg.sender].points += flags[_submitFor].points;
players[msg.sender].points = players[msg.sender].points < volMaxPoints ? players[msg.sender].points : volMaxPoints; players[msg.sender].points = players[msg.sender].points < volMaxPoints
? players[msg.sender].points
: volMaxPoints;
if (flags[_submitFor].onlyFirstSolver) { if (flags[_submitFor].onlyFirstSolver) {
flags[_submitFor].points = 0; flags[_submitFor].points = 0;
@ -120,24 +143,36 @@ contract CCTF9 is Verifier{
} }
/// maps an address to unsigned int /// maps an address to unsigned int
function addressToUint32 (address _addr) public pure returns (uint32[5] memory) function addressToUint32(address _addr)
public
pure
returns (uint32[5] memory)
{ {
uint256 targetUint = uint256(uint160(_addr)); uint256 targetUint = uint256(uint160(_addr));
uint32[5] memory addr = [uint32(targetUint/4294967296**4), uint32[5] memory addr = [
uint32(targetUint / 4294967296**4),
uint32(targetUint / 4294967296**3), uint32(targetUint / 4294967296**3),
uint32(targetUint / 4294967296**2), uint32(targetUint / 4294967296**2),
uint32(targetUint / 4294967296), uint32(targetUint / 4294967296),
uint32(targetUint)]; uint32(targetUint)
];
return addr; return addr;
} }
////////// Check status, scores, etc ////////// Check status, scores, etc
function getPlayerStatus(address _player) external view returns (PlayerStatus) { function getPlayerStatus(address _player)
external
view
returns (PlayerStatus)
{
return players[_player].status; return players[_player].status;
} }
function getPlayerPoints(address _player) external view returns (uint256) { function getPlayerPoints(address _player) external view returns (uint256) {
return players[_player].points < volMaxPoints ? players[_player].points : volMaxPoints; return
players[_player].points < volMaxPoints
? players[_player].points
: volMaxPoints;
} }
function getSuccessfulSubmissionCount() external view returns (uint256) { function getSuccessfulSubmissionCount() external view returns (uint256) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@ -0,0 +1 @@
node_modules/

View File

@ -0,0 +1,71 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Flag Submission Page</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/web3/1.6.1/web3.min.js"
integrity="sha512-5erpERW8MxcHDF7Xea9eBQPiRtxbse70pFcaHJuOhdEBQeAxGQjUwgJbuBDWve+xP/u5IoJbKjyJk50qCnMD7A=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script type = "text/javascript" src="./src/logic.js"></script>
</head>
<body style="height:1500px">
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="#">Flag Submission Page</a>
</div>
<ul class="nav navbar-nav">
<li class="active"><a href="leaderboard.html">Leaderboard</a></li>
<li><a href="sendflag.html">Send Flag</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><button class="btn btn-default navbar-btn" id="metamask-login" type="button" style="margin-right:20px"><span class="glyphicon glyphicon-log-in"></span> Connect Wallet</button></li>
<!--<button class="btn btn-danger navbar-btn">Button</button>-->
</ul>
</div>
</nav>
<div class="container" style="margin-top:50px">
<h1>Players and leaderboard</h1>
<table class="table table-striped" style="width: 75%" align="center">
<thead>
<tr>
<th>Player</th>
<th>Status</th>
<th>Points</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<script type='module'>
let loginbutton = document.getElementById('metamask-login');
window.addEventListener("load", function () {
let accounts;
ethereum.request({method: 'eth_requestAccounts'}).then(accounts => {
if (accounts.length > 0) {
loginbutton.textContent = accounts[0];
loginbutton.disabled = true;
}
});
});
loginbutton.addEventListener('click', event => {
let account;
ethereum.request({method: 'eth_requestAccounts'}).then(accounts =>{
account = accounts[0];
loginbutton.textContent = account;
loginbutton.disabled = true;
})
})
</script>
</body>
</html>

View File

@ -0,0 +1,16 @@
{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"crypto-js": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz",
"integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw=="
},
"zokrates-js": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/zokrates-js/-/zokrates-js-1.1.4.tgz",
"integrity": "sha512-xvfwJqWCsglFpRPdOwYHNV1Ge/ygT3upWI/6xjX3S6t0+rXG0+50yA0ZGuaBgGNGGDbnSLY2n6YZmWKG4gu95g=="
}
}
}

View File

@ -0,0 +1,6 @@
{
"dependencies": {
"crypto-js": "^4.1.1",
"zokrates-js": "^1.1.4"
}
}

View File

@ -0,0 +1,115 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Flag Submission Page</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/web3/1.6.1/web3.min.js" integrity="sha512-5erpERW8MxcHDF7Xea9eBQPiRtxbse70pFcaHJuOhdEBQeAxGQjUwgJbuBDWve+xP/u5IoJbKjyJk50qCnMD7A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<!--<script src="dist/bundle.js"></script>-->
</head>
<body style="height:1500px">
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="#">Flag Submission Page</a>
</div>
<ul class="nav navbar-nav">
<li>
<a href="leaderboard.html">Leaderboard</a>
</li>
<li class="active">
<a href="sendflag.html">Send Flag</a>
</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li>
<button class="btn btn-default navbar-btn" id="metamask-login" type="button" style="margin-right:20px">
<span class="glyphicon glyphicon-log-in"></span>
Connect Wallet</button>
</li>
<!--<button class="btn btn-danger navbar-btn">Button</button>-->
</ul>
</div>
</nav>
<div class="container" style="margin-top:50px">
<h1>Send the flag</h1>
<form class="form-inline" id="flag-form" style="display:none">
<div class="form-group">
<label for="flagbox">Flag id:</label>
<input type="number" class="form-control" id="flagID">
</div>
<div class="form-group">
<label for="flagbox">Proof:</label>
<input type="text" class="form-control" id="flagbox">
</div>
<button id="submitFlagBtn" type="button" class="btn btn-default btn-warning">Submit Flag</button>
</form>
</div>
<script type='module'>
var web3js;
var accounts;
const contractAddress = "0x970e1d481CEDbeaeFE776275F0d1b4E1B301bB3b";
window.addEventListener("load", function () {
let loginbutton = document.getElementById('metamask-login');
ethereum.request({method: 'eth_requestAccounts'}).then(acc => {
console.log(acc);
accounts = acc;
if (acc.length > 0) {
document.getElementById('flag-form').style.display = "block";
loginbutton.textContent = accounts[0];
loginbutton.disabled = true;
}
});
});
let loginbutton = document.getElementById('metamask-login');
loginbutton.addEventListener('click', event => {
let account;
ethereum.request({method: 'eth_requestAccounts'}).then(accounts => {
account = accounts[0];
document.getElementById('flag-form').style.display = "block";
loginbutton.textContent = account;
loginbutton.disabled = true;
})
})
let flagID = document.getElementById('flagID');
let flagbox = document.getElementById('flagbox');
let submitFlagBtn = document.getElementById('submitFlagBtn');
submitFlagBtn.addEventListener('click', event => {
contract.methods.SubmitFlag(JSON.parse(flagbox.value),flagID.value).send( { from: accounts[0] });
})
var contactABI;
var contract;
async function getAbi() {
fetch("/abi.json").then(a => a.json().then(abi => {
contactABI = abi
contract = new web3js.eth.Contract(contactABI, contractAddress);
}));
}
window.addEventListener("load", function () {
if (typeof web3 !== "undefined") {
web3js = new Web3(web3.currentProvider);
// console.log(web3);
getAbi();
} else {
console.log("No web3? You should consider trying MetaMask!");
}
});
</script>
</body>
</html>

View File

@ -0,0 +1,138 @@
const CCTF_STARTED_EVENT = 'CCTFStarted';
const PLAYER_REGISTERED_EVENT = 'PlayerRegistered';
const FLAG_ADDED_EVENT = 'FlagAdded';
const FLAG_SOLVED_EVENT = 'FlagSolved';
const FLAG_REMOVED_EVENT = 'FlagRemoved';
var FILTER_OPTS = {
filter: {
value: [],
},
fromBlock: 0
};
var web3;
var web3Listener;
const contractAddress = '0xf51B62641D4c472E6EC0DB7Ea50382aE66Ff092e';
var contract;
var contractAbi;
var currentBlock = 0;
function loadRegisteredPlayers(events){
registerContractEventListeners();
}
function appendRow(table, registrationEvent){
let returnValues = registrationEvent['returnValues'];
let address = returnValues['playerAddress'];
let row = document.createElement("tr");
let addressCol = document.createElement("td");
let statusCol = document.createElement("td");
let pointsCol = document.createElement("td");
row.id = address;
addressCol.id = `${address}.addressCol`
addressCol.innerText = String(address);
statusCol.id = `${address}.statusCol`
getPlayerStatus(address).then(result => updatePlayerStatus(address, result));
pointsCol.id = `${address}.pointsCol`
getPlayerPoints(address).then(result => updatePlayerPoints(address, result));
row.appendChild(addressCol);
row.appendChild(statusCol);
row.appendChild(pointsCol);
table.appendChild(row);
}
function updatePlayerStatus(address, statusEnum){
let statusString = "";
switch(statusEnum){
case '0':
statusString = "Unverified";
break;
case '1':
statusString = "Verified";
break;
case '2':
statusString = "Banned";
break;
}
let statusCol = document.getElementById(`${address}.statusCol`);
statusCol.innerText = statusString;
}
function updatePlayerPoints(address, points){
let pointsCol = document.getElementById(`${address}.pointsCol`);
pointsCol.innerText = points;
}
async function getPlayerStatus(address){
return await contract.methods.getPlayerStatus(address).call();
}
async function getPlayerPoints(address){
return await contract.methods.getPlayerPoints(address).call();
}
async function getPastEvents(eventName, callback) {
await contract.getPastEvents(eventName,
{
fromBlock: 0,
toBlock: 'latest',
},
(err, events) => {
callback(events);
});
};
window.addEventListener("load", function () {
if (typeof web3 !== 'undefined') {
web3 = new Web3(web3.currentProvider);
web3Listener = new Web3('wss://eth.toldi.eu/ws');
window.fetch('./abi.json').then((response) => response.json()).then((data) => {
contractAbi = data;
contract = new web3Listener.eth.Contract(contractAbi, contractAddress);
getPastEvents(PLAYER_REGISTERED_EVENT, loadRegisteredPlayers);
});
} else {
console.log("No web3? You should consider trying MetaMask!");
}
});
function registerContractEventListeners(){
contract.events.FlagSolved({
fromBlock: 0
}, function(error, event){ console.log(event); })
.on('data', function(event){
let address = event['returnValues']['solver'];
getPlayerPoints(address).then(result => updatePlayerPoints(address, result));
console.log('data ' + event)
})
.on('changed', function(event){
console.log('changed ' + event)
})
.on('connected', function(event){
console.log('connected ' + event)
})
.on('error', console.error);
contract.events.PlayerRegistered({
fromBlock: 0
}, function(error, event){ console.log(event); })
.on('data', function(event){
let table = document.querySelector('tbody');
appendRow(table, event);
console.log('data ' + event)
})
.on('changed', function(event){
console.log('changed ' + event)
})
.on('connected', function(event){
console.log('connected ' + event)
})
.on('error', console.error);
}

View File

@ -0,0 +1,103 @@
import sha256 from 'crypto-js/sha256.js';
import { initialize } from 'zokrates-js';
window.addEventListener('load', () => {
document.getElementById('submitFlagBtn')
.addEventListener('click', () => {
submitFlag(document.getElementById('flagbox').value);
});
});
const ZEROSTR = '0';
/* Converts a string into a stringified hexadecimal number */
function str2hex(string) {
return string.split('')
.map(c => c.charCodeAt(0).toString(16).padStart(2, '0'))
.join('');
}
/* Converts a stringified hexadecimal number into a string */
function hex2str(hex) {
return hex.split(/(\w\w)/g)
.filter(p => !!p)
.map(c => String.fromCharCode(parseInt(c, 16)))
.join('');
}
/* Prefix each element of a string a array with 0x */
function map_0xprefix(arr) {
return arr.map(s => '0x' + s);
}
/* Convert a string to a byte array */
function str2asciiarr(s) {
let charCodeArr = [];
for (let i = 0; i < s.length; i++) {
let code = s.charCodeAt(i);
charCodeArr.push(code);
}
return charCodeArr;
}
/* Convert a byte array to a string */
function asciiarr2str(arr) {
return String.fromCharCode(...arr);
}
/*
* Expand a simple number into a stringified u32[8] array for ZoKrates.
*/
function expand_number(number) {
const revstr = s => Array.from(s).reverse().join('');
const split8 = s => s.match(/.{1,8}/g);
const parts = split8(revstr(number.toString(16)))
.map(p => '0x' + revstr(p))
.map(p => parseInt(p, 16).toString())
.reverse();
return [...Array(8 - parts.length).fill(ZEROSTR), ...parts];
}
const proving_key = await (await fetch('/zok_proving.key')).text();
const program = await (await fetch('/zok_program')).arrayBuffer();
const abi = await (await fetch('/zok_abi.json')).json();
/* Get the proving key from the local server */
const PROVING_KEY_URI = 'http://localhost:8080/proving.key';
const proving_key = await (await fetch(PROVING_KEY_URI)).text();
const artefacts = { program: new Uint8Array(program), abi: abi };
console.log(artefacts);
function submitFlag(flag) {
initialize().then((defaultProvider) => {
const zokProvider = defaultProvider.withOptions({
backend: 'ark',
scheme: 'gm17',
});
const flag_ascii = str2asciiarr(flag);
const flag_ascii_padded = flag_ascii.concat(new Array(64 - flag_ascii.length).fill(0));
const flag_padded = asciiarr2str(flag_ascii_padded);
const hash = sha256(flag_padded).toString(); /* TODO get this from contract */
const addr = 'f6e3a49fca2eb57f286d516fa60154ebfd10d5ad'; /* TODO get this from metamask */
const flag_split = map_0xprefix(str2hex(flag).match(/.{1,2}/g));
const flag_split_padded = flag_split.concat(new Array(64 - flag_split.length).fill('0x0'));
const hash_split = map_0xprefix(hash.match(/.{1,8}/g));
const addr_split = map_0xprefix(addr.match(/.{1,8}/g));
// witness computation
console.log('witness');
const { witness, output } = zokProvider.computeWitness(artefacts, [hash_split, addr_split, flag_split_padded]);
// generate proof
console.log('proof');
const proof = zokProvider.generateProof(artefacts.program, witness, proving_key);
console.log(proof);
});
};

View File

@ -0,0 +1,16 @@
const path = require('path');
module.exports = {
entry: './src/main.mjs',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
experiments: {
syncWebAssembly: true,
topLevelAwait: true,
},
resolve: {
fullySpecified: false,
},
};

View File

@ -0,0 +1,37 @@
{
"inputs": [
{
"name": "hash",
"public": true,
"type": "array",
"components": {
"size": 8,
"type": "u32"
}
},
{
"name": "address",
"public": true,
"type": "array",
"components": {
"size": 5,
"type": "u32"
}
},
{
"name": "flag",
"public": false,
"type": "array",
"components": {
"size": 64,
"type": "u8"
}
}
],
"output": {
"type": "tuple",
"components": {
"elements": []
}
}
}

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@
kotlin.code.style=official

Binary file not shown.

View File

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

240
CCTF_Solutions_main/gradlew vendored 100755
View File

@ -0,0 +1,240 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

91
CCTF_Solutions_main/gradlew.bat vendored 100644
View File

@ -0,0 +1,91 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

Binary file not shown.

View File

@ -1,11 +1,10 @@
import "hashes/sha256/sha256Padded.zok" as sha256; import "hashes/sha256/sha256Padded.zok" as sha256;
from "utils/casts.zok" import cast; from "utils/casts.zok" import cast;
def main(public u32[5] address,private u8[64] flag) { def main(public u32[5] address,private u8[64] flag) -> u32[8] {
u8[20] addr8 = cast(address); u8[20] addr8 = cast(address);
u8[84] forHash = [...addr8,...flag];
u32[8] genHash = sha256(flag); u32[8] genHash = sha256(flag);
log("Hash: {} {} {} {} {} {} {} {}",genHash[0],genHash[1],genHash[2],genHash[3],genHash[4],genHash[5],genHash[6],genHash[7]); log("Hash: {} {} {} {} {} {} {} {}",genHash[0],genHash[1],genHash[2],genHash[3],genHash[4],genHash[5],genHash[6],genHash[7]);
//assert(genHash == hash); //assert(genHash == hash);
return; return genHash;
} }

Binary file not shown.

View File

@ -0,0 +1,3 @@
{
"lockfileVersion": 1
}

View File

@ -0,0 +1,40 @@
{
"scheme": "gm17",
"curve": "bn128",
"proof": {
"a": [
"0x2e2f42c53f6798af69959d12e5430c7ad95e996fb70cb8c36e4a231b2ac03c0b",
"0x2a5c123e0d79b4bba8737b9e39907143e64262f2ba57dbbe45d452d854fc5738"
],
"b": [
[
"0x0dcfbdd263e303b9b91a4d9ed7a2744c813df33ccbe0084137afba2fb4ad5a44",
"0x19913fbe1ee4e378a7fa0bb42cc6aeafdae7ae3fb84987cee06670a9f3587703"
],
[
"0x089188fa6a00185fefe01d7b8a36cdeb53c78ad8ca443dc65d550622486d5932",
"0x143a377418de72c160aeb2b25bbec57625ff725ea42576caaea29776572fec16"
]
],
"c": [
"0x0bd195f90adbfb7221a08ddbbf1cea3d8e1bd907399765534cdd0bf441edf816",
"0x2b100a24fb234a4222234ef1063db3aeeea3b220972f6530abec48444c35405b"
]
},
"inputs": [
"0x0000000000000000000000000000000000000000000000000000000070f32e51",
"0x00000000000000000000000000000000000000000000000000000000f57163d9",
"0x000000000000000000000000000000000000000000000000000000009dbce41a",
"0x000000000000000000000000000000000000000000000000000000002e52e481",
"0x00000000000000000000000000000000000000000000000000000000b87d45c4",
"0x000000000000000000000000000000000000000000000000000000007999ce35",
"0x000000000000000000000000000000000000000000000000000000003063db54",
"0x00000000000000000000000000000000000000000000000000000000ad1fb776",
"0x000000000000000000000000000000000000000000000000000000005b38da6a",
"0x00000000000000000000000000000000000000000000000000000000701c5685",
"0x0000000000000000000000000000000000000000000000000000000045dcfcb0",
"0x000000000000000000000000000000000000000000000000000000003fcb875f",
"0x0000000000000000000000000000000000000000000000000000000056beddc4"
]
}

View File

@ -3,7 +3,6 @@ from "utils/casts.zok" import cast;
def main(public u32[8] hash,public u32[5] address,private u8[64] flag){ def main(public u32[8] hash,public u32[5] address,private u8[64] flag){
u8[20] addr8 = cast(address); u8[20] addr8 = cast(address);
u8[84] forHash = [...addr8,...flag];
u32[8] genHash = sha256(flag); u32[8] genHash = sha256(flag);
log("Hash: {} {} {} {} {} {} {} {}",genHash[0],genHash[1],genHash[2],genHash[3],genHash[4],genHash[5],genHash[6],genHash[7]); log("Hash: {} {} {} {} {} {} {} {}",genHash[0],genHash[1],genHash[2],genHash[3],genHash[4],genHash[5],genHash[6],genHash[7]);
assert(genHash == hash); assert(genHash == hash);

View File

@ -0,0 +1,10 @@
/*
* This file was generated by the Gradle 'init' task.
*
* The settings file is used to specify which projects to include in your build.
*
* Detailed information about configuring a multi-project build in Gradle can be found
* in the user manual at https://docs.gradle.org/7.5.1/userguide/multi_project_builds.html
*/
rootProject.name = "PolkadotCCTFChallange"

View File

@ -0,0 +1,112 @@
package hu.bmeta.cctf
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.*
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import androidx.compose.ui.window.rememberWindowState
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
fun main() {
Zokrates.initZokrates()
application {
Window(
onCloseRequest = ::exitApplication,
title = "ZoKrates CTF Proof generator",
state = rememberWindowState(width = 300.dp, height = 300.dp)
) {
val count = remember { mutableStateOf(0) }
val address = remember { mutableStateOf("") }
val flag = remember { mutableStateOf("") }
val enabled = remember { mutableStateOf(true) }
MaterialTheme {
Column(Modifier.fillMaxSize().padding(5.dp), Arrangement.spacedBy(5.dp)) {
Text("ZoKrates Proof generator")
OutlinedTextField(address.value, {
address.value = it
}, placeholder = {
Text("Address")
})
OutlinedTextField(flag.value, {
flag.value = it
}, placeholder = {
Text("Flag")
})
Button(modifier = Modifier.align(Alignment.CenterHorizontally),
onClick = {
enabled.value = false
val address_ch = address.value.replace("0x","").chunked(8).map { java.lang.Long.parseLong(it.uppercase(),16).toString() }
val message = flag.value.map { it.code.toByte().toInt().toString() }.toMutableList()
for (i in 0 until 64 - message.size) {
message.add("0")
}
val hashArgs = mutableListOf<String>()
hashArgs.addAll(address_ch)
hashArgs.addAll(message)
GlobalScope.launch(Dispatchers.IO) {
Zokrates.compile("hash.zok","hash")
val result = Zokrates.computeWithness(hashArgs,"hash")
println(result)
val zkpArgs = mutableListOf<String>()
zkpArgs.addAll(result)
zkpArgs.addAll(address_ch)
zkpArgs.addAll(message)
Zokrates.compile("root.zok")
val results2 = Zokrates.computeWithness(zkpArgs)
println(results2)
Zokrates.generateProof(scheme = Zokrates.ProvingScheme.GM17)
println("Proof done!")
}
enabled.value = true
}, enabled = enabled.value) {
Text("Generate Proof!")
}
if(enabled.value.not()){
CircularProgressIndicator()
}
}
}
}
}
}
/*fun main() {
val address = listOf("1530452586","1880905349","1172110512","1070303071","1455349188")
val message = mutableListOf("116", "101", "115","116","0")
val random = Random()
for(i in 0 until 64-message.size){
message.add("0")
}
Zokrates.initZokrates()
Zokrates.compile("hash.zok","hash")
val hashArgs = mutableListOf<String>()
hashArgs.addAll(address)
hashArgs.addAll(message)
val result = Zokrates.computeWithness(hashArgs,"hash")
println(result)
val zkpArgs = mutableListOf<String>()
zkpArgs.addAll(result)
zkpArgs.addAll(address)
zkpArgs.addAll(message)
Zokrates.compile("root.zok")
val results2 = Zokrates.computeWithness(zkpArgs)
println(results2)
Zokrates.generateProof(scheme = Zokrates.ProvingScheme.GM17)
Zokrates.verify()
}*/

View File

@ -0,0 +1,144 @@
package hu.bmeta.cctf
import java.io.File
import java.lang.RuntimeException
import java.net.URL
object Zokrates {
enum class ProvingScheme{
G16,GM17
}
private var isInitDone = false
private lateinit var zokratesPath: String
private fun checkInit() {
if (isInitDone.not()) {
throw RuntimeException("")
}
}
private fun installZokrates() {
val tmp = File(System.getProperty("user.dir") + "/tmp").also { file ->
file.mkdir()
}
val dlFile = File(tmp, "zokrates.sh").also {
it.writeText(URL("https://raw.githubusercontent.com/ZoKrates/ZoKrates/master/scripts/one_liner.sh").readText())
it.setExecutable(true)
}
val pb = ProcessBuilder(dlFile.absoluteFile.absolutePath.toString(), "--to", ".zokrates")
val process = pb.start()
val exitCode = process.waitFor()
if (exitCode != 0) {
throw RuntimeException("")
}
dlFile.delete()
tmp.delete()
}
fun initZokrates() {
if (File(System.getProperty("user.dir") + "/.zokrates/bin/zokrates").exists().not()) {
installZokrates()
}
zokratesPath = System.getProperty("user.dir") + "/.zokrates/bin/zokrates"
isInitDone = true
}
fun compile(path: String, outPut: String = "out") {
checkInit()
compile(File(path), outPut)
}
fun compile(file: File, outPut: String = "out") {
checkInit()
val pb = ProcessBuilder(zokratesPath, "compile", "-i", file.absoluteFile.absolutePath.toString(), "-o", outPut)
val process = pb.start()
val exitCode = process.waitFor()
if (exitCode != 0) {
val output = process.inputStream.bufferedReader().use { it.readText() }
throw throw RuntimeException(output)
}
}
fun computeWithness(args: List<String>, input: String = "out", output: String = "witness"): List<String> {
checkInit()
val sb = StringBuilder()
var prefix = ""
args.forEach {
sb.append(prefix)
prefix = " "
sb.append(it)
}
println(sb.toString())
val command = mutableListOf(zokratesPath, "compute-witness", "-i", input, "-o", output, "-a").apply {
addAll(args)
}
val processBuilder = ProcessBuilder(command)
val process = processBuilder.start()
val exitCode = process.waitFor()
if (exitCode != 0) {
val output1 = process.inputStream.bufferedReader().use { it.readText() }
println("Error $output1")
throw throw RuntimeException(output1)
}
return readWitness(output)
}
fun readWitness(witness: String = "witness"): List<String> {
return File(witness).readText().split("\n").filter { it.startsWith("~out_") }.sorted().map { it.split(' ')[1] }
}
fun setup(input: String = "out") {
val pb = ProcessBuilder(zokratesPath, "setup", "-i", input)
val process = pb.start()
val exitCode = process.waitFor()
if (exitCode != 0) {
val output = process.inputStream.bufferedReader().use { it.readText() }
println("Error $output")
throw RuntimeException(output)
}
}
fun generateProof(input: String = "out", witness: String = "witness", proof: String = "proof.json", scheme: ProvingScheme = ProvingScheme.G16) {
val ps = if (scheme == ProvingScheme.G16) "g16" else "gm17"
val pb = ProcessBuilder(zokratesPath, "generate-proof", "-i", input, "-w", witness, "-j", proof,"-s",ps)
val process = pb.start()
val exitCode = process.waitFor()
if (exitCode != 0) {
val output = process.inputStream.bufferedReader().use { it.readText() }
println("Error $output")
throw RuntimeException(output)
}
}
fun verify() {
val pb = ProcessBuilder(zokratesPath, "verify")
val process = pb.start()
val exitCode = process.waitFor()
if (exitCode != 0) {
val output = process.inputStream.bufferedReader().use { it.readText() }
println("Error $output")
throw RuntimeException(output)
}
}
fun exportVerifier(input: String = "out", output: String = "verifier.sol") {
val pb = ProcessBuilder(zokratesPath, "export-verifier")
val process = pb.start()
val exitCode = process.waitFor()
if (exitCode != 0) {
val output = process.inputStream.bufferedReader().use { it.readText() }
println("Error $output")
throw RuntimeException(output)
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,54 @@
# Kusamaverse Proposal
- Authors: {Péter Bertalan, Nagymarosi Máté, Toldi Balázs, Debreczeni Máté}
- Status: {Draft}
- Created: {2022-12-07}
## Summary
Our first proposal is to create more comprehensive learning materials, because we found the
UX clarity to be somewhat lacking. It shall convey the values and objectives of the platform
clearly. Kusamaverse needs to improve its authentication and authorization capabilities.
## Motivation
In our experience, Kusamaverse is hard to understand for new users. The user's first impressions are
important, but in the current version it is formed by marketing materials and forms. The user is not
presented with what the platform provides and how it can be used.
## Specification
proposed changes:
1. Game-like guided learning experience
2. Guide/Mascot character
3. Written materials, stories, and resources
4. The integration of authorization and authentication
### 1. Game-like guided learning experience
Most games start with a introductory quest, which eases the player into the in-game environment.
Kusamaverse would greatly benefit from such an addition. The user would receive valuable context
via a story told by an "in-game" character.
### 2. Guide/Mascot character
A fun mascot that could introduce the player to the Kusamaverse environment. It would always
be avaliable to offer guidance, especially during the first steps. Instead of forms, initial
user preferences could be attained through conversation with the mascot. The objective is to
make the process as fun as possible.
### 3. Written materials, stories, resources
Even after deliberate searching, information about the Kusamaverse was hard to find. For those
that are interested in the background, easily digestable materials should be made more avaliable.
### 4. The integration of authorization and authentication
To the best of our knowledge, currently there is no way to ensure that only select people can
attend a space. Collaboration can be enhanced by limiting unwanted access, which can degrade
the experience.

23
README.md 100644
View File

@ -0,0 +1,23 @@
# BMEta solutions
## Proposals
### 1. Kusamaverse
Our proposal focuses on usability and UX.
It can be found [here](./Kusamaverse_Proposal).
### 2. RMRK
We propose a few [improvements and enhancements ](./RMRK_Proposal) to
the ticketing sytem use case of RMRK.
## Main track: CCTF
We were presented with a vulnerable Capture The Flag
smart contract, which we fixed by the help of ZKPs.
Using ZKPs allows some unique features and it could
help provide the community with new and exciting
ways to organize CTF events. The solution can be found
[here](./CCTF_Solutions_main).

View File

@ -0,0 +1,47 @@
# RMRK Proposal
- Authors: {Péter Bertalan, Nagymarosi Máté, Toldi Balázs, Debreczeni Máté}
- Status: {Draft}
- Created: {2022-12-07}
## Summary
The RMRK documentation currently includes a use case description of a
ticketing system, built on revealable and multi-resource NFTs.
We extend this application with a few additional ideas that take
advantage of RMRKs toolbox.
## Motivation
Classic solutions to ticketing come with their drawbacks. RMRK offers
new and versatile building blocks for making NFTs more powerful and
rich, while also eliminating a lot of the downsides. However, one aspect
not addressed by the current draft is scalping. Scalping is the practice
of buying large amounts of a scarce asset to resell it at a higher
price. Even centralized systems struggle to combat this issue. Our
proposal offers a possible mitigation.
## Specification
### Multi-use tickets
Instead of following the traditional scheme of one ticket per event, a
ticket could possibly be valid for a number of select or arbitrary
events. In this case, ticket validation would entail verifying a counter
or the presence of a specific PoA besides the secret keyword.
### A loyalty system
Dedicated fans deserve extra perks for their support. Our proposal
utilizes multi-resource NFTs to enable customers to attain a collection
of PoAs as part of a single NFT. This could be used to set up the basis
of a loyalty system. Owners of such collections would be able to
participate in presales, ensuring that they dont miss out on their
favourite event.
### Scalping mitigation
The loyalty system outlined already offers some level of protection
against scalpers, but we would further enhance this with the use of
Verifiable Credentials. These could be issued by a trusted organization
and the amount of tickets per capita could be limited.

3
package-lock.json generated 100644
View File

@ -0,0 +1,3 @@
{
"lockfileVersion": 1
}