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