def store_contract(contract, pipe=None):
        """
        Store a contract in the database.

        Arguments:
        contract    -- contract object to be store in the database

        Returns:
        list        -- list containing results of each query used to store an object (0s and 1s)
                    0s indicate that the field was updated (already present)
                    1s indicate that the field is new and was stored
            OR
        pipeline    -- pipeline object to continue inserting into redis with (if pipe was passed in)
        """
        rs = RedisService()
        rs.store_object(contract)
        r = rs._connect()
        pipe_created = False
        if pipe == None:
            pipe = r.pipeline()
            pipe_created = True
        pipe.zadd(ContractFilter.RATE, contract.rate, contract._hash)
        pipe.zadd(ContractFilter.AMOUNT, contract.amount, contract._hash)
        pipe.zadd(ContractFilter.CREATED, contract.created_timestamp,
                  contract._hash)
        pipe.zadd(ContractFilter.DURATION, contract.duration, contract._hash)
        pipe.zadd(ContractFilter.SIGN_END, contract.sign_end_timestamp,
                  contract._hash)

        if pipe_created:
            return pipe.execute()
        else:
            return pipe
Exemplo n.º 2
0
 def on_message(self, ws, message):
     msg = json.loads(message)
     if isinstance(msg, list) and msg[1] == "tu":
         # add this to the db
         price_stamp = self.trade_pricestamp_adaptor(msg)
         price_stamp.update_hash()
         rs = RedisService()
         rs.store_object(price_stamp)
         r = rs._connect()
         r.zadd('price_stamps', price_stamp.timestamp, price_stamp.price)
def test_remove_from_mempool():
    ts = TransactionService()
    rs = RedisService()

    t = Transaction('mem_test_hash4', '', 'mem_william', 'mem_naween', 14605,
                    1)
    rs.store_object(t)

    ts.remove_from_mem_pool(t)

    new_t = rs.get_object_by_hash('mem_test_hash4', Transaction)

    print(t.to_string())
    print(new_t.to_string())
    assert new_t.is_mempool == 0
def test_store_get_transaction_by_hash():
    service = RedisService()
    t = Transaction("test_hash", '', "test to", "test from", 50.123, 0)
    success = service.store_object(t)
    assert success

    new_t = service.get_object_by_hash("test_hash", Transaction)

    assert t.to_string() == new_t.to_string()
def test_get_field():
    service = RedisService()
    t = Transaction("test_hash_get", '', "test to_get", "test from_get", 501,
                    0)

    stored = service.store_object(t)

    assert stored

    result = service.get_field("transaction", "test_hash_get", "to_addr")

    assert result == ["test to_get"]
def setup():
    # run first to store test data in the database
    rs = RedisService()
    # store them
    for transaction in transactions:
        rs.store_object(transaction)
class BlockService():
    _host = ''
    _port = ''

    key_suffix = 'block:'
    max_block_height = 'max_block_height'

    rs = None
    def __init__(self):
        #settings = json.load(open('crypto_tulips/config/db_settings.json'))
        with open(file="crypto_tulips/config/db_settings.json", mode="r") as data_file:
            settings = json.load(data_file)
        self.host = settings["host"]
        self.port = settings["port"]
        #Logger.log("BlockService Initialized with redis running on " + self.host + ":" + self.port, 0, LoggingLevel.INFO)
        self.rs = RedisService()

    def store_block(self, block):
        """
        Store an entire block in redis. Will store fields and lists of objects. Will not store anything if the block's hash already exists in the database.

        Arguments:
        block   -- Block object to be stored

        Returns:
        list    -- list containing results of each query used to store an object (0s and 1s)
                0s indicate that the field was updated (already present)
                1s indicate that the field is new and was stored
        """
        r = self._connect()

        #rs = RedisService()

        # key to store list of objects under
        name = self.key_suffix + block._hash
        # if block isn't in the database already
        if not r.exists(name):
            pipe = r.pipeline()
            # store timestamp first
            pipe.rpush(name, block.prev_block)
            pipe.rpush(name, block.height)
            pipe.rpush(name, block.owner)
            pipe.rpush(name, block.signature)
            pipe.rpush(name, block.timestamp)

            # store string 'transactions' to help with retrieval parsing
            pipe.rpush(name, 'transactions')
            for transaction in block.transactions:
                transaction.is_mempool = 0
                # check if transaction is in the mempool
                set_name = transaction._to_index()[-1] + ":is_mempool:1"
                t_key = transaction._to_index()[-1] + ":" + transaction._hash
                if r.sismember(set_name, t_key):
                    # remove from list of mempool objects
                    pipe.srem(set_name, t_key)

                # store the transaction's hash under the block's list
                pipe.rpush(name, transaction._hash)
                # store the actual transaction object

                pipe = self.rs.store_object(transaction, r, pipe)

            pipe.rpush(name, 'pos_transactions')
            for pos_transaction in block.pos_transactions:
                pos_transaction.is_mempool = 0
                # check if pos transaction is in the mempool
                set_name = pos_transaction._to_index()[-1] + ":is_mempool:1"
                pt_key = pos_transaction._to_index()[-1] + ":" + pos_transaction._hash
                if r.sismember(set_name, pt_key):
                    # remove from list of mempool objects
                    pipe.srem(set_name, pt_key)

                pipe.rpush(name, pos_transaction._hash)
                pipe = self.rs.store_object(pos_transaction, r, pipe)

            pipe.rpush(name, 'contract_transactions')
            for contract_transaction in block.contract_transactions:
                contract_transaction.is_mempool = 0
                # check if contract transaction is in the mempool
                set_name = contract_transaction._to_index()[-1] + ":is_mempool:1"
                ct_key = contract_transaction._to_index()[-1] + ":" + contract_transaction._hash
                if r.sismember(set_name, ct_key):
                    # remove from list of mempool objects
                    pipe.srem(set_name, ct_key)

                pipe.rpush(name, contract_transaction._hash)
                pipe = self.rs.store_object(contract_transaction, r, pipe)

            pipe.rpush(name, 'contracts')
            for contract in block.contracts:
                contract.is_mempool = 0
                # check if contract is in the mempool
                set_name = contract._to_index()[-1] + ":is_mempool:1"
                c_key = contract._to_index()[-1] + ":" + contract._hash
                if r.sismember(set_name, c_key):
                    # remove from list of mempool objects
                    pipe.srem(set_name, c_key)

                pipe.rpush(name, contract._hash)
                pipe = ContractService.store_contract(contract, pipe)

            pipe.rpush(name, 'signed_contracts')
            for signed_contract in block.signed_contracts:
                signed_contract.is_mempool = 0
                # check if signed contract is in the mempool
                set_name = signed_contract._to_index()[-1] + ":is_mempool:1"
                sc_key = signed_contract._to_index()[-1] + ":" + signed_contract._hash
                if r.sismember(set_name, sc_key):
                    # remove from list of mempool objects
                    pipe.srem(set_name, sc_key)

                pipe.rpush(name, signed_contract._hash)
                pipe = SignedContractService.store_signed_contract(signed_contract, pipe)

            pipe.rpush(name, 'terminated_contracts')
            for terminated_contract in block.terminated_contracts:
                pipe.rpush(name, terminated_contract._hash)
                pipe = self.rs.store_object(terminated_contract, r, pipe)

            pipe.zadd('blocks', block.height, block._hash)

            # TODO max block height will not always be this, will change
            pipe.set(self.max_block_height, block.height)

            pipe.sadd("block:" + str(block.height), block._hash)
            return pipe.execute()
        else:
            print("Block with hash: " + block._hash + " already exists. Unable to update.")
            return []

    def find_by_hash(self, block_hash):
        """
        Find a block using it's hash. Will return the block object, fully populated with all of the objects encased in it.

        Arguments:
        block_hash      -- hash of the block to retrieve

        Returns:
        block object    -- Block object containing all of the objects included in the block
        """
        r = self._connect()

        #rs = RedisService()

        # get key to retrieve list of block's fields
        name = self.key_suffix + block_hash

        if r.exists(name):
            # get all of the fields in the list
            hashes = r.lrange(name, 0, -1)

            # timestamp will always be first
            prev_block = hashes[0]
            # remove for iteration
            hashes.remove(prev_block)

            # timestamp will always be first
            height = hashes[0]
            # remove for iteration
            hashes.remove(height)

            # timestamp will always be first
            owner = hashes[0]
            # remove for iteration
            hashes.remove(owner)

            # timestamp will always be first
            signature = hashes[0]
            # remove for iteration
            hashes.remove(signature)

            # timestamp will always be first
            timestamp = hashes[0]
            # remove for iteration
            hashes.remove(timestamp)

            prefix = ''
            # list to hold all of the objects
            transactions = []
            pos_transactions = []
            contract_transactions = []
            contracts = []
            signed_contracts = []
            terminated_contracts = []

            # temporary list to copy from
            temp_list = []
            obj = None
            contract_section = False
            signed_contract_section = False
            terminated_contract_section = False
            for h in hashes:
                # if at a new type of object, change some variables
                if h == 'transactions':
                    prefix = Transaction._to_index()[-1]
                    obj = Transaction
                    continue
                elif h == 'pos_transactions':
                    prefix = PosTransaction._to_index()[-1]
                    obj = PosTransaction
                    transactions = temp_list.copy()
                    temp_list.clear()
                    continue
                elif h == 'contract_transactions':
                    prefix = ContractTransaction._to_index()[-1]
                    obj = ContractTransaction
                    pos_transactions = temp_list.copy()
                    temp_list.clear()
                    continue
                elif h == 'contracts':
                    contract_section = True
                elif h == 'signed_contracts':
                    contract_section = False
                    signed_contract_section = True
                elif h == 'terminated_contracts':
                    signed_contract_section = False
                    terminated_contract_section = True


                # get the object from redis and add to the temporary list
                if contract_section:
                    contract = ContractService.get_contract_by_hash(h)
                    if contract != None:
                        contracts.append(contract)
                elif signed_contract_section:
                    signed_contract = SignedContractService.get_signed_contract_by_hash(h)
                    if signed_contract != None:
                        signed_contracts.append(signed_contract)
                elif terminated_contract_section:
                    terminated_contract = self.rs.get_object_by_full_key('terminated_contract:' + h, TerminatedContract, r)
                    if terminated_contract != None:
                        terminated_contracts.append(terminated_contract)
                else:
                    t = self.rs.get_object_by_full_key(prefix + ":" + h, obj, r)
                    temp_list.append(t)

            contract_transactions = temp_list.copy()
            temp_list.clear()

            # create block object and return it
            block = Block(block_hash, signature, owner, prev_block, height, transactions, pos_transactions, contract_transactions, contracts, signed_contracts, terminated_contracts, timestamp)
            return block
        else:
            return None

    def get_max_block_height(self):
        """
        Gets the largest block height currently stored.

        Returns:
        int -- height of last block
        """
        r = self._connect()
        if not r.exists(self.max_block_height):
            r.set(self.max_block_height, 0)
        return int(r.get(self.max_block_height))

    def find_by_height(self, block_height):
        """
        Find a block using it's height. Will return the block object, fully populated with all of the objects encased in it.

        Arguments:
        block_height    -- height of the block to retrieve

        Returns:
        block object    -- Block object containing all of the objects included in the block
        """
        r = self._connect()
        hashes = r.smembers("block:" + str(block_height))
        blocks = list()
        for h in hashes:
            block = self.find_by_hash(h)
            blocks.append(block)
        return blocks

    def get_blocks_after_height(self, block_height):
        """
        Get all blocks chained after a given height.

        Arguments:
        block_height    -- Block height of block to retrieve all blocks after

        Returns:
        list    -- list containing all of the blocks in the chain after the supplied block height
        """
        max_height = self.get_max_block_height()
        blocks = list()
        # want blocks between current and the max height
        if int(max_height) > int(block_height):
            # get blocks by height for heights between the supplied and max
            for height in range(int(block_height), int(max_height) + 1):
                new_blocks = self.find_by_height(height)
                blocks.extend(new_blocks)
        return blocks

    def get_blocks_after_hash(self, block_hash):
        """
        Get all blocks chained after a given hash.

        Arguments:
        block_hash  -- Block hash of block to retrieve all blocks after

        Returns:
        list    -- list containing all of the blocks in the chain after the supplied block hash
        """
        current_block = self.find_by_hash(block_hash)
        return self.get_blocks_after_height(current_block.height)

    def get_all_block_hashes(self):
        """
        Get all block hashes.

        Returns:
        list    -- list of strings containing all block hashes
        """
        r = self._connect()
        block_hashes = r.zrange('blocks', 0, -1)
        return block_hashes

    def _connect(self):
        # charset and decode_responses will need to be removed if we want this to be actually stored
        # as bytes (per: https://stackoverflow.com/questions/25745053/about-char-b-prefix-in-python3-4-1-client-connect-to-redis)
        return redis.StrictRedis(self.host, self.port, db=0, charset="utf-8", decode_responses="True")

    def get_all_transaction_up_to_block(self, block_hash):
        block = self.find_by_hash(block_hash)
        if block:
            transactions = block.transactions
            while block.prev_block:
                block = self.find_by_hash(block.prev_block)
                transactions.extend(block.transactions)
            return {transaction._hash: transaction for transaction in transactions}
        else:
            return {}

    def get_all_pos_transaction(self, block_hash):
        block = self.find_by_hash(block_hash)
        if block:
            pos_transactions = block.pos_transactions
            while block.prev_block:
                block = self.find_by_hash(block.prev_block)
                pos_transactions.extend(block.pos_transactions)
            return {pos_transaction._hash: pos_transaction for pos_transaction in pos_transactions}


    def get_all_objects_up_to_block(self, block):
        if block:
            transactions = block.transactions.copy()
            pos_transactions = block.pos_transactions.copy()
            contract_transactions = block.contract_transactions.copy()
            contracts = block.contracts.copy()
            signed_contracts = block.signed_contracts.copy()
            terminated_contracts = block.terminated_contracts.copy()
            owners = [block.owner]

            while block.prev_block:
                block = self.find_by_hash(block.prev_block)
                transactions.extend(block.transactions)
                pos_transactions.extend(block.pos_transactions)
                contract_transactions.extend(block.contract_transactions)
                contracts.extend(block.contracts)
                signed_contracts.extend(block.signed_contracts)
                terminated_contracts.extend(block.terminated_contracts)
                owners.append(block.owner)
            # convert to dictionaries
            transaction_dict = {transaction._hash: transaction for transaction in transactions}
            pos_transaction_dict = {pos_transaction._hash: pos_transaction for pos_transaction in pos_transactions}
            contract_transaction_dict = {contract_transaction._hash: contract_transaction for contract_transaction in contract_transactions}
            contract_dict = {contract._hash: contract for contract in contracts}
            signed_contract_dict = {signed_contract._hash: signed_contract for signed_contract in signed_contracts}
            terminated_contract_dict = {terminated_contract._hash: terminated_contract for terminated_contract in terminated_contracts}
            return {'transactions': transaction_dict, 'pos_transactions': pos_transaction_dict, 'contract_transactions': contract_transaction_dict, 'contracts': contract_dict, 'signed_contracts': signed_contract_dict, 'terminated_contracts': terminated_contract_dict, 'owners': owners}
        else:
            return {}