funga-eth

Ethereum implementation of the funga keystore and signer
Info | Log | Files | Refs | README | LICENSE

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