diff --git a/crypto_dev_signer/runnable/signer.py b/crypto_dev_signer/runnable/signer.py index 54c55b1..0f2d8ba 100755 --- a/crypto_dev_signer/runnable/signer.py +++ b/crypto_dev_signer/runnable/signer.py @@ -8,11 +8,6 @@ import json import logging import argparse from urllib.parse import urlparse -from http.server import ( - HTTPServer, - BaseHTTPRequestHandler, - ) - # external imports import confini @@ -77,7 +72,6 @@ logg.info('using dsn {}'.format(dsn)) logg.info('using socket {}'.format(config.get('SIGNER_SOCKET_PATH'))) re_http = r'^http' -re_tcp = r'^tcp' re_unix = r'^ipc' class MissingSecretError(BaseException): @@ -183,7 +177,7 @@ def start_server_tcp(spec): s = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) s.bind(spec) logg.debug('created tcp socket {}'.format(spec)) - start_server_socket(s) + start_server(s) def start_server_unix(socket_path): @@ -202,30 +196,15 @@ def start_server_unix(socket_path): s = socket.socket(family = socket.AF_UNIX, type = socket.SOCK_STREAM) s.bind(socket_path) logg.debug('created unix ipc socket {}'.format(socket_path)) - start_server_socket(s) + start_server(s) -def start_server_http(spec): - httpd = HTTPServer(spec, HTTPSignRequestHandler) - logg.debug('starting http server {}'.format(spec)) - httpd.serve_forever() - - -class SignerError(Exception): - - - def __init__(self, s): - super(SignerError, self).__init__(s) - self.jsonrpc_error = s - - - def to_jsonrpc(self): - return self.jsonrpc_error - - -class SignRequestHandler: - - def handle_jsonrpc(self, d): +def start_server(s): + s.listen(10) + logg.debug('server started') + while True: + (csock, caddr) = s.accept() + d = csock.recv(4096) j = None try: j = json.loads(d) @@ -233,105 +212,23 @@ class SignRequestHandler: logg.debug('{}'.format(d.decode('utf-8'))) except Exception as e: logg.exception('input error {}'.format(e)) - j = json.dumps(jsonrpc_error(None, JSONRPCParseError)).encode('utf-8') - raise SignerError(j) + csock.send(json.dumps(jsonrpc_error(None, JSONRPCParseError)).encode('utf-8')) + csock.close() + continue try: (rpc_id, r) = process_input(j) r = jsonrpc_ok(rpc_id, r) j = json.dumps(r).encode('utf-8') + csock.send(j) except ValueError as e: # TODO: handle cases to give better error context to caller logg.exception('process error {}'.format(e)) - j = json.dumps(jsonrpc_error(j['id'], JSONRPCServerError)).encode('utf-8') - raise SignerError(j) + csock.send(json.dumps(jsonrpc_error(j['id'], JSONRPCServerError)).encode('utf-8')) except UnknownAccountError as e: logg.exception('process unknown account error {}'.format(e)) - j = json.dumps(jsonrpc_error(j['id'], JSONRPCServerError)).encode('utf-8') - raise SignerError(j) + csock.send(json.dumps(jsonrpc_error(j['id'], JSONRPCServerError)).encode('utf-8')) - return j - - -class HTTPSignRequestHandler(BaseHTTPRequestHandler, SignRequestHandler): - - def do_POST(self): - if self.headers.get('Content-Type') != 'application/json': - self.send_response(400, 'me read json only') - self.end_headers() - return - - try: - if 'application/json' not in self.headers.get('Accept').split(','): - self.send_response(400, 'me json only speak') - self.end_headers() - return - except AttributeError: - pass - - l = self.headers.get('Content-Length') - try: - l = int(l) - except ValueError: - self.send_response(400, 'content length must be integer') - self.end_headers() - return - if l > 4096: - self.send_response(400, 'too much information') - self.end_headers() - return - if l < 0: - self.send_response(400, 'you are too negative') - self.end_headers() - return - - b = b'' - c = 0 - while c < l: - d = self.rfile.read(l-c) - if d == None: - break - b += d - c += len(d) - if c > 4096: - self.send_response(413, 'i should slap you around for lying about your size') - self.end_headers() - return - - try: - r = self.handle_jsonrpc(d) - except SignerError as e: - r = e.to_jsonrpc() - - #b = json.dumps(r).encode('utf-8') - l = len(r) - self.send_response(200, 'You are the Keymaster') - self.send_header('Content-Length', str(l)) - self.send_header('Cache-Control', 'no-cache') - self.send_header('Content-Type', 'application/json') - self.end_headers() - - c = 0 - while c < l: - n = self.wfile.write(r[c:]) - c += n - - -def start_server_socket(s): - s.listen(10) - logg.debug('server started') - handler = SignRequestHandler() - while True: - (csock, caddr) = s.accept() - d = csock.recv(4096) - - r = None - try: - r = handler.handle_jsonrpc(d) - except SignerError as e: - r = e.to_jsonrpc() - - csock.send(r) csock.close() s.close() @@ -358,20 +255,15 @@ def main(): try: arg = json.loads(sys.argv[1]) except: - logg.info('no json rpc command detected, starting socket server {}'.format(socket_url)) + logg.info('no json rpc command detected, starting socket server') scheme = 'ipc' if socket_url.scheme != '': scheme = socket_url.scheme - if re.match(re_tcp, socket_url.scheme): + if re.match(re_http, socket_url.scheme): socket_spec = socket_url.netloc.split(':') host = socket_spec[0] port = int(socket_spec[1]) start_server_tcp((host, port)) - elif re.match(re_http, socket_url.scheme): - socket_spec = socket_url.netloc.split(':') - host = socket_spec[0] - port = int(socket_spec[1]) - start_server_http((host, port)) else: start_server_unix(socket_url.path) sys.exit(0) diff --git a/requirements.txt b/requirements.txt index 7b319e1..d7cb654 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,6 @@ cryptography==3.2.1 pysha3==1.0.2 rlp==2.0.1 json-rpc==1.13.0 -jsonrpc-std~=0.0.1a2 confini>=0.3.6rc3,<0.5.0 coincurve==15.0.0 hexathon~=0.0.1a7 diff --git a/setup.py b/setup.py index 84b4f58..6233dfe 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ f.close() setup( name="crypto-dev-signer", - version="0.4.15a2", + version="0.4.15a1", description="A signer and keystore daemon and library for cryptocurrency software development", author="Louis Holbrook", author_email="dev@holbrook.no", diff --git a/tests/test_helper.py b/tests/test_helper.py new file mode 100644 index 0000000..0e46438 --- /dev/null +++ b/tests/test_helper.py @@ -0,0 +1,91 @@ +# standard imports +import unittest +import logging +import os + +# local imports +from crypto_dev_signer.keystore.dict import DictKeystore +from crypto_dev_signer.eth.signer import ReferenceSigner +from crypto_dev_signer.helper import TxExecutor +#from crypto_dev_signer.eth.helper import EthTxExecutor +from crypto_dev_signer.encoding import to_checksum_address + +logging.basicConfig(level=logging.DEBUG) +logg = logging.getLogger() + +script_dir = os.path.realpath(os.path.dirname(__file__)) + + +class MockEthTxBackend: + + def dispatcher(self, tx): + logg.debug('sender {}'.format(tx)) + return os.urandom(32) + + def reporter(self, tx): + logg.debug('reporter {}'.format(tx)) + + def verifier(self, rcpt): + logg.debug('reporter {}'.format(rcpt)) + + def fee_price_helper(self): + return 21 + + def fee_helper(self, tx): + logg.debug('fee helper tx {}'.format(tx)) + return 2 + + def builder(self, tx): + return tx + + def builder_two(self, tx): + tx['value'] = 10243 + tx['to'] = to_checksum_address('0x' + os.urandom(20).hex()) + tx['data'] = '0x' + if tx.get('feePrice') != None: + tx['gasPrice'] = tx['feePrice'] + del tx['feePrice'] + if tx.get('feeUnits') != None: + tx['gas'] = tx['feeUnits'] + del tx['feeUnits'] + return tx + + +class TestHelper(unittest.TestCase): + + def setUp(self): + logg.debug('setup') + self.db = DictKeystore() + + keystore_filename = 'UTC--2021-01-08T18-37-01.187235289Z--00a329c0648769a73afac7f9381e08fb43dbea72' + keystore_filepath = os.path.join(script_dir, 'testdata', keystore_filename) + + self.address_hex = self.db.import_keystore_file(keystore_filepath, '') + self.signer = ReferenceSigner(self.db) + + + def tearDown(self): + pass + + + def test_helper(self): + backend = MockEthTxBackend() + executor = TxExecutor(self.address_hex, self.signer, backend.builder, backend.dispatcher, backend.reporter, 666, 13, backend.fee_helper, backend.fee_price_helper, backend.verifier) + + tx_ish = {'from': self.address_hex} + executor.sign_and_send([backend.builder_two]) + + +# def test_eth_helper(self): +# backend = MockEthTxBackend() +# w3 = web3.Web3(web3.Web3.HTTPProvider('http://localhost:8545')) +# executor = EthTxExecutor(w3, self.address_hex, self.signer, 1337) +# +# tx_ish = {'from': self.address_hex} +# #executor.sign_and_send([backend.builder, backend.builder_two]) +# with self.assertRaises(ValueError): +# executor.sign_and_send([backend.builder_two]) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_sign.py b/tests/test_sign.py index 1ec4c63..334c334 100644 --- a/tests/test_sign.py +++ b/tests/test_sign.py @@ -1,12 +1,10 @@ -# standard imports +#!/usr/bin/python + import unittest import logging -import copy -# external imports from rlp import encode as rlp_encode -# local imports from crypto_dev_signer.eth.signer import ReferenceSigner from crypto_dev_signer.eth.transaction import EIP155Transaction @@ -25,22 +23,15 @@ tx_ints = { } tx_hexs = { - 'nonce': '0x', + 'nonce': '0x0', 'from': "0xEB014f8c8B418Db6b45774c326A0E64C78914dC0", - 'gasPrice': "0x04a817c800", + 'gasPrice': "0x4a817c800", 'gas': "0x5208", 'to': '0x3535353535353535353535353535353535353535', - 'value': "0x03e8", - 'data': "0xdeadbeef", + 'value': "0x3e8", + 'data': "deadbeef", } -tx_hexs_r = copy.copy(tx_hexs) -tx_hexs_r['r'] = '0x' -tx_hexs_r['s'] = '0x' -tx_hexs_r['v'] = '0x01' -del tx_hexs_r['from'] - - class pkGetter: def __init__(self, pk): @@ -77,14 +68,14 @@ class TestSign(unittest.TestCase): t = EIP155Transaction(tx_ints, 0) self.assertRegex(t.__class__.__name__, "Transaction") s = t.serialize() - self.assertDictEqual(s, tx_hexs_r) + self.assertEqual('{}'.format(s), "{'nonce': '0x', 'gasPrice': '0x04a817c800', 'gas': '0x5208', 'to': '0x3535353535353535353535353535353535353535', 'value': '0x03e8', 'data': '0xdeadbeef', 'v': '0x01', 'r': '0x', 's': '0x'}") r = t.rlp_serialize() self.assertEqual(r.hex(), 'ea808504a817c8008252089435353535353535353535353535353535353535358203e884deadbeef018080') t = EIP155Transaction(tx_hexs, 0) self.assertRegex(t.__class__.__name__, "Transaction") s = t.serialize() - self.assertDictEqual(s, tx_hexs_r) + self.assertEqual('{}'.format(s), "{'nonce': '0x', 'gasPrice': '0x04a817c800', 'gas': '0x5208', 'to': '0x3535353535353535353535353535353535353535', 'value': '0x03e8', 'data': '0xdeadbeef', 'v': '0x01', 'r': '0x', 's': '0x'}") r = t.rlp_serialize() self.assertEqual(r.hex(), 'ea808504a817c8008252089435353535353535353535353535353535353535358203e884deadbeef018080') @@ -100,6 +91,7 @@ class TestSign(unittest.TestCase): s = ReferenceSigner(self.pk_getter) z = s.sign_ethereum_message(tx_ints['from'], '666f6f') z = s.sign_ethereum_message(tx_ints['from'], b'foo') + logg.debug('zzz {}'.format(str(z))) if __name__ == '__main__':