handle.py (3564B)
1 # standard imports 2 import json 3 import logging 4 5 # external imports 6 from jsonrpc.exceptions import ( 7 JSONRPCServerError, 8 JSONRPCParseError, 9 JSONRPCInvalidParams, 10 ) 11 from hexathon import add_0x 12 13 # local imports 14 from funga.eth.transaction import EIP155Transaction 15 from funga.error import ( 16 UnknownAccountError, 17 SignerError, 18 ) 19 from funga.eth.cli.jsonrpc import jsonrpc_ok 20 from .jsonrpc import ( 21 jsonrpc_error, 22 is_valid_json, 23 ) 24 25 logg = logging.getLogger(__name__) 26 27 28 class SignRequestHandler: 29 30 keystore = None 31 signer = None 32 33 def process_input(self, j): 34 rpc_id = j['id'] 35 m = j['method'] 36 p = j['params'] 37 return (rpc_id, getattr(self, m)(p)) 38 39 40 def handle_jsonrpc(self, d): 41 j = None 42 try: 43 j = json.loads(d) 44 is_valid_json(j) 45 logg.debug('{}'.format(d.decode('utf-8'))) 46 except Exception as e: 47 logg.exception('input error {}'.format(e)) 48 j = json.dumps(jsonrpc_error(None, JSONRPCParseError)).encode('utf-8') 49 raise SignerError(j) 50 51 try: 52 (rpc_id, r) = self.process_input(j) 53 r = jsonrpc_ok(rpc_id, r) 54 j = json.dumps(r).encode('utf-8') 55 except ValueError as e: 56 # TODO: handle cases to give better error context to caller 57 logg.exception('process error {}'.format(e)) 58 j = json.dumps(jsonrpc_error(j['id'], JSONRPCServerError)).encode('utf-8') 59 raise SignerError(j) 60 except UnknownAccountError as e: 61 logg.exception('process unknown account error {}'.format(e)) 62 j = json.dumps(jsonrpc_error(j['id'], JSONRPCServerError)).encode('utf-8') 63 raise SignerError(j) 64 65 return j 66 67 68 def personal_newAccount(self, p): 69 password = p 70 if p.__class__.__name__ != 'str': 71 if p.__class__.__name__ != 'list': 72 e = JSONRPCInvalidParams() 73 e.data = 'parameter must be list containing one string' 74 raise ValueError(e) 75 logg.error('foo {}'.format(p)) 76 if len(p) != 1: 77 e = JSONRPCInvalidParams() 78 e.data = 'parameter must be list containing one string' 79 raise ValueError(e) 80 if p[0].__class__.__name__ != 'str': 81 e = JSONRPCInvalidParams() 82 e.data = 'parameter must be list containing one string' 83 raise ValueError(e) 84 password = p[0] 85 86 r = self.keystore.new(password) 87 88 return add_0x(r) 89 90 91 # TODO: move to translation module ("personal" rpc namespace is node-specific) 92 def personal_signTransaction(self, p): 93 logg.debug('got {} to sign'.format(p[0])) 94 t = EIP155Transaction(p[0], p[0]['nonce'], p[0]['chainId']) 95 raw_signed_tx = self.signer.sign_transaction_to_rlp(t, p[1]) 96 o = { 97 'raw': '0x' + raw_signed_tx.hex(), 98 'tx': t.serialize(), 99 } 100 return o 101 102 103 def eth_signTransaction(self, tx): 104 o = self.personal_signTransaction([tx[0], '']) 105 return o['raw'] 106 107 108 def eth_sign(self, p): 109 logg.debug('got message {} to sign'.format(p[1])) 110 message_type = type(p[1]).__name__ 111 if message_type != 'str': 112 raise ValueError('invalid message format, must be {}, not {}'.format(message_type)) 113 z = self.signer.sign_ethereum_message(p[0], p[1][2:]) 114 return add_0x(z.hex()) 115