funga-eth

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

sql.py (3659B)


      1 # standard imports
      2 import logging
      3 import base64
      4 
      5 # external imports
      6 from cryptography.fernet import Fernet
      7 #import psycopg2
      8 from sqlalchemy import create_engine, text
      9 from sqlalchemy.orm import sessionmaker
     10 import sha3
     11 from hexathon import (
     12         strip_0x,
     13         add_0x,
     14         )
     15 
     16 # local imports
     17 from .interface import EthKeystore
     18 #from . import keyapi
     19 from funga.error import UnknownAccountError
     20 from funga.eth.encoding import private_key_to_address
     21 
     22 logg = logging.getLogger(__name__)
     23 
     24 
     25 def to_bytes(x):
     26     return x.encode('utf-8')
     27 
     28 
     29 class SQLKeystore(EthKeystore):
     30 
     31         schema = [
     32     """CREATE TABLE IF NOT EXISTS ethereum (
     33         id SERIAL NOT NULL PRIMARY KEY,
     34         key_ciphertext VARCHAR(256) NOT NULL,
     35         wallet_address_hex CHAR(40) NOT NULL
     36         );
     37 """,
     38     """CREATE UNIQUE INDEX IF NOT EXISTS ethereum_address_idx ON ethereum ( wallet_address_hex );
     39 """,
     40     ]
     41 
     42         def __init__(self, dsn, **kwargs):
     43             super(SQLKeystore, self).__init__()
     44             logg.debug('starting db session with dsn {}'.format(dsn))
     45             self.db_engine = create_engine(dsn)
     46             self.db_session = sessionmaker(bind=self.db_engine)()
     47             for s in self.schema:
     48                 self.db_session.execute(s)
     49                 self.db_session.commit()
     50             self.symmetric_key = kwargs.get('symmetric_key')
     51             self.__rs = None
     52             self.__rs_crsr = 0
     53 
     54 
     55         def __del__(self):
     56             logg.debug('closing db session')
     57             self.db_session.close()
     58 
     59 
     60         def get(self, address, password=None):
     61             safe_address = strip_0x(address).lower()
     62             s = text('SELECT key_ciphertext FROM ethereum WHERE wallet_address_hex = :a')
     63             r = self.db_session.execute(s, {
     64                 'a': safe_address,
     65                 },
     66                 )
     67             try:
     68                 k = r.first()[0]
     69             except TypeError:
     70                 self.db_session.rollback()
     71                 raise UnknownAccountError(safe_address)
     72             self.db_session.commit()
     73             a = self._decrypt(k, password)
     74             return a
     75 
     76 
     77         def list(self):
     78             s = text('SELECT wallet_address_hex FROM ethereum')
     79             self.__rs = self.db_session.execute(s)
     80             addresses = []
     81             for r in self.__rs:
     82                 addresses.append(r)
     83             return addresses
     84 
     85 
     86         def import_key(self, pk, password=None):
     87             address_hex = private_key_to_address(pk)
     88             address_hex_clean = strip_0x(address_hex).lower()
     89 
     90             c = self._encrypt(pk.secret, password)
     91             s = text('INSERT INTO ethereum (wallet_address_hex, key_ciphertext) VALUES (:a, :c)') #%s, %s)')
     92             self.db_session.execute(s, {
     93                 'a': address_hex_clean,
     94                 'c': c.decode('utf-8'),
     95                 },
     96                 )
     97             self.db_session.commit()
     98             logg.info('added private key for address {}'.format(address_hex_clean))
     99             return add_0x(address_hex)
    100 
    101 
    102         def _encrypt(self, private_key, password):
    103             f = self._generate_encryption_engine(password)
    104             return f.encrypt(private_key)
    105 
    106 
    107         def _generate_encryption_engine(self, password):
    108             h = sha3.keccak_256()
    109             h.update(self.symmetric_key)
    110             if password != None:
    111                 password_bytes = to_bytes(password)
    112                 h.update(password_bytes)
    113             g = h.digest()
    114             return Fernet(base64.b64encode(g))
    115 
    116 
    117         def _decrypt(self, c, password):
    118             f = self._generate_encryption_engine(password)
    119             return f.decrypt(c.encode('utf-8'))