コード例 #1
0
ファイル: bot.py プロジェクト: loxadim/eth-alerts
class Bot(Singleton):
    def __init__(self):
        super(Bot, self).__init__()
        self.decoder = Decoder()
        self.web3 = Web3(
            RPCProvider(host=settings.ETHEREUM_NODE_HOST,
                        port=settings.ETHEREUM_NODE_PORT,
                        ssl=settings.ETHEREUM_NODE_SSL))
        self.batch = MailBatch()

    def next_block(self):
        return models.Daemon.get_solo().block_number

    def update_block(self):
        daemon = models.Daemon.get_solo()
        current = self.web3.eth.blockNumber
        if daemon.block_number < current:
            blocks_to_update = range(daemon.block_number + 1, current + 1)
            daemon.block_number = current
            daemon.save()
            return blocks_to_update
        else:
            return []

    def load_abis(self, contracts):
        alerts = Alert.objects.filter(contract__in=contracts)
        added = 0
        for alert in alerts:
            try:
                added += self.decoder.add_abi(loads(alert.abi))
            except ValueError:
                pass
        return added

    def get_logs(self, block_number):
        block = self.web3.eth.getBlock(block_number)
        logs = []
        if block and block.get(u'hash'):
            for tx in block[u'transactions']:
                receipt = self.web3.eth.getTransactionReceipt(tx)
                if receipt.get('logs'):
                    logs.extend(receipt[u'logs'])
            return logs
        else:
            raise UnknownBlock

    def filter_logs(self, logs, contracts):
        # filter by contracts
        all_alerts = Alert.objects.filter(
            contract__in=contracts).prefetch_related(
                'events__event_values').prefetch_related('dapp')
        filtered = {}
        for log in logs:
            # get alerts for same log contract (can be many)
            alerts = all_alerts.filter(contract=log[u'address'])

            for alert in alerts:
                # Get event names
                events = alert.events.filter(name=log[u'name'])
                if events.count():
                    # Get event property, if event property, discard unmatched values
                    add_event = True
                    if events[0].event_values.count():
                        # check that all parameters check in value or doesn't exist
                        for event_value in events[0].event_values.iterator():
                            for param in log[u'params']:
                                if event_value.property == param[u'name']:
                                    if event_value.value != param[u'value']:
                                        add_event = False

                    # add log
                    if add_event:
                        email = alert.dapp.user.email
                        dapp_name = alert.dapp.name
                        if not filtered.get(email):
                            filtered[email] = {}
                        if not filtered[email].get(dapp_name):
                            filtered[email][dapp_name] = []
                        filtered[email][dapp_name].append(log)

        return filtered

    def execute(self):

        # update block number
        # get blocks and decode logs
        for block in self.update_block():
            # first get un-decoded logs
            logs = self.get_logs(block)
            # get contract addresses
            contracts = []
            for log in logs:
                contracts.append(log[u'address'])
            contracts = set(contracts)

            # load abi's from alerts with contract addresses
            self.load_abis(contracts)

            # decode logs
            decoded = self.decoder.decode_logs(logs)

            # If decoded, filter correct logs and group by dapp and mail
            filtered = self.filter_logs(decoded, contracts)

            # add filtered logs to send mail
            for mail, dapp_logs in filtered.iteritems():
                self.batch.add_mail(mail, dapp_logs)

        # if blocknumber is the same, do nothing
        # Send mails in batch
        self.batch.send_mail()