Source code for bbc_ledger

# -*- coding: utf-8 -*-
import os
import sqlite3
import binascii

import sys
sys.path.extend(["../../"])
from bbc1.common import logger
from bbc1.core.bbc_types import ResourceType


transaction_tbl_definition = [
    ["transaction_id", "BLOB"], ["transaction_data", "BLOB"],
]

asset_info_definition = [
    ["id", "INTEGER"], ["transaction_id", "BLOB"], ["asset_group_id", "BLOB"],
    ["asset_id", "BLOB"], ["user_id", "BLOB"],
]

topology_info_definition = [
    ["id", "INTEGER"], ["transaction_id", "BLOB"], ["resource_type", "INTEGER"], ["resource_id", "BLOB"]
]


[docs]class BBcLedger: """ Database manager SQL style only (for PoC alpha version) """ def __init__(self, config, dbtype="sqlite", loglevel="all", logname=None): """ only support sqlite3 :param dbtype: type of database system """ self.config = config conf = self.config.get_config() self.logger = logger.get_logger(key="bbc_ledger", level=loglevel, logname=logname) if 'ledger' not in conf: self.logger.error("No 'ledger' entry in config!!") os._exit(1) if 'type' not in conf['ledger']: self.logger.error("No 'ledger'.'type' entry in config!!") os._exit(1) if conf['ledger']['type'] != "sqlite3": self.logger.error("Currently, only sqlite3 is supported.") os._exit(1) self.dbtype = dbtype self.db_name = dict() self.db = dict() self.db_cur = dict()
[docs] def add_domain(self, domain_id): """ Add domain in the ledger :param domain_id: :return: """ conf = self.config.get_config() self.db_name[domain_id] = dict() domain_id_str = binascii.b2a_hex(domain_id).decode() domain_dir = conf['workingdir'] + "/" + domain_id_str + "/" if not os.path.exists(domain_dir): os.mkdir(domain_dir, 0o777) self.db_name[domain_id]['transaction_db'] = domain_dir + \ conf['ledger'].get('transaction_db', "bbc_transaction.sqlite3") self.db_name[domain_id]['auxiliary_db'] = domain_dir + \ conf['ledger'].get('auxiliary_db', "bbc_aux.sqlite3") self.db[domain_id] = dict() self.db_cur[domain_id] = dict() self.create_table_in_db(domain_id, 'transaction_db', 'transaction_table', transaction_tbl_definition, primary_key=0, indices=[0]) self.create_table_in_db(domain_id, 'auxiliary_db', 'asset_info_table', asset_info_definition, primary_key=0, indices=[1, 2, 3, 4]) self.create_table_in_db(domain_id, 'auxiliary_db', 'topology_table', topology_info_definition, primary_key=0, indices=[1, 2, 3])
[docs] def open_db(self, domain_id, dbname): """ (internal use) open DB :param domain_id: :param dbname: :return: """ if domain_id not in self.db or domain_id not in self.db_cur: return self.db[domain_id][dbname] = sqlite3.connect(self.db_name[domain_id][dbname], isolation_level=None) self.db_cur[domain_id][dbname] = self.db[domain_id][dbname].cursor()
[docs] def close_db(self, domain_id, dbname): """ (internal use) close DB :param domain_id: :param dbname: :return: """ if domain_id not in self.db or domain_id not in self.db_cur: return self.db_cur[domain_id][dbname].close() self.db[domain_id][dbname].close()
[docs] def create_table_in_db(self, domain_id, dbname, tbl, tbl_definition, primary_key=0, indices=[]): """ (internal use) Create a new table in a DB :param domain_id: :param dbname: :param tbl: :param tbl_definition: :param primary_keys: :param indices: :return: """ if domain_id not in self.db or domain_id not in self.db_cur or domain_id not in self.db_name: return if self.check_table_existence(domain_id, dbname, tbl) is not None: return sql = "CREATE TABLE %s " % tbl sql += "(" sql += ", ".join(["%s %s" % (d[0],d[1]) for d in tbl_definition]) sql += ", PRIMARY KEY ("+tbl_definition[primary_key][0]+")" sql += ");" self.exec_sql(domain_id, dbname, sql) for idx in indices: self.exec_sql(domain_id, dbname, "CREATE INDEX %s_idx_%d ON %s (%s);" % (tbl, idx, tbl, tbl_definition[idx][0]))
[docs] def exec_sql_fetchone(self, domain_id, dbname, sql, *dat): """ (internal use) Exec SQL and get one record :param domain_id: :param dbname: :param sql: :param dat: :return: """ if domain_id not in self.db or domain_id not in self.db_cur or domain_id not in self.db_name: return None if dbname not in self.db[domain_id]: self.open_db(domain_id, dbname) if len(dat) > 0: ret = self.db_cur[domain_id][dbname].execute(sql, (*dat,)).fetchone() else: ret = self.db_cur[domain_id][dbname].execute(sql).fetchone() if ret is not None: ret = list(ret) return ret
[docs] def exec_sql(self, domain_id, dbname, sql, *dat): """ (internal use) Exec SQL and get all records :param domain_id: :param dbname: :param sql: :param dat: :return: """ if domain_id not in self.db or domain_id not in self.db_cur or domain_id not in self.db_name: return None if dbname not in self.db[domain_id]: self.open_db(domain_id, dbname) if len(dat) > 0: ret = self.db_cur[domain_id][dbname].execute(sql, (*dat,)) else: ret = self.db_cur[domain_id][dbname].execute(sql) if ret is not None: ret = list(ret) return ret
[docs] def check_table_existence(self, domain_id, dbname, name): """ (internal use) checking table existence :param domain_id: :param dbname: :param name: :return: the corresponding record array or None """ ret = self.exec_sql_fetchone(domain_id, dbname, "select * from sqlite_master where type='table' and name=?", name) return ret
[docs] def find_transaction_locally(self, domain_id, transaction_id): """ Find transaction data :param domain_id: :param transaction_id: :return: """ row = self.exec_sql_fetchone(domain_id, "transaction_db", "select * from transaction_table where transaction_id = ?", transaction_id) if row is not None: return row[1] return None
[docs] def find_by_sql_in_local_auxiliary_db(self, domain_id, sql, *dat): """ find entries by SQL :param domain_id: :param sql: :param *dat: :return: """ return self.exec_sql(domain_id, "auxiliary_db", sql, *dat)
[docs] def insert_transaction_locally(self, domain_id, transaction_id, data): """ Insert data in the local ledger :param domain_id: :param transaction_id: Transaction_ID :param data: Transaction Data (serialized) :param require_uniqueness: Ignore uniqueness if True :return: True/False """ if self.exec_sql_fetchone(domain_id, "transaction_db", "select * from transaction_table where transaction_id = ?", transaction_id) is not None: return False self.exec_sql(domain_id, "transaction_db", "insert into transaction_table values (?, ?)", transaction_id, data) return True
[docs] def insert_asset_info_locally(self, domain_id, transaction_id, asset_group_id, asset_id, user_id): """ Insert data in the local ledger :param domain_id: :param transaction_id: :param asset_group_id: :param asset_id: :param user_id: :return: True/False """ if self.exec_sql_fetchone(domain_id, "auxiliary_db", "select * from asset_info_table where transaction_id = ? AND asset_group_id = ? AND asset_id = ? AND user_id = ?", transaction_id, asset_group_id, asset_id, user_id) is not None: return False self.exec_sql(domain_id, "auxiliary_db", "insert into asset_info_table (transaction_id, asset_group_id, " "asset_id, user_id) values (?, ?, ?, ?)", transaction_id, asset_group_id, asset_id, user_id) return True
[docs] def insert_topology_info_locally(self, domain_id, transaction_id, resource_type, resource_id): """ Insert topology data for transactions :param domain_id: :param transaction_id: :param resource_type: :param resource_id: :return: """ if self.exec_sql_fetchone(domain_id, "auxiliary_db", "select * from topology_table where transaction_id = ? AND resource_type = ? AND resource_id = ?", transaction_id, resource_type, resource_id) is not None: return False self.exec_sql(domain_id, "auxiliary_db", "insert into topology_table (transaction_id, resource_type, " "resource_id) values (?, ?, ?)", transaction_id, resource_type, resource_id) return True
[docs] def remove(self, domain_id, transaction_id): """ Remove all data with the specified transaction_id :param domain_id: :param transaction_id: Transaction_ID :return: True/False """ if self.exec_sql_fetchone(domain_id, "transaction_db", "select * from transaction_table where transaction_id = ?", transaction_id) is not None: self.exec_sql(domain_id, "transaction_db", "delete from transaction_table where transaction_id = ?", transaction_id) if self.exec_sql_fetchone(domain_id, "auxiliary_db", "select * from asset_info_table where transaction_id = ?", transaction_id) is not None: self.exec_sql(domain_id, "auxiliary_db", "delete from asset_info_table where transaction_id = ?", transaction_id) if self.exec_sql_fetchone(domain_id, "auxiliary_db", "select * from topology_table where transaction_id = ?", transaction_id) is not None: self.exec_sql(domain_id, "auxiliary_db", "delete from topology_table where transaction_id = ?", transaction_id) self.exec_sql(domain_id, "auxiliary_db", "delete from topology_table where resource_id = ?", transaction_id) return True