diff --git a/plasma_cash/utils/db/__init__.py b/plasma_cash/child_chain/db/__init__.py similarity index 100% rename from plasma_cash/utils/db/__init__.py rename to plasma_cash/child_chain/db/__init__.py diff --git a/plasma_cash/utils/db/db_interface.py b/plasma_cash/child_chain/db/db_interface.py similarity index 100% rename from plasma_cash/utils/db/db_interface.py rename to plasma_cash/child_chain/db/db_interface.py diff --git a/plasma_cash/utils/db/exceptions.py b/plasma_cash/child_chain/db/exceptions.py similarity index 100% rename from plasma_cash/utils/db/exceptions.py rename to plasma_cash/child_chain/db/exceptions.py diff --git a/plasma_cash/utils/db/leveldb.py b/plasma_cash/child_chain/db/leveldb.py similarity index 100% rename from plasma_cash/utils/db/leveldb.py rename to plasma_cash/child_chain/db/leveldb.py diff --git a/plasma_cash/utils/db/memory_db.py b/plasma_cash/child_chain/db/memory_db.py similarity index 100% rename from plasma_cash/utils/db/memory_db.py rename to plasma_cash/child_chain/db/memory_db.py diff --git a/plasma_cash/client/db/db_interface.py b/plasma_cash/client/db/db_interface.py new file mode 100644 index 0000000..038e89f --- /dev/null +++ b/plasma_cash/client/db/db_interface.py @@ -0,0 +1,12 @@ +import abc + + +class DbInterface(abc.ABC): + + @abc.abstractmethod + def get_history(self, uid): + return NotImplemented + + @abc.abstractmethod + def save_history(self, uid, history): + return NotImplemented diff --git a/plasma_cash/client/db/memory_db.py b/plasma_cash/client/db/memory_db.py new file mode 100644 index 0000000..5be32d2 --- /dev/null +++ b/plasma_cash/client/db/memory_db.py @@ -0,0 +1,13 @@ +from .db_interface import DbInterface + + +class MemoryDb(DbInterface): + + def __init__(self): + self.uid = {} + + def get_history(self, uid): + return self.uid.get(uid) + + def save_history(self, uid, history): + self.uid[uid] = history diff --git a/plasma_cash/client/exceptions.py b/plasma_cash/client/exceptions.py index 252176e..ac3be2b 100644 --- a/plasma_cash/client/exceptions.py +++ b/plasma_cash/client/exceptions.py @@ -1,2 +1,6 @@ class RequestFailedException(Exception): """request failed without success http status""" + + +class TxHistoryNotFoundException(Exception): + """tx history is not found for specific block number""" diff --git a/plasma_cash/client/history.py b/plasma_cash/client/history.py new file mode 100644 index 0000000..511a8e3 --- /dev/null +++ b/plasma_cash/client/history.py @@ -0,0 +1,36 @@ +import rlp +from rlp.sedes import big_endian_int, binary, CountableList + +from .exceptions import TxHistoryNotFoundException + + +class History(rlp.Serializable): + EMPTY_TX = b'\x00' * 32 + + fields = [ + ('latest_tx_blk_num', big_endian_int), + ('tx_history', CountableList(binary)), + ('proofs', CountableList(binary)), + ('blk_num', CountableList(big_endian_int)) + ] + + def __init__(self, deposit_tx_blk_num, tx, proof): + self.latest_tx_blk_num = deposit_tx_blk_num + self.tx_history = [tx] + self.proofs = [proof] + self.blk_num = [deposit_tx_blk_num] + + def update_tx_history(self, blk_num, tx, proof): + if blk_num not in self.blk_num: + if tx != self.EMPTY_TX: + self.latest_tx_blk_num = max(self.latest_tx_blk_num, blk_num) + self.tx_history.append(tx) + self.proofs.append(proof) + self.blk_num.append(blk_num) + + def get_data_by_block(self, blk_num): + try: + idx = self.blk_num.index(blk_num) + except ValueError: + raise TxHistoryNotFoundException('tx history not found') + return self.tx_history[idx], self.proofs[idx] diff --git a/plasma_cash/dependency_config.py b/plasma_cash/dependency_config.py index 57e46e2..32bec6c 100644 --- a/plasma_cash/dependency_config.py +++ b/plasma_cash/dependency_config.py @@ -1,11 +1,11 @@ import os from plasma_cash.child_chain.child_chain import ChildChain +from plasma_cash.child_chain.db.leveldb import LevelDb +from plasma_cash.child_chain.db.memory_db import MemoryDb from plasma_cash.client.child_chain_client import ChildChainClient from plasma_cash.config import PROJECT_DIR, db_config, plasma_config from plasma_cash.root_chain.deployer import Deployer -from plasma_cash.utils.db.leveldb import LevelDb -from plasma_cash.utils.db.memory_db import MemoryDb class DependencyContainer(object): @@ -13,7 +13,6 @@ def __init__(self): self._root_chain = None self._child_chain = None self._child_chain_client = None - self._client = None self._db = None def get_db(self): diff --git a/unit_tests/utils/db/__init__.py b/unit_tests/child_chain/db/__init__.py similarity index 100% rename from unit_tests/utils/db/__init__.py rename to unit_tests/child_chain/db/__init__.py diff --git a/unit_tests/utils/db/test_leveldb.py b/unit_tests/child_chain/db/test_leveldb.py similarity index 90% rename from unit_tests/utils/db/test_leveldb.py rename to unit_tests/child_chain/db/test_leveldb.py index b0a6aac..6106045 100644 --- a/unit_tests/utils/db/test_leveldb.py +++ b/unit_tests/child_chain/db/test_leveldb.py @@ -2,8 +2,8 @@ from mockito import ANY, when from plasma_cash.child_chain.block import Block -from plasma_cash.utils.db.exceptions import BlockAlreadyExistsException -from plasma_cash.utils.db.leveldb import LevelDb +from plasma_cash.child_chain.db.exceptions import BlockAlreadyExistsException +from plasma_cash.child_chain.db.leveldb import LevelDb from unit_tests.unstub_mixin import UnstubMixin @@ -35,7 +35,7 @@ class TestLevelDb(UnstubMixin): @pytest.fixture(scope='function') def db(self): db = FakeLevelDb() - (when('plasma_cash.utils.db.leveldb.plyvel') + (when('plasma_cash.child_chain.db.leveldb.plyvel') .DB(ANY, create_if_missing=True).thenReturn(db)) return LevelDb('test_db') diff --git a/unit_tests/utils/db/test_memory_db.py b/unit_tests/child_chain/db/test_memory_db.py similarity index 88% rename from unit_tests/utils/db/test_memory_db.py rename to unit_tests/child_chain/db/test_memory_db.py index 31579d6..56c40e6 100644 --- a/unit_tests/utils/db/test_memory_db.py +++ b/unit_tests/child_chain/db/test_memory_db.py @@ -1,7 +1,7 @@ import pytest -from plasma_cash.utils.db.exceptions import BlockAlreadyExistsException -from plasma_cash.utils.db.memory_db import MemoryDb +from plasma_cash.child_chain.db.exceptions import BlockAlreadyExistsException +from plasma_cash.child_chain.db.memory_db import MemoryDb class TestMemoryDb(object): diff --git a/unit_tests/child_chain/test_child_chain.py b/unit_tests/child_chain/test_child_chain.py index 8375e0d..b4b6029 100644 --- a/unit_tests/child_chain/test_child_chain.py +++ b/unit_tests/child_chain/test_child_chain.py @@ -7,6 +7,7 @@ from plasma_cash.child_chain.block import Block from plasma_cash.child_chain.child_chain import ChildChain +from plasma_cash.child_chain.db.memory_db import MemoryDb from plasma_cash.child_chain.exceptions import (InvalidBlockNumException, InvalidBlockSignatureException, InvalidTxSignatureException, @@ -15,7 +16,6 @@ TxAmountMismatchException, TxWithSameUidAlreadyExists) from plasma_cash.child_chain.transaction import Transaction -from plasma_cash.utils.db.memory_db import MemoryDb from unit_tests.unstub_mixin import UnstubMixin diff --git a/unit_tests/client/db/__init__.py b/unit_tests/client/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/unit_tests/client/db/test_memory_db.py b/unit_tests/client/db/test_memory_db.py new file mode 100644 index 0000000..a87da2b --- /dev/null +++ b/unit_tests/client/db/test_memory_db.py @@ -0,0 +1,19 @@ +import pytest + +from plasma_cash.client.db.memory_db import MemoryDb + + +class TestMemoryDb(object): + @pytest.fixture(scope='function') + def db(self): + return MemoryDb() + + def test_save_and_get_history(self, db): + DUMMY_HISTORY = 'dummy history' + DUMMY_UID = 1 + db.save_history(DUMMY_UID, DUMMY_HISTORY) + assert db.get_history(DUMMY_UID) == DUMMY_HISTORY + + def test_history_not_found(self, db): + DUMMY_UID = 1 + assert db.get_history(DUMMY_UID) is None diff --git a/unit_tests/client/test_history.py b/unit_tests/client/test_history.py new file mode 100644 index 0000000..735e3e7 --- /dev/null +++ b/unit_tests/client/test_history.py @@ -0,0 +1,69 @@ +import pytest + +from plasma_cash.client.exceptions import TxHistoryNotFoundException +from plasma_cash.client.history import History + + +class TestHistory(object): + DUMMY_DEPOSIT_BLK_NUM = 1 + DUMMY_TX1 = 'dummy tx1' + DUMMY_PROOF1 = 'dummy proof1' + + @pytest.fixture(scope='function') + def history(self): + return History(self.DUMMY_DEPOSIT_BLK_NUM, self.DUMMY_TX1, self.DUMMY_PROOF1) + + def test_constructor(self): + history = History(self.DUMMY_DEPOSIT_BLK_NUM, self.DUMMY_TX1, self.DUMMY_PROOF1) + assert history.latest_tx_blk_num == self.DUMMY_DEPOSIT_BLK_NUM + assert history.tx_history == [self.DUMMY_TX1] + assert history.proofs == [self.DUMMY_PROOF1] + assert history.blk_num == [self.DUMMY_DEPOSIT_BLK_NUM] + + def test_update_history(self, history): + DUMMY_BLK_NUM = 2 + DUMMY_TX2 = 'dummy tx2' + DUMMY_PROOF2 = 'dummy proof2' + history.update_tx_history(DUMMY_BLK_NUM, DUMMY_TX2, DUMMY_PROOF2) + assert history.latest_tx_blk_num == 2 + assert history.tx_history == [self.DUMMY_TX1, DUMMY_TX2] + assert history.proofs == [self.DUMMY_PROOF1, DUMMY_PROOF2] + assert history.blk_num == [1, 2] + + def test_update_empty_history(self, history): + DUMMY_BLK_NUM = 2 + EMPTY_TX = b'\x00' * 32 + NON_INCLUSIVE_PROOF = 'non inclusion proof' + history.update_tx_history(DUMMY_BLK_NUM, EMPTY_TX, NON_INCLUSIVE_PROOF) + assert history.latest_tx_blk_num == 1 + assert history.tx_history == [self.DUMMY_TX1, EMPTY_TX] + assert history.proofs == [self.DUMMY_PROOF1, NON_INCLUSIVE_PROOF] + assert history.blk_num == [1, 2] + + def test_update_history_with_wrong_order(self, history): + DUMMY_BLK_NUM = 3 + DUMMY_TX3 = 'dummy tx3' + DUMMY_PROOF3 = 'dummy proof3' + history.update_tx_history(DUMMY_BLK_NUM, DUMMY_TX3, DUMMY_PROOF3) + assert history.tx_history == [self.DUMMY_TX1, DUMMY_TX3] + assert history.proofs == [self.DUMMY_PROOF1, DUMMY_PROOF3] + assert history.blk_num == [1, 3] + + DUMMY_BLK_NUM = 2 + DUMMY_TX2 = 'dummy tx2' + DUMMY_PROOF2 = 'dummy proof2' + history.update_tx_history(DUMMY_BLK_NUM, DUMMY_TX2, DUMMY_PROOF2) + assert history.tx_history == [self.DUMMY_TX1, DUMMY_TX3, DUMMY_TX2] + assert history.proofs == [self.DUMMY_PROOF1, DUMMY_PROOF3, DUMMY_PROOF2] + assert history.blk_num == [1, 3, 2] + + def test_get_data_by_block(self, history): + DUMMY_BLK_NUM = 1 + tx, proof = history.get_data_by_block(DUMMY_BLK_NUM) + assert tx == history.tx_history[0] + assert proof == history.proofs[0] + + def test_get_data_by_block_failed(self, history): + DUMMY_BLK_NUM = 2 + with pytest.raises(TxHistoryNotFoundException): + history.get_data_by_block(DUMMY_BLK_NUM)