commit 7deffc93a4431428106b9dd3996d86aa326d0349 Author: lash Date: Thu Aug 31 14:21:43 2023 +0100 initial commit diff --git a/gas3/__init__.py b/gas3/__init__.py new file mode 100644 index 0000000..eec0ee1 --- /dev/null +++ b/gas3/__init__.py @@ -0,0 +1,58 @@ +# standard imports +import os +import urllib.parse +import logging +import re + +# external imports +from leveldir.uuid import UUIDDir + +# local imports +from gas3.base import do_yield +from gas3.register import do_register +import gas3.error + + +logging.basicConfig(level=logging.DEBUG) +logg = logging.getLogger() + +data_path = os.environ.get('GAS3_DATA_DIR', 'data') +data_dir = UUIDDir(data_path) + +re_yield = re.compile('^/yield') +re_register = re.compile('^/register') + + +def application(env, start_response): + req = urllib.parse.urlparse(env['REQUEST_URI']) + scode = 200 + stext = 'OK' + + r = '' + if re.match(re_yield, req.path): + r = do_yield(req, env, data_dir) + elif re.match(re_register, req.path): + try: + r = do_register(req, env, data_dir) + except gas3.error.Used: + scode = 410 + stext = 'Code already used' + except gas3.error.InvalidInput as e: + scode = 400 + stext = 'Malformed request url' + r = str(e) + except FileNotFoundError: + scode = 403 + stext = 'No such code' + except Exception as e: + scode = 500 + stext = 'Server error' + r = str(e) + else: + r = '' + + + l = len(r) + status = str(scode) + ' ' + stext + start_response(status, [('Content-Length', str(l))]) + return [r.encode('utf-8')] diff --git a/gas3/base.py b/gas3/base.py new file mode 100644 index 0000000..5ddd5eb --- /dev/null +++ b/gas3/base.py @@ -0,0 +1,19 @@ +# standard imports +import uuid +import logging + +logg = logging.getLogger(__name__) + + +def do_yield(req, env, data_dir): + u = uuid.uuid4() + data_dir.add(u, b'\x00') + return str(u) + + +def check(s, data_dir): + p = data_dir.to_filepath(s) + f = open(p, 'rb') + r = f.read(20) + f.close() + return len(r) == 1 diff --git a/gas3/error.py b/gas3/error.py new file mode 100644 index 0000000..1b15b86 --- /dev/null +++ b/gas3/error.py @@ -0,0 +1,6 @@ +class InvalidInput(Exception): + pass + + +class Used(Exception): + pass diff --git a/gas3/register.py b/gas3/register.py new file mode 100644 index 0000000..535aa68 --- /dev/null +++ b/gas3/register.py @@ -0,0 +1,39 @@ +# standard imports +import logging + +# external imports +from chainlib.eth.address import to_checksum_address +from hexathon import strip_0x + +# local imports +from gas3.error import InvalidInput +from gas3.error import Used +from gas3.base import check + +logg = logging.getLogger(__name__) + + +def register_address(a): + pass + +def do_register(req, env, data_dir): + i = req.path.find('/', 1) + if i == -1: + raise InvalidInput(env.path) + p = req.path[i+1:] + r = None + r = check(p, data_dir) + if not r: + raise Used('already used: ' + p) + + v = env['wsgi.input'].read(42) + if type(v) == bytes: + v = v.decode('utf-8') + try: + v = strip_0x(v) + v = to_checksum_address(v) + except: + raise InvalidInput('invalid address: ' + str(v)) + + data_dir.add(p, bytes.fromhex(v)) + return v diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..241151f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +leveldir~=0.3.3rc1 +chainlib-eth~=0.5.2 diff --git a/test_requirements.txt b/test_requirements.txt new file mode 100644 index 0000000..fceb5cd --- /dev/null +++ b/test_requirements.txt @@ -0,0 +1,4 @@ +eth_tester==0.5.0b3 +py-evm==0.3.0a20 +rlp==2.0.1 +coverage==5.5 diff --git a/tests/test_register.py b/tests/test_register.py new file mode 100644 index 0000000..b288e92 --- /dev/null +++ b/tests/test_register.py @@ -0,0 +1,59 @@ +# standard imports +import io +import os +import unittest +import tempfile +import shutil +import logging +import urllib.parse + +# external imports +from chainlib.eth.unittest.ethtester import EthTesterCase +from hexathon import same as is_same_hex +from leveldir.uuid import UUIDDir +from gas3.base import do_yield +from gas3.base import check +from gas3.register import do_register +import gas3.error + +logging.basicConfig(level=logging.DEBUG) + + +class TestRegister(EthTesterCase): + + def setUp(self): + super(TestRegister, self).setUp() + self.data_path = tempfile.mkdtemp() + self.data_dir = UUIDDir(self.data_path) + + + def test_yield(self): + u = do_yield(None, {}, self.data_dir) + p = check(u, self.data_dir) + self.assertTrue(os.path.exists(p)) + + + def test_register(self): + u = do_yield(None, {}, self.data_dir) + req = urllib.parse.urlparse('http://localhost/register/' + u) + address_file = io.StringIO() + address_file.write(self.accounts[0] + '\n') + address_file.seek(0) + env = { + 'wsgi.input': address_file, + } + r = do_register(req, env, self.data_dir) + self.assertEqual(len(r), 40) + with self.assertRaises(gas3.error.Used): + r = do_register(req, env, self.data_dir) + + fp = self.data_dir.to_filepath(u) + f = open(fp, 'rb') + r = f.read() + f.close() + self.assertTrue(is_same_hex(r.hex(), self.accounts[0])) + + + +if __name__ == '__main__': + unittest.main()