diff --git a/src/sakia/data/entities/__init__.py b/src/sakia/data/entities/__init__.py index 99cf36c9a513103de5abb7a6f2cd7fcf7b42a502..40b3174b2e6f611d6ac04904801669e942085e5a 100644 --- a/src/sakia/data/entities/__init__.py +++ b/src/sakia/data/entities/__init__.py @@ -1,6 +1,5 @@ from .identity import Identity -from .community import Community -from .blockchain import Blockchain +from .blockchain import Blockchain, BlockchainParameters from .certification import Certification from .transaction import Transaction from .node import Node diff --git a/src/sakia/data/entities/blockchain.py b/src/sakia/data/entities/blockchain.py index bdf7bf2c43e6fd6d0aa392fa48c2357a42e10e3a..72d44d383a99239aee92d7ab50345c55d3d304fe 100644 --- a/src/sakia/data/entities/blockchain.py +++ b/src/sakia/data/entities/blockchain.py @@ -2,8 +2,46 @@ import attr from duniterpy.documents import block_uid, BlockUID +@attr.s() +class BlockchainParameters: + # The decimal percent growth of the UD every [dt] period + c = attr.ib(convert=float, default=0, cmp=False) + # Time period between two UD in seconds + dt = attr.ib(convert=int, default=0, cmp=False) + # UD(0), i.e. initial Universal Dividend + ud0 = attr.ib(convert=int, default=0, cmp=False) + # Minimum delay between 2 certifications of a same issuer, in seconds. Must be positive or zero + sig_period = attr.ib(convert=int, default=0, cmp=False) + # Maximum quantity of active certifications made by member + sig_stock = attr.ib(convert=int, default=0, cmp=False) + # Maximum delay in seconds a certification can wait before being expired for non-writing + sig_window = attr.ib(convert=int, default=0, cmp=False) + # Maximum age of a active signature (in seconds) + sig_validity = attr.ib(convert=int, default=0, cmp=False) + # Minimum quantity of signatures to be part of the WoT + sig_qty = attr.ib(convert=int, default=0, cmp=False) + # Minimum decimal percent of sentries to reach to match the distance rule + xpercent = attr.ib(convert=float, default=0, cmp=False) + # Maximum age of an active membership( in seconds) + ms_validity = attr.ib(convert=int, default=0, cmp=False) + # Maximum distance between each WoT member and a newcomer + step_max = attr.ib(convert=int, default=0, cmp=False) + # Number of blocks used for calculating median time + median_time_blocks = attr.ib(convert=int, default=0, cmp=False) + # The average time for writing 1 block (wished time) in seconds + avg_gen_time = attr.ib(convert=int, default=0, cmp=False) + # The number of blocks required to evaluate again PoWMin value + dt_diff_eval = attr.ib(convert=int, default=0, cmp=False) + # The number of previous blocks to check for personalized difficulty + blocks_rot = attr.ib(convert=int, default=0, cmp=False) + # The decimal percent of previous issuers to reach for personalized difficulty + percent_rot = attr.ib(convert=float, default=0, cmp=False) + + @attr.s() class Blockchain: + # Parameters in block 0 + parameters = attr.ib(default=BlockchainParameters()) # block number and hash current_buid = attr.ib(convert=block_uid, default=BlockUID.empty()) # Number of members diff --git a/src/sakia/data/entities/community.py b/src/sakia/data/entities/community.py deleted file mode 100644 index 2e483c091e0286046873def1bb908a573c12b56a..0000000000000000000000000000000000000000 --- a/src/sakia/data/entities/community.py +++ /dev/null @@ -1,42 +0,0 @@ -import attr - - -@attr.s() -class Community: - # The decimal percent growth of the UD every [dt] period - c = attr.ib(convert=float, default=0, cmp=False) - # Time period between two UD in seconds - dt = attr.ib(convert=int, default=0, cmp=False) - # UD(0), i.e. initial Universal Dividend - ud0 = attr.ib(convert=int, default=0, cmp=False) - # Minimum delay between 2 certifications of a same issuer, in seconds. Must be positive or zero - sig_period = attr.ib(convert=int, default=0, cmp=False) - # Maximum quantity of active certifications made by member - sig_stock = attr.ib(convert=int, default=0, cmp=False) - # Maximum delay in seconds a certification can wait before being expired for non-writing - sig_window = attr.ib(convert=int, default=0, cmp=False) - # Maximum age of a active signature (in seconds) - sig_validity = attr.ib(convert=int, default=0, cmp=False) - # Minimum quantity of signatures to be part of the WoT - sig_qty = attr.ib(convert=int, default=0, cmp=False) - # Minimum decimal percent of sentries to reach to match the distance rule - xpercent = attr.ib(convert=float, default=0, cmp=False) - # Maximum age of an active membership( in seconds) - ms_validity = attr.ib(convert=int, default=0, cmp=False) - # Maximum distance between each WoT member and a newcomer - step_max = attr.ib(convert=int, default=0, cmp=False) - # Number of blocks used for calculating median time - median_time_blocks = attr.ib(convert=int, default=0, cmp=False) - # The average time for writing 1 block (wished time) in seconds - avg_gen_time = attr.ib(convert=int, default=0, cmp=False) - # The number of blocks required to evaluate again PoWMin value - dt_diff_eval = attr.ib(convert=int, default=0, cmp=False) - # The number of previous blocks to check for personalized difficulty - blocks_rot = attr.ib(convert=int, default=0, cmp=False) - # The decimal percent of previous issuers to reach for personalized difficulty - percent_rot = attr.ib(convert=float, default=0, cmp=False) - # Currency name - currency = attr.ib(convert=str, default="", cmp=False) - - - diff --git a/src/sakia/data/processors/communities.py b/src/sakia/data/processors/communities.py deleted file mode 100644 index b9ad62a4e09e8bff3f4f7d4d2f9187dc139a656f..0000000000000000000000000000000000000000 --- a/src/sakia/data/processors/communities.py +++ /dev/null @@ -1,16 +0,0 @@ -import attr - - -@attr.s -class CommunityProcessor: - _repo = attr.ib() # :type sakia.data.repositories.CommunitiesRepo - - async def get_from_currency(self, currency): - """ - Get the community of a currency - - :param currency: - :rtype: sakia.data.entities.Community - """ - return self._repo.get_one(currency=currency) - diff --git a/src/sakia/data/repositories/__init__.py b/src/sakia/data/repositories/__init__.py index b047145e9178da572f0772ca381fef9ac3aec033..a7e8f3667e2f6335493a5e1f0c291265f7aa2eb2 100644 --- a/src/sakia/data/repositories/__init__.py +++ b/src/sakia/data/repositories/__init__.py @@ -1,5 +1,4 @@ from .identities import IdentitiesRepo -from .communities import CommunitiesRepo from .blockchains import BlockchainsRepo from .meta import MetaDatabase from .certifications import CertificationsRepo diff --git a/src/sakia/data/repositories/blockchains.py b/src/sakia/data/repositories/blockchains.py index cede12ddc233cc56b2162d6163c0cf9fea0653cd..055deab24b566301fa92073e7b8fc9b0d1ac13f8 100644 --- a/src/sakia/data/repositories/blockchains.py +++ b/src/sakia/data/repositories/blockchains.py @@ -1,6 +1,8 @@ +from typing import List + import attr -from ..entities import Blockchain +from ..entities import Blockchain, BlockchainParameters @attr.s(frozen=True) @@ -16,7 +18,8 @@ class BlockchainsRepo: :param sakia.data.entities.Blockchain blockchain: the blockchain to commit """ with self._conn: - blockchain_tuple = attr.astuple(blockchain) + blockchain_tuple = attr.astuple(blockchain.parameters) \ + + attr.astuple(blockchain, filter=attr.filters.exclude(Blockchain.parameters)) values = ",".join(['?'] * len(blockchain_tuple)) self._conn.execute("INSERT INTO blockchains VALUES ({0})".format(values), blockchain_tuple) @@ -26,7 +29,8 @@ class BlockchainsRepo: :param sakia.data.entities.Blockchain blockchain: the blockchain to update """ with self._conn: - updated_fields = attr.astuple(blockchain, filter=attr.filters.exclude(*BlockchainsRepo._primary_keys)) + updated_fields = attr.astuple(blockchain, filter=attr.filters.exclude( + Blockchain.parameters, *BlockchainsRepo._primary_keys)) where_fields = attr.astuple(blockchain, filter=attr.filters.include(*BlockchainsRepo._primary_keys)) self._conn.execute("""UPDATE blockchains SET current_buid=?, @@ -58,29 +62,49 @@ class BlockchainsRepo: c = self._conn.execute(request, tuple(values)) data = c.fetchone() if data: - return Blockchain(*data) + return Blockchain(BlockchainParameters(*data[:15]), *data[16:]) - def get_all(self, **search): + def get_all(self, offset=0, limit=1000, sort_by="currency", sort_order="ASC", **search) -> List[Blockchain]: """ Get all existing blockchain in the database corresponding to the search + :param int offset: offset in results to paginate + :param int limit: limit results to paginate + :param str sort_by: column name to sort by + :param str sort_order: sort order ASC or DESC :param dict search: the criterions of the lookup - :rtype: sakia.data.entities.Blockchain + :rtype: [sakia.data.entities.Blockchain] """ with self._conn: filters = [] values = [] - for k, v in search.items(): - operator = "LIKE" if k == "currency" else "=" - value = "%{value}%".format(value=v) if k == "currency" else v - filters.append("{key} {operator} ?".format(key=k, operator=operator)) - values.append(value) + if search: + for k, v in search.items(): + filters.append("{k}=?".format(k=k)) + values.append(v) - request = "SELECT * FROM blockchains WHERE {filters}".format(filters=" AND ".join(filters)) - - c = self._conn.execute(request, tuple(values)) + request = """SELECT * FROM blockchains WHERE {filters} + ORDER BY {sort_by} {sort_order} + LIMIT {limit} OFFSET {offset}""".format( + filters=" AND ".join(filters), + offset=offset, + limit=limit, + sort_by=sort_by, + sort_order=sort_order + ) + c = self._conn.execute(request, tuple(values)) + else: + request = """SELECT * FROM blockchains + ORDER BY {sort_by} {sort_order} + LIMIT {limit} OFFSET {offset}""".format( + offset=offset, + limit=limit, + sort_by=sort_by, + sort_order=sort_order + ) + c = self._conn.execute(request) datas = c.fetchall() if datas: - return [Blockchain(*data) for data in datas] + return [Blockchain(BlockchainParameters(*data[:15]), *data[16:]) for data in datas] return [] def drop(self, blockchain): diff --git a/src/sakia/data/repositories/communities.py b/src/sakia/data/repositories/communities.py deleted file mode 100644 index d2f21a1b73676c07be21d563da0374172ad1122b..0000000000000000000000000000000000000000 --- a/src/sakia/data/repositories/communities.py +++ /dev/null @@ -1,102 +0,0 @@ -import attr - -from ..entities import Community - - -@attr.s(frozen=True) -class CommunitiesRepo: - """The repository for Communities entities. - """ - _conn = attr.ib() # :type sqlite3.Connection - _primary_keys = (Community.currency,) - - def insert(self, community): - """ - Commit a community to the database - :param sakia.data.entities.Community community: the community to commit - """ - with self._conn: - community_tuple = attr.astuple(community) - values = ",".join(['?'] * len(community_tuple)) - self._conn.execute("INSERT INTO communities VALUES ({0})".format(values), community_tuple) - - def update(self, community): - """ - Update an existing community in the database - :param sakia.data.entities.Community community: the community to update - """ - with self._conn: - updated_fields = attr.astuple(community, filter=attr.filters.exclude(*CommunitiesRepo._primary_keys)) - where_fields = attr.astuple(community, filter=attr.filters.include(*CommunitiesRepo._primary_keys)) - self._conn.execute("""UPDATE communities SET - c=?, - dt=?, - ud0=?, - sig_period=?, - sig_stock=?, - sig_window=?, - sig_validity=?, - sig_qty=?, - xpercent=?, - ms_validity=?, - step_max=?, - median_time_blocks=?, - avg_gen_time=?, - dt_diff_eval=?, - blocks_rot=?, - percent_rot=? - WHERE - currency=?""", - updated_fields + where_fields) - - def get_one(self, **search): - """ - Get an existing community in the database - :param dict search: the criterions of the lookup - :rtype: sakia.data.entities.Community - """ - with self._conn: - filters = [] - values = [] - for k, v in search.items(): - filters.append("{k}=?".format(k=k)) - values.append(v) - - request = "SELECT * FROM communities WHERE {filters}".format(filters=" AND ".join(filters)) - - c = self._conn.execute(request, tuple(values)) - data = c.fetchone() - if data: - return Community(*data) - - def get_all(self, **search): - """ - Get all existing community in the database corresponding to the search - :param dict search: the criterions of the lookup - :rtype: sakia.data.entities.Community - """ - with self._conn: - filters = [] - values = [] - for k, v in search.items(): - operator = "LIKE" if k == "currency" else "=" - value = "%{value}%".format(value=v) if k == "currency" else v - filters.append("{key} {operator} ?".format(key=k, operator=operator)) - values.append(value) - - request = "SELECT * FROM communities WHERE {filters}".format(filters=" AND ".join(filters)) - - c = self._conn.execute(request, tuple(values)) - datas = c.fetchall() - if datas: - return [Community(*data) for data in datas] - return [] - - def drop(self, community): - """ - Drop an existing community from the database - :param sakia.data.entities.Community community: the community to update - """ - with self._conn: - where_fields = attr.astuple(community, filter=attr.filters.include(*CommunitiesRepo._primary_keys)) - self._conn.execute("DELETE FROM communities WHERE currency=?", where_fields) diff --git a/src/sakia/data/repositories/meta.sql b/src/sakia/data/repositories/meta.sql index 31e6af6c1431bd1637d8a1cd05605a9b8f52422e..f30c866d442537f026272a069cc02fd8dab6076f 100644 --- a/src/sakia/data/repositories/meta.sql +++ b/src/sakia/data/repositories/meta.sql @@ -16,8 +16,8 @@ CREATE TABLE IF NOT EXISTS identities( PRIMARY KEY (currency, pubkey, uid, blockstamp) ); --- COMMUNITIES TABLE -CREATE TABLE IF NOT EXISTS communities ( +-- BLOCKCHAIN TABLE +CREATE TABLE IF NOT EXISTS blockchains ( c FLOAT(1, 6), dt INT, ud0 INT, @@ -34,12 +34,6 @@ CREATE TABLE IF NOT EXISTS communities ( dt_diff_eval INT, blocks_rot INT, percent_rot FLOAT(1, 6), - currency VARCHAR(30), - PRIMARY KEY (currency) -); - --- BLOCKCHAIN TABLE -CREATE TABLE IF NOT EXISTS blockchains ( current_buid INT, nb_members INT, current_mass INT, diff --git a/src/sakia/tests/unit/data/test_blockchains_repo.py b/src/sakia/tests/unit/data/test_blockchains_repo.py index c52ef72229109bc5355753d8ed1abe4148b69746..8f831fd06dce814c07b387ab5d9e4577aff5ef11 100644 --- a/src/sakia/tests/unit/data/test_blockchains_repo.py +++ b/src/sakia/tests/unit/data/test_blockchains_repo.py @@ -3,7 +3,7 @@ import unittest from duniterpy.documents import BlockUID -from sakia.data.entities import Blockchain +from sakia.data.entities import Blockchain, BlockchainParameters from sakia.data.repositories import BlockchainsRepo, MetaDatabase @@ -23,6 +23,23 @@ class TestBlockchainsRepo(unittest.TestCase): meta_repo.upgrade_database() blockchains_repo = BlockchainsRepo(self.con) blockchains_repo.insert(Blockchain( + BlockchainParameters( + 0.1, + 86400, + 100000, + 10800, + 40, + 2629800, + 31557600, + 1, + 0.9, + 604800, + 5, + 12, + 300, + 25, + 10, + 0.66), "20-7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67", 10, 1000000, @@ -33,6 +50,23 @@ class TestBlockchainsRepo(unittest.TestCase): "testcurrency" )) blockchain = blockchains_repo.get_one(currency="testcurrency") + self.assertEqual(blockchain.parameters, BlockchainParameters( + 0.1, + 86400, + 100000, + 10800, + 40, + 2629800, + 31557600, + 1, + 0.9, + 604800, + 5, + 12, + 300, + 25, + 10, + 0.66)) self.assertEqual(blockchain.currency, "testcurrency") self.assertEqual(blockchain.current_buid, BlockUID(20, "7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67") @@ -49,6 +83,23 @@ class TestBlockchainsRepo(unittest.TestCase): meta_repo.upgrade_database() blockchains_repo = BlockchainsRepo(self.con) blockchains_repo.insert(Blockchain( + BlockchainParameters( + 0.1, + 86400, + 100000, + 10800, + 40, + 2629800, + 31557600, + 1, + 0.9, + 604800, + 5, + 12, + 300, + 25, + 10, + 0.66), "20-7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67", 10, 1000000, @@ -57,9 +108,25 @@ class TestBlockchainsRepo(unittest.TestCase): 0, 999999, "testcurrency" - ) - ) + )) blockchains_repo.insert(Blockchain( + BlockchainParameters( + 0.1, + 86400 * 365, + 100000, + 10800, + 40, + 2629800, + 31557600, + 1, + 0.9, + 604800, + 5, + 12, + 300, + 25, + 10, + 0.66), "20-7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67", 20, 1000000, @@ -68,14 +135,17 @@ class TestBlockchainsRepo(unittest.TestCase): 0, 999999, "testcurrency2" - ) - ) + )) + + blockchains = blockchains_repo.get_all() + # result sorted by currency name by default + self.assertEquals(86400, blockchains[0].parameters.dt) + self.assertEquals("testcurrency", blockchains[0].currency) + self.assertEquals(10, blockchains[0].nb_members) - blockchains = blockchains_repo.get_all(currency="testcurrency") - self.assertIn("testcurrency", [i.currency for i in blockchains]) - self.assertIn("testcurrency2", [i.currency for i in blockchains]) - self.assertIn(10, [i.nb_members for i in blockchains]) - self.assertIn(20, [i.nb_members for i in blockchains]) + self.assertEquals(86400*365, blockchains[1].parameters.dt) + self.assertEquals("testcurrency2", blockchains[1].currency) + self.assertEquals(20, blockchains[1].nb_members) def test_add_update_blockchain(self): meta_repo = MetaDatabase(self.con) @@ -83,6 +153,23 @@ class TestBlockchainsRepo(unittest.TestCase): meta_repo.upgrade_database() blockchains_repo = BlockchainsRepo(self.con) blockchain = Blockchain( + BlockchainParameters( + 0.1, + 86400, + 100000, + 10800, + 40, + 2629800, + 31557600, + 1, + 0.9, + 604800, + 5, + 12, + 300, + 25, + 10, + 0.66), "20-7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67", 10, 1000000,