From c3d0bca921cc60e9aaaf975305893d2f3b6a5dfc Mon Sep 17 00:00:00 2001 From: Vanshdeep Singh Date: Fri, 17 Aug 2018 07:47:11 +0000 Subject: [PATCH] Memoize data --- bigchaindb/backend/localmongodb/schema.py | 3 +- bigchaindb/common/transaction.py | 64 +++++++++++++++++++++-- bigchaindb/lib.py | 9 +++- bigchaindb/models.py | 2 +- 4 files changed, 70 insertions(+), 8 deletions(-) diff --git a/bigchaindb/backend/localmongodb/schema.py b/bigchaindb/backend/localmongodb/schema.py index 1e7f3614..746fcd6a 100644 --- a/bigchaindb/backend/localmongodb/schema.py +++ b/bigchaindb/backend/localmongodb/schema.py @@ -56,6 +56,7 @@ def create_transactions_secondary_index(conn, dbname): # to query the transactions for a transaction id, this field is unique conn.conn[dbname]['transactions'].create_index('id', + unique=True, name='transaction_id') # secondary index for asset uuid, this field is unique @@ -90,7 +91,7 @@ def create_assets_secondary_index(conn, dbname): def create_blocks_secondary_index(conn, dbname): conn.conn[dbname]['blocks']\ - .create_index([('height', DESCENDING)], name='height') + .create_index([('height', DESCENDING)], name='height', unique=True) def create_metadata_secondary_index(conn, dbname): diff --git a/bigchaindb/common/transaction.py b/bigchaindb/common/transaction.py index f232eb81..055c20e1 100644 --- a/bigchaindb/common/transaction.py +++ b/bigchaindb/common/transaction.py @@ -9,6 +9,8 @@ Attributes: from collections import namedtuple from copy import deepcopy from functools import reduce +import functools +import ujson import base58 from cryptoconditions import Fulfillment, ThresholdSha256, Ed25519Sha256 @@ -38,6 +40,51 @@ UnspentOutput = namedtuple( ) +def memoize(func): + cache = func.cache = {} + + @functools.wraps(func) + def memoized_func(*args, **kwargs): + key = args[1]['id'] + if key not in cache: + cache[key] = func(*args, **kwargs) + + return cache[key] + + return memoized_func + + +def memoize_class(func): + cache = func.cache = {} + + @functools.wraps(func) + def memoized_func(*args, **kwargs): + key = args[0].id + if key not in cache: + cache[key] = func(*args, **kwargs) + + return cache[key] + + return memoized_func + + +def memoize_input_valid(func): + cache = func.cache = {} + + @functools.wraps(func) + def memoized_func(*args, **kwargs): + inp_fulfillment = args[1].fulfillment + op = args[2] + msg = args[3] + key = '{}.{}.{}'.format(inp_fulfillment, op, msg) + if key not in cache: + cache[key] = func(*args, **kwargs) + + return cache[key] + + return memoized_func + + class Input(object): """A Input is used to spend assets locked by an Output. @@ -496,7 +543,7 @@ class Transaction(object): VERSION = '2.0' def __init__(self, operation, asset, inputs=None, outputs=None, - metadata=None, version=None, hash_id=None): + metadata=None, version=None, hash_id=None, tx_dict=None): """The constructor allows to create a customizable Transaction. Note: @@ -549,6 +596,7 @@ class Transaction(object): self.outputs = outputs or [] self.metadata = metadata self._id = hash_id + self.tx_dict = tx_dict @property def unspent_outputs(self): @@ -986,7 +1034,7 @@ class Transaction(object): raise ValueError('Inputs and ' 'output_condition_uris must have the same count') - tx_dict = self.to_dict() + tx_dict = self.tx_dict # self.to_dict() tx_dict = Transaction._remove_signatures(tx_dict) tx_dict['id'] = None tx_serialized = Transaction._to_str(tx_dict) @@ -999,6 +1047,7 @@ class Transaction(object): return all(validate(i, cond) for i, cond in enumerate(output_condition_uris)) + @memoize_input_valid def _input_valid(self, input_, operation, message, output_condition_uri=None): """Validates a single Input against a single Output. @@ -1044,6 +1093,7 @@ class Transaction(object): ffill_valid = parsed_ffill.validate(message=message.digest()) return output_valid and ffill_valid + @memoize_class def to_dict(self): """Transforms the object to a Python dictionary. @@ -1092,7 +1142,8 @@ class Transaction(object): return self._id def to_hash(self): - return self.to_dict()['id'] + return self.id + # return self.to_dict()['id'] @staticmethod def _to_str(value): @@ -1146,7 +1197,9 @@ class Transaction(object): tx_body (dict): The Transaction to be transformed. """ # NOTE: Remove reference to avoid side effects - tx_body = deepcopy(tx_body) + # tx_body = deepcopy(tx_body) + # tx_body = rapidjson.loads(rapidjson.dumps(tx_body)) + tx_body = ujson.loads(ujson.dumps(tx_body)) try: proposed_tx_id = tx_body['id'] except KeyError: @@ -1163,6 +1216,7 @@ class Transaction(object): raise InvalidHash(err_msg.format(proposed_tx_id)) @classmethod + @memoize def from_dict(cls, tx, skip_schema_validation=True): """Transforms a Python dictionary to a Transaction object. @@ -1180,7 +1234,7 @@ class Transaction(object): inputs = [Input.from_dict(input_) for input_ in tx['inputs']] outputs = [Output.from_dict(output) for output in tx['outputs']] return cls(tx['operation'], tx['asset'], inputs, outputs, - tx['metadata'], tx['version'], hash_id=tx['id']) + tx['metadata'], tx['version'], hash_id=tx['id'], tx_dict=tx) @classmethod def from_db(cls, bigchain, tx_dict_list): diff --git a/bigchaindb/lib.py b/bigchaindb/lib.py index a0b7e727..ec6f157b 100644 --- a/bigchaindb/lib.py +++ b/bigchaindb/lib.py @@ -139,7 +139,7 @@ class BigchainDB(object): txn_metadatas = [] for transaction_obj in transactions: # self.update_utxoset(transaction) - transaction = transaction_obj.to_dict() + transaction = transaction_obj.tx_dict if transaction['operation'] == transaction_obj.CREATE: asset = transaction.pop('asset') asset['id'] = transaction['id'] @@ -239,6 +239,13 @@ class BigchainDB(object): return backend.query.delete_unspent_outputs( self.connection, *unspent_outputs) + def is_commited(self, transaction_id): + transaction = backend.query.get_transaction(self.connection, transaction_id) + if transaction: + return True + else: + return False + def get_transaction(self, transaction_id): transaction = backend.query.get_transaction(self.connection, transaction_id) diff --git a/bigchaindb/models.py b/bigchaindb/models.py index 58cc9a99..6a8d4720 100644 --- a/bigchaindb/models.py +++ b/bigchaindb/models.py @@ -23,7 +23,7 @@ class Transaction(Transaction): if self.operation == Transaction.CREATE: duplicates = any(txn for txn in current_transactions if txn.id == self.id) - if bigchain.get_transaction(self.to_dict()['id']) or duplicates: + if bigchain.is_commited(self.id) or duplicates: raise DuplicateTransaction('transaction `{}` already exists' .format(self.id))