diff --git a/scripts/server.py b/scripts/server.py index 8228764..b079912 100644 --- a/scripts/server.py +++ b/scripts/server.py @@ -1,12 +1,44 @@ import socket import json import logging +import sys +import os -from jsonrpc.exceptions import JSONRPCParseError +from jsonrpc.exceptions import * + +from signer import ReferenceSigner +from keystore import ReferenceDatabase logging.basicConfig(level=logging.DEBUG) logg = logging.getLogger() +db = None +signer = None + + +def personal_new_account(p): + if p.__class__.__name__ != 'list': + e = JSONRPCInvalidParams() + e.data = 'parameter must be list containing one string' + raise ValueError(e ) + if len(p) != 1: + e = JSONRPCInvalidParams() + e.data = 'parameter must be list containing one string' + raise ValueError(e) + if p[0].__class__.__name__ != 'str': + e = JSONRPCInvalidParams() + e.data = 'parameter must be list containing one string' + raise ValueError(e) + + r = db.new(p[0]) + + return [r] + + +methods = { + 'personal_newAccount': personal_new_account, + } + def jsonrpc_error(id, err): return { @@ -22,23 +54,61 @@ def jsonrpc_ok(rpc_id, response): return { 'json-rpc': '2.0', 'id': rpc_id, - 'response': response, + 'result': response, } -s = socket.socket(family = socket.AF_UNIX, type = socket.SOCK_STREAM) -s.bind('/tmp/foo.ipc') -s.listen(10) -while True: - (csock, caddr) = s.accept() - d = csock.recv(4096) - try: - logg.debug('{}'.format(d.decode('utf-8'))) - json.loads(d) - csock.send(json.dumps(jsonrpc_ok(0, [])).encode('utf-8')) - except: - csock.send(json.dumps(jsonrpc_error(None, JSONRPCParseError)).encode('utf-8')) - csock.close() -s.close() +def process_input(j): -os.unlink('/tmp/foo.ipc') + rpc_id = j['id'] + + m = j['method'] + p = j['params'] + return (rpc_id, methods[m](p)) + + +def start_server(): + os.unlink('/tmp/foo.ipc') + s = socket.socket(family = socket.AF_UNIX, type = socket.SOCK_STREAM) + s.bind('/tmp/foo.ipc') + s.listen(10) + while True: + (csock, caddr) = s.accept() + d = csock.recv(4096) + try: + j = json.loads(b) + process_input(j) + logg.debug('{}'.format(d.decode('utf-8'))) + csock.send(json.dumps(jsonrpc_ok(0, [])).encode('utf-8')) + except: + csock.send(json.dumps(jsonrpc_error(None, JSONRPCParseError)).encode('utf-8')) + csock.close() + s.close() + + os.unlink('/tmp/foo.ipc') + + +def init(): + global db, signer + secret_hex = os.environ.get('SIGNER_SECRET') + secret = bytes.fromhex(secret_hex) + kw = { + 'symmetric_key': secret, + } + db = ReferenceDatabase('cic_signer', **kw) + signer = ReferenceSigner(db.get) + + +if __name__ == '__main__': + init() + arg = None + try: + arg = json.loads(sys.argv[1]) + except: + logg.info('no json rpc command detected, starting socket server') + start_server() + sys.exit(0) + + (rpc_id, response) = process_input(arg) + r = jsonrpc_ok(rpc_id, response) + sys.stdout.write(json.dumps(r)) diff --git a/src/keystore/postgres.py b/src/keystore/postgres.py index 013964c..5518665 100644 --- a/src/keystore/postgres.py +++ b/src/keystore/postgres.py @@ -9,6 +9,8 @@ from eth_keys import KeyAPI from eth_keys.backends import NativeECCBackend import sha3 +from common import strip_hex_prefix + keyapi = KeyAPI(NativeECCBackend) logging.basicConfig(level=logging.DEBUG) @@ -24,7 +26,7 @@ class ReferenceDatabase: def __init__(self, dbname, **kwargs): - self.conn = psycopg2.connect('dbname='+dbname) + self.conn = psycopg2.connect('dbname=' + dbname) self.cur = self.conn.cursor() self.symmetric_key = kwargs.get('symmetric_key') @@ -36,12 +38,20 @@ class ReferenceDatabase: return self._decrypt(k, password) - def new(self, address, password=None): + def new(self, password=None): b = os.urandom(32) pk = keyapi.PrivateKey(b) + + pubk = keyapi.private_key_to_public_key(pk) + address_hex = pubk.to_checksum_address() + address_hex_clean = strip_hex_prefix(address_hex) + + logg.debug('address {}'.format(address_hex_clean)) c = self._encrypt(pk.to_bytes(), password) s = sql.SQL('INSERT INTO ethereum (wallet_address_hex, key_ciphertext) VALUES (%s, %s)') - self.cur.execute(s, [ address, c.decode('utf-8') ]) + self.cur.execute(s, [ address_hex_clean, c.decode('utf-8') ]) + self.conn.commit() + return address_hex def _encrypt(self, private_key, password): @@ -64,7 +74,8 @@ class ReferenceDatabase: return f.decrypt(c.encode('utf-8')) - def __exit__(self): - self.conn + def __del__(self): + logg.debug('closing database') + self.conn.commit() self.cur.close() self.conn.close() diff --git a/src/signer/defaultsigner.py b/src/signer/defaultsigner.py index e4e886a..1039cf8 100644 --- a/src/signer/defaultsigner.py +++ b/src/signer/defaultsigner.py @@ -27,12 +27,12 @@ class ReferenceSigner(Signer): super(ReferenceSigner, self).__init__(keyGetter) - def signTransaction(self, tx): + def signTransaction(self, tx, password=None): s = tx.serialize() h = sha3.keccak_256() h.update(s) g = h.digest() - k = keys.PrivateKey(self.keyGetter(tx.sender)) + k = keys.PrivateKey(self.keyGetter(tx.sender, password)) z = keys.ecdsa_sign(message_hash=g, private_key=k) tx.v = (tx.v * 2) + 35 + z[64] tx.r = z[:32]