dragonchain/dragonchain

View on GitHub
dragonchain/lib/dao/transaction_dao.py

Summary

Maintainability
A
1 hr
Test Coverage
B
86%
# Copyright 2020 Dragonchain, Inc.
# Licensed under the Apache License, Version 2.0 (the "Apache License")
# with the following modification; you may not use this file except in
# compliance with the Apache License and the following modification to it:
# Section 6. Trademarks. is deleted and replaced with:
#      6. Trademarks. This License does not grant permission to use the trade
#         names, trademarks, service marks, or product names of the Licensor
#         and its affiliates, except as required to comply with Section 4(c) of
#         the License and to reproduce the content of the NOTICE file.
# You may obtain a copy of the Apache License at
#     http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the Apache License with the above modification is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the Apache License for the specific
# language governing permissions and limitations under the Apache License.

import uuid
import time
from typing import TYPE_CHECKING, Dict, Any

import redis

from dragonchain.lib.database import redisearch
from dragonchain.lib.interfaces import storage
from dragonchain.lib.dto import transaction_model
from dragonchain.lib import namespace
from dragonchain.lib import queue
from dragonchain.lib import keys
from dragonchain import logger

if TYPE_CHECKING:
    from dragonchain.lib.dto import l1_block_model

FOLDER = "TRANSACTION"
S3_OBJECT_ID = "s3_object_id"

_log = logger.get_logger()


def ledger_contract_action(action: str, txn_type: str, entrypoint: str, image_digest: str) -> None:
    """Ledgers contract data when submit contract is a success
    Args:
        action (Enum): Which action to perform when ledgering
        txn_type (str): Transaction type to post to the chain
        image_digest (str): Docker image SHA-256 to use in ledgering
    """
    model = transaction_model.TransactionModel(
        txn_type=namespace.Namespaces.Contract.value,
        dc_id=keys.get_public_id(),
        txn_id=str(uuid.uuid4()),
        tag=f"contract:{txn_type}",
        timestamp=str(int(time.time())),
        payload={"action": action, "txn_type": txn_type, "contract_entrypoint": entrypoint, "image_digest": image_digest},
    )
    queue.enqueue_generic(model.export_as_queue_task(), queue=queue.INCOMING_TX_KEY, deadline=0)


def store_full_txns(block_model: "l1_block_model.L1BlockModel") -> None:
    """
    Store the transactions object as a single file per block in storage.
    Also updates the indexes for each indexed transaction in ES with block information.
    """
    _log.info("[TRANSACTION DAO] Putting transaction to storage")
    storage.put(f"{FOLDER}/{block_model.block_id}", block_model.export_as_full_transactions().encode("utf-8"))
    block_model.store_transaction_payloads()
    txn_dict: Dict[str, Dict[str, Dict[str, Any]]] = {}
    txn_dict[redisearch.Indexes.transaction.value] = {}
    # O(N) loop where N = # of txn
    # Could optimize by grouping indexing of transactions in the block with matchking txn_types using redisearch.put_many_documents
    for txn in block_model.transactions:
        txn_dict[redisearch.Indexes.transaction.value][f"txn-{txn.txn_id}"] = {"block_id": txn.block_id}
        try:
            if not txn_dict.get(txn.txn_type):
                txn_dict[txn.txn_type] = {}
            txn_dict[txn.txn_type][txn.txn_id] = txn.export_as_search_index()
        except redis.exceptions.ResponseError as e:
            # If the index doesn't exist, we don't care that we couldn't place the index (transaction type is probably deleted)
            if str(e) != "Unknown index name":
                raise
            else:
                _log.warning(f"Txn type {txn.txn_type} for txn {txn.txn_id} failed to index. (Transaction type may simply be deleted?) Ignoring")
    # O(N) loop where N = # of txn types + 1
    for key, value in txn_dict.items():  # key = index, value = document
        redisearch.put_many_documents(key, value, upsert=True)