Remove python code
This commit is contained in:
committed by
Jacob Evans
parent
b7fcc70cd9
commit
4f7d6132fc
@@ -1,8 +0,0 @@
|
||||
|
||||
# Running Dev
|
||||
|
||||
start backend (see `backend/README.md`)
|
||||
`npm install`
|
||||
`npm run dev`
|
||||
|
||||
go to http://localhost:3000/vote.html
|
||||
1
packages/v0x/backend/.gitignore
vendored
1
packages/v0x/backend/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
env
|
||||
@@ -1,14 +0,0 @@
|
||||
|
||||
# Requirements
|
||||
|
||||
- python v3.6+
|
||||
- virtualenv
|
||||
- postgresql
|
||||
|
||||
# Starting
|
||||
|
||||
### Local dev
|
||||
|
||||
`./run.sh`
|
||||
|
||||
On Linux you will have to create a `v0x` database under the current user manually, or modity the `DATABASE_URL` variable in the script.
|
||||
@@ -1,6 +0,0 @@
|
||||
aiohttp==3.5.4
|
||||
asyncpg==0.18.3
|
||||
coloredlogs==10.0
|
||||
coloredlogs==10.0
|
||||
eth-abi==1.3.0
|
||||
ethereum==2.3.2
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
export DATABASE_URL="postgresql:///v0x"
|
||||
export DATABASE_SSL_ENABLED=0
|
||||
export PORT=5000
|
||||
|
||||
if ! pg_isready -q; then
|
||||
echo "Postgresql isn't running! you need to start it..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! psql $DATABASE_URL -c "SELECT 1" > /dev/null; then
|
||||
createdb v0x
|
||||
fi
|
||||
|
||||
if [ ! -d env ]; then
|
||||
virtualenv -p python3.7 env
|
||||
env/bin/pip install -r requirements.txt
|
||||
fi
|
||||
|
||||
env/bin/python -m v0x
|
||||
@@ -1,13 +0,0 @@
|
||||
import coloredlogs
|
||||
import logging
|
||||
import os
|
||||
from aiohttp import web
|
||||
from v0x.app import create_app
|
||||
|
||||
if __name__ == '__main__':
|
||||
coloredlogs.install()
|
||||
logging.getLogger('aiohttp.access').setLevel(logging.DEBUG)
|
||||
logging.getLogger('aiohttp.server').setLevel(logging.DEBUG)
|
||||
|
||||
app = create_app()
|
||||
web.run_app(app, port=os.environ.get('PORT', 5000))
|
||||
@@ -1,10 +0,0 @@
|
||||
from aiohttp import web
|
||||
from v0x.database import init_database
|
||||
from v0x.handlers import routes
|
||||
|
||||
|
||||
def create_app():
|
||||
app = web.Application()
|
||||
app.add_routes(routes)
|
||||
app.on_startup.append(init_database)
|
||||
return app
|
||||
@@ -1,45 +0,0 @@
|
||||
import asyncpg
|
||||
import os
|
||||
import ssl
|
||||
from aiohttp import web
|
||||
from distutils.util import strtobool
|
||||
|
||||
SSL_CTX = ssl.create_default_context()
|
||||
SSL_CTX.check_hostname = False
|
||||
SSL_CTX.verify_mode = ssl.CERT_NONE
|
||||
|
||||
async def init_database(app: web.Application):
|
||||
pool = await asyncpg.create_pool(
|
||||
dsn=os.environ.get('DATABASE_URL'),
|
||||
ssl=SSL_CTX if strtobool(os.environ.get('DATABASE_SSL_ENABLED', '0')) else None)
|
||||
async with pool.acquire() as con:
|
||||
await con.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS votes (
|
||||
campaign_id BIGINT,
|
||||
voter_address VARCHAR, -- hex
|
||||
preference VARCHAR,
|
||||
comment VARCHAR,
|
||||
zrx VARCHAR, -- hex
|
||||
timestamp TIMESTAMP WITHOUT TIME ZONE DEFAULT (NOW() AT TIME ZONE 'UTC'),
|
||||
signature VARCHAR, -- hex
|
||||
|
||||
PRIMARY KEY (campaign_id, voter_address)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS campaigns (
|
||||
campaign_id BIGSERIAL PRIMARY KEY,
|
||||
open_date TIMESTAMP WITHOUT TIME ZONE,
|
||||
close_date TIMESTAMP WITHOUT TIME ZONE,
|
||||
open_block BIGINT
|
||||
);
|
||||
""")
|
||||
# TODO: make an admin way to add this
|
||||
from datetime import datetime
|
||||
await con.execute(
|
||||
"""
|
||||
INSERT INTO campaigns (campaign_id, open_date, close_date, open_block)
|
||||
VALUES ($1, $2, $3, $4) ON CONFLICT (campaign_id) DO NOTHING;
|
||||
""",
|
||||
1, datetime.utcnow(), datetime.utcnow(), 0x6be328)
|
||||
app['pool'] = pool
|
||||
@@ -1,112 +0,0 @@
|
||||
from aiohttp import web
|
||||
from v0x.vote_utils import verify_signature
|
||||
|
||||
|
||||
routes = web.RouteTableDef()
|
||||
|
||||
|
||||
@routes.post('/api/vote')
|
||||
async def vote(request: web.Request) -> web.Response:
|
||||
data = await request.json()
|
||||
try:
|
||||
campaign_id = int(data['campaignId'])
|
||||
preference = data['preference']
|
||||
voter_address = data['voterAddress']
|
||||
signature = data['signature']
|
||||
comment = data.get('comment', '').strip()
|
||||
except KeyError as ke:
|
||||
return web.json_response({'error': 'Bad Arguments',
|
||||
'missingKeys': list(ke.args)}, status=400)
|
||||
except AttributeError:
|
||||
# raised if comments is not a string
|
||||
return web.json_response({
|
||||
'error': 'Bad Arguments',
|
||||
'invalidKeys': ['comment']
|
||||
}, status=400)
|
||||
except ValueError:
|
||||
# raised if campaignId is not convertable to an integer
|
||||
return web.json_response({
|
||||
'error': 'Bad Arguments',
|
||||
'invalidKeys': ['campaignId']
|
||||
}, status=400)
|
||||
|
||||
# signature validation
|
||||
try:
|
||||
signature_bytes = bytes.fromhex(signature[2:])
|
||||
if len(signature_bytes) != 65:
|
||||
return web.json_response({
|
||||
'error': 'Bad Arguments',
|
||||
'invalidKeys': ['signature']
|
||||
}, status=400)
|
||||
except (ValueError, TypeError):
|
||||
return web.json_response({
|
||||
'error': 'Bad Arguments',
|
||||
'invalidKeys': ['signature']
|
||||
}, status=400)
|
||||
|
||||
# address validation
|
||||
try:
|
||||
if len(bytes.fromhex(voter_address[2:])) != 20:
|
||||
return web.json_response({
|
||||
'error': 'Bad Arguments',
|
||||
'invalidKeys': ['voterAddress']
|
||||
}, status=400)
|
||||
voter_address = voter_address.lower()
|
||||
except (ValueError, TypeError):
|
||||
return web.json_response({
|
||||
'error': 'Bad Arguments',
|
||||
'invalidKeys': ['voterAddress']
|
||||
}, status=400)
|
||||
|
||||
# validate preference
|
||||
if preference != 'Yes' and preference != 'No':
|
||||
return web.json_response({
|
||||
'error': 'Bad Arguments',
|
||||
'invalidKeys': ['preference']
|
||||
}, status=400)
|
||||
|
||||
if not verify_signature(campaign_id, voter_address, preference, signature_bytes):
|
||||
return web.json_response({
|
||||
'error': 'Bad Arguments',
|
||||
'message': 'signature does not match voterAddress'
|
||||
}, status=400)
|
||||
|
||||
# TODO: verify campaign can be voted on now
|
||||
|
||||
async with request.app['pool'].acquire() as con:
|
||||
await con.execute("INSERT INTO votes "
|
||||
"(campaign_id, voter_address, preference, comment, signature) "
|
||||
"VALUES ($1, $2, $3, $4, $5) "
|
||||
"ON CONFLICT (campaign_id, voter_address) "
|
||||
"DO UPDATE SET "
|
||||
"preference = EXCLUDED.preference, "
|
||||
"comment = EXCLUDED.comment, "
|
||||
"signature = EXCLUDED.signature",
|
||||
campaign_id, voter_address, preference, comment, signature)
|
||||
|
||||
return web.json_response({'ok': True})
|
||||
|
||||
|
||||
@routes.get('/api/votes/{campaign_id}')
|
||||
async def list_votes(request: web.Request) -> web.Response:
|
||||
try:
|
||||
campaign_id = int(request.match_info['campaign_id'])
|
||||
except ValueError:
|
||||
return web.json_response({
|
||||
'error': 'Bad Arguments',
|
||||
'message': 'Invalid campaign Id'
|
||||
}, status=400)
|
||||
async with request.app['pool'].acquire() as con:
|
||||
votes = await con.fetch("SELECT * FROM votes "
|
||||
"WHERE campaign_id = $1 "
|
||||
"ORDER BY timestamp DESC",
|
||||
campaign_id)
|
||||
return web.json_response({'votes': [
|
||||
{
|
||||
'voterAddress': v['voter_address'],
|
||||
'preference': v['preference'],
|
||||
'comment': v['comment'],
|
||||
'timestamp': v['timestamp'].isoformat() + "Z",
|
||||
'zrx': v['zrx']
|
||||
} for v in votes
|
||||
]})
|
||||
@@ -1,44 +0,0 @@
|
||||
from eth_abi import encode_abi
|
||||
from ethereum.utils import sha3 as keccak256, encode_hex, safe_ord, big_endian_to_int, ecrecover_to_pub
|
||||
|
||||
EIP191_HEADER = b"\x19\x01"
|
||||
EIP712_DOMAIN_NAME = b"0x V0x"
|
||||
EIP712_DOMAIN_VERSION = b"1"
|
||||
EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = keccak256(
|
||||
b"EIP712Domain(" +
|
||||
b"string name," +
|
||||
b"string version" +
|
||||
b")")
|
||||
EIP712_DOMAIN_HASH = keccak256(encode_abi(
|
||||
['bytes32', 'bytes32', 'bytes32'],
|
||||
[EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH,
|
||||
keccak256(EIP712_DOMAIN_NAME),
|
||||
keccak256(EIP712_DOMAIN_VERSION)]))
|
||||
EIP712_VOTE_TYPE_HASH = keccak256(
|
||||
b"Vote(" +
|
||||
b"uint256 campaignId," +
|
||||
b"string preference" +
|
||||
b")")
|
||||
|
||||
def verify_signature(campaign_id, voter_address, preference, signature):
|
||||
# encode and hash vote
|
||||
encoded = encode_abi(
|
||||
['bytes32', 'uint256', 'bytes32'],
|
||||
[EIP712_VOTE_TYPE_HASH, campaign_id,
|
||||
keccak256(preference.encode('utf-8'))])
|
||||
hash_struct = keccak256(encoded)
|
||||
hashed = keccak256(EIP191_HEADER +
|
||||
EIP712_DOMAIN_HASH +
|
||||
hash_struct)
|
||||
|
||||
# ecrecover
|
||||
v = safe_ord(signature[64])
|
||||
r = big_endian_to_int(signature[0:32])
|
||||
s = big_endian_to_int(signature[32:64])
|
||||
if v == 0 or v == 1:
|
||||
v += 27
|
||||
|
||||
pub = ecrecover_to_pub(hashed, v, r, s)
|
||||
|
||||
recovered_address = '0x' + encode_hex(keccak256(pub)[-20:])
|
||||
return recovered_address == voter_address
|
||||
4756
packages/v0x/package-lock.json
generated
4756
packages/v0x/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "v0x",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"dev": "webpack-dev-server --mode=development",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"webpack": "^4.26.0",
|
||||
"webpack-cli": "^3.1.2",
|
||||
"webpack-dev-server": "^3.1.10"
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
Vote:
|
||||
<button onClick="vote('Yes')">Yes</button>
|
||||
<button onClick="vote('No')">No</button>
|
||||
Comment: <textarea id="comment"></textarea>
|
||||
<br/>
|
||||
<pre id="comments"></pre>
|
||||
<script type="text/javascript" src="vote.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,74 +0,0 @@
|
||||
// https://gist.github.com/bitpshr/076b164843f0414077164fe7fe3278d9
|
||||
// use the whole thing here preferably
|
||||
window.addEventListener('load', function() {
|
||||
console.log('...');
|
||||
if (window.ethereum) {
|
||||
window.web3 = new Web3(ethereum);
|
||||
ethereum.enable();
|
||||
}
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', '/api/votes/1', true);
|
||||
xhr.addEventListener("load", function() {
|
||||
document.getElementById("comments").innerHTML = xhr.responseText;
|
||||
});
|
||||
xhr.send();
|
||||
});
|
||||
|
||||
function signVote(preference) {
|
||||
let domainType = [
|
||||
{ name: "name", type: "string" },
|
||||
{ name: "version", type: "string" },
|
||||
];
|
||||
let voteType = [
|
||||
{ name: "campaignId", type: "uint256" },
|
||||
{ name: "preference", type: "string" },
|
||||
];
|
||||
|
||||
let domainData = {
|
||||
name: "0x V0x",
|
||||
version: "1"
|
||||
};
|
||||
var voteData = {
|
||||
campaignId: "1",
|
||||
preference: preference,
|
||||
};
|
||||
let zdata = JSON.stringify({
|
||||
types: {
|
||||
EIP712Domain: domainType,
|
||||
Vote: voteType,
|
||||
},
|
||||
domain: domainData,
|
||||
primaryType: "Vote",
|
||||
message: voteData
|
||||
});
|
||||
|
||||
signer = web3.eth.coinbase
|
||||
console.log(zdata, signer, preference);
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
function done(error, result) {
|
||||
if (error) { console.error(error); reject(error); }
|
||||
else { console.log(result); resolve(result.result); }
|
||||
}
|
||||
web3.currentProvider.sendAsync({
|
||||
method: "eth_signTypedData_v3",
|
||||
params: [signer, zdata],
|
||||
from: signer
|
||||
}, done)
|
||||
})
|
||||
}
|
||||
|
||||
function vote(preference) {
|
||||
signVote(preference).then(function(sig) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', '/api/vote', true);
|
||||
xhr.setRequestHeader("Content-Type", "application/json");
|
||||
xhr.send(JSON.stringify({
|
||||
preference: preference,
|
||||
voterAddress: web3.eth.coinbase,
|
||||
signature: sig,
|
||||
comment: document.getElementById("comment").value,
|
||||
campaignId: 1}));
|
||||
});
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
var path = require('path');
|
||||
|
||||
module.exports = {
|
||||
devServer: {
|
||||
contentBase: path.join(__dirname, '/src'),
|
||||
compress: true,
|
||||
host: '0.0.0.0',
|
||||
port: 3000,
|
||||
proxy: {
|
||||
'/api/': {
|
||||
target: 'http://localhost:5000/',
|
||||
secure: false,
|
||||
logLevel: 'debug',
|
||||
changeOrigin: true,
|
||||
xfwd: true
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user