import os import logging import random import alembic import argparse import datetime import hashlib import math import confini from cic_eth.db import SessionBase from cic_eth.db import dsn_from_config from cic_eth.db.enum import StatusEnum from cic_eth.db.models.tx import Tx from cic_eth.db.models.tx import TxCache from cic_eth.db.models.otx import Otx from cic_eth.db.models.otx import Otx # connect to database dsn = dsn_from_config(config) SessionBase.connect(dsn) pending_states = [ StatusEnum.PENDING, StatusEnum.SENDFAIL, StatusEnum.WAITFORGAS, ] synced_states = [ StatusEnum.SENT, StatusEnum.CANCELLED, ] failed_states = [ StatusEnum.REJECTED, StatusEnum.FUBAR, ] mined_states = [ StatusEnum.REVERTED, StatusEnum.SUCCESS, ] all_states = pending_states + synced_states + failed_states + mined_states class MalarkeyCommitter(): """Adds entries to the cic-eth transaction cache backend. All values generated by this class are deterministic but completely arbitrary. :param generator: Generator instance used to generate the transactions :type generator: Generator :param block_change_factor: Probabiliy of advancing to next block per entry (Optional; default: 0.6) :type block_change_factor: float :param tx_index_max: Maximum transaction index number to generate (Optional; default: 200) :type ix_index_max: int :param block_time_seconds: Seconds to advance block timestamp datetime on block advance :type block_time_seconds: int """ def __init__(self, generator, block_change_factor=0.6, tx_index_max=200, block_time_seconds=15): self.g = generator self.s = SessionBase.create_session() self.datetime = datetime.datetime.utcnow() self.block = 0 self.tx = 0 self.block_change_factor = block_change_factor self.tx_index_max = tx_index_max self.block_time_seconds = block_time_seconds def advance(self): """Randomly advances block number and transaction index. """ if random.random() < self.block_change_factor: self.block_advance() tx_advance = random.randint(0, self.tx_index_max - self.tx) self.tx_advance(tx_advance) def tx_advance(self, n=1): """Advance tx index number. :param n: Will be added to tx index (Optional; default: 1) :type n: int """ self.tx += n def block_advance(self, n=1): """Advance block number. :param n: Will be added to tx index (Optional; default: 1) :type n: int """ self.block += n self.datetime += datetime.timedelta(seconds=self.block_time_seconds) self.tx = 0 def stamp(self, entry): """Calls the random advance method, and sets the corresponding block, tx index and dates on the entry. """ self.advance() entry.mine(self.block, self.tx, self.datetime) def commit_func(self, entry): """Implements interface needed for GeneratorSession.commit_func, storing a given entry. Generates random block and tx index for entry, advancing date correspondingly. Also generates random transaction queue states for the entries: - For any entry with foreign origin, queue state will always be in the set of "mined" states if applicable. - For any entry with local origin and "mined" queue state, an incoming transaction record will be added. :param entry: Entry to commit :type entry: GeneratorEntry """ self.stamp(entry) nonce = self.g.get_nonce(entry.sender) tx_hash = '0x{:064x}'.format(random.getrandbits(256)) signed_tx_bytes = b'' h = hashlib.sha256() for i in range(4): h.update(tx_hash.encode('utf-8')) signed_tx_bytes += h.digest() signed_tx = '0x' + signed_tx_bytes.hex() state = None if entry.sender in self.g.addresses['foreign']: state = random.choice(mined_states) else: state = random.choice(all_states) otx = None tx = None if entry.sender in self.g.addresses['local']: otx = Otx( nonce, entry.sender, tx_hash, signed_tx, ) otx.status = state self.s.add(otx) self.s.flush() if state in mined_states: tx = Tx( tx_hash, state == StatusEnum.SUCCESS, ) self.s.add(tx) self.s.flush() self.s.commit() txc = TxCache( tx_hash, entry.sender, entry.recipient, entry.source_token, entry.destination_token, entry.from_value, entry.to_value, entry.block, entry.tx, entry.sender in self.g.addresses['local'], ) txc.date_registered = entry.datetime self.s.add(txc) self.s.commit() logg.info('committed {}'.format(entry))