import logging from sqlalchemy import ( Column, String, Numeric, Integer, ForeignKey, ) from web3.utils.formatters import hex_to_integer from ether_sql.models import base from ether_sql.globals import get_current_session from ether_sql.models import StorageDiff from sqlalchemy import func, desc, and_ logger = logging.getLogger(__name__) class Storage(base): """ Class mapping the current storage of the ethereum blockchain to the storage table in psql :param str address: Account address which has this state :param str position: The balance of the account :param str Storage: Value of storage """ __tablename__ = 'storage' id = Column(Integer, primary_key=True) address = Column(String(42), ForeignKey('state.address', ondelete='CASCADE')) position = Column(String(66), index=True, nullable=False) storage = Column(String(66), nullable=False) def to_dict(self): return { 'address': self.address, 'position': self.position, 'storage': self.storage} @classmethod def add_storage(cls, address, position, storage): storage = cls(address=address, position=position, storage=storage) return storage @classmethod def get_storage_at_block(cls, current_session, block_number): """ Gives the storage values at requested block :param int block_number: Block number of desired storage, always required since it is called from method `State.get_state_at_block` """ row_number_column = func.row_number().over( partition_by=[StorageDiff.address, StorageDiff.position], order_by=[StorageDiff.block_number.desc(), StorageDiff.transaction_index.desc()])\ .label('row_number') query = current_session.db_session.query(StorageDiff.address, StorageDiff.position, StorageDiff.storage_to.label('storage')) query = query.add_column(row_number_column) query = query.filter( and_( StorageDiff.block_number <= block_number, StorageDiff.storage_to != '0x0000000000000000000000000000000000000000000000000000000000000000')) query = query.from_self().filter(row_number_column == 1) for row in query: if row.storage is not None: if hex_to_integer(row.storage) != 0: storage = cls.add_storage(address=row.address, position=row.position, storage=row.storage) else: # not adding storage positions where value is 0, since parity discards these positions during export logger.debug('address: {}, position {}, storage: {} is zero'.format(row.address, row.position, row.storage)) current_session.db_session.add(storage) else: logger.debug('address: {}, position {}, storage: {} is none'.format(row.address, row.position, row.storage))