Skip to content
Snippets Groups Projects
Commit 602a3527 authored by inso's avatar inso Committed by GitHub
Browse files

Merge pull request #537 from vtexier/feature/backend

Feature/backend
parents e9191c5c b2c04d75
No related branches found
No related tags found
No related merge requests found
from .identity import Identity from .identity import Identity
from .community import Community from .blockchain import Blockchain, BlockchainParameters
from .certification import Certification from .certification import Certification
from .transaction import Transaction from .transaction import Transaction
from .node import Node from .node import Node
...@@ -2,13 +2,59 @@ import attr ...@@ -2,13 +2,59 @@ import attr
from duniterpy.documents import block_uid, BlockUID 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() @attr.s()
class Blockchain: 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()) current_buid = attr.ib(convert=block_uid, default=BlockUID.empty())
# Number of members
nb_members = attr.ib(convert=int, default=0, cmp=False) nb_members = attr.ib(convert=int, default=0, cmp=False)
# Current monetary mass in units
current_mass = attr.ib(convert=int, default=0, cmp=False) current_mass = attr.ib(convert=int, default=0, cmp=False)
# Median time in seconds
median_time = attr.ib(convert=int, default=0, cmp=False) median_time = attr.ib(convert=int, default=0, cmp=False)
# Last UD amount in units (multiply by 10^base)
last_ud = attr.ib(convert=int, default=0, cmp=False) last_ud = attr.ib(convert=int, default=0, cmp=False)
# Last UD base
last_ud_base = attr.ib(convert=int, default=0, cmp=False) last_ud_base = attr.ib(convert=int, default=0, cmp=False)
# Previous monetary mass in units
previous_mass = attr.ib(convert=int, default=0, cmp=False) previous_mass = attr.ib(convert=int, default=0, cmp=False)
# Currency name
currency = attr.ib(convert=str, default="", cmp=False) currency = attr.ib(convert=str, default="", cmp=False)
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)
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)
from .identities import IdentitiesRepo from .identities import IdentitiesRepo
from .communities import CommunitiesRepo from .blockchains import BlockchainsRepo
from .meta import MetaDatabase from .meta import MetaDatabase
from .certifications import CertificationsRepo from .certifications import CertificationsRepo
from .transactions import TransactionsRepo from .transactions import TransactionsRepo
......
from typing import List
import attr
from ..entities import Blockchain, BlockchainParameters
@attr.s(frozen=True)
class BlockchainsRepo:
"""The repository for Blockchain entities.
"""
_conn = attr.ib() # :type sqlite3.Connection
_primary_keys = (Blockchain.currency,)
def insert(self, blockchain):
"""
Commit a blockchain to the database
:param sakia.data.entities.Blockchain blockchain: the blockchain to commit
"""
with self._conn:
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)
def update(self, blockchain):
"""
Update an existing blockchain in the database
:param sakia.data.entities.Blockchain blockchain: the blockchain to update
"""
with self._conn:
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=?,
nb_members=?,
current_mass=?,
median_time=?,
last_ud=?,
last_ud_base=?,
previous_mass=?
WHERE
currency=?""",
updated_fields + where_fields)
def get_one(self, **search):
"""
Get an existing blockchain in the database
:param dict search: the criterions of the lookup
:rtype: sakia.data.entities.Blockchain
"""
with self._conn:
filters = []
values = []
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))
data = c.fetchone()
if data:
return Blockchain(BlockchainParameters(*data[:15]), *data[16:])
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]
"""
with self._conn:
filters = []
values = []
if search:
for k, v in search.items():
filters.append("{k}=?".format(k=k))
values.append(v)
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(BlockchainParameters(*data[:15]), *data[16:]) for data in datas]
return []
def drop(self, blockchain):
"""
Drop an existing blockchain from the database
:param sakia.data.entities.Blockchain blockchain: the blockchain to update
"""
with self._conn:
where_fields = attr.astuple(blockchain, filter=attr.filters.include(*BlockchainsRepo._primary_keys))
self._conn.execute("DELETE FROM blockchains WHERE currency=?", where_fields)
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)
...@@ -16,8 +16,8 @@ CREATE TABLE IF NOT EXISTS identities( ...@@ -16,8 +16,8 @@ CREATE TABLE IF NOT EXISTS identities(
PRIMARY KEY (currency, pubkey, uid, blockstamp) PRIMARY KEY (currency, pubkey, uid, blockstamp)
); );
-- COMMUNITY TABLE -- BLOCKCHAIN TABLE
CREATE TABLE IF NOT EXISTS communities( CREATE TABLE IF NOT EXISTS blockchains (
c FLOAT(1, 6), c FLOAT(1, 6),
dt INT, dt INT,
ud0 INT, ud0 INT,
...@@ -34,6 +34,13 @@ CREATE TABLE IF NOT EXISTS communities( ...@@ -34,6 +34,13 @@ CREATE TABLE IF NOT EXISTS communities(
dt_diff_eval INT, dt_diff_eval INT,
blocks_rot INT, blocks_rot INT,
percent_rot FLOAT(1, 6), percent_rot FLOAT(1, 6),
current_buid INT,
nb_members INT,
current_mass INT,
median_time INT,
last_ud INT,
last_ud_base INT,
previous_mass INT,
currency VARCHAR(30), currency VARCHAR(30),
PRIMARY KEY (currency) PRIMARY KEY (currency)
); );
......
import sqlite3
import unittest
from duniterpy.documents import BlockUID
from sakia.data.entities import Blockchain, BlockchainParameters
from sakia.data.repositories import BlockchainsRepo, MetaDatabase
class TestBlockchainsRepo(unittest.TestCase):
def setUp(self):
sqlite3.register_adapter(BlockUID, str)
sqlite3.register_adapter(bool, int)
sqlite3.register_converter("BOOLEAN", lambda v: bool(int(v)))
self.con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_DECLTYPES)
def tearDown(self):
self.con.close()
def test_add_get_drop_blockchain(self):
meta_repo = MetaDatabase(self.con)
meta_repo.prepare()
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,
86400,
100000,
0,
999999,
"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")
)
self.assertEqual(blockchain.nb_members, 10)
blockchains_repo.drop(blockchain)
blockchain = blockchains_repo.get_one(currency="testcurrency")
self.assertIsNone(blockchain)
def test_add_get_multiple_blockchain(self):
meta_repo = MetaDatabase(self.con)
meta_repo.prepare()
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,
86400,
100000,
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,
86400,
100000,
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)
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)
meta_repo.prepare()
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,
86400,
100000,
0,
999999,
"testcurrency"
)
blockchains_repo.insert(blockchain)
blockchain.nb_members = 30
blockchains_repo.update(blockchain)
blockchain2 = blockchains_repo.get_one(currency="testcurrency")
self.assertEquals(30, blockchain2.nb_members)
...@@ -7,7 +7,7 @@ from sakia.data.entities import Community ...@@ -7,7 +7,7 @@ from sakia.data.entities import Community
from sakia.data.repositories import CommunitiesRepo, MetaDatabase from sakia.data.repositories import CommunitiesRepo, MetaDatabase
class TestIdentitiesRepo(unittest.TestCase): class TestCommunitiesRepo(unittest.TestCase):
def setUp(self): def setUp(self):
sqlite3.register_adapter(BlockUID, str) sqlite3.register_adapter(BlockUID, str)
sqlite3.register_adapter(bool, int) sqlite3.register_adapter(bool, int)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment