#!/usr/bin/python3 import socket import json import logging import sys import os from jsonrpc.exceptions import * from crypto_dev_signer.eth.signer import ReferenceSigner from crypto_dev_signer.eth.transaction import EIP155Transaction from crypto_dev_signer.keystore import ReferenceKeystore logging.basicConfig(level=logging.DEBUG) logg = logging.getLogger() db = None signer = None chainId = 8995 class MissingSecretError(BaseException): def __init__(self, message): super(MissingSecretError, self).__init__(message) pass def personal_new_account(p): password = p if p.__class__.__name__ != 'str': if p.__class__.__name__ != 'list': e = JSONRPCInvalidParams() e.data = 'parameter must be list containing one string' raise ValueError(e) logg.error('foo {}'.format(p)) 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) password = p[0] r = db.new(password) return r def personal_sign_transaction(p): t = EIP155Transaction(p[0], p[0]['nonce'], 8995) z = signer.signTransaction(t, p[1]) raw_signed_tx = t.rlp_serialize() o = { 'raw': '0x' + raw_signed_tx.hex(), 'tx': t.serialize(), } logg.debug('signed {}'.format(o)) return o # TODO: temporary workaround for platform, since personal_signTransaction is missing from web3.py def eth_signTransaction(tx): return personal_sign_transaction([tx, '']) methods = { 'personal_newAccount': personal_new_account, 'personal_signTransaction': personal_sign_transaction, 'eth_signTransaction': eth_signTransaction, } def jsonrpc_error(rpc_id, err): return { 'json-rpc': '2.0', 'id': rpc_id, 'error': { 'code': err.CODE, 'message': err.MESSAGE, }, } def jsonrpc_ok(rpc_id, response): return { 'json-rpc': '2.0', 'id': rpc_id, 'result': response, } def is_valid_json(j): if j.get('id') == 'None': raise ValueError('id missing') return True def process_input(j): rpc_id = j['id'] m = j['method'] p = j['params'] return (rpc_id, methods[m](p)) def start_server(): try: os.unlink('/tmp/foo.ipc') except FileNotFoundError: pass 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) j = None try: j = json.loads(d) is_valid_json(j) logg.debug('{}'.format(d.decode('utf-8'))) except Exception as e: logg.error('input error {}'.format(e)) csock.send(json.dumps(jsonrpc_error(None, JSONRPCParseError)).encode('utf-8')) csock.close() continue try: (rpc_id, r) = process_input(j) csock.send(json.dumps(jsonrpc_ok(rpc_id, r)).encode('utf-8')) except Exception as e: # TODO: handle cases to give better error context to caller logg.error('process error {}'.format(e)) csock.send(json.dumps(jsonrpc_error(j['id'], JSONRPCServerError)).encode('utf-8')) csock.close() s.close() os.unlink('/tmp/foo.ipc') def init(): global db, signer secret_hex = '' try: secret_hex = os.environ['SIGNER_SECRET'] except KeyError as e: raise MissingSecretError('please set the SIGNER_SECRET environment variable to a valid hex value') secret = bytes.fromhex(secret_hex) kw = { 'symmetric_key': secret, } db = ReferenceKeystore(os.environ.get('SIGNER_DATABASE', 'cic_signer'), **kw) signer = ReferenceSigner(db) 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))